| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
There was a problem hiding this comment.
It's great to see us moving towards supporting general recursive types! Looks good, just various small comments.
Using a plugin is a smart way to find all the places where we have dangerous isinstance calls. I had considered using NewType hacks but it would produce more confusing error messages.
Sorry, something went wrong.
| A type alias to another type. | ||
|
|
||
| To support recursive type aliases we don't immediately expand a type alias | ||
| during semantic analysis, but create an in stance of this type that records the target alias |
There was a problem hiding this comment.
Typo: "in stance"
Sorry, something went wrong.
| """Expand to the target type exactly once. | ||
|
|
||
| This doesn't do full expansion, i.e. the result can contain another | ||
| (or even this same) type alias. |
There was a problem hiding this comment.
Maybe this should be considered an internal helper (perhaps prefixed with _) and we could suggest using get_proper_type instead.
Sorry, something went wrong.
| raise NotImplementedError('TODO') | ||
|
|
||
| @property | ||
| def can_be_true(self) -> bool: # type: ignore |
There was a problem hiding this comment.
The # type: ignore may break mypyc is strange ways, so it would be better not to use it.
Sorry, something went wrong.
There was a problem hiding this comment.
There is actually a mypy false positive #6759 that doesn't allow fixing this. I will leave a comment here with a link to issue.
Sorry, something went wrong.
| return self.alias.target.can_be_true | ||
|
|
||
| @property | ||
| def can_be_false(self) -> bool: # type: ignore |
There was a problem hiding this comment.
Similar to above.
Sorry, something went wrong.
|
|
||
| class TypeAliasType(Type): | ||
| """ | ||
| A type alias to another type. |
There was a problem hiding this comment.
Style nit: move the first line to the same line with """ ("""A type alias ...).
Sorry, something went wrong.
| definition node (mypy.nodes.TypeAlias) and type arguments (for generic aliases). | ||
|
|
||
| This is very similar to how TypeInfo vs Instance interact. | ||
| """ |
There was a problem hiding this comment.
Style nit: Add empty line after """, for consistency.
Sorry, something went wrong.
| """ | ||
| A type alias to another type. | ||
|
|
||
| To support recursive type aliases we don't immediately expand a type alias |
There was a problem hiding this comment.
Add a prominent note that this is not being used yet, and the implementation is still incomplete?
Sorry, something went wrong.
| if variables is None: | ||
| variables = [] | ||
| self.arg_types = arg_types | ||
| self.arg_types = list(arg_types) |
There was a problem hiding this comment.
What about making sure that these are expanded enough so that they are proper types (but may contain type alias references)? Not sure if this would be worth it, but it might let us avoid a bunch of get_proper_type calls elsewhere.
Sorry, something went wrong.
There was a problem hiding this comment.
It looks like this is actually not possible. Actually even what I did for unions is problematic, for example if we have (not very meaningful, since technically A = int) A = Union[int, A]. To represent this we must have somewhere a union that contains an item that is A. I will try to fix this now.
Sorry, something went wrong.
There was a problem hiding this comment.
Actually these kind of "trivial" recursive unions are the most problematic, maybe we should actually ban them. Another solution would be to actually remove aliases being defined from immediate union, but this can silently hide an error, so I don't like this.
Sorry, something went wrong.
There was a problem hiding this comment.
After some more thinking I think I convinced myself that aliases like A = Union[int, A] are really bad. Essentially they are not much different from A = A, for example all situations like this:
x: A
x.to_bytes()cause infinite recursion. Also, it is very hard to imagine any practical use for such aliases. Finally, they can be easily detected during semantic analysis and banned.
If we ban them, then we can keep UnionType.items and TypeType.item proper. I however didn't find any simple consistent representation schema where we could also make TupleType.items and CallableType.arg_types (and .ret_type) proper, mostly because of things like A = Union[int, Callable[[], A]].
@JukkaL If you don't have objections, I would merge this to get things moving (sorry for pinging during vacation :-)).
Sorry, something went wrong.
|
|
||
|
|
||
| class TupleType(Type): | ||
| class TupleType(ProperType): |
There was a problem hiding this comment.
Similar to above, I can imagine that expanding items could be useful (or not).
Sorry, something went wrong.
| if (isinstance(callee_node, (Var, SYMBOL_FUNCBASE_TYPES)) | ||
| and callee_node.type): | ||
| callee_node_type = callee_node.type | ||
| callee_node_type = get_proper_type(callee_node.type) |
There was a problem hiding this comment.
Since this change may affect plugins, it needs to be documented in plugin docs and the plugins changelog issue before the next mypy release.
Sorry, something went wrong.
There was a problem hiding this comment.
Yes, I actually added this expansion to minimize possible breakages (at least few of our plugins used isinstance() with these types).
Sorry, something went wrong.
…7366) This completes the addition of expansion points before every `isinstance()` checks on types. This essentially does the same as #7330. Couple comments: * Many checks (probably more than half) in type checker are against `AnyType`, `NoneType`, and `UninhabitedType`, but technically these are valid targets for aliases, so I still do expansions there. * I added couple hooks to the plugin to flag redundant `get_proper_type()` and `get_proper_types()` calls, however sometimes when they trigger they add some additional spurious errors about non-matching overloads. I tried to fix this, but it looks like this requires calling `get_function_hook()` once on a whole overload (now it is called for every item). Next part will contain implementation of the visitors.
…7885) This is the last part of plumbing for recursive types (previous #7366 and #7330). Here I implement visitors and related functions. I convinced myself that we need to only be more careful when a recursive type is checked against another recursive one, so I only special-case these. Logic is similar to how protocols behave, because very roughly type alias can be imagined as a protocol with single property: ```python A = Union[T, Tuple[A[T], ...]] class A(Protocol[T]): @Property def __base__(self) -> Union[T, Tuple[A[T], ...]]: ... ``` but where `TypeAliasType` plays role of `Instance` and `TypeAlias` plays role of `TypeInfo`. Next two pull requests will contain some non-trivial implementation logic.
| Back | FazBrowse Home | New Git URL |
During planning discussions one of the main concerns about recursive types was the fact that we have hundreds of places where certain types are special-cased using isinstance(), and fixing all of them will take weeks.
So I did a little experiment this weekend, to understand how bad it actually is. I wrote a simple mypy plugin for mypy self-check, and it discovered 800+ such call sites. This looks pretty bad, but it turns out that fixing half of them (roughly 400 plugin errors) took me less than 2 days. This is kind of a triumph of our tooling :-) (i.e. mypy plugin + PyCharm plugin).
Taking into account results of this experiment I propose to actually go ahead and implement recursive types. Here are some comments: