Additional design changes from review

* Move OutputVerbosity functionality directly in Config
* Use strings instead of an enum for verbosity types
This commit is contained in:
Patrick Lannigan 2023-11-10 09:56:20 -05:00
parent b8714de594
commit 50c0b93770
No known key found for this signature in database
GPG Key ID: BBF5D9DED1E4AAF9
9 changed files with 90 additions and 134 deletions

View File

@ -2,4 +2,4 @@ Added the new :confval:`verbosity_assertions` configuration option for fine-grai
See :ref:`Fine-grained verbosity <pytest.fine_grained_verbosity>` for more details.
For plugin authors, :attr:`config.output_verbosity <pytest.Config.output_verbosity>` can be used to retrieve the verbosity level for a specific :class:`pytest.VerbosityType`.
For plugin authors, :attr:`config.output_verbosity <pytest.Config.get_verbosity>` can be used to retrieve the verbosity level for a specific verbosity type.

View File

@ -961,15 +961,6 @@ OptionGroup
.. autoclass:: pytest.OptionGroup()
:members:
OutputVerbosity
~~~~~~~~~~~~~~~
.. autoclass:: pytest.OutputVerbosity()
:members:
.. autoclass:: pytest.VerbosityType()
:members:
PytestPluginManager
~~~~~~~~~~~~~~~~~~~

View File

@ -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."

View File

@ -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()

View File

@ -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.

View File

@ -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) <pytest.Config.get_verbosity>`.
"""
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 <pytest.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) <pytest.OutputVerbosity.get>`.
"""
parser.addini(
OutputVerbosity._ini_name(verbosity_type),
help=help,
type="string",
default=OutputVerbosity.DEFAULT,
)
def _assertion_supported() -> bool:
try:
assert False

View File

@ -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",

View File

@ -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:

View File

@ -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
)