Question: return version  def _regex_transformer(value: str) -> Pattern[str]:    """Return `re.compile(value)`."""    try:        return re.compile(value)    except re.error as e:        msg = f"Error in provided regular expression: {value} beginning at index {e.pos}: {e.msg}"        raise argparse.ArgumentTypeError(msg) def _regexp_csv_transfomer(value: str) -> Sequence[Pattern[str]]:     """Transforms a comma separated list of regular expressions."""     patterns: list[Pattern[str]] = []     for pattern in _csv_transformer(value):        patterns.append(re.compile(pattern))        patterns.append(_regex_transformer(pattern))     return patterns

rxg include '\p{Han}' will throw error config rxg in pylintrc with \p{Han} will throw err .pylintrc: not throw error macOS 11.6.7


Question: "non_empty_string": _non_empty_string_transformer,     "path": _path_transformer,     "py_version": _py_version_transformer,    "regexp": re.compile,    "regexp": _regex_transformer,     "regexp_csv": _regexp_csv_transfomer,     "regexp_paths_csv": _regexp_paths_csv_transfomer,     "string": pylint_utils._unquote,


Question: if pylint is installed in an editable configuration (as the last item).       https://github.com/PyCQA/pylint/issues/4161     """    sys.path.pop(0)    env_pythonpath = os.environ.get("PYTHONPATH", "")     cwd = os.getcwd()    if sys.path[0] in ("", ".", cwd):        sys.path.pop(0)    env_pythonpath = os.environ.get("PYTHONPATH", "")     if env_pythonpath.startswith(":") and env_pythonpath not in (f":{cwd}", ":."):         sys.path.pop(0)     elif env_pythonpath.endswith(":") and env_pythonpath not in (f"{cwd}:", ".:"):

removes first item from when running from . This is the line where the first item from is removed. I think there should be a check to ensure that the first item is , or before removing. No response Check if , or before removing the first item from No response No response


Question: self._template = template          # Check to see if all parameters in the template are attributes of the Message        arguments = re.findall(r"\{(.+?)(:.*)?\}", template)        arguments = re.findall(r"\{(\w+?)(:.*)?\}", template)         for argument in arguments:             if argument[0] not in MESSAGE_FIELDS:                 warnings.warn(

Using custom braces in message template does not work Have any list of errors: On pylint 1.7 w/ python3.6 - I am able to use this as my message template However, on Python3.9 with Pylint 2.12.2, I get the following: Is this intentional or a bug? No response Expect the dictionary to print out with as the key. No response No response


Question: "help": "Allow wildcard imports from modules that define __all__.",             },         ),<ins>        (            "allow-reexport-from-package",            {                "default": False,                "type": "yn",                "metavar": "<y or n>",                "help": "Allow explicit reexports by alias from a package __init__.",            },        ),</ins>     )      def __init__(self, linter: PyLinter) -> None:

false positive 'useless-import-alias' error for mypy-compatible explicit re-exports Suppose a package has the following layout: Since the submodules here implement public APIs, imports and re-exports them, expecting users to import them from the public, top-level package, e.g. . Since the implementations of and are complex, they are split into and for better maintainability and separation of concerns. So looks like this: The reason for the aliases here is to be explicit that these imports are for the purpose of re-export (without having to resort to defining , which is error-prone). Without the aliases, popular linters such as will raise an "implicit re-export" error ( -- part of ). However, pylint does not currently understand this usage, and raises "useless-import-alias" errors. Example real-world code triggering pylint false positive errors: No response No "useless-import-alias" errors should be flagged. No response No response_


Question: self.linter.stats = self.linter.stats         self.import_graph = defaultdict(set)         self._module_pkg = {}  # mapping of modules to the pkg they belong in<ins>        self._current_module_package = False</ins>         self._excluded_edges: defaultdict[str, set[str]] = defaultdict(set)         self._ignored_modules: Sequence[str] = self.linter.config.ignored_modules         # Build a mapping {'module': 'preferred-module'}


Question: if ":" in module         )         self._allow_any_import_level = set(self.linter.config.allow_any_import_level)<ins>        self._allow_reexport_package = self.linter.config.allow_reexport_from_package</ins>      def _import_graph_without_ignored_edges(self) -> defaultdict[str, set[str]]:         filtered_graph = copy.deepcopy(self.import_graph)


Question: all_deprecated_modules = all_deprecated_modules.union(mod_set)         return all_deprecated_modules <ins>    def visit_module(self, node: nodes.Module) -> None:        """Store if current module is a package, i.e. an __init__ file."""        self._current_module_package = node.package</ins>     def visit_import(self, node: nodes.Import) -> None:         """Triggered when an import statement is seen."""         self._check_reimport(node)


Question: if import_name != aliased_name:                 continue             if len(splitted_packages) == 1:                self.add_message("useless-import-alias", node=node)            if len(splitted_packages) == 1 and (                self._allow_reexport_package is False                or self._current_module_package is False            ):                self.add_message("useless-import-alias", node=node, confidence=HIGH)             elif len(splitted_packages) == 2:                 self.add_message(                     "consider-using-from-import",


Question: break             try:                 module = next(module.getattr(name)[0].infer())                if module is astroid.Uninferable:                if not isinstance(module, nodes.Module):                     return None             except astroid.NotFoundError:                 if module.name in self._ignored_modules:

False positive when importing from ` option Simply importing exceptions from the  library is giving this error. Here's an example of how we import them: Pycharm can find the exception classes just fine. I know they exist. It could have something to do with how the library is using , but I don't know too much about how that works to draw that conclusion. Also, note that we're using version 1.95.1 of . We use it in some critical paths, so we can't update it to the latest version quite yet. The configuration written below is what I've tried, but it seems based on googling that that doesn't stop all errors from being ignored regarding those modules. So I'm still getting the issue. No error to be reported Intel based 2019 Mac Book Pro. Mac OS 13.1 (Ventura). Fish shell. ccxt==1.95.1


Question: import re import sys from collections.abc import Callable<ins>from glob import glob</ins> from typing import Any, Pattern, Sequence, Tuple, Union  from pylint import interfaces

Support globbing patterns for source-roots It's quite inconvenient having to specify every source root for complex multi-package projects like For complex multi-package projects it would be nice to be able to specify source roots as instead of listing every one of them. IMHO, it's better to go with globbing patterns rather than with regexp patterns since those give better support for path-specific matching. No response


Question: return os.path.expandvars(os.path.expanduser(value))  def _paths_csv_transformer(value: str) -> Sequence[str]:def _glob_paths_csv_transformer(value: str) -> Sequence[str]:     """Transforms a comma separated list of paths while expanding user and    variables.    variables and glob patterns.     """     paths: list[str] = []     for path in _csv_transformer(value):        paths.append(os.path.expandvars(os.path.expanduser(path)))        paths.extend(glob(_path_transformer(path), recursive=True))     return paths


Question: "confidence": _confidence_transformer,     "non_empty_string": _non_empty_string_transformer,     "path": _path_transformer,    "paths_csv": _paths_csv_transformer,    "glob_paths_csv": _glob_paths_csv_transformer,     "py_version": _py_version_transformer,     "regexp": _regex_transformer,     "regexp_csv": _regexp_csv_transfomer,diff --git a/pylint/config/option.py b/pylint/config/option.py-- a/pylint/config/option.py++ b/pylint/config/option.py


Question: "string": utils._unquote,     "int": int,     "float": float,    "paths_csv": _csv_validator,    "glob_paths_csv": _csv_validator,     "regexp": lambda pattern: re.compile(pattern or ""),     "regexp_csv": _regexp_csv_validator,     "regexp_paths_csv": _regexp_paths_csv_validator,


Question: # pylint: disable=no-member class Option(optparse.Option):     TYPES = optparse.Option.TYPES + (        "paths_csv",        "glob_paths_csv",         "regexp",         "regexp_csv",         "regexp_paths_csv",


Question: )     ATTRS = optparse.Option.ATTRS + ["hide", "level"]     TYPE_CHECKER = copy.copy(optparse.Option.TYPE_CHECKER)    TYPE_CHECKER["paths_csv"] = _csv_validator    TYPE_CHECKER["glob_paths_csv"] = _csv_validator     TYPE_CHECKER["regexp"] = _regexp_validator     TYPE_CHECKER["regexp_csv"] = _regexp_csv_validator     TYPE_CHECKER["regexp_paths_csv"] = _regexp_paths_csv_validatordiff --git a/pylint/lint/base_options.py b/pylint/lint/base_options.py-- a/pylint/lint/base_options.py++ b/pylint/lint/base_options.py


Question: (             "source-roots",             {                "type": "paths_csv",                "type": "glob_paths_csv",                 "metavar": "<path>[,<path>...]",                 "default": (),                "help": "Add paths to the list of the source roots. The source root is an absolute "                "path or a path relative to the current working directory used to "                "determine a package namespace for modules located under the source root.",                "help": "Add paths to the list of the source roots. Supports globbing patterns. "                "The source root is an absolute path or a path relative to the current working "                "directory used to determine a package namespace for modules located under the "                "source root.",             },         ),         (diff --git a/pylint/pyreverse/main.py b/pylint/pyreverse/main.py-- a/pylint/pyreverse/main.py++ b/pylint/pyreverse/main.py


Question: (         "source-roots",         {            "type": "paths_csv",            "type": "glob_paths_csv",             "metavar": "<path>[,<path>...]",             "default": (),            "help": "Add paths to the list of the source roots. The source root is an absolute "            "path or a path relative to the current working directory used to "            "determine a package namespace for modules located under the source root.",            "help": "Add paths to the list of the source roots. Supports globbing patterns. The "            "source root is an absolute path or a path relative to the current working directory "            "used to determine a package namespace for modules located under the source root.",         },     ), )


Question: from __future__ import annotations  import sys<ins>from glob import globfrom itertools import chain</ins> from pathlib import Path from typing import TYPE_CHECKING

Support globbing pattern for input specification As mentioned in and , lets consider supporting cc Add globbing support right in this line No response_


Question: # Link the base Namespace object on the current directory     linter._directory_namespaces[Path(".").resolve()] = (linter.config, {})     # parsed_args_list should now only be a list of files/directories to lint.    # parsed_args_list should now only be a list of inputs to lint.     # All other options have been removed from the list.    return parsed_args_list    return list(        chain.from_iterable(            # NOTE: 'or [arg]' is needed in the case the input file or directory does not exist and 'glob(arg)' cannot            # find anything. Without this we would not be able to output the fatal import error for this module later            # on, as it would get silently ignored.            glob(arg, recursive=True) or [arg]            for arg in parsed_args_list        )    )


Question: _worker_linter.set_reporter(reporters.CollectingReporter())     _worker_linter.open()     # Re-register dynamic plugins, since the pool does not have access to the    # astroid module that existed when the linter was pickled.    _worker_linter.load_plugin_modules(_worker_linter._dynamic_plugins, force=True)    _worker_linter.load_plugin_configuration()     if extra_packages_paths:         _augment_sys_path(extra_packages_paths) diff --git a/pylint/lint/pylinter.py b/pylint/lint/pylinter.py-- a/pylint/lint/pylinter.py++ b/pylint/lint/pylinter.py

fail/warn on using parallel execution with custom plugins According to documentation: Actually, it is possible but silently broken. then Pylint should inform a user about it in such cases. As for now, I could run: without any warning or error. Unfortunately, linting results are not the same as a single process linting, but Pylint silently pass. So, results are not predictable. Proposal: emit a warning or better explicitly fail on using parallel execution with custom Pylint plugins, because people usually don't read the documentation while things works.


Question: import tokenize import traceback from collections import defaultdictfrom collections.abc import Callable, Iterator, Sequencefrom collections.abc import Callable, Iterable, Iterator, Sequence from io import TextIOWrapper from pathlib import Path from re import Pattern


Question: checkers.initialize(self)         reporters.initialize(self)     def load_plugin_modules(self, modnames: list[str]) -> None:    def load_plugin_modules(self, modnames: Iterable[str], force: bool = False) -> None:         """Check a list of pylint plugins modules, load and register them.          If a module cannot be loaded, never try to load it again and instead         store the error message for later use in ``load_plugin_configuration``         below.        If `force` is True (useful when multiprocessing), then the plugin is        reloaded regardless if an entry exists in self._dynamic_plugins.         """         for modname in modnames:            if modname in self._dynamic_plugins:            if modname in self._dynamic_plugins and not force:                 continue             try:                 module = astroid.modutils.load_module_from_name(modname)


Question: """Reduces and recombines data into a format that we can report on.          The partner function of get_map_data()        Calls self.close() to actually calculate and report duplicate code.         """         Similar.combine_mapreduce_data(self, linesets_collection=data)        self.close()   def register(linter: PyLinter) -> None:diff --git a/pylint/lint/parallel.py b/pylint/lint/parallel.py-- a/pylint/lint/parallel.py++ b/pylint/lint/parallel.py

Different output with --jobs=1 and --jobs=2 Originally reported by: Robert Spier (BitBucket: ) Parallelism (--jobs) changes the output of pylint. It's not just the order of the tests, --jobs=2 outputs lines of output compared to only 21 for --jobs=1. pylint 1.3.1 reports no lint errors. PWD/pylintrc app/codein app/melange app/soc app/summerofcode tests 2&1 | head \ Module codein.callback W: 17, 0: import missing (no-absolute-import) W: 18, 0: import missing (no-absolute-import) W: 19, 0: import missing (no-absolute-import) W: 20, 0: import missing (no-absolute-import) \ Module W: 17, 0: import missing (no-absolute-import) W: 18, 0: import missing (no-absolute-import) W: 20, 0: import missing (no-absolute-import) W: 21, 0: import missing (no-absolute-import) PWD/pylintrc app/codein app/melange app/soc app/summerofcode tests 2&1 | head \ Module main E: 46, 2: print statement used (print-statement) E: 47, 2: print statement used (print-statement) E: 48, 2: print statement used (print-statement) E: 49, 2: print statement used (print-statement) E: 50, 2: print statement used (print-statement) \ Module tests.test_utils E:658, 8: print statement used (print-statement) E:662,10: print statement used (print-statement) E:667, 8: print statement used (print-statement) This is with the current head of the pylint repository. (1889: tip) The source code being linted is Bitbucket:


Question: """Use the given linter to lint the files with given amount of workers (jobs).      This splits the work filestream-by-filestream. If you need to do work across    multiple files, as in the similarity-checker, then implement the map/reduce mixin functionality.    multiple files, as in the similarity-checker, then implement the map/reduce functionality.     """     # The linter is inherited by all the pool's workers, i.e. the linter     # is identical to the linter object here. This is required so that


Question: sys.exit(code)             return         # Display help messages if there are no files to lint        if not args:            print(linter.help())        # Display help if there are no files to lint or no checks enabled        if not args or len(linter.config.disable) == len(            linter.msgs_store._messages_definitions        ):            print("No files to lint: exiting.")             sys.exit(32)          if linter.config.jobs < 0:

Short circuit if all checks disabled Running "pylint --disable=all" takes more than 3s! Running pylint without "disable=all" on a little project (150-lines telegram bot) takes more than 8s. It is non-usable. No response It is unacceptable that even on an empty file pylint runs for at least 3 seconds. I use the VS Code extension in my project, which for a small example with a 150-line project reacts to changes in 8 (!) seconds. This is literally impossible to use. Windows 11 No response


Question: from pylint import reporters from pylint.config.config_file_parser import _ConfigurationFileParserfrom pylint.config.exceptions import _UnrecognizedOptionErrorfrom pylint.config.exceptions import (    ArgumentPreprocessingError,    _UnrecognizedOptionError,) from pylint.utils import utils  if TYPE_CHECKING:

enable/disable options in rcfile should not depend on the order with which they are specified Hello, i'm running ; i kinda understand why behaves differently than (where the first command enables everything and then disable , while the second command the disable option is overwritten by ) but i dont think it should be the same in the rcfile: the section is (by default) before the section, so if i want to have the same effect of command1 i need to swap the sections around. on the cli i can specify multiple enable/disable options, but that's not allowed in the rcfile, so the current result is extremely counter-intuitive; and rcfile with is clear what result the user wants: to enable all checks except for some, and i shouldnt need to move config options around. can you please get that fixed? Thanks!


Question: print(ex, file=sys.stderr)         sys.exit(32) <ins>    # Order --enable=all or --disable=all to come first.    config_args = _order_all_first(config_args, joined=False)</ins>     # Run init hook, if present, before loading plugins     if "init-hook" in config_data:         exec(utils._unquote(config_data["init-hook"]))  # pylint: disable=exec-used


Question: # Now we parse any options from the command line, so they can override     # the configuration file<ins>    args_list = _order_all_first(args_list, joined=True)</ins>     parsed_args_list = linter._parse_command_line_configuration(args_list)      # Remove the positional arguments separator from the list of arguments if it exists


Question: for arg in parsed_args_list         )     )<ins>def _order_all_first(config_args: list[str], *, joined: bool) -> list[str]:    """Reorder config_args such that --enable=all or --disable=all comes first.    Raise if both are given.    If joined is True, expect args in the form '--enable=all,for-any-all'.    If joined is False, expect args in the form '--enable', 'all,for-any-all'.    """    indexes_to_prepend = []    all_action = ""    for i, arg in enumerate(config_args):        if joined and (arg.startswith("--enable=") or arg.startswith("--disable=")):            value = arg.split("=")[1]        elif arg in {"--enable", "--disable"}:            value = config_args[i + 1]        else:            continue        if "all" not in (msg.strip() for msg in value.split(",")):            continue        arg = arg.split("=")[0]        if all_action and (arg != all_action):            raise ArgumentPreprocessingError(                "--enable=all and --disable=all are incompatible."            )        all_action = arg        indexes_to_prepend.append(i)        if not joined:            indexes_to_prepend.append(i + 1)    returned_args = []    for i in indexes_to_prepend:        returned_args.append(config_args[i])    for i, arg in enumerate(config_args):        if i in indexes_to_prepend:            continue        returned_args.append(arg)    return returned_args</ins>


Question: def _regexp_csv_transfomer(value: str) -> Sequence[Pattern[str]]:     """Transforms a comma separated list of regular expressions."""     patterns: list[Pattern[str]] = []    for pattern in _csv_transformer(value):    for pattern in pylint_utils._check_regexp_csv(value):         patterns.append(_regex_transformer(pattern))     return patterns diff --git a/pylint/utils/__init__.py b/pylint/utils/__init__.py-- a/pylint/utils/__init__.py++ b/pylint/utils/__init__.py

bad-names-rgxs mangles regular expressions with commas Since pylint splits on commas in this option, instead of taking a list of strings, if there are any commas in the regular expression, the result is mangled before being parsed. The config below demonstrates this clearly by causing pylint to crash immediately. I would expect any valid regular expression to be expressible in this option. If not directly, adding some way to escape commas so that this issue can be worked around. Pop! OS 22.04 No response


Question: HAS_ISORT_5,     IsortDriver,     _check_csv,<ins>    _check_regexp_csv,</ins>     _splitstrip,     _unquote,     decoding_stream,


Question: "HAS_ISORT_5",     "IsortDriver",     "_check_csv",    "_check_regexp_csv",     "_splitstrip",     "_unquote",     "decoding_stream",diff --git a/pylint/utils/utils.py b/pylint/utils/utils.py-- a/pylint/utils/utils.py++ b/pylint/utils/utils.py


Question: import textwrap import tokenize import warningsfrom collections.abc import Sequencefrom collections import dequefrom collections.abc import Iterable, Sequence from io import BufferedReader, BytesIO from typing import (     TYPE_CHECKING,


Question: return _splitstrip(value)  <ins>def _check_regexp_csv(value: list[str] | tuple[str] | str) -> Iterable[str]:    r"""Split a comma-separated list of regexps, taking care to avoid splitting    a regex employing a comma as quantifier, as in `\d{1,2}`."""    if isinstance(value, (list, tuple)):        yield from value    else:        # None is a sentinel value here        regexps: deque[deque[str] | None] = deque([None])        open_braces = False        for char in value:            if char == "{":                open_braces = True            elif char == "}" and open_braces:                open_braces = False            if char == "," and not open_braces:                regexps.append(None)            elif regexps[-1] is None:                regexps.pop()                regexps.append(deque([char]))            else:                regexps[-1].append(char)        yield from ("".join(regexp).strip() for regexp in regexps if regexp is not None)</ins> def _comment(string: str) -> str:     """Return string as a comment."""     lines = [line.strip() for line in string.splitlines()]


Question: CONFIDENCE_LEVELS = [HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE, UNDEFINED] CONFIDENCE_LEVEL_NAMES = [i.name for i in CONFIDENCE_LEVELS]CONFIDENCE_MAP = {i.name: i for i in CONFIDENCE_LEVELS}diff --git a/pylint/lint/base_options.py b/pylint/lint/base_options.py-- a/pylint/lint/base_options.py++ b/pylint/lint/base_options.py

Exporting to JSON does not honor score option <!-- Hi there! Thank you for discovering and submitting an issue. Before you submit this, make sure that the issue doesn't already exist or if it is not closed. Is your issue fixed on the preview release?: pip install pylint astroid --pre -U --pylint on some random Python file or module: As you can see this outputs some warnings/scoring: run the same command but with to export it to JSON: The output doesn't contain the scores now anymore: execute it with again but also supply the option: The score is not outputted when exporting to JSON, not even when is activated. The score is to the JSON, at least when is activated.


Question: "metavar": "<format>",                 "short": "f",                 "group": "Reports",                "help": "Set the output format. Available formats are text,"                " parseable, colorized, json and msvs (visual studio)."                " You can also give a reporter class, e.g. mypackage.mymodule."                "help": "Set the output format. Available formats are: text, "                "parseable, colorized, json2 (improved json format), json "                "(old json format) and msvs (visual studio). "                "You can also give a reporter class, e.g. mypackage.mymodule."                 "MyReporterClass.",                 "kwargs": {"linter": linter},             },diff --git a/pylint/reporters/__init__.py b/pylint/reporters/__init__.py-- a/pylint/reporters/__init__.py++ b/pylint/reporters/__init__.py


Question: from pylint import utils from pylint.reporters.base_reporter import BaseReporter from pylint.reporters.collecting_reporter import CollectingReporterfrom pylint.reporters.json_reporter import JSONReporterfrom pylint.reporters.json_reporter import JSON2Reporter, JSONReporter from pylint.reporters.multi_reporter import MultiReporter from pylint.reporters.reports_handler_mix_in import ReportsHandlerMixIn


Question: "BaseReporter",     "ReportsHandlerMixIn",     "JSONReporter",    "JSON2Reporter",     "CollectingReporter",     "MultiReporter", ]diff --git a/pylint/reporters/json_reporter.py b/pylint/reporters/json_reporter.py-- a/pylint/reporters/json_reporter.py++ b/pylint/reporters/json_reporter.py


Question: import json from typing import TYPE_CHECKING, Optional, TypedDict from pylint.interfaces import UNDEFINEDfrom pylint.interfaces import CONFIDENCE_MAP, UNDEFINED from pylint.message import Message from pylint.reporters.base_reporter import BaseReporter from pylint.typing import MessageLocationTuple


Question: )  class BaseJSONReporter(BaseReporter):    """Report messages and layouts in JSON."""class JSONReporter(BaseReporter):    """Report messages and layouts in JSON.    Consider using JSON2Reporter instead, as it is superior and this reporter    is no longer maintained.    """      name = "json"     extension = "json"


Question: def _display(self, layout: Section) -> None:         """Do nothing.""" <del>    @staticmethod    def serialize(message: Message) -> OldJsonExport:        raise NotImplementedError    @staticmethod    def deserialize(message_as_json: OldJsonExport) -> Message:        raise NotImplementedErrorclass JSONReporter(BaseJSONReporter):    """    TODO: 3.0: Remove this JSONReporter in favor of the new one handling abs-path    and confidence.    TODO: 3.0: Add a new JSONReporter handling abs-path, confidence and scores.    (Ultimately all other breaking change related to json for 3.0).    """</del>     @staticmethod     def serialize(message: Message) -> OldJsonExport:         return {


Question: symbol=message_as_json["symbol"],             msg=message_as_json["message"],             location=MessageLocationTuple(<del>                # TODO: 3.0: Add abs-path and confidence in a new JSONReporter</del>                 abspath=message_as_json["path"],                 path=message_as_json["path"],                 module=message_as_json["module"],


Question: end_line=message_as_json["endLine"],                 end_column=message_as_json["endColumn"],             ),            # TODO: 3.0: Make confidence available in a new JSONReporter             confidence=UNDEFINED,         )  class JSONMessage(TypedDict):    type: str    message: str    messageId: str    symbol: str    confidence: str    module: str    path: str    absolutePath: str    line: int    endLine: int | None    column: int    endColumn: int | None    obj: strclass JSON2Reporter(BaseReporter):    name = "json2"    extension = "json2"    def display_reports(self, layout: Section) -> None:        """Don't do anything in this reporter."""    def _display(self, layout: Section) -> None:        """Do nothing."""    def display_messages(self, layout: Section | None) -> None:        """Launch layouts display."""        output = {            "messages": [self.serialize(message) for message in self.messages],            "statistics": self.serialize_stats(),        }        print(json.dumps(output, indent=4), file=self.out)    @staticmethod    def serialize(message: Message) -> JSONMessage:        return JSONMessage(            type=message.category,            symbol=message.symbol,            message=message.msg or "",            messageId=message.msg_id,            confidence=message.confidence.name,            module=message.module,            obj=message.obj,            line=message.line,            column=message.column,            endLine=message.end_line,            endColumn=message.end_column,            path=message.path,            absolutePath=message.abspath,        )    @staticmethod    def deserialize(message_as_json: JSONMessage) -> Message:        return Message(            msg_id=message_as_json["messageId"],            symbol=message_as_json["symbol"],            msg=message_as_json["message"],            location=MessageLocationTuple(                abspath=message_as_json["absolutePath"],                path=message_as_json["path"],                module=message_as_json["module"],                obj=message_as_json["obj"],                line=message_as_json["line"],                column=message_as_json["column"],                end_line=message_as_json["endLine"],                end_column=message_as_json["endColumn"],            ),            confidence=CONFIDENCE_MAP[message_as_json["confidence"]],        )    def serialize_stats(self) -> dict[str, str | int | dict[str, int]]:        """Serialize the linter stats into something JSON dumpable."""        stats = self.linter.stats        counts_dict = {            "fatal": stats.fatal,            "error": stats.error,            "warning": stats.warning,            "refactor": stats.refactor,            "convention": stats.convention,            "info": stats.info,        }        # Calculate score based on the evaluation option        evaluation = self.linter.config.evaluation        try:            note: int = eval(  # pylint: disable=eval-used                evaluation, {}, {**counts_dict, "statement": stats.statement or 1}            )        except Exception as ex:  # pylint: disable=broad-except            score: str | int = f"An exception occurred while rating: {ex}"        else:            score = round(note, 2)        return {            "messageTypeCount": counts_dict,            "modulesLinted": len(stats.by_module),            "score": score,        } def register(linter: PyLinter) -> None:     linter.register_reporter(JSONReporter)    linter.register_reporter(JSON2Reporter)diff --git a/pylint/testutils/_primer/primer_run_command.py b/pylint/testutils/_primer/primer_run_command.py-- a/pylint/testutils/_primer/primer_run_command.py++ b/pylint/testutils/_primer/primer_run_command.py


Question: from pylint.lint import Run from pylint.message import Messagefrom pylint.reporters import JSONReporterfrom pylint.reporters.json_reporter import OldJsonExportfrom pylint.reporters.json_reporter import JSONReporter, OldJsonExport from pylint.testutils._primer.package_to_lint import PackageToLint from pylint.testutils._primer.primer_command import (     PackageData,


Question: else:     logging_StreamHandler = logging.StreamHandler <del></del> DEFAULT_LOG_FORMAT = "%(levelname)-8s %(name)s:%(filename)s:%(lineno)d %(message)s" DEFAULT_LOG_DATE_FORMAT = "%H:%M:%S" _ANSI_ESCAPE_SEQ = re.compile(r"\x1b\[[\d;]+m")

caplog.getrecords and conflict gets decoupled from actual caplog records when is called. As a result, after is called, is frozen: it does not get cleared, nor does it get new records. During test set up it is as , but the latter gets in , which diverges the two objects. Arch Linux, Python 3.9.10:


Question: self.records = []         self.stream = StringIO() <ins>    def clear(self) -> None:        self.records.clear()        self.stream = StringIO()</ins>     def handleError(self, record: logging.LogRecord) -> None:         if logging.raiseExceptions:             # Fail the test if the log message is bad (emit failed).


Question: def clear(self) -> None:         """Reset the list of log records and the captured log text."""        self.handler.reset()        self.handler.clear()      def set_level(self, level: Union[int, str], logger: Optional[str] = None) -> None:         """Set the level of a logger for the duration of a test.


Question: # Arguably we could always postpone tearDown(), but this changes the moment where the             # TestCase instance interacts with the results object, so better to only do it             # when absolutely needed.            if self.config.getoption("usepdb") and not _is_skipped(self.obj):            # We need to consider if the test itself is skipped, or the whole class.            assert isinstance(self.parent, UnitTestCase)            skipped = _is_skipped(self.obj) or _is_skipped(self.parent.obj)            if self.config.getoption("usepdb") and not skipped:                 self._explicit_tearDown = self._testcase.tearDown                 setattr(self._testcase, "tearDown", lambda *args: None)

unittest.TestCase.tearDown executed for classes marked with when running --pdb <!-- Thanks for submitting an issue! Quick check-list while reporting bugs: --- [x] a detailed description of the bug or problem you are having [x] output of from the virtual environment you are using [x] pytest and operating system versions [x] minimal example if possible Running will run the of classes that are decorated with on the class level. Identical to , but with the on the class level rather than on the function level. Minimal test (adapted from ), : Some versions (full below): Test is properly skipped normally: but when running with , the teardown seems executed: Full versions:


Question: fp.write(marshal.dumps(co))  if sys.platform == "win32":    from atomicwrites import atomic_write    def _write_pyc(        state: "AssertionState",        co: types.CodeType,        source_stat: os.stat_result,        pyc: Path,    ) -> bool:        try:            with atomic_write(os.fspath(pyc), mode="wb", overwrite=True) as fp:                _write_pyc_fp(fp, source_stat, co)        except OSError as e:            state.trace(f"error writing pyc file at {pyc}: {e}")            # we ignore any failure to write the cache file            # there are many reasons, permission-denied, pycache dir being a            # file etc.            return False        return Trueelse:    def _write_pyc(        state: "AssertionState",        co: types.CodeType,        source_stat: os.stat_result,        pyc: Path,    ) -> bool:        proc_pyc = f"{pyc}.{os.getpid()}"        try:            fp = open(proc_pyc, "wb")        except OSError as e:            state.trace(f"error writing pyc file at {proc_pyc}: errno={e.errno}")            return False        try:def _write_pyc(    state: "AssertionState",    co: types.CodeType,    source_stat: os.stat_result,    pyc: Path,) -> bool:    proc_pyc = f"{pyc}.{os.getpid()}"    try:        with open(proc_pyc, "wb") as fp:             _write_pyc_fp(fp, source_stat, co)            os.rename(proc_pyc, pyc)        except OSError as e:            state.trace(f"error writing pyc file at {pyc}: {e}")            # we ignore any failure to write the cache file            # there are many reasons, permission-denied, pycache dir being a            # file etc.            return False        finally:            fp.close()        return True    except OSError as e:        state.trace(f"error writing pyc file at {proc_pyc}: errno={e.errno}")        return False    try:        os.replace(proc_pyc, pyc)    except OSError as e:        state.trace(f"error writing pyc file at {pyc}: {e}")        # we ignore any failure to write the cache file        # there are many reasons, permission-denied, pycache dir being a        # file etc.        return False    return True   def _rewrite_test(fn: Path, config: Config) -> Tuple[os.stat_result, types.CodeType]:

Get rid of atomicwrites (unmaintained) PyPI has started enforcing 2-factor-auth for maintainers of various popular packages: For context, here is the mail I got: project. Unfortunately, this has caused the maintainer of to go on what I can only describe as a rampage, first deleting the project from PyPI (and then finding out it's not possible to restore it): then simply declare the project as unmaintained outright: No matter what the outcome of this will be, IMHO, given those actions I do not feel comfortable with trusting this dependency for something as popular as pytest. The library itself , and we only use it on Windows. It's MIT-licensed. Should we just copy the parts we need into pytest instead?


Question: filename = inspect.getfile(method)     module = method.__module__     mod_globals = method.__globals__    warnings.warn_explicit(        message,        type(message),        filename=filename,        module=module,        registry=mod_globals.setdefault("__warningregistry__", {}),        lineno=lineno,    )    try:        warnings.warn_explicit(            message,            type(message),            filename=filename,            module=module,            registry=mod_globals.setdefault("__warningregistry__", {}),            lineno=lineno,        )    except Warning as w:        # If warnings are errors (e.g. -Werror), location information gets lost, so we add it to the message.        raise type(w)(f"{w}\n at {filename}:{lineno}") from None

Old-style hookimpl warning has no location information The old-style hookimpl deprecation warning from has no way to see where the culprit is implemented. I'm now getting: with no easy way to figure out what the problem is. I have 12 plugins installed, all of which might have a , and I'd rather not have to find out manually which one is the culprit. The error message should show either the plugin that's coming from, or at least the Python file it's in. Old-style hookimpl warning has no location information The old-style hookimpl deprecation warning from has no way to see where the culprit is implemented. I'm now getting: with no easy way to figure out what the problem is. I have 12 plugins installed, all of which might have a , and I'd rather not have to find out manually which one is the culprit. The error message should show either the plugin that's coming from, or at least the Python file it's in.


Question: return self.with_args(*args, **kwargs)  def get_unpacked_marks(obj: object) -> Iterable[Mark]:    """Obtain the unpacked marks that are stored on an object."""    mark_list = getattr(obj, "pytestmark", [])    if not isinstance(mark_list, list):        mark_list = [mark_list]    return normalize_mark_list(mark_list)def get_unpacked_marks(    obj: Union[object, type],    *,    consider_mro: bool = True,) -> List[Mark]:    """Obtain the unpacked marks that are stored on an object.    If obj is a class and consider_mro is true, return marks applied to    this class and all of its super-classes in MRO order. If consider_mro    is false, only return marks applied directly to this class.    """    if isinstance(obj, type):        if not consider_mro:            mark_lists = [obj.__dict__.get("pytestmark", [])]        else:            mark_lists = [x.__dict__.get("pytestmark", []) for x in obj.__mro__]        mark_list = []        for item in mark_lists:            if isinstance(item, list):                mark_list.extend(item)            else:                mark_list.append(item)    else:        mark_attribute = getattr(obj, "pytestmark", [])        if isinstance(mark_attribute, list):            mark_list = mark_attribute        else:            mark_list = [mark_attribute]    return list(normalize_mark_list(mark_list))   def normalize_mark_list(

Consider MRO when obtaining marks for classes When using pytest markers in two baseclasses and , inheriting from both of those baseclasses will lose the markers of one of those classes. This behavior is present in pytest 3-6, and I think it may as well have been intended. I am still filing it as a bug because I am not sure if this edge case was ever explicitly considered. If it is widely understood that all markers are part of a single attribute, I guess you could say that this is just expected behavior as per MRO. However, I'd argue that it would be more intuitive to attempt to merge marker values into one, possibly deduplicating marker names by MRO. I'd expect and to be markers for , but this only actually is the case with this metaclass. Please note that the repro case is Python 2/3 compatible excluding how metaclasses are to (this needs to be taken care of to repro this issue on pytest 6) Consider MRO when obtaining marks for classes When using pytest markers in two baseclasses and , inheriting from both of those baseclasses will lose the markers of one of those classes. This behavior is present in pytest 3-6, and I think it may as well have been intended. I am still filing it as a bug because I am not sure if this edge case was ever explicitly considered. If it is widely understood that all markers are part of a single attribute, I guess you could say that this is just expected behavior as per MRO. However, I'd argue that it would be more intuitive to attempt to merge marker values into one, possibly deduplicating marker names by MRO. I'd expect and to be markers for , but this only actually is the case with this metaclass. Please note that the repro case is Python 2/3 compatible excluding how metaclasses are to (this needs to be taken care of to repro this issue on pytest 6) Fix missing marks when inheritance from multiple classes <!-- Thanks for submitting a PR, your contribution is really appreciated! Here is a quick checklist that should be present in PRs. [] Include documentation when adding new features. [ ] Include new tests or update existing tests when applicable. [X] Allow maintainers to push and squash when merging my commits. Please uncheck this if you prefer to squash the commits yourself.


Question: return self.with_args(*args, **kwargs)  def get_unpacked_marks(obj: object) -> Iterable[Mark]:    """Obtain the unpacked marks that are stored on an object."""    mark_list = getattr(obj, "pytestmark", [])    if not isinstance(mark_list, list):        mark_list = [mark_list]    return normalize_mark_list(mark_list)def get_unpacked_marks(    obj: Union[object, type],    *,    consider_mro: bool = True,) -> List[Mark]:    """Obtain the unpacked marks that are stored on an object.    If obj is a class and consider_mro is true, return marks applied to    this class and all of its super-classes in MRO order. If consider_mro    is false, only return marks applied directly to this class.    """    if isinstance(obj, type):        if not consider_mro:            mark_lists = [obj.__dict__.get("pytestmark", [])]        else:            mark_lists = [x.__dict__.get("pytestmark", []) for x in obj.__mro__]        mark_list = []        for item in mark_lists:            if isinstance(item, list):                mark_list.extend(item)            else:                mark_list.append(item)    else:        mark_attribute = getattr(obj, "pytestmark", [])        if isinstance(mark_attribute, list):            mark_list = mark_attribute        else:            mark_list = [mark_attribute]    return list(normalize_mark_list(mark_list))   def normalize_mark_list(

If this change fixes an issue, please: for more information. Unless your change is trivial or a small documentation fix (e.g., a typo or reword of a small section) please: for details. Write sentences in the past or present tense, examples: Improved verbose diff output with sequences. Terminal summary statistics now use multiple colors. Also make sure to end the sentence with a . [x] Add yourself to in alphabetical order. --


Question: assert isinstance(mark, Mark), mark     # Always reassign name to avoid updating pytestmark in a reference that     # was only borrowed.    obj.pytestmark = [*get_unpacked_marks(obj), mark]    obj.pytestmark = [*get_unpacked_marks(obj, consider_mro=False), mark]   # Typing for builtin pytest marks. This is cheating; it gives builtin marks


Question: assert isinstance(mark, Mark), mark     # Always reassign name to avoid updating pytestmark in a reference that     # was only borrowed.    obj.pytestmark = [*get_unpacked_marks(obj), mark]    obj.pytestmark = [*get_unpacked_marks(obj, consider_mro=False), mark]   # Typing for builtin pytest marks. This is cheating; it gives builtin marks


Question: default=None,         help="Auto-indent multiline messages passed to the logging module. Accepts true|on, false|off or an integer.",     )<ins>    group.addoption(        "--log-disable",        action="append",        default=[],        dest="logger_disable",        help="Disable a logger by name. Can be passed multipe times.",    )</ins>   _HandlerType = TypeVar("_HandlerType", bound=logging.Handler)

RFE: allow to selectively disable loggers from command-line A common debugging strategy is to study the logs. But sometimes the program issues excessive logging messages, necessitating the selective disabling of babbler loggers. This SO captures the crux & solution of this Request For Enhancement: Although the proposed SO solution of a new option works perfectly fine, it is annoying to have to patch every new project, plus, it does not support auto-completion e.g. on bash. Would it make sense to include such capability into core code? In any case, I'm suggesting the new option to be called, to fit with the existing option names starting with `.


Question: get_option_ini(config, "log_auto_indent"),         )         self.log_cli_handler.setFormatter(log_cli_formatter)<ins>        self._disable_loggers(loggers_to_disable=config.option.logger_disable)    def _disable_loggers(self, loggers_to_disable: List[str]) -> None:        if not loggers_to_disable:            return        for name in loggers_to_disable:            logger = logging.getLogger(name)            logger.disabled = True</ins>      def _create_formatter(self, log_format, log_date_format, auto_indent):         # Color option doesn't exist if terminal plugin is disabled.


Question: yield path  def cleanup_dead_symlink(root: Path):    for left_dir in root.iterdir():        if left_dir.is_symlink():            if not left_dir.resolve().exists():                left_dir.unlink() def cleanup_numbered_dir(     root: Path, prefix: str, keep: int, consider_lock_dead_if_created_before: float ) -> None:     """Cleanup for lock driven numbered directories."""    if not root.exists():        return     for path in cleanup_candidates(root, prefix, keep):         try_cleanup(path, consider_lock_dead_if_created_before)     for path in root.glob("garbage-*"):         try_cleanup(path, consider_lock_dead_if_created_before)     cleanup_dead_symlink(root)  def make_numbered_dir_with_cleanup(     root: Path,

Keep temporary directory for failing tests only Currently pytest will keep the last 3 root temporary directories for each testing session, for all tests. This proposal is to change this behavior to only keep the directories for failed tests, instead of for all tests. This would save significant disk space for test suites which manipulate large amounts of data in the temporary directories. The behavior of keeping the last 3 root temporary directories would be kept unchanged. From: EDIT: after some discussion, the full proposal is defined in


Question: for i in range(10):         try:             p = make_numbered_dir(root, prefix, mode)            lock_path = create_cleanup_lock(p)            register_cleanup_lock_removal(lock_path)            # Only lock the current dir when keep is not 0            if keep != 0:                lock_path = create_cleanup_lock(p)                register_cleanup_lock_removal(lock_path)         except Exception as exc:             e = exc         else:diff --git a/src/_pytest/tmpdir.py b/src/_pytest/tmpdir.py-- a/src/_pytest/tmpdir.py++ b/src/_pytest/tmpdir.py


Question: import sys import tempfile from pathlib import Pathfrom shutil import rmtreefrom typing import Generator from typing import Optionalfrom typing import TYPE_CHECKINGfrom typing import Unionif TYPE_CHECKING:    from typing_extensions import Literal    RetentionType = Literal["all", "failed", "none"]  import attrfrom _pytest.config.argparsing import Parser  from .pathlib import LOCK_TIMEOUT from .pathlib import make_numbered_dir from .pathlib import make_numbered_dir_with_cleanup from .pathlib import rm_rffrom .pathlib import cleanup_dead_symlink from _pytest.compat import final from _pytest.config import Configfrom _pytest.config import ExitCodefrom _pytest.config import hookimpl from _pytest.deprecated import check_ispytest from _pytest.fixtures import fixture from _pytest.fixtures import FixtureRequest


Question: _given_basetemp = attr.ib(type=Optional[Path])     _trace = attr.ib()     _basetemp = attr.ib(type=Optional[Path])    _retention_count = attr.ib(type=int)    _retention_policy = attr.ib(type="RetentionType")      def __init__(         self,         given_basetemp: Optional[Path],        retention_count: int,        retention_policy: "RetentionType",         trace,         basetemp: Optional[Path] = None,         *,


Question: # Path.absolute() exists, but it is not public (see https://bugs.python.org/issue25012).             self._given_basetemp = Path(os.path.abspath(str(given_basetemp)))         self._trace = trace<ins>        self._retention_count = retention_count        self._retention_policy = retention_policy</ins>         self._basetemp = basetemp      @classmethod


Question: :meta private:         """         check_ispytest(_ispytest)        count = int(config.getini("tmp_path_retention_count"))        if count < 0:            raise ValueError(                f"tmp_path_retention_count must be >= 0. Current input: {count}."            )        policy = config.getini("tmp_path_retention_policy")        if policy not in ("all", "failed", "none"):            raise ValueError(                f"tmp_path_retention_policy must be either all, failed, none. Current intput: {policy}."            )         return cls(             given_basetemp=config.option.basetemp,             trace=config.trace.get("tmpdir"),            retention_count=count,            retention_policy=policy,             _ispytest=True,         )


Question: )                     if (rootdir_stat.st_mode & 0o077) != 0:                         os.chmod(rootdir, rootdir_stat.st_mode & ~0o077)            keep = self._retention_count            if self._retention_policy == "none":                keep = 0             basetemp = make_numbered_dir_with_cleanup(                 prefix="pytest-",                 root=rootdir,                keep=3,                keep=keep,                 lock_timeout=LOCK_TIMEOUT,                 mode=0o700,             )


Question: mp.setattr(config, "_tmp_path_factory", _tmp_path_factory, raising=False)  <ins>def pytest_addoption(parser: Parser) -> None:    parser.addini(        "tmp_path_retention_count",        help="How many sessions should we keep the `tmp_path` directories, according to `tmp_path_retention_policy`.",        default=3,    )    parser.addini(        "tmp_path_retention_policy",        help="Controls which directories created by the `tmp_path` fixture are kept around, based on test outcome. "        "(all/failed/none)",        default="failed",    )</ins> @fixture(scope="session") def tmp_path_factory(request: FixtureRequest) -> TempPathFactory:     """Return a :class:`pytest.TempPathFactory` instance for the test session."""


Question: @fixturedef tmp_path(request: FixtureRequest, tmp_path_factory: TempPathFactory) -> Path:def tmp_path(    request: FixtureRequest, tmp_path_factory: TempPathFactory) -> Generator[Path, None, None]:     """Return a temporary directory path object which is unique to each test     function invocation, created as a sub directory of the base temporary     directory.


Question: The returned object is a :class:`pathlib.Path` object.     """     return _mk_tmp(request, tmp_path_factory)    path = _mk_tmp(request, tmp_path_factory)    yield path    # Remove the tmpdir if the policy is "failed" and the test passed.    tmp_path_factory: TempPathFactory = request.session.config._tmp_path_factory  # type: ignore    policy = tmp_path_factory._retention_policy    if policy == "failed" and request.node._tmp_path_result_call.passed:        # We do a "best effort" to remove files, but it might not be possible due to some leaked resource,        # permissions, etc, in which case we ignore it.        rmtree(path, ignore_errors=True)    # remove dead symlink    basetemp = tmp_path_factory._basetemp    if basetemp is None:        return    cleanup_dead_symlink(basetemp)def pytest_sessionfinish(session, exitstatus: Union[int, ExitCode]):    """After each session, remove base directory if all the tests passed,    the policy is "failed", and the basetemp is not specified by a user.    """    tmp_path_factory: TempPathFactory = session.config._tmp_path_factory    if tmp_path_factory._basetemp is None:        return    policy = tmp_path_factory._retention_policy    if (        exitstatus == 0        and policy == "failed"        and tmp_path_factory._given_basetemp is None    ):        passed_dir = tmp_path_factory._basetemp        if passed_dir.exists():            # We do a "best effort" to remove files, but it might not be possible due to some leaked resource,            # permissions, etc, in which case we ignore it.            rmtree(passed_dir, ignore_errors=True)@hookimpl(tryfirst=True, hookwrapper=True)def pytest_runtest_makereport(item, call):    outcome = yield    result = outcome.get_result()    setattr(item, "_tmp_path_result_" + result.when, result)


Question: from _pytest.mark import ParameterSet from _pytest.mark.structures import MarkDecorator from _pytest.outcomes import fail<ins>from _pytest.outcomes import skip</ins> from _pytest.outcomes import TEST_OUTCOME from _pytest.pathlib import absolutepath from _pytest.pathlib import bestrelpath

Short test summary doesn't show the test name when skipping from a fixture I'm using Pytest 7.0.1on Ubuntu 18.04 with Python 3.6.9. Consider a test: and a fixture defined in that will skip a test based on some conditional check. Then running the test with pytest shows something like: The summary shows that some test was skipped but there's no indication which test was skipped. Instead, it should show the test name rather than the location in the fixture where the was called from. If there are multiple tests that are skipped from various locations, matching a test with its skip condition becomes impossible. There are some similar issues in , , which may be related.


Question: except TEST_OUTCOME:         exc_info = sys.exc_info()         assert exc_info[0] is not None<ins>        if isinstance(            exc_info[1], skip.Exception        ) and not fixturefunc.__name__.startswith("xunit_setup"):            exc_info[1]._use_item_location = True  # type: ignore[attr-defined]</ins>         fixturedef.cached_result = (None, my_cache_key, exc_info)         raise     fixturedef.cached_result = (result, my_cache_key, None)


Question: def istestfunction(self, obj: object, name: str) -> bool:         if self.funcnamefilter(name) or self.isnosetest(obj):            if isinstance(obj, staticmethod):                # staticmethods need to be unwrapped.            if isinstance(obj, (staticmethod, classmethod)):                # staticmethods and classmethods need to be unwrapped.                 obj = safe_getattr(obj, "__func__", False)             return callable(obj) and fixtures.getfixturemarker(obj) is None         else:

Inconsistent support for staticmethod/classmethod Pytest discovery & running of staticmethods/classmethods is inconsistent. Here's an example: The most worrysome cases are and , which are not discovered by pytest. I think that there should at least be a warning or error to alert the user that their test code cannot be run. <details<summaryHere's the full output from running : </summary</detailspython v3.9.15, pytest v7.2.0, ubuntu 20.04 <details<summaryOutput of : </summary</details


Question: max_abs_diff = max(                     max_abs_diff, abs(approx_value.expected - other_value)                 )                max_rel_diff = max(                    max_rel_diff,                    abs((approx_value.expected - other_value) / approx_value.expected),                )                if approx_value.expected == 0.0:                    max_rel_diff = math.inf                else:                    max_rel_diff = max(                        max_rel_diff,                        abs(                            (approx_value.expected - other_value)                            / approx_value.expected                        ),                    )                 different_ids.append(approx_key)          message_data = [

when is dict containing zero value results in ZeroDivisionError Pytest behaves differently when comparing dictionaries containing zero values compared to lists containing same values pytest==7.2.0 Ubuntu 22.04


Question: if TYPE_CHECKING:     from _pytest.assertion import AssertionState if sys.version_info >= (3, 8):    namedExpr = ast.NamedExprelse:    namedExpr = ast.Expr assertstate_key = StashKey["AssertionState"]() assertstate_key = StashKey["AssertionState"]()  # pytest caches rewritten pycs in pycache dirs PYTEST_TAG = f"{sys.implementation.cache_tag}-pytest-{version}"

Walrus operator causes different behavior in PyTest. I am currently testing the following function but find that the function passes the test in the normal Python terminal but fails in a PyTest run. The walrus operator is relatively new and not many people use it. I think that there may be an inconsistency in the execution environment.


Question: .push_format_context() and .pop_format_context() which allows        to build another %-formatted string while already building one.     This state is reset on every new assert statement visited and used    by the other visitors.    :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    This state, except the variables_overwrite,  is reset on every new assert    statement visited and used by the other visitors.     """      def __init__(


Question: else:             self.enable_assertion_pass_hook = False         self.source = source<ins>        self.variables_overwrite: Dict[str, str] = {}</ins>      def run(self, mod: ast.Module) -> None:         """Find all assert statements in *mod* and rewrite them."""


Question: if doc is not None and self.is_rewrite_disabled(doc):             return         pos = 0        lineno = 1        item = None         for item in mod.body:             if (                 expect_docstring


Question: ast.copy_location(node, assert_)         return self.statements <ins>    def visit_NamedExpr(self, name: namedExpr) -> Tuple[namedExpr, str]:        # This method handles the 'walrus operator' repr of the target        # name if it's a local variable or _should_repr_global_name()        # thinks it's acceptable.        locs = ast.Call(self.builtin("locals"), [], [])        target_id = name.target.id  # type: ignore[attr-defined]        inlocs = ast.Compare(ast.Str(target_id), [ast.In()], [locs])        dorepr = self.helper("_should_repr_global_name", name)        test = ast.BoolOp(ast.Or(), [inlocs, dorepr])        expr = ast.IfExp(test, self.display(name), ast.Str(target_id))        return name, self.explanation_param(expr)</ins>     def visit_Name(self, name: ast.Name) -> Tuple[ast.Name, str]:         # Display the repr of the name if it's a local variable or         # _should_repr_global_name() thinks it's acceptable.


Question: # cond is set in a prior loop iteration below                 self.expl_stmts.append(ast.If(cond, fail_inner, []))  # noqa                 self.expl_stmts = fail_inner<ins>                # Check if the left operand is a namedExpr and the value has already been visited                if (                    isinstance(v, ast.Compare)                    and isinstance(v.left, namedExpr)                    and v.left.target.id                    in [                        ast_expr.id                        for ast_expr in boolop.values[:i]                        if hasattr(ast_expr, "id")                    ]                ):                    pytest_temp = self.variable()                    self.variables_overwrite[v.left.target.id] = pytest_temp                    v.left.target.id = pytest_temp</ins>             self.push_format_context()             res, expl = self.visit(v)             body.append(ast.Assign([ast.Name(res_var, ast.Store())], res))


Question: def visit_Compare(self, comp: ast.Compare) -> Tuple[ast.expr, str]:         self.push_format_context()<ins>        # 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.id = self.variables_overwrite[comp.left.id]</ins>         left_res, left_expl = self.visit(comp.left)         if isinstance(comp.left, (ast.Compare, ast.BoolOp)):             left_expl = f"({left_expl})"


Question: syms = []         results = [left_res]         for i, op, next_operand in it:<ins>            if (                isinstance(next_operand, namedExpr)                and isinstance(left_res, ast.Name)                and next_operand.target.id == left_res.id            ):                next_operand.target.id = self.variable()                self.variables_overwrite[left_res.id] = next_operand.target.id</ins>             next_res, next_expl = self.visit(next_operand)             if isinstance(next_operand, (ast.Compare, ast.BoolOp)):                 next_expl = f"({next_expl})"


Question: res: ast.expr = ast.BoolOp(ast.And(), load_names)         else:             res = load_names[0]<ins></ins>         return res, self.explanation_param(self.pop_format_context(expl_call))


Question: import os import shutil import sys<ins>import types</ins> import uuid import warnings from enum import Enum

Use of causes on Python 3.12 alpha The current Python 3.12 alpha has made a change to , deprecating the parameter and replacing it with an parameter. Something in Pytest's temp path fixtures calls an function which calls with the parameter. When warnings are treated as errors, this makes pytest fail any test using temp paths. Add to cpython 3.12 (alpha) has an API to test results (python/cpython). This would not be an issue, except it was designed to trigger a warning if the test result doesn't have such a method (hence e.g. python/cpython). This means when using pytest as runner for unittest tests and running an error is triggered as pytest's test result (which seems to be ) does not support this protocol. Now obviously this should be non-blocking, as hopefully nobody is running CI which blocks on testing 3.12, but there you go, I figure an early forewarning can't hurt.


Question: from typing import Iterator from typing import Optional from typing import Set<ins>from typing import Tuplefrom typing import Type</ins> from typing import TypeVar from typing import Union


Question: return path.joinpath(".lock")  def on_rm_rf_error(func, path: str, exc, *, start_path: Path) -> bool:def on_rm_rf_error(    func,    path: str,    excinfo: Union[        BaseException,        Tuple[Type[BaseException], BaseException, Optional[types.TracebackType]],    ],    *,    start_path: Path,) -> bool:     """Handle known read-only errors during rmtree.      The returned value is used only by our own tests.     """    exctype, excvalue = exc[:2]    if isinstance(excinfo, BaseException):        exc = excinfo    else:        exc = excinfo[1]      # Another process removed the file in the middle of the "rm_rf" (xdist for example).     # More context: https://github.com/pytest-dev/pytest/issues/5974#issuecomment-543799018    if isinstance(excvalue, FileNotFoundError):    if isinstance(exc, FileNotFoundError):         return False     if not isinstance(excvalue, PermissionError):    if not isinstance(exc, PermissionError):         warnings.warn(            PytestWarning(f"(rm_rf) error removing {path}\n{exctype}: {excvalue}")            PytestWarning(f"(rm_rf) error removing {path}\n{type(exc)}: {exc}")         )         return False


Question: warnings.warn(                 PytestWarning(                     "(rm_rf) unknown function {} when removing {}:\n{}: {}".format(                        func, path, exctype, excvalue                        func, path, type(exc), exc                     )                 )             )


Question: are read-only."""     path = ensure_extended_length_path(path)     onerror = partial(on_rm_rf_error, start_path=path)    shutil.rmtree(str(path), onerror=onerror)    if sys.version_info >= (3, 12):        shutil.rmtree(str(path), onexc=onerror)    else:        shutil.rmtree(str(path), onerror=onerror)   def find_prefixed(root: Path, prefix: str) -> Iterator[Path]:diff --git a/src/_pytest/unittest.py b/src/_pytest/unittest.py-- a/src/_pytest/unittest.py++ b/src/_pytest/unittest.py


Question: def stopTest(self, testcase: "unittest.TestCase") -> None:         pass <ins>    def addDuration(self, testcase: "unittest.TestCase", elapsed: float) -> None:        pass</ins>     def runtest(self) -> None:         from _pytest.debugging import maybe_wrap_pytest_function_for_tracing


Question: # Internal API for local conftest plugin handling.     #     def _set_initial_conftests(        self, namespace: argparse.Namespace, rootpath: Path        self,        namespace: argparse.Namespace,        rootpath: Path,        testpaths_ini: Sequence[str],     ) -> None:         """Load initial conftest files given a preparsed "namespace".

Pytest trying to check if custom argument is a file crashes due to filename being too long I have a custom flag defined in , and when I try to assign it to a value that is too long pytest crashes before ever getting to my code. This reproduces even if the flag isn't defined, and even if the current working directory is . Failing example: If I reduce the length of the flag, I get the expected behavior for my project, and this different and expected error from my pytest MVP: I did a little digging into my version of pytest (7.0.0) to make sure I wasn't doing something wrong, but it looks like there is a blind call to with a path constructed from the argument in : It seems to me like there should be a try or something here, since in cases like mine the argument may not be a file at all, and that can cause OS level errors. Operating System: Ubuntu 20.04 LTS [x] a detailed description of the bug or problem you are having [x] output of from the virtual environment you are using [x] pytest and operating system versions [x] minimal example if possible Pytest trying to check if custom argument is a file crashes due to filename being too long I have a custom flag defined in , and when I try to assign it to a value that is too long pytest crashes before ever getting to my code. This reproduces even if the flag isn't defined, and even if the current working directory is . Failing example: If I reduce the length of the flag, I get the expected behavior for my project, and this different and expected error from my pytest MVP: I did a little digging into my version of pytest (7.0.0) to make sure I wasn't doing something wrong, but it looks like there is a blind call to with a path constructed from the argument in : It seems to me like there should be a try or something here, since in cases like mine the argument may not be a file at all, and that can cause OS level errors. Operating System: Ubuntu 20.04 LTS [x] a detailed description of the bug or problem you are having [x] output of from the virtual environment you are using [x] pytest and operating system versions [x] minimal example if possible


Question: )         self._noconftest = namespace.noconftest         self._using_pyargs = namespace.pyargs        testpaths = namespace.file_or_dir        testpaths = namespace.file_or_dir + testpaths_ini         foundanchor = False         for testpath in testpaths:             path = str(testpath)


Question: if i != -1:                 path = path[:i]             anchor = absolutepath(current / path)            if anchor.exists():  # we found some file object            # Ensure we do not break if what appears to be an anchor            # is in fact a very long option (#10169).            try:                anchor_exists = anchor.exists()            except OSError:  # pragma: no cover                anchor_exists = False            if anchor_exists:                 self._try_load_conftest(anchor, namespace.importmode, rootpath)                 foundanchor = True         if not foundanchor:


Question: @hookimpl(trylast=True)     def pytest_load_initial_conftests(self, early_config: "Config") -> None:         self.pluginmanager._set_initial_conftests(            early_config.known_args_namespace, rootpath=early_config.rootpath            early_config.known_args_namespace,            rootpath=early_config.rootpath,            testpaths_ini=self.getini("testpaths"),         )      def _initini(self, args: Sequence[str]) -> None:


Question: ]                 ):                     pytest_temp = self.variable()                    self.variables_overwrite[v.left.target.id] = pytest_temp                    self.variables_overwrite[                        v.left.target.id                    ] = v.left  # type:ignore[assignment]                     v.left.target.id = pytest_temp             self.push_format_context()             res, expl = self.visit(v)

UnboundLocalError: cannot access local variable 'x' where it is not associated with a value There seems to be a regression in pytest version when a walrus operator is used in an assert line. Code: Fails the test with error: in pytest version , whereas with pytest version it passes successfully. My Python version is . Looks like it has to do with PR .


Question: 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]             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[                    keyword.value.id                ]  # type:ignore[assignment]             res, expl = self.visit(keyword.value)             new_kwargs.append(ast.keyword(keyword.arg, res))             if keyword.arg:


Question: 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.id = self.variables_overwrite[comp.left.id]            comp.left = self.variables_overwrite[                comp.left.id            ]  # type:ignore[assignment]        if isinstance(comp.left, namedExpr):            self.variables_overwrite[                comp.left.target.id            ] = comp.left  # type:ignore[assignment]         left_res, left_expl = self.visit(comp.left)         if isinstance(comp.left, (ast.Compare, ast.BoolOp)):             left_expl = f"({left_expl})"


Question: and next_operand.target.id == left_res.id             ):                 next_operand.target.id = self.variable()                self.variables_overwrite[left_res.id] = next_operand.target.id                self.variables_overwrite[                    left_res.id                ] = next_operand  # type:ignore[assignment]             next_res, next_expl = self.visit(next_operand)             if isinstance(next_operand, (ast.Compare, ast.BoolOp)):                 next_expl = f"({next_expl})"


Question: args = []                         for path in testpaths:                             args.extend(sorted(glob.iglob(path, recursive=True)))<ins>                        if testpaths and not args:                            warning_text = (                                "No files were found in testpaths; "                                "consider removing or adjusting your testpaths configuration. "                                "Searching recursively from the current directory instead."                            )                            self.issue_config_time_warning(                                PytestConfigWarning(warning_text), stacklevel=3                            )</ins>                 if not args:                     source = Config.ArgsSource.INCOVATION_DIR                     args = [str(self.invocation_params.dir)]

Issue warning/error if 'testpaths' does not match any files/folders We should issue a warning (or even an error?) if does not match any files or folders. I think an error is reasonable, even if it might break some incorrectly-configured suite out there. Originally posted by in This is not really a bug, but an intended (albeit questionable) behavior: The values of are actually globs, so globbing for in the root yields nothing. Given it finds nothing, pytest will behave as if called from the command-line without any parameters, which makes it search recursively from the current directory looking for to collect. If you create the directory, then pytest will correctly search in that directory only. I agree those 2 facts are surprising: fact that is a glob. This is but easy to overlook, probably we should add a glob to the example there. silently not finding anything, and then proceeding as usual. I don't think we can do anything more for 1, but for 2 seems like we should at least emit a warning if is defined but does not match anything.


Question: import re from contextlib import contextmanager from contextlib import nullcontextfrom datetime import datetimefrom datetime import timedeltafrom datetime import timezone from io import StringIOfrom logging import LogRecord from pathlib import Path from typing import AbstractSet from typing import Dict

support sub-second granularity/precision in (strftime ) tl;dr pytest processing strftime specifier from arguments would allow me to accurately merge log messages from disparate sub-systems Tests I run have pytest log messages that print at the second granularity for the datetimestamp, e.g. . At the same time, other log file messages not generated by pytest print sub-second datetimestamps, e.g. . When reviewing the various logs, there are many message from other system components that are printing many log messages per second. Because pytest log messages are lacking sub-second precision, I am unable to align pytest log messages within other system log messages. For example, the system-under-test generates a log file like: and the driver of tests, pytest, generates a log file like: The pytest log messages cannot be precisely ordered among the other log messages that occurred during the datetime second , there were many things that occurred in the other system components within that second. Given the following pytest code To add sub-second granularity, it seems sensible to add within the but then I see the confusing output of pytest logging is ignoring the strftime specifier! I want pytest log messages to print sub-second granularity, e.g. process strftime within settings. <!-- A clear and concise description of what you want to happen. --Support strftime specifier in the various settings for date-format, e.g. , , . <!-- Provide examples of real-world use cases that this would enable and how it solves the problem described above. --In my complex testing system, this means all log messages would be printed to millisecond precision. This allows engineers investigating issues to more accurately merge disparate testing system logs by their natural ordering mechanism of a datetimestamp. <!-- Have you tried to workaround the problem using a pytest plugin or other tools? Or a different approach to solving this issue? Please elaborate here. --I can set the format to include . However, it's a little confusing to have to manipulate log datetimestamps by two different mechanisms, and . On the command-line run: This prints datetimestamps with millisecond precision <br /It is more intuitive for pytest to process the Python strftime specifier within all options.


Question: return _ANSI_ESCAPE_SEQ.sub("", text)  class ColoredLevelFormatter(logging.Formatter):class DatetimeFormatter(logging.Formatter):    """A logging formatter which formats record with    :func:`datetime.datetime.strftime` formatter instead of    :func:`time.strftime` in case of microseconds in format string.    """    def formatTime(self, record: LogRecord, datefmt=None) -> str:        if datefmt and "%f" in datefmt:            ct = self.converter(record.created)            tz = timezone(timedelta(seconds=ct.tm_gmtoff), ct.tm_zone)            # Construct `datetime.datetime` object from `struct_time`            # and msecs information from `record`            dt = datetime(*ct[0:6], microsecond=round(record.msecs * 1000), tzinfo=tz)            return dt.strftime(datefmt)        # Use `logging.Formatter` for non-microsecond formats        return super().formatTime(record, datefmt)class ColoredLevelFormatter(DatetimeFormatter):     """A logging formatter which colorizes the %(levelname)..s part of the     log format passed to __init__."""


Question: config, "log_file_date_format", "log_date_format"         )         log_file_formatter = logging.Formatter(        log_file_formatter = DatetimeFormatter(             log_file_format, datefmt=log_file_date_format         )         self.log_file_handler.setFormatter(log_file_formatter)


Question: create_terminal_writer(self._config), log_format, log_date_format             )         else:            formatter = logging.Formatter(log_format, log_date_format)            formatter = DatetimeFormatter(log_format, log_date_format)          formatter._style = PercentStyleMultiline(             formatter._style._fmt, auto_indent=auto_indent


Question: #     def _set_initial_conftests(         self,        namespace: argparse.Namespace,        args: Sequence[Union[str, Path]],        pyargs: bool,        noconftest: bool,         rootpath: Path,        testpaths_ini: Sequence[str],        confcutdir: Optional[Path],        importmode: Union[ImportMode, str],     ) -> None:         """Load initial conftest files given a preparsed "namespace".

Pytest 7.3.2 changes in behaviour regarding and In , we have two test suites - the unit tests at and the integration test suite at . Both and are listed in testpaths- We then run either or using /. Each / dir contains a file, which adds some options using . One option that is common to both test suites is . Before 7.3.2, this setup seemed to work, we could run both unit tests and integration tests without issue. But on 7.3.2 (perhaps since ?) we get the following error: Is this an issue in our configuration, or a bug? Should we no longer use testpaths to list all the test suites? <details<summarypip list output</summary</detailsXref


Question: common options will not confuse our logic here.         """         current = Path.cwd()        self._confcutdir = (            absolutepath(current / namespace.confcutdir)            if namespace.confcutdir            else None        )        self._noconftest = namespace.noconftest        self._using_pyargs = namespace.pyargs        testpaths = namespace.file_or_dir + testpaths_ini        self._confcutdir = absolutepath(current / confcutdir) if confcutdir else None        self._noconftest = noconftest        self._using_pyargs = pyargs         foundanchor = False        for testpath in testpaths:            path = str(testpath)        for intitial_path in args:            path = str(intitial_path)             # remove node-id syntax             i = path.find("::")             if i != -1:


Question: except OSError:  # pragma: no cover                 anchor_exists = False             if anchor_exists:                self._try_load_conftest(anchor, namespace.importmode, rootpath)                self._try_load_conftest(anchor, importmode, rootpath)                 foundanchor = True         if not foundanchor:            self._try_load_conftest(current, namespace.importmode, rootpath)            self._try_load_conftest(current, importmode, rootpath)      def _is_in_confcutdir(self, path: Path) -> bool:         """Whether a path is within the confcutdir.


Question: @hookimpl(trylast=True)     def pytest_load_initial_conftests(self, early_config: "Config") -> None:        # We haven't fully parsed the command line arguments yet, so        # early_config.args it not set yet. But we need it for        # discovering the initial conftests. So "pre-run" the logic here.        # It will be done for real in `parse()`.        args, args_source = early_config._decide_args(            args=early_config.known_args_namespace.file_or_dir,            pyargs=early_config.known_args_namespace.pyargs,            testpaths=early_config.getini("testpaths"),            invocation_dir=early_config.invocation_params.dir,            rootpath=early_config.rootpath,            warn=False,        )         self.pluginmanager._set_initial_conftests(            early_config.known_args_namespace,            args=args,            pyargs=early_config.known_args_namespace.pyargs,            noconftest=early_config.known_args_namespace.noconftest,             rootpath=early_config.rootpath,            testpaths_ini=self.getini("testpaths"),            confcutdir=early_config.known_args_namespace.confcutdir,            importmode=early_config.known_args_namespace.importmode,         )      def _initini(self, args: Sequence[str]) -> None:


Question: return args <ins>    def _decide_args(        self,        *,        args: List[str],        pyargs: List[str],        testpaths: List[str],        invocation_dir: Path,        rootpath: Path,        warn: bool,    ) -> Tuple[List[str], ArgsSource]:        """Decide the args (initial paths/nodeids) to use given the relevant inputs.        :param warn: Whether can issue warnings.        """        if args:            source = Config.ArgsSource.ARGS            result = args        else:            if invocation_dir == rootpath:                source = Config.ArgsSource.TESTPATHS                if pyargs:                    result = testpaths                else:                    result = []                    for path in testpaths:                        result.extend(sorted(glob.iglob(path, recursive=True)))                    if testpaths and not result:                        if warn:                            warning_text = (                                "No files were found in testpaths; "                                "consider removing or adjusting your testpaths configuration. "                                "Searching recursively from the current directory instead."                            )                            self.issue_config_time_warning(                                PytestConfigWarning(warning_text), stacklevel=3                            )            else:                result = []            if not result:                source = Config.ArgsSource.INCOVATION_DIR                result = [str(invocation_dir)]        return result, source</ins>     def _preparse(self, args: List[str], addopts: bool = True) -> None:         if addopts:             env_addopts = os.environ.get("PYTEST_ADDOPTS", "")


Question: self.hook.pytest_cmdline_preparse(config=self, args=args)         self._parser.after_preparse = True  # type: ignore         try:            source = Config.ArgsSource.ARGS             args = self._parser.parse_setoption(                 args, self.option, namespace=self.option             )            if not args:                if self.invocation_params.dir == self.rootpath:                    source = Config.ArgsSource.TESTPATHS                    testpaths: List[str] = self.getini("testpaths")                    if self.known_args_namespace.pyargs:                        args = testpaths                    else:                        args = []                        for path in testpaths:                            args.extend(sorted(glob.iglob(path, recursive=True)))                        if testpaths and not args:                            warning_text = (                                "No files were found in testpaths; "                                "consider removing or adjusting your testpaths configuration. "                                "Searching recursively from the current directory instead."                            )                            self.issue_config_time_warning(                                PytestConfigWarning(warning_text), stacklevel=3                            )                if not args:                    source = Config.ArgsSource.INCOVATION_DIR                    args = [str(self.invocation_params.dir)]            self.args = args            self.args_source = source            self.args, self.args_source = self._decide_args(                args=args,                pyargs=self.known_args_namespace.pyargs,                testpaths=self.getini("testpaths"),                invocation_dir=self.invocation_params.dir,                rootpath=self.rootpath,                warn=True,            )         except PrintHelp:             pass


Question: expect_docstring                 and isinstance(item, ast.Expr)                 and isinstance(item.value, ast.Constant)<ins>                and isinstance(item.value.value, str)</ins>             ):                 doc = item.value.value                 if self.is_rewrite_disabled(doc):

Rewrite fails when first expression of file is a number and mistaken as docstring <!-- Thanks for submitting an issue! Quick check-list while reporting bugs: --- [x] a detailed description of the bug or problem you are having [x] output of from the virtual environment you are using [x] pytest and operating system versions [x] minimal example if possible use report an error it seems a error in collecting testcase


Question: if mode is ImportMode.importlib:         module_name = module_name_from_path(path, root)<ins>        with contextlib.suppress(KeyError):            return sys.modules[module_name]</ins>          for meta_importer in sys.meta_path:             spec = meta_importer.find_spec(module_name, [str(path.parent)])

Module imported twice under import-mode=importlib In pmxbot/pmxbot I'm attempting to switch pmxbot off of pkgresources style namespace packaging to PEP 420 namespace packages. To do so, I've needed to switch to for the and re-organize the tests to avoid import errors on the tests. Yet even after working around these issues, the tests are failing when the effect of doesn't seem to have had any effect. Investigating deeper, I see that initializer is executed and performs its actions (setting a class variable ), but when that happens, there are two different versions of present, one in and another found in : I haven't yet made a minimal reproducer, but I wanted to first capture this condition.


Question: return len(self._list)      def pop(self, cls: Type[Warning] = Warning) -> "warnings.WarningMessage":        """Pop the first recorded warning, raise exception if not exists."""        """Pop the first recorded warning which is an instance of ``cls``,        but not an instance of a child class of any other match.        Raises ``AssertionError`` if there is no match.        """        best_idx: Optional[int] = None         for i, w in enumerate(self._list):            if issubclass(w.category, cls):                return self._list.pop(i)            if w.category == cls:                return self._list.pop(i)  # exact match, stop looking            if issubclass(w.category, cls) and (                best_idx is None                or not issubclass(w.category, self._list[best_idx].category)            ):                best_idx = i        if best_idx is not None:            return self._list.pop(best_idx)         __tracebackhide__ = True         raise AssertionError(f"{cls!r} not found in warning list")

() improperly matches warning When trying to pop a specific warning from a WarningsRecorder instance, the wrong warning is returned. I believe the issue is that pop uses issubclass I believe the correct comparison should be: Here is a minimum working example that triggers the buggy behavior: The test output is pytest 7.2.1 on archlinux. virtual environment is a clean conda environment with only python and pytest (and their dependencies installed from conda-forge). If this is indeed a bug, I'm happy to open a PR with my proposed solution. () improperly matches warning When trying to pop a specific warning from a WarningsRecorder instance, the wrong warning is returned. I believe the issue is that pop uses issubclass I believe the correct comparison should be: Here is a minimum working example that triggers the buggy behavior: The test output is pytest 7.2.1 on archlinux. virtual environment is a clean conda environment with only python and pytest (and their dependencies installed from conda-forge). If this is indeed a bug, I'm happy to open a PR with my proposed solution.


Question: approx_side_as_map.items(), other_side.values()         ):             if approx_value != other_value:                max_abs_diff = max(                    max_abs_diff, abs(approx_value.expected - other_value)                )                if approx_value.expected == 0.0:                    max_rel_diff = math.inf                else:                    max_rel_diff = max(                        max_rel_diff,                        abs(                            (approx_value.expected - other_value)                            / approx_value.expected                        ),                if approx_value.expected is not None and other_value is not None:                    max_abs_diff = max(                        max_abs_diff, abs(approx_value.expected - other_value)                     )                    if approx_value.expected == 0.0:                        max_rel_diff = math.inf                    else:                        max_rel_diff = max(                            max_rel_diff,                            abs(                                (approx_value.expected - other_value)                                / approx_value.expected                            ),                        )                 different_ids.append(approx_key)          message_data = [

fails with When using to test float and one of the objects in the statement contain I see the following TypeError: TypeError: unsupported operand type(s) for -: 'float' and 'NoneType'. macOS 12.6.3 python 3.10.4 pytest 7.2.1


Question: try:         result = call_fixture_func(fixturefunc, request, kwargs)     except TEST_OUTCOME as e:        if isinstance(e, skip.Exception) and not fixturefunc.__name__.startswith(            "xunit_setup"        ):        if isinstance(e, skip.Exception):            # The test requested a fixture which caused a skip.            # Don't show the fixture as the skip location, as then the user            # wouldn't know which test skipped.             e._use_item_location = True         fixturedef.cached_result = (None, my_cache_key, e)         raise

fixtures: show test as skip location if skipped from an xunit setup function PR made it so that if a fixture calls , the skip location is shown as the test function, not the fixture. But it excluded xunit setup fixtures from this. I suspect this was done to make a pre-existing test pass, however I think that the same reason for fixtures applies to xunit fixtures just as well, so we shouldn't exclude it. Would also remove a string-hack that was used to implement this exclusion...


Question: """         visit `ast.Call` nodes on Python3.5 and after         """<ins>        if isinstance(call.func, ast.Name) and call.func.id == "all":            return self._visit_all(call)</ins>         new_func, func_expl = self.visit(call.func)         arg_expls = []         new_args = []

Unroll the iterable for all/any calls to get better reports Sometime I need to assert some predicate on all of an iterable, and for that the builtin functions / are great - but the failure messages aren't useful at all! For example - the same test written in three ways: A generator expression A list comprehension A for loop The only one that gives a meaningful report is the for loop - but it's way more wordy, and asserts don't translate to a for loop nicely (I'll have to write a or a helper function - yuck) I propose the assertion re-writer "unrolls" the iterator to the third form, and then uses the already existing reports. [x] Include a detailed description of the bug or suggestion [x] of the virtual environment you are using [x] pytest and operating system versions [x] Minimal example if possible


Question: outer_expl = "%s\n{%s = %s\n}" % (res_expl, res_expl, expl)         return res, outer_expl <ins>    def _visit_all(self, call):        """Special rewrite for the builtin all function, see #5062"""        if not isinstance(call.args[0], (ast.GeneratorExp, ast.ListComp)):            return        gen_exp = call.args[0]        assertion_module = ast.Module(            body=[ast.Assert(test=gen_exp.elt, lineno=1, msg="", col_offset=1)]        )        AssertionRewriter(module_path=None, config=None).run(assertion_module)        for_loop = ast.For(            iter=gen_exp.generators[0].iter,            target=gen_exp.generators[0].target,            body=assertion_module.body,            orelse=[],        )        self.statements.append(for_loop)        return (            ast.Num(n=1),            "",        )  # Return an empty expression, all the asserts are in the for_loop</ins>     def visit_Starred(self, starred):         # From Python 3.5, a Starred node can appear in a function call         res, expl = self.visit(starred.value)


Question: """         visit `ast.Call nodes on 3.4 and below`         """<ins>        if isinstance(call.func, ast.Name) and call.func.id == "all":            return self._visit_all(call)</ins>         new_func, func_expl = self.visit(call.func)         arg_expls = []         new_args = []


Question: return attr_func  <ins>def _check_record_param_type(param, v):    """Used by record_testsuite_property to check that the given parameter name is of the proper    type"""    __tracebackhide__ = True    if not isinstance(v, six.string_types):        msg = "{param} parameter needs to be a string, but {g} given"        raise TypeError(msg.format(param=param, g=type(v).__name__))@pytest.fixture(scope="session")def record_testsuite_property(request):    """    Records a new ``<property>`` tag as child of the root ``<testsuite>``. This is suitable to    writing global information regarding the entire test suite, and is compatible with ``xunit2`` JUnit family.    This is a ``session``-scoped fixture which is called with ``(name, value)``. Example:    .. code-block:: python        def test_foo(record_testsuite_property):            record_testsuite_property("ARCH", "PPC")            record_testsuite_property("STORAGE_TYPE", "CEPH")    ``name`` must be a string, ``value`` will be converted to a string and properly xml-escaped.    """    __tracebackhide__ = True    def record_func(name, value):        """noop function in case --junitxml was not passed in the command-line"""        __tracebackhide__ = True        _check_record_param_type("name", name)    xml = getattr(request.config, "_xml", None)    if xml is not None:        record_func = xml.add_global_property  # noqa    return record_func</ins> def pytest_addoption(parser):     group = parser.getgroup("terminal reporting")     group.addoption(

Invalid XML schema for <propertiestags in JUnit reports The problem: JUnit breaks when it reads an XML generated by pytest if plugins make use of . This behavior happens with newer versions of hypothesis (). In fact, as already mentioned in , is adding inside which seems to be wrong (it should be inside ). See: . It happens with all junit families. Reproducing: I was trying to create a PR to fix this, but when I saw code I realized that what is needed to do could have more implications that I though. I think that nobody uses this feature with JUnit (as it breaks) and removing that is something to think about.


Question: self.node_reporters = {}  # nodeid -> _NodeReporter         self.node_reporters_ordered = []         self.global_properties = []<ins></ins>         # List of reports that failed on call but teardown is pending.         self.open_reports = []         self.cnt_double_fail_tests = 0


Question: terminalreporter.write_sep("-", "generated xml file: %s" % (self.logfile))      def add_global_property(self, name, value):        self.global_properties.append((str(name), bin_xml_escape(value)))        __tracebackhide__ = True        _check_record_param_type("name", name)        self.global_properties.append((name, bin_xml_escape(value)))      def _get_global_properties_node(self):         """Return a Junit node containing custom properties, if any.