Implementation:MarketSquare Robotframework browser Call Js Keyword
Document Type
API Doc -- Documents the Browser.call_js_keyword method, which provides a programmatic Python API for invoking JavaScript extension functions via the gRPC bridge without relying on auto-generated keyword wrappers.
API Summary
The call_js_keyword method allows Python code (plugins, other library components, or direct programmatic use) to call any registered JavaScript extension function by name, passing keyword arguments that are serialized and transmitted to the Node.js side via gRPC.
Source Reference
| File | Lines | Description |
|---|---|---|
Browser/browser.py |
L1086-1110 | call_js_keyword method definition
|
API: call_js_keyword
Signature
def call_js_keyword(self, keyword_name: str, **args) -> Any:
Parameters
| Parameter | Type | Description |
|---|---|---|
keyword_name |
str |
The name of the JavaScript function to call (as it was exported in the module) |
**args |
keyword arguments | Named arguments to pass to the JavaScript function; reserved names are automatically handled |
Returns
Any-- The deserialized return value from the JavaScript function, orNoneif the function returns no value
Source Code
def call_js_keyword(self, keyword_name: str, **args) -> Any:
reserved = {
"logger": "RESERVED",
"playwright": "RESERVED",
"page": "RESERVED",
"context": "RESERVED",
"browser": "RESERVED",
}
_args_browser_internal = {
"arguments": [
(arg_name, reserved.get(arg_name, value))
for arg_name, value in args.items()
]
}
with self.playwright.grpc_channel() as stub:
responses = stub.CallExtensionKeyword(
Request().KeywordCall(
name=keyword_name, arguments=json.dumps(_args_browser_internal)
)
)
for response in responses:
logger.info(response.log)
if response.json == "":
return None
return json.loads(response.json)
Behavior
1. Reserved Parameter Handling
The method defines a dictionary of reserved parameter names and their sentinel values:
reserved = {
"logger": "RESERVED",
"playwright": "RESERVED",
"page": "RESERVED",
"context": "RESERVED",
"browser": "RESERVED",
}
When building the argument list, each argument name is checked against this dictionary. If the name matches a reserved name, the value "RESERVED" is substituted regardless of what the caller provided. Otherwise, the caller's value is used as-is.
This ensures that even if a caller inadvertently passes values for reserved parameters, the Node.js side will still inject the correct live Playwright objects.
2. Argument Serialization
Arguments are structured as a list of (name, value) tuples inside a dictionary:
_args_browser_internal = {
"arguments": [
(arg_name, reserved.get(arg_name, value))
for arg_name, value in args.items()
]
}
This structure is serialized to JSON with json.dumps() and passed as the arguments field of a Request().KeywordCall protobuf message.
3. gRPC Call
with self.playwright.grpc_channel() as stub:
responses = stub.CallExtensionKeyword(
Request().KeywordCall(
name=keyword_name, arguments=json.dumps(_args_browser_internal)
)
)
The method opens a gRPC channel to the Node.js Playwright wrapper and calls the CallExtensionKeyword RPC method. This returns a stream of response messages.
4. Response Processing
for response in responses:
logger.info(response.log)
if response.json == "":
return None
return json.loads(response.json)
Each response in the stream may contain a log message (from calls to the logger function on the JS side), which is forwarded to Python's logger.info().
After iterating through all responses, the final response's json field is examined:
- If empty (
""): the method returnsNone - Otherwise: the JSON string is parsed with
json.loads()and the resulting Python object is returned
Comparison with Auto-Generated Wrappers
The auto-generated keyword wrappers (created by _jskeyword_call) and call_js_keyword use the same underlying mechanism:
| Aspect | Auto-Generated Wrapper | call_js_keyword |
|---|---|---|
| Invocation | Via Robot Framework keyword resolution | Direct Python method call |
| Arguments | Positional/keyword with Python defaults | Keyword-only (**args)
|
| Reserved handling | Hardcoded "RESERVED" in generated code |
Dynamic lookup in reserved dict
|
| gRPC call | stub.CallExtensionKeyword |
stub.CallExtensionKeyword
|
| Return handling | json.loads or return |
json.loads or None
|
The key difference is that call_js_keyword is a single generic entry point, while the auto-generated wrappers provide type-specific signatures matching each JavaScript function.
Usage Example
From Python code within a Browser library plugin or component:
# Call a JS extension function named "withDefaultValue"
result = self.library.call_js_keyword("withDefaultValue", a="custom_value")
# Call with reserved parameters (they will be auto-replaced with "RESERVED")
result = self.library.call_js_keyword(
"myNewStyleFunkyKeyword",
selector="#heading",
page="ignored", # will be replaced with "RESERVED"
logger="ignored", # will be replaced with "RESERVED"
)
Related
- MarketSquare_Robotframework_browser_Custom_Keyword_Invocation -- Principle document for the invocation flow
- MarketSquare_Robotframework_browser_Create_Lib_Component_From_Jsextension -- How auto-generated wrappers are created (alternative invocation path)
- MarketSquare_Robotframework_browser_JavaScript_Module_Authoring -- Rules for the JavaScript functions being called