Skip to content

Simplify positional arguments compatibility code in pytest.fixture()#6999

Merged
nicoddemus merged 1 commit into
pytest-dev:masterfrom
bluetech:simplify-fixture-compat
May 8, 2020
Merged

Simplify positional arguments compatibility code in pytest.fixture()#6999
nicoddemus merged 1 commit into
pytest-dev:masterfrom
bluetech:simplify-fixture-compat

Conversation

@bluetech
Copy link
Copy Markdown
Member

@bluetech bluetech commented Apr 1, 2020

The dynamic scope feature added in 10bf6aa necessitated some wrangling of arguments in pytest.fixture(). In particular, it deprecated positional arguments in favor of keyword-only arguments, while keeping backward compatibility.

The way it did this avoided some code duplication but ended up being quite hard to follow and to annotate with types.

Replace it with some straightforward code, which is not very DRY but is simple and easy to remove when the time comes.

Comment thread src/_pytest/fixtures.py
warnings.warn(FIXTURE_POSITIONAL_ARGUMENTS, stacklevel=2)
# End backward compatiblity.

if fixture_function and params is None and autouse is False:
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

BTW, I'm interested to know why the params is None and autouse is False condition exists here, if anyone knows... Seems weird to just ignore fixture_function if params or autouse are specified?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Judging from the # direct decoration comment, that's to support

@pytest.fixture
def func():
    ....

rather than

@pytest.fixture(...)
def func():
    ...

so there's nothing being ignored, because there are no arguments given at all in that case.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Right, the if fixture_function part I understand, what I'm having trouble figuring out is the and params is None and autouse is False part.

The reason I care about it is that I am working on adding type-annotating pytest.fixture(). Without this extra params condition, it is straightforward to distinguish the "direct decoration" case, but with it it is more difficult.

Comment thread src/_pytest/fixtures.py
# End backward compatiblity.

if fixture_function and params is None and autouse is False:
# direct decoration
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Also curious why id is omitted here (in the line below - GitHub doesn't let me comment on it).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ids are used for the parametrize which won't be used here due to params is None condition.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Right. Still seems better to me not to ignore it, so any inconsistencies (like passing ids but not params) will be detected.

Comment thread src/_pytest/fixtures.py
FIXTURE_ARGS_ORDER = ("scope", "params", "autouse", "ids", "name")


def _parse_fixture_args(callable_or_scope, *args, **kwargs):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

TBH I'm OK with this implementation; I like that it is general enough that we can reuse it eventually when needed.

Having said that if others prefer the new version I'm OK with that too.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I don't think we would ever want to reuse it, because it's deprecated and new code should be keyword-only from the start. Any new parameters to pytest.fixture() would be keyword-only too. So this whole code block is "read-only" and I think it's better to have it straightforward and localized (and shorter).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yeah I have that feeling like I wanted to reuse this somewhere, but can't really remember it right now. 😁

The dynamic scope feature added in 10bf6aa
necessitated some wrangling of arguments in pytest.fixture(). In
particular, it deprecated positional arguments in favor of keyword-only
arguments, while keeping backward compatibility.

The way it did this avoided some code duplication but ended up being
quite hard to follow and to annotate with types.

Replace it with some straightforward code, which is not very DRY but is
simple and easy to remove when the time comes.
@bluetech bluetech force-pushed the simplify-fixture-compat branch from 71e7485 to 03451c3 Compare April 2, 2020 11:53
@bluetech
Copy link
Copy Markdown
Member Author

bluetech commented Apr 2, 2020

After looking again I've made some further changes:

  • Renamed callable_or_scope to fixture_function. The previous name is really internal-facing, let's use the proper name we would use without compat/deprecation. Also moved its compat check to inside the block marked with the comments.

  • Moved if params is not None: params = list(params) to after the compat check -- was a slight bug in the previous code.

  • Added tests to fix the coverage hopefully.

Copy link
Copy Markdown
Contributor

@aklajnert aklajnert left a comment

Choose a reason for hiding this comment

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

It kinda hurts my eyes seeing so much if statements and makes me want to immediately revert to the previous code (as I'm the original author). But I need to agree, that this version is much easier to read and remove in the future.

Comment thread src/_pytest/fixtures.py
# End backward compatiblity.

if fixture_function and params is None and autouse is False:
# direct decoration
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ids are used for the parametrize which won't be used here due to params is None condition.

@bluetech
Copy link
Copy Markdown
Member Author

It kinda hurts my eyes seeing so much if statements and makes me want to immediately revert to the previous code (as I'm the original author). But I need to agree, that this version is much easier to read and remove in the future.

😄 Definitely ugly, but it's localized and easier to handle when adding type annotations, so I think the advantages outweigh the ugliness.

@nicoddemus nicoddemus merged commit 5dd987e into pytest-dev:master May 8, 2020
@bluetech bluetech deleted the simplify-fixture-compat branch June 17, 2020 15:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants