playwright-python
playwright-python copied to clipboard
Wrong ProxySettings type hints
Context:
- Playwright Version: 1.21.0
- Operating System: Mac
- Python version: 3.10.1
- Browser: All
Code Snippet
import asyncio
from playwright.async_api import async_playwright, ProxySettings
async def main():
pw = await async_playwright().start()
browser = await pw.firefox.launch()
proxy_settings = ProxySettings(
server="http://localhost:8080", username=None, password=None
)
ctx = await browser.new_context(proxy=proxy_settings, ignore_https_errors=True)
page = await ctx.new_page()
resp = await page.goto("https://httpbin.org/ip")
print(resp)
asyncio.run(main())
Describe the bug
- run mitmproxy, or any other HTTP/HTTPS proxy.
- Run the sample.
- playwright crashes with
Traceback (most recent call last):
File "/private/tmp/main.py", line 19, in <module>
asyncio.run(main())
File "/Users/povilas/.pyenv/versions/3.10.1/lib/python3.10/asyncio/runners.py", line 44, in run
return loop.run_until_complete(main)
File "/Users/povilas/.pyenv/versions/3.10.1/lib/python3.10/asyncio/base_events.py", line 641, in run_until_complete
return future.result()
File "/private/tmp/main.py", line 12, in main
ctx = await browser.new_context(proxy=proxy_settings, ignore_https_errors=True)
File "/private/tmp/pyenv/lib/python3.10/site-packages/playwright/async_api/_generated.py", line 11317, in new_context
await self._async(
File "/private/tmp/pyenv/lib/python3.10/site-packages/playwright/_impl/_browser.py", line 117, in new_context
channel = await self._channel.send("newContext", params)
File "/private/tmp/pyenv/lib/python3.10/site-packages/playwright/_impl/_connection.py", line 39, in send
return await self.inner_send(method, params, False)
File "/private/tmp/pyenv/lib/python3.10/site-packages/playwright/_impl/_connection.py", line 63, in inner_send
result = next(iter(done)).result()
playwright._impl._api_types.Error: proxy.username: expected string, got object
-
ProxySettings
declaration is
class ProxySettings(TypedDict, total=False):
server: str
bypass: Optional[str]
username: Optional[str]
password: Optional[str]
- But clearly
None
is not allowed forusername
andpassword
. This, however, works:
proxy_settings = ProxySettings(
server="http://localhost:8080", username="", password=""
)
I reproduced the error as expected when declaring proxy_settings
as:
proxy_settings = ProxySettings(
server="http://localhost:8080", username=None, password=None
)
# results in a dict with these items:
# dict_items([('server', 'http://localhost:8080'), ('username', None), ('password', None)])
Note, that another workaround is:
proxy_settings = ProxySettings(
server="http://localhost:8080"
)
# results in a dict with these items:
# dict_items([('server', 'http://localhost:8080')])
So, username
and password
are implicitly optional, but if you explicitly declare them as None
things break (which is an issue).
A clean fix to the problem is updating locals_to_params
as:
def locals_to_params(args: Dict) -> Dict:
copy = {}
for key in args:
if key == "self":
continue
if args[key] is not None:
copy[key] = args[key] if not isinstance(args[key], Dict) else locals_to_params(args[key])
return copy