← 返回首页
Fixture override order should be determined by visibility? · Issue #14513 · pytest-dev/pytest · GitHub
Skip to content

Navigation Menu

Toggle navigation
Sign in
Appearance settings
Search or jump to...

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Include my email address so I can be contacted

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Resetting focus

Fixture override order should be determined by visibility? #14513

New issue
New issue

Description

Background

Pytest stores fixture definitions (FixtureDefs) for the same name in a list. When a test requests a fixture (by name), the fixturedef to use is picked as follows:

  • The fixturedefs that are not visible to the node are filtered out (matchfactories)
  • Then the last remaining fixturedef in the list (i.e. at position -1) is used

If the used fixturedef itself then requests the same fixture name, then the next one in the list from the end is used (i.e. at position -2), and so on. In other words the order of the list determines the override chain.

How is the order of the list determined?

  • As plugins are discovered and modules & classes are collected, the discovered fixturedefs are appended to the list for the name.
  • There is one tweak to the above: fixtures which have "no location" (registered by non-conftest plugins) are inserted before fixtures with location, so they are last in the override chain (ref).

Problem

I started looking at exposing a pytest.register_fixture function (#12376), and I want to get rid of the "has location" notion (see #12376 (comment)), because it relies on a None baseid which we want to get rid of (and always use a Node, specifically Session for current no-location fixturedefs).

There is also IMO a more conceptual problem with the mechanism: the override chain order is determined by the order in which fixtures are registered, which is kind of incidental. This is currently not really a problem in practice, since the fixture registration order mostly matches the order we want (since 46478fa improved things) -- core plugins, 3rd party plugins, the collection tree (in depth-first order). But with pytest.register_fixture we will be giving more freedom to get into odd situations, e.g.:

# conftest.py import pytest @pytest.hookimpl(wrapper=True) def pytest_collection(session): result = yield fm = session._fixturemanager fm._register_fixture(name="fix", func=lambda: "item", node=session.items[0]) fm._register_fixture(name="fix", func=lambda: "session", node=session) return result
# test.py def test_it(fix): assert fix == "item"

I would except fix to be "item" here, because if someone registered the fixture specifically for the item, it should be preferred over some more general fixture. But currently fix is actually "session", just because of the order of the two register_fixture calls.

Proposal / idea

It seems to me that there is a natural partial order that we should impose -- the visibility of the fixturedefs. So e.g. a fixture registered on the Module will be preferred over a fixture registered on the Session`. This generalizes the somewhat clumsy "no location" ordering.

So my current idea is:

  1. Get rid of/deprecate FixtureDef.has_location.
  2. Let < be the partial order on nodes such that a node is < its ancestors. Order the fixturedef list for a name by fixturedef.node (in reverse, similar to now). For non-comparable nodes, maintain the registration order (last to register wins).

Remember that matchfactories filters out non-visible fixturedefs, so from the perspective of an item, the override chain will be strictly ordered (first by the visibility, than by registration order).

Not sure if it can be implemented cleanly and efficiently yet, I will try it soon.

Metadata

Metadata

Assignees

Labels

topic: fixturesanything involving fixtures directly or indirectlytype: proposalproposal for a new feature, often to gather opinions or design the API around the new feature

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions

    Footer

    © 2026 GitHub, Inc.