Skip to content

Commit

Permalink
doctest: fix autouse fixtures possibly not getting picked up
Browse files Browse the repository at this point in the history
Fix #11929.

Figured out what's going on. We have the following collection tree:

```
<Dir pyspacewar>
  <Dir src>
    <Package pyspacewar>
      <Package tests>
        <DoctestModule test_main.py>
          <DoctestItem pyspacewar.tests.test_main.doctest_main>
```

And the `test_main.py` contains an autouse fixture (`fake_game_ui`) that
`doctest_main` needs in order to run properly. The fixture doesn't run!
It doesn't run because nothing collects the fixtures from (calls
`parsefactories()` on) the `test_main.py` `DoctestModule`.

How come it only started happening with commit
ab63ebb? Turns out it mostly only
worked accidentally. Each `DoctestModule` is also collected as a normal
`Module`, with the `Module` collected after the `DoctestModule`. For
example, if we add a non-doctest test to `test_main.py`, the collection
tree looks like this:

```
<Dir pyspacewar>
  <Dir src>
    <Package pyspacewar>
      <Package tests>
        <DoctestModule test_main.py>
          <DoctestItem pyspacewar.tests.test_main.doctest_main>
        <Module test_main.py>
          <Function test_it>
```

Now, `Module` *does* collect fixtures. When autouse fixtures are
collected, they are added to the `_nodeid_autousenames` dict.

Before ab63ebb, `DoctestItem` consults
`_nodeid_autousenames` at *setup* time. At this point, the `Module` has
collected and so it ended up picking the autouse fixture (this relies on
another "accident", that the `DoctestModule` and `Module` have the same
node ID).

After ab63ebb, `DoctestItem` consults
`_nodeid_autousenames` at *collection* time (= when it's created). At
this point, the `Module` hasn't collected yet, so the autouse fixture is
not picked out.

The fix is simple -- have `DoctestModule.collect()` call
`parsefactories`. From some testing I've done it shouldn't have negative
consequences (I hope).
  • Loading branch information
bluetech committed Feb 7, 2024
1 parent 6e5008f commit 9cd14b4
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 0 deletions.
1 change: 1 addition & 0 deletions changelog/11929.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix a regression in pytest 8.0.0 whereby autouse fixtures defined in a module get ignored by the doctests in the module.
4 changes: 4 additions & 0 deletions src/_pytest/doctest.py
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,10 @@ def _from_module(self, module, object):
else:
raise

# While doctests currently don't support fixtures directly, we still
# need to pick up autouse fixtures.
self.session._fixturemanager.parsefactories(self)

# Uses internal doctest module parsing mechanism.
finder = MockAwareDocTestFinder()
optionflags = get_optionflags(self.config)
Expand Down
32 changes: 32 additions & 0 deletions testing/test_doctest.py
Original file line number Diff line number Diff line change
Expand Up @@ -1376,6 +1376,38 @@ def auto(request):
str(result.stdout.no_fnmatch_line("*FAILURES*"))
result.stdout.fnmatch_lines(["*=== 1 passed in *"])

@pytest.mark.parametrize("scope", [*SCOPES, "package"])
def test_auto_use_defined_in_same_module(
self, pytester: Pytester, scope: str
) -> None:
"""Autouse fixtures defined in the same module as the doctest get picked
up properly.
Regression test for #11929.
"""
pytester.makepyfile(
f"""
import pytest
AUTO = "the fixture did not run"
@pytest.fixture(autouse=True, scope="{scope}")
def auto(request):
global AUTO
AUTO = "the fixture ran"
def my_doctest():
'''My doctest.
>>> my_doctest()
'the fixture ran'
'''
return AUTO
"""
)
result = pytester.runpytest("--doctest-modules")
result.assert_outcomes(passed=1)


class TestDoctestNamespaceFixture:
SCOPES = ["module", "session", "class", "function"]
Expand Down

0 comments on commit 9cd14b4

Please sign in to comment.