diff --git a/playwright/_impl/_async_base.py b/playwright/_impl/_async_base.py index 4d678f668..8c15f6944 100644 --- a/playwright/_impl/_async_base.py +++ b/playwright/_impl/_async_base.py @@ -15,7 +15,7 @@ import asyncio import traceback from types import TracebackType -from typing import Any, Awaitable, Callable, Generic, Type, TypeVar, Union +from typing import Any, Awaitable, Callable, Generic, Type, TypeVar from playwright._impl._impl_to_api_mapping import ImplToApiMapping, ImplWrapper @@ -73,19 +73,17 @@ def _wrap_handler(self, handler: Any) -> Callable[..., None]: return mapping.wrap_handler(handler) return handler - def on(self, event: str, f: Callable[..., Union[Awaitable[None], None]]) -> None: + def on(self, event: Any, f: Any) -> None: """Registers the function ``f`` to the event name ``event``.""" self._impl_obj.on(event, self._wrap_handler(f)) - def once(self, event: str, f: Callable[..., Union[Awaitable[None], None]]) -> None: + def once(self, event: Any, f: Any) -> None: """The same as ``self.on``, except that the listener is automatically removed after being called. """ self._impl_obj.once(event, self._wrap_handler(f)) - def remove_listener( - self, event: str, f: Callable[..., Union[Awaitable[None], None]] - ) -> None: + def remove_listener(self, event: Any, f: Any) -> None: """Removes the function ``f`` from ``event``.""" self._impl_obj.remove_listener(event, self._wrap_handler(f)) diff --git a/playwright/_impl/_sync_base.py b/playwright/_impl/_sync_base.py index 4e0272420..93d0784f1 100644 --- a/playwright/_impl/_sync_base.py +++ b/playwright/_impl/_sync_base.py @@ -115,17 +115,17 @@ def _wrap_handler(self, handler: Any) -> Callable[..., None]: return mapping.wrap_handler(handler) return handler - def on(self, event: str, f: Callable[..., None]) -> None: + def on(self, event: Any, f: Any) -> None: """Registers the function ``f`` to the event name ``event``.""" self._impl_obj.on(event, self._wrap_handler(f)) - def once(self, event: str, f: Callable[..., None]) -> None: + def once(self, event: Any, f: Any) -> None: """The same as ``self.on``, except that the listener is automatically removed after being called. """ self._impl_obj.once(event, self._wrap_handler(f)) - def remove_listener(self, event: str, f: Callable[..., None]) -> None: + def remove_listener(self, event: Any, f: Any) -> None: """Removes the function ``f`` from ``event``.""" self._impl_obj.remove_listener(event, self._wrap_handler(f)) diff --git a/playwright/async_api/_generated.py b/playwright/async_api/_generated.py index 2d45f1e0a..20097f446 100644 --- a/playwright/async_api/_generated.py +++ b/playwright/async_api/_generated.py @@ -40,6 +40,7 @@ StorageState, ViewportSize, ) +from playwright._impl._api_types import Error from playwright._impl._async_base import ( AsyncBase, AsyncContextManager, @@ -693,6 +694,100 @@ async def handle(route, request): class WebSocket(AsyncBase): + @typing.overload + def on( + self, + event: Literal["close"], + f: typing.Callable[["WebSocket"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Fired when the websocket closes.""" + + @typing.overload + def on( + self, + event: Literal["framereceived"], + f: typing.Callable[ + ["typing.Dict"], "typing.Union[typing.Awaitable[None], None]" + ], + ) -> None: + """ + Fired when the websocket receives a frame.""" + + @typing.overload + def on( + self, + event: Literal["framesent"], + f: typing.Callable[ + ["typing.Dict"], "typing.Union[typing.Awaitable[None], None]" + ], + ) -> None: + """ + Fired when the websocket sends a frame.""" + + @typing.overload + def on( + self, + event: Literal["socketerror"], + f: typing.Callable[["str"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Fired when the websocket has an error.""" + + def on( + self, + event: str, + f: typing.Callable[..., typing.Union[typing.Awaitable[None], None]], + ) -> None: + return super().on(event=event, f=f) + + @typing.overload + def once( + self, + event: Literal["close"], + f: typing.Callable[["WebSocket"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Fired when the websocket closes.""" + + @typing.overload + def once( + self, + event: Literal["framereceived"], + f: typing.Callable[ + ["typing.Dict"], "typing.Union[typing.Awaitable[None], None]" + ], + ) -> None: + """ + Fired when the websocket receives a frame.""" + + @typing.overload + def once( + self, + event: Literal["framesent"], + f: typing.Callable[ + ["typing.Dict"], "typing.Union[typing.Awaitable[None], None]" + ], + ) -> None: + """ + Fired when the websocket sends a frame.""" + + @typing.overload + def once( + self, + event: Literal["socketerror"], + f: typing.Callable[["str"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Fired when the websocket has an error.""" + + def once( + self, + event: str, + f: typing.Callable[..., typing.Union[typing.Awaitable[None], None]], + ) -> None: + return super().once(event=event, f=f) + @property def url(self) -> str: """WebSocket.url @@ -5001,6 +5096,24 @@ async def set_checked( class Worker(AsyncBase): + def on( + self, + event: Literal["close"], + f: typing.Callable[["Worker"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when this dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is terminated.""" + return super().on(event=event, f=f) + + def once( + self, + event: Literal["close"], + f: typing.Callable[["Worker"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when this dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is terminated.""" + return super().once(event=event, f=f) + @property def url(self) -> str: """Worker.url @@ -5446,6 +5559,504 @@ async def delete(self) -> NoneType: class Page(AsyncContextManager): + @typing.overload + def on( + self, + event: Literal["close"], + f: typing.Callable[["Page"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when the page closes.""" + + @typing.overload + def on( + self, + event: Literal["console"], + f: typing.Callable[ + ["ConsoleMessage"], "typing.Union[typing.Awaitable[None], None]" + ], + ) -> None: + """ + Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. Also + emitted if the page throws an error or a warning. + + The arguments passed into `console.log` appear as arguments on the event handler. + + An example of handling `console` event: + + ```py + async def print_args(msg): + for arg in msg.args: + print(await arg.json_value()) + + page.on(\"console\", print_args) + await page.evaluate(\"console.log('hello', 5, {foo: 'bar'})\") + ```""" + + @typing.overload + def on( + self, + event: Literal["crash"], + f: typing.Callable[["Page"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when the page crashes. Browser pages might crash if they try to allocate too much memory. When the page crashes, + ongoing and subsequent operations will throw. + + The most common way to deal with crashes is to catch an exception: + + ```py + try: + # crash might happen during a click. + await page.click(\"button\") + # or while waiting for an event. + await page.wait_for_event(\"popup\") + except Error as e: + # when the page crashes, exception message contains \"crash\". + ```""" + + @typing.overload + def on( + self, + event: Literal["dialog"], + f: typing.Callable[["Dialog"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must** + either `dialog.accept()` or `dialog.dismiss()` the dialog - otherwise the page will + [freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking) waiting for the dialog, and + actions like click will never finish. + + > NOTE: When no `page.on('dialog')` listeners are present, all dialogs are automatically dismissed.""" + + @typing.overload + def on( + self, + event: Literal["domcontentloaded"], + f: typing.Callable[["Page"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when the JavaScript [`DOMContentLoaded`](https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded) + event is dispatched.""" + + @typing.overload + def on( + self, + event: Literal["download"], + f: typing.Callable[["Download"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when attachment download started. User can access basic file operations on downloaded content via the passed + `Download` instance. + + > NOTE: Browser context **must** be created with the `acceptDownloads` set to `true` when user needs access to the + downloaded content. If `acceptDownloads` is not set, download events are emitted, but the actual download is not + performed and user has no access to the downloaded files.""" + + @typing.overload + def on( + self, + event: Literal["filechooser"], + f: typing.Callable[ + ["FileChooser"], "typing.Union[typing.Awaitable[None], None]" + ], + ) -> None: + """ + Emitted when a file chooser is supposed to appear, such as after clicking the ``. Playwright can + respond to it via setting the input files using `file_chooser.set_files()` that can be uploaded after that. + + ```py + page.on(\"filechooser\", lambda file_chooser: file_chooser.set_files(\"/tmp/myfile.pdf\")) + ```""" + + @typing.overload + def on( + self, + event: Literal["frameattached"], + f: typing.Callable[["Frame"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when a frame is attached.""" + + @typing.overload + def on( + self, + event: Literal["framedetached"], + f: typing.Callable[["Frame"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when a frame is detached.""" + + @typing.overload + def on( + self, + event: Literal["framenavigated"], + f: typing.Callable[["Frame"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when a frame is navigated to a new url.""" + + @typing.overload + def on( + self, + event: Literal["load"], + f: typing.Callable[["Page"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when the JavaScript [`load`](https://developer.mozilla.org/en-US/docs/Web/Events/load) event is dispatched.""" + + @typing.overload + def on( + self, + event: Literal["pageerror"], + f: typing.Callable[["Error"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when an uncaught exception happens within the page.""" + + @typing.overload + def on( + self, + event: Literal["popup"], + f: typing.Callable[["Page"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when the page opens a new tab or window. This event is emitted in addition to the + `browser_context.on('page')`, but only for popups relevant to this page. + + The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a + popup with `window.open('http://example.com')`, this event will fire when the network request to \"http://example.com\" is + done and its response has started loading in the popup. + + ```py + async with page.expect_event(\"popup\") as page_info: + page.evaluate(\"window.open('https://example.com')\") + popup = await page_info.value + print(await popup.evaluate(\"location.href\")) + ``` + + > NOTE: Use `page.wait_for_load_state()` to wait until the page gets to a particular state (you should not need it + in most cases).""" + + @typing.overload + def on( + self, + event: Literal["request"], + f: typing.Callable[["Request"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when a page issues a request. The [request] object is read-only. In order to intercept and mutate requests, see + `page.route()` or `browser_context.route()`.""" + + @typing.overload + def on( + self, + event: Literal["requestfailed"], + f: typing.Callable[["Request"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when a request fails, for example by timing out. + + > NOTE: HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request will + complete with `page.on('request_finished')` event and not with `page.on('request_failed')`. A request will only be + considered failed when the client cannot get an HTTP response from the server, e.g. due to network error + net::ERR_FAILED.""" + + @typing.overload + def on( + self, + event: Literal["requestfinished"], + f: typing.Callable[["Request"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when a request finishes successfully after downloading the response body. For a successful response, the + sequence of events is `request`, `response` and `requestfinished`.""" + + @typing.overload + def on( + self, + event: Literal["response"], + f: typing.Callable[["Response"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when [response] status and headers are received for a request. For a successful response, the sequence of events + is `request`, `response` and `requestfinished`.""" + + @typing.overload + def on( + self, + event: Literal["websocket"], + f: typing.Callable[["WebSocket"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when `WebSocket` request is sent.""" + + @typing.overload + def on( + self, + event: Literal["worker"], + f: typing.Callable[["Worker"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when a dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is spawned by the + page.""" + + def on( + self, + event: str, + f: typing.Callable[..., typing.Union[typing.Awaitable[None], None]], + ) -> None: + return super().on(event=event, f=f) + + @typing.overload + def once( + self, + event: Literal["close"], + f: typing.Callable[["Page"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when the page closes.""" + + @typing.overload + def once( + self, + event: Literal["console"], + f: typing.Callable[ + ["ConsoleMessage"], "typing.Union[typing.Awaitable[None], None]" + ], + ) -> None: + """ + Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. Also + emitted if the page throws an error or a warning. + + The arguments passed into `console.log` appear as arguments on the event handler. + + An example of handling `console` event: + + ```py + async def print_args(msg): + for arg in msg.args: + print(await arg.json_value()) + + page.on(\"console\", print_args) + await page.evaluate(\"console.log('hello', 5, {foo: 'bar'})\") + ```""" + + @typing.overload + def once( + self, + event: Literal["crash"], + f: typing.Callable[["Page"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when the page crashes. Browser pages might crash if they try to allocate too much memory. When the page crashes, + ongoing and subsequent operations will throw. + + The most common way to deal with crashes is to catch an exception: + + ```py + try: + # crash might happen during a click. + await page.click(\"button\") + # or while waiting for an event. + await page.wait_for_event(\"popup\") + except Error as e: + # when the page crashes, exception message contains \"crash\". + ```""" + + @typing.overload + def once( + self, + event: Literal["dialog"], + f: typing.Callable[["Dialog"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must** + either `dialog.accept()` or `dialog.dismiss()` the dialog - otherwise the page will + [freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking) waiting for the dialog, and + actions like click will never finish. + + > NOTE: When no `page.on('dialog')` listeners are present, all dialogs are automatically dismissed.""" + + @typing.overload + def once( + self, + event: Literal["domcontentloaded"], + f: typing.Callable[["Page"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when the JavaScript [`DOMContentLoaded`](https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded) + event is dispatched.""" + + @typing.overload + def once( + self, + event: Literal["download"], + f: typing.Callable[["Download"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when attachment download started. User can access basic file operations on downloaded content via the passed + `Download` instance. + + > NOTE: Browser context **must** be created with the `acceptDownloads` set to `true` when user needs access to the + downloaded content. If `acceptDownloads` is not set, download events are emitted, but the actual download is not + performed and user has no access to the downloaded files.""" + + @typing.overload + def once( + self, + event: Literal["filechooser"], + f: typing.Callable[ + ["FileChooser"], "typing.Union[typing.Awaitable[None], None]" + ], + ) -> None: + """ + Emitted when a file chooser is supposed to appear, such as after clicking the ``. Playwright can + respond to it via setting the input files using `file_chooser.set_files()` that can be uploaded after that. + + ```py + page.on(\"filechooser\", lambda file_chooser: file_chooser.set_files(\"/tmp/myfile.pdf\")) + ```""" + + @typing.overload + def once( + self, + event: Literal["frameattached"], + f: typing.Callable[["Frame"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when a frame is attached.""" + + @typing.overload + def once( + self, + event: Literal["framedetached"], + f: typing.Callable[["Frame"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when a frame is detached.""" + + @typing.overload + def once( + self, + event: Literal["framenavigated"], + f: typing.Callable[["Frame"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when a frame is navigated to a new url.""" + + @typing.overload + def once( + self, + event: Literal["load"], + f: typing.Callable[["Page"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when the JavaScript [`load`](https://developer.mozilla.org/en-US/docs/Web/Events/load) event is dispatched.""" + + @typing.overload + def once( + self, + event: Literal["pageerror"], + f: typing.Callable[["Error"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when an uncaught exception happens within the page.""" + + @typing.overload + def once( + self, + event: Literal["popup"], + f: typing.Callable[["Page"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when the page opens a new tab or window. This event is emitted in addition to the + `browser_context.on('page')`, but only for popups relevant to this page. + + The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a + popup with `window.open('http://example.com')`, this event will fire when the network request to \"http://example.com\" is + done and its response has started loading in the popup. + + ```py + async with page.expect_event(\"popup\") as page_info: + page.evaluate(\"window.open('https://example.com')\") + popup = await page_info.value + print(await popup.evaluate(\"location.href\")) + ``` + + > NOTE: Use `page.wait_for_load_state()` to wait until the page gets to a particular state (you should not need it + in most cases).""" + + @typing.overload + def once( + self, + event: Literal["request"], + f: typing.Callable[["Request"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when a page issues a request. The [request] object is read-only. In order to intercept and mutate requests, see + `page.route()` or `browser_context.route()`.""" + + @typing.overload + def once( + self, + event: Literal["requestfailed"], + f: typing.Callable[["Request"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when a request fails, for example by timing out. + + > NOTE: HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request will + complete with `page.on('request_finished')` event and not with `page.on('request_failed')`. A request will only be + considered failed when the client cannot get an HTTP response from the server, e.g. due to network error + net::ERR_FAILED.""" + + @typing.overload + def once( + self, + event: Literal["requestfinished"], + f: typing.Callable[["Request"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when a request finishes successfully after downloading the response body. For a successful response, the + sequence of events is `request`, `response` and `requestfinished`.""" + + @typing.overload + def once( + self, + event: Literal["response"], + f: typing.Callable[["Response"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when [response] status and headers are received for a request. For a successful response, the sequence of events + is `request`, `response` and `requestfinished`.""" + + @typing.overload + def once( + self, + event: Literal["websocket"], + f: typing.Callable[["WebSocket"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when `WebSocket` request is sent.""" + + @typing.overload + def once( + self, + event: Literal["worker"], + f: typing.Callable[["Worker"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when a dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is spawned by the + page.""" + + def once( + self, + event: str, + f: typing.Callable[..., typing.Union[typing.Awaitable[None], None]], + ) -> None: + return super().once(event=event, f=f) + @property def accessibility(self) -> "Accessibility": """Page.accessibility @@ -9015,6 +9626,242 @@ async def set_checked( class BrowserContext(AsyncContextManager): + @typing.overload + def on( + self, + event: Literal["backgroundpage"], + f: typing.Callable[["Page"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + > NOTE: Only works with Chromium browser's persistent context. + + Emitted when new background page is created in the context. + + ```py + background_page = await context.wait_for_event(\"backgroundpage\") + ```""" + + @typing.overload + def on( + self, + event: Literal["close"], + f: typing.Callable[ + ["BrowserContext"], "typing.Union[typing.Awaitable[None], None]" + ], + ) -> None: + """ + Emitted when Browser context gets closed. This might happen because of one of the following: + - Browser context is closed. + - Browser application is closed or crashed. + - The `browser.close()` method was called.""" + + @typing.overload + def on( + self, + event: Literal["page"], + f: typing.Callable[["Page"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + The event is emitted when a new Page is created in the BrowserContext. The page may still be loading. The event will + also fire for popup pages. See also `page.on('popup')` to receive events about popups relevant to a specific page. + + The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a + popup with `window.open('http://example.com')`, this event will fire when the network request to \"http://example.com\" is + done and its response has started loading in the popup. + + ```py + async with context.expect_page() as page_info: + await page.click(\"a[target=_blank]\"), + page = await page_info.value + print(await page.evaluate(\"location.href\")) + ``` + + > NOTE: Use `page.wait_for_load_state()` to wait until the page gets to a particular state (you should not need it + in most cases).""" + + @typing.overload + def on( + self, + event: Literal["request"], + f: typing.Callable[["Request"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when a request is issued from any pages created through this context. The [request] object is read-only. To only + listen for requests from a particular page, use `page.on('request')`. + + In order to intercept and mutate requests, see `browser_context.route()` or `page.route()`.""" + + @typing.overload + def on( + self, + event: Literal["requestfailed"], + f: typing.Callable[["Request"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when a request fails, for example by timing out. To only listen for failed requests from a particular page, use + `page.on('request_failed')`. + + > NOTE: HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request will + complete with `browser_context.on('request_finished')` event and not with `browser_context.on('request_failed')`.""" + + @typing.overload + def on( + self, + event: Literal["requestfinished"], + f: typing.Callable[["Request"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when a request finishes successfully after downloading the response body. For a successful response, the + sequence of events is `request`, `response` and `requestfinished`. To listen for successful requests from a particular + page, use `page.on('request_finished')`.""" + + @typing.overload + def on( + self, + event: Literal["response"], + f: typing.Callable[["Response"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when [response] status and headers are received for a request. For a successful response, the sequence of events + is `request`, `response` and `requestfinished`. To listen for response events from a particular page, use + `page.on('response')`.""" + + @typing.overload + def on( + self, + event: Literal["serviceworker"], + f: typing.Callable[["Worker"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + > NOTE: Service workers are only supported on Chromium-based browsers. + + Emitted when new service worker is created in the context.""" + + def on( + self, + event: str, + f: typing.Callable[..., typing.Union[typing.Awaitable[None], None]], + ) -> None: + return super().on(event=event, f=f) + + @typing.overload + def once( + self, + event: Literal["backgroundpage"], + f: typing.Callable[["Page"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + > NOTE: Only works with Chromium browser's persistent context. + + Emitted when new background page is created in the context. + + ```py + background_page = await context.wait_for_event(\"backgroundpage\") + ```""" + + @typing.overload + def once( + self, + event: Literal["close"], + f: typing.Callable[ + ["BrowserContext"], "typing.Union[typing.Awaitable[None], None]" + ], + ) -> None: + """ + Emitted when Browser context gets closed. This might happen because of one of the following: + - Browser context is closed. + - Browser application is closed or crashed. + - The `browser.close()` method was called.""" + + @typing.overload + def once( + self, + event: Literal["page"], + f: typing.Callable[["Page"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + The event is emitted when a new Page is created in the BrowserContext. The page may still be loading. The event will + also fire for popup pages. See also `page.on('popup')` to receive events about popups relevant to a specific page. + + The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a + popup with `window.open('http://example.com')`, this event will fire when the network request to \"http://example.com\" is + done and its response has started loading in the popup. + + ```py + async with context.expect_page() as page_info: + await page.click(\"a[target=_blank]\"), + page = await page_info.value + print(await page.evaluate(\"location.href\")) + ``` + + > NOTE: Use `page.wait_for_load_state()` to wait until the page gets to a particular state (you should not need it + in most cases).""" + + @typing.overload + def once( + self, + event: Literal["request"], + f: typing.Callable[["Request"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when a request is issued from any pages created through this context. The [request] object is read-only. To only + listen for requests from a particular page, use `page.on('request')`. + + In order to intercept and mutate requests, see `browser_context.route()` or `page.route()`.""" + + @typing.overload + def once( + self, + event: Literal["requestfailed"], + f: typing.Callable[["Request"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when a request fails, for example by timing out. To only listen for failed requests from a particular page, use + `page.on('request_failed')`. + + > NOTE: HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request will + complete with `browser_context.on('request_finished')` event and not with `browser_context.on('request_failed')`.""" + + @typing.overload + def once( + self, + event: Literal["requestfinished"], + f: typing.Callable[["Request"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when a request finishes successfully after downloading the response body. For a successful response, the + sequence of events is `request`, `response` and `requestfinished`. To listen for successful requests from a particular + page, use `page.on('request_finished')`.""" + + @typing.overload + def once( + self, + event: Literal["response"], + f: typing.Callable[["Response"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when [response] status and headers are received for a request. For a successful response, the sequence of events + is `request`, `response` and `requestfinished`. To listen for response events from a particular page, use + `page.on('response')`.""" + + @typing.overload + def once( + self, + event: Literal["serviceworker"], + f: typing.Callable[["Worker"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + > NOTE: Service workers are only supported on Chromium-based browsers. + + Emitted when new service worker is created in the context.""" + + def once( + self, + event: str, + f: typing.Callable[..., typing.Union[typing.Awaitable[None], None]], + ) -> None: + return super().once(event=event, f=f) + @property def pages(self) -> typing.List["Page"]: """BrowserContext.pages @@ -9815,6 +10662,28 @@ async def detach(self) -> NoneType: class Browser(AsyncContextManager): + def on( + self, + event: Literal["disconnected"], + f: typing.Callable[["Browser"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when Browser gets disconnected from the browser application. This might happen because of one of the following: + - Browser application is closed or crashed. + - The `browser.close()` method was called.""" + return super().on(event=event, f=f) + + def once( + self, + event: Literal["disconnected"], + f: typing.Callable[["Browser"], "typing.Union[typing.Awaitable[None], None]"], + ) -> None: + """ + Emitted when Browser gets disconnected from the browser application. This might happen because of one of the following: + - Browser application is closed or crashed. + - The `browser.close()` method was called.""" + return super().once(event=event, f=f) + @property def contexts(self) -> typing.List["BrowserContext"]: """Browser.contexts diff --git a/playwright/sync_api/_generated.py b/playwright/sync_api/_generated.py index 9c51ef92f..f77db5ef6 100644 --- a/playwright/sync_api/_generated.py +++ b/playwright/sync_api/_generated.py @@ -40,6 +40,7 @@ StorageState, ViewportSize, ) +from playwright._impl._api_types import Error from playwright._impl._browser import Browser as BrowserImpl from playwright._impl._browser_context import BrowserContext as BrowserContextImpl from playwright._impl._browser_type import BrowserType as BrowserTypeImpl @@ -689,6 +690,72 @@ def handle(route, request): class WebSocket(SyncBase): + @typing.overload + def on( + self, event: Literal["close"], f: typing.Callable[["WebSocket"], "None"] + ) -> None: + """ + Fired when the websocket closes.""" + + @typing.overload + def on( + self, + event: Literal["framereceived"], + f: typing.Callable[["typing.Dict"], "None"], + ) -> None: + """ + Fired when the websocket receives a frame.""" + + @typing.overload + def on( + self, event: Literal["framesent"], f: typing.Callable[["typing.Dict"], "None"] + ) -> None: + """ + Fired when the websocket sends a frame.""" + + @typing.overload + def on( + self, event: Literal["socketerror"], f: typing.Callable[["str"], "None"] + ) -> None: + """ + Fired when the websocket has an error.""" + + def on(self, event: str, f: typing.Callable[..., None]) -> None: + return super().on(event=event, f=f) + + @typing.overload + def once( + self, event: Literal["close"], f: typing.Callable[["WebSocket"], "None"] + ) -> None: + """ + Fired when the websocket closes.""" + + @typing.overload + def once( + self, + event: Literal["framereceived"], + f: typing.Callable[["typing.Dict"], "None"], + ) -> None: + """ + Fired when the websocket receives a frame.""" + + @typing.overload + def once( + self, event: Literal["framesent"], f: typing.Callable[["typing.Dict"], "None"] + ) -> None: + """ + Fired when the websocket sends a frame.""" + + @typing.overload + def once( + self, event: Literal["socketerror"], f: typing.Callable[["str"], "None"] + ) -> None: + """ + Fired when the websocket has an error.""" + + def once(self, event: str, f: typing.Callable[..., None]) -> None: + return super().once(event=event, f=f) + @property def url(self) -> str: """WebSocket.url @@ -4974,6 +5041,20 @@ def set_checked( class Worker(SyncBase): + def on( + self, event: Literal["close"], f: typing.Callable[["Worker"], "None"] + ) -> None: + """ + Emitted when this dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is terminated.""" + return super().on(event=event, f=f) + + def once( + self, event: Literal["close"], f: typing.Callable[["Worker"], "None"] + ) -> None: + """ + Emitted when this dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is terminated.""" + return super().once(event=event, f=f) + @property def url(self) -> str: """Worker.url @@ -5409,6 +5490,404 @@ def delete(self) -> NoneType: class Page(SyncContextManager): + @typing.overload + def on(self, event: Literal["close"], f: typing.Callable[["Page"], "None"]) -> None: + """ + Emitted when the page closes.""" + + @typing.overload + def on( + self, event: Literal["console"], f: typing.Callable[["ConsoleMessage"], "None"] + ) -> None: + """ + Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. Also + emitted if the page throws an error or a warning. + + The arguments passed into `console.log` appear as arguments on the event handler. + + An example of handling `console` event: + + ```py + def print_args(msg): + for arg in msg.args: + print(arg.json_value()) + + page.on(\"console\", print_args) + page.evaluate(\"console.log('hello', 5, {foo: 'bar'})\") + ```""" + + @typing.overload + def on(self, event: Literal["crash"], f: typing.Callable[["Page"], "None"]) -> None: + """ + Emitted when the page crashes. Browser pages might crash if they try to allocate too much memory. When the page crashes, + ongoing and subsequent operations will throw. + + The most common way to deal with crashes is to catch an exception: + + ```py + try: + # crash might happen during a click. + page.click(\"button\") + # or while waiting for an event. + page.wait_for_event(\"popup\") + except Error as e: + # when the page crashes, exception message contains \"crash\". + ```""" + + @typing.overload + def on( + self, event: Literal["dialog"], f: typing.Callable[["Dialog"], "None"] + ) -> None: + """ + Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must** + either `dialog.accept()` or `dialog.dismiss()` the dialog - otherwise the page will + [freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking) waiting for the dialog, and + actions like click will never finish. + + > NOTE: When no `page.on('dialog')` listeners are present, all dialogs are automatically dismissed.""" + + @typing.overload + def on( + self, event: Literal["domcontentloaded"], f: typing.Callable[["Page"], "None"] + ) -> None: + """ + Emitted when the JavaScript [`DOMContentLoaded`](https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded) + event is dispatched.""" + + @typing.overload + def on( + self, event: Literal["download"], f: typing.Callable[["Download"], "None"] + ) -> None: + """ + Emitted when attachment download started. User can access basic file operations on downloaded content via the passed + `Download` instance. + + > NOTE: Browser context **must** be created with the `acceptDownloads` set to `true` when user needs access to the + downloaded content. If `acceptDownloads` is not set, download events are emitted, but the actual download is not + performed and user has no access to the downloaded files.""" + + @typing.overload + def on( + self, event: Literal["filechooser"], f: typing.Callable[["FileChooser"], "None"] + ) -> None: + """ + Emitted when a file chooser is supposed to appear, such as after clicking the ``. Playwright can + respond to it via setting the input files using `file_chooser.set_files()` that can be uploaded after that. + + ```py + page.on(\"filechooser\", lambda file_chooser: file_chooser.set_files(\"/tmp/myfile.pdf\")) + ```""" + + @typing.overload + def on( + self, event: Literal["frameattached"], f: typing.Callable[["Frame"], "None"] + ) -> None: + """ + Emitted when a frame is attached.""" + + @typing.overload + def on( + self, event: Literal["framedetached"], f: typing.Callable[["Frame"], "None"] + ) -> None: + """ + Emitted when a frame is detached.""" + + @typing.overload + def on( + self, event: Literal["framenavigated"], f: typing.Callable[["Frame"], "None"] + ) -> None: + """ + Emitted when a frame is navigated to a new url.""" + + @typing.overload + def on(self, event: Literal["load"], f: typing.Callable[["Page"], "None"]) -> None: + """ + Emitted when the JavaScript [`load`](https://developer.mozilla.org/en-US/docs/Web/Events/load) event is dispatched.""" + + @typing.overload + def on( + self, event: Literal["pageerror"], f: typing.Callable[["Error"], "None"] + ) -> None: + """ + Emitted when an uncaught exception happens within the page.""" + + @typing.overload + def on(self, event: Literal["popup"], f: typing.Callable[["Page"], "None"]) -> None: + """ + Emitted when the page opens a new tab or window. This event is emitted in addition to the + `browser_context.on('page')`, but only for popups relevant to this page. + + The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a + popup with `window.open('http://example.com')`, this event will fire when the network request to \"http://example.com\" is + done and its response has started loading in the popup. + + ```py + with page.expect_event(\"popup\") as page_info: + page.evaluate(\"window.open('https://example.com')\") + popup = page_info.value + print(popup.evaluate(\"location.href\")) + ``` + + > NOTE: Use `page.wait_for_load_state()` to wait until the page gets to a particular state (you should not need it + in most cases).""" + + @typing.overload + def on( + self, event: Literal["request"], f: typing.Callable[["Request"], "None"] + ) -> None: + """ + Emitted when a page issues a request. The [request] object is read-only. In order to intercept and mutate requests, see + `page.route()` or `browser_context.route()`.""" + + @typing.overload + def on( + self, event: Literal["requestfailed"], f: typing.Callable[["Request"], "None"] + ) -> None: + """ + Emitted when a request fails, for example by timing out. + + > NOTE: HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request will + complete with `page.on('request_finished')` event and not with `page.on('request_failed')`. A request will only be + considered failed when the client cannot get an HTTP response from the server, e.g. due to network error + net::ERR_FAILED.""" + + @typing.overload + def on( + self, event: Literal["requestfinished"], f: typing.Callable[["Request"], "None"] + ) -> None: + """ + Emitted when a request finishes successfully after downloading the response body. For a successful response, the + sequence of events is `request`, `response` and `requestfinished`.""" + + @typing.overload + def on( + self, event: Literal["response"], f: typing.Callable[["Response"], "None"] + ) -> None: + """ + Emitted when [response] status and headers are received for a request. For a successful response, the sequence of events + is `request`, `response` and `requestfinished`.""" + + @typing.overload + def on( + self, event: Literal["websocket"], f: typing.Callable[["WebSocket"], "None"] + ) -> None: + """ + Emitted when `WebSocket` request is sent.""" + + @typing.overload + def on( + self, event: Literal["worker"], f: typing.Callable[["Worker"], "None"] + ) -> None: + """ + Emitted when a dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is spawned by the + page.""" + + def on(self, event: str, f: typing.Callable[..., None]) -> None: + return super().on(event=event, f=f) + + @typing.overload + def once( + self, event: Literal["close"], f: typing.Callable[["Page"], "None"] + ) -> None: + """ + Emitted when the page closes.""" + + @typing.overload + def once( + self, event: Literal["console"], f: typing.Callable[["ConsoleMessage"], "None"] + ) -> None: + """ + Emitted when JavaScript within the page calls one of console API methods, e.g. `console.log` or `console.dir`. Also + emitted if the page throws an error or a warning. + + The arguments passed into `console.log` appear as arguments on the event handler. + + An example of handling `console` event: + + ```py + def print_args(msg): + for arg in msg.args: + print(arg.json_value()) + + page.on(\"console\", print_args) + page.evaluate(\"console.log('hello', 5, {foo: 'bar'})\") + ```""" + + @typing.overload + def once( + self, event: Literal["crash"], f: typing.Callable[["Page"], "None"] + ) -> None: + """ + Emitted when the page crashes. Browser pages might crash if they try to allocate too much memory. When the page crashes, + ongoing and subsequent operations will throw. + + The most common way to deal with crashes is to catch an exception: + + ```py + try: + # crash might happen during a click. + page.click(\"button\") + # or while waiting for an event. + page.wait_for_event(\"popup\") + except Error as e: + # when the page crashes, exception message contains \"crash\". + ```""" + + @typing.overload + def once( + self, event: Literal["dialog"], f: typing.Callable[["Dialog"], "None"] + ) -> None: + """ + Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must** + either `dialog.accept()` or `dialog.dismiss()` the dialog - otherwise the page will + [freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking) waiting for the dialog, and + actions like click will never finish. + + > NOTE: When no `page.on('dialog')` listeners are present, all dialogs are automatically dismissed.""" + + @typing.overload + def once( + self, event: Literal["domcontentloaded"], f: typing.Callable[["Page"], "None"] + ) -> None: + """ + Emitted when the JavaScript [`DOMContentLoaded`](https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded) + event is dispatched.""" + + @typing.overload + def once( + self, event: Literal["download"], f: typing.Callable[["Download"], "None"] + ) -> None: + """ + Emitted when attachment download started. User can access basic file operations on downloaded content via the passed + `Download` instance. + + > NOTE: Browser context **must** be created with the `acceptDownloads` set to `true` when user needs access to the + downloaded content. If `acceptDownloads` is not set, download events are emitted, but the actual download is not + performed and user has no access to the downloaded files.""" + + @typing.overload + def once( + self, event: Literal["filechooser"], f: typing.Callable[["FileChooser"], "None"] + ) -> None: + """ + Emitted when a file chooser is supposed to appear, such as after clicking the ``. Playwright can + respond to it via setting the input files using `file_chooser.set_files()` that can be uploaded after that. + + ```py + page.on(\"filechooser\", lambda file_chooser: file_chooser.set_files(\"/tmp/myfile.pdf\")) + ```""" + + @typing.overload + def once( + self, event: Literal["frameattached"], f: typing.Callable[["Frame"], "None"] + ) -> None: + """ + Emitted when a frame is attached.""" + + @typing.overload + def once( + self, event: Literal["framedetached"], f: typing.Callable[["Frame"], "None"] + ) -> None: + """ + Emitted when a frame is detached.""" + + @typing.overload + def once( + self, event: Literal["framenavigated"], f: typing.Callable[["Frame"], "None"] + ) -> None: + """ + Emitted when a frame is navigated to a new url.""" + + @typing.overload + def once( + self, event: Literal["load"], f: typing.Callable[["Page"], "None"] + ) -> None: + """ + Emitted when the JavaScript [`load`](https://developer.mozilla.org/en-US/docs/Web/Events/load) event is dispatched.""" + + @typing.overload + def once( + self, event: Literal["pageerror"], f: typing.Callable[["Error"], "None"] + ) -> None: + """ + Emitted when an uncaught exception happens within the page.""" + + @typing.overload + def once( + self, event: Literal["popup"], f: typing.Callable[["Page"], "None"] + ) -> None: + """ + Emitted when the page opens a new tab or window. This event is emitted in addition to the + `browser_context.on('page')`, but only for popups relevant to this page. + + The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a + popup with `window.open('http://example.com')`, this event will fire when the network request to \"http://example.com\" is + done and its response has started loading in the popup. + + ```py + with page.expect_event(\"popup\") as page_info: + page.evaluate(\"window.open('https://example.com')\") + popup = page_info.value + print(popup.evaluate(\"location.href\")) + ``` + + > NOTE: Use `page.wait_for_load_state()` to wait until the page gets to a particular state (you should not need it + in most cases).""" + + @typing.overload + def once( + self, event: Literal["request"], f: typing.Callable[["Request"], "None"] + ) -> None: + """ + Emitted when a page issues a request. The [request] object is read-only. In order to intercept and mutate requests, see + `page.route()` or `browser_context.route()`.""" + + @typing.overload + def once( + self, event: Literal["requestfailed"], f: typing.Callable[["Request"], "None"] + ) -> None: + """ + Emitted when a request fails, for example by timing out. + + > NOTE: HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request will + complete with `page.on('request_finished')` event and not with `page.on('request_failed')`. A request will only be + considered failed when the client cannot get an HTTP response from the server, e.g. due to network error + net::ERR_FAILED.""" + + @typing.overload + def once( + self, event: Literal["requestfinished"], f: typing.Callable[["Request"], "None"] + ) -> None: + """ + Emitted when a request finishes successfully after downloading the response body. For a successful response, the + sequence of events is `request`, `response` and `requestfinished`.""" + + @typing.overload + def once( + self, event: Literal["response"], f: typing.Callable[["Response"], "None"] + ) -> None: + """ + Emitted when [response] status and headers are received for a request. For a successful response, the sequence of events + is `request`, `response` and `requestfinished`.""" + + @typing.overload + def once( + self, event: Literal["websocket"], f: typing.Callable[["WebSocket"], "None"] + ) -> None: + """ + Emitted when `WebSocket` request is sent.""" + + @typing.overload + def once( + self, event: Literal["worker"], f: typing.Callable[["Worker"], "None"] + ) -> None: + """ + Emitted when a dedicated [WebWorker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) is spawned by the + page.""" + + def once(self, event: str, f: typing.Callable[..., None]) -> None: + return super().once(event=event, f=f) + @property def accessibility(self) -> "Accessibility": """Page.accessibility @@ -8959,6 +9438,196 @@ def set_checked( class BrowserContext(SyncContextManager): + @typing.overload + def on( + self, event: Literal["backgroundpage"], f: typing.Callable[["Page"], "None"] + ) -> None: + """ + > NOTE: Only works with Chromium browser's persistent context. + + Emitted when new background page is created in the context. + + ```py + background_page = context.wait_for_event(\"backgroundpage\") + ```""" + + @typing.overload + def on( + self, event: Literal["close"], f: typing.Callable[["BrowserContext"], "None"] + ) -> None: + """ + Emitted when Browser context gets closed. This might happen because of one of the following: + - Browser context is closed. + - Browser application is closed or crashed. + - The `browser.close()` method was called.""" + + @typing.overload + def on(self, event: Literal["page"], f: typing.Callable[["Page"], "None"]) -> None: + """ + The event is emitted when a new Page is created in the BrowserContext. The page may still be loading. The event will + also fire for popup pages. See also `page.on('popup')` to receive events about popups relevant to a specific page. + + The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a + popup with `window.open('http://example.com')`, this event will fire when the network request to \"http://example.com\" is + done and its response has started loading in the popup. + + ```py + with context.expect_page() as page_info: + page.click(\"a[target=_blank]\"), + page = page_info.value + print(page.evaluate(\"location.href\")) + ``` + + > NOTE: Use `page.wait_for_load_state()` to wait until the page gets to a particular state (you should not need it + in most cases).""" + + @typing.overload + def on( + self, event: Literal["request"], f: typing.Callable[["Request"], "None"] + ) -> None: + """ + Emitted when a request is issued from any pages created through this context. The [request] object is read-only. To only + listen for requests from a particular page, use `page.on('request')`. + + In order to intercept and mutate requests, see `browser_context.route()` or `page.route()`.""" + + @typing.overload + def on( + self, event: Literal["requestfailed"], f: typing.Callable[["Request"], "None"] + ) -> None: + """ + Emitted when a request fails, for example by timing out. To only listen for failed requests from a particular page, use + `page.on('request_failed')`. + + > NOTE: HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request will + complete with `browser_context.on('request_finished')` event and not with `browser_context.on('request_failed')`.""" + + @typing.overload + def on( + self, event: Literal["requestfinished"], f: typing.Callable[["Request"], "None"] + ) -> None: + """ + Emitted when a request finishes successfully after downloading the response body. For a successful response, the + sequence of events is `request`, `response` and `requestfinished`. To listen for successful requests from a particular + page, use `page.on('request_finished')`.""" + + @typing.overload + def on( + self, event: Literal["response"], f: typing.Callable[["Response"], "None"] + ) -> None: + """ + Emitted when [response] status and headers are received for a request. For a successful response, the sequence of events + is `request`, `response` and `requestfinished`. To listen for response events from a particular page, use + `page.on('response')`.""" + + @typing.overload + def on( + self, event: Literal["serviceworker"], f: typing.Callable[["Worker"], "None"] + ) -> None: + """ + > NOTE: Service workers are only supported on Chromium-based browsers. + + Emitted when new service worker is created in the context.""" + + def on(self, event: str, f: typing.Callable[..., None]) -> None: + return super().on(event=event, f=f) + + @typing.overload + def once( + self, event: Literal["backgroundpage"], f: typing.Callable[["Page"], "None"] + ) -> None: + """ + > NOTE: Only works with Chromium browser's persistent context. + + Emitted when new background page is created in the context. + + ```py + background_page = context.wait_for_event(\"backgroundpage\") + ```""" + + @typing.overload + def once( + self, event: Literal["close"], f: typing.Callable[["BrowserContext"], "None"] + ) -> None: + """ + Emitted when Browser context gets closed. This might happen because of one of the following: + - Browser context is closed. + - Browser application is closed or crashed. + - The `browser.close()` method was called.""" + + @typing.overload + def once( + self, event: Literal["page"], f: typing.Callable[["Page"], "None"] + ) -> None: + """ + The event is emitted when a new Page is created in the BrowserContext. The page may still be loading. The event will + also fire for popup pages. See also `page.on('popup')` to receive events about popups relevant to a specific page. + + The earliest moment that page is available is when it has navigated to the initial url. For example, when opening a + popup with `window.open('http://example.com')`, this event will fire when the network request to \"http://example.com\" is + done and its response has started loading in the popup. + + ```py + with context.expect_page() as page_info: + page.click(\"a[target=_blank]\"), + page = page_info.value + print(page.evaluate(\"location.href\")) + ``` + + > NOTE: Use `page.wait_for_load_state()` to wait until the page gets to a particular state (you should not need it + in most cases).""" + + @typing.overload + def once( + self, event: Literal["request"], f: typing.Callable[["Request"], "None"] + ) -> None: + """ + Emitted when a request is issued from any pages created through this context. The [request] object is read-only. To only + listen for requests from a particular page, use `page.on('request')`. + + In order to intercept and mutate requests, see `browser_context.route()` or `page.route()`.""" + + @typing.overload + def once( + self, event: Literal["requestfailed"], f: typing.Callable[["Request"], "None"] + ) -> None: + """ + Emitted when a request fails, for example by timing out. To only listen for failed requests from a particular page, use + `page.on('request_failed')`. + + > NOTE: HTTP Error responses, such as 404 or 503, are still successful responses from HTTP standpoint, so request will + complete with `browser_context.on('request_finished')` event and not with `browser_context.on('request_failed')`.""" + + @typing.overload + def once( + self, event: Literal["requestfinished"], f: typing.Callable[["Request"], "None"] + ) -> None: + """ + Emitted when a request finishes successfully after downloading the response body. For a successful response, the + sequence of events is `request`, `response` and `requestfinished`. To listen for successful requests from a particular + page, use `page.on('request_finished')`.""" + + @typing.overload + def once( + self, event: Literal["response"], f: typing.Callable[["Response"], "None"] + ) -> None: + """ + Emitted when [response] status and headers are received for a request. For a successful response, the sequence of events + is `request`, `response` and `requestfinished`. To listen for response events from a particular page, use + `page.on('response')`.""" + + @typing.overload + def once( + self, event: Literal["serviceworker"], f: typing.Callable[["Worker"], "None"] + ) -> None: + """ + > NOTE: Service workers are only supported on Chromium-based browsers. + + Emitted when new service worker is created in the context.""" + + def once(self, event: str, f: typing.Callable[..., None]) -> None: + return super().once(event=event, f=f) + @property def pages(self) -> typing.List["Page"]: """BrowserContext.pages @@ -9748,6 +10417,24 @@ def detach(self) -> NoneType: class Browser(SyncContextManager): + def on( + self, event: Literal["disconnected"], f: typing.Callable[["Browser"], "None"] + ) -> None: + """ + Emitted when Browser gets disconnected from the browser application. This might happen because of one of the following: + - Browser application is closed or crashed. + - The `browser.close()` method was called.""" + return super().on(event=event, f=f) + + def once( + self, event: Literal["disconnected"], f: typing.Callable[["Browser"], "None"] + ) -> None: + """ + Emitted when Browser gets disconnected from the browser application. This might happen because of one of the following: + - Browser application is closed or crashed. + - The `browser.close()` method was called.""" + return super().once(event=event, f=f) + @property def contexts(self) -> typing.List["BrowserContext"]: """Browser.contexts diff --git a/scripts/documentation_provider.py b/scripts/documentation_provider.py index e52c2197f..626515ade 100644 --- a/scripts/documentation_provider.py +++ b/scripts/documentation_provider.py @@ -55,6 +55,7 @@ def _patch_case(self) -> None: continue members = {} self.classes[clazz["name"]] = clazz + events = [] for member in clazz["members"]: if not works_for_python(member): continue @@ -63,11 +64,11 @@ def _patch_case(self) -> None: self._add_link(member["kind"], clazz["name"], member_name, new_name) if member["kind"] == "event": - continue - - new_name = to_snake_case(new_name) - member["name"] = new_name - members[new_name] = member + events.append(member) + else: + new_name = to_snake_case(new_name) + member["name"] = new_name + members[new_name] = member apply_type_or_override(member) if "args" in member: @@ -93,6 +94,7 @@ def _patch_case(self) -> None: member["args"] = args clazz["members"] = members + clazz["events"] = events def _add_link(self, kind: str, clazz: str, member: str, alias: str) -> None: match = re.match(r"(JS|CDP|[A-Z])([^.]+)", clazz) @@ -200,6 +202,39 @@ def print_entry( f"Parameter not implemented: {class_name}.{method_name}({name}=)" ) + def print_events(self, class_name: str) -> None: + clazz = self.classes[class_name] + if events := clazz["events"]: + doc = [] + for event_type in ["on", "once"]: + for event in events: + return_type = ( + "typing.Union[typing.Awaitable[None], None]" + if self.is_async + else "None" + ) + func_arg = self.serialize_doc_type(event["type"], "") + if func_arg.startswith("{"): + func_arg = "typing.Dict" + if len(events) > 1: + doc.append(" @typing.overload") + impl = "" + if len(events) == 1: + impl = f" return super().{event_type}(event=event,f=f)" + doc.append( + f" def {event_type}(self, event: Literal['{event['name'].lower()}'], f: typing.Callable[['{func_arg}'], '{return_type}']) -> None:" + ) + doc.append( + f' """{self.beautify_method_comment(event["comment"]," " * 8)}"""' + ) + doc.append(impl) + if len(events) > 1: + doc.append( + f" def {event_type}(self, event: str, f: typing.Callable[...,{return_type}]) -> None:" + ) + doc.append(f" return super().{event_type}(event=event,f=f)") + print("\n".join(doc)) + def indent_paragraph(self, p: str, indent: str) -> str: lines = p.split("\n") result = [lines[0]] @@ -386,10 +421,9 @@ def inner_serialize_doc_type(self, type: Any, direction: str) -> str: ) ) return f"{{{', '.join(items)}}}" - if type_name == "boolean": return "bool" - if type_name == "string": + if type_name.lower() == "string": return "str" if type_name == "any" or type_name == "Serializable": return "Any" diff --git a/scripts/generate_api.py b/scripts/generate_api.py index 3d306b53e..3b1852d21 100644 --- a/scripts/generate_api.py +++ b/scripts/generate_api.py @@ -237,7 +237,7 @@ def return_value(value: Any) -> List[str]: from playwright._impl._video import Video as VideoImpl from playwright._impl._tracing import Tracing as TracingImpl from playwright._impl._locator import Locator as LocatorImpl - +from playwright._impl._api_types import Error """ diff --git a/scripts/generate_async_api.py b/scripts/generate_async_api.py index 066fce439..751ea711d 100755 --- a/scripts/generate_async_api.py +++ b/scripts/generate_async_api.py @@ -47,6 +47,7 @@ def generate(t: Any) -> None: base_sync_class = base_class print(f"class {class_name}({base_sync_class}):") print("") + documentation_provider.print_events(class_name) for [name, type] in get_type_hints(t, api_globals).items(): print("") print(" @property") diff --git a/scripts/generate_sync_api.py b/scripts/generate_sync_api.py index 2efaeddb5..99f636fe3 100755 --- a/scripts/generate_sync_api.py +++ b/scripts/generate_sync_api.py @@ -48,6 +48,7 @@ def generate(t: Any) -> None: base_sync_class = base_class print(f"class {class_name}({base_sync_class}):") print("") + documentation_provider.print_events(class_name) for [name, type] in get_type_hints(t, api_globals).items(): print("") print(" @property") diff --git a/setup.cfg b/setup.cfg index 669f82642..47e20a6fc 100644 --- a/setup.cfg +++ b/setup.cfg @@ -14,7 +14,7 @@ warn_redundant_casts = True warn_unused_configs = True check_untyped_defs = True disallow_untyped_defs = True -[mypy-tests/async.*] +[mypy-tests.*] ignore_errors = True [flake8] ignore =