import inspect
from typing import Any, Callable, Dict, List, Optional, Sequence, Union
from patchright._impl._errors import Error
from patchright._impl._map import Map
API_ATTR = "_pw_api_instance_"
IMPL_ATTR = "_pw_impl_instance_"
class ImplWrapper:
def __init__(self, impl_obj: Any) -> None:
self._impl_obj = impl_obj
def __repr__(self) -> str:
return self._impl_obj.__repr__()
class ImplToApiMapping:
def __init__(self) -> None:
self._mapping: Dict[type, type] = {}
def register(self, impl_class: type, api_class: type) -> None:
self._mapping[impl_class] = api_class
def from_maybe_impl(
self, obj: Any, visited: Optional[Map[Any, Union[List, Dict]]] = None
) -> Any:
if not visited:
visited = Map()
if not obj:
return obj
if isinstance(obj, dict):
if obj in visited:
return visited[obj]
o: Dict = {}
visited[obj] = o
for name, value in obj.items():
o[name] = self.from_maybe_impl(value, visited)
return o
if isinstance(obj, list):
if obj in visited:
return visited[obj]
a: List = []
visited[obj] = a
for item in obj:
a.append(self.from_maybe_impl(item, visited))
return a
api_class = self._mapping.get(type(obj))
if api_class:
api_instance = getattr(obj, API_ATTR, None)
if not api_instance:
api_instance = api_class(obj)
setattr(obj, API_ATTR, api_instance)
return api_instance
else:
return obj
def from_impl(self, obj: Any) -> Any:
assert obj
result = self.from_maybe_impl(obj)
assert result
return result
def from_impl_nullable(self, obj: Any = None) -> Optional[Any]:
return self.from_impl(obj) if obj else None
def from_impl_list(self, items: Sequence[Any]) -> List[Any]:
return list(map(lambda a: self.from_impl(a), items))
def from_impl_dict(self, map: Dict[str, Any]) -> Dict[str, Any]:
return {name: self.from_impl(value) for name, value in map.items()}
def to_impl(
self, obj: Any, visited: Optional[Map[Any, Union[List, Dict]]] = None
) -> Any:
if visited is None:
visited = Map()
try:
if not obj:
return obj
if isinstance(obj, dict):
if obj in visited:
return visited[obj]
o: Dict = {}
visited[obj] = o
for name, value in obj.items():
o[name] = self.to_impl(value, visited)
return o
if isinstance(obj, list):
if obj in visited:
return visited[obj]
a: List = []
visited[obj] = a
for item in obj:
a.append(self.to_impl(item, visited))
return a
if isinstance(obj, ImplWrapper):
return obj._impl_obj
return obj
except RecursionError:
raise Error("Maximum argument depth exceeded")
def wrap_handler(self, handler: Callable[..., Any]) -> Callable[..., None]:
def wrapper_func(*args: Any) -> Any:
arg_count = len(inspect.signature(handler).parameters)
return handler(
*list(map(lambda a: self.from_maybe_impl(a), args))[:arg_count]
)
if inspect.ismethod(handler):
wrapper = getattr(handler.__self__, IMPL_ATTR + handler.__name__, None)
if not wrapper:
wrapper = wrapper_func
setattr(handler.__self__, IMPL_ATTR + handler.__name__, wrapper)
return wrapper
wrapper = getattr(handler, IMPL_ATTR, None)
if not wrapper:
wrapper = wrapper_func
setattr(handler, IMPL_ATTR, wrapper)
return wrapper