From 50c0b937701632567cb35ed58cf9093a53cf3ecc Mon Sep 17 00:00:00 2001
From: Patrick Lannigan
Date: Fri, 10 Nov 2023 09:56:20 -0500
Subject: [PATCH] Additional design changes from review
* Move OutputVerbosity functionality directly in Config
* Use strings instead of an enum for verbosity types
---
changelog/11387.feature.rst | 2 +-
doc/en/reference/reference.rst | 9 --
src/_pytest/assertion/__init__.py | 6 +-
src/_pytest/assertion/truncate.py | 4 +-
src/_pytest/assertion/util.py | 3 +-
src/_pytest/config/__init__.py | 142 +++++++++++++-----------------
src/pytest/__init__.py | 4 -
testing/test_assertion.py | 30 +++----
testing/test_config.py | 24 +++--
9 files changed, 90 insertions(+), 134 deletions(-)
diff --git a/changelog/11387.feature.rst b/changelog/11387.feature.rst
index 9cb4dd40e..bfcf25575 100644
--- a/changelog/11387.feature.rst
+++ b/changelog/11387.feature.rst
@@ -2,4 +2,4 @@ Added the new :confval:`verbosity_assertions` configuration option for fine-grai
See :ref:`Fine-grained verbosity ` for more details.
-For plugin authors, :attr:`config.output_verbosity ` can be used to retrieve the verbosity level for a specific :class:`pytest.VerbosityType`.
+For plugin authors, :attr:`config.output_verbosity ` can be used to retrieve the verbosity level for a specific verbosity type.
diff --git a/doc/en/reference/reference.rst b/doc/en/reference/reference.rst
index 81940d865..180a70eed 100644
--- a/doc/en/reference/reference.rst
+++ b/doc/en/reference/reference.rst
@@ -961,15 +961,6 @@ OptionGroup
.. autoclass:: pytest.OptionGroup()
:members:
-OutputVerbosity
-~~~~~~~~~~~~~~~
-
-.. autoclass:: pytest.OutputVerbosity()
- :members:
-
-.. autoclass:: pytest.VerbosityType()
- :members:
-
PytestPluginManager
~~~~~~~~~~~~~~~~~~~
diff --git a/src/_pytest/assertion/__init__.py b/src/_pytest/assertion/__init__.py
index 8b442463c..6f729179a 100644
--- a/src/_pytest/assertion/__init__.py
+++ b/src/_pytest/assertion/__init__.py
@@ -12,8 +12,6 @@ from _pytest.assertion import util
from _pytest.assertion.rewrite import assertstate_key
from _pytest.config import Config
from _pytest.config import hookimpl
-from _pytest.config import OutputVerbosity
-from _pytest.config import VerbosityType
from _pytest.config.argparsing import Parser
from _pytest.nodes import Item
@@ -44,9 +42,9 @@ def pytest_addoption(parser: Parser) -> None:
help="Enables the pytest_assertion_pass hook. "
"Make sure to delete any previously generated pyc cache files.",
)
- OutputVerbosity._add_ini(
+ Config._add_ini(
parser,
- VerbosityType.Assertions,
+ Config.VERBOSITY_ASSERTIONS,
help=(
"Specify a verbosity level for assertions, overriding the main level. "
"Higher levels will provide more detailed explanation when an assertion fails."
diff --git a/src/_pytest/assertion/truncate.py b/src/_pytest/assertion/truncate.py
index d472e239a..b96ce9956 100644
--- a/src/_pytest/assertion/truncate.py
+++ b/src/_pytest/assertion/truncate.py
@@ -7,7 +7,7 @@ from typing import List
from typing import Optional
from _pytest.assertion import util
-from _pytest.config import VerbosityType
+from _pytest.config import Config
from _pytest.nodes import Item
DEFAULT_MAX_LINES = 8
@@ -26,7 +26,7 @@ def truncate_if_required(
def _should_truncate_item(item: Item) -> bool:
"""Whether or not this test item is eligible for truncation."""
- verbose = item.config.output_verbosity.get(VerbosityType.Assertions)
+ verbose = item.config.get_verbosity(Config.VERBOSITY_ASSERTIONS)
return verbose < 2 and not util.running_on_ci()
diff --git a/src/_pytest/assertion/util.py b/src/_pytest/assertion/util.py
index 147875eec..65abe8d23 100644
--- a/src/_pytest/assertion/util.py
+++ b/src/_pytest/assertion/util.py
@@ -20,7 +20,6 @@ from _pytest._io.saferepr import _pformat_dispatch
from _pytest._io.saferepr import saferepr
from _pytest._io.saferepr import saferepr_unlimited
from _pytest.config import Config
-from _pytest.config import VerbosityType
# The _reprcompare attribute on the util module is used by the new assertion
# interpretation code and assertion rewriter to detect this plugin was
@@ -169,7 +168,7 @@ def assertrepr_compare(
config, op: str, left: Any, right: Any, use_ascii: bool = False
) -> Optional[List[str]]:
"""Return specialised explanations for some operators/operands."""
- verbose = config.output_verbosity.get(VerbosityType.Assertions)
+ verbose = config.get_verbosity(Config.VERBOSITY_ASSERTIONS)
# Strings which normalize equal are often hard to distinguish when printed; use ascii() to make this easier.
# See issue #3246.
diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py
index f2c96652a..52bffe393 100644
--- a/src/_pytest/config/__init__.py
+++ b/src/_pytest/config/__init__.py
@@ -13,7 +13,6 @@ import shlex
import sys
import types
import warnings
-from enum import Enum
from functools import lru_cache
from pathlib import Path
from textwrap import dedent
@@ -23,6 +22,7 @@ from typing import Any
from typing import Callable
from typing import cast
from typing import Dict
+from typing import Final
from typing import final
from typing import Generator
from typing import IO
@@ -1022,12 +1022,6 @@ class Config:
self.args_source = Config.ArgsSource.ARGS
self.args: List[str] = []
- self.output_verbosity = OutputVerbosity(self)
- """Access to output verbosity configuration.
-
- :type: OutputVerbosity
- """
-
if TYPE_CHECKING:
from _pytest.cacheprovider import Cache
@@ -1640,6 +1634,66 @@ class Config:
"""Deprecated, use getoption(skip=True) instead."""
return self.getoption(name, skip=True)
+ #: Verbosity for failed assertions (see :confval:`verbosity_assertions`).
+ VERBOSITY_ASSERTIONS: Final = "assertions"
+ _KNOWN_VERBOSITY_TYPES: Final = {VERBOSITY_ASSERTIONS}
+ _VERBOSITY_INI_DEFAULT = "auto"
+
+ def get_verbosity(self, verbosity_type: Optional[str] = None) -> int:
+ r"""Access to fine-grained verbosity levels.
+
+ .. code-block:: ini
+
+ # content of pytest.ini
+ [pytest]
+ verbosity_assertions = 2
+
+ .. code-block:: console
+
+ pytest -v
+
+ .. code-block:: python
+
+ print(config.get_verbosity()) # 1
+ print(config.get_verbosity(Config.VERBOSITY_ASSERTIONS)) # 2
+ """
+ global_level = self.option.verbose
+ assert isinstance(global_level, int)
+ if (
+ verbosity_type is None
+ or verbosity_type not in Config._KNOWN_VERBOSITY_TYPES
+ ):
+ return global_level
+
+ level = self.getini(Config._ini_name(verbosity_type))
+
+ if level == Config._VERBOSITY_INI_DEFAULT:
+ return global_level
+
+ return int(level)
+
+ @staticmethod
+ def _ini_name(verbosity_type: str) -> str:
+ return f"verbosity_{verbosity_type}"
+
+ @staticmethod
+ def _add_ini(parser: "Parser", verbosity_type: str, help: str) -> None:
+ """Add a output verbosity configuration option for the given output type.
+
+ :param parser: Parser for command line arguments and ini-file values.
+ :param verbosity_type: Fine-grained verbosity category.
+ :param help: Description of the output this type controls.
+
+ The value should be retrieved via a call to
+ :py:func:`config.get_verbosity(type) `.
+ """
+ parser.addini(
+ Config._ini_name(verbosity_type),
+ help=help,
+ type="string",
+ default=Config._VERBOSITY_INI_DEFAULT,
+ )
+
def _warn_about_missing_assertion(self, mode: str) -> None:
if not _assertion_supported():
if mode == "plain":
@@ -1669,80 +1723,6 @@ class Config:
)
-class VerbosityType(Enum):
- """Fine-grained verbosity categories."""
-
- #: Application wide, controlled by ``-v``/``-q``.
- Global = "global"
-
- #: Verbosity for failed assertions (see :confval:`verbosity_assertions`).
- Assertions = "assertions"
-
-
-class OutputVerbosity:
- r"""Access to fine-grained verbosity levels.
-
- Access via :attr:`config.output_verbosity `.
-
- .. code-block:: ini
-
- # content of pytest.ini
- [pytest]
- verbosity_assertions = 2
-
- .. code-block:: console
-
- pytest -v
-
- .. code-block:: python
-
- print(config.output_verbosity.get()) # 1
- print(config.output_verbosity.get(VerbosityType.Assertions)) # 2
- """
-
- DEFAULT = "auto"
-
- def __init__(self, config: Config) -> None:
- self._config = config
-
- def get(self, verbosity_type: VerbosityType = VerbosityType.Global) -> int:
- """Return verbosity level for the given output type.
-
- :param verbosity_type: Fine-grained verbosity category.
-
- If the level is not configured, the value of ``config.option.verbose``.
- """
- level = self._config.getini(OutputVerbosity._ini_name(verbosity_type))
-
- if level == OutputVerbosity.DEFAULT:
- assert isinstance(self._config.option.verbose, int)
- return self._config.option.verbose
-
- return int(level)
-
- @staticmethod
- def _ini_name(verbosity_type: VerbosityType) -> str:
- return f"verbosity_{verbosity_type.value}"
-
- @staticmethod
- def _add_ini(parser: "Parser", verbosity_type: VerbosityType, help: str) -> None:
- """Add a output verbosity configuration option for the given output type.
-
- :param parser: Parser for command line arguments and ini-file values.
- :param verbosity_type: Fine-grained verbosity category.
- :param help: Description of the output this type controls.
-
- The value should be retrieved via a call to
- :py:func:`config.output_verbosity.get(type) `.
- """
- parser.addini(
- OutputVerbosity._ini_name(verbosity_type),
- help=help,
- type="string",
- default=OutputVerbosity.DEFAULT,
- )
-
-
def _assertion_supported() -> bool:
try:
assert False
diff --git a/src/pytest/__init__.py b/src/pytest/__init__.py
index f0e0cff8c..0aa496a2f 100644
--- a/src/pytest/__init__.py
+++ b/src/pytest/__init__.py
@@ -15,10 +15,8 @@ from _pytest.config import ExitCode
from _pytest.config import hookimpl
from _pytest.config import hookspec
from _pytest.config import main
-from _pytest.config import OutputVerbosity
from _pytest.config import PytestPluginManager
from _pytest.config import UsageError
-from _pytest.config import VerbosityType
from _pytest.config.argparsing import OptionGroup
from _pytest.config.argparsing import Parser
from _pytest.debugging import pytestPDB as __pytestPDB
@@ -128,7 +126,6 @@ __all__ = [
"Module",
"MonkeyPatch",
"OptionGroup",
- "OutputVerbosity",
"Package",
"param",
"Parser",
@@ -164,7 +161,6 @@ __all__ = [
"TestReport",
"TestShortLogReport",
"UsageError",
- "VerbosityType",
"WarningsRecorder",
"warns",
"xfail",
diff --git a/testing/test_assertion.py b/testing/test_assertion.py
index 4df33bd32..43f4d39cc 100644
--- a/testing/test_assertion.py
+++ b/testing/test_assertion.py
@@ -13,7 +13,7 @@ import pytest
from _pytest import outcomes
from _pytest.assertion import truncate
from _pytest.assertion import util
-from _pytest.config import VerbosityType
+from _pytest.config import Config as _Config
from _pytest.monkeypatch import MonkeyPatch
from _pytest.pytester import Pytester
@@ -23,26 +23,20 @@ def mock_config(verbose: int = 0, assertion_override: Optional[int] = None):
def _highlight(self, source, lexer):
return source
- class OutputVerbosity:
- @property
- def verbose(self) -> int:
- return verbose
+ class Config:
+ def get_terminal_writer(self):
+ return TerminalWriter()
- def get(self, verbosity_type: VerbosityType = VerbosityType.Global) -> int:
- if verbosity_type == VerbosityType.Assertions:
+ def get_verbosity(self, verbosity_type: Optional[str] = None) -> int:
+ if verbosity_type is None:
+ return verbose
+ if verbosity_type == _Config.VERBOSITY_ASSERTIONS:
if assertion_override is not None:
return assertion_override
return verbose
raise KeyError(f"Not mocked out: {verbosity_type}")
- class Config:
- def __init__(self) -> None:
- self.output_verbosity = OutputVerbosity()
-
- def get_terminal_writer(self):
- return TerminalWriter()
-
return Config()
@@ -53,13 +47,13 @@ class TestMockConfig:
def test_verbose_exposes_value(self):
config = mock_config(verbose=TestMockConfig.SOME_VERBOSITY_LEVEL)
- assert config.output_verbosity.verbose == TestMockConfig.SOME_VERBOSITY_LEVEL
+ assert config.get_verbosity() == TestMockConfig.SOME_VERBOSITY_LEVEL
def test_get_assertion_override_not_set_verbose_value(self):
config = mock_config(verbose=TestMockConfig.SOME_VERBOSITY_LEVEL)
assert (
- config.output_verbosity.get(VerbosityType.Assertions)
+ config.get_verbosity(_Config.VERBOSITY_ASSERTIONS)
== TestMockConfig.SOME_VERBOSITY_LEVEL
)
@@ -70,7 +64,7 @@ class TestMockConfig:
)
assert (
- config.output_verbosity.get(VerbosityType.Assertions)
+ config.get_verbosity(_Config.VERBOSITY_ASSERTIONS)
== TestMockConfig.SOME_OTHER_VERBOSITY_LEVEL
)
@@ -78,7 +72,7 @@ class TestMockConfig:
config = mock_config(verbose=TestMockConfig.SOME_VERBOSITY_LEVEL)
with pytest.raises(KeyError):
- config.output_verbosity.get(VerbosityType.Global)
+ config.get_verbosity("--- NOT A VERBOSITY LEVEL ---")
class TestImportHookInstallation:
diff --git a/testing/test_config.py b/testing/test_config.py
index e4bec2c51..cf3b56753 100644
--- a/testing/test_config.py
+++ b/testing/test_config.py
@@ -20,9 +20,7 @@ from _pytest.config import _strtobool
from _pytest.config import Config
from _pytest.config import ConftestImportFailure
from _pytest.config import ExitCode
-from _pytest.config import OutputVerbosity
from _pytest.config import parse_warning_filter
-from _pytest.config import VerbosityType
from _pytest.config.argparsing import Parser
from _pytest.config.exceptions import UsageError
from _pytest.config.findpaths import determine_setup
@@ -2186,14 +2184,14 @@ class TestDebugOptions:
)
-class TestOutputVerbosity:
- SOME_OUTPUT_TYPE = VerbosityType.Assertions
+class TestVerbosity:
+ SOME_OUTPUT_TYPE = Config.VERBOSITY_ASSERTIONS
SOME_OUTPUT_VERBOSITY_LEVEL = 5
class VerbosityIni:
def pytest_addoption(self, parser: Parser) -> None:
- OutputVerbosity._add_ini(
- parser, TestOutputVerbosity.SOME_OUTPUT_TYPE, help="some help text"
+ Config._add_ini(
+ parser, TestVerbosity.SOME_OUTPUT_TYPE, help="some help text"
)
def test_level_matches_verbose_when_not_specified(
@@ -2208,34 +2206,34 @@ class TestOutputVerbosity:
),
encoding="utf-8",
)
- pytester.plugins = [TestOutputVerbosity.VerbosityIni()]
+ pytester.plugins = [TestVerbosity.VerbosityIni()]
config = pytester.parseconfig(tmp_path)
assert (
- config.output_verbosity.get(TestOutputVerbosity.SOME_OUTPUT_TYPE)
+ config.get_verbosity(TestVerbosity.SOME_OUTPUT_TYPE)
== config.option.verbose
)
def test_level_matches_specified_override(
self, pytester: Pytester, tmp_path: Path
) -> None:
- setting_name = f"verbosity_{TestOutputVerbosity.SOME_OUTPUT_TYPE.value}"
+ setting_name = f"verbosity_{TestVerbosity.SOME_OUTPUT_TYPE}"
tmp_path.joinpath("pytest.ini").write_text(
textwrap.dedent(
f"""\
[pytest]
addopts = --verbose
- {setting_name} = {TestOutputVerbosity.SOME_OUTPUT_VERBOSITY_LEVEL}
+ {setting_name} = {TestVerbosity.SOME_OUTPUT_VERBOSITY_LEVEL}
"""
),
encoding="utf-8",
)
- pytester.plugins = [TestOutputVerbosity.VerbosityIni()]
+ pytester.plugins = [TestVerbosity.VerbosityIni()]
config = pytester.parseconfig(tmp_path)
assert (
- config.output_verbosity.get(TestOutputVerbosity.SOME_OUTPUT_TYPE)
- == TestOutputVerbosity.SOME_OUTPUT_VERBOSITY_LEVEL
+ config.get_verbosity(TestVerbosity.SOME_OUTPUT_TYPE)
+ == TestVerbosity.SOME_OUTPUT_VERBOSITY_LEVEL
)