Drop Python 3.7

This commit is contained in:
Zac Hatfield-Dodds 2023-06-30 14:55:42 -07:00
parent 81cfb3fc87
commit f4e3b4ad98
43 changed files with 99 additions and 285 deletions

View File

@ -0,0 +1,2 @@
Drop support for Python 3.7, which `reached end-of-life on 2023-06-27
<https://devguide.python.org/versions/>`__.

View File

@ -15,12 +15,10 @@
# #
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
# The short X.Y version. # The short X.Y version.
import ast
import os import os
import shutil import shutil
import sys import sys
from textwrap import dedent from textwrap import dedent
from typing import List
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from _pytest import __version__ as version from _pytest import __version__ as version
@ -451,25 +449,6 @@ def setup(app: "sphinx.application.Sphinx") -> None:
configure_logging(app) configure_logging(app)
# Make Sphinx mark classes with "final" when decorated with @final.
# We need this because we import final from pytest._compat, not from
# typing (for Python < 3.8 compat), so Sphinx doesn't detect it.
# To keep things simple we accept any `@final` decorator.
# Ref: https://github.com/pytest-dev/pytest/pull/7780
import sphinx.pycode.ast
import sphinx.pycode.parser
original_is_final = sphinx.pycode.parser.VariableCommentPicker.is_final
def patched_is_final(self, decorators: List[ast.expr]) -> bool:
if original_is_final(self, decorators):
return True
return any(
sphinx.pycode.ast.unparse(decorator) == "final" for decorator in decorators
)
sphinx.pycode.parser.VariableCommentPicker.is_final = patched_is_final
# legacypath.py monkey-patches pytest.Testdir in. Import the file so # legacypath.py monkey-patches pytest.Testdir in. Import the file so
# that autodoc can discover references to it. # that autodoc can discover references to it.
import _pytest.legacypath # noqa: F401 import _pytest.legacypath # noqa: F401

View File

@ -17,7 +17,6 @@ classifiers =
Operating System :: POSIX Operating System :: POSIX
Programming Language :: Python :: 3 Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.10
@ -50,9 +49,8 @@ install_requires =
pluggy>=0.12,<2.0 pluggy>=0.12,<2.0
colorama;sys_platform=="win32" colorama;sys_platform=="win32"
exceptiongroup>=1.0.0rc8;python_version<"3.11" exceptiongroup>=1.0.0rc8;python_version<"3.11"
importlib-metadata>=0.12;python_version<"3.8"
tomli>=1.0.0;python_version<"3.11" tomli>=1.0.0;python_version<"3.11"
python_requires = >=3.7 python_requires = >=3.8
package_dir = package_dir =
=src =src
setup_requires = setup_requires =

View File

@ -17,6 +17,7 @@ from typing import Any
from typing import Callable from typing import Callable
from typing import ClassVar from typing import ClassVar
from typing import Dict from typing import Dict
from typing import final
from typing import Generic from typing import Generic
from typing import Iterable from typing import Iterable
from typing import List from typing import List
@ -42,7 +43,6 @@ from _pytest._code.source import Source
from _pytest._io import TerminalWriter from _pytest._io import TerminalWriter
from _pytest._io.saferepr import safeformat from _pytest._io.saferepr import safeformat
from _pytest._io.saferepr import saferepr from _pytest._io.saferepr import saferepr
from _pytest.compat import final
from _pytest.compat import get_real_func from _pytest.compat import get_real_func
from _pytest.deprecated import check_ispytest from _pytest.deprecated import check_ispytest
from _pytest.pathlib import absolutepath from _pytest.pathlib import absolutepath

View File

@ -149,8 +149,7 @@ def get_statement_startend2(lineno: int, node: ast.AST) -> Tuple[int, Optional[i
values: List[int] = [] values: List[int] = []
for x in ast.walk(node): for x in ast.walk(node):
if isinstance(x, (ast.stmt, ast.ExceptHandler)): if isinstance(x, (ast.stmt, ast.ExceptHandler)):
# Before Python 3.8, the lineno of a decorated class or function pointed at the decorator. # The lineno points to the class/def, so need to include the decorators.
# Since Python 3.8, the lineno points to the class/def, so need to include the decorators.
if isinstance(x, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)): if isinstance(x, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)):
for d in x.decorator_list: for d in x.decorator_list:
values.append(d.lineno - 1) values.append(d.lineno - 1)

View File

@ -2,12 +2,12 @@
import os import os
import shutil import shutil
import sys import sys
from typing import final
from typing import Optional from typing import Optional
from typing import Sequence from typing import Sequence
from typing import TextIO from typing import TextIO
from .wcwidth import wcswidth from .wcwidth import wcswidth
from _pytest.compat import final
# This code was initially copied from py 1.8.1, file _io/terminalwriter.py. # This code was initially copied from py 1.8.1, file _io/terminalwriter.py.

View File

@ -44,17 +44,6 @@ from _pytest.stash import StashKey
if TYPE_CHECKING: if TYPE_CHECKING:
from _pytest.assertion import AssertionState from _pytest.assertion import AssertionState
if sys.version_info >= (3, 8):
namedExpr = ast.NamedExpr
astNameConstant = ast.Constant
astStr = ast.Constant
astNum = ast.Constant
else:
namedExpr = ast.Expr
astNameConstant = ast.NameConstant
astStr = ast.Str
astNum = ast.Num
assertstate_key = StashKey["AssertionState"]() assertstate_key = StashKey["AssertionState"]()
@ -686,12 +675,9 @@ class AssertionRewriter(ast.NodeVisitor):
if ( if (
expect_docstring expect_docstring
and isinstance(item, ast.Expr) and isinstance(item, ast.Expr)
and isinstance(item.value, astStr) and isinstance(item.value, ast.Constant)
): ):
if sys.version_info >= (3, 8): doc = item.value.value
doc = item.value.value
else:
doc = item.value.s
if self.is_rewrite_disabled(doc): if self.is_rewrite_disabled(doc):
return return
expect_docstring = False expect_docstring = False
@ -823,7 +809,7 @@ class AssertionRewriter(ast.NodeVisitor):
current = self.stack.pop() current = self.stack.pop()
if self.stack: if self.stack:
self.explanation_specifiers = self.stack[-1] self.explanation_specifiers = self.stack[-1]
keys = [astStr(key) for key in current.keys()] keys = [ast.Constant(key) for key in current.keys()]
format_dict = ast.Dict(keys, list(current.values())) format_dict = ast.Dict(keys, list(current.values()))
form = ast.BinOp(expl_expr, ast.Mod(), format_dict) form = ast.BinOp(expl_expr, ast.Mod(), format_dict)
name = "@py_format" + str(next(self.variable_counter)) name = "@py_format" + str(next(self.variable_counter))
@ -877,16 +863,16 @@ class AssertionRewriter(ast.NodeVisitor):
negation = ast.UnaryOp(ast.Not(), top_condition) negation = ast.UnaryOp(ast.Not(), top_condition)
if self.enable_assertion_pass_hook: # Experimental pytest_assertion_pass hook if self.enable_assertion_pass_hook: # Experimental pytest_assertion_pass hook
msg = self.pop_format_context(astStr(explanation)) msg = self.pop_format_context(ast.Constant(explanation))
# Failed # Failed
if assert_.msg: if assert_.msg:
assertmsg = self.helper("_format_assertmsg", assert_.msg) assertmsg = self.helper("_format_assertmsg", assert_.msg)
gluestr = "\n>assert " gluestr = "\n>assert "
else: else:
assertmsg = astStr("") assertmsg = ast.Constant("")
gluestr = "assert " gluestr = "assert "
err_explanation = ast.BinOp(astStr(gluestr), ast.Add(), msg) err_explanation = ast.BinOp(ast.Constant(gluestr), ast.Add(), msg)
err_msg = ast.BinOp(assertmsg, ast.Add(), err_explanation) err_msg = ast.BinOp(assertmsg, ast.Add(), err_explanation)
err_name = ast.Name("AssertionError", ast.Load()) err_name = ast.Name("AssertionError", ast.Load())
fmt = self.helper("_format_explanation", err_msg) fmt = self.helper("_format_explanation", err_msg)
@ -902,8 +888,8 @@ class AssertionRewriter(ast.NodeVisitor):
hook_call_pass = ast.Expr( hook_call_pass = ast.Expr(
self.helper( self.helper(
"_call_assertion_pass", "_call_assertion_pass",
astNum(assert_.lineno), ast.Constant(assert_.lineno),
astStr(orig), ast.Constant(orig),
fmt_pass, fmt_pass,
) )
) )
@ -922,7 +908,7 @@ class AssertionRewriter(ast.NodeVisitor):
variables = [ variables = [
ast.Name(name, ast.Store()) for name in self.format_variables ast.Name(name, ast.Store()) for name in self.format_variables
] ]
clear_format = ast.Assign(variables, astNameConstant(None)) clear_format = ast.Assign(variables, ast.Constant(None))
self.statements.append(clear_format) self.statements.append(clear_format)
else: # Original assertion rewriting else: # Original assertion rewriting
@ -933,9 +919,9 @@ class AssertionRewriter(ast.NodeVisitor):
assertmsg = self.helper("_format_assertmsg", assert_.msg) assertmsg = self.helper("_format_assertmsg", assert_.msg)
explanation = "\n>assert " + explanation explanation = "\n>assert " + explanation
else: else:
assertmsg = astStr("") assertmsg = ast.Constant("")
explanation = "assert " + explanation explanation = "assert " + explanation
template = ast.BinOp(assertmsg, ast.Add(), astStr(explanation)) template = ast.BinOp(assertmsg, ast.Add(), ast.Constant(explanation))
msg = self.pop_format_context(template) msg = self.pop_format_context(template)
fmt = self.helper("_format_explanation", msg) fmt = self.helper("_format_explanation", msg)
err_name = ast.Name("AssertionError", ast.Load()) err_name = ast.Name("AssertionError", ast.Load())
@ -947,7 +933,7 @@ class AssertionRewriter(ast.NodeVisitor):
# Clear temporary variables by setting them to None. # Clear temporary variables by setting them to None.
if self.variables: if self.variables:
variables = [ast.Name(name, ast.Store()) for name in self.variables] variables = [ast.Name(name, ast.Store()) for name in self.variables]
clear = ast.Assign(variables, astNameConstant(None)) clear = ast.Assign(variables, ast.Constant(None))
self.statements.append(clear) self.statements.append(clear)
# Fix locations (line numbers/column offsets). # Fix locations (line numbers/column offsets).
for stmt in self.statements: for stmt in self.statements:
@ -955,26 +941,26 @@ class AssertionRewriter(ast.NodeVisitor):
ast.copy_location(node, assert_) ast.copy_location(node, assert_)
return self.statements return self.statements
def visit_NamedExpr(self, name: namedExpr) -> Tuple[namedExpr, str]: def visit_NamedExpr(self, name: ast.NamedExpr) -> Tuple[ast.NamedExpr, str]:
# This method handles the 'walrus operator' repr of the target # This method handles the 'walrus operator' repr of the target
# name if it's a local variable or _should_repr_global_name() # name if it's a local variable or _should_repr_global_name()
# thinks it's acceptable. # thinks it's acceptable.
locs = ast.Call(self.builtin("locals"), [], []) locs = ast.Call(self.builtin("locals"), [], [])
target_id = name.target.id # type: ignore[attr-defined] target_id = name.target.id # type: ignore[attr-defined]
inlocs = ast.Compare(astStr(target_id), [ast.In()], [locs]) inlocs = ast.Compare(ast.Constant(target_id), [ast.In()], [locs])
dorepr = self.helper("_should_repr_global_name", name) dorepr = self.helper("_should_repr_global_name", name)
test = ast.BoolOp(ast.Or(), [inlocs, dorepr]) test = ast.BoolOp(ast.Or(), [inlocs, dorepr])
expr = ast.IfExp(test, self.display(name), astStr(target_id)) expr = ast.IfExp(test, self.display(name), ast.Constant(target_id))
return name, self.explanation_param(expr) return name, self.explanation_param(expr)
def visit_Name(self, name: ast.Name) -> Tuple[ast.Name, str]: def visit_Name(self, name: ast.Name) -> Tuple[ast.Name, str]:
# Display the repr of the name if it's a local variable or # Display the repr of the name if it's a local variable or
# _should_repr_global_name() thinks it's acceptable. # _should_repr_global_name() thinks it's acceptable.
locs = ast.Call(self.builtin("locals"), [], []) locs = ast.Call(self.builtin("locals"), [], [])
inlocs = ast.Compare(astStr(name.id), [ast.In()], [locs]) inlocs = ast.Compare(ast.Constant(name.id), [ast.In()], [locs])
dorepr = self.helper("_should_repr_global_name", name) dorepr = self.helper("_should_repr_global_name", name)
test = ast.BoolOp(ast.Or(), [inlocs, dorepr]) test = ast.BoolOp(ast.Or(), [inlocs, dorepr])
expr = ast.IfExp(test, self.display(name), astStr(name.id)) expr = ast.IfExp(test, self.display(name), ast.Constant(name.id))
return name, self.explanation_param(expr) return name, self.explanation_param(expr)
def visit_BoolOp(self, boolop: ast.BoolOp) -> Tuple[ast.Name, str]: def visit_BoolOp(self, boolop: ast.BoolOp) -> Tuple[ast.Name, str]:
@ -993,10 +979,10 @@ class AssertionRewriter(ast.NodeVisitor):
# cond is set in a prior loop iteration below # cond is set in a prior loop iteration below
self.expl_stmts.append(ast.If(cond, fail_inner, [])) # noqa self.expl_stmts.append(ast.If(cond, fail_inner, [])) # noqa
self.expl_stmts = fail_inner self.expl_stmts = fail_inner
# Check if the left operand is a namedExpr and the value has already been visited # Check if the left operand is a ast.NamedExpr and the value has already been visited
if ( if (
isinstance(v, ast.Compare) isinstance(v, ast.Compare)
and isinstance(v.left, namedExpr) and isinstance(v.left, ast.NamedExpr)
and v.left.target.id and v.left.target.id
in [ in [
ast_expr.id ast_expr.id
@ -1012,7 +998,7 @@ class AssertionRewriter(ast.NodeVisitor):
self.push_format_context() self.push_format_context()
res, expl = self.visit(v) res, expl = self.visit(v)
body.append(ast.Assign([ast.Name(res_var, ast.Store())], res)) body.append(ast.Assign([ast.Name(res_var, ast.Store())], res))
expl_format = self.pop_format_context(astStr(expl)) expl_format = self.pop_format_context(ast.Constant(expl))
call = ast.Call(app, [expl_format], []) call = ast.Call(app, [expl_format], [])
self.expl_stmts.append(ast.Expr(call)) self.expl_stmts.append(ast.Expr(call))
if i < levels: if i < levels:
@ -1024,7 +1010,7 @@ class AssertionRewriter(ast.NodeVisitor):
self.statements = body = inner self.statements = body = inner
self.statements = save self.statements = save
self.expl_stmts = fail_save self.expl_stmts = fail_save
expl_template = self.helper("_format_boolop", expl_list, astNum(is_or)) expl_template = self.helper("_format_boolop", expl_list, ast.Constant(is_or))
expl = self.pop_format_context(expl_template) expl = self.pop_format_context(expl_template)
return ast.Name(res_var, ast.Load()), self.explanation_param(expl) return ast.Name(res_var, ast.Load()), self.explanation_param(expl)
@ -1098,7 +1084,7 @@ class AssertionRewriter(ast.NodeVisitor):
comp.left = self.variables_overwrite[ comp.left = self.variables_overwrite[
comp.left.id comp.left.id
] # type:ignore[assignment] ] # type:ignore[assignment]
if isinstance(comp.left, namedExpr): if isinstance(comp.left, ast.NamedExpr):
self.variables_overwrite[ self.variables_overwrite[
comp.left.target.id comp.left.target.id
] = comp.left # type:ignore[assignment] ] = comp.left # type:ignore[assignment]
@ -1114,7 +1100,7 @@ class AssertionRewriter(ast.NodeVisitor):
results = [left_res] results = [left_res]
for i, op, next_operand in it: for i, op, next_operand in it:
if ( if (
isinstance(next_operand, namedExpr) isinstance(next_operand, ast.NamedExpr)
and isinstance(left_res, ast.Name) and isinstance(left_res, ast.Name)
and next_operand.target.id == left_res.id and next_operand.target.id == left_res.id
): ):
@ -1127,9 +1113,9 @@ class AssertionRewriter(ast.NodeVisitor):
next_expl = f"({next_expl})" next_expl = f"({next_expl})"
results.append(next_res) results.append(next_res)
sym = BINOP_MAP[op.__class__] sym = BINOP_MAP[op.__class__]
syms.append(astStr(sym)) syms.append(ast.Constant(sym))
expl = f"{left_expl} {sym} {next_expl}" expl = f"{left_expl} {sym} {next_expl}"
expls.append(astStr(expl)) expls.append(ast.Constant(expl))
res_expr = ast.Compare(left_res, [op], [next_res]) res_expr = ast.Compare(left_res, [op], [next_res])
self.statements.append(ast.Assign([store_names[i]], res_expr)) self.statements.append(ast.Assign([store_names[i]], res_expr))
left_res, left_expl = next_res, next_expl left_res, left_expl = next_res, next_expl
@ -1173,7 +1159,7 @@ def try_makedirs(cache_dir: Path) -> bool:
def get_cache_dir(file_path: Path) -> Path: def get_cache_dir(file_path: Path) -> Path:
"""Return the cache directory to write .pyc files for the given .py file path.""" """Return the cache directory to write .pyc files for the given .py file path."""
if sys.version_info >= (3, 8) and sys.pycache_prefix: if sys.pycache_prefix:
# given: # given:
# prefix = '/tmp/pycs' # prefix = '/tmp/pycs'
# path = '/home/user/proj/test_app.py' # path = '/home/user/proj/test_app.py'

View File

@ -6,6 +6,7 @@ import json
import os import os
from pathlib import Path from pathlib import Path
from typing import Dict from typing import Dict
from typing import final
from typing import Generator from typing import Generator
from typing import Iterable from typing import Iterable
from typing import List from typing import List
@ -18,7 +19,6 @@ from .pathlib import rm_rf
from .reports import CollectReport from .reports import CollectReport
from _pytest import nodes from _pytest import nodes
from _pytest._io import TerminalWriter from _pytest._io import TerminalWriter
from _pytest.compat import final
from _pytest.config import Config from _pytest.config import Config
from _pytest.config import ExitCode from _pytest.config import ExitCode
from _pytest.config import hookimpl from _pytest.config import hookimpl

View File

@ -11,6 +11,7 @@ from types import TracebackType
from typing import Any from typing import Any
from typing import AnyStr from typing import AnyStr
from typing import BinaryIO from typing import BinaryIO
from typing import final
from typing import Generator from typing import Generator
from typing import Generic from typing import Generic
from typing import Iterable from typing import Iterable
@ -24,7 +25,6 @@ from typing import Type
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing import Union from typing import Union
from _pytest.compat import final
from _pytest.config import Config from _pytest.config import Config
from _pytest.config import hookimpl from _pytest.config import hookimpl
from _pytest.config.argparsing import Parser from _pytest.config.argparsing import Parser

View File

@ -12,23 +12,12 @@ from inspect import signature
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
from typing import Callable from typing import Callable
from typing import Generic
from typing import NoReturn from typing import NoReturn
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from typing import TypeVar from typing import TypeVar
import py import py
# fmt: off
# Workaround for https://github.com/sphinx-doc/sphinx/issues/10351.
# If `overload` is imported from `compat` instead of from `typing`,
# Sphinx doesn't recognize it as `overload` and the API docs for
# overloaded functions look good again. But type checkers handle
# it fine.
# fmt: on
if True:
from typing import overload as overload
if TYPE_CHECKING: if TYPE_CHECKING:
from typing_extensions import Final from typing_extensions import Final
@ -58,17 +47,6 @@ class NotSetType(enum.Enum):
NOTSET: Final = NotSetType.token # noqa: E305 NOTSET: Final = NotSetType.token # noqa: E305
# fmt: on # fmt: on
if sys.version_info >= (3, 8):
import importlib.metadata
importlib_metadata = importlib.metadata
else:
import importlib_metadata as importlib_metadata # noqa: F401
def _format_args(func: Callable[..., Any]) -> str:
return str(signature(func))
def is_generator(func: object) -> bool: def is_generator(func: object) -> bool:
genfunc = inspect.isgeneratorfunction(func) genfunc = inspect.isgeneratorfunction(func)
@ -338,47 +316,6 @@ def safe_isclass(obj: object) -> bool:
return False return False
if TYPE_CHECKING:
if sys.version_info >= (3, 8):
from typing import final as final
else:
from typing_extensions import final as final
elif sys.version_info >= (3, 8):
from typing import final as final
else:
def final(f):
return f
if sys.version_info >= (3, 8):
from functools import cached_property as cached_property
else:
class cached_property(Generic[_S, _T]):
__slots__ = ("func", "__doc__")
def __init__(self, func: Callable[[_S], _T]) -> None:
self.func = func
self.__doc__ = func.__doc__
@overload
def __get__(
self, instance: None, owner: type[_S] | None = ...
) -> cached_property[_S, _T]:
...
@overload
def __get__(self, instance: _S, owner: type[_S] | None = ...) -> _T:
...
def __get__(self, instance, owner=None):
if instance is None:
return self
value = instance.__dict__[self.func.__name__] = self.func(instance)
return value
def get_user_id() -> int | None: def get_user_id() -> int | None:
"""Return the current user id, or None if we cannot get it reliably on the current platform.""" """Return the current user id, or None if we cannot get it reliably on the current platform."""
# win32 does not have a getuid() function. # win32 does not have a getuid() function.

View File

@ -5,6 +5,7 @@ import copy
import dataclasses import dataclasses
import enum import enum
import glob import glob
import importlib.metadata
import inspect import inspect
import os import os
import re import re
@ -21,6 +22,7 @@ from typing import Any
from typing import Callable from typing import Callable
from typing import cast from typing import cast
from typing import Dict from typing import Dict
from typing import final
from typing import Generator from typing import Generator
from typing import IO from typing import IO
from typing import Iterable from typing import Iterable
@ -48,8 +50,6 @@ from .findpaths import determine_setup
from _pytest._code import ExceptionInfo from _pytest._code import ExceptionInfo
from _pytest._code import filter_traceback from _pytest._code import filter_traceback
from _pytest._io import TerminalWriter from _pytest._io import TerminalWriter
from _pytest.compat import final
from _pytest.compat import importlib_metadata # type: ignore[attr-defined]
from _pytest.outcomes import fail from _pytest.outcomes import fail
from _pytest.outcomes import Skipped from _pytest.outcomes import Skipped
from _pytest.pathlib import absolutepath from _pytest.pathlib import absolutepath
@ -257,7 +257,8 @@ default_plugins = essential_plugins + (
"logging", "logging",
"reports", "reports",
"python_path", "python_path",
*(["unraisableexception", "threadexception"] if sys.version_info >= (3, 8) else []), "unraisableexception",
"threadexception",
"faulthandler", "faulthandler",
) )
@ -1216,7 +1217,7 @@ class Config:
package_files = ( package_files = (
str(file) str(file)
for dist in importlib_metadata.distributions() for dist in importlib.metadata.distributions()
if any(ep.group == "pytest11" for ep in dist.entry_points) if any(ep.group == "pytest11" for ep in dist.entry_points)
for file in dist.files or [] for file in dist.files or []
) )

View File

@ -7,6 +7,7 @@ from typing import Any
from typing import Callable from typing import Callable
from typing import cast from typing import cast
from typing import Dict from typing import Dict
from typing import final
from typing import List from typing import List
from typing import Mapping from typing import Mapping
from typing import NoReturn from typing import NoReturn
@ -17,7 +18,6 @@ from typing import TYPE_CHECKING
from typing import Union from typing import Union
import _pytest._io import _pytest._io
from _pytest.compat import final
from _pytest.config.exceptions import UsageError from _pytest.config.exceptions import UsageError
from _pytest.deprecated import ARGUMENT_PERCENT_DEFAULT from _pytest.deprecated import ARGUMENT_PERCENT_DEFAULT
from _pytest.deprecated import ARGUMENT_TYPE_STR from _pytest.deprecated import ARGUMENT_TYPE_STR

View File

@ -1,4 +1,4 @@
from _pytest.compat import final from typing import final
@final @final

View File

@ -13,6 +13,7 @@ from typing import Any
from typing import Callable from typing import Callable
from typing import cast from typing import cast
from typing import Dict from typing import Dict
from typing import final
from typing import Generator from typing import Generator
from typing import Generic from typing import Generic
from typing import Iterable from typing import Iterable
@ -21,6 +22,7 @@ from typing import List
from typing import MutableMapping from typing import MutableMapping
from typing import NoReturn from typing import NoReturn
from typing import Optional from typing import Optional
from typing import overload
from typing import Sequence from typing import Sequence
from typing import Set from typing import Set
from typing import Tuple from typing import Tuple
@ -35,10 +37,8 @@ from _pytest._code import getfslineno
from _pytest._code.code import FormattedExcinfo from _pytest._code.code import FormattedExcinfo
from _pytest._code.code import TerminalRepr from _pytest._code.code import TerminalRepr
from _pytest._io import TerminalWriter from _pytest._io import TerminalWriter
from _pytest.compat import _format_args
from _pytest.compat import _PytestWrapper from _pytest.compat import _PytestWrapper
from _pytest.compat import assert_never from _pytest.compat import assert_never
from _pytest.compat import final
from _pytest.compat import get_real_func from _pytest.compat import get_real_func
from _pytest.compat import get_real_method from _pytest.compat import get_real_method
from _pytest.compat import getfuncargnames from _pytest.compat import getfuncargnames
@ -47,7 +47,6 @@ from _pytest.compat import getlocation
from _pytest.compat import is_generator from _pytest.compat import is_generator
from _pytest.compat import NOTSET from _pytest.compat import NOTSET
from _pytest.compat import NotSetType from _pytest.compat import NotSetType
from _pytest.compat import overload
from _pytest.compat import safe_getattr from _pytest.compat import safe_getattr
from _pytest.config import _PluggyPlugin from _pytest.config import _PluggyPlugin
from _pytest.config import Config from _pytest.config import Config
@ -729,8 +728,10 @@ class FixtureRequest:
p = bestrelpath(session.path, fs) p = bestrelpath(session.path, fs)
else: else:
p = fs p = fs
args = _format_args(factory) lines.append(
lines.append("%s:%d: def %s%s" % (p, lineno + 1, factory.__name__, args)) "%s:%d: def %s%s"
% (p, lineno + 1, factory.__name__, inspect.signature(factory))
)
return lines return lines
def __repr__(self) -> str: def __repr__(self) -> str:

View File

@ -3,6 +3,7 @@ import dataclasses
import shlex import shlex
import subprocess import subprocess
from pathlib import Path from pathlib import Path
from typing import final
from typing import List from typing import List
from typing import Optional from typing import Optional
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
@ -11,7 +12,6 @@ from typing import Union
from iniconfig import SectionWrapper from iniconfig import SectionWrapper
from _pytest.cacheprovider import Cache from _pytest.cacheprovider import Cache
from _pytest.compat import final
from _pytest.compat import LEGACY_PATH from _pytest.compat import LEGACY_PATH
from _pytest.compat import legacy_path from _pytest.compat import legacy_path
from _pytest.config import Config from _pytest.config import Config

View File

@ -13,6 +13,7 @@ from logging import LogRecord
from pathlib import Path from pathlib import Path
from typing import AbstractSet from typing import AbstractSet
from typing import Dict from typing import Dict
from typing import final
from typing import Generator from typing import Generator
from typing import List from typing import List
from typing import Mapping from typing import Mapping
@ -25,7 +26,6 @@ from typing import Union
from _pytest import nodes from _pytest import nodes
from _pytest._io import TerminalWriter from _pytest._io import TerminalWriter
from _pytest.capture import CaptureManager from _pytest.capture import CaptureManager
from _pytest.compat import final
from _pytest.config import _strtobool from _pytest.config import _strtobool
from _pytest.config import Config from _pytest.config import Config
from _pytest.config import create_terminal_writer from _pytest.config import create_terminal_writer

View File

@ -9,10 +9,12 @@ import sys
from pathlib import Path from pathlib import Path
from typing import Callable from typing import Callable
from typing import Dict from typing import Dict
from typing import final
from typing import FrozenSet from typing import FrozenSet
from typing import Iterator from typing import Iterator
from typing import List from typing import List
from typing import Optional from typing import Optional
from typing import overload
from typing import Sequence from typing import Sequence
from typing import Set from typing import Set
from typing import Tuple from typing import Tuple
@ -22,8 +24,6 @@ from typing import Union
import _pytest._code import _pytest._code
from _pytest import nodes from _pytest import nodes
from _pytest.compat import final
from _pytest.compat import overload
from _pytest.config import Config from _pytest.config import Config
from _pytest.config import directory_arg from _pytest.config import directory_arg
from _pytest.config import ExitCode from _pytest.config import ExitCode

View File

@ -18,7 +18,6 @@ import ast
import dataclasses import dataclasses
import enum import enum
import re import re
import sys
import types import types
from typing import Callable from typing import Callable
from typing import Iterator from typing import Iterator
@ -27,12 +26,6 @@ from typing import NoReturn
from typing import Optional from typing import Optional
from typing import Sequence from typing import Sequence
if sys.version_info >= (3, 8):
astNameConstant = ast.Constant
else:
astNameConstant = ast.NameConstant
__all__ = [ __all__ = [
"Expression", "Expression",
"ParseError", "ParseError",
@ -138,7 +131,7 @@ IDENT_PREFIX = "$"
def expression(s: Scanner) -> ast.Expression: def expression(s: Scanner) -> ast.Expression:
if s.accept(TokenType.EOF): if s.accept(TokenType.EOF):
ret: ast.expr = astNameConstant(False) ret: ast.expr = ast.Constant(False)
else: else:
ret = expr(s) ret = expr(s)
s.accept(TokenType.EOF, reject=True) s.accept(TokenType.EOF, reject=True)

View File

@ -5,6 +5,7 @@ import warnings
from typing import Any from typing import Any
from typing import Callable from typing import Callable
from typing import Collection from typing import Collection
from typing import final
from typing import Iterable from typing import Iterable
from typing import Iterator from typing import Iterator
from typing import List from typing import List
@ -23,7 +24,6 @@ from typing import Union
from .._code import getfslineno from .._code import getfslineno
from ..compat import ascii_escaped from ..compat import ascii_escaped
from ..compat import final
from ..compat import NOTSET from ..compat import NOTSET
from ..compat import NotSetType from ..compat import NotSetType
from _pytest.config import Config from _pytest.config import Config

View File

@ -5,6 +5,7 @@ import sys
import warnings import warnings
from contextlib import contextmanager from contextlib import contextmanager
from typing import Any from typing import Any
from typing import final
from typing import Generator from typing import Generator
from typing import List from typing import List
from typing import Mapping from typing import Mapping
@ -15,7 +16,6 @@ from typing import Tuple
from typing import TypeVar from typing import TypeVar
from typing import Union from typing import Union
from _pytest.compat import final
from _pytest.fixtures import fixture from _pytest.fixtures import fixture
from _pytest.warning_types import PytestWarning from _pytest.warning_types import PytestWarning

View File

@ -1,5 +1,6 @@
import os import os
import warnings import warnings
from functools import cached_property
from inspect import signature from inspect import signature
from pathlib import Path from pathlib import Path
from typing import Any from typing import Any
@ -23,7 +24,6 @@ from _pytest._code import getfslineno
from _pytest._code.code import ExceptionInfo from _pytest._code.code import ExceptionInfo
from _pytest._code.code import TerminalRepr from _pytest._code.code import TerminalRepr
from _pytest._code.code import Traceback from _pytest._code.code import Traceback
from _pytest.compat import cached_property
from _pytest.compat import LEGACY_PATH from _pytest.compat import LEGACY_PATH
from _pytest.config import Config from _pytest.config import Config
from _pytest.config import ConftestImportFailure from _pytest.config import ConftestImportFailure

View File

@ -7,23 +7,12 @@ from typing import Callable
from typing import cast from typing import cast
from typing import NoReturn from typing import NoReturn
from typing import Optional from typing import Optional
from typing import Protocol
from typing import Type from typing import Type
from typing import TypeVar from typing import TypeVar
from _pytest.deprecated import KEYWORD_MSG_ARG from _pytest.deprecated import KEYWORD_MSG_ARG
TYPE_CHECKING = False # Avoid circular import through compat.
if TYPE_CHECKING:
from typing_extensions import Protocol
else:
# typing.Protocol is only available starting from Python 3.8. It is also
# available from typing_extensions, but we don't want a runtime dependency
# on that. So use a dummy runtime implementation.
from typing import Generic
Protocol = Generic
class OutcomeException(BaseException): class OutcomeException(BaseException):
"""OutcomeException and its subclass instances indicate and contain info """OutcomeException and its subclass instances indicate and contain info

View File

@ -20,6 +20,7 @@ from pathlib import Path
from typing import Any from typing import Any
from typing import Callable from typing import Callable
from typing import Dict from typing import Dict
from typing import final
from typing import Generator from typing import Generator
from typing import IO from typing import IO
from typing import Iterable from typing import Iterable
@ -40,7 +41,6 @@ from iniconfig import SectionWrapper
from _pytest import timing from _pytest import timing
from _pytest._code import Source from _pytest._code import Source
from _pytest.capture import _get_multicapture from _pytest.capture import _get_multicapture
from _pytest.compat import final
from _pytest.compat import NOTSET from _pytest.compat import NOTSET
from _pytest.compat import NotSetType from _pytest.compat import NotSetType
from _pytest.config import _PluggyPlugin from _pytest.config import _PluggyPlugin

View File

@ -15,6 +15,7 @@ from pathlib import Path
from typing import Any from typing import Any
from typing import Callable from typing import Callable
from typing import Dict from typing import Dict
from typing import final
from typing import Generator from typing import Generator
from typing import Iterable from typing import Iterable
from typing import Iterator from typing import Iterator
@ -40,7 +41,6 @@ from _pytest._io import TerminalWriter
from _pytest._io.saferepr import saferepr from _pytest._io.saferepr import saferepr
from _pytest.compat import ascii_escaped from _pytest.compat import ascii_escaped
from _pytest.compat import assert_never from _pytest.compat import assert_never
from _pytest.compat import final
from _pytest.compat import get_default_arg_names from _pytest.compat import get_default_arg_names
from _pytest.compat import get_real_func from _pytest.compat import get_real_func
from _pytest.compat import getimfunc from _pytest.compat import getimfunc

View File

@ -9,9 +9,11 @@ from typing import Any
from typing import Callable from typing import Callable
from typing import cast from typing import cast
from typing import ContextManager from typing import ContextManager
from typing import final
from typing import List from typing import List
from typing import Mapping from typing import Mapping
from typing import Optional from typing import Optional
from typing import overload
from typing import Pattern from typing import Pattern
from typing import Sequence from typing import Sequence
from typing import Tuple from typing import Tuple
@ -20,17 +22,14 @@ from typing import TYPE_CHECKING
from typing import TypeVar from typing import TypeVar
from typing import Union from typing import Union
import _pytest._code
from _pytest.compat import STRING_TYPES
from _pytest.outcomes import fail
if TYPE_CHECKING: if TYPE_CHECKING:
from numpy import ndarray from numpy import ndarray
import _pytest._code
from _pytest.compat import final
from _pytest.compat import STRING_TYPES
from _pytest.compat import overload
from _pytest.outcomes import fail
def _non_numeric_type_error(value, at: Optional[str]) -> TypeError: def _non_numeric_type_error(value, at: Optional[str]) -> TypeError:
at_str = f" at {at}" if at else "" at_str = f" at {at}" if at else ""
return TypeError( return TypeError(

View File

@ -5,18 +5,18 @@ from pprint import pformat
from types import TracebackType from types import TracebackType
from typing import Any from typing import Any
from typing import Callable from typing import Callable
from typing import final
from typing import Generator from typing import Generator
from typing import Iterator from typing import Iterator
from typing import List from typing import List
from typing import Optional from typing import Optional
from typing import overload
from typing import Pattern from typing import Pattern
from typing import Tuple from typing import Tuple
from typing import Type from typing import Type
from typing import TypeVar from typing import TypeVar
from typing import Union from typing import Union
from _pytest.compat import final
from _pytest.compat import overload
from _pytest.deprecated import check_ispytest from _pytest.deprecated import check_ispytest
from _pytest.deprecated import WARNS_NONE_ARG from _pytest.deprecated import WARNS_NONE_ARG
from _pytest.fixtures import fixture from _pytest.fixtures import fixture

View File

@ -5,6 +5,7 @@ from pprint import pprint
from typing import Any from typing import Any
from typing import cast from typing import cast
from typing import Dict from typing import Dict
from typing import final
from typing import Iterable from typing import Iterable
from typing import Iterator from typing import Iterator
from typing import List from typing import List
@ -29,7 +30,6 @@ from _pytest._code.code import ReprLocals
from _pytest._code.code import ReprTraceback from _pytest._code.code import ReprTraceback
from _pytest._code.code import TerminalRepr from _pytest._code.code import TerminalRepr
from _pytest._io import TerminalWriter from _pytest._io import TerminalWriter
from _pytest.compat import final
from _pytest.config import Config from _pytest.config import Config
from _pytest.nodes import Collector from _pytest.nodes import Collector
from _pytest.nodes import Item from _pytest.nodes import Item

View File

@ -6,6 +6,7 @@ import sys
from typing import Callable from typing import Callable
from typing import cast from typing import cast
from typing import Dict from typing import Dict
from typing import final
from typing import Generic from typing import Generic
from typing import List from typing import List
from typing import Optional from typing import Optional
@ -23,7 +24,6 @@ from _pytest import timing
from _pytest._code.code import ExceptionChainRepr from _pytest._code.code import ExceptionChainRepr
from _pytest._code.code import ExceptionInfo from _pytest._code.code import ExceptionInfo
from _pytest._code.code import TerminalRepr from _pytest._code.code import TerminalRepr
from _pytest.compat import final
from _pytest.config.argparsing import Parser from _pytest.config.argparsing import Parser
from _pytest.deprecated import check_ispytest from _pytest.deprecated import check_ispytest
from _pytest.nodes import Collector from _pytest.nodes import Collector

View File

@ -18,6 +18,7 @@ from typing import Callable
from typing import cast from typing import cast
from typing import ClassVar from typing import ClassVar
from typing import Dict from typing import Dict
from typing import final
from typing import Generator from typing import Generator
from typing import List from typing import List
from typing import Mapping from typing import Mapping
@ -40,7 +41,6 @@ from _pytest._code.code import ExceptionRepr
from _pytest._io import TerminalWriter from _pytest._io import TerminalWriter
from _pytest._io.wcwidth import wcswidth from _pytest._io.wcwidth import wcswidth
from _pytest.assertion.util import running_on_ci from _pytest.assertion.util import running_on_ci
from _pytest.compat import final
from _pytest.config import _PluggyPlugin from _pytest.config import _PluggyPlugin
from _pytest.config import Config from _pytest.config import Config
from _pytest.config import ExitCode from _pytest.config import ExitCode

View File

@ -7,38 +7,32 @@ from pathlib import Path
from shutil import rmtree from shutil import rmtree
from typing import Any from typing import Any
from typing import Dict from typing import Dict
from typing import final
from typing import Generator from typing import Generator
from typing import Literal
from typing import Optional from typing import Optional
from typing import TYPE_CHECKING
from typing import Union from typing import Union
from _pytest.nodes import Item from .pathlib import cleanup_dead_symlinks
from _pytest.reports import CollectReport
from _pytest.stash import StashKey
if TYPE_CHECKING:
from typing_extensions import Literal
RetentionType = Literal["all", "failed", "none"]
from _pytest.config.argparsing import Parser
from .pathlib import LOCK_TIMEOUT from .pathlib import LOCK_TIMEOUT
from .pathlib import make_numbered_dir from .pathlib import make_numbered_dir
from .pathlib import make_numbered_dir_with_cleanup from .pathlib import make_numbered_dir_with_cleanup
from .pathlib import rm_rf from .pathlib import rm_rf
from .pathlib import cleanup_dead_symlinks from _pytest.compat import get_user_id
from _pytest.compat import final, get_user_id
from _pytest.config import Config from _pytest.config import Config
from _pytest.config import ExitCode from _pytest.config import ExitCode
from _pytest.config import hookimpl from _pytest.config import hookimpl
from _pytest.config.argparsing import Parser
from _pytest.deprecated import check_ispytest from _pytest.deprecated import check_ispytest
from _pytest.fixtures import fixture from _pytest.fixtures import fixture
from _pytest.fixtures import FixtureRequest from _pytest.fixtures import FixtureRequest
from _pytest.monkeypatch import MonkeyPatch from _pytest.monkeypatch import MonkeyPatch
from _pytest.nodes import Item
from _pytest.reports import CollectReport
from _pytest.stash import StashKey
tmppath_result_key = StashKey[Dict[str, bool]]() tmppath_result_key = StashKey[Dict[str, bool]]()
RetentionType = Literal["all", "failed", "none"]
@final @final

View File

@ -3,12 +3,11 @@ import inspect
import warnings import warnings
from types import FunctionType from types import FunctionType
from typing import Any from typing import Any
from typing import final
from typing import Generic from typing import Generic
from typing import Type from typing import Type
from typing import TypeVar from typing import TypeVar
from _pytest.compat import final
class PytestWarning(UserWarning): class PytestWarning(UserWarning):
"""Base class for all warnings emitted by pytest.""" """Base class for all warnings emitted by pytest."""

View File

@ -1,10 +1,10 @@
import dataclasses import dataclasses
import importlib.metadata
import os import os
import sys import sys
import types import types
import pytest import pytest
from _pytest.compat import importlib_metadata
from _pytest.config import ExitCode from _pytest.config import ExitCode
from _pytest.pathlib import symlink_or_skip from _pytest.pathlib import symlink_or_skip
from _pytest.pytester import Pytester from _pytest.pytester import Pytester
@ -139,7 +139,7 @@ class TestGeneralUsage:
def my_dists(): def my_dists():
return (DummyDist(entry_points),) return (DummyDist(entry_points),)
monkeypatch.setattr(importlib_metadata, "distributions", my_dists) monkeypatch.setattr(importlib.metadata, "distributions", my_dists)
params = ("-p", "mycov") if load_cov_early else () params = ("-p", "mycov") if load_cov_early else ()
pytester.runpytest_inprocess(*params) pytester.runpytest_inprocess(*params)
if load_cov_early: if load_cov_early:

View File

@ -439,14 +439,9 @@ comment 4
''' '''
for line in range(2, 6): for line in range(2, 6):
assert str(getstatement(line, source)) == " x = 1" assert str(getstatement(line, source)) == " x = 1"
if sys.version_info >= (3, 8) or hasattr(sys, "pypy_version_info"): for line in range(6, 8):
tqs_start = 8
else:
tqs_start = 10
assert str(getstatement(10, source)) == '"""'
for line in range(6, tqs_start):
assert str(getstatement(line, source)) == " assert False" assert str(getstatement(line, source)) == " assert False"
for line in range(tqs_start, 10): for line in range(8, 10):
assert str(getstatement(line, source)) == '"""\ncomment 4\n"""' assert str(getstatement(line, source)) == '"""\ncomment 4\n"""'

View File

@ -19,7 +19,6 @@ from hypothesis import strategies
import pytest import pytest
from _pytest import fixtures from _pytest import fixtures
from _pytest import python from _pytest import python
from _pytest.compat import _format_args
from _pytest.compat import getfuncargnames from _pytest.compat import getfuncargnames
from _pytest.compat import NOTSET from _pytest.compat import NOTSET
from _pytest.outcomes import fail from _pytest.outcomes import fail
@ -1036,27 +1035,6 @@ class TestMetafunc:
""" """
) )
def test_format_args(self) -> None:
def function1():
pass
assert _format_args(function1) == "()"
def function2(arg1):
pass
assert _format_args(function2) == "(arg1)"
def function3(arg1, arg2="qwe"):
pass
assert _format_args(function3) == "(arg1, arg2='qwe')"
def function4(arg1, *args, **kwargs):
pass
assert _format_args(function4) == "(arg1, *args, **kwargs)"
class TestMetafuncFunctional: class TestMetafuncFunctional:
def test_attributes(self, pytester: Pytester) -> None: def test_attributes(self, pytester: Pytester) -> None:

View File

@ -199,8 +199,8 @@ class TestImportHookInstallation:
return check return check
""", """,
"mainwrapper.py": """\ "mainwrapper.py": """\
import importlib.metadata
import pytest import pytest
from _pytest.compat import importlib_metadata
class DummyEntryPoint(object): class DummyEntryPoint(object):
name = 'spam' name = 'spam'
@ -220,7 +220,7 @@ class TestImportHookInstallation:
def distributions(): def distributions():
return (DummyDistInfo(),) return (DummyDistInfo(),)
importlib_metadata.distributions = distributions importlib.metadata.distributions = distributions
pytest.main() pytest.main()
""", """,
"test_foo.py": """\ "test_foo.py": """\

View File

@ -131,9 +131,8 @@ class TestAssertionRewrite:
for n in [node, *ast.iter_child_nodes(node)]: for n in [node, *ast.iter_child_nodes(node)]:
assert n.lineno == 3 assert n.lineno == 3
assert n.col_offset == 0 assert n.col_offset == 0
if sys.version_info >= (3, 8): assert n.end_lineno == 6
assert n.end_lineno == 6 assert n.end_col_offset == 3
assert n.end_col_offset == 3
def test_dont_rewrite(self) -> None: def test_dont_rewrite(self) -> None:
s = """'PYTEST_DONT_REWRITE'\nassert 14""" s = """'PYTEST_DONT_REWRITE'\nassert 14"""
@ -1270,9 +1269,6 @@ class TestIssue2121:
result.stdout.fnmatch_lines(["*E*assert (1 + 1) == 3"]) result.stdout.fnmatch_lines(["*E*assert (1 + 1) == 3"])
@pytest.mark.skipif(
sys.version_info < (3, 8), reason="walrus operator not available in py<38"
)
class TestIssue10743: class TestIssue10743:
def test_assertion_walrus_operator(self, pytester: Pytester) -> None: def test_assertion_walrus_operator(self, pytester: Pytester) -> None:
pytester.makepyfile( pytester.makepyfile(
@ -1441,9 +1437,6 @@ class TestIssue10743:
assert result.ret == 0 assert result.ret == 0
@pytest.mark.skipif(
sys.version_info < (3, 8), reason="walrus operator not available in py<38"
)
class TestIssue11028: class TestIssue11028:
def test_assertion_walrus_operator_in_operand(self, pytester: Pytester) -> None: def test_assertion_walrus_operator_in_operand(self, pytester: Pytester) -> None:
pytester.makepyfile( pytester.makepyfile(
@ -1957,16 +1950,10 @@ class TestPyCacheDir:
) )
def test_get_cache_dir(self, monkeypatch, prefix, source, expected) -> None: def test_get_cache_dir(self, monkeypatch, prefix, source, expected) -> None:
monkeypatch.delenv("PYTHONPYCACHEPREFIX", raising=False) monkeypatch.delenv("PYTHONPYCACHEPREFIX", raising=False)
if prefix is not None and sys.version_info < (3, 8):
pytest.skip("pycache_prefix not available in py<38")
monkeypatch.setattr(sys, "pycache_prefix", prefix, raising=False) monkeypatch.setattr(sys, "pycache_prefix", prefix, raising=False)
assert get_cache_dir(Path(source)) == Path(expected) assert get_cache_dir(Path(source)) == Path(expected)
@pytest.mark.skipif(
sys.version_info < (3, 8), reason="pycache_prefix not available in py<38"
)
@pytest.mark.skipif( @pytest.mark.skipif(
sys.version_info[:2] == (3, 9) and sys.platform.startswith("win"), sys.version_info[:2] == (3, 9) and sys.platform.startswith("win"),
reason="#9298", reason="#9298",

View File

@ -1,5 +1,6 @@
import enum import enum
import sys import sys
from functools import cached_property
from functools import partial from functools import partial
from functools import wraps from functools import wraps
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
@ -8,7 +9,6 @@ from typing import Union
import pytest import pytest
from _pytest.compat import _PytestWrapper from _pytest.compat import _PytestWrapper
from _pytest.compat import assert_never from _pytest.compat import assert_never
from _pytest.compat import cached_property
from _pytest.compat import get_real_func from _pytest.compat import get_real_func
from _pytest.compat import is_generator from _pytest.compat import is_generator
from _pytest.compat import safe_getattr from _pytest.compat import safe_getattr

View File

@ -1,4 +1,5 @@
import dataclasses import dataclasses
import importlib.metadata
import os import os
import re import re
import sys import sys
@ -13,7 +14,6 @@ from typing import Union
import _pytest._code import _pytest._code
import pytest import pytest
from _pytest.compat import importlib_metadata
from _pytest.config import _get_plugin_specs_as_list from _pytest.config import _get_plugin_specs_as_list
from _pytest.config import _iter_rewritable_modules from _pytest.config import _iter_rewritable_modules
from _pytest.config import _strtobool from _pytest.config import _strtobool
@ -475,7 +475,7 @@ class TestParseIni:
pytester.makepyfile(myplugin1_module="# my plugin module") pytester.makepyfile(myplugin1_module="# my plugin module")
pytester.syspathinsert() pytester.syspathinsert()
monkeypatch.setattr(importlib_metadata, "distributions", my_dists) monkeypatch.setattr(importlib.metadata, "distributions", my_dists)
monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD", raising=False) monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD", raising=False)
pytester.makeini(ini_file_text) pytester.makeini(ini_file_text)
@ -1003,7 +1003,7 @@ def test_preparse_ordering_with_setuptools(
def my_dists(): def my_dists():
return (Dist,) return (Dist,)
monkeypatch.setattr(importlib_metadata, "distributions", my_dists) monkeypatch.setattr(importlib.metadata, "distributions", my_dists)
pytester.makeconftest( pytester.makeconftest(
""" """
pytest_plugins = "mytestplugin", pytest_plugins = "mytestplugin",
@ -1036,7 +1036,7 @@ def test_setuptools_importerror_issue1479(
def distributions(): def distributions():
return (Distribution(),) return (Distribution(),)
monkeypatch.setattr(importlib_metadata, "distributions", distributions) monkeypatch.setattr(importlib.metadata, "distributions", distributions)
with pytest.raises(ImportError): with pytest.raises(ImportError):
pytester.parseconfig() pytester.parseconfig()
@ -1063,7 +1063,7 @@ def test_importlib_metadata_broken_distribution(
def distributions(): def distributions():
return (Distribution(),) return (Distribution(),)
monkeypatch.setattr(importlib_metadata, "distributions", distributions) monkeypatch.setattr(importlib.metadata, "distributions", distributions)
pytester.parseconfig() pytester.parseconfig()
@ -1091,7 +1091,7 @@ def test_plugin_preparse_prevents_setuptools_loading(
def distributions(): def distributions():
return (Distribution(),) return (Distribution(),)
monkeypatch.setattr(importlib_metadata, "distributions", distributions) monkeypatch.setattr(importlib.metadata, "distributions", distributions)
args = ("-p", "no:mytestplugin") if block_it else () args = ("-p", "no:mytestplugin") if block_it else ()
config = pytester.parseconfig(*args) config = pytester.parseconfig(*args)
config.pluginmanager.import_plugin("mytestplugin") config.pluginmanager.import_plugin("mytestplugin")
@ -1140,7 +1140,7 @@ def test_disable_plugin_autoload(
return (Distribution(),) return (Distribution(),)
monkeypatch.setenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD", "1") monkeypatch.setenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD", "1")
monkeypatch.setattr(importlib_metadata, "distributions", distributions) monkeypatch.setattr(importlib.metadata, "distributions", distributions)
monkeypatch.setitem(sys.modules, "mytestplugin", PseudoPlugin()) # type: ignore[misc] monkeypatch.setitem(sys.modules, "mytestplugin", PseudoPlugin()) # type: ignore[misc]
config = pytester.parseconfig(*parse_args) config = pytester.parseconfig(*parse_args)
has_loaded = config.pluginmanager.get_plugin("mytestplugin") is not None has_loaded = config.pluginmanager.get_plugin("mytestplugin") is not None

View File

@ -1,7 +1,7 @@
from _pytest.compat import importlib_metadata import importlib.metadata
def test_pytest_entry_points_are_identical(): def test_pytest_entry_points_are_identical():
dist = importlib_metadata.distribution("pytest") dist = importlib.metadata.distribution("pytest")
entry_map = {ep.name: ep for ep in dist.entry_points} entry_map = {ep.name: ep for ep in dist.entry_points}
assert entry_map["pytest"].value == entry_map["py.test"].value assert entry_map["pytest"].value == entry_map["py.test"].value

View File

@ -1142,12 +1142,10 @@ def test_errors_in_xfail_skip_expressions(pytester: Pytester) -> None:
""" """
) )
result = pytester.runpytest() result = pytester.runpytest()
markline = " ^" markline = " ^"
pypy_version_info = getattr(sys, "pypy_version_info", None) pypy_version_info = getattr(sys, "pypy_version_info", None)
if pypy_version_info is not None and pypy_version_info < (6,): if pypy_version_info is not None and pypy_version_info < (6,):
markline = markline[5:] markline = markline[1:]
elif sys.version_info >= (3, 8) or hasattr(sys, "pypy_version_info"):
markline = markline[4:]
if sys.version_info[:2] >= (3, 10): if sys.version_info[:2] >= (3, 10):
expected = [ expected = [

View File

@ -1,13 +1,7 @@
import sys
import pytest import pytest
from _pytest.pytester import Pytester from _pytest.pytester import Pytester
if sys.version_info < (3, 8):
pytest.skip("threadexception plugin needs Python>=3.8", allow_module_level=True)
@pytest.mark.filterwarnings("default::pytest.PytestUnhandledThreadExceptionWarning") @pytest.mark.filterwarnings("default::pytest.PytestUnhandledThreadExceptionWarning")
def test_unhandled_thread_exception(pytester: Pytester) -> None: def test_unhandled_thread_exception(pytester: Pytester) -> None:
pytester.makepyfile( pytester.makepyfile(

View File

@ -1354,9 +1354,6 @@ def test_plain_unittest_does_not_support_async(pytester: Pytester) -> None:
result.stdout.fnmatch_lines(expected_lines) result.stdout.fnmatch_lines(expected_lines)
@pytest.mark.skipif(
sys.version_info < (3, 8), reason="Feature introduced in Python 3.8"
)
def test_do_class_cleanups_on_success(pytester: Pytester) -> None: def test_do_class_cleanups_on_success(pytester: Pytester) -> None:
testpath = pytester.makepyfile( testpath = pytester.makepyfile(
""" """
@ -1382,9 +1379,6 @@ def test_do_class_cleanups_on_success(pytester: Pytester) -> None:
assert passed == 3 assert passed == 3
@pytest.mark.skipif(
sys.version_info < (3, 8), reason="Feature introduced in Python 3.8"
)
def test_do_class_cleanups_on_setupclass_failure(pytester: Pytester) -> None: def test_do_class_cleanups_on_setupclass_failure(pytester: Pytester) -> None:
testpath = pytester.makepyfile( testpath = pytester.makepyfile(
""" """
@ -1409,9 +1403,6 @@ def test_do_class_cleanups_on_setupclass_failure(pytester: Pytester) -> None:
assert passed == 1 assert passed == 1
@pytest.mark.skipif(
sys.version_info < (3, 8), reason="Feature introduced in Python 3.8"
)
def test_do_class_cleanups_on_teardownclass_failure(pytester: Pytester) -> None: def test_do_class_cleanups_on_teardownclass_failure(pytester: Pytester) -> None:
testpath = pytester.makepyfile( testpath = pytester.makepyfile(
""" """

View File

@ -1,13 +1,7 @@
import sys
import pytest import pytest
from _pytest.pytester import Pytester from _pytest.pytester import Pytester
if sys.version_info < (3, 8):
pytest.skip("unraisableexception plugin needs Python>=3.8", allow_module_level=True)
@pytest.mark.filterwarnings("default::pytest.PytestUnraisableExceptionWarning") @pytest.mark.filterwarnings("default::pytest.PytestUnraisableExceptionWarning")
def test_unraisable(pytester: Pytester) -> None: def test_unraisable(pytester: Pytester) -> None:
pytester.makepyfile( pytester.makepyfile(