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
1 change: 1 addition & 0 deletions playwright/accessibility.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class Accessibility:
def __init__(self, channel: Channel) -> None:
self._channel = channel
self._loop = channel._connection._loop
self._dispatcher_fiber = channel._connection._dispatcher_fiber

async def snapshot(
self, interestingOnly: bool = None, root: ElementHandle = None
Expand Down
26 changes: 12 additions & 14 deletions playwright/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@
import asyncio
import sys
import traceback
from pathlib import Path
from typing import Any, Callable, Dict, Optional, Union

from greenlet import greenlet
from pyee import AsyncIOEventEmitter

from playwright.helper import ParsedMessagePayload, parse_error
from playwright.sync_base import dispatcher_fiber
from playwright.transport import Transport


Expand Down Expand Up @@ -74,6 +74,7 @@ def __init__(
) -> None:
super().__init__(loop=parent._loop)
self._loop: asyncio.AbstractEventLoop = parent._loop
self._dispatcher_fiber: Any = parent._dispatcher_fiber
self._type = type
self._guid = guid
self._connection: Connection = (
Expand Down Expand Up @@ -116,33 +117,30 @@ def __init__(self, connection: "Connection") -> None:

class Connection:
def __init__(
self,
input: asyncio.StreamReader,
output: asyncio.StreamWriter,
object_factory: Any,
loop: asyncio.AbstractEventLoop,
self, dispatcher_fiber: Any, object_factory: Any, driver_executable: Path
) -> None:
self._transport = Transport(input, output, loop)
self._dispatcher_fiber: Any = dispatcher_fiber
self._transport = Transport(driver_executable)
self._transport.on_message = lambda msg: self._dispatch(msg)
self._waiting_for_object: Dict[str, Any] = {}
self._last_id = 0
self._loop = loop
self._objects: Dict[str, ChannelOwner] = {}
self._callbacks: Dict[int, ProtocolCallback] = {}
self._root_object = RootChannelOwner(self)
self._object_factory = object_factory
self._is_sync = False

def run_sync(self) -> None:
async def run_as_sync(self) -> None:
self._is_sync = True
self._transport.run_sync()
await self.run()

def run_async(self) -> None:
self._transport.run_async()
async def run(self) -> None:
self._loop = asyncio.get_running_loop()
self._root_object = RootChannelOwner(self)
await self._transport.run()

def stop_sync(self) -> None:
self._transport.stop()
dispatcher_fiber().switch()
self._dispatcher_fiber.switch()

def stop_async(self) -> None:
self._transport.stop()
Expand Down
1 change: 1 addition & 0 deletions playwright/file_chooser.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def __init__(
) -> None:
self._page = page
self._loop = page._loop
self._dispatcher_fiber = page._dispatcher_fiber
self._element_handle = element_handle
self._is_multiple = is_multiple

Expand Down
3 changes: 3 additions & 0 deletions playwright/input.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class Keyboard:
def __init__(self, channel: Channel) -> None:
self._channel = channel
self._loop = channel._connection._loop
self._dispatcher_fiber = channel._connection._dispatcher_fiber

async def down(self, key: str) -> None:
await self._channel.send("keyboardDown", locals_to_params(locals()))
Expand All @@ -41,6 +42,7 @@ class Mouse:
def __init__(self, channel: Channel) -> None:
self._channel = channel
self._loop = channel._connection._loop
self._dispatcher_fiber = channel._connection._dispatcher_fiber

async def move(self, x: float, y: float, steps: int = None) -> None:
await self._channel.send("mouseMove", locals_to_params(locals()))
Expand Down Expand Up @@ -83,6 +85,7 @@ class Touchscreen:
def __init__(self, channel: Channel) -> None:
self._channel = channel
self._loop = channel._connection._loop
self._dispatcher_fiber = channel._connection._dispatcher_fiber

async def tap(self, x: float, y: float) -> None:
await self._channel.send("touchscreenTap", locals_to_params(locals()))
66 changes: 32 additions & 34 deletions playwright/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
from playwright.path_utils import get_file_dirname
from playwright.playwright import Playwright
from playwright.sync_api import Playwright as SyncPlaywright
from playwright.sync_base import dispatcher_fiber, set_dispatcher_fiber


def compute_driver_executable() -> Path:
Expand All @@ -39,47 +38,43 @@ def compute_driver_executable() -> Path:
return package_path / "driver" / "playwright-cli"


async def run_driver_async() -> Connection:
driver_executable = compute_driver_executable()

proc = await asyncio.create_subprocess_exec(
str(driver_executable),
"run-driver",
stdin=asyncio.subprocess.PIPE,
stdout=asyncio.subprocess.PIPE,
stderr=sys.stderr,
limit=32768,
)
assert proc.stdout
assert proc.stdin
connection = Connection(
proc.stdout, proc.stdin, create_remote_object, asyncio.get_event_loop()
)
return connection


def run_driver() -> Connection:
loop = asyncio.get_event_loop()
if loop.is_running():
raise Error("Can only run one Playwright at a time.")
return loop.run_until_complete(run_driver_async())


class SyncPlaywrightContextManager:
def __init__(self) -> None:
self._connection = run_driver()
self._playwright: SyncPlaywright

def __enter__(self) -> SyncPlaywright:
def greenlet_main() -> None:
loop = None
own_loop = None
try:
loop = asyncio.get_running_loop()
except RuntimeError:
loop = asyncio.new_event_loop()
own_loop = loop

if loop.is_running():
raise Error("Can only run one Playwright at a time.")

loop.run_until_complete(self._connection.run_as_sync())

if own_loop:
loop.run_until_complete(loop.shutdown_asyncgens())
loop.close()

dispatcher_fiber = greenlet(greenlet_main)
self._connection = Connection(
dispatcher_fiber, create_remote_object, compute_driver_executable()
)

g_self = greenlet.getcurrent()

def callback_wrapper(playwright_impl: Playwright) -> None:
self._playwright = SyncPlaywright(playwright_impl)
g_self.switch()

self._connection.call_on_object_with_known_name("Playwright", callback_wrapper)
set_dispatcher_fiber(greenlet(lambda: self._connection.run_sync()))
dispatcher_fiber().switch()

dispatcher_fiber.switch()
playwright = self._playwright
playwright.stop = self.__exit__ # type: ignore
return playwright
Expand All @@ -96,8 +91,12 @@ def __init__(self) -> None:
self._connection: Connection

async def __aenter__(self) -> AsyncPlaywright:
self._connection = await run_driver_async()
self._connection.run_async()
self._connection = Connection(
None, create_remote_object, compute_driver_executable()
)
loop = asyncio.get_running_loop()
self._connection._loop = loop
loop.create_task(self._connection.run())
playwright = AsyncPlaywright(
await self._connection.wait_for_object_with_known_name("Playwright")
)
Expand All @@ -113,8 +112,7 @@ async def __aexit__(self, *args: Any) -> None:

if sys.platform == "win32":
# Use ProactorEventLoop in 3.7, which is default in 3.8
loop = asyncio.ProactorEventLoop()
asyncio.set_event_loop(loop)
asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())


def main() -> None:
Expand Down
30 changes: 15 additions & 15 deletions playwright/sync_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@ def expect_event(
page.setDefaultTimeout(timeout) methods.
"""
return EventContextManager(
self._loop, self._impl_obj.waitForEvent(event, predicate, timeout)
self, self._impl_obj.waitForEvent(event, predicate, timeout)
)

def isClosed(self) -> bool:
Expand Down Expand Up @@ -3198,7 +3198,7 @@ def expect_load_state(
page.setDefaultTimeout(timeout) methods.
"""
return EventContextManager(
self._loop, self._impl_obj.waitForLoadState(state, timeout)
self, self._impl_obj.waitForLoadState(state, timeout)
)

def expect_navigation(
Expand Down Expand Up @@ -3227,7 +3227,7 @@ def expect_navigation(
page.setDefaultTimeout(timeout) methods.
"""
return EventContextManager(
self._loop, self._impl_obj.waitForNavigation(url, waitUntil, timeout)
self, self._impl_obj.waitForNavigation(url, waitUntil, timeout)
)


Expand Down Expand Up @@ -5561,7 +5561,7 @@ def expect_event(
page.setDefaultTimeout(timeout) methods.
"""
return EventContextManager(
self._loop, self._impl_obj.waitForEvent(event, predicate, timeout)
self, self._impl_obj.waitForEvent(event, predicate, timeout)
)

def expect_console_message(
Expand Down Expand Up @@ -5590,7 +5590,7 @@ def expect_console_message(
"""
event = "console"
return EventContextManager(
self._loop, self._impl_obj.waitForEvent(event, predicate, timeout)
self, self._impl_obj.waitForEvent(event, predicate, timeout)
)

def expect_download(
Expand Down Expand Up @@ -5619,7 +5619,7 @@ def expect_download(
"""
event = "download"
return EventContextManager(
self._loop, self._impl_obj.waitForEvent(event, predicate, timeout)
self, self._impl_obj.waitForEvent(event, predicate, timeout)
)

def expect_file_chooser(
Expand Down Expand Up @@ -5648,7 +5648,7 @@ def expect_file_chooser(
"""
event = "filechooser"
return EventContextManager(
self._loop, self._impl_obj.waitForEvent(event, predicate, timeout)
self, self._impl_obj.waitForEvent(event, predicate, timeout)
)

def expect_load_state(
Expand Down Expand Up @@ -5676,7 +5676,7 @@ def expect_load_state(
page.setDefaultTimeout(timeout) methods.
"""
return EventContextManager(
self._loop, self._impl_obj.waitForLoadState(state, timeout)
self, self._impl_obj.waitForLoadState(state, timeout)
)

def expect_navigation(
Expand Down Expand Up @@ -5705,7 +5705,7 @@ def expect_navigation(
page.setDefaultTimeout(timeout) methods.
"""
return EventContextManager(
self._loop, self._impl_obj.waitForNavigation(url, waitUntil, timeout)
self, self._impl_obj.waitForNavigation(url, waitUntil, timeout)
)

def expect_popup(
Expand Down Expand Up @@ -5734,7 +5734,7 @@ def expect_popup(
"""
event = "popup"
return EventContextManager(
self._loop, self._impl_obj.waitForEvent(event, predicate, timeout)
self, self._impl_obj.waitForEvent(event, predicate, timeout)
)

def expect_request(
Expand Down Expand Up @@ -5763,7 +5763,7 @@ def expect_request(
page.setDefaultTimeout(timeout) methods.
"""
return EventContextManager(
self._loop, self._impl_obj.waitForRequest(url, predicate, timeout)
self, self._impl_obj.waitForRequest(url, predicate, timeout)
)

def expect_response(
Expand Down Expand Up @@ -5792,7 +5792,7 @@ def expect_response(
page.setDefaultTimeout(timeout) methods.
"""
return EventContextManager(
self._loop, self._impl_obj.waitForResponse(url, predicate, timeout)
self, self._impl_obj.waitForResponse(url, predicate, timeout)
)

def expect_worker(
Expand Down Expand Up @@ -5821,7 +5821,7 @@ def expect_worker(
"""
event = "worker"
return EventContextManager(
self._loop, self._impl_obj.waitForEvent(event, predicate, timeout)
self, self._impl_obj.waitForEvent(event, predicate, timeout)
)


Expand Down Expand Up @@ -6241,7 +6241,7 @@ def expect_event(
page.setDefaultTimeout(timeout) methods.
"""
return EventContextManager(
self._loop, self._impl_obj.waitForEvent(event, predicate, timeout)
self, self._impl_obj.waitForEvent(event, predicate, timeout)
)

def expect_page(
Expand Down Expand Up @@ -6270,7 +6270,7 @@ def expect_page(
"""
event = "page"
return EventContextManager(
self._loop, self._impl_obj.waitForEvent(event, predicate, timeout)
self, self._impl_obj.waitForEvent(event, predicate, timeout)
)


Expand Down
Loading