Skip to content

Commit b5f8151

Browse files
authored
api: use bytes, not string|bytes for file input buffers (microsoft#254)
1 parent f565f59 commit b5f8151

File tree

10 files changed

+113
-88
lines changed

10 files changed

+113
-88
lines changed

playwright/async_api.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,7 +1186,7 @@ async def setInputFiles(
11861186
11871187
Parameters
11881188
----------
1189-
files : Union[str, pathlib.Path, {"name": str, "mimeType": str, "buffer": Union[bytes, str]}, List[str], List[pathlib.Path], List[{"name": str, "mimeType": str, "buffer": Union[bytes, str]}]]
1189+
files : Union[str, pathlib.Path, {"name": str, "mimeType": str, "buffer": bytes}, List[str], List[pathlib.Path], List[{"name": str, "mimeType": str, "buffer": bytes}]]
11901190
timeout : Optional[int]
11911191
Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the browserContext.setDefaultTimeout(timeout) or page.setDefaultTimeout(timeout) methods.
11921192
noWaitAfter : Optional[bool]
@@ -1662,7 +1662,7 @@ async def setFiles(
16621662
16631663
Parameters
16641664
----------
1665-
files : Union[str, {"name": str, "mimeType": str, "buffer": Union[bytes, str]}, List[str], List[{"name": str, "mimeType": str, "buffer": Union[bytes, str]}]]
1665+
files : Union[str, {"name": str, "mimeType": str, "buffer": bytes}, List[str], List[{"name": str, "mimeType": str, "buffer": bytes}]]
16661666
timeout : Optional[int]
16671667
Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the browserContext.setDefaultTimeout(timeout) or page.setDefaultTimeout(timeout) methods.
16681668
noWaitAfter : Optional[bool]
@@ -2588,7 +2588,7 @@ async def setInputFiles(
25882588
----------
25892589
selector : str
25902590
A selector to search for element to click. If there are multiple elements satisfying the selector, the first will be clicked. See working with selectors for more details.
2591-
files : Union[str, pathlib.Path, {"name": str, "mimeType": str, "buffer": Union[bytes, str]}, List[str], List[pathlib.Path], List[{"name": str, "mimeType": str, "buffer": Union[bytes, str]}]]
2591+
files : Union[str, pathlib.Path, {"name": str, "mimeType": str, "buffer": bytes}, List[str], List[pathlib.Path], List[{"name": str, "mimeType": str, "buffer": bytes}]]
25922592
timeout : Optional[int]
25932593
Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the browserContext.setDefaultTimeout(timeout) or page.setDefaultTimeout(timeout) methods.
25942594
noWaitAfter : Optional[bool]
@@ -4648,7 +4648,7 @@ async def setInputFiles(
46484648
----------
46494649
selector : str
46504650
A selector to search for element to click. If there are multiple elements satisfying the selector, the first will be clicked. See working with selectors for more details.
4651-
files : Union[str, {"name": str, "mimeType": str, "buffer": Union[bytes, str]}, List[str], List[{"name": str, "mimeType": str, "buffer": Union[bytes, str]}]]
4651+
files : Union[str, {"name": str, "mimeType": str, "buffer": bytes}, List[str], List[{"name": str, "mimeType": str, "buffer": bytes}]]
46524652
timeout : Optional[int]
46534653
Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the browserContext.setDefaultTimeout(timeout) or page.setDefaultTimeout(timeout) methods.
46544654
noWaitAfter : Optional[bool]

playwright/element_handle.py

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
MouseButton,
2828
MousePosition,
2929
SelectOption,
30+
SetFilePayload,
3031
locals_to_params,
3132
)
3233
from playwright.js_handle import (
@@ -291,22 +292,27 @@ def convert_select_option_values(arg: ValuesToSelect) -> Any:
291292

292293
def normalize_file_payloads(
293294
files: Union[str, Path, FilePayload, List[str], List[Path], List[FilePayload]]
294-
) -> List[FilePayload]:
295+
) -> List[SetFilePayload]:
295296
file_list = files if isinstance(files, list) else [files]
296-
file_payloads: List[FilePayload] = []
297+
file_payloads: List[SetFilePayload] = []
297298
for item in file_list:
298299
if isinstance(item, str) or isinstance(item, Path):
299300
with open(item, mode="rb") as fd:
300-
file: FilePayload = {
301-
"name": os.path.basename(item),
302-
"mimeType": mimetypes.guess_type(str(Path(item)))[0]
303-
or "application/octet-stream",
304-
"buffer": base64.b64encode(fd.read()).decode(),
305-
}
306-
file_payloads.append(file)
301+
file_payloads.append(
302+
{
303+
"name": os.path.basename(item),
304+
"mimeType": mimetypes.guess_type(str(Path(item)))[0]
305+
or "application/octet-stream",
306+
"buffer": base64.b64encode(fd.read()).decode(),
307+
}
308+
)
307309
else:
308-
if isinstance(item["buffer"], bytes):
309-
item["buffer"] = base64.b64encode(item["buffer"]).decode()
310-
file_payloads.append(item)
310+
file_payloads.append(
311+
{
312+
"name": item["name"],
313+
"mimeType": item["mimeType"],
314+
"buffer": base64.b64encode(item["buffer"]).decode(),
315+
}
316+
)
311317

312318
return file_payloads

playwright/helper.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,13 @@ class MousePosition(TypedDict):
5959
class FilePayload(TypedDict):
6060
name: str
6161
mimeType: str
62-
buffer: Union[bytes, str]
62+
buffer: bytes
63+
64+
65+
class SetFilePayload(TypedDict):
66+
name: str
67+
mimeType: str
68+
buffer: str
6369

6470

6571
class SelectOption(TypedDict):

playwright/sync_api.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1224,7 +1224,7 @@ def setInputFiles(
12241224
12251225
Parameters
12261226
----------
1227-
files : Union[str, pathlib.Path, {"name": str, "mimeType": str, "buffer": Union[bytes, str]}, List[str], List[pathlib.Path], List[{"name": str, "mimeType": str, "buffer": Union[bytes, str]}]]
1227+
files : Union[str, pathlib.Path, {"name": str, "mimeType": str, "buffer": bytes}, List[str], List[pathlib.Path], List[{"name": str, "mimeType": str, "buffer": bytes}]]
12281228
timeout : Optional[int]
12291229
Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the browserContext.setDefaultTimeout(timeout) or page.setDefaultTimeout(timeout) methods.
12301230
noWaitAfter : Optional[bool]
@@ -1718,7 +1718,7 @@ def setFiles(
17181718
17191719
Parameters
17201720
----------
1721-
files : Union[str, {"name": str, "mimeType": str, "buffer": Union[bytes, str]}, List[str], List[{"name": str, "mimeType": str, "buffer": Union[bytes, str]}]]
1721+
files : Union[str, {"name": str, "mimeType": str, "buffer": bytes}, List[str], List[{"name": str, "mimeType": str, "buffer": bytes}]]
17221722
timeout : Optional[int]
17231723
Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the browserContext.setDefaultTimeout(timeout) or page.setDefaultTimeout(timeout) methods.
17241724
noWaitAfter : Optional[bool]
@@ -2683,7 +2683,7 @@ def setInputFiles(
26832683
----------
26842684
selector : str
26852685
A selector to search for element to click. If there are multiple elements satisfying the selector, the first will be clicked. See working with selectors for more details.
2686-
files : Union[str, pathlib.Path, {"name": str, "mimeType": str, "buffer": Union[bytes, str]}, List[str], List[pathlib.Path], List[{"name": str, "mimeType": str, "buffer": Union[bytes, str]}]]
2686+
files : Union[str, pathlib.Path, {"name": str, "mimeType": str, "buffer": bytes}, List[str], List[pathlib.Path], List[{"name": str, "mimeType": str, "buffer": bytes}]]
26872687
timeout : Optional[int]
26882688
Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the browserContext.setDefaultTimeout(timeout) or page.setDefaultTimeout(timeout) methods.
26892689
noWaitAfter : Optional[bool]
@@ -4835,7 +4835,7 @@ def setInputFiles(
48354835
----------
48364836
selector : str
48374837
A selector to search for element to click. If there are multiple elements satisfying the selector, the first will be clicked. See working with selectors for more details.
4838-
files : Union[str, {"name": str, "mimeType": str, "buffer": Union[bytes, str]}, List[str], List[{"name": str, "mimeType": str, "buffer": Union[bytes, str]}]]
4838+
files : Union[str, {"name": str, "mimeType": str, "buffer": bytes}, List[str], List[{"name": str, "mimeType": str, "buffer": bytes}]]
48394839
timeout : Optional[int]
48404840
Maximum time in milliseconds, defaults to 30 seconds, pass `0` to disable timeout. The default value can be changed by using the browserContext.setDefaultTimeout(timeout) or page.setDefaultTimeout(timeout) methods.
48414841
noWaitAfter : Optional[bool]

scripts/expected_api_mismatch.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,10 @@ Parameter not documented: BrowserContext.addInitScript(path=)
7474
Parameter not documented: Page.addInitScript(path=)
7575

7676
# File payload
77-
Parameter type mismatch in FileChooser.setFiles(files=): documented as Union[str, List[str], Dict, List[Dict]], code has Union[str, {"name": str, "mimeType": str, "buffer": Union[bytes, str]}, List[str], List[{"name": str, "mimeType": str, "buffer": Union[bytes, str]}]]
78-
Parameter type mismatch in Page.setInputFiles(files=): documented as Union[str, List[str], Dict, List[Dict]], code has Union[str, {"name": str, "mimeType": str, "buffer": Union[bytes, str]}, List[str], List[{"name": str, "mimeType": str, "buffer": Union[bytes, str]}]]
79-
Parameter type mismatch in ElementHandle.setInputFiles(files=): documented as Union[str, List[str], Dict, List[Dict]], code has Union[str, pathlib.Path, {"name": str, "mimeType": str, "buffer": Union[bytes, str]}, List[str], List[pathlib.Path], List[{"name": str, "mimeType": str, "buffer": Union[bytes, str]}]]
80-
Parameter type mismatch in Frame.setInputFiles(files=): documented as Union[str, List[str], Dict, List[Dict]], code has Union[str, pathlib.Path, {"name": str, "mimeType": str, "buffer": Union[bytes, str]}, List[str], List[pathlib.Path], List[{"name": str, "mimeType": str, "buffer": Union[bytes, str]}]]
77+
Parameter type mismatch in FileChooser.setFiles(files=): documented as Union[str, List[str], Dict, List[Dict]], code has Union[str, {"name": str, "mimeType": str, "buffer": bytes}, List[str], List[{"name": str, "mimeType": str, "buffer": bytes}]]
78+
Parameter type mismatch in Page.setInputFiles(files=): documented as Union[str, List[str], Dict, List[Dict]], code has Union[str, {"name": str, "mimeType": str, "buffer": bytes}, List[str], List[{"name": str, "mimeType": str, "buffer": bytes}]]
79+
Parameter type mismatch in ElementHandle.setInputFiles(files=): documented as Union[str, List[str], Dict, List[Dict]], code has Union[str, pathlib.Path, {"name": str, "mimeType": str, "buffer": bytes}, List[str], List[pathlib.Path], List[{"name": str, "mimeType": str, "buffer": bytes}]]
80+
Parameter type mismatch in Frame.setInputFiles(files=): documented as Union[str, List[str], Dict, List[Dict]], code has Union[str, pathlib.Path, {"name": str, "mimeType": str, "buffer": bytes}, List[str], List[pathlib.Path], List[{"name": str, "mimeType": str, "buffer": bytes}]]
8181

8282
# Select option
8383
Parameter type mismatch in ElementHandle.selectOption(values=): documented as Union[str, ElementHandle, List[str], Dict, List[ElementHandle], List[Dict], NoneType], code has Union[str, ElementHandle, {"value": Optional[str], "label": Optional[str], "index": Optional[str]}, List[str], List[ElementHandle], List[{"value": Optional[str], "label": Optional[str], "index": Optional[str]}], NoneType]

tests/async/test_click.py

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -809,13 +809,10 @@ async def test_fail_when_element_detaches_after_animation(page, server):
809809
promise = asyncio.create_task(handle.click())
810810
await asyncio.sleep(0) # execute scheduled tasks, but don't await them
811811
await page.evaluate("stopButton(true)")
812-
error = None
813-
try:
814-
error = await promise
815-
except Error as e:
816-
error = e
812+
with pytest.raises(Error) as exc_info:
813+
await promise
817814
assert await page.evaluate("window.clicked") is None
818-
assert "Element is not attached to the DOM" in error.message
815+
assert "Element is not attached to the DOM" in exc_info.value.message
819816

820817

821818
async def test_retry_when_element_detaches_after_animation(page, server):
@@ -950,16 +947,12 @@ async def test_click_the_button_when_window_inner_width_is_corrupted(page, serve
950947

951948

952949
async def test_timeout_when_click_opens_alert(page, server):
953-
dialog_promise = asyncio.create_task(page.waitForEvent("dialog"))
954-
await asyncio.sleep(0) # execute scheduled tasks, but don't await them
955950
await page.setContent('<div onclick="window.alert(123)">Click me</div>')
956-
error = None
957-
try:
958-
await page.click("div", timeout=3000)
959-
except TimeoutError as e:
960-
error = e
961-
assert "Timeout 3000ms exceeded" in error.message
962-
dialog = await dialog_promise
951+
async with page.expect_event("dialog") as dialog_info:
952+
with pytest.raises(Error) as exc_info:
953+
await page.click("div", timeout=3000)
954+
assert "Timeout 3000ms exceeded" in exc_info.value.message
955+
dialog = await dialog_info.value
963956
await dialog.dismiss()
964957

965958

tests/async/test_input.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,10 @@ async def test_should_emit_event(page: Page, server):
7777

7878
async def test_should_work_when_file_input_is_attached_to_DOM(page: Page, server):
7979
await page.setContent("<input type=file>")
80-
file_chooser = asyncio.create_task(page.waitForEvent("filechooser"))
81-
await asyncio.sleep(0) # execute scheduled tasks, but don't await them
82-
await page.click("input")
83-
assert await file_chooser
80+
async with page.expect_event("filechooser") as fc_info:
81+
await page.click("input")
82+
file_chooser = await fc_info.value
83+
assert file_chooser
8484

8585

8686
async def test_should_work_when_file_input_is_not_attached_to_DOM(page, server):

tests/async/test_page.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ async def test_wait_for_request_should_work_with_url_match(page, server):
224224
assert request.url == server.PREFIX + "/digits/1.png"
225225

226226

227-
async def test_wait_for_event_should_fail_with_error_upon_disconnect(page, server):
227+
async def test_wait_for_event_should_fail_with_error_upon_disconnect(page):
228228
future = asyncio.create_task(page.waitForEvent("download"))
229229
await asyncio.sleep(0) # execute scheduled tasks, but don't await them
230230
await page.close()

tests/async/test_worker.py

Lines changed: 40 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,12 @@ async def test_workers_page_workers(page, server):
3838

3939

4040
async def test_workers_should_emit_created_and_destroyed_events(page: Page):
41-
worker_createdpromise = asyncio.create_task(page.waitForEvent("worker"))
42-
await asyncio.sleep(0) # execute scheduled tasks, but don't await them
43-
worker_obj = await page.evaluateHandle(
44-
"() => new Worker(URL.createObjectURL(new Blob(['1'], {type: 'application/javascript'})))"
45-
)
46-
worker = await worker_createdpromise
41+
worker_obj = None
42+
async with page.expect_event("worker") as event_info:
43+
worker_obj = await page.evaluateHandle(
44+
"() => new Worker(URL.createObjectURL(new Blob(['1'], {type: 'application/javascript'})))"
45+
)
46+
worker = await event_info.value
4747
worker_this_obj = await worker.evaluateHandle("() => this")
4848
worker_destroyed_promise: Future[Worker] = asyncio.Future()
4949
worker.once("close", lambda w: worker_destroyed_promise.set_result(w))
@@ -78,12 +78,11 @@ async def test_workers_should_have_JSHandles_for_console_logs(page):
7878

7979

8080
async def test_workers_should_evaluate(page):
81-
worker_createdpromise = asyncio.create_task(page.waitForEvent("worker"))
82-
await asyncio.sleep(0) # execute scheduled tasks, but don't await them
83-
await page.evaluate(
84-
"() => new Worker(URL.createObjectURL(new Blob(['console.log(1)'], {type: 'application/javascript'})))"
85-
)
86-
worker = await worker_createdpromise
81+
async with page.expect_event("worker") as event_info:
82+
await page.evaluate(
83+
"() => new Worker(URL.createObjectURL(new Blob(['console.log(1)'], {type: 'application/javascript'})))"
84+
)
85+
worker = await event_info.value
8786
assert await worker.evaluate("1+1") == 2
8887

8988

@@ -105,12 +104,11 @@ async def test_workers_should_report_errors(page):
105104

106105
async def test_workers_should_clear_upon_navigation(server, page):
107106
await page.goto(server.EMPTY_PAGE)
108-
worker_createdpromise = asyncio.create_task(page.waitForEvent("worker"))
109-
await asyncio.sleep(0) # execute scheduled tasks, but don't await them
110-
await page.evaluate(
111-
'() => new Worker(URL.createObjectURL(new Blob(["console.log(1)"], {type: "application/javascript"})))'
112-
)
113-
worker = await worker_createdpromise
107+
async with page.expect_event("worker") as event_info:
108+
await page.evaluate(
109+
'() => new Worker(URL.createObjectURL(new Blob(["console.log(1)"], {type: "application/javascript"})))'
110+
)
111+
worker = await event_info.value
114112
assert len(page.workers) == 1
115113
destroyed = []
116114
worker.once("close", lambda _: destroyed.append(True))
@@ -121,12 +119,11 @@ async def test_workers_should_clear_upon_navigation(server, page):
121119

122120
async def test_workers_should_clear_upon_cross_process_navigation(server, page):
123121
await page.goto(server.EMPTY_PAGE)
124-
worker_createdpromise = asyncio.create_task(page.waitForEvent("worker"))
125-
await asyncio.sleep(0) # execute scheduled tasks, but don't await them
126-
await page.evaluate(
127-
"() => new Worker(URL.createObjectURL(new Blob(['console.log(1)'], {type: 'application/javascript'})))"
128-
)
129-
worker = await worker_createdpromise
122+
async with page.expect_event("worker") as event_info:
123+
await page.evaluate(
124+
"() => new Worker(URL.createObjectURL(new Blob(['console.log(1)'], {type: 'application/javascript'})))"
125+
)
126+
worker = await event_info.value
130127
assert len(page.workers) == 1
131128
destroyed = []
132129
worker.once("close", lambda _: destroyed.append(True))
@@ -141,14 +138,14 @@ async def test_workers_should_report_network_activity(page, server):
141138
page.goto(server.PREFIX + "/worker/worker.html"),
142139
)
143140
url = server.PREFIX + "/one-style.css"
144-
request_promise = asyncio.create_task(page.waitForRequest(url))
145-
response_promise = asyncio.create_task(page.waitForResponse(url))
146-
await asyncio.sleep(0) # execute scheduled tasks, but don't await them
147-
await worker.evaluate(
148-
"url => fetch(url).then(response => response.text()).then(console.log)", url
149-
)
150-
request = await request_promise
151-
response = await response_promise
141+
async with page.expect_request(url) as request_info, page.expect_response(
142+
url
143+
) as response_info:
144+
await worker.evaluate(
145+
"url => fetch(url).then(response => response.text()).then(console.log)", url
146+
)
147+
request = await request_info.value
148+
response = await response_info.value
152149
assert request.url == url
153150
assert response.request == request
154151
assert response.ok
@@ -158,17 +155,17 @@ async def test_workers_should_report_network_activity_on_worker_creation(page, s
158155
# Chromium needs waitForDebugger enabled for this one.
159156
await page.goto(server.EMPTY_PAGE)
160157
url = server.PREFIX + "/one-style.css"
161-
request_promise = asyncio.create_task(page.waitForRequest(url))
162-
response_promise = asyncio.create_task(page.waitForResponse(url))
163-
await asyncio.sleep(0) # execute scheduled tasks, but don't await them
164-
await page.evaluate(
165-
"""url => new Worker(URL.createObjectURL(new Blob([`
166-
fetch("${url}").then(response => response.text()).then(console.log);
167-
`], {type: 'application/javascript'})))""",
168-
url,
169-
)
170-
request = await request_promise
171-
response = await response_promise
158+
async with page.expect_request(url) as request_info, page.expect_response(
159+
url
160+
) as response_info:
161+
await page.evaluate(
162+
"""url => new Worker(URL.createObjectURL(new Blob([`
163+
fetch("${url}").then(response => response.text()).then(console.log);
164+
`], {type: 'application/javascript'})))""",
165+
url,
166+
)
167+
request = await request_info.value
168+
response = await response_info.value
172169
assert request.url == url
173170
assert response.request == request
174171
assert response.ok

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