From f308043be81720854f6ce8dd2a823a3df16368f6 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Tue, 12 Nov 2024 12:42:07 +0100 Subject: [PATCH] [pylint consider-using-any-or-all] Fix or disable all detected issues (#12954) * Avoid constructing a list of names * noqa a a suggestion in already nested for --- AUTHORS | 1 + changelog/12842.doc.rst | 2 + doc/en/how-to/types.rst | 137 +++++++++++++++++++++++++++++++++++ pyproject.toml | 1 - src/_pytest/mark/__init__.py | 10 +-- 5 files changed, 142 insertions(+), 9 deletions(-) create mode 100644 changelog/12842.doc.rst create mode 100644 doc/en/how-to/types.rst diff --git a/AUTHORS b/AUTHORS index c38f74d998..c3c806fb75 100644 --- a/AUTHORS +++ b/AUTHORS @@ -256,6 +256,7 @@ lovetheguitar Lukas Bednar Luke Murphy Maciek Fijalkowski +Maggie Chung Maho Maik Figura Mandeep Bhutani diff --git a/changelog/12842.doc.rst b/changelog/12842.doc.rst new file mode 100644 index 0000000000..22d351fc3d --- /dev/null +++ b/changelog/12842.doc.rst @@ -0,0 +1,2 @@ +Added dedicated page about using types with pytest +See :ref:`types` for detailed usage. diff --git a/doc/en/how-to/types.rst b/doc/en/how-to/types.rst new file mode 100644 index 0000000000..83f1bc1aa0 --- /dev/null +++ b/doc/en/how-to/types.rst @@ -0,0 +1,137 @@ +.. _types: + +How to use Types with Pytest +=========================================== + +You can add typing in pytest functions that helps to specify what type of data a function expects and returns. +This guide explains how to apply typing in pytest to improve code readability and prevent type-related errors. + + +1. Introduction to Typing +----------------- + +Typing in Python helps developers specify the expected data types of variables, function parameters, and return values. +It improves code clarity and prevents type errors. + +For example: + + +.. code-block:: python + def add(x: int, y: int) -> int: + return x + y + +In this example: +`x: int` and `y: int` mean that `x` and `y` should be integers. +`-> int` shows that the function returns an integer. + +It makes the intentions clear that both the input and output are expected to be integers. + +2. Typing Test Functions +----------------- +Test functions in pytest check whether the code runs correctly. +While test functions do not return values, we can add `-> None` as the return type. + +For example: + +.. code-block:: python + import pytest + + + def test_add() -> None: + result = add(2, 3) + assert result == 5 + +In this function: +`test_add` is typed as `-> None` because it does not return anything. +The assertion `assert result == 5` checks if the result is correct. + +Example result: +If the input data type is incorrect, like passing `add("2", 3)`, a `TypeError` will occur. + +3. Typing Fixtures +----------------- +Fixtures in pytest helps set up data or provides resources needed for tests. +Adding type hints helps clarify what kind of data the fixtures returns, making code easier to read and easier to debug. + +* Basic Fixture Typing: + +If a fixture returns a number, you can specify it returns an `int`: + +.. code-block:: python + import pytest + + + @pytest.fixture + def sample_fixture() -> int: + return 38 + + + def test_sample_fixture(sample_fixture: int) -> None: + assert sample_fixture == 38 + +In this example: +`sample_fixture()` is typed to return an `int`. +In `test_sample_fixture`, using typing with fixtures, it ensures the return is an integer. +If you change the return from an integer to a string (``"42"``), the test will fail. + + +* Typing Fixtures with Lists and Dictionaries: +Here are the examples showing how to use List and Dict types in pytest. +When you want to use complex data structures like lists or dictionaries, import `List` and `Dict` from Python's `typing` module to specify the types. +Note: From Python 3.5 or later, typing module is built-in module in Python. + + +.. code-block:: python + from typing import List + import pytest + + + # Fixture returning a list of integers + @pytest.fixture + def sample_list() -> List[int]: + return [5, 10, 15] + + + def test_sample_list(sample_list: List[int]) -> None: + assert sum(sample_list) == 30 +In this example, `List[int]` ensures that the list contains only integers, allowing functions like sum() to operate. + + +.. code-block:: python + from typing import Dict + import pytest + + + # Fixture returning a dictionary + @pytest.fixture + def sample_dict() -> Dict[str, int]: + return {"a": 50, "b": 100} + + + def test_sample_dict(sample_dict: Dict[str, int]) -> None: + assert sample_dict["a"] == 50 +In this example, `Dict[str, int]` ensures that each key is a string and each value is an integer, ensuring clarity and consistency when accessing dictionary elements by key. + + +4. Typing Parameterized Tests +---------- +`@pytest.mark.parametrize` allows developer to run the test multiple times with different sets of arguments. +Adding types helps to maintain consistency of values, especially for complex inputs. + +For example, you are testing if adding 1 to `input_value` results in `expected_output` for each set of arguments. + +.. code-block:: python + import pytest + + + @pytest.mark.parametrize("input_value, expected_output", [(1, 2), (5, 6), (100, 101)]) + def test_increment(input_value: int, expected_output: int) -> None: + assert input_value + 1 == expected_output + +In this example: +You are expecting the type of both `input_value` and `expected_output` are integers. + + +Conclusion +---------- +Using typing in pytest helps improve readability of the tests and fixture returns, and prevent errors. diff --git a/pyproject.toml b/pyproject.toml index ad0bca4374..be99392a85 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -213,7 +213,6 @@ disable = [ "condition-evals-to-constant", "consider-alternative-union-syntax", "confusing-consecutive-elif", - "consider-using-any-or-all", "consider-using-assignment-expr", "consider-using-dict-items", "consider-using-from-import", diff --git a/src/_pytest/mark/__init__.py b/src/_pytest/mark/__init__.py index f76e521205..a6f0155751 100644 --- a/src/_pytest/mark/__init__.py +++ b/src/_pytest/mark/__init__.py @@ -193,12 +193,7 @@ def __call__(self, subname: str, /, **kwargs: str | int | bool | None) -> bool: if kwargs: raise UsageError("Keyword expressions do not support call parameters.") subname = subname.lower() - names = (name.lower() for name in self._names) - - for name in names: - if subname in name: - return True - return False + return any(subname in name.lower() for name in self._names) def deselect_by_keyword(items: list[Item], config: Config) -> None: @@ -243,10 +238,9 @@ def __call__(self, name: str, /, **kwargs: str | int | bool | None) -> bool: if not (matches := self.own_mark_name_mapping.get(name, [])): return False - for mark in matches: + for mark in matches: # pylint: disable=consider-using-any-or-all if all(mark.kwargs.get(k, NOT_SET) == v for k, v in kwargs.items()): return True - return False