diff --git a/doc/en/_templates/slim_searchbox.html b/doc/en/_templates/slim_searchbox.html
index e98ad4ed905..f088ff8d312 100644
--- a/doc/en/_templates/slim_searchbox.html
+++ b/doc/en/_templates/slim_searchbox.html
@@ -5,11 +5,10 @@
-
+
{%- endif %}
diff --git a/doc/en/announce/index.rst b/doc/en/announce/index.rst
index 78045114667..5374e8c7596 100644
--- a/doc/en/announce/index.rst
+++ b/doc/en/announce/index.rst
@@ -6,6 +6,7 @@ Release announcements
:maxdepth: 2
+ release-8.0.2
release-8.0.1
release-8.0.0
release-8.0.0rc2
diff --git a/doc/en/announce/release-8.0.2.rst b/doc/en/announce/release-8.0.2.rst
new file mode 100644
index 00000000000..c42159c57cf
--- /dev/null
+++ b/doc/en/announce/release-8.0.2.rst
@@ -0,0 +1,18 @@
+pytest-8.0.2
+=======================================
+
+pytest 8.0.2 has just been released to PyPI.
+
+This is a bug-fix release, being a drop-in replacement. To upgrade::
+
+ pip install --upgrade pytest
+
+The full changelog is available at https://docs.pytest.org/en/stable/changelog.html.
+
+Thanks to all of the contributors to this release:
+
+* Ran Benita
+
+
+Happy testing,
+The pytest Development Team
diff --git a/doc/en/changelog.rst b/doc/en/changelog.rst
index f5019541ae3..f01842c9b92 100644
--- a/doc/en/changelog.rst
+++ b/doc/en/changelog.rst
@@ -28,6 +28,21 @@ with advance notice in the **Deprecations** section of releases.
.. towncrier release notes start
+pytest 8.0.2 (2024-02-24)
+=========================
+
+Bug Fixes
+---------
+
+- `#11895 `_: Fix collection on Windows where initial paths contain the short version of a path (for example ``c:\PROGRA~1\tests``).
+
+
+- `#11953 `_: Fix an ``IndexError`` crash raising from ``getstatementrange_ast``.
+
+
+- `#12021 `_: Reverted a fix to `--maxfail` handling in pytest 8.0.0 because it caused a regression in pytest-xdist whereby session fixture teardowns may get executed multiple times when the max-fails is reached.
+
+
pytest 8.0.1 (2024-02-16)
=========================
@@ -85,6 +100,8 @@ Bug Fixes
- `#11706 `_: Fix reporting of teardown errors in higher-scoped fixtures when using `--maxfail` or `--stepwise`.
+ NOTE: This change was reverted in pytest 8.0.2 to fix a `regression `_ it caused in pytest-xdist.
+
- `#11758 `_: Fixed ``IndexError: string index out of range`` crash in ``if highlighted[-1] == "\n" and source[-1] != "\n"``.
This bug was introduced in pytest 8.0.0rc1.
diff --git a/doc/en/example/parametrize.rst b/doc/en/example/parametrize.rst
index 99afaded84c..c6ac6489979 100644
--- a/doc/en/example/parametrize.rst
+++ b/doc/en/example/parametrize.rst
@@ -162,7 +162,7 @@ objects, they are still using the default pytest representation:
rootdir: /home/sweet/project
collected 8 items
-
+
@@ -239,7 +239,7 @@ If you just collect tests you'll also nicely see 'advanced' and 'basic' as varia
rootdir: /home/sweet/project
collected 4 items
-
+
@@ -318,7 +318,7 @@ Let's first see how it looks like at collection time:
rootdir: /home/sweet/project
collected 2 items
-
+
diff --git a/doc/en/example/pythoncollection.rst b/doc/en/example/pythoncollection.rst
index 89acb7d3b65..7207ca2ae63 100644
--- a/doc/en/example/pythoncollection.rst
+++ b/doc/en/example/pythoncollection.rst
@@ -152,7 +152,7 @@ The test collection would look like this:
configfile: pytest.ini
collected 2 items
-
+
@@ -215,7 +215,7 @@ You can always peek at the collection tree without running tests like this:
configfile: pytest.ini
collected 3 items
-
+
diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst
index 114d69328c2..f1919886495 100644
--- a/doc/en/getting-started.rst
+++ b/doc/en/getting-started.rst
@@ -22,7 +22,7 @@ Install ``pytest``
.. code-block:: bash
$ pytest --version
- pytest 8.0.1
+ pytest 8.0.2
.. _`simpletest`:
diff --git a/doc/en/how-to/fixtures.rst b/doc/en/how-to/fixtures.rst
index 281b0d7f284..960e60c92b4 100644
--- a/doc/en/how-to/fixtures.rst
+++ b/doc/en/how-to/fixtures.rst
@@ -1418,7 +1418,7 @@ Running the above tests results in the following test IDs being used:
rootdir: /home/sweet/project
collected 12 items
-
+
diff --git a/doc/en/requirements.txt b/doc/en/requirements.txt
index 36801746aae..52415740470 100644
--- a/doc/en/requirements.txt
+++ b/doc/en/requirements.txt
@@ -2,7 +2,7 @@ pallets-sphinx-themes
pluggy>=1.2.0
pygments-pytest>=2.3.0
sphinx-removed-in>=0.2.0
-sphinx>=5,<8
+sphinx>=7
sphinxcontrib-trio
sphinxcontrib-svg2pdfconverter
# Pin packaging because it no longer handles 'latest' version, which
diff --git a/src/_pytest/_code/source.py b/src/_pytest/_code/source.py
index 359da868c2d..835cd1d7b6a 100644
--- a/src/_pytest/_code/source.py
+++ b/src/_pytest/_code/source.py
@@ -196,7 +196,9 @@ def getstatementrange_ast(
# by using the BlockFinder helper used which inspect.getsource() uses itself.
block_finder = inspect.BlockFinder()
# If we start with an indented line, put blockfinder to "started" mode.
- block_finder.started = source.lines[start][0].isspace()
+ block_finder.started = (
+ bool(source.lines[start]) and source.lines[start][0].isspace()
+ )
it = ((x + "\n") for x in source.lines[start:end])
try:
for tok in tokenize.generate_tokens(lambda: next(it)):
diff --git a/src/_pytest/main.py b/src/_pytest/main.py
index f1c05754b2f..fd9dddfa318 100644
--- a/src/_pytest/main.py
+++ b/src/_pytest/main.py
@@ -902,6 +902,10 @@ def collect(self) -> Iterator[Union[nodes.Item, nodes.Collector]]:
# Path part e.g. `/a/b/` in `/a/b/test_file.py::TestIt::test_it`.
if isinstance(matchparts[0], Path):
is_match = node.path == matchparts[0]
+ if sys.platform == "win32" and not is_match:
+ # In case the file paths do not match, fallback to samefile() to
+ # account for short-paths on Windows (#11895).
+ is_match = os.path.samefile(node.path, matchparts[0])
# Name part e.g. `TestIt` in `/a/b/test_file.py::TestIt::test_it`.
else:
# TODO: Remove parametrized workaround once collection structure contains
diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py
index 131b125ed59..5befb0af11c 100644
--- a/src/_pytest/runner.py
+++ b/src/_pytest/runner.py
@@ -132,10 +132,6 @@ def runtestprotocol(
show_test_item(item)
if not item.config.getoption("setuponly", False):
reports.append(call_and_report(item, "call", log))
- # If the session is about to fail or stop, teardown everything - this is
- # necessary to correctly report fixture teardown errors (see #11706)
- if item.session.shouldfail or item.session.shouldstop:
- nextitem = None
reports.append(call_and_report(item, "teardown", log, nextitem=nextitem))
# After all teardown hooks have been called
# want funcargs and request info to go away.
diff --git a/testing/test_collection.py b/testing/test_collection.py
index b2780eb73ae..c7923c5efb6 100644
--- a/testing/test_collection.py
+++ b/testing/test_collection.py
@@ -3,9 +3,11 @@
import pprint
import shutil
import sys
+import tempfile
import textwrap
from typing import List
+from _pytest.assertion.util import running_on_ci
from _pytest.config import ExitCode
from _pytest.fixtures import FixtureRequest
from _pytest.main import _in_venv
@@ -1758,3 +1760,29 @@ def test_foo(): assert True
assert result.ret == ExitCode.OK
assert result.parseoutcomes() == {"passed": 1}
+
+
+@pytest.mark.skipif(not sys.platform.startswith("win"), reason="Windows only")
+def test_collect_short_file_windows(pytester: Pytester) -> None:
+ """Reproducer for #11895: short paths not colleced on Windows."""
+ short_path = tempfile.mkdtemp()
+ if "~" not in short_path: # pragma: no cover
+ if running_on_ci():
+ # On CI, we are expecting that under the current GitHub actions configuration,
+ # tempfile.mkdtemp() is producing short paths, so we want to fail to prevent
+ # this from silently changing without us noticing.
+ pytest.fail(
+ f"tempfile.mkdtemp() failed to produce a short path on CI: {short_path}"
+ )
+ else:
+ # We want to skip failing this test locally in this situation because
+ # depending on the local configuration tempfile.mkdtemp() might not produce a short path:
+ # For example, user might have configured %TEMP% exactly to avoid generating short paths.
+ pytest.skip(
+ f"tempfile.mkdtemp() failed to produce a short path: {short_path}, skipping"
+ )
+
+ test_file = Path(short_path).joinpath("test_collect_short_file_windows.py")
+ test_file.write_text("def test(): pass", encoding="UTF-8")
+ result = pytester.runpytest(short_path)
+ assert result.parseoutcomes() == {"passed": 1}
diff --git a/testing/test_runner.py b/testing/test_runner.py
index 769ce149234..d0fae834bcd 100644
--- a/testing/test_runner.py
+++ b/testing/test_runner.py
@@ -1088,53 +1088,3 @@ def func() -> None:
with pytest.raises(TypeError) as excinfo:
OutcomeException(func) # type: ignore
assert str(excinfo.value) == expected
-
-
-def test_teardown_session_failed(pytester: Pytester) -> None:
- """Test that higher-scoped fixture teardowns run in the context of the last
- item after the test session bails early due to --maxfail.
-
- Regression test for #11706.
- """
- pytester.makepyfile(
- """
- import pytest
-
- @pytest.fixture(scope="module")
- def baz():
- yield
- pytest.fail("This is a failing teardown")
-
- def test_foo(baz):
- pytest.fail("This is a failing test")
-
- def test_bar(): pass
- """
- )
- result = pytester.runpytest("--maxfail=1")
- result.assert_outcomes(failed=1, errors=1)
-
-
-def test_teardown_session_stopped(pytester: Pytester) -> None:
- """Test that higher-scoped fixture teardowns run in the context of the last
- item after the test session bails early due to --stepwise.
-
- Regression test for #11706.
- """
- pytester.makepyfile(
- """
- import pytest
-
- @pytest.fixture(scope="module")
- def baz():
- yield
- pytest.fail("This is a failing teardown")
-
- def test_foo(baz):
- pytest.fail("This is a failing test")
-
- def test_bar(): pass
- """
- )
- result = pytester.runpytest("--stepwise")
- result.assert_outcomes(failed=1, errors=1)