class ODataBatchV3Request
ODataBatchV3Request is a specialized OData request handler for constructing and processing OData v3 batch requests using multipart/mixed HTTP format.
/tf/active/vicechatdev/SPFCsync/venv/lib64/python3.11/site-packages/office365/runtime/odata/v3/batch_request.py
18 - 148
complex
Purpose
This class extends ODataRequest to handle batch operations in OData v3 protocol. It serializes multiple queries into a single multipart/mixed HTTP request, sends them as a batch, and deserializes the multipart response back into individual query responses. This is useful for optimizing network traffic by combining multiple OData operations (GET, POST, PUT, DELETE) into a single HTTP request.
Source Code
class ODataBatchV3Request(ODataRequest):
def build_request(self, query):
"""
Construct a OData v3 Batch request
:type query: office365.runtime.queries.batch.BatchQuery
"""
request = RequestOptions(query.url)
request.method = HttpMethod.Post
media_type = "multipart/mixed"
content_type = "; ".join(
[media_type, "boundary={0}".format(query.current_boundary)]
)
request.ensure_header("Content-Type", content_type)
request.data = self._prepare_payload(query)
return request
def process_response(self, response, query):
"""
Parses an HTTP response.
:type response: requests.Response
:type query: office365.runtime.queries.batch.BatchQuery
"""
for sub_qry, sub_resp in self._extract_response(response, query):
sub_resp.raise_for_status()
super(ODataBatchV3Request, self).process_response(sub_resp, sub_qry)
def _extract_response(self, response, query):
"""Parses a multipart/mixed response body from the position defined by the context.
:type response: requests.Response
:type query: office365.runtime.queries.batch.BatchQuery
"""
content_type = response.headers["Content-Type"].encode("ascii")
http_body = b"Content-Type: " + content_type + b"\r\n\r\n" + response.content
message = message_from_bytes_or_string(http_body) # type: Message
query_id = 0
for raw_response in message.get_payload():
if raw_response.get_content_type() == "application/http":
qry = query.ordered_queries[query_id]
query_id += 1
yield qry, self._deserialize_response(raw_response)
def _prepare_payload(self, query):
"""
Serializes a batch request body.
:type query: office365.runtime.queries.batch.BatchQuery
"""
main_message = Message()
main_message.add_header("Content-Type", "multipart/mixed")
main_message.set_boundary(query.current_boundary)
if query.has_change_sets:
change_set_message = Message()
change_set_boundary = create_boundary("changeset_", True)
change_set_message.add_header("Content-Type", "multipart/mixed")
change_set_message.set_boundary(change_set_boundary)
for qry in query.change_sets:
request = qry.build_request()
message = self._serialize_request(request)
change_set_message.attach(message)
main_message.attach(change_set_message)
for qry in query.get_queries:
request = qry.build_request()
message = self._serialize_request(request)
main_message.attach(message)
return message_as_bytes_or_string(main_message)
@staticmethod
def _normalize_headers(headers_raw):
"""
:type headers_raw: list[str]
"""
headers = {}
for header_line in headers_raw:
k, v = header_line.split(":", 1)
headers[k.title()] = v.strip()
return CaseInsensitiveDict(headers)
def _deserialize_response(self, raw_response):
"""
:type raw_response: Message
"""
response = raw_response.get_payload(decode=True)
lines = list(filter(None, response.decode("utf-8").split("\r\n")))
response_status_regex = "^HTTP/1\\.\\d (\\d{3}) (.*)$"
status_result = re.match(response_status_regex, lines[0])
status_info = status_result.groups()
resp = requests.Response()
resp.status_code = int(status_info[0])
if status_info[1] == "No Content" or len(lines) < 3:
resp.headers = self._normalize_headers(lines[1:])
resp._content = bytes(str("").encode("utf-8"))
else:
resp._content = bytes(str(lines[-1]).encode("utf-8"))
resp.headers = self._normalize_headers(lines[1:-1])
return resp
@staticmethod
def _serialize_request(request):
"""Serializes a part of a batch request to a string. A part can be either a GET request or
a change set grouping several CUD (create, update, delete) requests.
:type request: RequestOptions
"""
eol = "\r\n"
method = request.method
if "X-HTTP-Method" in request.headers:
method = request.headers["X-HTTP-Method"]
lines = ["{method} {url} HTTP/1.1".format(method=method, url=request.url)] + [
":".join(h) for h in request.headers.items()
]
if request.data:
lines.append(eol)
lines.append(json.dumps(request.data))
raw_content = eol + eol.join(lines) + eol
payload = raw_content.encode("utf-8").lstrip()
message = Message()
message.add_header("Content-Type", "application/http")
message.add_header("Content-Transfer-Encoding", "binary")
message.set_payload(payload)
return message
Parameters
| Name | Type | Default | Kind |
|---|---|---|---|
bases |
ODataRequest | - |
Parameter Details
bases: Inherits from ODataRequest, which provides base functionality for OData request handling. The class does not define its own __init__ method, so it uses the parent class constructor.
Return Value
Instantiation returns an ODataBatchV3Request object. The build_request method returns a RequestOptions object configured for batch requests. The process_response method has no return value but processes responses in-place. The _extract_response method yields tuples of (query, response) pairs. The _prepare_payload method returns a serialized batch request body as bytes or string.
Class Interface
Methods
build_request(self, query: BatchQuery) -> RequestOptions
Purpose: Constructs an OData v3 batch request with proper multipart/mixed formatting and headers
Parameters:
query: A BatchQuery object containing the URL, boundary, and collection of queries to be batched
Returns: A RequestOptions object configured with POST method, multipart/mixed content-type header, and serialized batch payload
process_response(self, response: requests.Response, query: BatchQuery) -> None
Purpose: Parses a multipart/mixed HTTP batch response and processes each sub-response individually
Parameters:
response: The HTTP response object from the batch requestquery: The original BatchQuery object used to build the request
Returns: None. Processes responses in-place by calling parent class process_response for each sub-query
_extract_response(self, response: requests.Response, query: BatchQuery) -> Generator[Tuple[Query, requests.Response], None, None]
Purpose: Parses the multipart/mixed response body and extracts individual query-response pairs
Parameters:
response: The HTTP response object containing the batch responsequery: The BatchQuery object with ordered_queries list
Returns: Generator yielding tuples of (query, response) for each sub-request in the batch
_prepare_payload(self, query: BatchQuery) -> Union[bytes, str]
Purpose: Serializes a batch request body into multipart/mixed format with proper boundaries and structure
Parameters:
query: The BatchQuery object containing change sets and GET queries to serialize
Returns: Serialized batch request body as bytes or string in multipart/mixed format
_normalize_headers(headers_raw: List[str]) -> CaseInsensitiveDict
static
Purpose: Converts raw header lines into a case-insensitive dictionary
Parameters:
headers_raw: List of header strings in 'Key: Value' format
Returns: CaseInsensitiveDict containing parsed headers with title-cased keys
_deserialize_response(self, raw_response: Message) -> requests.Response
Purpose: Deserializes a single HTTP response from a multipart message part
Parameters:
raw_response: Email Message object containing the raw HTTP response
Returns: A requests.Response object with parsed status code, headers, and content
_serialize_request(request: RequestOptions) -> Message
static
Purpose: Serializes a single request into a multipart message part with proper HTTP formatting
Parameters:
request: RequestOptions object containing method, URL, headers, and data
Returns: Email Message object containing the serialized HTTP request
Dependencies
jsonreemail.messagerequestsrequests.structuresoffice365.runtime.compatoffice365.runtime.http.http_methodoffice365.runtime.http.request_optionsoffice365.runtime.odata.requestoffice365.runtime.queries.batch
Required Imports
import json
import re
from email.message import Message
import requests
from requests.structures import CaseInsensitiveDict
from office365.runtime.compat import message_as_bytes_or_string
from office365.runtime.compat import message_from_bytes_or_string
from office365.runtime.http.http_method import HttpMethod
from office365.runtime.http.request_options import RequestOptions
from office365.runtime.odata.request import ODataRequest
from office365.runtime.queries.batch import create_boundary
Usage Example
from office365.runtime.odata.v3.batch_request import ODataBatchV3Request
from office365.runtime.queries.batch import BatchQuery
# Create a batch request handler
batch_request = ODataBatchV3Request()
# Assume we have a BatchQuery object with multiple queries
batch_query = BatchQuery(url='https://api.example.com/$batch')
# Add queries to batch_query (implementation depends on BatchQuery class)
# Build the batch request
request_options = batch_request.build_request(batch_query)
# Send the request (using requests library or similar)
import requests
response = requests.request(
method=request_options.method,
url=request_options.url,
headers=request_options.headers,
data=request_options.data
)
# Process the batch response
batch_request.process_response(response, batch_query)
Best Practices
- Always ensure the BatchQuery object is properly configured with a valid URL and boundary before calling build_request
- The process_response method will raise HTTP exceptions for failed sub-requests, so wrap calls in try-except blocks
- The class maintains no internal state between method calls, making it safe to reuse for multiple batch operations
- Use this class when you need to combine multiple OData operations to reduce network overhead
- Ensure the OData endpoint supports v3 batch protocol with multipart/mixed format
- The _extract_response method is a generator, yielding query-response pairs for memory efficiency
- Change sets (CUD operations) are automatically grouped together in the batch payload
- GET requests are processed separately from change sets in the batch structure
Similar Components
AI-powered semantic similarity - components with related functionality:
-
class ODataV4BatchRequest 87.9% similar
-
class ODataRequest 71.8% similar
-
class GraphRequest 58.1% similar
-
class BatchCreationResult 57.2% similar
-
class SharePointRequest 55.2% similar