-
Notifications
You must be signed in to change notification settings - Fork 1k
feat: added BrowserType.connect #630
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: added BrowserType.connect #630
Conversation
d2cd2df
to
e0c873d
Compare
e0c873d
to
d212d33
Compare
d212d33
to
7895112
Compare
5cb2cd3
to
bd6820d
Compare
ad3eebb
to
d779566
Compare
9322212
to
8b5a21f
Compare
I was able to fix the last skipped test def test_browser_type_connect_should_reject_navigation_when_browser_closes(
server: Server, browser_type: BrowserType, launch_server
):
import greenlet
remote_server = launch_server()
browser = browser_type.connect(remote_server.ws_endpoint)
page = browser.new_page()
server.set_route("/one-style.css", lambda r: None)
g = greenlet.greenlet(browser.close)
page.on("request", lambda: g.switch())
with pytest.raises(Error) as exc_info:
page.goto(server.PREFIX + "/one-style.html")
assert "Playwright connection closed" in exc_info.value.message |
02bb278
to
4b6490b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The failing tests seems flaky
Let me summarize the test failures:
So its only the second one which is relevant but causes 3 bots to fail. Will take a more detailed look now. |
927f992
to
a7847ed
Compare
playwright/_impl/_browser.py
Outdated
@@ -145,6 +146,10 @@ async def new_page( | |||
return page | |||
|
|||
async def close(self) -> None: | |||
if self._is_connected_over_websocket: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you want to move it to under _is_closed_or_closing = True? Also, why is logic different from the one in TS? Does TS close the browser on browser.close?
To me, the ideal workflow would be: close
terminates the underlying connection, connection termination calls _on_close everywhere. That way, terminating connection due to networking issues would result in the same proper cleanup.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
move it below it.
TS does it via an event, see here in another place: https://github.com/microsoft/playwright/blob/fff1f3d45c243bec65c7e6c43e6fe5e36607dc59/src/client/browserType.ts#L163-L179
playwright/_impl/_browser.py
Outdated
@@ -154,6 +159,14 @@ async def close(self) -> None: | |||
if not is_safe_close_error(e): | |||
raise e | |||
|
|||
def _notify_remote_closed(self) -> None: | |||
# Emulate all pages, contexts and the browser closing upon disconnect. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I.e I would expect this to be called on web socket disconnect somehow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the naming came originally from Java, but open to change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I looked at the TS version, looks like you don't need any special code here, just close the browser. But our server seems to be missing the connection termination upon close for some reason.
|
||
def stop(self) -> None: | ||
self._stopped = True | ||
self._loop.create_task(self._connection.close()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Declare self._connection in init, did our mypy break?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes its broken currently (only the impl folder - most important one...), will follow up to fix it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can automatically infer that I set it in WebSocketTransport.run()
But yes your assumption was also correct that mypy was broken. Fixed in #643
pre_launched_browser = playwright._initializer.get("preLaunchedBrowser") | ||
assert pre_launched_browser | ||
browser = cast(Browser, from_channel(pre_launched_browser)) | ||
browser._is_remote = True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that you are adding it, we need to introduce the code that depends on it. That would be our artifacts, namely 'download' and 'video'.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch, added it.
79b8d72
to
ae908a3
Compare
d4adea9
to
3349e4d
Compare
@@ -50,6 +50,7 @@ def __init__( | |||
self._is_connected = True | |||
self._is_closed_or_closing = False | |||
self._is_remote = False | |||
self._is_connected_over_websocket = False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like you can use is_remote here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
remote is also used for connect_over_cdp, we only want to close the underlying connection when connect is used.
assert pre_launched_browser | ||
browser = cast(Browser, from_channel(pre_launched_browser)) | ||
browser._is_remote = True | ||
browser._is_connected_over_websocket = True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it looks the same as is_remote...
browser._is_remote = True | ||
browser._is_connected_over_websocket = True | ||
|
||
transport.once("close", browser._on_close) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this looks like a layering violation, should go transport -> connection -> browser. Not a big deal though.
I check the code from master(9d79662), but import asyncio
from playwright.async_api import async_playwright
async def main():
async with async_playwright() as p:
browser = await p.chromium.connect("ws://127.0.0.1:12345/9c2251a08a8f1092b72af9b0f9d035bf")
print(browser)
page = await browser.new_page()
print(page)
await page.goto('http://whatsmyuseragent.org/')
await browser.close()
asyncio.run(main()) |
Good catch, will take a look. |
Closes #529
Quite an early implementation, need to think about the last skipped test.