Skip to content

Commit 0c26cbf

Browse files
authored
feat(roll): roll Playwright 1.15.0-next-1631655106000 (microsoft#905)
1 parent d12962b commit 0c26cbf

File tree

13 files changed

+387
-136
lines changed

13 files changed

+387
-136
lines changed

CONTRIBUTING.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ pre-commit run --all-files
4949

5050
For more details look at the [CI configuration](./blob/master/.github/workflows/ci.yml).
5151

52+
Collect coverage
53+
54+
```sh
55+
pytest --browser chromium --cov-report html --cov=playwright
56+
open htmlcov/index.html
57+
```
58+
5259
### Regenerating APIs
5360

5461
```bash

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ Playwright is a Python library to automate [Chromium](https://www.chromium.org/H
44

55
| | Linux | macOS | Windows |
66
| :--- | :---: | :---: | :---: |
7-
| Chromium <!-- GEN:chromium-version -->95.0.4636.0<!-- GEN:stop --> ||||
7+
| Chromium <!-- GEN:chromium-version -->96.0.4641.0<!-- GEN:stop --> ||||
88
| WebKit <!-- GEN:webkit-version -->15.0<!-- GEN:stop --> ||||
9-
| Firefox <!-- GEN:firefox-version -->91.0<!-- GEN:stop --> ||||
9+
| Firefox <!-- GEN:firefox-version -->92.0<!-- GEN:stop --> ||||
1010

1111
## Documentation
1212

playwright/_impl/_api_structures.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# limitations under the License.
1414

1515
import sys
16-
from typing import List, Optional, Union
16+
from typing import Dict, List, Optional, Union
1717

1818
if sys.version_info >= (3, 8): # pragma: no cover
1919
from typing import Literal, TypedDict
@@ -139,3 +139,12 @@ class SecurityDetails(TypedDict):
139139
subjectName: Optional[str]
140140
validFrom: Optional[float]
141141
validTo: Optional[float]
142+
143+
144+
class NameValue(TypedDict):
145+
name: str
146+
value: str
147+
148+
149+
HeadersArray = List[NameValue]
150+
Headers = Dict[str, str]

playwright/_impl/_browser_type.py

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
ForcedColors,
3939
ReducedMotion,
4040
locals_to_params,
41-
not_installed_error,
4241
)
4342
from playwright._impl._transport import WebSocketTransport
4443
from playwright._impl._wait_helper import throw_on_timeout
@@ -86,12 +85,7 @@ async def launch(
8685
) -> Browser:
8786
params = locals_to_params(locals())
8887
normalize_launch_params(params)
89-
try:
90-
return from_channel(await self._channel.send("launch", params))
91-
except Exception as e:
92-
if "npx playwright install" in str(e):
93-
raise not_installed_error(f'"{self.name}" browser was not found.')
94-
raise e
88+
return from_channel(await self._channel.send("launch", params))
9589

9690
async def launch_persistent_context(
9791
self,
@@ -144,16 +138,11 @@ async def launch_persistent_context(
144138
params = locals_to_params(locals())
145139
await normalize_context_params(self._connection._is_sync, params)
146140
normalize_launch_params(params)
147-
try:
148-
context = from_channel(
149-
await self._channel.send("launchPersistentContext", params)
150-
)
151-
context._options = params
152-
return context
153-
except Exception as e:
154-
if "npx playwright install" in str(e):
155-
raise not_installed_error(f'"{self.name}" browser was not found.')
156-
raise e
141+
context = from_channel(
142+
await self._channel.send("launchPersistentContext", params)
143+
)
144+
context._options = params
145+
return context
157146

158147
async def connect_over_cdp(
159148
self,

playwright/_impl/_helper.py

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
)
3737
from urllib.parse import urljoin
3838

39+
from playwright._impl._api_structures import NameValue
3940
from playwright._impl._api_types import Error, TimeoutError
4041

4142
if sys.version_info >= (3, 8): # pragma: no cover
@@ -69,15 +70,10 @@ class ErrorPayload(TypedDict, total=False):
6970
value: Any
7071

7172

72-
class Header(TypedDict):
73-
name: str
74-
value: str
75-
76-
7773
class ContinueParameters(TypedDict, total=False):
7874
url: Optional[str]
7975
method: Optional[str]
80-
headers: Optional[List[Header]]
76+
headers: Optional[List[NameValue]]
8177
postData: Optional[str]
8278

8379

@@ -234,20 +230,6 @@ def is_safe_close_error(error: Exception) -> bool:
234230
)
235231

236232

237-
def not_installed_error(message: str) -> Exception:
238-
return Error(
239-
f"""
240-
================================================================================
241-
{message}
242-
Please complete Playwright installation via running
243-
244-
"python -m playwright install"
245-
246-
================================================================================
247-
"""
248-
)
249-
250-
251233
to_snake_case_regex = re.compile("((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))")
252234

253235

playwright/_impl/_input.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ async def dblclick(
8080
) -> None:
8181
await self.click(x, y, delay=delay, button=button, clickCount=2)
8282

83+
async def wheel(self, deltaX: float, deltaY: float) -> None:
84+
await self._channel.send("mouseWheel", locals_to_params(locals()))
85+
8386

8487
class Touchscreen:
8588
def __init__(self, channel: Channel) -> None:

playwright/_impl/_network.py

Lines changed: 64 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@
1616
import base64
1717
import json
1818
import mimetypes
19+
from collections import defaultdict
1920
from pathlib import Path
2021
from types import SimpleNamespace
2122
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Union, cast
2223
from urllib import parse
2324

2425
from playwright._impl._api_structures import (
26+
Headers,
27+
HeadersArray,
2528
RemoteAddr,
2629
RequestSizes,
2730
ResourceTiming,
@@ -34,7 +37,7 @@
3437
from_nullable_channel,
3538
)
3639
from playwright._impl._event_context_manager import EventContextManagerImpl
37-
from playwright._impl._helper import ContinueParameters, Header, locals_to_params
40+
from playwright._impl._helper import ContinueParameters, locals_to_params
3841
from playwright._impl._wait_helper import WaitHelper
3942

4043
if TYPE_CHECKING: # pragma: no cover
@@ -64,8 +67,8 @@ def __init__(
6467
"responseStart": -1,
6568
"responseEnd": -1,
6669
}
67-
self._headers: List[Header] = self._initializer["headers"]
68-
self._all_headers_future: Optional[asyncio.Future[List[Header]]] = None
70+
self._provisional_headers = RawHeaders(self._initializer["headers"])
71+
self._all_headers_future: Optional[asyncio.Future[RawHeaders]] = None
6972

7073
def __repr__(self) -> str:
7174
return f"<Request url={self.url!r} method={self.method!r}>"
@@ -115,10 +118,6 @@ def post_data_buffer(self) -> Optional[bytes]:
115118
return None
116119
return base64.b64decode(b64_content)
117120

118-
@property
119-
def headers(self) -> Dict[str, str]:
120-
return headers_array_to_object(self._headers, True)
121-
122121
async def response(self) -> Optional["Response"]:
123122
return from_nullable_channel(await self._channel.send("response"))
124123

@@ -145,25 +144,27 @@ def failure(self) -> Optional[str]:
145144
def timing(self) -> ResourceTiming:
146145
return self._timing
147146

148-
async def all_headers(self) -> Dict[str, str]:
149-
return headers_array_to_object(await self._get_headers_if_needed(), True)
147+
@property
148+
def headers(self) -> Headers:
149+
return self._provisional_headers.headers()
150150

151-
async def headers_array(self) -> List[List[str]]:
152-
return list(
153-
map(
154-
lambda header: [header["name"], header["value"]],
155-
await self._get_headers_if_needed(),
156-
)
157-
)
151+
async def all_headers(self) -> Headers:
152+
return (await self._actual_headers()).headers()
153+
154+
async def headers_array(self) -> HeadersArray:
155+
return (await self._actual_headers()).headers_array()
156+
157+
async def header_value(self, name: str) -> Optional[str]:
158+
return (await self._actual_headers()).get(name)
158159

159-
async def _get_headers_if_needed(self) -> List[Header]:
160+
async def _actual_headers(self) -> "RawHeaders":
160161
if not self._all_headers_future:
161162
self._all_headers_future = asyncio.Future()
162163
response = await self.response()
163164
if not response:
164-
return self._headers
165+
return self._provisional_headers
165166
headers = await response._channel.send("rawRequestHeaders")
166-
self._all_headers_future.set_result(headers)
167+
self._all_headers_future.set_result(RawHeaders(headers))
167168
return await self._all_headers_future
168169

169170

@@ -256,10 +257,10 @@ def __init__(
256257
self._request._timing["connectEnd"] = timing["connectEnd"]
257258
self._request._timing["requestStart"] = timing["requestStart"]
258259
self._request._timing["responseStart"] = timing["responseStart"]
259-
self._headers = headers_array_to_object(
260-
cast(List[Header], self._initializer["headers"]), True
260+
self._provisional_headers = RawHeaders(
261+
cast(HeadersArray, self._initializer["headers"])
261262
)
262-
self._raw_headers_future: Optional[asyncio.Future[List[Header]]] = None
263+
self._raw_headers_future: Optional[asyncio.Future[RawHeaders]] = None
263264
self._finished_future: asyncio.Future[bool] = asyncio.Future()
264265

265266
def __repr__(self) -> str:
@@ -284,25 +285,26 @@ def status_text(self) -> str:
284285
return self._initializer["statusText"]
285286

286287
@property
287-
def headers(self) -> Dict[str, str]:
288-
return self._headers.copy()
288+
def headers(self) -> Headers:
289+
return self._provisional_headers.headers()
289290

290-
async def all_headers(self) -> Dict[str, str]:
291-
return headers_array_to_object(await self._get_headers_if_needed(), True)
291+
async def all_headers(self) -> Headers:
292+
return (await self._actual_headers()).headers()
292293

293-
async def headers_array(self) -> List[List[str]]:
294-
return list(
295-
map(
296-
lambda header: [header["name"], header["value"]],
297-
await self._get_headers_if_needed(),
298-
)
299-
)
294+
async def headers_array(self) -> HeadersArray:
295+
return (await self._actual_headers()).headers_array()
296+
297+
async def header_value(self, name: str) -> Optional[str]:
298+
return (await self._actual_headers()).get(name)
299+
300+
async def header_values(self, name: str) -> List[str]:
301+
return (await self._actual_headers()).get_all(name)
300302

301-
async def _get_headers_if_needed(self) -> List[Header]:
303+
async def _actual_headers(self) -> "RawHeaders":
302304
if not self._raw_headers_future:
303305
self._raw_headers_future = asyncio.Future()
304-
headers = cast(List[Header], await self._channel.send("rawResponseHeaders"))
305-
self._raw_headers_future.set_result(headers)
306+
headers = cast(HeadersArray, await self._channel.send("rawResponseHeaders"))
307+
self._raw_headers_future.set_result(RawHeaders(headers))
306308
return await self._raw_headers_future
307309

308310
async def server_addr(self) -> Optional[RemoteAddr]:
@@ -420,12 +422,32 @@ def _on_close(self) -> None:
420422
self.emit(WebSocket.Events.Close, self)
421423

422424

423-
def serialize_headers(headers: Dict[str, str]) -> List[Header]:
425+
def serialize_headers(headers: Dict[str, str]) -> HeadersArray:
424426
return [{"name": name, "value": value} for name, value in headers.items()]
425427

426428

427-
def headers_array_to_object(headers: List[Header], lower_case: bool) -> Dict[str, str]:
428-
return {
429-
(header["name"].lower() if lower_case else header["name"]): header["value"]
430-
for header in headers
431-
}
429+
class RawHeaders:
430+
def __init__(self, headers: HeadersArray) -> None:
431+
self._headers_array = headers
432+
self._headers_map: Dict[str, Dict[str, bool]] = defaultdict(dict)
433+
for header in headers:
434+
self._headers_map[header["name"].lower()][header["value"]] = True
435+
436+
def get(self, name: str) -> Optional[str]:
437+
values = self.get_all(name)
438+
if not values:
439+
return None
440+
separator = "\n" if name.lower() == "set-cookie" else ", "
441+
return separator.join(values)
442+
443+
def get_all(self, name: str) -> List[str]:
444+
return list(self._headers_map[name.lower()].keys())
445+
446+
def headers(self) -> Dict[str, str]:
447+
result = {}
448+
for name in self._headers_map.keys():
449+
result[name] = cast(str, self.get(name))
450+
return result
451+
452+
def headers_array(self) -> HeadersArray:
453+
return self._headers_array

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

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

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


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy