Skip to content

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

Merged
merged 11 commits into from
Apr 24, 2021

Conversation

mxschmitt
Copy link
Member

Closes #529

Quite an early implementation, need to think about the last skipped test.

@mxschmitt mxschmitt force-pushed the feature/browser-type-connect branch 2 times, most recently from d2cd2df to e0c873d Compare April 21, 2021 08:19
@mxschmitt mxschmitt force-pushed the feature/browser-type-connect branch from e0c873d to d212d33 Compare April 21, 2021 08:57
@mxschmitt mxschmitt force-pushed the feature/browser-type-connect branch from d212d33 to 7895112 Compare April 21, 2021 09:11
@mxschmitt mxschmitt force-pushed the feature/browser-type-connect branch 5 times, most recently from 5cb2cd3 to bd6820d Compare April 21, 2021 10:15
@microsoft microsoft deleted a comment from mxschmitt Apr 21, 2021
@mxschmitt mxschmitt force-pushed the feature/browser-type-connect branch 3 times, most recently from ad3eebb to d779566 Compare April 21, 2021 13:04
@mxschmitt mxschmitt marked this pull request as ready for review April 21, 2021 13:53
@mxschmitt mxschmitt force-pushed the feature/browser-type-connect branch from 9322212 to 8b5a21f Compare April 21, 2021 15:10
@kumaraditya303
Copy link
Contributor

I was able to fix the last skipped test test_browser_type_connect_should_reject_navigation_when_browser_closes with this code, what it does essentially is runs the greenlet nested with other greenlets hence it does not block, you can try the code given below also it is passing the tests locally to me:

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

@mxschmitt mxschmitt force-pushed the feature/browser-type-connect branch from 02bb278 to 4b6490b Compare April 22, 2021 09:38
Copy link
Contributor

@kumaraditya303 kumaraditya303 left a 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

@mxschmitt
Copy link
Member Author

mxschmitt commented Apr 22, 2021

Let me summarize the test failures:

  • test_wait_for_url_should_work -> not related to this PR, failed before needs to be checked some day
  • test_browser_type_connect_should_reject_navigation_when_browser_closes (all browsers which happens sometimes, and sync and async)-> flaky, can't repro locally -> fixed

So its only the second one which is relevant but causes 3 bots to fail. Will take a more detailed look now.

@mxschmitt mxschmitt force-pushed the feature/browser-type-connect branch from 927f992 to a7847ed Compare April 22, 2021 16:05
@@ -145,6 +146,10 @@ async def new_page(
return page

async def close(self) -> None:
if self._is_connected_over_websocket:
Copy link
Member

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.

Copy link
Member Author

Choose a reason for hiding this comment

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

@@ -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.
Copy link
Member

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.

Copy link
Member Author

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.

Copy link
Member

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())
Copy link
Member

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?

Copy link
Member Author

@mxschmitt mxschmitt Apr 22, 2021

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.

Copy link
Member Author

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
Copy link
Member

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'.

Copy link
Member Author

Choose a reason for hiding this comment

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

good catch, added it.

@mxschmitt mxschmitt force-pushed the feature/browser-type-connect branch from 79b8d72 to ae908a3 Compare April 23, 2021 12:55
@mxschmitt mxschmitt force-pushed the feature/browser-type-connect branch from d4adea9 to 3349e4d Compare April 23, 2021 15:45
@@ -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
Copy link
Member

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.

Copy link
Member Author

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
Copy link
Member

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)
Copy link
Member

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.

@mxschmitt mxschmitt merged commit 9d79662 into microsoft:master Apr 24, 2021
@x0day
Copy link
Contributor

x0day commented Apr 25, 2021

I check the code from master(9d79662), but BrowserType.connect not handle the ConnectionRefusedError ?

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())

@mxschmitt
Copy link
Member Author

Good catch, will take a look.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[BUG] Bring browser_type.connect in.
4 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy