Implementation:MarketSquare Robotframework browser Format Response
Format Response
API documentation for the internal helper functions that transform raw HTTP response data into structured Python objects.
Overview
The response formatting helpers are module-level functions defined at the top of Browser/keywords/network.py. They are not Robot Framework keywords -- they are internal Python functions used by the HTTP, Wait For Response, and related keywords to process raw gRPC response data into structured, queryable dictionaries. These functions handle JSON auto-parsing of response bodies, header string-to-dictionary conversion, and Content-Type detection.
Source Location
| Function | File | Lines |
|---|---|---|
_get_headers |
Browser/keywords/network.py |
L26-31 |
_format_response |
Browser/keywords/network.py |
L34-40 |
_jsonize_content |
Browser/keywords/network.py |
L43-54 |
Function: _get_headers
Signature
def _get_headers(body: str, headers: dict):
Purpose
Determines whether the request body is JSON and, if so, automatically adds a Content-Type: application/json header. This function is called before sending an HTTP request (used by the http keyword).
Implementation
def _get_headers(body: str, headers: dict):
try:
json.loads(body)
return {"Content-Type": "application/json", **headers}
except json.decoder.JSONDecodeError:
return headers
Behavior
- Attempts to parse the
bodystring as JSON usingjson.loads(). - If parsing succeeds: Returns a new dictionary with
Content-Type: application/jsonmerged with the caller-provided headers. The caller's headers take precedence due to the**headersspread appearing after the Content-Type entry (if the caller explicitly sets Content-Type, it overrides the auto-detected value). - If parsing fails (raises
json.decoder.JSONDecodeError): Returns the caller-provided headers unchanged.
Parameters
| Parameter | Type | Description |
|---|---|---|
body |
str |
The request body string to test for JSON validity. |
headers |
dict |
The caller-provided request headers dictionary. |
Returns
A dictionary of headers, potentially with Content-Type: application/json prepended.
Function: _format_response
Signature
def _format_response(response: dict) -> dict:
Purpose
Transforms a raw response dictionary (deserialized from gRPC JSON) into a properly structured dictionary with parsed headers and auto-parsed JSON bodies. This is the main entry point for response formatting.
Implementation
def _format_response(response: dict) -> dict:
_jsonize_content(response, "body")
if "request" in response:
request = response["request"]
_jsonize_content(request, "postData")
logger.info(response)
return response
Behavior
- Calls
_jsonize_content(response, "body")to parse response headers and conditionally parse the response body as JSON. - If the response dictionary contains a
requestsub-dictionary (present inWait For Responseresults), calls_jsonize_content(request, "postData")to parse request headers and conditionally parse the request's post data as JSON. - Logs the formatted response at INFO level.
- Returns the modified response dictionary (the dictionary is mutated in place by
_jsonize_content).
Parameters
| Parameter | Type | Description |
|---|---|---|
response |
dict |
The raw response dictionary, typically produced by json.loads() on the gRPC response JSON.
|
Returns
The same dictionary, modified in place with parsed headers and optionally parsed body/postData.
Function: _jsonize_content
Signature
def _jsonize_content(data, bodykey):
Purpose
Converts stringified headers to a dictionary and conditionally parses a body field (identified by bodykey) from JSON string to Python dictionary, based on the Content-Type header.
Implementation
def _jsonize_content(data, bodykey):
headers = json.loads(data.get("headers", "{}"))
data["headers"] = headers
lower_headers = {k.lower(): v for k, v in headers.items()}
if (
"content-type" in lower_headers
and "application/json" in lower_headers["content-type"]
and bodykey in data
and data[bodykey]
):
with contextlib.suppress(json.decoder.JSONDecodeError, TypeError):
data[bodykey] = json.loads(data[bodykey])
Behavior
- Header parsing: Parses the
headersfield from a JSON string to a Python dictionary. Ifheadersis missing, defaults to an empty dictionary ("{}"). - Case-insensitive Content-Type check: Creates a lowercase copy of all header keys to perform a case-insensitive lookup for
content-type. - Conditional body parsing: If all of the following conditions are true, the body field is parsed as JSON:
- The headers contain a
content-typekey (case-insensitive). - The content-type value contains the substring
application/json. - The
bodykeyexists in the data dictionary. - The body value is truthy (not None, not empty string).
- The headers contain a
- Error suppression: If JSON parsing fails (
JSONDecodeError) or encounters a type error (TypeError), the error is silently suppressed and the body remains as its original string value.
Parameters
| Parameter | Type | Description |
|---|---|---|
data |
dict |
The dictionary to process (either the response dict or the nested request dict). |
bodykey |
str |
The key name of the body field to conditionally parse. Typically "body" for responses or "postData" for requests.
|
Returns
None. The function modifies the data dictionary in place.
How These Functions Are Used
In the HTTP keyword (L104):
# Before sending the request:
headers=json.dumps(_get_headers(body, headers))
# After receiving the response:
response_dict = _format_response(json.loads(response.json))
return DotDict(response_dict)
In _wait_for_http_response (L134-140):
response_json = json.loads(responce.json)
# Body may have been streamed in chunks:
if response_json.get("body", not_none) is not None and body:
response_json["body"] = body
return _format_response(response_json)
Data Flow Diagram
The complete data flow from Node.js to the Robot Framework test:
Node.js (fetch/waitForResponse)
|
v
gRPC Response (JSON string)
| - headers: '{"content-type": "application/json"}'
| - body: '{"users": [{"id": 1, "name": "John"}]}'
| - status: 200
|
v
json.loads() -> Python dict
| - headers: '{"content-type": "application/json"}' (still a string)
| - body: '{"users": [{"id": 1, "name": "John"}]}' (still a string)
|
v
_format_response() -> _jsonize_content()
| - headers: {"content-type": "application/json"} (now a dict)
| - body: {"users": [{"id": 1, "name": "John"}]} (now a dict)
|
v
DotDict() wrapping
| - ${response.headers} -> dict
| - ${response.body.users} -> list
| - ${response.body.users[0].name} -> "John"
|
v
Robot Framework test assertions
Important Notes
- These functions are module-level (not methods on the
Networkclass), so they do not have access toselfor any library state. - The
_format_responsefunction mutates its input dictionary rather than creating a copy. This is intentional for efficiency. - The
_get_headersfunction is only used for outgoing requests (theHTTPkeyword), while_format_responseand_jsonize_contentare used for incoming response processing. - The Content-Type check uses
in(substring match), so content types likeapplication/json; charset=utf-8are correctly detected.