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

Issue 2836 fixture collection bug #2862

Merged
merged 13 commits into from
Oct 24, 2017
Merged

Issue 2836 fixture collection bug #2862

merged 13 commits into from
Oct 24, 2017

Conversation

tom-dalton-fanduel
Copy link
Contributor

@tom-dalton-fanduel tom-dalton-fanduel commented Oct 23, 2017

Attempted fix for #2836.

The existing handling of 'nodeid' when determining what fixtures are visible is overly simplistic - it considers 'tests/food' to be a 'child' node of 'tests/foo', when in fact they are sibling nodes. This causes fixtures from 'tests/foo/conftest.py' to be used in tests like 'tests/food/test_food.py'.

This PR makes the hanlding more sophisticated - splitting the node id into components and then ensuring the base node id parts fully match the target node id's initial parts. This is still a little imperfect, in that would treat foo/bar::Baz::() as if it was the same as foo/bar/Baz/(). That seems a somewhat unlikely situation...

I'm not super keen on having _splitnode and _ischildnode as members of FixtureManager, but I don't really see any obvious other place for them and I was reluctant to create a while new module/class for these two 'nodeid' helper functions. I'm not 100% sure where the spec/definiton of a node id comes from, so maybe there is a better place, very open to suggestions on that. If someone with some better knowledge of what 'nodeid's look like can have a look over this it would be good - hopefulyl I haven't missed any edge cases but without having a good definition I am slightly worried about it.

The fixtures module didn't (seem to) have any existing unit tests, so I've added one for my new FixtureManager_ischildnode method (mainly for my own sanity). I've also added a more 'integration'-y test to test_collection.py which will catch regression and (hopefully) follows the style of other tests there too. Hopefully test_collection is the right place for that test - certainly the fixture code I've modified seems to be run as part of the collection phase.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.01%) to 92.249% when pulling de9d116 on tom-dalton-fanduel:issue-2836-fixture-collection-bug into 4cb60da on pytest-dev:master.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.01%) to 92.249% when pulling de9d116 on tom-dalton-fanduel:issue-2836-fixture-collection-bug into 4cb60da on pytest-dev:master.

Copy link

@nimbol nimbol left a comment

Choose a reason for hiding this comment

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

Just wanted to input my €0.02. Overall this looks like a solid improvement to me.

@@ -1130,7 +1130,40 @@ def getfixturedefs(self, argname, nodeid):
else:
return tuple(self._matchfactories(fixturedefs, nodeid))

@classmethod
def _splitnode(cls, nodeid):
Copy link

Choose a reason for hiding this comment

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

It appears that this can be a staticmethod instead. The class isn't referenced anywhere in this method.

Copy link
Contributor Author

@tom-dalton-fanduel tom-dalton-fanduel Oct 23, 2017

Choose a reason for hiding this comment

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

I'm not a fan of staticmethods, though mainly for inheritance related reasons and doubt anyone is subclassing fixtruemanager, let alone doing so and then wanting to change these. It feels like they should probably be plain functions in a 'node' module somewhere but hopefully someone more familiar with pytest can guide me on that... (see also my comment about _splitnode in the description).

Copy link
Member

Choose a reason for hiding this comment

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

since both helpers are completely unrelated to the class, how about just moving them out of the class instead

if '::' in last_part:
# Replace single last element 'test_foo.py::Bar::()' with multiple elements 'test_foo.py', 'Bar', '()'
parts[-1:] = last_part.split("::")
return parts
Copy link

Choose a reason for hiding this comment

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

Just a suggestion to simplify the code: remove the special case for the empty nodeid. The following would return functionally equivalent results (it would just return [''] instead of []) without special-casing anything:

sep = py.path.local.sep
parts = nodeid.split(sep)
parts[-1:] = parts[-1].split("::")
return parts

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, I've shortened this down a bit (and made the docstring longer to compensate ;-))

@coveralls
Copy link

Coverage Status

Coverage increased (+0.01%) to 92.248% when pulling ee7e1c9 on tom-dalton-fanduel:issue-2836-fixture-collection-bug into 4cb60da on pytest-dev:master.

Copy link
Member

@RonnyPfannschmidt RonnyPfannschmidt left a comment

Choose a reason for hiding this comment

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

at first glance this is a good and practical approach given that we didn't make the node tree available out there

i believe the helper functions should be put into normal functions for now

personally i hope we can eventually put in a better way to match fixtures <> nodes than walking strings, but since that takes time i'm rather glad you came up with a simple and easy to understand solution to the problem at hand

👍

@@ -0,0 +1,3 @@
Attempted fix for https://github.com/pytest-dev/pytest/issues/2836
Copy link
Member

Choose a reason for hiding this comment

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

i propose match fixture paths against actual path segments in order to avoid matching folders which share a prefix

CC @nicoddemus

@@ -1130,7 +1130,40 @@ def getfixturedefs(self, argname, nodeid):
else:
return tuple(self._matchfactories(fixturedefs, nodeid))

@classmethod
def _splitnode(cls, nodeid):
Copy link
Member

Choose a reason for hiding this comment

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

since both helpers are completely unrelated to the class, how about just moving them out of the class instead

@tom-dalton-fanduel
Copy link
Contributor Author

Thanks a lot for the feedback - I've pushed changes as suggested. Have put the node functions in a new module but let me know if you'd rather they remained in fixtures.py.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.01%) to 92.246% when pulling 655ab0b on tom-dalton-fanduel:issue-2836-fixture-collection-bug into 4cb60da on pytest-dev:master.

@RonnyPfannschmidt
Copy link
Member

the linting issues are newly found issues we need to addto the ignore list
the xdist failure is a flaky test we are aware of so this should be mergable

CC @nicoddemus

Also fixed 'E704 multiple statements on one line (def)' in python_api
@nicoddemus
Copy link
Member

Thanks a lot @tom-dalton-fanduel for the PR, we appreciate it!

I have added the two flake8 new exceptions to the ignore list (E722: "do not use bare except" and E741: "ambiguous variable name 'l'") and fixed another one.

After CI passes I we can merge this. 👍

@coveralls
Copy link

Coverage Status

Coverage increased (+0.01%) to 92.247% when pulling a3ec3df on tom-dalton-fanduel:issue-2836-fixture-collection-bug into 4cb60da on pytest-dev:master.

@nicoddemus
Copy link
Member

Tests are failing on Windows, I suspect we also need to split by os.altsep when not None.

_pytest/nodes.py Outdated
if nodeid == '':
# If there is no root node at all, return an empty list so the caller's logic can remain sane
return []
parts = nodeid.split(py.path.local.sep)
Copy link
Member

Choose a reason for hiding this comment

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

https://github.com/pytest-dev/pytest/blob/master/_pytest/main.py#L519-L527 seems to generally replace the path separator with '/' for consistent node ids across operating systems, i think introducing a constant for that is necessary

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've created a constant SEP in the nodes module, and used that in all the places I could see that used a literal '/' in the context of node ids. I subsequently noticed there is a Node class in main.py (I hadn't noticed that before now), but I can't see an (easy) way to move the new plain functions into that class, since they're used with strings rather than actual node objects.

@tom-dalton-fanduel
Copy link
Contributor Author

tom-dalton-fanduel commented Oct 24, 2017

@nicoddemus is the failing Windows build visible (to me) anywhere? I don't see anything obviously windows-y in Travis...

Edit: Nevermind - I see that Appveyor is the Windows CI builds 🤦‍♂️

@coveralls
Copy link

Coverage Status

Coverage increased (+0.01%) to 92.25% when pulling a5ac19c on tom-dalton-fanduel:issue-2836-fixture-collection-bug into 4cb60da on pytest-dev:master.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.01%) to 92.249% when pulling f5e72d2 on tom-dalton-fanduel:issue-2836-fixture-collection-bug into 4cb60da on pytest-dev:master.

@RonnyPfannschmidt
Copy link
Member

this now is a beautiful cleanup 👍 - the ci issues are from a flake8 update and a bug that @nicoddemus is currently working on

@RonnyPfannschmidt
Copy link
Member

merging as the failures are unrelated, good work 👍

@RonnyPfannschmidt RonnyPfannschmidt merged commit 5631a86 into pytest-dev:master Oct 24, 2017
@tom-dalton-fanduel
Copy link
Contributor Author

Party Parrot

@nicoddemus
Copy link
Member

Hahaha that's an awesome gif! 😄

@tom-dalton-fanduel
Copy link
Contributor Author

Yep - while searching for an appropriate "I am officially a contibutor to pytest, hooray" gif, I came across Party Parrot as a Service and couldn't resist!

@tom-dalton-fanduel tom-dalton-fanduel deleted the issue-2836-fixture-collection-bug branch October 25, 2017 09:21
@nicoddemus
Copy link
Member

I can say with all certainty that this is one of the most useful services of all time. 😆

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.

None yet

5 participants