-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Avoid autoremoval of imported pytest fixtures #3295
Comments
Why do you import the fixture? It shouldn't be needed generally. |
It's not in a conftest.py file, it's in a separate library. |
I'm admittedly not familiar enough with |
The usual pattern is to use # conftest.py
pytest_plugins = ("my_library.pytest_plugin",)
Yup. Pytest does dependency injection based on the function name. You can also override the fixture name in the decorator call. |
I'm using workarounds, e.g. |
@edgarrmondragon's suggested solution is not really a workaround, it's preferred way to do it, because importing fixtures might lead to subtle problems. pytest docs recommend against importing fixtures: https://docs.pytest.org/en/7.2.x/how-to/fixtures.html#using-fixtures-from-other-projects I understand your concern about ruff breaking code, but since it points to a real problem IMO ruff should not add special code for it. |
I've been thinking and I agree. Ruff should not concern it with the intricacies of pytest. Thanks for the discussion and references, I will close the issue. |
I'm a little confused by this conclusion - pytest's docs only recommend against importing fixtures from other projects. Is there any recommendation against importing fixtures from elsewhere in your own project? Likewise, |
I agree this is a common pattern e.g. https://github.com/PrefectHQ/prefect/blob/16f46ca49838cbdb250d113bb149f13ca819302d/tests/conftest.py#L68-L78 I'm not sure it's feasible for Ruff to address this though. |
I'm fine with the verdict, but wanted to add a note +1 in issues I hit while trying to enable ruff on https://github.com/apache/superset , somehow we've been using this import pattern across the 1700 test modules ... |
I have to say I agree with @emosenkis on this one, the conclusion reached here doesn't make much sense to me at all. as @emosenkis points out, the dox linked to only recommend against importing fixtures from other projects, it makes no mention of importing from other modules in the same project. The documentation linked to also mentions only "minor consequences", not anything severe - to my reading importing from another module is fine. Further, in my case adding the fixture in question to conftest rather than leaving it where it is currently defined would cause a circular import - that's why it's defined where it is, e.g: conftest.py: ...
def some_helper():
some_logic_here(thing_defined_in_conftest)
something = more_logic(other_thing_defined_in_conftest)
return something test_thing.py: from thing import Thing
from conftest import some_helper
@fixture(scope='function')
def my_thing() -> Thing:
something = some_helper()
other_logic_here()
return Thing()
test_other_thing.py: from thing import Thing
from test_thing import my_thing
def test_something(my_thing:Thing)
some_logic()
do_things_with(my_thing)
assert things_about(my_thing) In this case the I think ruff should be addressing this, and as far as I can see it shouldn't be too difficult to do - all you need to do is find the definition for any unused items which are being imported and see if they're wrapped by a pytest @fixture decorator before removing them. I intend to continue importing my fixtures from other modules in line with best practice, unless someone can suggest a better/cleaner alternative. I'll have to consider whether to turn off the 'remove unused imports' option until such time as this code-breaking behaviour is changed 😢 |
Hmm. I'm pretty hesitant to re-implement pytest logic in Ruff directly, which is effectively what we'd be doing here. We also don't support doing that kind of lookup across files right now so we couldn't support it even if we wanted to (though we could in the future once we've shipped multi-file analysis and type inference). E.g., in this case, we have no way of knowing that that from thing import Thing
from test_thing import my_thing
def test_something(my_thing:Thing)
some_logic()
do_things_with(my_thing)
assert things_about(my_thing) |
Thanks for the quick response :) You'd have to go find the definition for I suppose a less-automagic and less-complicated fix would be to allow me to maintain a list of imports which are allowed to be unused, i.e something like this in pyproject.toml: [tool.ruff.lint]
ignore_unused_imports = [
'my_thing', # or maybe 'test_thing.my_thing'?
] maybe something like Something like this would also generalise: i.e it allows fixing this issue for things other than pytest fixtures that have the same problem (I can see it being an issue for anything that uses dependency injection using a similar pattern to pytest), without either littering my code with #noqa's, or turning off F401 altogether (which is what I have reluctantly done). and as a bonus I could write a thing to find all my fixtures and add them in here, too. |
Just to re-iterate on the point made above: this takes working code and breaks it. I think Ruff should, as a matter of principle, never do that. |
I fully understand that! As a data-point, I note that PyCharm must have also re-implemented the logic, as it is able to determine which fixtures are used and which aren't (screenshot to show coloring): As already highlighted, the workaround cited from the A workaround that hasn't been mentioned yet is to re-export the imports to trick So:
Becomes:
Whilst I appreciate the idea, I find this view to be a bit too extreme - since imports can have side-effects (even though they shouldn't) then you can never consider any import to be a safe fix removal. The same applies for importing in order to expose an interface - in practice One option that |
What would stop Ruff making this a linting rule? Is it able to identify the import as a fixture and apply the export if missing? If so, why can't it ignore F401 if it identifies the import as a fixture? |
I think this relates to the point that @charliermarsh was making in #3295 (comment). Namely, that requires inference to determine that the imported thing is a fixture. The workaround I proposed above still feels to me like a bit of a workaround. I wonder if the heuristic that I suggested might just be good enough to address the majority of cases:
|
Currently imported pytest fixtures are removed by the ruff autoremoval feature (unused import). After running ruff my tests fail because the fixtures are removed.
Example
Tested on
ruff 0.0.253
The text was updated successfully, but these errors were encountered: