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
Type annotate fixtures.py & related #6736
Conversation
afad3d8
to
9bda23f
Compare
fixturedefs = self._arg2fixturedefs.get(argname, None) | ||
if fixturedefs is None: | ||
# we arrive here because of a dynamic call to | ||
# getfixturevalue(argname) usage which was naturally | ||
# not known at parsing/collection time | ||
assert self._pyfuncitem.parent is not None |
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.
This is immediately dereferenced below so should be fine.
""" Dynamically run a named fixture function. | ||
|
||
Declaring fixtures via function argument is recommended where possible. | ||
But if you can only decide whether to use another fixture at test | ||
setup time, you may use this function to retrieve it inside a fixture | ||
or test function body. | ||
""" | ||
return self._get_active_fixturedef(argname).cached_result[0] | ||
fixturedef = self._get_active_fixturedef(argname) | ||
assert fixturedef.cached_result is not None |
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.
Immediately dereferenced below.
while 1: | ||
fixturedef = getattr(current, "_fixturedef", None) | ||
if fixturedef is None: | ||
values.reverse() | ||
return values | ||
values.append(fixturedef) | ||
assert isinstance(current, SubRequest) |
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.
This attribute is only set on SubRequest
.
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.
That would make a helpful in-code comment for future maintainers 😄
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.
I don't know, once the assert is in, the implication is reversed -- the access is legal because of the assert, not the other way :)
@@ -669,20 +718,21 @@ def _schedule_finalizers(self, fixturedef, subrequest): | |||
super()._schedule_finalizers(fixturedef, subrequest) | |||
|
|||
|
|||
scopes = "session package module class function".split() | |||
scopes = ["session", "package", "module", "class", "function"] # type: List[_Scope] |
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.
Changed to make the type annotation work (_Scope
is a Literal
).
"""Look up the index of ``scope`` and raise a descriptive value error | ||
if not defined. | ||
""" | ||
strscopes = scopes # type: Sequence[str] |
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.
Just a hack to expand the type of scopes
to allow checking against an str
rather than _Scope
.
@@ -871,6 +927,7 @@ def finish(self, request): | |||
exceptions.append(sys.exc_info()) | |||
if exceptions: | |||
_, val, tb = exceptions[0] | |||
assert val is not None |
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.
Dereferenced just below.
yield | ||
if request.config.option.setupshow: | ||
if hasattr(request, "param"): | ||
# Save the fixture parameter so ._show_fixture_action() can | ||
# display it now and during the teardown (in .finish()). | ||
if fixturedef.ids: | ||
if callable(fixturedef.ids): | ||
fixturedef.cached_param = fixturedef.ids(request.param) |
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.
Changed so I only need to ignore once.
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.
Maybe a helper method FixtureDef.set_cached_param
would be appropriate? Though I can see an argument that it's not worth the trouble when we have to maintain the current option for backwards-compatibility.
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.
Yeah, I'm not sure it's worth it.
9bda23f
to
3856200
Compare
3856200
to
c0c4ca9
Compare
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 @bluetech! A few questions and suggestions for further annotations below, but overall this looks great to me 😁
yield | ||
if request.config.option.setupshow: | ||
if hasattr(request, "param"): | ||
# Save the fixture parameter so ._show_fixture_action() can | ||
# display it now and during the teardown (in .finish()). | ||
if fixturedef.ids: | ||
if callable(fixturedef.ids): | ||
fixturedef.cached_param = fixturedef.ids(request.param) |
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.
Maybe a helper method FixtureDef.set_cached_param
would be appropriate? Though I can see an argument that it's not worth the trouble when we have to maintain the current option for backwards-compatibility.
@@ -111,7 +137,9 @@ def get_scope_node(node, scope): | |||
return node.getparent(cls) | |||
|
|||
|
|||
def add_funcarg_pseudo_fixture_def(collector, metafunc, fixturemanager): | |||
def add_funcarg_pseudo_fixture_def( | |||
collector, metafunc: "Metafunc", fixturemanager: "FixtureManager" |
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.
Worth annotating collector
? AFAICT it's an Optional[Type[nodes.Node]]
.
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.
When I'm not sure of something, and too lazy to check/verify, I leave it unannotated. Usually some later PR adds it :)
while 1: | ||
fixturedef = getattr(current, "_fixturedef", None) | ||
if fixturedef is None: | ||
values.reverse() | ||
return values | ||
values.append(fixturedef) | ||
assert isinstance(current, SubRequest) |
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.
That would make a helpful in-code comment for future maintainers 😄
self, | ||
request: "FixtureRequest", | ||
scope: "_Scope", | ||
param, |
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.
param: object,
?
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.
IIRC, this one can also be NOTSET
, and I'd like to distinguish it in the type annotation from just an object
(although object
subsumes everything) just for clarity.
I think there is another PR that does that although I sort of lost track myself already 👣
@@ -805,7 +855,7 @@ def _teardown_yield_fixture(fixturefunc, it): | |||
) | |||
|
|||
|
|||
def _eval_scope_callable(scope_callable, fixture_name, config): | |||
def _eval_scope_callable(scope_callable, fixture_name: str, config: Config) -> str: |
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.
scope_callable: Callable[..., str]
- speccing keyword arguments is pretty fiddly, but even without the arguments specified this could catch a fair few issues.
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.
I prefer to leave this one for for later.
@@ -1317,7 +1379,9 @@ def _getautousenames(self, nodeid): | |||
autousenames.extend(basenames) | |||
return autousenames | |||
|
|||
def getfixtureclosure(self, fixturenames, parentnode, ignore_args=()): | |||
def getfixtureclosure( | |||
self, fixturenames: Tuple[str, ...], parentnode, ignore_args: Sequence[str] = () |
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.
parentnode: nodes.Node
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.
I'm not sure, I prefer to leave this one.
c0c4ca9
to
cba62b6
Compare
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 for the review @Zac-HD. I applied (some) of your suggestions. Also rebased.
@@ -111,7 +137,9 @@ def get_scope_node(node, scope): | |||
return node.getparent(cls) | |||
|
|||
|
|||
def add_funcarg_pseudo_fixture_def(collector, metafunc, fixturemanager): | |||
def add_funcarg_pseudo_fixture_def( | |||
collector, metafunc: "Metafunc", fixturemanager: "FixtureManager" |
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.
When I'm not sure of something, and too lazy to check/verify, I leave it unannotated. Usually some later PR adds it :)
while 1: | ||
fixturedef = getattr(current, "_fixturedef", None) | ||
if fixturedef is None: | ||
values.reverse() | ||
return values | ||
values.append(fixturedef) | ||
assert isinstance(current, SubRequest) |
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.
I don't know, once the assert is in, the implication is reversed -- the access is legal because of the assert, not the other way :)
self, | ||
request: "FixtureRequest", | ||
scope: "_Scope", | ||
param, |
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.
IIRC, this one can also be NOTSET
, and I'd like to distinguish it in the type annotation from just an object
(although object
subsumes everything) just for clarity.
I think there is another PR that does that although I sort of lost track myself already 👣
@@ -805,7 +855,7 @@ def _teardown_yield_fixture(fixturefunc, it): | |||
) | |||
|
|||
|
|||
def _eval_scope_callable(scope_callable, fixture_name, config): | |||
def _eval_scope_callable(scope_callable, fixture_name: str, config: Config) -> str: |
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.
I prefer to leave this one for for later.
@@ -1317,7 +1379,9 @@ def _getautousenames(self, nodeid): | |||
autousenames.extend(basenames) | |||
return autousenames | |||
|
|||
def getfixtureclosure(self, fixturenames, parentnode, ignore_args=()): | |||
def getfixtureclosure( | |||
self, fixturenames: Tuple[str, ...], parentnode, ignore_args: Sequence[str] = () |
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.
I'm not sure, I prefer to leave this one.
yield | ||
if request.config.option.setupshow: | ||
if hasattr(request, "param"): | ||
# Save the fixture parameter so ._show_fixture_action() can | ||
# display it now and during the teardown (in .finish()). | ||
if fixturedef.ids: | ||
if callable(fixturedef.ids): | ||
fixturedef.cached_param = fixturedef.ids(request.param) |
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.
Yeah, I'm not sure it's worth it.
Extracted from #6717.
Depends/based on #6737.