Skip to content
Merged
Show file tree
Hide file tree
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
65 changes: 64 additions & 1 deletion playwright/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import sys
import typing

from playwright.sync_base import SyncBase
from playwright.sync_base import SyncBase, mapping

if sys.version_info >= (3, 8): # pragma: no cover
from typing import Literal
Expand Down Expand Up @@ -128,6 +128,9 @@ def isNavigationRequest(self) -> bool:
return self._sync(self._async_obj.isNavigationRequest())


mapping.register(RequestAsync, Request)


class Response(SyncBase):
def __init__(self, obj: ResponseAsync):
super().__init__(obj)
Expand Down Expand Up @@ -200,6 +203,9 @@ def json(self) -> typing.Union[typing.Dict, typing.List]:
return self._sync(self._async_obj.json())


mapping.register(ResponseAsync, Response)


class Route(SyncBase):
def __init__(self, obj: RouteAsync):
super().__init__(obj)
Expand Down Expand Up @@ -258,6 +264,9 @@ def continue_(
)


mapping.register(RouteAsync, Route)


class Keyboard(SyncBase):
def __init__(self, obj: KeyboardAsync):
super().__init__(obj)
Expand Down Expand Up @@ -305,6 +314,9 @@ def press(self, key: str, delay: int = None) -> NoneType:
return self._sync(self._async_obj.press(key=key, delay=delay))


mapping.register(KeyboardAsync, Keyboard)


class Mouse(SyncBase):
def __init__(self, obj: MouseAsync):
super().__init__(obj)
Expand Down Expand Up @@ -371,6 +383,9 @@ def dblclick(
)


mapping.register(MouseAsync, Mouse)


class JSHandle(SyncBase):
def __init__(self, obj: JSHandleAsync):
super().__init__(obj)
Expand Down Expand Up @@ -440,6 +455,9 @@ def jsonValue(self) -> typing.Any:
return self._sync(self._async_obj.jsonValue())


mapping.register(JSHandleAsync, JSHandle)


class ElementHandle(SyncBase):
def __init__(self, obj: ElementHandleAsync):
super().__init__(obj)
Expand Down Expand Up @@ -707,6 +725,9 @@ def evalOnSelectorAll(
)


mapping.register(ElementHandleAsync, ElementHandle)


class Accessibility(SyncBase):
def __init__(self, obj: AccessibilityAsync):
super().__init__(obj)
Expand Down Expand Up @@ -746,6 +767,9 @@ def snapshot(
)


mapping.register(AccessibilityAsync, Accessibility)


class Frame(SyncBase):
def __init__(self, obj: FrameAsync):
super().__init__(obj)
Expand Down Expand Up @@ -1174,6 +1198,9 @@ def title(self) -> str:
return self._sync(self._async_obj.title())


mapping.register(FrameAsync, Frame)


class Worker(SyncBase):
def __init__(self, obj: WorkerAsync):
super().__init__(obj)
Expand Down Expand Up @@ -1226,6 +1253,9 @@ def evaluateHandle(
)


mapping.register(WorkerAsync, Worker)


class Selectors(SyncBase):
def __init__(self, obj: SelectorsAsync):
super().__init__(obj)
Expand Down Expand Up @@ -1267,6 +1297,9 @@ def register(
)


mapping.register(SelectorsAsync, Selectors)


class ConsoleMessage(SyncBase):
def __init__(self, obj: ConsoleMessageAsync):
super().__init__(obj)
Expand Down Expand Up @@ -1315,6 +1348,9 @@ def location(self) -> ConsoleMessageLocation:
return self._async_obj.location


mapping.register(ConsoleMessageAsync, ConsoleMessage)


class Dialog(SyncBase):
def __init__(self, obj: DialogAsync):
super().__init__(obj)
Expand Down Expand Up @@ -1361,6 +1397,9 @@ def dismiss(self) -> NoneType:
return self._sync(self._async_obj.dismiss())


mapping.register(DialogAsync, Dialog)


class Download(SyncBase):
def __init__(self, obj: DownloadAsync):
super().__init__(obj)
Expand Down Expand Up @@ -1410,6 +1449,9 @@ def path(self) -> typing.Union[str, NoneType]:
return self._sync(self._async_obj.path())


mapping.register(DownloadAsync, Download)


class BindingCall(SyncBase):
def __init__(self, obj: BindingCallAsync):
super().__init__(obj)
Expand Down Expand Up @@ -1445,6 +1487,9 @@ def call(self, func: typing.Callable[[typing.Dict], typing.Any]) -> NoneType:
return self._sync(self._async_obj.call(func=func))


mapping.register(BindingCallAsync, BindingCall)


class Page(SyncBase):
def __init__(self, obj: PageAsync):
super().__init__(obj)
Expand Down Expand Up @@ -2075,6 +2120,9 @@ def pdf(
)


mapping.register(PageAsync, Page)


class BrowserContext(SyncBase):
def __init__(self, obj: BrowserContextAsync):
super().__init__(obj)
Expand Down Expand Up @@ -2197,6 +2245,9 @@ def close(self) -> NoneType:
return self._sync(self._async_obj.close())


mapping.register(BrowserContextAsync, BrowserContext)


class Browser(SyncBase):
def __init__(self, obj: BrowserAsync):
super().__init__(obj)
Expand Down Expand Up @@ -2327,6 +2378,9 @@ def close(self) -> NoneType:
return self._sync(self._async_obj.close())


mapping.register(BrowserAsync, Browser)


class BrowserServer(SyncBase):
def __init__(self, obj: BrowserServerAsync):
super().__init__(obj)
Expand Down Expand Up @@ -2373,6 +2427,9 @@ def close(self) -> NoneType:
return self._sync(self._async_obj.close())


mapping.register(BrowserServerAsync, BrowserServer)


class BrowserType(SyncBase):
def __init__(self, obj: BrowserTypeAsync):
super().__init__(obj)
Expand Down Expand Up @@ -2568,6 +2625,9 @@ def connect(
)


mapping.register(BrowserTypeAsync, BrowserType)


class Playwright(SyncBase):
def __init__(self, obj: PlaywrightAsync):
super().__init__(obj)
Expand Down Expand Up @@ -2618,3 +2678,6 @@ def selectors(self) -> "Selectors":
@property
def devices(self) -> typing.Dict:
return self._async_obj.devices


mapping.register(PlaywrightAsync, Playwright)
34 changes: 30 additions & 4 deletions playwright/sync_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,29 @@
# limitations under the License.

import asyncio
from typing import Any, Callable, Union
from typing import Any, Callable, List, Tuple, Union

from playwright.wait_helper import WaitHelper

loop = asyncio.get_event_loop()


class AsyncToSyncMapping:
mapping: List[Tuple[type, type]] = []

def register(self, async_class: type, sync_class: type) -> None:
self.mapping.append((async_class, sync_class))

def get_sync_class(self, input_async_inst: object) -> Any:
for (async_class, sync_class) in self.mapping:
if isinstance(input_async_inst, async_class):
return sync_class
raise ValueError("should never happen")


mapping = AsyncToSyncMapping()


class Event:
def __init__(
self,
Expand All @@ -34,14 +50,15 @@ def __init__(
wait_helper.reject_on_timeout(
timeout or 30000, f'Timeout while waiting for event "${event}"'
)
self._future = asyncio.create_task(
self._future = loop.create_task(
wait_helper.wait_for_event(sync_base._async_obj, event, predicate)
)

@property
def value(self) -> Any:
if not self._value:
self._value = loop.run_until_complete(self._future)
value = loop.run_until_complete(self._future)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have some events with the primitive type values.

self._value = mapping.get_sync_class(value)._from_async(value)
return self._value


Expand All @@ -66,11 +83,20 @@ class SyncBase:
def __init__(self, async_obj: Any) -> None:
self._async_obj = async_obj

def __str__(self) -> str:
return self._async_obj.__str__()

def _sync(self, future: asyncio.Future) -> Any:
return loop.run_until_complete(future)

def _map_event(self, handler: Callable[[Any], None]) -> Callable[[Any], None]:
return lambda event: handler(mapping.get_sync_class(event)._from_async(event))

def on(self, event_name: str, handler: Any) -> None:
self._async_obj.on(event_name, handler)
self._async_obj.on(event_name, self._map_event(handler))

def once(self, event_name: str, handler: Any) -> None:
self._async_obj.once(event_name, self._map_event(handler))

def remove_listener(self, event_name: str, handler: Any) -> None:
self._async_obj.remove_listener(event_name, handler)
Expand Down
5 changes: 3 additions & 2 deletions playwright/wait_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
class WaitHelper:
def __init__(self) -> None:
self._failures: List[asyncio.Future] = []
self._loop = asyncio.get_event_loop()

def reject_on_event(
self,
Expand All @@ -37,15 +38,15 @@ def reject_on_timeout(self, timeout: int, message: str) -> None:
if timeout == 0:
return
self.reject_on(
asyncio.create_task(asyncio.sleep(timeout / 1000)), TimeoutError(message)
self._loop.create_task(asyncio.sleep(timeout / 1000)), TimeoutError(message)
)

def reject_on(self, future: asyncio.Future, error: Error) -> None:
async def future_wrapper() -> Error:
await future
return error

result = asyncio.create_task(future_wrapper())
result = self._loop.create_task(future_wrapper())
result.add_done_callback(lambda f: future.cancel())
self._failures.append(result)

Expand Down
3 changes: 2 additions & 1 deletion scripts/generate_sync_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ def generate(t: Any) -> None:
prefix = " return " + prefix + f"self._sync(self._async_obj.{name}("
suffix = "))" + suffix
print(f"{prefix}{arguments(value, len(prefix))}{suffix}")
print(f"mapping.register({short_name(t)}Async, {short_name(t)})")


def main() -> None:
Expand All @@ -222,7 +223,7 @@ def main() -> None:

import typing
import sys
from playwright.sync_base import SyncBase
from playwright.sync_base import SyncBase, mapping

if sys.version_info >= (3, 8): # pragma: no cover
from typing import Literal
Expand Down
Loading