Skip to content

Commit 7f93f70

Browse files
authored
enh(sync): event class mapping to sync instances (microsoft#87)
1 parent 682d938 commit 7f93f70

File tree

5 files changed

+300
-8
lines changed

5 files changed

+300
-8
lines changed

playwright/sync.py

+64-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import sys
1616
import typing
1717

18-
from playwright.sync_base import SyncBase
18+
from playwright.sync_base import SyncBase, mapping
1919

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

130130

131+
mapping.register(RequestAsync, Request)
132+
133+
131134
class Response(SyncBase):
132135
def __init__(self, obj: ResponseAsync):
133136
super().__init__(obj)
@@ -200,6 +203,9 @@ def json(self) -> typing.Union[typing.Dict, typing.List]:
200203
return self._sync(self._async_obj.json())
201204

202205

206+
mapping.register(ResponseAsync, Response)
207+
208+
203209
class Route(SyncBase):
204210
def __init__(self, obj: RouteAsync):
205211
super().__init__(obj)
@@ -258,6 +264,9 @@ def continue_(
258264
)
259265

260266

267+
mapping.register(RouteAsync, Route)
268+
269+
261270
class Keyboard(SyncBase):
262271
def __init__(self, obj: KeyboardAsync):
263272
super().__init__(obj)
@@ -305,6 +314,9 @@ def press(self, key: str, delay: int = None) -> NoneType:
305314
return self._sync(self._async_obj.press(key=key, delay=delay))
306315

307316

317+
mapping.register(KeyboardAsync, Keyboard)
318+
319+
308320
class Mouse(SyncBase):
309321
def __init__(self, obj: MouseAsync):
310322
super().__init__(obj)
@@ -371,6 +383,9 @@ def dblclick(
371383
)
372384

373385

386+
mapping.register(MouseAsync, Mouse)
387+
388+
374389
class JSHandle(SyncBase):
375390
def __init__(self, obj: JSHandleAsync):
376391
super().__init__(obj)
@@ -440,6 +455,9 @@ def jsonValue(self) -> typing.Any:
440455
return self._sync(self._async_obj.jsonValue())
441456

442457

458+
mapping.register(JSHandleAsync, JSHandle)
459+
460+
443461
class ElementHandle(SyncBase):
444462
def __init__(self, obj: ElementHandleAsync):
445463
super().__init__(obj)
@@ -707,6 +725,9 @@ def evalOnSelectorAll(
707725
)
708726

709727

728+
mapping.register(ElementHandleAsync, ElementHandle)
729+
730+
710731
class Accessibility(SyncBase):
711732
def __init__(self, obj: AccessibilityAsync):
712733
super().__init__(obj)
@@ -746,6 +767,9 @@ def snapshot(
746767
)
747768

748769

770+
mapping.register(AccessibilityAsync, Accessibility)
771+
772+
749773
class Frame(SyncBase):
750774
def __init__(self, obj: FrameAsync):
751775
super().__init__(obj)
@@ -1174,6 +1198,9 @@ def title(self) -> str:
11741198
return self._sync(self._async_obj.title())
11751199

11761200

1201+
mapping.register(FrameAsync, Frame)
1202+
1203+
11771204
class Worker(SyncBase):
11781205
def __init__(self, obj: WorkerAsync):
11791206
super().__init__(obj)
@@ -1226,6 +1253,9 @@ def evaluateHandle(
12261253
)
12271254

12281255

1256+
mapping.register(WorkerAsync, Worker)
1257+
1258+
12291259
class Selectors(SyncBase):
12301260
def __init__(self, obj: SelectorsAsync):
12311261
super().__init__(obj)
@@ -1267,6 +1297,9 @@ def register(
12671297
)
12681298

12691299

1300+
mapping.register(SelectorsAsync, Selectors)
1301+
1302+
12701303
class ConsoleMessage(SyncBase):
12711304
def __init__(self, obj: ConsoleMessageAsync):
12721305
super().__init__(obj)
@@ -1315,6 +1348,9 @@ def location(self) -> ConsoleMessageLocation:
13151348
return self._async_obj.location
13161349

13171350

1351+
mapping.register(ConsoleMessageAsync, ConsoleMessage)
1352+
1353+
13181354
class Dialog(SyncBase):
13191355
def __init__(self, obj: DialogAsync):
13201356
super().__init__(obj)
@@ -1361,6 +1397,9 @@ def dismiss(self) -> NoneType:
13611397
return self._sync(self._async_obj.dismiss())
13621398

13631399

1400+
mapping.register(DialogAsync, Dialog)
1401+
1402+
13641403
class Download(SyncBase):
13651404
def __init__(self, obj: DownloadAsync):
13661405
super().__init__(obj)
@@ -1410,6 +1449,9 @@ def path(self) -> typing.Union[str, NoneType]:
14101449
return self._sync(self._async_obj.path())
14111450

14121451

1452+
mapping.register(DownloadAsync, Download)
1453+
1454+
14131455
class BindingCall(SyncBase):
14141456
def __init__(self, obj: BindingCallAsync):
14151457
super().__init__(obj)
@@ -1445,6 +1487,9 @@ def call(self, func: typing.Callable[[typing.Dict], typing.Any]) -> NoneType:
14451487
return self._sync(self._async_obj.call(func=func))
14461488

14471489

1490+
mapping.register(BindingCallAsync, BindingCall)
1491+
1492+
14481493
class Page(SyncBase):
14491494
def __init__(self, obj: PageAsync):
14501495
super().__init__(obj)
@@ -2075,6 +2120,9 @@ def pdf(
20752120
)
20762121

20772122

2123+
mapping.register(PageAsync, Page)
2124+
2125+
20782126
class BrowserContext(SyncBase):
20792127
def __init__(self, obj: BrowserContextAsync):
20802128
super().__init__(obj)
@@ -2197,6 +2245,9 @@ def close(self) -> NoneType:
21972245
return self._sync(self._async_obj.close())
21982246

21992247

2248+
mapping.register(BrowserContextAsync, BrowserContext)
2249+
2250+
22002251
class Browser(SyncBase):
22012252
def __init__(self, obj: BrowserAsync):
22022253
super().__init__(obj)
@@ -2327,6 +2378,9 @@ def close(self) -> NoneType:
23272378
return self._sync(self._async_obj.close())
23282379

23292380

2381+
mapping.register(BrowserAsync, Browser)
2382+
2383+
23302384
class BrowserServer(SyncBase):
23312385
def __init__(self, obj: BrowserServerAsync):
23322386
super().__init__(obj)
@@ -2373,6 +2427,9 @@ def close(self) -> NoneType:
23732427
return self._sync(self._async_obj.close())
23742428

23752429

2430+
mapping.register(BrowserServerAsync, BrowserServer)
2431+
2432+
23762433
class BrowserType(SyncBase):
23772434
def __init__(self, obj: BrowserTypeAsync):
23782435
super().__init__(obj)
@@ -2568,6 +2625,9 @@ def connect(
25682625
)
25692626

25702627

2628+
mapping.register(BrowserTypeAsync, BrowserType)
2629+
2630+
25712631
class Playwright(SyncBase):
25722632
def __init__(self, obj: PlaywrightAsync):
25732633
super().__init__(obj)
@@ -2618,3 +2678,6 @@ def selectors(self) -> "Selectors":
26182678
@property
26192679
def devices(self) -> typing.Dict:
26202680
return self._async_obj.devices
2681+
2682+
2683+
mapping.register(PlaywrightAsync, Playwright)

playwright/sync_base.py

+30-4
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,29 @@
1313
# limitations under the License.
1414

1515
import asyncio
16-
from typing import Any, Callable, Union
16+
from typing import Any, Callable, List, Tuple, Union
1717

1818
from playwright.wait_helper import WaitHelper
1919

2020
loop = asyncio.get_event_loop()
2121

2222

23+
class AsyncToSyncMapping:
24+
mapping: List[Tuple[type, type]] = []
25+
26+
def register(self, async_class: type, sync_class: type) -> None:
27+
self.mapping.append((async_class, sync_class))
28+
29+
def get_sync_class(self, input_async_inst: object) -> Any:
30+
for (async_class, sync_class) in self.mapping:
31+
if isinstance(input_async_inst, async_class):
32+
return sync_class
33+
raise ValueError("should never happen")
34+
35+
36+
mapping = AsyncToSyncMapping()
37+
38+
2339
class Event:
2440
def __init__(
2541
self,
@@ -34,14 +50,15 @@ def __init__(
3450
wait_helper.reject_on_timeout(
3551
timeout or 30000, f'Timeout while waiting for event "${event}"'
3652
)
37-
self._future = asyncio.create_task(
53+
self._future = loop.create_task(
3854
wait_helper.wait_for_event(sync_base._async_obj, event, predicate)
3955
)
4056

4157
@property
4258
def value(self) -> Any:
4359
if not self._value:
44-
self._value = loop.run_until_complete(self._future)
60+
value = loop.run_until_complete(self._future)
61+
self._value = mapping.get_sync_class(value)._from_async(value)
4562
return self._value
4663

4764

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

86+
def __str__(self) -> str:
87+
return self._async_obj.__str__()
88+
6989
def _sync(self, future: asyncio.Future) -> Any:
7090
return loop.run_until_complete(future)
7191

92+
def _map_event(self, handler: Callable[[Any], None]) -> Callable[[Any], None]:
93+
return lambda event: handler(mapping.get_sync_class(event)._from_async(event))
94+
7295
def on(self, event_name: str, handler: Any) -> None:
73-
self._async_obj.on(event_name, handler)
96+
self._async_obj.on(event_name, self._map_event(handler))
97+
98+
def once(self, event_name: str, handler: Any) -> None:
99+
self._async_obj.once(event_name, self._map_event(handler))
74100

75101
def remove_listener(self, event_name: str, handler: Any) -> None:
76102
self._async_obj.remove_listener(event_name, handler)

playwright/wait_helper.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
class WaitHelper:
2424
def __init__(self) -> None:
2525
self._failures: List[asyncio.Future] = []
26+
self._loop = asyncio.get_event_loop()
2627

2728
def reject_on_event(
2829
self,
@@ -37,15 +38,15 @@ def reject_on_timeout(self, timeout: int, message: str) -> None:
3738
if timeout == 0:
3839
return
3940
self.reject_on(
40-
asyncio.create_task(asyncio.sleep(timeout / 1000)), TimeoutError(message)
41+
self._loop.create_task(asyncio.sleep(timeout / 1000)), TimeoutError(message)
4142
)
4243

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

48-
result = asyncio.create_task(future_wrapper())
49+
result = self._loop.create_task(future_wrapper())
4950
result.add_done_callback(lambda f: future.cancel())
5051
self._failures.append(result)
5152

scripts/generate_sync_api.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ def generate(t: Any) -> None:
200200
prefix = " return " + prefix + f"self._sync(self._async_obj.{name}("
201201
suffix = "))" + suffix
202202
print(f"{prefix}{arguments(value, len(prefix))}{suffix}")
203+
print(f"mapping.register({short_name(t)}Async, {short_name(t)})")
203204

204205

205206
def main() -> None:
@@ -222,7 +223,7 @@ def main() -> None:
222223
223224
import typing
224225
import sys
225-
from playwright.sync_base import SyncBase
226+
from playwright.sync_base import SyncBase, mapping
226227
227228
if sys.version_info >= (3, 8): # pragma: no cover
228229
from typing import Literal

0 commit comments

Comments
 (0)