-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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 6341 disallow session config in fromparent #6387
Fix 6341 disallow session config in fromparent #6387
Conversation
fdcd45f
to
4fcacde
Compare
src/_pytest/nodes.py
Outdated
@@ -144,8 +144,10 @@ def __init__( | |||
self._nodeid += "::" + self.name | |||
|
|||
@classmethod | |||
def from_parent(cls, parent, *, name): | |||
return cls._create(parent=parent, name=name) | |||
def from_parent(cls, parent, **kw): |
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 you explain this change? The previous signature already didn't allow config
and session
, why change to **kw
here?
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 is to allow super().from_parent
- else subclasses would have to use horrendous hacks around the internal '_create' method
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.
FWIW, I still think we should not have / need from_parent
to begin with, but could use raising TypeError and/or type annotations. See #5975 (comment). I've hoped there to go back to "the simpler pattern" before that PR was merged even.
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.
@blueyed from_parent
will very likely stay in order to manage the logic parts of node construction
its very likely that Node.from_parent
will eventually manage passing config
, session
, parent
and nodeid
to the Node
constructor - in which case it will stay forever
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.
IIUC, adding from_parent
is needed to allow for a deprecation of the current way of constructing Node
and subclasses.
Again IIUC, the current Node
constructor (and subclasses) allow for the creation of broken states and configuration, for example Function
and Session
objects with different Config
s, so the idea is for __init__
to no longer be public API, used only internally.
Perhaps it would help if @blueyed provided a code sample of what the Node.__init__
method would looke like in his mind?
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.
Perhaps it would help if @blueyed provided a code sample of what the
Node.__init__
method would looke like in his mind?
It could behave as from_parent
if only parent
is provided, and ensure that no other (conflicting) kwargs are there (raising a DeprecationWarning (and later TypeError) then, e.g. for when config
or session
is passed also).
My main issue here is that it complicates things in general, and requires plugins to adopt it (adding a compat layer there), e.g. pytest-asyncio (https://github.com/pytest-dev/pytest-asyncio/blob/6f72dd9f419f7945906d566a85ba8d50b8d8324d/pytest_asyncio/plugin.py#L39).
IIUC, adding
from_parent
is needed to allow for a deprecation of the current way of constructingNode
and subclasses.
Well, the deprecation is for having to use from_parent
after all.
From what I can tell we could also have deprecations via __init__
for when an old/unsupported combination/pattern is used.
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.
for me a key problem is construction correctness, right now there are just too many things entangled that cant be easily be de-tangled with a constructor without keeping unreasonably smart __init__
methods -- imho the unreasonable smartness needs to be dissolved or moved into factory functions/methods (like from_parent
)
i have been fighting with the details over a long time now as i tried repeatedly to make FunctionDefinition
an actual part of the collection tree, and it just keeps breaking and breaking in strange places
my goal is to remove those breakage points one by one so i c an put things in place, one of those stumbling blocks are unreasonably smart constructors (they just do way too much)
i'm happy to go for a different solution that works, but so far nobody has show one, as such im slowly clipping away at the things that make future work utterly horrible for me,
as for the constructors as they currently work - im not aware of a way to untangle them without making them inaccessible at once to allow horrendous internal breakages that wont affect users
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.
also sorry for the late reply, i missed the comment earlier, @nicoddemus brought it to my attention
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.
@RonnyPfannschmidt I can see that it makes sense to get there, but it is bad when plugins have to add compat code for it then, i.e. it would be best to remove it again before releasing it.
As for getting there with type annotations and in small steps see e.g. #6461.
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.
@blueyed its very likely going to stay (so the __init__
wont have to have logic) and i expect it to take many months to completely sort things out
src/_pytest/nodes.py
Outdated
@@ -144,8 +144,10 @@ def __init__( | |||
self._nodeid += "::" + self.name | |||
|
|||
@classmethod | |||
def from_parent(cls, parent, *, name): | |||
return cls._create(parent=parent, name=name) | |||
def from_parent(cls, parent, **kw): |
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.
IIUC, adding from_parent
is needed to allow for a deprecation of the current way of constructing Node
and subclasses.
Again IIUC, the current Node
constructor (and subclasses) allow for the creation of broken states and configuration, for example Function
and Session
objects with different Config
s, so the idea is for __init__
to no longer be public API, used only internally.
Perhaps it would help if @blueyed provided a code sample of what the Node.__init__
method would looke like in his mind?
src/_pytest/nodes.py
Outdated
@@ -144,8 +144,10 @@ def __init__( | |||
self._nodeid += "::" + self.name | |||
|
|||
@classmethod | |||
def from_parent(cls, parent, *, name): | |||
return cls._create(parent=parent, name=name) | |||
def from_parent(cls, parent, **kw): |
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 noticed that none of Node.from_parent
has docstrings or type annotations. Missing docstrings also mean they don't appear in the reference doc.
I believe we should explain here how subclasses are meant to use super()
that's mentioned in the changelog entry.
I think this is valid for all from_parent
methods, but it is absolutely crucial for Node.from_parent
: users will be drawn to this method after seeing the warning when Nodes are created via __init__
, so this should really be a great docstring IMO. 😁
put it into wip as i didn't get to clean-up for personal reasons, currently i'm considering it as part of my weekend to-do as i don't think i get to it tomorrow either |
No worries! |
86a581f
to
2958129
Compare
2958129
to
a852ba7
Compare
a852ba7
to
8ba0b7b
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.
.
@@ -4,3 +4,7 @@ Instead they are new constructed via ``Node.from_parent``. | |||
|
|||
This transitional mechanism enables us to detangle the very intensely | |||
entangled ``Node`` relationships by enforcing more controlled creation/configruation patterns. | |||
|
|||
As part of that session/config are already disallowed parameters and as we work on the details we might need disallow a few more as well. |
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.
As part of that session/config are already disallowed parameters and as we work on the details we might need disallow a few more as well. | |
As part of that session/config are already disallowed parameters and as we work on the details we might need to disallow a few more as well. |
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 grammar fix was not included.
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.
Should have type hints for return types, and signatures.
I'm still in the boat that the whole Therefore I suggest to rather branch it off into an actual "feature branch" to get it sorted out there, or to not do any releases from |
We can settle on waiting on releasing off features, but how can we ensure we don't stay like this forever? I have 3 suggestions:
As it stands we are in the middle of things, with |
@nicoddemus from_parent is also the approach to detangleing node construction and the node tree i have taken after repeatedly failing it before currently i dont think i can sort out constructor entanglement/controll flow sanely without detaching users from the api |
I'm aware, but @blueyed is unconvinced and wants this to be sorted out by using |
its not just about those 2 parameters, its about iteratively landing a required major refactoring that rearranges entanglements in the Node Tree and constructors within it from_parent is an attempt to externalize this in a controlled manner that can keep much better compatibility forward right now i don't believe there is a sustainable way to deliver that set of refactoring in sane manner with just deprecation and time (unless we are ok with doing a breaking release every 1-2 months for a while as things sort out iteratively) |
Btw: the |
I think you mean https://mail.python.org/pipermail/pytest-dev/2019-October/004841.html (from 2019-10-24) then, which was not really a discussion, but only a reference to the PR (#5975 (from 2019-10-16)). I gave the same feedback there back then (#5975 (comment)), but it was merged nonetheless (by you). As for your suggestions: I've started some WIP on reverting it myself in blueyed#143, but have not looked into it a while. But it is blocking myself from merging features into my fork. I think it would make sense for @RonnyPfannschmidt to keep on working on it given the current pattern(s) he wants/has to use, but do so in a separate branch. Given that the consent from https://mail.python.org/pipermail/pytest-dev/2019-November/004860.html however has been to use master and release branches we could also just go with option 3, which appears to be the long-term goal anyway. Given all that I am not holding anything back in this regard, and have only repeated myself because you've asked. |
But my review comment still applies: having all those |
That discussion seemed resolved to me, you asked something, @RonnyPfannschmidt replied, and that was that.
I don't think it is good practice to change our workflow without having the proper docs in place:
IMHO after the appropriate changes are merged, we can start with the new workflow officially. In case it is not clear, I'm not volunteering to update those documents, because as I mentioned before I'm not sure it is a good idea.
Fair enough. 👍 |
at first glance it seems like mymy refuses to handle this, even for |
@bluetech ping - do you have any idea how to declare named constructors that correctly take the current type and return it, as far as i can tell my attempt fails |
11e48b3
to
8cfc5a1
Compare
src/_pytest/python.py
Outdated
@@ -45,6 +47,9 @@ | |||
from _pytest.warning_types import PytestCollectionWarning | |||
from _pytest.warning_types import PytestUnhandledCoroutineWarning | |||
|
|||
if False: # TYPE_CHECKING | |||
from typing import Type |
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.
Please use from _pytest.compat import TYPE_CHECKING
.
8cfc5a1
to
8ba0b7b
Compare
calling off the typing after repeated issues with mypy on classlevel super and passing over/around named constructor input/output types |
Do still have plans to do so? I'm asking to know if we should hold on to release |
I looked at it. Overall the metaclass constructions seems quite problematic for typing, from two perspectives:
So I now regret having proposed it in the first place! What do you think about trying the other approach I suggested in the comment #5975 (comment)? Namely, to add an internal underscored keyword-only argument to the Node One thing the metaclass approach does is that it applies recursively to all |
@bluetech one of the intents is to take away public constructors, |
@RonnyPfannschmidt I am thinking about something like this: class Node: # No metaclass
def __init__(
self,
name,
parent: Optional["Node"] = None,
config: Optional[Config] = None,
session: Optional["Session"] = None,
fspath: Optional[py.path.local] = None,
nodeid: Optional[str] = None,
*,
_internal_to_pytest_i_know_what_i_am_doing=False,
) -> None:
if not _internal_to_pytest_i_know_what_i_am_doing:
# Raise deprecation warning, can error in the future.
....
@classmethod
def from_parent(cls, parent, *, name):
return cls(parent=parent, name=name, _internal_to_pytest_i_know_what_i_am_doing=True) So if the ctor is used directly, without setting the internal kwarg, can do whatever is needed. And hopefully the kwarg has a scary enough name that people don't try to work around it :) |
@bluetech that is a problem for subclasssing however |
@RonnyPfannschmidt why is that? (I might have a guess but I'm not sure). And do you mean internal (pytest repo) subclasses, or external? |
@bluetech both, until a subclass initialization protocol is established (atm the ctors do a lot of magic |
One thing I'm missing is who is the intended users of this API (I don't have much experience with the pytest ecosystem). Without it it's hard to tell what's the best way to "protect" it. (A) Regular pytest users. (A) and (B) I assume are irrelevant. (C) cases already need to call This leaves (D). But are there actually plugins which do that (construct existing node types)? |
C and D are targeted, the details are outlined on the ml |
But (C) still needs to call If we remove (C) from consideration, then the problem for subclassing is not relevant, and the internal kwarg approach becomes viable. |
@bluetech thats why a subclass initialization protocol is necessary, you cant hide away init, but you can affect a great deal of interaction with it - and still, |
anyway, this pr is about disallowing certain arguments, not reiterating the details, that should be done on the ml and with a alternative that grasps the issues with the different ctors and how they entangle |
Ps, I don't consider the separate branch sustainable for me |
Guys decided to merge this PR because |
@bluetech maybe this is relevant? davidhalter/jedi-vim#995 (comment) |
fixes #6341
changelog
folder, with a name like<ISSUE NUMBER>.<TYPE>.rst
. See changelog/README.rst for details.