Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: pytest-dev/pytest
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 7.4.2
Choose a base ref
...
head repository: pytest-dev/pytest
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 7.4.3
Choose a head ref
  • 14 commits
  • 21 files changed
  • 8 contributors

Commits on Sep 7, 2023

  1. Merge pull request #11408 from pytest-dev/release-7.4.2

    Prepare release 7.4.2
    nicoddemus authored Sep 7, 2023
    Copy the full SHA
    b0c4775 View commit details

Commits on Sep 8, 2023

  1. Adjustments to the release process (#11410) (#11415)

    As discussed in #11408:
    
    * Improve documentation for the release process.
    * Fix the description for the PRs created by the `prepare release pr` workflow.
    * Fix pushing tag in the `deploy` workflow.
    
    (cherry picked from commit e5c81fa)
    nicoddemus authored Sep 8, 2023
    Copy the full SHA
    c39bdf6 View commit details

Commits on Sep 9, 2023

  1. Fix assert rewriting with assignment expressions (#11414)

    Fixes #11239
    
    (cherry picked from commit 7259e8d)
    cdce8p authored and nicoddemus committed Sep 9, 2023
    Copy the full SHA
    5341b9c View commit details
  2. Skip test_assertion_walrus_different_test_cases on Python 3.7

    nicoddemus committed Sep 9, 2023

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    721a088 View commit details
  3. [7.4.x] fix: closes #11343's [attr-defined] type errors (#11421)

    Co-authored-by: Warren Markham <rabbitsinwarrens@gmail.com>
    github-actions[bot] and WarrenTheRabbit authored Sep 9, 2023
    Copy the full SHA
    d849a3e View commit details
  4. Merge pull request #11419 from nicoddemus/backport-11414-to-7.4.x

    [7.4.x] Fix assert rewriting with assignment expressions (#11414)
    nicoddemus authored Sep 9, 2023
    Copy the full SHA
    946634c View commit details

Commits on Sep 10, 2023

  1. [7.4.x] Fix --import-mode=importlib when root contains __init__.py

    …file (#11426)
    
    Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
    github-actions[bot] and nicoddemus authored Sep 10, 2023
    Copy the full SHA
    1944dc0 View commit details

Commits on Sep 11, 2023

  1. Force terminal width when running tests (#11425) (#11432)

    Related to #11423
    
    (cherry picked from commit 241f2a8)
    nicoddemus authored Sep 11, 2023
    Copy the full SHA
    f8bb857 View commit details

Commits on Sep 20, 2023

  1. [7.4.x] fix for ValueError raised in faulthandler teardown code (#11455)

    Co-authored-by: Simon Blanchard <bnomis@gmail.com>
    github-actions[bot] and bnomis authored Sep 20, 2023
    Copy the full SHA
    21fe071 View commit details

Commits on Oct 23, 2023

  1. [7.4.x] Configure ReadTheDocs to fail on warnings (#11540)

    Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
    github-actions[bot] and nicoddemus authored Oct 23, 2023
    Copy the full SHA
    a517827 View commit details
  2. [7.4.x] Ensure logging tests always cleanup after themselves (#11541)

    Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
    github-actions[bot] and nicoddemus authored Oct 23, 2023
    Copy the full SHA
    5dc7725 View commit details

Commits on Oct 24, 2023

  1. [7.4.x] fix #10447 - consider marks in reverse mro order to give base…

    … classes priority (#11545)
    
    Co-authored-by: Ronny Pfannschmidt <opensource@ronnypfannschmidt.de>
    github-actions[bot] and RonnyPfannschmidt authored Oct 24, 2023
    Copy the full SHA
    44ad1c9 View commit details
  2. Copy the full SHA
    a0714aa View commit details
  3. Tweak changelog.rst

    nicoddemus authored Oct 24, 2023
    Copy the full SHA
    2390610 View commit details
2 changes: 2 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -36,8 +36,10 @@ jobs:
timeout-minutes: 30
permissions:
id-token: write
contents: write
steps:
- uses: actions/checkout@v3

- name: Download Package
uses: actions/download-artifact@v3
with:
4 changes: 4 additions & 0 deletions .readthedocs.yml
Original file line number Diff line number Diff line change
@@ -9,6 +9,10 @@ python:
path: .
- requirements: doc/en/requirements.txt

sphinx:
configuration: doc/en/conf.py
fail_on_warning: true

build:
os: ubuntu-20.04
tools:
2 changes: 2 additions & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
@@ -231,6 +231,7 @@ Maho
Maik Figura
Mandeep Bhutani
Manuel Krebber
Marc Mueller
Marc Schlaich
Marcelo Duarte Trevisani
Marcin Bachry
@@ -336,6 +337,7 @@ Serhii Mozghovyi
Seth Junot
Shantanu Jain
Shubham Adep
Simon Blanchard
Simon Gomizelj
Simon Holesch
Simon Kerr
3 changes: 2 additions & 1 deletion RELEASING.rst
Original file line number Diff line number Diff line change
@@ -134,7 +134,8 @@ Releasing
Both automatic and manual processes described above follow the same steps from this point onward.

#. After all tests pass and the PR has been approved, trigger the ``deploy`` job
in https://github.com/pytest-dev/pytest/actions/workflows/deploy.yml.
in https://github.com/pytest-dev/pytest/actions/workflows/deploy.yml, using the ``release-MAJOR.MINOR.PATCH`` branch
as source.

This job will require approval from ``pytest-dev/core``, after which it will publish to PyPI
and tag the repository.
1 change: 1 addition & 0 deletions doc/en/announce/index.rst
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ Release announcements
:maxdepth: 2


release-7.4.3
release-7.4.2
release-7.4.1
release-7.4.0
19 changes: 19 additions & 0 deletions doc/en/announce/release-7.4.3.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
pytest-7.4.3
=======================================

pytest 7.4.3 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:

* Bruno Oliveira
* Marc Mueller


Happy testing,
The pytest Development Team
15 changes: 15 additions & 0 deletions doc/en/changelog.rst
Original file line number Diff line number Diff line change
@@ -28,6 +28,21 @@ with advance notice in the **Deprecations** section of releases.

.. towncrier release notes start
pytest 7.4.3 (2023-10-24)
=========================

Bug Fixes
---------

- `#10447 <https://github.com/pytest-dev/pytest/issues/10447>`_: Markers are now considered in the reverse mro order to ensure base class markers are considered first -- this resolves a regression.


- `#11239 <https://github.com/pytest-dev/pytest/issues/11239>`_: Fixed ``:=`` in asserts impacting unrelated test cases.


- `#11439 <https://github.com/pytest-dev/pytest/issues/11439>`_: Handled an edge case where :data:`sys.stderr` might already be closed when :ref:`faulthandler` is tearing down.


pytest 7.4.2 (2023-09-07)
=========================

2 changes: 1 addition & 1 deletion doc/en/getting-started.rst
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ Install ``pytest``
.. code-block:: bash
$ pytest --version
pytest 7.4.2
pytest 7.4.3
.. _`simpletest`:

2 changes: 1 addition & 1 deletion doc/en/reference/customize.rst
Original file line number Diff line number Diff line change
@@ -90,7 +90,7 @@ and can also be used to hold pytest configuration if they have a ``[pytest]`` se
setup.cfg
~~~~~~~~~

``setup.cfg`` files are general purpose configuration files, used originally by :doc:`distutils <python:distutils/configfile>`, and can also be used to hold pytest configuration
``setup.cfg`` files are general purpose configuration files, used originally by ``distutils`` (now deprecated) and `setuptools <https://setuptools.pypa.io/en/latest/userguide/declarative_config.html>`__, and can also be used to hold pytest configuration
if they have a ``[tool:pytest]`` section.

.. code-block:: ini
12 changes: 9 additions & 3 deletions scripts/prepare-release-pr.py
Original file line number Diff line number Diff line change
@@ -31,10 +31,16 @@ class InvalidFeatureRelease(Exception):
SLUG = "pytest-dev/pytest"

PR_BODY = """\
Created automatically from manual trigger.
Created by the [prepare release pr](https://github.com/pytest-dev/pytest/actions/workflows/prepare-release-pr.yml)
workflow.
Once all builds pass and it has been **approved** by one or more maintainers, the build
can be released by pushing a tag `{version}` to this repository.
Once all builds pass and it has been **approved** by one or more maintainers,
start the [deploy](https://github.com/pytest-dev/pytest/actions/workflows/deploy.yml) workflow, using these parameters:
* `Use workflow from`: `release-{version}`.
* `Release version`: `{version}`.
After the `deploy` workflow has been approved by a core maintainer, the package will be uploaded to PyPI automatically.
"""


54 changes: 40 additions & 14 deletions src/_pytest/assertion/rewrite.py
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@
import sys
import tokenize
import types
from collections import defaultdict
from pathlib import Path
from pathlib import PurePath
from typing import Callable
@@ -56,13 +57,20 @@
astNum = ast.Num


class Sentinel:
pass


assertstate_key = StashKey["AssertionState"]()

# pytest caches rewritten pycs in pycache dirs
PYTEST_TAG = f"{sys.implementation.cache_tag}-pytest-{version}"
PYC_EXT = ".py" + (__debug__ and "c" or "o")
PYC_TAIL = "." + PYTEST_TAG + PYC_EXT

# Special marker that denotes we have just left a scope definition
_SCOPE_END_MARKER = Sentinel()


class AssertionRewritingHook(importlib.abc.MetaPathFinder, importlib.abc.Loader):
"""PEP302/PEP451 import hook which rewrites asserts."""
@@ -645,6 +653,8 @@ class AssertionRewriter(ast.NodeVisitor):
.push_format_context() and .pop_format_context() which allows
to build another %-formatted string while already building one.
:scope: A tuple containing the current scope used for variables_overwrite.
:variables_overwrite: A dict filled with references to variables
that change value within an assert. This happens when a variable is
reassigned with the walrus operator
@@ -666,7 +676,10 @@ def __init__(
else:
self.enable_assertion_pass_hook = False
self.source = source
self.variables_overwrite: Dict[str, str] = {}
self.scope: tuple[ast.AST, ...] = ()
self.variables_overwrite: defaultdict[
tuple[ast.AST, ...], Dict[str, str]
] = defaultdict(dict)

def run(self, mod: ast.Module) -> None:
"""Find all assert statements in *mod* and rewrite them."""
@@ -732,9 +745,17 @@ def run(self, mod: ast.Module) -> None:
mod.body[pos:pos] = imports

# Collect asserts.
nodes: List[ast.AST] = [mod]
self.scope = (mod,)
nodes: List[Union[ast.AST, Sentinel]] = [mod]
while nodes:
node = nodes.pop()
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)):
self.scope = tuple((*self.scope, node))
nodes.append(_SCOPE_END_MARKER)
if node == _SCOPE_END_MARKER:
self.scope = self.scope[:-1]
continue
assert isinstance(node, ast.AST)
for name, field in ast.iter_fields(node):
if isinstance(field, list):
new: List[ast.AST] = []
@@ -1005,7 +1026,7 @@ def visit_BoolOp(self, boolop: ast.BoolOp) -> Tuple[ast.Name, str]:
]
):
pytest_temp = self.variable()
self.variables_overwrite[
self.variables_overwrite[self.scope][
v.left.target.id
] = v.left # type:ignore[assignment]
v.left.target.id = pytest_temp
@@ -1048,17 +1069,20 @@ def visit_Call(self, call: ast.Call) -> Tuple[ast.Name, str]:
new_args = []
new_kwargs = []
for arg in call.args:
if isinstance(arg, ast.Name) and arg.id in self.variables_overwrite:
arg = self.variables_overwrite[arg.id] # type:ignore[assignment]
if isinstance(arg, ast.Name) and arg.id in self.variables_overwrite.get(
self.scope, {}
):
arg = self.variables_overwrite[self.scope][
arg.id
] # type:ignore[assignment]
res, expl = self.visit(arg)
arg_expls.append(expl)
new_args.append(res)
for keyword in call.keywords:
if (
isinstance(keyword.value, ast.Name)
and keyword.value.id in self.variables_overwrite
):
keyword.value = self.variables_overwrite[
if isinstance(
keyword.value, ast.Name
) and keyword.value.id in self.variables_overwrite.get(self.scope, {}):
keyword.value = self.variables_overwrite[self.scope][
keyword.value.id
] # type:ignore[assignment]
res, expl = self.visit(keyword.value)
@@ -1094,12 +1118,14 @@ def visit_Attribute(self, attr: ast.Attribute) -> Tuple[ast.Name, str]:
def visit_Compare(self, comp: ast.Compare) -> Tuple[ast.expr, str]:
self.push_format_context()
# We first check if we have overwritten a variable in the previous assert
if isinstance(comp.left, ast.Name) and comp.left.id in self.variables_overwrite:
comp.left = self.variables_overwrite[
if isinstance(
comp.left, ast.Name
) and comp.left.id in self.variables_overwrite.get(self.scope, {}):
comp.left = self.variables_overwrite[self.scope][
comp.left.id
] # type:ignore[assignment]
if isinstance(comp.left, namedExpr):
self.variables_overwrite[
self.variables_overwrite[self.scope][
comp.left.target.id
] = comp.left # type:ignore[assignment]
left_res, left_expl = self.visit(comp.left)
@@ -1119,7 +1145,7 @@ def visit_Compare(self, comp: ast.Compare) -> Tuple[ast.expr, str]:
and next_operand.target.id == left_res.id
):
next_operand.target.id = self.variable()
self.variables_overwrite[
self.variables_overwrite[self.scope][
left_res.id
] = next_operand # type:ignore[assignment]
next_res, next_expl = self.visit(next_operand)
25 changes: 17 additions & 8 deletions src/_pytest/compat.py
Original file line number Diff line number Diff line change
@@ -380,15 +380,24 @@ def __get__(self, instance, owner=None):


def get_user_id() -> int | None:
"""Return the current user id, or None if we cannot get it reliably on the current platform."""
# win32 does not have a getuid() function.
# On Emscripten, getuid() is a stub that always returns 0.
if sys.platform in ("win32", "emscripten"):
"""Return the current process's real user id or None if it could not be
determined.
:return: The user id or None if it could not be determined.
"""
# mypy follows the version and platform checking expectation of PEP 484:
# https://mypy.readthedocs.io/en/stable/common_issues.html?highlight=platform#python-version-and-system-platform-checks
# Containment checks are too complex for mypy v1.5.0 and cause failure.
if sys.platform == "win32" or sys.platform == "emscripten":
# win32 does not have a getuid() function.
# Emscripten has a return 0 stub.
return None
# getuid shouldn't fail, but cpython defines such a case.
# Let's hope for the best.
uid = os.getuid()
return uid if uid != -1 else None
else:
# On other platforms, a return value of -1 is assumed to indicate that
# the current process's real user id could not be determined.
ERROR = -1
uid = os.getuid()
return uid if uid != ERROR else None


# Perform exhaustiveness checking.
3 changes: 1 addition & 2 deletions src/_pytest/faulthandler.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import io
import os
import sys
from typing import Generator
@@ -51,7 +50,7 @@ def get_stderr_fileno() -> int:
if fileno == -1:
raise AttributeError()
return fileno
except (AttributeError, io.UnsupportedOperation):
except (AttributeError, ValueError):
# pytest-xdist monkeypatches sys.stderr with an object that is not an actual file.
# https://docs.python.org/3/library/faulthandler.html#issue-with-file-descriptors
# This is potentially dangerous, but the best we can do.
4 changes: 3 additions & 1 deletion src/_pytest/mark/structures.py
Original file line number Diff line number Diff line change
@@ -373,7 +373,9 @@ def get_unpacked_marks(
if not consider_mro:
mark_lists = [obj.__dict__.get("pytestmark", [])]
else:
mark_lists = [x.__dict__.get("pytestmark", []) for x in obj.__mro__]
mark_lists = [
x.__dict__.get("pytestmark", []) for x in reversed(obj.__mro__)
]
mark_list = []
for item in mark_lists:
if isinstance(item, list):
5 changes: 3 additions & 2 deletions src/_pytest/pathlib.py
Original file line number Diff line number Diff line change
@@ -623,8 +623,9 @@ def module_name_from_path(path: Path, root: Path) -> str:
# Use the parts for the relative path to the root path.
path_parts = relative_path.parts

# Module name for packages do not contain the __init__ file.
if path_parts[-1] == "__init__":
# Module name for packages do not contain the __init__ file, unless
# the `__init__.py` file is at the root.
if len(path_parts) >= 2 and path_parts[-1] == "__init__":
path_parts = path_parts[:-1]

return ".".join(path_parts)
9 changes: 9 additions & 0 deletions testing/conftest.py
Original file line number Diff line number Diff line change
@@ -21,6 +21,15 @@ def restore_tracing():
sys.settrace(orig_trace)


@pytest.fixture(autouse=True)
def set_column_width(monkeypatch: pytest.MonkeyPatch) -> None:
"""
Force terminal width to 80: some tests check the formatting of --help, which is sensible
to terminal width.
"""
monkeypatch.setenv("COLUMNS", "80")


@pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_collection_modifyitems(items):
"""Prefer faster tests.
Loading