-
-
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
Is it sus that fake unreachable blocks aren't type checked? #11511
Comments
Nice one, lol. |
This is an "int / float" subtyping glitch (still not sure if we can call this a bug). Normally this would either be:
I will see if there's anything I can do. |
There isn't a 'glitch' issue category 🤣 |
Any heads up as to where I can start looking for the relevant code? Again, I'm new here. Not exactly my position to state this as 'not a bug' lol. |
Sorry for the confusion! I need to explain better what I meant. Since mypy uses nominal subtyping, it was decided to create new "fake" type hierarchy for >>> int.mro()
[<class 'int'>, <class 'object'>]
>>> float.mro()
[<class 'float'>, <class 'object'>]
>>> bool.mro()
[<class 'bool'>, <class 'int'>, <class 'object'>] Typechecking: float -> int -> bool So, all these types can still be used just the way we actually use them. This is kinda special. But, we still have a narrowing problem with When we run this with def foo(i: float) -> None:
if not isinstance(i, float):
i.AMONGUS # E: Statement is unreachable But, in runtime - this is reachable. The only thing we can do here is to special case narrowing of |
Yeah, I know the hot mess of float int duck typing i: float = 1
i.is_integer 😬😬😬😬😬😬😬 |
I'd agree that special casing reachability of float is the best way to handle this situation, but honestly I don't see it as all that big of an issue. |
I've made a simple PoC: def process_fake_type_hierarchies(
self,
expr_node: Expression,
expr_type: Type,
type_maps: Tuple[TypeMap, TypeMap],
) -> Tuple[TypeMap, TypeMap]:
"""Forcefully adds fake type hierarchies into a type map.
For example, we add `builtins.int` to `no_type_map` when `isinstance(some, float)` is used.
Because in runtime `int` is not a subclass of `float`.
"""
yes_map, no_map = type_maps
for union_item in union_items(expr_type):
if isinstance(union_item, Instance) and union_item.type.fullname == 'builtins.float':
if no_map is None:
no_map = {}
current_type = no_map.get(expr_node)
runtime_types = [
self.named_type('builtins.int'),
self.named_type('builtins.bool'),
]
if current_type is not None:
runtime_types.append(current_type)
no_map[expr_node] = UnionType(runtime_types)
return yes_map, no_map With this change it works like this: I am going to test this a bit more and then send a PR. |
Don't forget about |
And there's the bytes nightmare |
This ducktyping stuff is extremely unintuitive, what do you think about reveal_type revealing all the ducklings? a: float = 1
reveal_type(a) # float | int | complex |
I believe that this can resolve in quite a lot of possible problems in the future. I will keep this change as simple as possible 🙂 |
My current implementation went out of control. It is so complex and probably incorrect, that I am not going to continue. Sorry! Someone else might try fixing this! |
I think it would be better to make |
It would remove the need for special casing to infinity and reflect the actual behaviour. I recommend closing this in favour of #11516 |
i think it's an issue that unreachable code isn't type-checked at all, regardless of whether or not it's actually unreachable, see #11649 |
AttributeError: 'int' object has no attribute 'AMONGUS'
The text was updated successfully, but these errors were encountered: