Skip to content
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

avoid using __file__ in pytest_plugin_registered as can be wrong on Windows #11825

Merged
merged 4 commits 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 @@ -490,15 +490,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 @@ -1483,25 +1483,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 1504 in src/_pytest/fixtures.py

View check run for this annotation

Codecov / codecov/patch

src/_pytest/fixtures.py#L1504

Added line #L1504 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 @@ -63,12 +63,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 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