-
-
Notifications
You must be signed in to change notification settings - Fork 292
Ensure the *_fields
attributes of __init__
/postinit
are cohesive
#1218
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
Conversation
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.
Thank you for taking the time to review this part of the code and for undergoing the huge work of making it coherent. I have some small suggestions.
@@ -2105,8 +2108,7 @@ def postinit( | |||
:param keywords: The keywords given to the class definition. | |||
:type keywords: list(Keyword) or None | |||
""" | |||
if keywords is not 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.
This was a very strange way to do it.
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.
What is strange about it? The default for self.keywords
is an empty list. If I remember correctly, this change was added recently to fix a crash in pylint.
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.
Well usually you use if keyword is None:
to apply the default value when the default value is a mutable like []
in order to avoid the problem of mutable default argument being function attribute, so doing the opposite where you initialize only if it's not None "feel" wrong.
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.
See the relevant PR for this change here:
#1181
Co-authored-by: Pierre Sassoulas <[email protected]>
Co-authored-by: Pierre Sassoulas <[email protected]>
Co-authored-by: Pierre Sassoulas <[email protected]>
Co-authored-by: Pierre Sassoulas <[email protected]>
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.
IMO we should not do these changes. For most, if not all, there is a good reason why it was designed that way. Some (limited) areas can certainly be improved, but these are changes that are more likely to introduce bugs than fix anything.
As for why some attributes are added with postinit
and other in __init__
, I don't know the full answer to that. I believe the idea was to pass most with __init__
but that isn't possible for children nodes. Thus they are added in postinit
. Besides that, whatever makes most sense.
Moving some, just for the point of doing it, will add more potential for errors that is IMO just unnecessary.
lineno: Optional[int] = None, | ||
col_offset: Optional[int] = 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.
The ast node arguments
doesn't have lineno
or col_offset
information. IMO it doesn't make sense to add them. https://docs.python.org/3.10/library/ast.html
@@ -1328,7 +1332,7 @@ def postinit( | |||
self.target = target | |||
self.annotation = annotation | |||
self.value = value | |||
self.simple = simple | |||
self.simple = simple or self.simple |
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 can easily break. simple
is a boolean integer so either 0
or 1
.
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.
Yeah this was a forehead-slapper.
lineno: Optional[int] = None, | ||
col_offset: Optional[int] = 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.
Same here, the ast Comprehension
node doesn't have lineno
or col_offset
information.
https://docs.python.org/3.10/library/ast.html
@@ -1800,7 +1811,7 @@ def postinit( | |||
self.iter = iter | |||
if ifs is not None: | |||
self.ifs = ifs | |||
self.is_async = is_async | |||
self.is_async = is_async or self.is_async |
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 break too!
self.fromname: Optional[str] = fromname # can be None | ||
self.modname = self.fromname # For backwards compatibility |
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 shouldn't be part of this PR. IMO it's not worth it to do this change in isolation, there is no really need for it.
@@ -4076,6 +4088,7 @@ class FormattedValue(NodeNG): | |||
""" | |||
|
|||
_astroid_fields = ("value", "format_spec") | |||
_other_other_fields = ("conversion",) |
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.
Why should this be in _other_other_fields
and not _other_fields
? It's just an optional integer.
@@ -4252,7 +4265,7 @@ class EvaluatedObject(NodeNG): | |||
|
|||
name = "EvaluatedObject" | |||
_astroid_fields = ("original",) | |||
_other_fields = ("value",) | |||
_other_other_fields = ("value",) |
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.
Why here?
lineno: Optional[int] = None, | ||
col_offset: Optional[int] = 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.
MatchCase
doesn't have lineno
or col_offset
information.
kwd_attrs: typing.List[str], | ||
kwd_patterns: typing.List[Pattern], | ||
kwd_attrs: typing.Optional[typing.List[str]] = 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.
IMO it does make more sense to keep kwd_attrs
and kwd_patterns
together in postinit
. No need to move one to __init__
.
@@ -2105,8 +2108,7 @@ def postinit( | |||
:param keywords: The keywords given to the class definition. | |||
:type keywords: list(Keyword) or None | |||
""" | |||
if keywords is not 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.
What is strange about it? The default for self.keywords
is an empty list. If I remember correctly, this change was added recently to fix a crash in pylint.
It says in https://github.com/PyCQA/astroid/blob/3f98fa0be2279ff422a0e157705f4e3ea1e4d48f/doc/extending.rst that
So in addition to having these follow a convention for developer's sake conventions also helps allow code to make assumptions. So I wouldn't say it's "just for the point of doing it". It's to address some tech debt / code uncleanliness. |
We might need to update the documentation then 😉 |
We've done some pretty heavy refactor and breaking change in 3.0 for consistency's sake, Marc, what is different here ? It think having a clear rule like:
Would make astroid development easier and reduce mistakes then the need to retro-engineer classes to see how they were implemented if they do not follow the general "rule". Is a breaking change in 3.0 really that much more impacting than what we already did ? |
The breaking changes so far have been because we moved code around. So changing the import is usually enough. Then there are the cases which shouldn't happen anyway, like passing This change in contrast will break code just because it "might" improve consistency. IMO this is dangerous and not worth it. As I've pointed out already, just from a few lines, there would have been multiple regressions. True, we can fix them but who is to say those don't introduce new bugs in plugins. Usually I think it's better to limit breaking changes to an absolute minimum and only do them when there is a clear benefit. To highlight something different. Although it might fix inconsistencies, it also introduces new ones. All node types have A last point, the |
Ok, I'm convinced, it seemed really nice on paper @thejcannon thank you for the proposal. But the inconsistencies coming from the AST that will keep on coming and the bigger changes required for lib that are not us (pylint) are pretty convincing reasons to not fix this. |
Bummer. Cheers anyways. |
* Based on suggestions from pylint-dev#1218
* Based on suggestions from pylint-dev#1218
* Based on suggestions from #1218
Steps
Description
It was discovered (and the fix is needed by) #1194 that there isn't strict cohesion between the
*_fields
attributes and the parameters in__init__
andpostinit
, so I've added tests which enforces this.Through testing a handful of violators have been found, so a deprecation warning decorator has been added, and parameters have been updated accordingly.
Type of Changes
Related Issue