From 01c5f3f11250d54ee834db1ab3192cd79f8ac988 Mon Sep 17 00:00:00 2001 From: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> Date: Sun, 25 Apr 2021 11:47:48 +0530 Subject: [PATCH 1/2] fix: add missing headers param --- playwright/_impl/_browser_type.py | 21 +++++++++++++++------ playwright/_impl/_file_chooser.py | 2 +- playwright/_impl/_frame.py | 5 +++-- playwright/_impl/_network.py | 3 ++- playwright/_impl/_transport.py | 7 ++++++- setup.py | 2 +- 6 files changed, 28 insertions(+), 12 deletions(-) diff --git a/playwright/_impl/_browser_type.py b/playwright/_impl/_browser_type.py index b7b8f0ad8..3305ba6f5 100644 --- a/playwright/_impl/_browser_type.py +++ b/playwright/_impl/_browser_type.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +import os from pathlib import Path from typing import Dict, List, Optional, Union, cast @@ -126,7 +127,7 @@ async def launch_persistent_context( recordVideoDir: Union[Path, str] = None, recordVideoSize: ViewportSize = None, ) -> BrowserContext: - userDataDir = str(Path(userDataDir)) + userDataDir = os.fspath(userDataDir) params = locals_to_params(locals()) normalize_context_params(self._connection._is_sync, params) normalize_launch_params(params) @@ -142,7 +143,11 @@ async def launch_persistent_context( raise e async def connect_over_cdp( - self, endpointURL: str, timeout: float = None, slow_mo: float = None + self, + endpointURL: str, + timeout: float = None, + slow_mo: float = None, + headers: Dict[str, str] = None, ) -> Browser: params = locals_to_params(locals()) params["sdkLanguage"] = ( @@ -162,9 +167,13 @@ async def connect_over_cdp( return browser async def connect( - self, ws_endpoint: str, timeout: float = None, slow_mo: float = None + self, + ws_endpoint: str, + timeout: float = None, + slow_mo: float = None, + headers: Dict[str, str] = None, ) -> Browser: - transport = WebSocketTransport(ws_endpoint, timeout) + transport = WebSocketTransport(ws_endpoint, timeout, headers) connection = Connection( self._connection._dispatcher_fiber, @@ -198,6 +207,6 @@ def normalize_launch_params(params: Dict) -> None: params["ignoreAllDefaultArgs"] = True del params["ignoreDefaultArgs"] if "executablePath" in params: - params["executablePath"] = str(Path(params["executablePath"])) + params["executablePath"] = os.fspath(params["executablePath"]) if "downloadsPath" in params: - params["downloadsPath"] = str(Path(params["downloadsPath"])) + params["downloadsPath"] = os.fspath(params["downloadsPath"]) diff --git a/playwright/_impl/_file_chooser.py b/playwright/_impl/_file_chooser.py index fce4ade04..31bfbd6ee 100644 --- a/playwright/_impl/_file_chooser.py +++ b/playwright/_impl/_file_chooser.py @@ -69,7 +69,7 @@ def normalize_file_payloads( file_payloads.append( { "name": os.path.basename(item), - "mimeType": mimetypes.guess_type(str(Path(item)))[0] + "mimeType": mimetypes.guess_type(os.fspath(item))[0] or "application/octet-stream", "buffer": base64.b64encode(fd.read()).decode(), } diff --git a/playwright/_impl/_frame.py b/playwright/_impl/_frame.py index c33bd1e97..fe6967beb 100644 --- a/playwright/_impl/_frame.py +++ b/playwright/_impl/_frame.py @@ -13,6 +13,7 @@ # limitations under the License. import asyncio +import os import sys from pathlib import Path from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Union, cast @@ -361,7 +362,7 @@ async def add_script_tag( params = locals_to_params(locals()) if path: with open(path, "r") as file: - params["content"] = file.read() + "\n//# sourceURL=" + str(Path(path)) + params["content"] = file.read() + "\n//# sourceURL=" + os.fspath(path) del params["path"] return from_channel(await self._channel.send("addScriptTag", params)) @@ -372,7 +373,7 @@ async def add_style_tag( if path: with open(path, "r") as file: params["content"] = ( - file.read() + "\n/*# sourceURL=" + str(Path(path)) + "*/" + file.read() + "\n/*# sourceURL=" + os.fspath(path) + "*/" ) del params["path"] return from_channel(await self._channel.send("addStyleTag", params)) diff --git a/playwright/_impl/_network.py b/playwright/_impl/_network.py index 9f30da908..47a33c721 100644 --- a/playwright/_impl/_network.py +++ b/playwright/_impl/_network.py @@ -15,6 +15,7 @@ import base64 import json import mimetypes +import os from pathlib import Path from types import SimpleNamespace from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Union, cast @@ -179,7 +180,7 @@ async def fulfill( headers["content-type"] = params["contentType"] elif path: headers["content-type"] = ( - mimetypes.guess_type(str(Path(path)))[0] or "application/octet-stream" + mimetypes.guess_type(os.fspath(path))[0] or "application/octet-stream" ) if length and "content-length" not in headers: headers["content-length"] = str(length) diff --git a/playwright/_impl/_transport.py b/playwright/_impl/_transport.py index fb20f3392..9c9be3ad4 100644 --- a/playwright/_impl/_transport.py +++ b/playwright/_impl/_transport.py @@ -137,13 +137,16 @@ def send(self, message: Dict) -> None: class WebSocketTransport(AsyncIOEventEmitter, Transport): - def __init__(self, ws_endpoint: str, timeout: float = None) -> None: + def __init__( + self, ws_endpoint: str, timeout: float = None, headers: Dict[str, str] = None + ) -> None: super().__init__() Transport.__init__(self) self._stopped = False self.ws_endpoint = ws_endpoint self.timeout = timeout + self.headers = headers self._loop: asyncio.AbstractEventLoop def request_stop(self) -> None: @@ -163,6 +166,8 @@ async def run(self) -> None: if self.timeout is not None: options["close_timeout"] = self.timeout / 1000 options["ping_timeout"] = self.timeout / 1000 + if self.headers is not None: + options["extra_headers"] = self.headers self._connection = await websockets.connect(self.ws_endpoint, **options) while not self._stopped: diff --git a/setup.py b/setup.py index 6d3fdf500..5668bcb09 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ InWheel = None from wheel.bdist_wheel import bdist_wheel as BDistWheelCommand -driver_version = "1.11.0-next-1619111599000" +driver_version = "1.11.0-next-1619321988000" def extractall(zip: zipfile.ZipFile, path: str) -> None: From 998487c7b06cc55a4bcf979421da897fc6c6b6d7 Mon Sep 17 00:00:00 2001 From: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> Date: Sun, 25 Apr 2021 12:35:42 +0530 Subject: [PATCH 2/2] docs: added docs for headers --- playwright/_impl/_browser_type.py | 7 ++-- playwright/_impl/_file_chooser.py | 2 +- playwright/_impl/_frame.py | 5 +-- playwright/_impl/_network.py | 3 +- playwright/_impl/_transport.py | 2 +- playwright/async_api/_generated.py | 60 ++++++++++++++++++++++++++---- playwright/sync_api/_generated.py | 60 ++++++++++++++++++++++++++---- setup.py | 2 +- tests/async/test_download.py | 2 +- 9 files changed, 116 insertions(+), 27 deletions(-) diff --git a/playwright/_impl/_browser_type.py b/playwright/_impl/_browser_type.py index 3305ba6f5..f78ba82c0 100644 --- a/playwright/_impl/_browser_type.py +++ b/playwright/_impl/_browser_type.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os from pathlib import Path from typing import Dict, List, Optional, Union, cast @@ -127,7 +126,7 @@ async def launch_persistent_context( recordVideoDir: Union[Path, str] = None, recordVideoSize: ViewportSize = None, ) -> BrowserContext: - userDataDir = os.fspath(userDataDir) + userDataDir = str(Path(userDataDir)) params = locals_to_params(locals()) normalize_context_params(self._connection._is_sync, params) normalize_launch_params(params) @@ -207,6 +206,6 @@ def normalize_launch_params(params: Dict) -> None: params["ignoreAllDefaultArgs"] = True del params["ignoreDefaultArgs"] if "executablePath" in params: - params["executablePath"] = os.fspath(params["executablePath"]) + params["executablePath"] = str(Path(params["executablePath"])) if "downloadsPath" in params: - params["downloadsPath"] = os.fspath(params["downloadsPath"]) + params["downloadsPath"] = str(Path(params["downloadsPath"])) diff --git a/playwright/_impl/_file_chooser.py b/playwright/_impl/_file_chooser.py index 31bfbd6ee..fce4ade04 100644 --- a/playwright/_impl/_file_chooser.py +++ b/playwright/_impl/_file_chooser.py @@ -69,7 +69,7 @@ def normalize_file_payloads( file_payloads.append( { "name": os.path.basename(item), - "mimeType": mimetypes.guess_type(os.fspath(item))[0] + "mimeType": mimetypes.guess_type(str(Path(item)))[0] or "application/octet-stream", "buffer": base64.b64encode(fd.read()).decode(), } diff --git a/playwright/_impl/_frame.py b/playwright/_impl/_frame.py index fe6967beb..c33bd1e97 100644 --- a/playwright/_impl/_frame.py +++ b/playwright/_impl/_frame.py @@ -13,7 +13,6 @@ # limitations under the License. import asyncio -import os import sys from pathlib import Path from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Union, cast @@ -362,7 +361,7 @@ async def add_script_tag( params = locals_to_params(locals()) if path: with open(path, "r") as file: - params["content"] = file.read() + "\n//# sourceURL=" + os.fspath(path) + params["content"] = file.read() + "\n//# sourceURL=" + str(Path(path)) del params["path"] return from_channel(await self._channel.send("addScriptTag", params)) @@ -373,7 +372,7 @@ async def add_style_tag( if path: with open(path, "r") as file: params["content"] = ( - file.read() + "\n/*# sourceURL=" + os.fspath(path) + "*/" + file.read() + "\n/*# sourceURL=" + str(Path(path)) + "*/" ) del params["path"] return from_channel(await self._channel.send("addStyleTag", params)) diff --git a/playwright/_impl/_network.py b/playwright/_impl/_network.py index 47a33c721..9f30da908 100644 --- a/playwright/_impl/_network.py +++ b/playwright/_impl/_network.py @@ -15,7 +15,6 @@ import base64 import json import mimetypes -import os from pathlib import Path from types import SimpleNamespace from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Union, cast @@ -180,7 +179,7 @@ async def fulfill( headers["content-type"] = params["contentType"] elif path: headers["content-type"] = ( - mimetypes.guess_type(os.fspath(path))[0] or "application/octet-stream" + mimetypes.guess_type(str(Path(path)))[0] or "application/octet-stream" ) if length and "content-length" not in headers: headers["content-length"] = str(length) diff --git a/playwright/_impl/_transport.py b/playwright/_impl/_transport.py index 9c9be3ad4..b315318b3 100644 --- a/playwright/_impl/_transport.py +++ b/playwright/_impl/_transport.py @@ -162,7 +162,7 @@ async def wait_until_stopped(self) -> None: async def run(self) -> None: await super().run() - options = {} + options: Dict[str, Any] = {} if self.timeout is not None: options["close_timeout"] = self.timeout / 1000 options["ping_timeout"] = self.timeout / 1000 diff --git a/playwright/async_api/_generated.py b/playwright/async_api/_generated.py index 0e573e32d..39b6f10c4 100644 --- a/playwright/async_api/_generated.py +++ b/playwright/async_api/_generated.py @@ -560,7 +560,7 @@ async def handle(route, request): \"foo\": \"bar\" # set \"foo\" header \"origin\": None # remove \"origin\" header } - await route.continue(headers=headers) + await route.continue_(headers=headers) } await page.route(\"**/*\", handle) ``` @@ -6384,6 +6384,18 @@ async def route( await browser.close() ``` + It is possible to examine the request to decide the route action. For example, mocking all requests that contain some + post data, and leaving all other requests as is: + + ```py + def handle_route(route): + if (\"my-string\" in route.request.post_data) + route.fulfill(body=\"mocked-data\") + else + route.continue_() + await page.route(\"/api/**\", handle_route) + ``` + Page routes take precedence over browser context routes (set up with `browser_context.route()`) when request matches both handlers. @@ -7865,13 +7877,15 @@ def expect_request( ) -> AsyncEventContextManager["Request"]: """Page.expect_request - Waits for the matching request and returns it. + Waits for the matching request and returns it. See [waiting for event](./events.md#waiting-for-event) for more details + about events. ```py async with page.expect_request(\"http://example.com/resource\") as first: await page.click('button') first_request = await first.value + # or with a lambda async with page.expect_request(lambda request: request.url == \"http://example.com\" and request.method == \"get\") as second: await page.click('img') second_request = await second.value @@ -7906,7 +7920,7 @@ def expect_response( ) -> AsyncEventContextManager["Response"]: """Page.expect_response - Returns the matched response. + Returns the matched response. See [waiting for event](./events.md#waiting-for-event) for more details about events. ```py async with page.expect_response(\"https://example.com/resource\") as response_info: @@ -8481,6 +8495,18 @@ async def route( await browser.close() ``` + It is possible to examine the request to decide the route action. For example, mocking all requests that contain some + post data, and leaving all other requests as is: + + ```py + def handle_route(route): + if (\"my-string\" in route.request.post_data) + route.fulfill(body=\"mocked-data\") + else + route.continue_() + await context.route(\"/api/**\", handle_route) + ``` + Page routes (set up with `page.route()`) take precedence over browser context routes when request matches both handlers. @@ -9567,7 +9593,12 @@ async def launch_persistent_context( ) async def connect_over_cdp( - self, endpoint_url: str, *, timeout: float = None, slow_mo: float = None + self, + endpoint_url: str, + *, + timeout: float = None, + slow_mo: float = None, + headers: typing.Optional[typing.Dict[str, str]] = None ) -> "Browser": """BrowserType.connect_over_cdp @@ -9588,6 +9619,8 @@ async def connect_over_cdp( slow_mo : Union[float, NoneType] Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on. Defaults to 0. + headers : Union[Dict[str, str], NoneType] + Additional HTTP headers to be sent with connect request. Optional. Returns ------- @@ -9598,13 +9631,21 @@ async def connect_over_cdp( await self._async( "browser_type.connect_over_cdp", self._impl_obj.connect_over_cdp( - endpointURL=endpoint_url, timeout=timeout, slow_mo=slow_mo + endpointURL=endpoint_url, + timeout=timeout, + slow_mo=slow_mo, + headers=mapping.to_impl(headers), ), ) ) async def connect( - self, ws_endpoint: str, *, timeout: float = None, slow_mo: float = None + self, + ws_endpoint: str, + *, + timeout: float = None, + slow_mo: float = None, + headers: typing.Optional[typing.Dict[str, str]] = None ) -> "Browser": """BrowserType.connect @@ -9620,6 +9661,8 @@ async def connect( slow_mo : Union[float, NoneType] Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on. Defaults to 0. + headers : Union[Dict[str, str], NoneType] + Additional HTTP headers to be sent with web socket connect request. Optional. Returns ------- @@ -9630,7 +9673,10 @@ async def connect( await self._async( "browser_type.connect", self._impl_obj.connect( - ws_endpoint=ws_endpoint, timeout=timeout, slow_mo=slow_mo + ws_endpoint=ws_endpoint, + timeout=timeout, + slow_mo=slow_mo, + headers=mapping.to_impl(headers), ), ) ) diff --git a/playwright/sync_api/_generated.py b/playwright/sync_api/_generated.py index 48647b948..f4c244b0f 100644 --- a/playwright/sync_api/_generated.py +++ b/playwright/sync_api/_generated.py @@ -560,7 +560,7 @@ def handle(route, request): \"foo\": \"bar\" # set \"foo\" header \"origin\": None # remove \"origin\" header } - route.continue(headers=headers) + route.continue_(headers=headers) } page.route(\"**/*\", handle) ``` @@ -6345,6 +6345,18 @@ def route( browser.close() ``` + It is possible to examine the request to decide the route action. For example, mocking all requests that contain some + post data, and leaving all other requests as is: + + ```py + def handle_route(route): + if (\"my-string\" in route.request.post_data) + route.fulfill(body=\"mocked-data\") + else + route.continue_() + page.route(\"/api/**\", handle_route) + ``` + Page routes take precedence over browser context routes (set up with `browser_context.route()`) when request matches both handlers. @@ -7819,13 +7831,15 @@ def expect_request( ) -> EventContextManager["Request"]: """Page.expect_request - Waits for the matching request and returns it. + Waits for the matching request and returns it. See [waiting for event](./events.md#waiting-for-event) for more details + about events. ```py with page.expect_request(\"http://example.com/resource\") as first: page.click('button') first_request = first.value + # or with a lambda with page.expect_request(lambda request: request.url == \"http://example.com\" and request.method == \"get\") as second: page.click('img') second_request = second.value @@ -7860,7 +7874,7 @@ def expect_response( ) -> EventContextManager["Response"]: """Page.expect_response - Returns the matched response. + Returns the matched response. See [waiting for event](./events.md#waiting-for-event) for more details about events. ```py with page.expect_response(\"https://example.com/resource\") as response_info: @@ -8427,6 +8441,18 @@ def route( browser.close() ``` + It is possible to examine the request to decide the route action. For example, mocking all requests that contain some + post data, and leaving all other requests as is: + + ```py + def handle_route(route): + if (\"my-string\" in route.request.post_data) + route.fulfill(body=\"mocked-data\") + else + route.continue_() + context.route(\"/api/**\", handle_route) + ``` + Page routes (set up with `page.route()`) take precedence over browser context routes when request matches both handlers. @@ -9513,7 +9539,12 @@ def launch_persistent_context( ) def connect_over_cdp( - self, endpoint_url: str, *, timeout: float = None, slow_mo: float = None + self, + endpoint_url: str, + *, + timeout: float = None, + slow_mo: float = None, + headers: typing.Optional[typing.Dict[str, str]] = None ) -> "Browser": """BrowserType.connect_over_cdp @@ -9534,6 +9565,8 @@ def connect_over_cdp( slow_mo : Union[float, NoneType] Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on. Defaults to 0. + headers : Union[Dict[str, str], NoneType] + Additional HTTP headers to be sent with connect request. Optional. Returns ------- @@ -9544,13 +9577,21 @@ def connect_over_cdp( self._sync( "browser_type.connect_over_cdp", self._impl_obj.connect_over_cdp( - endpointURL=endpoint_url, timeout=timeout, slow_mo=slow_mo + endpointURL=endpoint_url, + timeout=timeout, + slow_mo=slow_mo, + headers=mapping.to_impl(headers), ), ) ) def connect( - self, ws_endpoint: str, *, timeout: float = None, slow_mo: float = None + self, + ws_endpoint: str, + *, + timeout: float = None, + slow_mo: float = None, + headers: typing.Optional[typing.Dict[str, str]] = None ) -> "Browser": """BrowserType.connect @@ -9566,6 +9607,8 @@ def connect( slow_mo : Union[float, NoneType] Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on. Defaults to 0. + headers : Union[Dict[str, str], NoneType] + Additional HTTP headers to be sent with web socket connect request. Optional. Returns ------- @@ -9576,7 +9619,10 @@ def connect( self._sync( "browser_type.connect", self._impl_obj.connect( - ws_endpoint=ws_endpoint, timeout=timeout, slow_mo=slow_mo + ws_endpoint=ws_endpoint, + timeout=timeout, + slow_mo=slow_mo, + headers=mapping.to_impl(headers), ), ) ) diff --git a/setup.py b/setup.py index 5668bcb09..8c69f92e3 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ InWheel = None from wheel.bdist_wheel import bdist_wheel as BDistWheelCommand -driver_version = "1.11.0-next-1619321988000" +driver_version = "1.11.0-next-1619452681000" def extractall(zip: zipfile.ZipFile, path: str) -> None: diff --git a/tests/async/test_download.py b/tests/async/test_download.py index 0bc939505..0708a34d1 100644 --- a/tests/async/test_download.py +++ b/tests/async/test_download.py @@ -194,7 +194,7 @@ async def test_should_error_when_saving_after_deletion(tmpdir, browser, server): await download.delete() with pytest.raises(Error) as exc: await download.save_as(user_path) - assert "File already deleted. Save before deleting." in exc.value.message + assert "Target page, context or browser has been closed" in exc.value.message await page.close()