-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
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
Implemented the dynamic scope feature. #5776
Conversation
Codecov Report
@@ Coverage Diff @@
## features #5776 +/- ##
============================================
- Coverage 96.33% 96.26% -0.08%
============================================
Files 117 117
Lines 25995 26064 +69
Branches 2498 2506 +8
============================================
+ Hits 25042 25090 +48
- Misses 650 669 +19
- Partials 303 305 +2
Continue to review full report at Codecov.
|
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.
Hi @aklajnert, thanks a lot for the contribution!
The contribution overall looks great, and besides my comments, I think we are also missing a few things:
-
linting
can be fixed by runningtox -e linting
or installingpre-commit
(see docs). -
We need some docs about this feature, possibly in
doc/en/fixture.rst
. -
We need two changelog entries, I suggest:
1682.deprecation.rst
:Passing ``scope`` as a positional parameter to ``@pytest.fixture`` has been deprecated, pass it by keyword instead.
1682.feature.rst
:The ``scope`` parameter of ``@pytest.fixture`` can now be a callable that receives the ``config`` object as keyword-only parameter. See `the docs <LINK>`__ for more information.
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 feedback. About the missing things in the PR, I am aware of them, but I just wanted to show my code first, to make sure that my approach is correct.
Great!
For example, if the user wants to control scope via an environment variable, he doesn't need the config at all. The dynamic scope function could be defined as def func(_): ..., which will be better for pylint than unused argument.
The user can ignore keyword arguments using def func(**_):
in the same way.
My intent with this is that we might want to pass new arguments in the future, and using keyword arguments would allow us to call the method even if the user doesn't declare all arguments, similar to how pluggy
does with hook implementations.
@nicoddemus I think the code is finished, however, I'm not sure about a few things:
|
Hi @aklajnert I have also an implementation of this feature (using hooks, and with some limitations) but I've found that without invocation scoped fixtures it's very hard to make it work reliably unless you mark all the fixtures as dynamically scoped depending on the same configuration option, which is not always convenient. |
To be honest, I don't understand the idea of the |
The problem I have is that if I define a fixture
I need fixture If I understand it correctly, invocation scoped fixtures allows a fixture to have multiple scopes depending on the scope on which is it used. If a session scoped fixture uses it, it becomes session scoped, but if a function scoped fixture or function uses it, a new instance of the fixture with the function scope is created. In that way a invocation scoped fixture would solve the issue I described before with dynamically scoped fixtures. |
Thanks, @pablochacin I understand now. This indeed seems tricky. |
I implemented this same mechanism and started having the problem with mixed scopes. I think in a large project, with many fixtures, it is not viable to implement dynamic scope without invocation scope. |
I don't know pytest internals very well, but in my understanding, if you use the dynamic scope after the fixture is defined, it will be exactly the same as you would define the scope in a traditional way. For example, the callable below when used as dynamic scope is equivalent to statically set def dynamic_scope(**_):
return "session" Therefore I can't see why the invocation scoped fixtures are necessary here. |
Because in the example I gave above, if fixture
But consider now that fixture
Unless I'm missing something here, this won't work unless |
I guess you're right, but I still think it isn't connected with the dynamic scope feature. def dynamic_scope(**_):
return "session"
@pytest.fixture(scope=dynamic_scope)
def fixture_a():
pass
@pytest.fixture(scope="session")
def fixture_b():
pass I have no experience with nested fixtures with different scopes, but I think the dynamic scope feature shouldn't change the current behavior. |
Maybe @nicoddemus or @RonnyPfannschmidt can shed some light on this topic |
from my pov invocation scope and dynamic scope are orthogonal in terms of implementation right now to use dynamic scopes sensible in a larger setup, one needs to use it on all involved fixtures with better scoping in general one would only need it in key fixtures, and the dependent ones would follow suit as needed (this one needs some trying out and experimentation in the future) as far as i can tell the implementation as given is reasonable and a enhancement also its open to future expansion/refinement in a way that seems ok to follow on |
Absolutly so @RonnyPfannschmidt It was not my intention to point this proposal as incorrect or not useful. I only wanted to bring attention about the issues users will very likely find (as I did) Sorry @aklajnert if it sounded as a criticism to your proposal. That said, any chance to make dynamic scope work any time soon, @RonnyPfannschmidt? I was looking at the implementation that was finally reverted and I must confess it is beyond my understanding of pytest's internals at this point. |
That one will take a while, the current internals make invocation scopes and multi scopes very tricky and brittle We will probably need a sprint and a few refactorings to get it to work |
@RonnyPfannschmidt do you mean that this PR won't be merged anytime soon? I'm a bit confused as it seems to be stuck in limbo, and I don't have any feedback on the changes. I know the tests are failing but they seem to be unrelated. |
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.
no it actually should push forward soon
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.
Looks great @aklajnert, thanks a lot for all the hard work and sorry for the delay! 👍
I fixed the conflict and took the liberty of changing the tests to plain tests. Thanks again for the hard work! |
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.
lovely add-on detail work
Thanks again @aklajnert! |
pytest 5.2.0 added support for callable scopes. In order to distinguish those (`@pytest.fixture(scope=fn)`) from decorators (`@pytest.fixture`), it added extra arguments which was not expected by our wrapper. See pytest-dev/pytest#5776 for the change. Fixes the following error: ImportError while loading conftest 'test/conftest.py'. test/conftest.py:42: in <module> from fixtures_ws import * test/fixtures_ws.py:198: in <module> @fixtures.fixture test/fixtures.py:36: in fixture return pytest.fixture(scope, params, autouse, ids, name) E TypeError: 'bool' object is not iterable We do not use non-keyword arguments, so it is safe to use `*` instead of `*args` in the prototype. Change-Id: I96220e0e85249ad58880e5de75f8987a0fdc16ef
pytest 5.2.0 added support for callable scopes. In order to distinguish those (`@pytest.fixture(scope=fn)`) from decorators (`@pytest.fixture`), it added extra arguments which was not expected by our wrapper. See pytest-dev/pytest#5776 for the change. Fixes the following error: ImportError while loading conftest 'test/conftest.py'. test/conftest.py:42: in <module> from fixtures_ws import * test/fixtures_ws.py:198: in <module> @fixtures.fixture test/fixtures.py:36: in fixture return pytest.fixture(scope, params, autouse, ids, name) E TypeError: 'bool' object is not iterable We do not use non-keyword arguments, so it is safe to use `*` instead of `*args` in the prototype. Change-Id: I96220e0e85249ad58880e5de75f8987a0fdc16ef Reviewed-on: https://code.wireshark.org/review/34672 Petri-Dish: Peter Wu <peter@lekensteyn.nl> Tested-by: Petri Dish Buildbot Reviewed-by: Peter Wu <peter@lekensteyn.nl>
pytest 5.2.0 added support for callable scopes. In order to distinguish those (`@pytest.fixture(scope=fn)`) from decorators (`@pytest.fixture`), it added extra arguments which was not expected by our wrapper. See pytest-dev/pytest#5776 for the change. Fixes the following error: ImportError while loading conftest 'test/conftest.py'. test/conftest.py:42: in <module> from fixtures_ws import * test/fixtures_ws.py:198: in <module> @fixtures.fixture test/fixtures.py:36: in fixture return pytest.fixture(scope, params, autouse, ids, name) E TypeError: 'bool' object is not iterable We do not use non-keyword arguments, so it is safe to use `*` instead of `*args` in the prototype. Change-Id: I96220e0e85249ad58880e5de75f8987a0fdc16ef Reviewed-on: https://code.wireshark.org/review/34672 Petri-Dish: Peter Wu <peter@lekensteyn.nl> Tested-by: Petri Dish Buildbot Reviewed-by: Peter Wu <peter@lekensteyn.nl> (cherry picked from commit ba35c23) Reviewed-on: https://code.wireshark.org/review/34685
pytest 5.2.0 added support for callable scopes. In order to distinguish those (`@pytest.fixture(scope=fn)`) from decorators (`@pytest.fixture`), it added extra arguments which was not expected by our wrapper. See pytest-dev/pytest#5776 for the change. Fixes the following error: ImportError while loading conftest 'test/conftest.py'. test/conftest.py:42: in <module> from fixtures_ws import * test/fixtures_ws.py:198: in <module> @fixtures.fixture test/fixtures.py:36: in fixture return pytest.fixture(scope, params, autouse, ids, name) E TypeError: 'bool' object is not iterable We do not use non-keyword arguments, so it is safe to use `*` instead of `*args` in the prototype. Change-Id: I96220e0e85249ad58880e5de75f8987a0fdc16ef Reviewed-on: https://code.wireshark.org/review/34672 Petri-Dish: Peter Wu <peter@lekensteyn.nl> Tested-by: Petri Dish Buildbot Reviewed-by: Peter Wu <peter@lekensteyn.nl> (cherry picked from commit ba35c23) Reviewed-on: https://code.wireshark.org/review/34685
This is an attempt to implement the dynamic scope feature described in #1682.