Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix pytest.mark.parametrize rules to check calls instead of decorators #14515

Merged
merged 10 commits into from
Nov 25, 2024

Conversation

harupy
Copy link
Contributor

@harupy harupy commented Nov 21, 2024

Summary

Currently, rules for pytest.mark.parametrize (e.g. /pytest-parametrize-names-wrong-type (PT006)) only check decorators, but pytest.mark.parametrize can appear as a function call as show below.

# To parametrize all tests in a module, you can assign to pytest mark


import pytest

pytestmark = pytest.mark.parametrize("n,expected", [(1, 2), (3, 4)])


class TestClass:
    def test_simple_case(self, n, expected):
        assert n + 1 == expected

    def test_weird_simple_case(self, n, expected):
        assert (n * 1) + 1 == expected

Test Plan

Existing tests and a new test case to ensure the fix is valid.

@harupy
Copy link
Contributor Author

harupy commented Nov 21, 2024

The upstream rule also checks calls:

https://github.com/m-burst/flake8-pytest-style/blob/2addab8b533ea101e25d022a37dc32d89db6c688/flake8_pytest_style/visitors/parametrize.py#L130-L132

    def visit_Call(self, node: ast.Call) -> None:
        if is_parametrize_call(node):
            self._check_parametrize_call(node)

Copy link
Contributor

github-actions bot commented Nov 21, 2024

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

ℹ️ ecosystem check detected linter changes. (+50 -0 violations, +0 -0 fixes in 2 projects; 53 projects unchanged)

apache/airflow (+48 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --ignore RUF9 --output-format concise --preview --select ALL

+ helm_tests/airflow_aux/test_annotations.py:249:5: PT006 Wrong type passed to first argument of `@pytest.mark.parametrize`; expected `tuple`
+ providers/tests/dbt/cloud/hooks/test_dbt.py:157:18: PT006 Wrong type passed to first argument of `@pytest.mark.parametrize`; expected `tuple`
+ providers/tests/dbt/cloud/hooks/test_dbt.py:168:18: PT006 Wrong type passed to first argument of `@pytest.mark.parametrize`; expected `tuple`
+ providers/tests/dbt/cloud/hooks/test_dbt.py:178:18: PT006 Wrong type passed to first argument of `@pytest.mark.parametrize`; expected `tuple`
+ providers/tests/dbt/cloud/hooks/test_dbt.py:201:18: PT006 Wrong type passed to first argument of `@pytest.mark.parametrize`; expected `tuple`
+ providers/tests/dbt/cloud/hooks/test_dbt.py:216:18: PT006 Wrong type passed to first argument of `@pytest.mark.parametrize`; expected `tuple`
+ providers/tests/dbt/cloud/hooks/test_dbt.py:235:18: PT006 Wrong type passed to first argument of `@pytest.mark.parametrize`; expected `tuple`
+ providers/tests/dbt/cloud/hooks/test_dbt.py:254:18: PT006 Wrong type passed to first argument of `@pytest.mark.parametrize`; expected `tuple`
+ providers/tests/dbt/cloud/hooks/test_dbt.py:273:18: PT006 Wrong type passed to first argument of `@pytest.mark.parametrize`; expected `tuple`
+ providers/tests/dbt/cloud/hooks/test_dbt.py:294:18: PT006 Wrong type passed to first argument of `@pytest.mark.parametrize`; expected `tuple`
+ providers/tests/dbt/cloud/hooks/test_dbt.py:315:18: PT006 Wrong type passed to first argument of `@pytest.mark.parametrize`; expected `tuple`
+ providers/tests/dbt/cloud/hooks/test_dbt.py:334:18: PT006 Wrong type passed to first argument of `@pytest.mark.parametrize`; expected `tuple`
+ providers/tests/dbt/cloud/hooks/test_dbt.py:356:18: PT006 Wrong type passed to first argument of `@pytest.mark.parametrize`; expected `tuple`
+ providers/tests/dbt/cloud/hooks/test_dbt.py:388:18: PT006 Wrong type passed to first argument of `@pytest.mark.parametrize`; expected `tuple`
+ providers/tests/dbt/cloud/hooks/test_dbt.py:423:18: PT006 Wrong type passed to first argument of `@pytest.mark.parametrize`; expected `tuple`
+ providers/tests/dbt/cloud/hooks/test_dbt.py:453:18: PT006 Wrong type passed to first argument of `@pytest.mark.parametrize`; expected `tuple`
+ providers/tests/dbt/cloud/hooks/test_dbt.py:458:18: PT006 Wrong type passed to first argument of `@pytest.mark.parametrize`; expected `tuple`
+ providers/tests/dbt/cloud/hooks/test_dbt.py:512:18: PT006 Wrong type passed to first argument of `@pytest.mark.parametrize`; expected `tuple`
+ providers/tests/dbt/cloud/hooks/test_dbt.py:534:18: PT006 Wrong type passed to first argument of `@pytest.mark.parametrize`; expected `tuple`
+ providers/tests/dbt/cloud/hooks/test_dbt.py:559:18: PT006 Wrong type passed to first argument of `@pytest.mark.parametrize`; expected `tuple`
+ providers/tests/dbt/cloud/hooks/test_dbt.py:586:18: PT006 Wrong type passed to first argument of `@pytest.mark.parametrize`; expected `tuple`
+ providers/tests/dbt/cloud/hooks/test_dbt.py:603:18: PT006 Wrong type passed to first argument of `@pytest.mark.parametrize`; expected `tuple`
+ providers/tests/dbt/cloud/hooks/test_dbt.py:624:18: PT006 Wrong type passed to first argument of `@pytest.mark.parametrize`; expected `tuple`
+ providers/tests/dbt/cloud/hooks/test_dbt.py:692:18: PT006 Wrong type passed to first argument of `@pytest.mark.parametrize`; expected `tuple`
+ providers/tests/dbt/cloud/hooks/test_dbt.py:711:18: PT006 Wrong type passed to first argument of `@pytest.mark.parametrize`; expected `tuple`
... 23 additional changes omitted for rule PT006
+ providers/tests/microsoft/azure/hooks/test_data_factory.py:151:13: PT007 Wrong values type in `@pytest.mark.parametrize` expected `list` of `tuple`

pypa/setuptools (+2 -0 violations, +0 -0 fixes)

ruff check --no-cache --exit-zero --ignore RUF9 --output-format concise --preview

+ pkg_resources/tests/test_working_set.py:107:9: PT006 Wrong type passed to first argument of `@pytest.mark.parametrize`; expected `tuple`
+ setuptools/tests/test_egg_info.py:308:17: PT006 Wrong type passed to first argument of `@pytest.mark.parametrize`; expected `tuple`

Changes by rule (2 rules affected)

code total + violation - violation + fix - fix
PT006 49 49 0 0 0
PT007 1 1 0 0 0

}
}
pub(crate) fn parametrize(checker: &mut Checker, call: &ExprCall) {
if !is_pytest_parametrize(call, checker.semantic()) {
Copy link
Contributor Author

@harupy harupy Nov 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated to this PR, but PT rules don't have a seen_module guard. Shoud we add it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah we could.

Copy link
Contributor Author

@harupy harupy Nov 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds like a nice good first issue.

Comment on lines 803 to 817
if checker.enabled(Rule::PytestParametrizeNamesWrongType) {
if let Some(names) = call.arguments.find_argument("argnames", 0) {
check_names(checker, call, names);
}
}
if checker.enabled(Rule::PytestParametrizeValuesWrongType) {
let names = call.arguments.find_argument("argnames", 0);
let values = call.arguments.find_argument("argvalues", 1);
if let (Some(names), Some(values)) = (names, values) {
check_values(checker, names, values);
}
}
if checker.enabled(Rule::PytestDuplicateParametrizeTestCases) {
if let Some(values) = call.arguments.find_argument("argvalues", 1) {
check_duplicates(checker, values);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This aligns with the upstream implementation:

https://github.com/m-burst/flake8-pytest-style/blob/2addab8b533ea101e25d022a37dc32d89db6c688/flake8_pytest_style/utils.py#L96

def extract_parametrize_call_args(node: ast.Call) -> Optional[ParametrizeArgs]:
    """Extracts argnames, argvalues and ids from a parametrize call."""
    args = get_simple_call_args(node)

    names_arg = args.get_argument('argnames', 0)
    if names_arg is None:
        return None

    values_arg = args.get_argument('argvalues', 1)
    ids_arg = args.get_argument('ids')
    return ParametrizeArgs(names_arg, values_arg, ids_arg)

@MichaReiser MichaReiser added the rule Implementing or modifying a lint rule label Nov 25, 2024
@MichaReiser
Copy link
Member

Thanks for the how-to-guide and API reference. That helps a lot with building the required context to review the rule. I really appreciate it :)

Copy link
Member

@MichaReiser MichaReiser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great. Did you already look through the ecosystem changes? I can do it, if you haven't.

I think we should gate this change behind preview mode because I consider this a "significant increase in scope" and we should only make such changes in minor releases.

@harupy
Copy link
Contributor Author

harupy commented Nov 25, 2024

@MichaReiser Thanks for the comment. This PR has the two changes. Do we want to gate both of them?

  1. Only calls in decorators are covered -> Any calls are covered.
  2. Only positional arguments are covered -> Both positional and keyword arguments are covered.

@MichaReiser
Copy link
Member

Oh right. Making 1. preview-only for now seems more important to me than 2. We can have a look at the ecosystem change to understand how much impact 2 has but I think it's fine to not gate 2.

@harupy
Copy link
Contributor Author

harupy commented Nov 25, 2024

For Airflow, 2 has more impacts (more changes) than 1.

@MichaReiser
Copy link
Member

Right, I should have taken a closer look at the ecosystem results. We should then probably make 2 a preview style change as well. It's a bit annoying codewise but feels most in line with our versioning policy

@MichaReiser MichaReiser added the preview Related to preview mode features label Nov 25, 2024
@MichaReiser MichaReiser merged commit fa22bd6 into astral-sh:main Nov 25, 2024
20 checks passed
@harupy harupy deleted the fix-pytest-parametrize branch November 25, 2024 12:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
preview Related to preview mode features rule Implementing or modifying a lint rule
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants