Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

[8.0.x] avoid using __file__ in pytest_plugin_registered as can be wrong on Windows #11831

Merged
merged 1 commit into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ repos:
additional_dependencies:
- iniconfig>=1.1.0
- attrs>=19.2.0
- pluggy
- packaging
- tomli
- types-pkg_resources
Expand Down
1 change: 1 addition & 0 deletions changelog/11825.improvement.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The :hook:`pytest_plugin_registered` hook has a new ``plugin_name`` parameter containing the name by which ``plugin`` is registered.
12 changes: 8 additions & 4 deletions src/_pytest/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -496,15 +496,19 @@ def register(
)
)
return None
ret: Optional[str] = super().register(plugin, name)
if ret:
plugin_name = super().register(plugin, name)
if plugin_name is not None:
self.hook.pytest_plugin_registered.call_historic(
kwargs=dict(plugin=plugin, manager=self)
kwargs=dict(
plugin=plugin,
plugin_name=plugin_name,
manager=self,
)
)

if isinstance(plugin, types.ModuleType):
self.consider_module(plugin)
return ret
return plugin_name

def getplugin(self, name: str):
# Support deprecated naming because plugins (xdist e.g.) use it.
Expand Down
38 changes: 20 additions & 18 deletions src/_pytest/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -1495,25 +1495,27 @@

return FuncFixtureInfo(argnames, initialnames, names_closure, arg2fixturedefs)

def pytest_plugin_registered(self, plugin: _PluggyPlugin) -> None:
nodeid = None
try:
p = absolutepath(plugin.__file__) # type: ignore[attr-defined]
except AttributeError:
pass
def pytest_plugin_registered(self, plugin: _PluggyPlugin, plugin_name: str) -> None:
# Fixtures defined in conftest plugins are only visible to within the
# conftest's directory. This is unlike fixtures in non-conftest plugins
# which have global visibility. So for conftests, construct the base
# nodeid from the plugin name (which is the conftest path).
if plugin_name and plugin_name.endswith("conftest.py"):
# Note: we explicitly do *not* use `plugin.__file__` here -- The
# difference is that plugin_name has the correct capitalization on
# case-insensitive systems (Windows) and other normalization issues
# (issue #11816).
conftestpath = absolutepath(plugin_name)
try:
nodeid = str(conftestpath.parent.relative_to(self.config.rootpath))
except ValueError:
nodeid = ""
if nodeid == ".":
nodeid = ""
if os.sep != nodes.SEP:
nodeid = nodeid.replace(os.sep, nodes.SEP)

Check warning on line 1516 in src/_pytest/fixtures.py

View check run for this annotation

Codecov / codecov/patch

src/_pytest/fixtures.py#L1516

Added line #L1516 was not covered by tests
else:
# Construct the base nodeid which is later used to check
# what fixtures are visible for particular tests (as denoted
# by their test id).
if p.name == "conftest.py":
try:
nodeid = str(p.parent.relative_to(self.config.rootpath))
except ValueError:
nodeid = ""
if nodeid == ".":
nodeid = ""
if os.sep != nodes.SEP:
nodeid = nodeid.replace(os.sep, nodes.SEP)
nodeid = None

self.parsefactories(plugin, nodeid)

Expand Down
7 changes: 5 additions & 2 deletions src/_pytest/hookspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,15 @@ def pytest_addhooks(pluginmanager: "PytestPluginManager") -> None:

@hookspec(historic=True)
def pytest_plugin_registered(
plugin: "_PluggyPlugin", manager: "PytestPluginManager"
plugin: "_PluggyPlugin",
plugin_name: str,
manager: "PytestPluginManager",
) -> None:
"""A new pytest plugin got registered.

:param plugin: The plugin module or instance.
:param pytest.PytestPluginManager manager: pytest plugin manager.
:param plugin_name: The name by which the plugin is registered.
:param manager: The pytest plugin manager.

.. note::
This hook is incompatible with hook wrappers.
Expand Down
Loading