_sync_base.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. # Copyright (c) Microsoft Corporation.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. import asyncio
  15. import inspect
  16. import traceback
  17. from contextlib import AbstractContextManager
  18. from types import TracebackType
  19. from typing import (
  20. Any,
  21. Callable,
  22. Coroutine,
  23. Generator,
  24. Generic,
  25. Optional,
  26. Type,
  27. TypeVar,
  28. Union,
  29. cast,
  30. )
  31. import greenlet
  32. from playwright._impl._helper import Error
  33. from playwright._impl._impl_to_api_mapping import ImplToApiMapping, ImplWrapper
  34. mapping = ImplToApiMapping()
  35. T = TypeVar("T")
  36. Self = TypeVar("Self", bound="SyncContextManager")
  37. class EventInfo(Generic[T]):
  38. def __init__(self, sync_base: "SyncBase", future: "asyncio.Future[T]") -> None:
  39. self._sync_base = sync_base
  40. self._future = future
  41. g_self = greenlet.getcurrent()
  42. self._future.add_done_callback(lambda _: g_self.switch())
  43. @property
  44. def value(self) -> T:
  45. while not self._future.done():
  46. self._sync_base._dispatcher_fiber.switch()
  47. asyncio._set_running_loop(self._sync_base._loop)
  48. exception = self._future.exception()
  49. if exception:
  50. raise exception
  51. return cast(T, mapping.from_maybe_impl(self._future.result()))
  52. def _cancel(self) -> None:
  53. self._future.cancel()
  54. def is_done(self) -> bool:
  55. return self._future.done()
  56. class EventContextManager(Generic[T], AbstractContextManager):
  57. def __init__(self, sync_base: "SyncBase", future: "asyncio.Future[T]") -> None:
  58. self._event = EventInfo[T](sync_base, future)
  59. def __enter__(self) -> EventInfo[T]:
  60. return self._event
  61. def __exit__(
  62. self,
  63. exc_type: Optional[Type[BaseException]],
  64. exc_val: Optional[BaseException],
  65. exc_tb: Optional[TracebackType],
  66. ) -> None:
  67. if exc_val:
  68. self._event._cancel()
  69. else:
  70. self._event.value
  71. class SyncBase(ImplWrapper):
  72. def __init__(self, impl_obj: Any) -> None:
  73. super().__init__(impl_obj)
  74. self._loop: asyncio.AbstractEventLoop = impl_obj._loop
  75. self._dispatcher_fiber = impl_obj._dispatcher_fiber
  76. def __str__(self) -> str:
  77. return self._impl_obj.__str__()
  78. def _sync(
  79. self,
  80. coro: Union[Coroutine[Any, Any, Any], Generator[Any, Any, Any]],
  81. ) -> Any:
  82. __tracebackhide__ = True
  83. if self._loop.is_closed():
  84. coro.close()
  85. raise Error("Event loop is closed! Is Playwright already stopped?")
  86. g_self = greenlet.getcurrent()
  87. task: asyncio.tasks.Task[Any] = self._loop.create_task(coro)
  88. setattr(task, "__pw_stack__", inspect.stack(0))
  89. setattr(task, "__pw_stack_trace__", traceback.extract_stack(limit=10))
  90. task.add_done_callback(lambda _: g_self.switch())
  91. while not task.done():
  92. self._dispatcher_fiber.switch()
  93. asyncio._set_running_loop(self._loop)
  94. return task.result()
  95. def _wrap_handler(
  96. self, handler: Union[Callable[..., Any], Any]
  97. ) -> Callable[..., None]:
  98. if callable(handler):
  99. return mapping.wrap_handler(handler)
  100. return handler
  101. def on(self, event: Any, f: Any) -> None:
  102. """Registers the function ``f`` to the event name ``event``."""
  103. self._impl_obj.on(event, self._wrap_handler(f))
  104. def once(self, event: Any, f: Any) -> None:
  105. """The same as ``self.on``, except that the listener is automatically
  106. removed after being called.
  107. """
  108. self._impl_obj.once(event, self._wrap_handler(f))
  109. def remove_listener(self, event: Any, f: Any) -> None:
  110. """Removes the function ``f`` from ``event``."""
  111. self._impl_obj.remove_listener(event, self._wrap_handler(f))
  112. class SyncContextManager(SyncBase):
  113. def __enter__(self: Self) -> Self:
  114. return self
  115. def __exit__(
  116. self,
  117. exc_type: Optional[Type[BaseException]],
  118. exc_val: Optional[BaseException],
  119. _traceback: Optional[TracebackType],
  120. ) -> None:
  121. self.close()
  122. def close(self) -> None: ...