-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
[WIP] typing improvements #6449
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
Changes from all commits
8666b27
604b84f
b96077b
b95394b
0a8328a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -47,6 +47,8 @@ | |
if TYPE_CHECKING: | ||
from typing import Type | ||
|
||
_PluggyPlugin = object | ||
|
||
|
||
hookimpl = HookimplMarker("pytest") | ||
hookspec = HookspecMarker("pytest") | ||
|
@@ -172,7 +174,10 @@ def directory_arg(path, optname): | |
builtin_plugins.add("pytester") | ||
|
||
|
||
def get_config(args=None, plugins=None): | ||
def get_config( | ||
args: Optional[Union[List, Tuple]] = None, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If yes, it's OK to leave them out for now, leaving them out indicates a TODO. If no, I prefer to put explicit |
||
plugins: Optional[Sequence[Union[str, object]]] = None, | ||
) -> "Config": | ||
# subsequent calls to main will create a fresh instance | ||
pluginmanager = PytestPluginManager() | ||
config = Config( | ||
|
@@ -191,7 +196,7 @@ def get_config(args=None, plugins=None): | |
return config | ||
|
||
|
||
def get_plugin_manager(): | ||
def get_plugin_manager() -> PluginManager: | ||
""" | ||
Obtain a new instance of the | ||
:py:class:`_pytest.config.PytestPluginManager`, with default plugins | ||
|
@@ -203,7 +208,10 @@ def get_plugin_manager(): | |
return get_config().pluginmanager | ||
|
||
|
||
def _prepareconfig(args=None, plugins=None): | ||
def _prepareconfig( | ||
args: Optional[Union[List, Tuple]] = None, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like this can also be a |
||
plugins: Optional[Sequence[Union[str, "_PluggyPlugin"]]] = None, | ||
): | ||
if args is None: | ||
args = sys.argv[1:] | ||
elif isinstance(args, py.path.local): | ||
|
@@ -732,7 +740,9 @@ class InvocationParams: | |
plugins = attr.ib() | ||
dir = attr.ib(type=Path) | ||
|
||
def __init__(self, pluginmanager, *, invocation_params=None): | ||
def __init__( | ||
self, pluginmanager: "PytestPluginManager", *, invocation_params=None | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe (OK if you left it out intentionally). |
||
) -> None: | ||
from .argparsing import Parser, FILE_OR_DIR | ||
|
||
if invocation_params is None: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,8 +7,10 @@ | |
from collections import deque | ||
from collections import OrderedDict | ||
from typing import Dict | ||
from typing import Generator | ||
from typing import List | ||
from typing import Tuple | ||
from typing import Union | ||
|
||
import attr | ||
import py | ||
|
@@ -29,6 +31,7 @@ | |
from _pytest.compat import NOTSET | ||
from _pytest.compat import safe_getattr | ||
from _pytest.compat import TYPE_CHECKING | ||
from _pytest.config import Config | ||
from _pytest.deprecated import FIXTURE_POSITIONAL_ARGUMENTS | ||
from _pytest.deprecated import FUNCARGNAMES | ||
from _pytest.outcomes import fail | ||
|
@@ -38,6 +41,9 @@ | |
from typing import Type | ||
|
||
from _pytest import nodes | ||
from _pytest.main import Session | ||
from _pytest.python import Class # noqa: F401 | ||
from _pytest.python import Function | ||
|
||
|
||
@attr.s(frozen=True) | ||
|
@@ -46,7 +52,7 @@ class PseudoFixtureDef: | |
scope = attr.ib() | ||
|
||
|
||
def pytest_sessionstart(session): | ||
def pytest_sessionstart(session: "Session") -> None: | ||
import _pytest.python | ||
import _pytest.nodes | ||
|
||
|
@@ -179,30 +185,43 @@ def getfixturemarker(obj): | |
return None | ||
|
||
|
||
def get_parametrized_fixture_keys(item, scopenum): | ||
def get_parametrized_fixture_keys( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Depending on how much effort you're willing to expand on this function, it looks like this can be something like this: @overload
def get_parametrized_fixture_keys(item: "nodes.Item", scopenum: Literal[0]) -> Iterator[Tuple[str, int]]: ...
@overload
def get_parametrized_fixture_keys(item: "nodes.Item", scopenum: Literal[1, 2]) -> Iterator[Tuple[str, int, py.path.local]]: ...
@overload
def get_parametrized_fixture_keys(item: "nodes.Item", scopenum: Literal[4]) -> Iterator[Tuple[str, int, py.path.local, "Class"]]: ... Whether it needs |
||
item: "nodes.Item", # only used for Function (with callspec), kept for B/C. | ||
scopenum: int, | ||
) -> Generator[ | ||
Union[ | ||
Tuple[str, int], | ||
Tuple[str, int, py.path.local], | ||
Tuple[str, int, py.path.local, "Class"], | ||
], | ||
None, | ||
None, | ||
]: | ||
""" return list of keys for all parametrized arguments which match | ||
the specified scope. """ | ||
assert scopenum < scopenum_function # function | ||
assert scopenum < scopenum_function, (scopenum, scopenum_function) | ||
|
||
try: | ||
cs = item.callspec | ||
cs = item.callspec # type: ignore[attr-defined] # noqa: F821 | ||
except AttributeError: | ||
pass | ||
else: | ||
# cs.indices.items() is random order of argnames. Need to | ||
# sort this so that different calls to | ||
# get_parametrized_fixture_keys will be deterministic. | ||
for argname, param_index in sorted(cs.indices.items()): | ||
if cs._arg2scopenum[argname] != scopenum: | ||
continue | ||
return | ||
if TYPE_CHECKING: | ||
assert isinstance(item, Function) | ||
|
||
# cs.indices.items() is random order of argnames. Need to | ||
# sort this so that different calls to | ||
# get_parametrized_fixture_keys will be deterministic. | ||
for argname, param_index in sorted(cs.indices.items()): | ||
if cs._arg2scopenum[argname] == scopenum: | ||
if scopenum == 0: # session | ||
key = (argname, param_index) | ||
yield (argname, param_index) | ||
elif scopenum == 1: # package | ||
key = (argname, param_index, item.fspath.dirpath()) | ||
yield (argname, param_index, item.fspath.dirpath()) | ||
elif scopenum == 2: # module | ||
key = (argname, param_index, item.fspath) | ||
elif scopenum == 3: # class | ||
key = (argname, param_index, item.fspath, item.cls) | ||
yield key | ||
yield (argname, param_index, item.fspath) | ||
else: # class | ||
assert scopenum == 3, scopenum | ||
yield (argname, param_index, item.fspath, item.cls) | ||
|
||
|
||
# algorithm for sorting on a per-parametrized resource setup basis | ||
|
@@ -346,7 +365,7 @@ class FixtureRequest: | |
the fixture is parametrized indirectly. | ||
""" | ||
|
||
def __init__(self, pyfuncitem): | ||
def __init__(self, pyfuncitem: "Function") -> None: | ||
self._pyfuncitem = pyfuncitem | ||
#: fixture for which this request is being performed | ||
self.fixturename = None | ||
|
@@ -355,7 +374,7 @@ def __init__(self, pyfuncitem): | |
self._fixture_defs = {} # type: Dict[str, FixtureDef] | ||
fixtureinfo = pyfuncitem._fixtureinfo | ||
self._arg2fixturedefs = fixtureinfo.name2fixturedefs.copy() | ||
self._arg2index = {} | ||
self._arg2index = {} # type: Dict[str, int] | ||
self._fixturemanager = pyfuncitem.session._fixturemanager | ||
|
||
@property | ||
|
@@ -393,7 +412,7 @@ def _getnextfixturedef(self, argname): | |
return fixturedefs[index] | ||
|
||
@property | ||
def config(self): | ||
def config(self) -> Config: | ||
""" the pytest config object associated with this request. """ | ||
return self._pyfuncitem.config | ||
|
||
|
@@ -1186,7 +1205,7 @@ def yield_fixture( | |
|
||
|
||
@fixture(scope="session") | ||
def pytestconfig(request): | ||
def pytestconfig(request: FixtureRequest) -> Config: | ||
"""Session-scoped fixture that returns the :class:`_pytest.config.Config` object. | ||
|
||
Example:: | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bluetech since this might be used elsewhere - where should be put this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As long is we keep it internal for now (I mean, not intended to be exposed in the "public" typing in the future), I guess putting it where
PytestPluginManager
is defined makes most sense?A short comment here would be helpful, something like
BTW, I wouldn't put it under
TYPE_CHECKING
, this way we can use it without the annoying quotes.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Amended in a39b4e2 (#6580).