Skip to content

Commit 6565810

Browse files
authored
chore(roll): roll Playwright to v1.46 (microsoft#2499)
1 parent c13cd03 commit 6565810

22 files changed

+1174
-364
lines changed

README.md

Lines changed: 3 additions & 3 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 -->127.0.6533.17<!-- GEN:stop --> ||||
8-
| WebKit <!-- GEN:webkit-version -->17.4<!-- GEN:stop --> ||||
9-
| Firefox <!-- GEN:firefox-version -->127.0<!-- GEN:stop --> ||||
7+
| Chromium <!-- GEN:chromium-version -->128.0.6613.7<!-- GEN:stop --> ||||
8+
| WebKit <!-- GEN:webkit-version -->18.0<!-- GEN:stop --> ||||
9+
| Firefox <!-- GEN:firefox-version -->128.0<!-- GEN:stop --> ||||
1010

1111
## Documentation
1212

playwright/_impl/_api_structures.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
from pathlib import Path
1516
from typing import Any, Dict, List, Literal, Optional, Sequence, TypedDict, Union
1617

1718
# These are the structures that we like keeping in a JSON form for their potential
@@ -100,6 +101,14 @@ class StorageState(TypedDict, total=False):
100101
origins: List[OriginState]
101102

102103

104+
class ClientCertificate(TypedDict, total=False):
105+
origin: str
106+
certPath: Optional[Union[str, Path]]
107+
keyPath: Optional[Union[str, Path]]
108+
pfxPath: Optional[Union[str, Path]]
109+
passphrase: Optional[str]
110+
111+
103112
class ResourceTiming(TypedDict):
104113
startTime: float
105114
domainLookupStart: float

playwright/_impl/_browser.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from typing import TYPE_CHECKING, Dict, List, Optional, Pattern, Sequence, Union, cast
1919

2020
from playwright._impl._api_structures import (
21+
ClientCertificate,
2122
Geolocation,
2223
HttpCredentials,
2324
ProxySettings,
@@ -41,7 +42,7 @@
4142
make_dirs_for_file,
4243
prepare_record_har_options,
4344
)
44-
from playwright._impl._network import serialize_headers
45+
from playwright._impl._network import serialize_headers, to_client_certificates_protocol
4546
from playwright._impl._page import Page
4647

4748
if TYPE_CHECKING: # pragma: no cover
@@ -120,6 +121,7 @@ async def new_context(
120121
recordHarUrlFilter: Union[Pattern[str], str] = None,
121122
recordHarMode: HarMode = None,
122123
recordHarContent: HarContentPolicy = None,
124+
clientCertificates: List[ClientCertificate] = None,
123125
) -> BrowserContext:
124126
params = locals_to_params(locals())
125127
await prepare_browser_context_params(params)
@@ -165,6 +167,7 @@ async def new_page(
165167
recordHarUrlFilter: Union[Pattern[str], str] = None,
166168
recordHarMode: HarMode = None,
167169
recordHarContent: HarContentPolicy = None,
170+
clientCertificates: List[ClientCertificate] = None,
168171
) -> Page:
169172
params = locals_to_params(locals())
170173

@@ -253,3 +256,8 @@ async def prepare_browser_context_params(params: Dict) -> None:
253256
params["forcedColors"] = "no-override"
254257
if "acceptDownloads" in params:
255258
params["acceptDownloads"] = "accept" if params["acceptDownloads"] else "deny"
259+
260+
if "clientCertificates" in params:
261+
params["clientCertificates"] = await to_client_certificates_protocol(
262+
params["clientCertificates"]
263+
)

playwright/_impl/_browser_type.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@
1515
import asyncio
1616
import pathlib
1717
from pathlib import Path
18-
from typing import TYPE_CHECKING, Dict, Optional, Pattern, Sequence, Union, cast
18+
from typing import TYPE_CHECKING, Dict, List, Optional, Pattern, Sequence, Union, cast
1919

2020
from playwright._impl._api_structures import (
21+
ClientCertificate,
2122
Geolocation,
2223
HttpCredentials,
2324
ProxySettings,
@@ -147,6 +148,7 @@ async def launch_persistent_context(
147148
recordHarUrlFilter: Union[Pattern[str], str] = None,
148149
recordHarMode: HarMode = None,
149150
recordHarContent: HarContentPolicy = None,
151+
clientCertificates: List[ClientCertificate] = None,
150152
) -> BrowserContext:
151153
userDataDir = str(Path(userDataDir)) if userDataDir else ""
152154
params = locals_to_params(locals())

playwright/_impl/_fetch.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import playwright._impl._network as network
2323
from playwright._impl._api_structures import (
24+
ClientCertificate,
2425
FilePayload,
2526
FormField,
2627
Headers,
@@ -42,7 +43,7 @@
4243
object_to_array,
4344
to_impl,
4445
)
45-
from playwright._impl._network import serialize_headers
46+
from playwright._impl._network import serialize_headers, to_client_certificates_protocol
4647
from playwright._impl._tracing import Tracing
4748

4849
if typing.TYPE_CHECKING:
@@ -71,6 +72,7 @@ async def new_context(
7172
userAgent: str = None,
7273
timeout: float = None,
7374
storageState: Union[StorageState, str, Path] = None,
75+
clientCertificates: List[ClientCertificate] = None,
7476
) -> "APIRequestContext":
7577
params = locals_to_params(locals())
7678
if "storageState" in params:
@@ -81,6 +83,9 @@ async def new_context(
8183
)
8284
if "extraHTTPHeaders" in params:
8385
params["extraHTTPHeaders"] = serialize_headers(params["extraHTTPHeaders"])
86+
params["clientCertificates"] = await to_client_certificates_protocol(
87+
params.get("clientCertificates")
88+
)
8489
context = cast(
8590
APIRequestContext,
8691
from_channel(await self.playwright._channel.send("newRequest", params)),
@@ -118,6 +123,7 @@ async def delete(
118123
failOnStatusCode: bool = None,
119124
ignoreHTTPSErrors: bool = None,
120125
maxRedirects: int = None,
126+
maxRetries: int = None,
121127
) -> "APIResponse":
122128
return await self.fetch(
123129
url,
@@ -131,6 +137,7 @@ async def delete(
131137
failOnStatusCode=failOnStatusCode,
132138
ignoreHTTPSErrors=ignoreHTTPSErrors,
133139
maxRedirects=maxRedirects,
140+
maxRetries=maxRetries,
134141
)
135142

136143
async def head(
@@ -145,6 +152,7 @@ async def head(
145152
failOnStatusCode: bool = None,
146153
ignoreHTTPSErrors: bool = None,
147154
maxRedirects: int = None,
155+
maxRetries: int = None,
148156
) -> "APIResponse":
149157
return await self.fetch(
150158
url,
@@ -158,6 +166,7 @@ async def head(
158166
failOnStatusCode=failOnStatusCode,
159167
ignoreHTTPSErrors=ignoreHTTPSErrors,
160168
maxRedirects=maxRedirects,
169+
maxRetries=maxRetries,
161170
)
162171

163172
async def get(
@@ -172,6 +181,7 @@ async def get(
172181
failOnStatusCode: bool = None,
173182
ignoreHTTPSErrors: bool = None,
174183
maxRedirects: int = None,
184+
maxRetries: int = None,
175185
) -> "APIResponse":
176186
return await self.fetch(
177187
url,
@@ -185,6 +195,7 @@ async def get(
185195
failOnStatusCode=failOnStatusCode,
186196
ignoreHTTPSErrors=ignoreHTTPSErrors,
187197
maxRedirects=maxRedirects,
198+
maxRetries=maxRetries,
188199
)
189200

190201
async def patch(
@@ -199,6 +210,7 @@ async def patch(
199210
failOnStatusCode: bool = None,
200211
ignoreHTTPSErrors: bool = None,
201212
maxRedirects: int = None,
213+
maxRetries: int = None,
202214
) -> "APIResponse":
203215
return await self.fetch(
204216
url,
@@ -212,6 +224,7 @@ async def patch(
212224
failOnStatusCode=failOnStatusCode,
213225
ignoreHTTPSErrors=ignoreHTTPSErrors,
214226
maxRedirects=maxRedirects,
227+
maxRetries=maxRetries,
215228
)
216229

217230
async def put(
@@ -226,6 +239,7 @@ async def put(
226239
failOnStatusCode: bool = None,
227240
ignoreHTTPSErrors: bool = None,
228241
maxRedirects: int = None,
242+
maxRetries: int = None,
229243
) -> "APIResponse":
230244
return await self.fetch(
231245
url,
@@ -239,6 +253,7 @@ async def put(
239253
failOnStatusCode=failOnStatusCode,
240254
ignoreHTTPSErrors=ignoreHTTPSErrors,
241255
maxRedirects=maxRedirects,
256+
maxRetries=maxRetries,
242257
)
243258

244259
async def post(
@@ -253,6 +268,7 @@ async def post(
253268
failOnStatusCode: bool = None,
254269
ignoreHTTPSErrors: bool = None,
255270
maxRedirects: int = None,
271+
maxRetries: int = None,
256272
) -> "APIResponse":
257273
return await self.fetch(
258274
url,
@@ -266,6 +282,7 @@ async def post(
266282
failOnStatusCode=failOnStatusCode,
267283
ignoreHTTPSErrors=ignoreHTTPSErrors,
268284
maxRedirects=maxRedirects,
285+
maxRetries=maxRetries,
269286
)
270287

271288
async def fetch(
@@ -281,6 +298,7 @@ async def fetch(
281298
failOnStatusCode: bool = None,
282299
ignoreHTTPSErrors: bool = None,
283300
maxRedirects: int = None,
301+
maxRetries: int = None,
284302
) -> "APIResponse":
285303
url = urlOrRequest if isinstance(urlOrRequest, str) else None
286304
request = (
@@ -304,6 +322,7 @@ async def fetch(
304322
failOnStatusCode,
305323
ignoreHTTPSErrors,
306324
maxRedirects,
325+
maxRetries,
307326
)
308327

309328
async def _inner_fetch(
@@ -320,6 +339,7 @@ async def _inner_fetch(
320339
failOnStatusCode: bool = None,
321340
ignoreHTTPSErrors: bool = None,
322341
maxRedirects: int = None,
342+
maxRetries: int = None,
323343
) -> "APIResponse":
324344
if self._close_reason:
325345
raise TargetClosedError(self._close_reason)
@@ -329,6 +349,9 @@ async def _inner_fetch(
329349
assert (
330350
maxRedirects is None or maxRedirects >= 0
331351
), "'max_redirects' must be greater than or equal to '0'"
352+
assert (
353+
maxRetries is None or maxRetries >= 0
354+
), "'max_retries' must be greater than or equal to '0'"
332355
url = url or (request.url if request else url)
333356
method = method or (request.method if request else "GET")
334357
# Cannot call allHeaders() here as the request may be paused inside route handler.
@@ -392,6 +415,7 @@ async def _inner_fetch(
392415
"failOnStatusCode": failOnStatusCode,
393416
"ignoreHTTPSErrors": ignoreHTTPSErrors,
394417
"maxRedirects": maxRedirects,
418+
"maxRetries": maxRetries,
395419
},
396420
)
397421
return APIResponse(self, response)

playwright/_impl/_js_handle.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@
1515
import collections.abc
1616
import datetime
1717
import math
18+
import traceback
1819
from pathlib import Path
1920
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
2021
from urllib.parse import ParseResult, urlparse, urlunparse
2122

2223
from playwright._impl._connection import Channel, ChannelOwner, from_channel
23-
from playwright._impl._errors import is_target_closed_error
24+
from playwright._impl._errors import Error, is_target_closed_error
2425
from playwright._impl._map import Map
2526

2627
if TYPE_CHECKING: # pragma: no cover
@@ -140,6 +141,20 @@ def serialize_value(
140141
value.astimezone(datetime.timezone.utc), "%Y-%m-%dT%H:%M:%S.%fZ"
141142
)
142143
}
144+
if isinstance(value, Exception):
145+
return {
146+
"e": {
147+
"m": str(value),
148+
"n": (value.name or "")
149+
if isinstance(value, Error)
150+
else value.__class__.__name__,
151+
"s": (value.stack or "")
152+
if isinstance(value, Error)
153+
else "".join(
154+
traceback.format_exception(type(value), value=value, tb=None)
155+
),
156+
}
157+
}
143158
if isinstance(value, bool):
144159
return {"b": value}
145160
if isinstance(value, (int, float)):
@@ -207,6 +222,12 @@ def parse_value(value: Any, refs: Optional[Dict[int, Any]] = None) -> Any:
207222
if "bi" in value:
208223
return int(value["bi"])
209224

225+
if "e" in value:
226+
error = Error(value["e"]["m"])
227+
error._name = value["e"]["n"]
228+
error._stack = value["e"]["s"]
229+
return error
230+
210231
if "a" in value:
211232
a: List = []
212233
refs[value["id"]] = a

playwright/_impl/_network.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
from urllib import parse
3737

3838
from playwright._impl._api_structures import (
39+
ClientCertificate,
3940
Headers,
4041
HeadersArray,
4142
RemoteAddr,
@@ -50,7 +51,7 @@
5051
)
5152
from playwright._impl._errors import Error
5253
from playwright._impl._event_context_manager import EventContextManagerImpl
53-
from playwright._impl._helper import locals_to_params
54+
from playwright._impl._helper import async_readfile, locals_to_params
5455
from playwright._impl._waiter import Waiter
5556

5657
if TYPE_CHECKING: # pragma: no cover
@@ -83,6 +84,34 @@ def serialize_headers(headers: Dict[str, str]) -> HeadersArray:
8384
]
8485

8586

87+
async def to_client_certificates_protocol(
88+
clientCertificates: Optional[List[ClientCertificate]],
89+
) -> Optional[List[Dict[str, str]]]:
90+
if not clientCertificates:
91+
return None
92+
out = []
93+
for clientCertificate in clientCertificates:
94+
out_record = {
95+
"origin": clientCertificate["origin"],
96+
}
97+
if passphrase := clientCertificate.get("passphrase"):
98+
out_record["passphrase"] = passphrase
99+
if pfx_path := clientCertificate.get("pfxPath"):
100+
out_record["pfx"] = base64.b64encode(
101+
await async_readfile(pfx_path)
102+
).decode()
103+
if cert_path := clientCertificate.get("certPath"):
104+
out_record["cert"] = base64.b64encode(
105+
await async_readfile(cert_path)
106+
).decode()
107+
if key_path := clientCertificate.get("keyPath"):
108+
out_record["key"] = base64.b64encode(
109+
await async_readfile(key_path)
110+
).decode()
111+
out.append(out_record)
112+
return out
113+
114+
86115
class Request(ChannelOwner):
87116
def __init__(
88117
self, parent: ChannelOwner, type: str, guid: str, initializer: Dict
@@ -410,6 +439,7 @@ async def fetch(
410439
headers: Dict[str, str] = None,
411440
postData: Union[Any, str, bytes] = None,
412441
maxRedirects: int = None,
442+
maxRetries: int = None,
413443
timeout: float = None,
414444
) -> "APIResponse":
415445
return await self._connection.wrap_api_call(
@@ -420,6 +450,7 @@ async def fetch(
420450
headers,
421451
postData,
422452
maxRedirects=maxRedirects,
453+
maxRetries=maxRetries,
423454
timeout=timeout,
424455
)
425456
)

0 commit comments

Comments
 (0)