Skip to content

[py] Simplified complicated conditional in __convert_to_local_value function in bidi/script.py using singledispatchmethod #15989

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: trunk
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 74 additions & 45 deletions py/selenium/webdriver/common/bidi/script.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import datetime
import math
from dataclasses import dataclass
from functools import singledispatchmethod
from typing import Any, Optional

from selenium.common.exceptions import WebDriverException
Expand Down Expand Up @@ -232,6 +233,74 @@ def from_json(cls, json: dict[str, Any]) -> "RealmDestroyed":
return cls(realm=json["realm"])


class _SupportedTypes:
def __init__(self, script):
self.script = script

@singledispatchmethod
def _type(self, value):
# default case for str and other types, convert to string
return {"type": "string", "value": str(value)}

@_type.register
def _(self, value: None):
return {"type": "null"}

@_type.register
def _(self, value: bool):
return {"type": "boolean", "value": value}

@_type.register
def _(self, value: set):
return {"type": "set", "value": [self.script.convert_to_local_value(item) for item in value]}

@_type.register
def _(self, value: list):
return {"type": "array", "value": [self.script.convert_to_local_value(item) for item in value]}

@_type.register
def _(self, value: tuple):
return {"type": "array", "value": [self.script.convert_to_local_value(item) for item in value]}

@_type.register
def _(self, value: dict):
value = [
[self.script.convert_to_local_value(k), self.script.convert_to_local_value(v)] for k, v in value.items()
]
return {"type": "object", "value": value}

@_type.register
def _(self, value: int):
JS_MAX_SAFE_INTEGER = 9007199254740991
if value > JS_MAX_SAFE_INTEGER or value < -JS_MAX_SAFE_INTEGER:
return {"type": "bigint", "value": str(value)}
return {"type": "number", "value": value}

@_type.register
def _(self, value: float):
if math.isnan(value):
return {"type": "number", "value": "NaN"}
elif math.isinf(value):
if value > 0:
return {"type": "number", "value": "Infinity"}
else:
return {"type": "number", "value": "-Infinity"}
elif value == 0.0 and math.copysign(1.0, value) < 0:
return {"type": "number", "value": "-0"}
return {"type": "number", "value": value}

@_type.register
def _(self, value: datetime.datetime):
# Convert Python datetime to JavaScript Date (ISO 8601 format)
return {"type": "date", "value": value.isoformat() + "Z" if value.tzinfo is None else value.isoformat()}

@_type.register
def _(self, value: datetime.date):
# Convert Python date to JavaScript Date
dt = datetime.datetime.combine(value, datetime.time.min).replace(tzinfo=datetime.timezone.utc)
return {"type": "date", "value": dt.isoformat()}


class Script:
"""BiDi implementation of the script module."""

Expand Down Expand Up @@ -334,51 +403,11 @@ def __convert_to_local_value(self, value) -> dict:
"""
Converts a Python value to BiDi LocalValue format.
"""
if value is None:
return {"type": "null"}
elif isinstance(value, bool):
return {"type": "boolean", "value": value}
elif isinstance(value, (int, float)):
if isinstance(value, float):
if math.isnan(value):
return {"type": "number", "value": "NaN"}
elif math.isinf(value):
if value > 0:
return {"type": "number", "value": "Infinity"}
else:
return {"type": "number", "value": "-Infinity"}
elif value == 0.0 and math.copysign(1.0, value) < 0:
return {"type": "number", "value": "-0"}

JS_MAX_SAFE_INTEGER = 9007199254740991
if isinstance(value, int) and (value > JS_MAX_SAFE_INTEGER or value < -JS_MAX_SAFE_INTEGER):
return {"type": "bigint", "value": str(value)}

return {"type": "number", "value": value}

elif isinstance(value, str):
return {"type": "string", "value": value}
elif isinstance(value, datetime.datetime):
# Convert Python datetime to JavaScript Date (ISO 8601 format)
return {"type": "date", "value": value.isoformat() + "Z" if value.tzinfo is None else value.isoformat()}
elif isinstance(value, datetime.date):
# Convert Python date to JavaScript Date
dt = datetime.datetime.combine(value, datetime.time.min).replace(tzinfo=datetime.timezone.utc)
return {"type": "date", "value": dt.isoformat()}
elif isinstance(value, set):
return {"type": "set", "value": [self.__convert_to_local_value(item) for item in value]}
elif isinstance(value, (list, tuple)):
return {"type": "array", "value": [self.__convert_to_local_value(item) for item in value]}
elif isinstance(value, dict):
return {
"type": "object",
"value": [
[self.__convert_to_local_value(k), self.__convert_to_local_value(v)] for k, v in value.items()
],
}
else:
# For other types, convert to string
return {"type": "string", "value": str(value)}
supported_types = _SupportedTypes(self)
return getattr(supported_types, "_type")(value)

def convert_to_local_value(self, value) -> dict:
return self.__convert_to_local_value(value)

# low-level APIs for script module
def _add_preload_script(
Expand Down