Skip to content

support overloads and add overloads to wait_for_selector for when it returns None #851

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 8 commits into from
Closed
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ _repo_version.py
coverage.xml
junit/
htmldocs/
.venv
52 changes: 51 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,63 @@ pre-commit run --all-files

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

### Regenerating APIs
### APIs

#### Regenerating

```bash
./scripts/update_api.sh
pre-commit run --all-files
```

#### Differences between `_impl` and exposed API method signatures

- optional arguments are automatically converted to keyword arguments, unless the method has overloads. for example:
```py
def wait_for_selector(self, selector: str, timeout: float = None, state: str = None): ...
```
becomes
```py
def wait_for_selector(self, selector: str, *, timeout: float = None, state: str = None): ...
```

- overloads must be defined using `@api_overload` in order for the generate scripts to be able to see them at runtime.
```py
from playwright._impl._overload import api_overload

@api_overload
async def wait_for_selector(
self,
selector: str,
*,
timeout: float = None,
state: Literal["attached", "visible"] = None,
strict: bool = None,
) -> ElementHandle:
pass

@api_overload # type: ignore[no-redef]
async def wait_for_selector(
self,
selector: str,
*,
timeout: float = None,
state: Literal["detached", "hidden"],
strict: bool = None,
) -> None:
pass

async def wait_for_selector( # type: ignore[no-redef]
self,
selector: str,
*,
timeout: float = None,
state: Literal["attached", "detached", "hidden", "visible"] = None,
strict: bool = None,
) -> Optional[ElementHandle]:
...
```

## Contributor License Agreement

This project welcomes contributions and suggestions. Most contributions require you to agree to a
Expand Down
22 changes: 22 additions & 0 deletions playwright/_impl/_element_handle.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
parse_result,
serialize_argument,
)
from playwright._impl._overload import api_overload

if sys.version_info >= (3, 8): # pragma: no cover
from typing import Literal
Expand Down Expand Up @@ -309,9 +310,30 @@ async def wait_for_element_state(
) -> None:
await self._channel.send("waitForElementState", locals_to_params(locals()))

@api_overload
async def wait_for_selector(
self,
selector: str,
*,
state: Literal["attached", "visible"] = None,
timeout: float = None,
) -> "ElementHandle":
...

@api_overload # type: ignore[no-redef]
async def wait_for_selector(
self,
selector: str,
*,
state: Literal["detached", "hidden"],
timeout: float = None,
) -> None:
...

async def wait_for_selector( # type: ignore[no-redef]
self,
selector: str,
*,
state: Literal["attached", "detached", "hidden", "visible"] = None,
timeout: float = None,
) -> Optional["ElementHandle"]:
Expand Down
24 changes: 24 additions & 0 deletions playwright/_impl/_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
)
from playwright._impl._locator import Locator
from playwright._impl._network import Response
from playwright._impl._overload import api_overload
from playwright._impl._wait_helper import WaitHelper

if sys.version_info >= (3, 8): # pragma: no cover
Expand Down Expand Up @@ -270,9 +271,32 @@ async def query_selector_all(self, selector: str) -> List[ElementHandle]:
)
)

@api_overload
async def wait_for_selector(
self,
selector: str,
*,
strict: bool = None,
timeout: float = None,
state: Literal["attached", "visible"] = None,
) -> ElementHandle:
...

@api_overload # type: ignore[no-redef]
async def wait_for_selector(
self,
selector: str,
*,
strict: bool = None,
timeout: float = None,
state: Literal["detached", "hidden"],
) -> None:
...

async def wait_for_selector( # type: ignore[no-redef]
self,
selector: str,
*,
strict: bool = None,
timeout: float = None,
state: Literal["attached", "detached", "hidden", "visible"] = None,
Expand Down
16 changes: 16 additions & 0 deletions playwright/_impl/_overload.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from collections import defaultdict
import inspect
from typing import Callable


def api_overload(fn: Callable) -> Callable:
"""
Creates an overload that's visible to the api generate scripts. use this decorator instead of `typing.overload` when exposing overloads from `_impl`.
You will need to suppress mypy errors using a `# type: ignore[no-redef]` comment
"""
dictionary = inspect.getmodule(fn).__dict__
overloads_key = "__overloads__"
if dictionary.get(overloads_key) is None:
dictionary[overloads_key] = defaultdict(list)
dictionary[overloads_key][fn.__name__].append(fn)
return fn
24 changes: 24 additions & 0 deletions playwright/_impl/_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
serialize_argument,
)
from playwright._impl._network import Request, Response, Route, serialize_headers
from playwright._impl._overload import api_overload
from playwright._impl._video import Video
from playwright._impl._wait_helper import WaitHelper

Expand Down Expand Up @@ -325,9 +326,32 @@ async def query_selector(
async def query_selector_all(self, selector: str) -> List[ElementHandle]:
return await self._main_frame.query_selector_all(selector)

@api_overload
async def wait_for_selector(
self,
selector: str,
*,
timeout: float = None,
state: Literal["attached", "visible"] = None,
strict: bool = None,
) -> ElementHandle:
...

@api_overload # type: ignore[no-redef]
async def wait_for_selector(
self,
selector: str,
*,
timeout: float = None,
state: Literal["detached", "hidden"],
strict: bool = None,
) -> None:
...

async def wait_for_selector( # type: ignore[no-redef]
self,
selector: str,
*,
timeout: float = None,
state: Literal["attached", "detached", "hidden", "visible"] = None,
strict: bool = None,
Expand Down
64 changes: 64 additions & 0 deletions playwright/async_api/_generated.py
Original file line number Diff line number Diff line change
Expand Up @@ -2462,6 +2462,26 @@ async def wait_for_element_state(
)
)

@typing.overload
async def wait_for_selector(
self,
selector: str,
*,
state: Literal["attached", "visible"] = None,
timeout: float = None
) -> "ElementHandle":
pass

@typing.overload
async def wait_for_selector(
self,
selector: str,
*,
state: Literal["detached", "hidden"],
timeout: float = None
) -> NoneType:
pass

async def wait_for_selector(
self,
selector: str,
Expand Down Expand Up @@ -3118,6 +3138,28 @@ async def query_selector_all(self, selector: str) -> typing.List["ElementHandle"
)
)

@typing.overload
async def wait_for_selector(
self,
selector: str,
*,
strict: bool = None,
timeout: float = None,
state: Literal["attached", "visible"] = None
) -> "ElementHandle":
pass

@typing.overload
async def wait_for_selector(
self,
selector: str,
*,
strict: bool = None,
timeout: float = None,
state: Literal["detached", "hidden"]
) -> NoneType:
pass

async def wait_for_selector(
self,
selector: str,
Expand Down Expand Up @@ -5536,6 +5578,28 @@ async def query_selector_all(self, selector: str) -> typing.List["ElementHandle"
)
)

@typing.overload
async def wait_for_selector(
self,
selector: str,
*,
timeout: float = None,
state: Literal["attached", "visible"] = None,
strict: bool = None
) -> "ElementHandle":
pass

@typing.overload
async def wait_for_selector(
self,
selector: str,
*,
timeout: float = None,
state: Literal["detached", "hidden"],
strict: bool = None
) -> NoneType:
pass

async def wait_for_selector(
self,
selector: str,
Expand Down
64 changes: 64 additions & 0 deletions playwright/sync_api/_generated.py
Original file line number Diff line number Diff line change
Expand Up @@ -2445,6 +2445,26 @@ def wait_for_element_state(
)
)

@typing.overload
def wait_for_selector(
self,
selector: str,
*,
state: Literal["attached", "visible"] = None,
timeout: float = None
) -> "ElementHandle":
pass

@typing.overload
def wait_for_selector(
self,
selector: str,
*,
state: Literal["detached", "hidden"],
timeout: float = None
) -> NoneType:
pass

def wait_for_selector(
self,
selector: str,
Expand Down Expand Up @@ -3099,6 +3119,28 @@ def query_selector_all(self, selector: str) -> typing.List["ElementHandle"]:
)
)

@typing.overload
def wait_for_selector(
self,
selector: str,
*,
strict: bool = None,
timeout: float = None,
state: Literal["attached", "visible"] = None
) -> "ElementHandle":
pass

@typing.overload
def wait_for_selector(
self,
selector: str,
*,
strict: bool = None,
timeout: float = None,
state: Literal["detached", "hidden"]
) -> NoneType:
pass

def wait_for_selector(
self,
selector: str,
Expand Down Expand Up @@ -5505,6 +5547,28 @@ def query_selector_all(self, selector: str) -> typing.List["ElementHandle"]:
)
)

@typing.overload
def wait_for_selector(
self,
selector: str,
*,
timeout: float = None,
state: Literal["attached", "visible"] = None,
strict: bool = None
) -> "ElementHandle":
pass

@typing.overload
def wait_for_selector(
self,
selector: str,
*,
timeout: float = None,
state: Literal["detached", "hidden"],
strict: bool = None
) -> NoneType:
pass

def wait_for_selector(
self,
selector: str,
Expand Down
Loading