-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
migrate node fs paths to pathlib primary and reorg ctors/from_parent #8037
migrate node fs paths to pathlib primary and reorg ctors/from_parent #8037
Conversation
while fixing the typing issues it seems i introduced a bug in collection i keep missing atm im under the impression that resolving it will require a new type of node construction for sessions in order to avoid certain issues |
the failure in the plugins testing is due to a unavoidable issue with pytest-flakes, i'll check out whether it an be coordinated away |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm yeah the break is unfortunate, I wonder if there's some trick we could do with taking both fspath
/ fs_path
parameters for some time and then deprecate / adjust? might make for a smoother transition for plugins (though of course, way more work for us!)
most of my comments are little type things, overall seems good 👍
7de346a
to
c75ee78
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall is looking good! Left a few comments. 👍
src/_pytest/nodes.py
Outdated
|
||
#: Filesystem path where this node was collected from (can be None). | ||
self.fspath = fspath or getattr(parent, "fspath", None) | ||
self.fs_path = fs_path |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Being a bit nitpicky, but fs_path
and fspath
are too close to one another, I fear users will get the two confused all the time.
How about: path
for the Path
version? After all the fs
prefix is a bit redudant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @nicoddemus, the name fs_path
is not very pretty.
One thing against just path
is that someday we'll also want to migrate our hooks to Path
and their the py.path param is always path
so the Path param would need a different name. And path
is not very distinguishable either. But I can't think of something else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about file_path
? This is always a file right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there are situations where it ought to be a folder
- external collection utilities
- collection roots (a concept i intend to introduce as extension of the testpaths configuration (i think it needs a ticket)
- Packages ought to use it as well
self.fspath = fspath | ||
|
||
session = session or parent.session | ||
name = str(rel) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will end up with a name containing \
on Windows because we are no longer doing name = name.replace(os.sep, SEP)
. Is that intentional?
Also str
seems redudant:
>>> local("src/pytest").relto(local("."))
'src\\pytest'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
relto is from py.path.local - relative pathlib paths are relative
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh I see, thanks, but does the comment about name.replace
still apply?
>>> local("src/pytest").relto(local("."))
'src\\pytest'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it does
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh OK so this needs fixing then. 👍
nodeid = self.fspath.relto(session.config.rootdir) | ||
assert parent is not None | ||
session = parent.session | ||
relp = session._bestrelpathcache[known_path] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice
assert parent is not None | ||
session = parent.session | ||
relp = session._bestrelpathcache[known_path] | ||
if not relp.startswith(".." + os.sep): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we have a comment here explaining the purpose of this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gentle ping
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a full review, just some comment.
src/_pytest/main.py
Outdated
@@ -458,7 +458,12 @@ class Session(nodes.FSCollector): | |||
|
|||
def __init__(self, config: Config) -> None: | |||
super().__init__( | |||
config.rootdir, parent=None, config=config, session=self, nodeid="" | |||
fs_path=Path(config.rootdir), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fs_path=Path(config.rootdir), | |
fs_path=config.rootpath, |
src/_pytest/main.py
Outdated
key = (type(x), x.fspath) | ||
if key in node_cache2: | ||
yield node_cache2[key] | ||
keya = (type(x), x.fspath) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest key2
to match node_cache2
.
src/_pytest/main.py
Outdated
key = (type(node), node.nodeid) | ||
if key in matchnodes_cache: | ||
rep = matchnodes_cache[key] | ||
keyb = (type(node), str(node.nodeid)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here I would call it key_matchnodes
.
Why is the str(node.nodeid)
needed? Isn't nodeid
already an str?
src/_pytest/nodes.py
Outdated
|
||
#: Filesystem path where this node was collected from (can be None). | ||
self.fspath = fspath or getattr(parent, "fspath", None) | ||
self.fs_path = fs_path |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with @nicoddemus, the name fs_path
is not very pretty.
One thing against just path
is that someday we'll also want to migrate our hooks to Path
and their the py.path param is always path
so the Path param would need a different name. And path
is not very distinguishable either. But I can't think of something else.
also helps to implement pytest-dev/pytest#8037
as soon as the pytest-flakes maintainer fins some time to merge/release the pipeline should get green, this still is a breaking change in terms of requiring people to actually write proper ctors im thinking of reintroducing implying for a little while and using the pytest-flakes pr as a example for propper ctors |
Cool thanks for the follow up
Not sure I follow can you please clarify that? |
Aka having the node ctors imply things and warning about it so plugins can migrate |
Ahh got it, thanks! |
@nicoddemus @bluetech it seems like the fragile base class issue is hitting this pr and many plugins way harder than what id hope for im not sure whether we should fix plugisn or reiterate the approach |
47833e9
to
57b1f33
Compare
What you mean exactly, in terms of the number of arguments that can be passed?
This might be the best way to go, as at least plugins will continue to work, and we can issue a warning for a few releases suggesting the change. With 7.0 we can then make the final breaking change. |
2fe61c1
to
0ee0897
Compare
@nicoddemus hitting another case of rebase demonstrating why primarily acceptance tests is a massive problem, will debug sometime later |
b68763e
to
442bae3
Compare
576997a
to
6934346
Compare
6934346
to
de835f2
Compare
Co-authored-by: Ran Benita <[email protected]>
de835f2
to
424f826
Compare
Co-authored-by: Bruno Oliveira <[email protected]>
for more information, see https://pre-commit.ci
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be easier to evaluate this PR if it were split into at least two parts:
- The implied-arguments deprecation
- The pathlib conversions
Because they seem not really related, and it will be easier to review each one of these separately. But if it's too much work that's OK.
Regarding the implied-arguments deprecation - the code looks good to me but at a higher level I'm still not quite sure what is the reason for the deprecation, or of from_parent
in general?
Regarding the pathlib conversion, that's great, I think it's the last major piece 👍 But the code is a little hard to review, having a PR which just does this part would really help me at least. Also regarding the name, I prefer path
over fs_path
, it's quite minor but I don't like how fs_path
looks (in the hookspecs we stuck with path
and fspath
).
"_nodeid", | ||
"_store", | ||
"__dict__", | ||
) | ||
|
||
name: str |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is mypy not able to figure these out on its own?
fspath: Optional[py.path.local] = kw.pop("fspath", None) | ||
|
||
if fspath is not None: | ||
assert fs_path is None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
assert fs_path is None | |
assert fs_path is None, "Specify either fspath or fs_path, not both" |
@@ -0,0 +1 @@ | |||
Deprecate implying node ctor arguments. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think if I read this sentence as a user I would not be able to grok what it's about, so some more details would be good here, like what exactly is deprecated, and how to fix.
Also we usually add a corresponding note in the docs/deprecations.rst
file (can just be a copy/paste of the changelog entry mostly).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. 👍
"implying {type.__name__}.{arg} from parent.{arg} has been deprecated\n" | ||
"please use the Node.from_parent to have them be implied by the superclass named ctors", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"implying {type.__name__}.{arg} from parent.{arg} has been deprecated\n" | |
"please use the Node.from_parent to have them be implied by the superclass named ctors", | |
"Implying {type.__name__}.{arg} from parent.{arg} has been deprecated.\n" | |
"Please use Node.from_parent to have them be implied by the superclass named constructors.", |
config: Optional[Config] = None, | ||
session: Optional["Session"] = None, | ||
nodeid: Optional[str] = None, | ||
parent: Node, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this one i missed while restoring ctor based implication
@@ -0,0 +1 @@ | |||
Introduce ``Node.fs_path`` as pathlib replacement for ``Node.fspath`` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Introduce ``Node.fs_path`` as pathlib replacement for ``Node.fspath`` | |
Introduce ``Node.fs_path`` as pathlib replacement for ``Node.fspath``. |
Also I would add another paragraph:
This is part of our continuous effort of eventually phasing out pytest's dependency to `py <https://py.readthedocs.io/en/latest/>`__.
config: Optional[Config] = None, | ||
session: "Optional[Session]" = None, | ||
fspath: Optional[py.path.local] = None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By dropping fspath
(the py.path.local
variant) aren't we breaking the API? If so I think a better option would be to still support fspath
, converting it internally to fs_path
and issue a deprecation warning.
def fspath(self) -> py.path.local: | ||
"""Filesystem path where this node was collected from (can be None). | ||
|
||
Since pytest 6.2, prefer to use `fs_path` instead which returns a `pathlib.Path` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since pytest 6.2, prefer to use `fs_path` instead which returns a `pathlib.Path` | |
Since pytest 6.2, prefer to use `fs_path` instead which returns a :class:`pathlib.Path` |
the overall input here has convinced me to do the extra work of extraction and splitting originally i planned to simplify things by dropping implication at the same time, but all i accomplished is learning that i had to bring it back, so im going to reiterate today and take what i learned to do a number of prs to make things prettier |
as PyObjMixin is always supposed to be mixed in the mro before nodes.Node the behavior doesn't change, but all the typing information carry over to help mypy. extracted from pytest-dev#8037
as PyObjMixin is always supposed to be mixed in the mro before nodes.Node the behavior doesn't change, but all the typing information carry over to help mypy. extracted from pytest-dev#8037
as PyObjMixin is always supposed to be mixed in the mro before nodes.Node the behavior doesn't change, but all the typing information carry over to help mypy. extracted from pytest-dev#8037
closing as resolved in other prs by me, thanks for the help |
@pytest-dev/core with this i'm starting my continuation of the removal of both, too smart ctors annd migrating fspath to a pathlib version
its going to be a draft for a while while i iterate and figure the exact details, please scrutinize meanwhile ^^