-
-
Notifications
You must be signed in to change notification settings - Fork 278
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
Create UninferableBase
#1741
Create UninferableBase
#1741
Conversation
Pull Request Test Coverage Report for Build 3503985614
💛 - Coveralls |
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 this is a neat change, but I don't really like the name. I don't have particularly good proposal either, I'm thinking about UninferableType
or TypeOfUninferable
. Maybe astroid.typing.Uninferable
and there is two Uninferable
with a different namespace ?
Yeah the issue I had with that is that that doesn't really make sense a year from now. |
We have something similar already ? Maybe we could use the same scheme for consistency ? We could maybe bother Marc. This is a little naming brain teaser that will affect a looooot of typing later and should not take him too long to give his opinion on. |
No 🤣 I meant, normal classes also don't have
I tagged him in the original issue. I was hesitant to retag here. |
Ha, all right. Yeah that's not very satisfying (actually it's the reason why I don't like the name, but the issue is not the name itself it's the way it's implemented that make the typing not work like every other typing in python). The |
That's bound to cause issues somewhere down the line. I'm not even sure you can do: from typing import Uninferable
Uninferable = Uninferable() That seems like a naming clash. Anyway, this has a similar issue as |
Yeah, that would be: from typing import Uninferable as InstantiateMePleaseUninferable
Uninferable = InstantiateMePleaseUninferable() Then in code: import astroid
from astroid.utils import Uninferable
def myfunc(node: astroid.typing.Uninferable | nodes.NodeNG) -> None:
if node is Uninferable:
print(...) |
I don't think we should use the same name for different things (which is what the current issue basically boils down to) and I don't think it makes sense to put the class definition in the Anyway, imo in a years time I think it makes the most sense to have a name for the base class that shows that it is a base class rather than a name that shows where the refactoring effort originated from. |
Yeah I don't have a good solution, maybe there isn't one; |
Came across something similar recently, in the ...
class _MISSING_TYPE:
pass
MISSING = _MISSING_TYPE()
... This is how they define sentinel values. That doesn't solve the typing issue though which got me thinking. I.e. the change would just be to remove the |
Yeah that should work but I was just thinking about two three years from now. |
The underscore has the advantage that it's (somewhat) private and we can change it if we want. |
@cdce8p Note that both from __future__ import annotations
class _MyClass:
...
MyClass = _MyClass()
def f(x: _MyClass | str) -> None:
if x is MyClass:
reveal_type(x)
else:
reveal_type(x) ❯ mypy test.py --strict
test.py:13: note: Revealed type is "Union[test._MyClass, builtins.str]"
test.py:15: note: Revealed type is "Union[test._MyClass, builtins.str]"
Success: no issues found in 1 source file And: from __future__ import annotations
class _MyClass:
...
MyClass = _MyClass()
def f(x: type[MyClass] | str) -> None:
if x is MyClass:
reveal_type(x)
else:
reveal_type(x) ❯ mypy test.py --strict
test.py:11: error: Variable "test.MyClass" is not valid as a type [valid-type]
test.py:11: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
test.py:12: error: Non-overlapping identity check (left operand type: "Union[Type[MyClass?], str]", right operand type: "_MyClass") [comparison-overlap]
test.py:13: note: Revealed type is "Union[Type[MyClass?], builtins.str]"
test.py:15: note: Revealed type is "Union[Type[MyClass?], builtins.str]"
Found 2 errors in 1 file (checked 1 source file) In the last example I might be missing something here but I think this resembles the potential situation with |
astroid/util.py
Outdated
|
||
def __repr__(self): | ||
This is meant to be used as a singleton. Use util.Uninferable to access 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.
Would we rather point to astroid.Uninferable
, which is where pylint
usually accesses it?
I don't think there is a real good solution here, but it would be really nice to get this merged and start working on the migration to I'm open to other preferences as I don't really have a preference myself but I do think we should bite the bullet and choose one solution. |
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 this is the best approach. It's unfortunate that the sentinel is title-cased (instead of UNINFERABLE
), but it's not worth changing this point.
One small suggestion to avoid exporting UninferableBase from the module.
astroid/__init__.py
Outdated
@@ -180,7 +180,7 @@ | |||
|
|||
# isort: on | |||
|
|||
from astroid.util import Uninferable | |||
from astroid.util import Uninferable, UninferableBase |
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 would probably skip adding this 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 "necessary" for pylint
I think, but perhaps also a good question to ask now.
What do we want to do in pylint
use node: UninferableBase
or node: type[Uninferable]
? Some goes for checking node is Uninferable
or isinstance(node, UninferableBase)
.
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.
Definitely UninferableBase
. You pointed out above the trouble with type[Uninferable]
. (The pyright dev said it was straight wrong.)
Some goes for checking node is Uninferable or isinstance(node, UninferableBase)?
Either. The latter is safer against copying/unpickling.
This is "necessary" for pylint I think
I think I'm missing this part; do you mind spelling it out? It could be imported from astroid.util
instead of astroid
.
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.
Ah yeah, but I thought we put all things that are public as classes directly in astroid
. But I do prefer importing from util
myself so I'll remove 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.
Ah yeah, but I thought we put all things that are public as classes directly in astroid.
I think it's a totally random and difficult to maintain snapshot of shortcuts. I'd like to stop adding to it, if possible:
>>> import astroid
>>> astroid.PY310_PLUS
False
>>> astroid.PY311_PLUS
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: module 'astroid' has no attribute 'PY311_PLUS'
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.
importing from astroid directly has the advantage that we can refactor everything the way we want without breaking the API. And astroid has some really nasty construct and import cycle internally that makes this valuable.
1b758c6
to
f022df8
Compare
I did the clean up of the code base in this PR 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.
I have no strong opinion about this so I trust the decision taken without re-reading the whole discussion.
I think Däniel's solution is the same as Marc's suggested here, just not using the underscore. (Which makes sense, it has to be public at least to pylint to let pylint use it as a type.) There was some more discussion about typing that got us sidetracked, but what's being implemented in this PR is the easy to understand, classic way to type. |
Codecov Report
Additional details and impacted files@@ Coverage Diff @@
## main #1741 +/- ##
==========================================
+ Coverage 92.74% 92.76% +0.01%
==========================================
Files 94 94
Lines 10962 10956 -6
==========================================
- Hits 10167 10163 -4
+ Misses 795 793 -2
Flags with carried forward coverage won't be shown. Click here to find out more.
|
Steps
Description
Closes #1680.
Type of Changes