Skip to content

Commit f9abeae

Browse files
fix: run blocking call in threadpool (microsoft#780)
1 parent 39a9cb1 commit f9abeae

File tree

10 files changed

+73
-45
lines changed

10 files changed

+73
-45
lines changed

playwright/_impl/_browser.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
from playwright._impl._helper import (
3232
ColorScheme,
3333
ReducedMotion,
34+
async_readfile,
3435
is_safe_close_error,
3536
locals_to_params,
3637
)
@@ -106,7 +107,7 @@ async def new_context(
106107
storageState: Union[StorageState, str, Path] = None,
107108
) -> BrowserContext:
108109
params = locals_to_params(locals())
109-
normalize_context_params(self._connection._is_sync, params)
110+
await normalize_context_params(self._connection._is_sync, params)
110111

111112
channel = await self._channel.send("newContext", params)
112113
context = from_channel(channel)
@@ -190,7 +191,7 @@ async def stop_tracing(self) -> bytes:
190191
return base64.b64decode(encoded_binary)
191192

192193

193-
def normalize_context_params(is_sync: bool, params: Dict) -> None:
194+
async def normalize_context_params(is_sync: bool, params: Dict) -> None:
194195
params["sdkLanguage"] = "python" if is_sync else "python-async"
195196
if params.get("noViewport"):
196197
del params["noViewport"]
@@ -214,5 +215,6 @@ def normalize_context_params(is_sync: bool, params: Dict) -> None:
214215
if "storageState" in params:
215216
storageState = params["storageState"]
216217
if not isinstance(storageState, dict):
217-
with open(storageState, "r") as f:
218-
params["storageState"] = json.load(f)
218+
params["storageState"] = json.loads(
219+
(await async_readfile(storageState)).decode()
220+
)

playwright/_impl/_browser_context.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
TimeoutSettings,
3535
URLMatch,
3636
URLMatcher,
37+
async_readfile,
38+
async_writefile,
3739
is_safe_close_error,
3840
locals_to_params,
3941
)
@@ -214,8 +216,7 @@ async def add_init_script(
214216
self, script: str = None, path: Union[str, Path] = None
215217
) -> None:
216218
if path:
217-
with open(path, "r") as file:
218-
script = file.read()
219+
script = (await async_readfile(path)).decode()
219220
if not isinstance(script, str):
220221
raise Error("Either path or script parameter must be specified")
221222
await self._channel.send("addInitScript", dict(source=script))
@@ -298,8 +299,7 @@ async def _pause(self) -> None:
298299
async def storage_state(self, path: Union[str, Path] = None) -> StorageState:
299300
result = await self._channel.send_return_as_dict("storageState")
300301
if path:
301-
with open(path, "w") as f:
302-
json.dump(result, f)
302+
await async_writefile(path, json.dumps(result))
303303
return result
304304

305305
async def wait_for_event(

playwright/_impl/_browser_type.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ async def launch_persistent_context(
135135
) -> BrowserContext:
136136
userDataDir = str(Path(userDataDir))
137137
params = locals_to_params(locals())
138-
normalize_context_params(self._connection._is_sync, params)
138+
await normalize_context_params(self._connection._is_sync, params)
139139
normalize_launch_params(params)
140140
try:
141141
context = from_channel(

playwright/_impl/_element_handle.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from playwright._impl._helper import (
2424
KeyboardModifier,
2525
MouseButton,
26+
async_writefile,
2627
locals_to_params,
2728
make_dirs_for_file,
2829
)
@@ -187,7 +188,7 @@ async def set_input_files(
187188
noWaitAfter: bool = None,
188189
) -> None:
189190
params = locals_to_params(locals())
190-
params["files"] = normalize_file_payloads(files)
191+
params["files"] = await normalize_file_payloads(files)
191192
await self._channel.send("setInputFiles", params)
192193

193194
async def focus(self) -> None:
@@ -249,8 +250,7 @@ async def screenshot(
249250
decoded_binary = base64.b64decode(encoded_binary)
250251
if path:
251252
make_dirs_for_file(path)
252-
with open(path, "wb") as fd:
253-
fd.write(decoded_binary)
253+
await async_writefile(path, decoded_binary)
254254
return decoded_binary
255255

256256
async def query_selector(self, selector: str) -> Optional["ElementHandle"]:

playwright/_impl/_file_chooser.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from typing import TYPE_CHECKING, List, Union
1919

2020
from playwright._impl._api_structures import FilePayload
21+
from playwright._impl._helper import async_readfile
2122

2223
if TYPE_CHECKING: # pragma: no cover
2324
from playwright._impl._element_handle import ElementHandle
@@ -57,20 +58,19 @@ async def set_files(
5758
await self._element_handle.set_input_files(files, timeout, noWaitAfter)
5859

5960

60-
def normalize_file_payloads(
61+
async def normalize_file_payloads(
6162
files: Union[str, Path, FilePayload, List[Union[str, Path]], List[FilePayload]]
6263
) -> List:
6364
file_list = files if isinstance(files, list) else [files]
6465
file_payloads: List = []
6566
for item in file_list:
66-
if isinstance(item, str) or isinstance(item, Path):
67-
with open(item, mode="rb") as fd:
68-
file_payloads.append(
69-
{
70-
"name": os.path.basename(item),
71-
"buffer": base64.b64encode(fd.read()).decode(),
72-
}
73-
)
67+
if isinstance(item, (str, Path)):
68+
file_payloads.append(
69+
{
70+
"name": os.path.basename(item),
71+
"buffer": base64.b64encode(await async_readfile(item)).decode(),
72+
}
73+
)
7474
else:
7575
file_payloads.append(
7676
{

playwright/_impl/_frame.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
MouseButton,
3737
URLMatch,
3838
URLMatcher,
39+
async_readfile,
3940
locals_to_params,
4041
monotonic_time,
4142
)
@@ -360,21 +361,26 @@ async def add_script_tag(
360361
) -> ElementHandle:
361362
params = locals_to_params(locals())
362363
if path:
363-
with open(path, "r") as file:
364-
params["content"] = file.read() + "\n//# sourceURL=" + str(Path(path))
365-
del params["path"]
364+
params["content"] = (
365+
(await async_readfile(path)).decode()
366+
+ "\n//# sourceURL="
367+
+ str(Path(path))
368+
)
369+
del params["path"]
366370
return from_channel(await self._channel.send("addScriptTag", params))
367371

368372
async def add_style_tag(
369373
self, url: str = None, path: Union[str, Path] = None, content: str = None
370374
) -> ElementHandle:
371375
params = locals_to_params(locals())
372376
if path:
373-
with open(path, "r") as file:
374-
params["content"] = (
375-
file.read() + "\n/*# sourceURL=" + str(Path(path)) + "*/"
376-
)
377-
del params["path"]
377+
params["content"] = (
378+
(await async_readfile(path)).decode()
379+
+ "\n/*# sourceURL="
380+
+ str(Path(path))
381+
+ "*/"
382+
)
383+
del params["path"]
378384
return from_channel(await self._channel.send("addStyleTag", params))
379385

380386
async def click(
@@ -492,7 +498,7 @@ async def set_input_files(
492498
noWaitAfter: bool = None,
493499
) -> None:
494500
params = locals_to_params(locals())
495-
params["files"] = normalize_file_payloads(files)
501+
params["files"] = await normalize_file_payloads(files)
496502
await self._channel.send("setInputFiles", params)
497503

498504
async def type(

playwright/_impl/_helper.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14-
14+
import asyncio
1515
import fnmatch
1616
import math
1717
import os
@@ -231,3 +231,21 @@ def make_dirs_for_file(path: Union[Path, str]) -> None:
231231
if not os.path.isabs(path):
232232
path = Path.cwd() / path
233233
os.makedirs(os.path.dirname(path), exist_ok=True)
234+
235+
236+
async def async_writefile(file: Union[str, Path], data: Union[str, bytes]) -> None:
237+
def inner() -> None:
238+
with open(file, "w" if isinstance(data, str) else "wb") as fh:
239+
fh.write(data)
240+
241+
loop = asyncio.get_running_loop()
242+
await loop.run_in_executor(None, inner)
243+
244+
245+
async def async_readfile(file: Union[str, Path]) -> bytes:
246+
def inner() -> bytes:
247+
with open(file, "rb") as fh:
248+
return fh.read()
249+
250+
loop = asyncio.get_running_loop()
251+
return await loop.run_in_executor(None, inner)

playwright/_impl/_page.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@
5454
URLMatcher,
5555
URLMatchRequest,
5656
URLMatchResponse,
57+
async_readfile,
58+
async_writefile,
5759
is_safe_close_error,
5860
locals_to_params,
5961
make_dirs_for_file,
@@ -498,8 +500,7 @@ async def add_init_script(
498500
self, script: str = None, path: Union[str, Path] = None
499501
) -> None:
500502
if path:
501-
with open(path, "r") as file:
502-
script = file.read()
503+
script = (await async_readfile(path)).decode()
503504
if not isinstance(script, str):
504505
raise Error("Either path or script parameter must be specified")
505506
await self._channel.send("addInitScript", dict(source=script))
@@ -542,8 +543,7 @@ async def screenshot(
542543
decoded_binary = base64.b64decode(encoded_binary)
543544
if path:
544545
make_dirs_for_file(path)
545-
with open(path, "wb") as fd:
546-
fd.write(decoded_binary)
546+
await async_writefile(path, decoded_binary)
547547
return decoded_binary
548548

549549
async def title(self) -> str:
@@ -751,8 +751,7 @@ async def pdf(
751751
decoded_binary = base64.b64decode(encoded_binary)
752752
if path:
753753
make_dirs_for_file(path)
754-
with open(path, "wb") as fd:
755-
fd.write(decoded_binary)
754+
await async_writefile(path, decoded_binary)
756755
return decoded_binary
757756

758757
@property

playwright/_impl/_selectors.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
from playwright._impl._api_types import Error
1919
from playwright._impl._connection import ChannelOwner
20+
from playwright._impl._helper import async_readfile
2021

2122

2223
class Selectors(ChannelOwner):
@@ -35,8 +36,7 @@ async def register(
3536
if not script and not path:
3637
raise Error("Either source or path should be specified")
3738
if path:
38-
with open(path, "r") as file:
39-
script = file.read()
39+
script = (await async_readfile(path)).decode()
4040
params: Dict = dict(name=name, source=script)
4141
if contentScript:
4242
params["contentScript"] = True

playwright/_impl/_stream.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,12 @@ def __init__(
2626
super().__init__(parent, type, guid, initializer)
2727

2828
async def save_as(self, path: Union[str, Path]) -> None:
29-
with open(path, mode="wb") as file:
30-
while True:
31-
binary = await self._channel.send("read")
32-
if not binary:
33-
break
34-
file.write(base64.b64decode(binary))
29+
file = await self._loop.run_in_executor(None, lambda: open(path, "wb"))
30+
while True:
31+
binary = await self._channel.send("read")
32+
if not binary:
33+
break
34+
await self._loop.run_in_executor(
35+
None, lambda: file.write(base64.b64decode(binary))
36+
)
37+
await self._loop.run_in_executor(None, lambda: file.close())

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