Skip to content

Commit 1ca8831

Browse files
Merge branch 'fix-output-order-dictionary-keys' of https://github.com/ritvi-alagusankar/pytest into fix-output-order-dictionary-keys
2 parents 07f81b2 + 8c36994 commit 1ca8831

File tree

14 files changed

+341
-105
lines changed

14 files changed

+341
-105
lines changed

.pre-commit-config.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
repos:
22
- repo: https://github.com/astral-sh/ruff-pre-commit
3-
rev: "v0.12.1"
3+
rev: "v0.12.3"
44
hooks:
55
- id: ruff
66
args: ["--fix"]
@@ -12,7 +12,7 @@ repos:
1212
- id: end-of-file-fixer
1313
- id: check-yaml
1414
- repo: https://github.com/woodruffw/zizmor-pre-commit
15-
rev: v1.10.0
15+
rev: v1.11.0
1616
hooks:
1717
- id: zizmor
1818
- repo: https://github.com/adamchainz/blacken-docs
@@ -48,7 +48,7 @@ repos:
4848
# on <3.11
4949
- exceptiongroup>=1.0.0rc8
5050
- repo: https://github.com/RobertCraigie/pyright-python
51-
rev: v1.1.402
51+
rev: v1.1.403
5252
hooks:
5353
- id: pyright
5454
files: ^(src/|scripts/)

changelog/13478.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed a crash when using :confval:`console_output_style` with ``times`` and a module is skipped.

changelog/478.feature.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Support PEP420 (implicit namespace packages) as `--pyargs` target when :confval:`consider_namespace_packages` is `true` in the config.
2+
3+
Previously, this option only impacted package names, now it also impacts tests discovery.

doc/en/how-to/assert.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,7 @@ Note that you still get the benefits of assertion introspection, the only change
556556
the ``.pyc`` files won't be cached on disk.
557557

558558
Additionally, rewriting will silently skip caching if it cannot write new ``.pyc`` files,
559-
i.e. in a read-only filesystem or a zipfile.
559+
e.g. in a read-only filesystem or a zipfile.
560560

561561

562562
Disabling assert rewriting

doc/en/how-to/doctest.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ While the built-in pytest support provides a good set of functionalities for usi
307307
doctests, if you use them extensively you might be interested in those external packages
308308
which add many more features, and include pytest integration:
309309

310-
* `pytest-doctestplus <https://github.com/astropy/pytest-doctestplus>`__: provides
310+
* `pytest-doctestplus <https://github.com/scientific-python/pytest-doctestplus>`__: provides
311311
advanced doctest support and enables the testing of reStructuredText (".rst") files.
312312

313313
* `Sybil <https://sybil.readthedocs.io>`__: provides a way to test examples in

doc/en/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
.. sidebar:: **Next Open Trainings and Events**
44

5-
- `pytest - simple, rapid and fun testing with Python <https://ep2025.europython.eu/session/pytest-simple-rapid-and-fun-testing-with-python/>`_, at `EuroPython 2025 <https://ep2025.europython.eu/>`_, **July 14th** (3h), Prague, Czech Republic
5+
- `Testen mit pytest <https://www.letsboot.ch/kurs/pytest>`_ (German), via `Letsboot <https://www.letsboot.ch/>`_ (3 day in-depth training), **October 29th -- 31st**, Zurich (CH)
66
- `Professional Testing with Python <https://python-academy.com/courses/python_course_testing.html>`_, via `Python Academy <https://www.python-academy.com/>`_ (3 day in-depth training), **March 3th -- 5th 2026**, Leipzig (DE) / Remote
77

88
Also see :doc:`previous talks and blogposts <talks>`

doc/en/reference/plugin_list.rst

Lines changed: 191 additions & 87 deletions
Large diffs are not rendered by default.

doc/en/reference/reference.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,6 +1384,7 @@ passed multiple times. The expected format is ``name=value``. For example::
13841384
when collecting Python modules. Default is ``False``.
13851385

13861386
Set to ``True`` if the package you are testing is part of a namespace package.
1387+
Namespace packages are also supported as ``--pyargs`` target.
13871388

13881389
Only `native namespace packages <https://packaging.python.org/en/latest/guides/packaging-namespace-packages/#native-namespace-packages>`__
13891390
are supported, with no plans to support `legacy namespace packages <https://packaging.python.org/en/latest/guides/packaging-namespace-packages/#legacy-namespace-packages>`__.

src/_pytest/main.py

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,9 @@ def perform_collect(
774774
self._collection_cache = {}
775775
self.items = []
776776
items: Sequence[nodes.Item | nodes.Collector] = self.items
777+
consider_namespace_packages: bool = self.config.getini(
778+
"consider_namespace_packages"
779+
)
777780
try:
778781
initialpaths: list[Path] = []
779782
initialpaths_with_parents: list[Path] = []
@@ -782,6 +785,7 @@ def perform_collect(
782785
self.config.invocation_params.dir,
783786
arg,
784787
as_pypath=self.config.option.pyargs,
788+
consider_namespace_packages=consider_namespace_packages,
785789
)
786790
self._initial_parts.append(collection_argument)
787791
initialpaths.append(collection_argument.path)
@@ -981,7 +985,9 @@ def genitems(self, node: nodes.Item | nodes.Collector) -> Iterator[nodes.Item]:
981985
node.ihook.pytest_collectreport(report=rep)
982986

983987

984-
def search_pypath(module_name: str) -> str | None:
988+
def search_pypath(
989+
module_name: str, *, consider_namespace_packages: bool = False
990+
) -> str | None:
985991
"""Search sys.path for the given a dotted module name, and return its file
986992
system path if found."""
987993
try:
@@ -991,13 +997,29 @@ def search_pypath(module_name: str) -> str | None:
991997
# ValueError: not a module name
992998
except (AttributeError, ImportError, ValueError):
993999
return None
994-
if spec is None or spec.origin is None or spec.origin == "namespace":
1000+
1001+
if spec is None:
9951002
return None
996-
elif spec.submodule_search_locations:
997-
return os.path.dirname(spec.origin)
998-
else:
1003+
1004+
if (
1005+
spec.submodule_search_locations is None
1006+
or len(spec.submodule_search_locations) == 0
1007+
):
1008+
# Must be a simple module.
9991009
return spec.origin
10001010

1011+
if consider_namespace_packages:
1012+
# If submodule_search_locations is set, it's a package (regular or namespace).
1013+
# Typically there is a single entry, but documentation claims it can be empty too
1014+
# (e.g. if the package has no physical location).
1015+
return spec.submodule_search_locations[0]
1016+
1017+
if spec.origin is None:
1018+
# This is only the case for namespace packages
1019+
return None
1020+
1021+
return os.path.dirname(spec.origin)
1022+
10011023

10021024
@dataclasses.dataclass(frozen=True)
10031025
class CollectionArgument:
@@ -1009,7 +1031,11 @@ class CollectionArgument:
10091031

10101032

10111033
def resolve_collection_argument(
1012-
invocation_path: Path, arg: str, *, as_pypath: bool = False
1034+
invocation_path: Path,
1035+
arg: str,
1036+
*,
1037+
as_pypath: bool = False,
1038+
consider_namespace_packages: bool = False,
10131039
) -> CollectionArgument:
10141040
"""Parse path arguments optionally containing selection parts and return (fspath, names).
10151041
@@ -1049,7 +1075,9 @@ def resolve_collection_argument(
10491075
parts[-1] = f"{parts[-1]}{squacket}{rest}"
10501076
module_name = None
10511077
if as_pypath:
1052-
pyarg_strpath = search_pypath(strpath)
1078+
pyarg_strpath = search_pypath(
1079+
strpath, consider_namespace_packages=consider_namespace_packages
1080+
)
10531081
if pyarg_strpath is not None:
10541082
module_name = strpath
10551083
strpath = pyarg_strpath

src/_pytest/terminal.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -734,7 +734,9 @@ def _get_progress_information_message(self) -> str:
734734
last_in_module = tests_completed == tests_in_module
735735
if self.showlongtestinfo or last_in_module:
736736
self._timing_nodeids_reported.update(r.nodeid for r in not_reported)
737-
return format_node_duration(sum(r.duration for r in not_reported))
737+
return format_node_duration(
738+
sum(r.duration for r in not_reported if isinstance(r, TestReport))
739+
)
738740
return ""
739741
if collected:
740742
return f" [{len(self._progress_nodeids_reported) * 100 // collected:3d}%]"

0 commit comments

Comments
 (0)