This commit is contained in:
2026-10-27 17:51:34 -03:00
parent 752bb94d48
commit 197a54e41d
422 changed files with 314452 additions and 68 deletions
@@ -0,0 +1,268 @@
# Copyright (c) Microsoft Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Python package `playwright` is a Python library to automate Chromium,
Firefox and WebKit with a single API. Playwright is built to enable cross-browser
web automation that is ever-green, capable, reliable and fast.
"""
from typing import Any, Optional, Union, overload
import playwright._impl._api_structures
import playwright._impl._errors
import playwright._impl._form_data
import playwright.sync_api._generated
from playwright._impl._assertions import (
APIResponseAssertions as APIResponseAssertionsImpl,
)
from playwright._impl._assertions import LocatorAssertions as LocatorAssertionsImpl
from playwright._impl._assertions import PageAssertions as PageAssertionsImpl
from playwright.sync_api._context_manager import PlaywrightContextManager
from playwright.sync_api._generated import (
APIRequest,
APIRequestContext,
APIResponse,
APIResponseAssertions,
Browser,
BrowserContext,
BrowserType,
CDPSession,
ConsoleMessage,
Dialog,
Download,
ElementHandle,
FileChooser,
Frame,
FrameLocator,
JSHandle,
Keyboard,
Locator,
LocatorAssertions,
Mouse,
Page,
PageAssertions,
Playwright,
Request,
Response,
Route,
Selectors,
Touchscreen,
Video,
WebError,
WebSocket,
WebSocketRoute,
Worker,
)
ChromiumBrowserContext = BrowserContext
Cookie = playwright._impl._api_structures.Cookie
FilePayload = playwright._impl._api_structures.FilePayload
FormData = playwright._impl._form_data.FormData
FloatRect = playwright._impl._api_structures.FloatRect
Geolocation = playwright._impl._api_structures.Geolocation
HttpCredentials = playwright._impl._api_structures.HttpCredentials
BrowserBindResult = playwright._impl._api_structures.BrowserBindResult
DebuggerLocation = playwright._impl._api_structures.DebuggerLocation
DebuggerPausedDetails = playwright._impl._api_structures.DebuggerPausedDetails
PdfMargins = playwright._impl._api_structures.PdfMargins
Position = playwright._impl._api_structures.Position
ProxySettings = playwright._impl._api_structures.ProxySettings
ResourceTiming = playwright._impl._api_structures.ResourceTiming
ScreencastActionStyle = playwright._impl._api_structures.ScreencastActionStyle
ScreencastFrame = playwright._impl._api_structures.ScreencastFrame
ScreencastSize = playwright._impl._api_structures.ScreencastSize
SourceLocation = playwright._impl._api_structures.SourceLocation
StorageState = playwright._impl._api_structures.StorageState
StorageStateCookie = playwright._impl._api_structures.StorageStateCookie
ViewportSize = playwright._impl._api_structures.ViewportSize
VirtualCredential = playwright._impl._api_structures.VirtualCredential
WebMCPTool = playwright._impl._api_structures.WebMCPTool
WebMCPToolAnnotations = playwright._impl._api_structures.WebMCPToolAnnotations
Error = playwright._impl._errors.Error
TimeoutError = playwright._impl._errors.TimeoutError
def sync_playwright() -> PlaywrightContextManager:
return PlaywrightContextManager()
class Expect:
_unset: Any = object()
def __init__(self) -> None:
self._timeout: Optional[float] = None
def set_options(self, timeout: Optional[float] = _unset) -> None:
"""
This method sets global `expect()` options.
Args:
timeout (float): Timeout value in milliseconds. Default to 5000 milliseconds.
Returns:
None
"""
if timeout is not self._unset:
self._timeout = timeout
@overload
def __call__(
self, actual: Page, message: Optional[str] = None
) -> PageAssertions: ...
@overload
def __call__(
self, actual: Locator, message: Optional[str] = None
) -> LocatorAssertions: ...
@overload
def __call__(
self, actual: APIResponse, message: Optional[str] = None
) -> APIResponseAssertions: ...
def __call__(
self, actual: Union[Page, Locator, APIResponse], message: Optional[str] = None
) -> Union[PageAssertions, LocatorAssertions, APIResponseAssertions]:
return self._dispatch(actual, message, is_soft=False)
@overload
def soft(self, actual: Page, message: Optional[str] = None) -> PageAssertions: ...
@overload
def soft(
self, actual: Locator, message: Optional[str] = None
) -> LocatorAssertions: ...
@overload
def soft(
self, actual: APIResponse, message: Optional[str] = None
) -> APIResponseAssertions: ...
def soft(
self, actual: Union[Page, Locator, APIResponse], message: Optional[str] = None
) -> Union[PageAssertions, LocatorAssertions, APIResponseAssertions]:
"""
Creates a [soft assertion](https://playwright.dev/python/docs/test-assertions#soft-assertions).
Failing soft assertions do not abort test execution, but mark the test
as failed. Multiple failures from the same test are surfaced together
at the end of the test.
Requires the [pytest-playwright](https://pypi.org/project/pytest-playwright/)
plugin to establish the per-test scope that collects soft assertion
failures.
"""
return self._dispatch(actual, message, is_soft=True)
def _dispatch(
self,
actual: Union[Page, Locator, APIResponse],
message: Optional[str],
is_soft: bool,
) -> Union[PageAssertions, LocatorAssertions, APIResponseAssertions]:
if isinstance(actual, Page):
return PageAssertions(
PageAssertionsImpl(
actual._impl_obj,
self._timeout,
message=message,
is_soft=is_soft,
)
)
elif isinstance(actual, Locator):
return LocatorAssertions(
LocatorAssertionsImpl(
actual._impl_obj,
self._timeout,
message=message,
is_soft=is_soft,
)
)
elif isinstance(actual, APIResponse):
return APIResponseAssertions(
APIResponseAssertionsImpl(
actual._impl_obj,
self._timeout,
message=message,
is_soft=is_soft,
)
)
raise ValueError(f"Unsupported type: {type(actual)}")
expect = Expect()
__all__ = [
"expect",
"APIRequest",
"APIRequestContext",
"APIResponse",
"Browser",
"BrowserBindResult",
"BrowserContext",
"BrowserType",
"CDPSession",
"ChromiumBrowserContext",
"ConsoleMessage",
"Cookie",
"Dialog",
"Download",
"ElementHandle",
"Error",
"FileChooser",
"FilePayload",
"FloatRect",
"FormData",
"Frame",
"FrameLocator",
"Geolocation",
"HttpCredentials",
"JSHandle",
"Keyboard",
"Locator",
"Mouse",
"DebuggerLocation",
"DebuggerPausedDetails",
"Page",
"PdfMargins",
"Position",
"Playwright",
"ProxySettings",
"Request",
"ResourceTiming",
"Response",
"Route",
"ScreencastActionStyle",
"ScreencastFrame",
"ScreencastSize",
"Selectors",
"SourceLocation",
"StorageState",
"StorageStateCookie",
"sync_playwright",
"TimeoutError",
"Touchscreen",
"Video",
"ViewportSize",
"VirtualCredential",
"WebError",
"WebMCPTool",
"WebMCPToolAnnotations",
"WebSocket",
"WebSocketRoute",
"Worker",
]
@@ -0,0 +1,117 @@
# Copyright (c) Microsoft Corporation.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import asyncio
from typing import TYPE_CHECKING, Any, Optional
from greenlet import GreenletExit, greenlet
from playwright._impl._connection import Connection
from playwright._impl._errors import Error
from playwright._impl._greenlets import MainGreenlet
from playwright._impl._object_factory import create_remote_object
from playwright._impl._transport import PipeTransport
from playwright.sync_api._generated import Playwright as SyncPlaywright
if TYPE_CHECKING:
from asyncio.unix_events import AbstractChildWatcher
class PlaywrightContextManager:
def __init__(self) -> None:
self._playwright: SyncPlaywright
self._loop: asyncio.AbstractEventLoop
self._own_loop = False
self._watcher: Optional[AbstractChildWatcher] = None
self._exit_was_called = False
def __enter__(self) -> SyncPlaywright:
try:
self._loop = asyncio.get_running_loop()
except RuntimeError:
self._loop = asyncio.new_event_loop()
self._own_loop = True
if self._loop.is_running():
raise Error(
"""It looks like you are using Playwright Sync API inside the asyncio loop.
Please use the Async API instead."""
)
g_self = greenlet.getcurrent()
# Create a new fiber for the protocol dispatcher. It will be pumping events
# until the end of times. We will pass control to that fiber every time we
# block while waiting for a response.
def greenlet_main() -> None:
task = self._loop.create_task(self._connection.run_as_sync())
while not task.done():
try:
self._loop.run_until_complete(task)
except GreenletExit:
raise
except BaseException as exc:
if task.done():
raise
# asyncio contains exceptions raised in tasks and callbacks.
# An exception that escapes the loop while the connection
# still runs comes from a signal handler, e.g. pytest-timeout
# or Ctrl+C, that fired while the loop waited for the driver.
# The user code is suspended in a sync call in the greenlet
# that created us: raise it there, and stay alive. The loop
# and its tasks are intact, so the next sync call resumes
# them.
g_self.throw(exc)
# Between sync calls the user code marks the loop as running,
# see wait_for_future. run_forever() refuses to start then.
asyncio._set_running_loop(None)
dispatcher_fiber = MainGreenlet(greenlet_main)
self._connection = Connection(
dispatcher_fiber,
create_remote_object,
PipeTransport(self._loop),
self._loop,
)
# Wait until initialize completes (not just Playwright __create__), matching async.
self._connection.playwright_future.add_done_callback(lambda _: g_self.switch())
dispatcher_fiber.switch()
try:
self._playwright = SyncPlaywright(
self._connection.playwright_future.result()
)
except BaseException:
self.__exit__()
raise
self._playwright.stop = self.__exit__ # type: ignore
return self._playwright
def start(self) -> SyncPlaywright:
return self.__enter__()
def __exit__(self, *args: Any) -> None:
if self._exit_was_called:
return
self._exit_was_called = True
self._connection.stop_sync()
if self._watcher:
self._watcher.close()
if self._own_loop:
tasks = asyncio.all_tasks(self._loop)
for t in [t for t in tasks if not (t.done() or t.cancelled())]:
t.cancel()
self._loop.run_until_complete(self._loop.shutdown_asyncgens())
self._loop.close()
File diff suppressed because it is too large Load Diff