Repository navigation
Package scoped fixture will not execute teardown #8189
Description
Activity
- addedstatus: help wanteddevelopers would like help from experts on this topicdevelopers would like help from experts on this topictopic: fixturesanything involving fixtures directly or indirectlyanything involving fixtures directly or indirectly
on Dec 26, 2020 Just to make sure I understand, what you expect is that the first example have the same behavior as the second?
You have 3 packages in play:
test,test.sub1,test.sub2. In the first example the fixture's scope is packagetest. In the second example the fixtures' scopes aretest.sub1andtest.sub2.@Zac-HD, so should I understand package fixture scope such as:
- starts before the first test requiring the fixture
- ends with the last test within package scope
- package scope is literally the package fixture was defined in
To be honest I am a little confuesd with your explanation because every other scope, except of maybe session has different behavior.
For example what should happen if:- there is a single module scoped fixture in conftest file at test-root directory named say 'my_fixture'
- there is some amount of packages in same test-root directory
- there is some amount of test modules in each package
- there are some tests in each module and each of them require the same 'my_fixture' fixture
What would setup/teardown plan look like in this case? And to answer your question about my expectation - i kinda expect the same setup/teardown behavior to be applied to package level scopes, with exception that the boundary is on the package but not the module.
And to proove my point further - another way to get expected (at least by me) behavior is:
- have test directory structure
~ tree test test ├── __init__.py ├── conftest.py ├── sub1 │ └── __init__.py │ └── conftest.py │ └── test_foo.py └── sub2 └── __init__.py └── conftest.py └── test_bar.py- leave
test/conftest.pythe same as in report - have
test/sub1/conftest.pyandtest/sub2/conftest.pysimply import fixture and nothing else
Reacted by Edmund@Zac-HD I think the best way to summarize is that currently package scope is related to "the package" when I expect it to be for "a package"
Thanks for the details @hennadii-demchenko. I agree with your intuition here. I think the package scope and/or conftest handling is quite buggy. There's also some related discussion in #7777 (in the sense that it affects the behavior here).
I'm marking this as a bug. Personally I do plan to dig into this more soon.
Reacted by Edmund- addedtype: bugproblem that needs to be addressedproblem that needs to be addressed
on Dec 29, 2020 @bluetech, thanks I'll keep an eye on the progress.
Here's a quick tip on workaround for other people who might stumble upon this issue (it is easy although not very beatiful)
All you need to do is:- create
conftestfile in each package you want to use the fixture - just import the fixture function in
conftestfile
Reacted by Edmund- create
@bluetech any luck on getting back to this issue?
to add extra context, each import of a fixture creates a new definition point, thus iporting the fixtures to closer file location points fixes the issues by creating new definitions
its not clear to me whats the exact scoping of package scope fixtures, as they can have multiple points of reference its something tricky to get right
Hey, it's been a while. Any chance this is gonna be fixed at all?
I just met this exact issue.
- added 2 commits that reference this issue
on Apr 26, 2022 13 remaining items
@bluetech, sure, I expect that the package fixture has setup executed at the beginning of each package, and teardown at the end of each package.
Example:(.venv) $ pytest test --setup-plan =========================== test session starts ============================ platform linux -- Python 3.11.7, pytest-8.0.1, pluggy-1.4.0 rootdir: /dev/shm collected 2 items test/sub1/test_foo.py SETUP P package_scoped test/sub1/test_foo.py::test_foo (fixtures used: package_scoped) TEARDOWN P package_scoped test/sub2/test_bar.py SETUP P package_scoped test/sub2/test_bar.py::test_foo (fixtures used: package_scoped) TEARDOWN P package_scopedOk, I think I finally get where the confusion comes from.
Consider following:
- there's a package "papa"
- there's two sub-packages in it: "alpha" and "bravo"
- there's a fixture "foxtrot" inside a contest file in the directory of package "papa"
My assumptions are:
- when I declare only two tests inside of the "alpha" package, both of which are using fixture "foxtrot"
- there will be setup just before the first "alpha" test
- there will be teardown just after the second "alpha" test
- the "foxtrot" fixture doesn't have to be explicitly declared anywhere inside of the "alpha" package
- when I also declare three tests inside of the "bravo" package, where the fixture "foxtrot" is used only in the second test
- there will be setup just before the second "bravo" test
- there will be teardown just after the second "bravo" test
- the "foxtrot" fixture doesn't have to be explicitly declared anywhere inside of the "bravo" package
What I think is happening:
the pytest finds the declaration of the "foxtrot" fixture and assumes it applies to the boundaries of the "papa" package but not to the sub-packages ("alpha" and "bravo")Reacted by Edmund@bluetech, I am pretty sure this issue will be easily forgotten since it is in a closed state. I understand it is better to open a new one and mention this, please confirm or deny.
I'm seeing something similar, where we have a package fixture defined in conftest and three packages that each use the fixture. I expected the fixture to run three times, once for each package, but it is running only once per session.
Maybe the same as #8712?
There seems to be an obscure edge when the workaround is not properly applied - who knows, could it give some insights to help find a solution to the core issue...
Let's say that the example above is modified so the test in the
bravopackage gets the fixture by importing the fixture in the test module itself, instead of importing it inbravo/conftest.py(which is non-existent in this case):test-pytest-pkgconfig (master)$ git ls-tree -r HEAD: 100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 alpha/__init__.py 100644 blob 9c38efcc7709d279136b2f4f0b3d4c27f0589cb8 alpha/conftest.py 100644 blob 71a86e745c7495f5cde764116d3cf95fe063325a alpha/test.py 100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 bravo/__init__.py 100644 blob bbc10c110a091de13ea8944be24517e40e8e418b bravo/test.py 100644 blob a9188659c3a0132ad7b25334a303ecebb4834934 pkgfixtures.pyThe setup will appear to work as long as
alpharuns beforebravo:test-pytest-pkgconfig (master)$ pytest --setup-plan alpha/test.py bravo/test.py === platform linux -- Python 3.11.2, pytest-7.2.1, pluggy-1.0.0+repack rootdir: /home/user/tmp/test-pytest-pkgconfig plugins: order-1.0.1, dependency-0.5.1 collected 2 items alpha/test.py SETUP P foxtrot alpha/test.py::test_alpha (fixtures used: foxtrot) TEARDOWN P foxtrot bravo/test.py SETUP P foxtrot bravo/test.py::test_bravo (fixtures used: foxtrot) TEARDOWN P foxtrot... but as soon as for some reason bravo runs first,
pytestgots some extra confusion, nesting the feature inside itself, which can cause unexpected behaviour depending on what the feature does:test-pytest-pkgconfig (master)$ pytest --setup-plan bravo/test.py alpha/test.py === platform linux -- Python 3.11.2, pytest-7.2.1, pluggy-1.0.0+repack rootdir: /home/user/tmp/test-pytest-pkgconfig plugins: order-1.0.1, dependency-0.5.1 collected 2 items bravo/test.py SETUP P foxtrot bravo/test.py::test_bravo (fixtures used: foxtrot) alpha/test.py SETUP P foxtrot alpha/test.py::test_alpha (fixtures used: foxtrot) TEARDOWN P foxtrot TEARDOWN P foxtrotI'm quite new on the subject, so I'll try to summarize what I understand of the issue.
- fixtures of scopes
function,class,module,sessionare bound to the function|class|module that uses them (since any given line in a test module unambiguously defines one object of each type), or to whatever session uses them, which makes them quite similar. This allows to declare them wherever we want in the package hierarchy's config files - OTOH fixtures of scope 'package' have limited capabilities in terms of what package they bind to in the package hierarchy (since any given line in a test module usually belongs to more than one package, all nested), so there is currently not enough info in the place package-scoped fixtures are used to determine which package they refer to. I guess this is why the package where a package-scoped fixture is declared is used as the package to bind the said fixture to, is that correct?
If that understanding is correct, I expect we could solve the whole issue by letting the usage declaration of the fixture declare which package scope it refers to. And indeed, I see no reason not to extend this to all other scopes, so any given fixture could be used with any scope depending on the needs of the caller, rather than hardcoding a single possible scope for all callers (which can possibly cause headaches on a large and varied test repository) - after all the package-scoped fixtures to give us more flexibility, because we can choose to which level of the package hierarchy we bind them.
Such a scope specification may not be possible to achieve on fixtures that get only declared as a parameter of the test function, but this would be easily dealt with by adding a decorator:
@pytest.mark.usefixtures("foo", "bar", scope="function") class TestFoo: def test_foo(bar): ...package scope would need something more to specify the package itself, which could be a scope of
package:test/alpha, suggesting here to use the syntax of thenodeidprefix rather than pythontest.alphasyntax for consistency with the common naming scheme, but it could also be useful to refer to "this package" or "parent package" - and for this flexibility it could be good to use the same syntax as python imports (allowing eg.test.alphato specify an enclosing package,.for "this package" and...for the grand-parent, and forbidding to refer to any package not part of our ancestry).Does this make any sense to anyone else?
- fixtures of scopes
- added 2 commits that reference this issue
on May 26, 2025
I am experiencing a weird issue:
'package' scoped fixture will not execute teardown after last test in package is completed if afformentioned fixture is defined outside of package
Environment
Example to reproduce
Result
If I create two conftest files in each package with the package scoped fixture I intend to use then I can see it behaving as I was expecting.
Example: