| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
This reverts commit fa4a681.
|
@gvanrossum @JukkaL I added this to the description. |
Sorry, something went wrong.
|
I did a quick pass on the diff and tried it with mypy, and I like it. The changes seem to speed up a no-op mypy invocation (mypy) by roughly 20%, which is nice (though I didn't do very careful benchmarking). There still seems to be maybe a 5-10% slowdown in startup time due to type annotations, but this seems acceptable already. The generic type object cache hit rate when running mypy -x was about 78% -- increasing the cache size to 50 improved that to 80%. A further increase only had a marginal effect. The improvement with a larger cache might be higher to larger codebases, but it's hard to tell. |
Sorry, something went wrong.
There was a problem hiding this comment.
This is great! Here are some nits. My main concern is over __slots__ but even that's minor. I haven't reviewed everything (in fact I only reviewed src/typing.py) but I think we should just go ahead and release this with 3.6b2 and hope for the best. Maybe we'll get some real-world feedback from that and we can tweak things before 3.5.3 is finalized (sorry, there's no date in PEP 478, but we'll get ample warning, and I expect it to be around the same time as 3.6.0 goes out, mid December according to PEP 494).
Sorry, something went wrong.
| @@ -1,5 +1,6 @@ | |||
| import abc | |||
| from abc import abstractmethod, abstractproperty | |||
| from functools import lru_cache, wraps | |||
There was a problem hiding this comment.
I would rather see import functools, like the following imports. (abstractmethod and -property are special because they are used a lot, and also well-known).
Sorry, something went wrong.
|
|
||
|
|
||
| class Final: | ||
| class TypingBase(metaclass=TypingMeta, _root=True): |
There was a problem hiding this comment.
I think this class name should start with _, and ditto for Final below (and probably also TypingMeta above).
Sorry, something went wrong.
| class TypingBase(metaclass=TypingMeta, _root=True): | ||
| """Indicator of special typing constructs.""" | ||
|
|
||
| # things that are not classes also need these |
There was a problem hiding this comment.
Can you capitalize sentences and add a period at the end?
Sorry, something went wrong.
| """Wrapper to hold a forward reference.""" | ||
|
|
||
| def __new__(cls, arg): | ||
| def __init__(self, arg): |
There was a problem hiding this comment.
OK, but I still think it should call super().
Sorry, something went wrong.
| assert not isinstance(impl_type, TypingMeta), repr(impl_type) | ||
| self.name = name | ||
| self.type_var = type_var | ||
| self.type_var = _type_check(type_var, "Type alias accepts only types") |
There was a problem hiding this comment.
Questionable, since this is not an end user API. If this error ever were to trigger that would mean the author of typing.py itself had made a mistake. That's why it was just an assert.
Sorry, something went wrong.
| """ | ||
|
|
||
| def __new__(cls, name, *constraints, bound=None, | ||
| def __init__(self, name, *constraints, bound=None, |
There was a problem hiding this comment.
Again, I'd still like the super() method called.
Sorry, something went wrong.
| return '{}.{}'.format(cls.__module__, cls.__name__[1:]) | ||
|
|
||
|
|
||
| class Final(TypingBase, _root=True): |
There was a problem hiding this comment.
Maybe rename to _FinalTypingBase?
Sorry, something went wrong.
| class Final: | ||
| class TypingBase(metaclass=TypingMeta, _root=True): | ||
| """Indicator of special typing constructs.""" | ||
|
|
There was a problem hiding this comment.
We should have a __slots__ here too, otherwise the __slots__ in the subclass is pointless. Also all the final classes (and probably also those inheriting from TypingBase) should have their own __slots__.
(I guess it's a little-known fact that __slots__ only suppresses the __dict__ when used for every class in the MRO...)
Sorry, something went wrong.
|
|
||
|
|
||
| def tp_cache(func): | ||
| cached = lru_cache(maxsize=50)(func) |
There was a problem hiding this comment.
Isn't this cache size way too small? I'd expect that a program that benefits from this cache would be pretty large so it would have way more than 50 different generic type instantiations (if I understand that that's what it's caching -- it looks like it's only used on __getitem__ in GenericMeta).
Though when the cache works it's amazing -- I measured 20x speedup on List[int]!
Also, I printed the cache info for running mypy itself, and there 50 seems fine -- with maxsize=50 there are 120 misses, while with maxsize=1000 there are 115 misses, and currsize is also 115, so it seems mypy itself contains 115 distinct generic type instantiations. (The hit count when running on itself was 1071, so that would point to roughly 10x speedup.)
The default maxsize for lru_cache() is 128 -- perhaps just leave it up to that?
Sorry, something went wrong.
|
@gvanrossum
|
Sorry, something went wrong.
|
@JukkaL |
Sorry, something went wrong.
There was a problem hiding this comment.
I am going to merge this as-is. While there are a few things left to do, it's easier to do them in a follow-up PR -- this one is just too big to review already.
Sorry, something went wrong.
|
|
||
| __slots__ = () | ||
|
|
||
|
|
There was a problem hiding this comment.
Only one blank line is required.
Sorry, something went wrong.
| isinstance(args[1], tuple)): | ||
| # Close enough. | ||
| raise TypeError("Cannot subclass %r" % cls) | ||
| return object.__new__(cls) |
There was a problem hiding this comment.
Couldn't this use super()?
Sorry, something went wrong.
| class Final: | ||
|
|
||
| class _FinalTypingBase(_TypingBase, _root=True): | ||
| """Mix-in class to prevent instantiation.""" |
There was a problem hiding this comment.
I'd update the docstring to hint that it prevents instantiation unless _root=True is given.
Sorry, something went wrong.
| assert isinstance(type_var, type), repr(type_var) | ||
| assert isinstance(impl_type, type), repr(impl_type) | ||
| assert not isinstance(impl_type, TypingMeta), repr(impl_type) | ||
| assert isinstance(type_var, (type, _TypingBase)) |
There was a problem hiding this comment.
Maybe add repr(type_var) as the message?
Sorry, something went wrong.
| def __new__(cls, *args, **kwds): | ||
| """Constructor. | ||
|
|
||
| This only exists to give a better error message in case |
There was a problem hiding this comment.
Maybe getting a better error isn't enough reason to insert an extra function call? Or does the cache make this moot?
Sorry, something went wrong.
| Union[Manager, int, Employee] == Union[int, Employee] | ||
| Union[Employee, Manager] == Employee | ||
|
|
||
| - Corollary: if Any is present it is the sole survivor, e.g.:: |
There was a problem hiding this comment.
Over at mypy we've convinced ourselves (with some difficulty!) that this is wrong. Union[int, Any] should not be simplified to Any. Intuitively, the reason is that if x has type Union[int, Any] then calling x.lower() would be a mistake because it's wrong to call .lower() on an int (even though it's okay to call it on an Any). More about this at python/mypy#1642.
Sorry, something went wrong.
|
|
||
| Union[int, Any] == Any | ||
|
|
||
| - Similar for object:: |
There was a problem hiding this comment.
Note that this statement is not in question -- in fact this is the reason that we were initially misguided about Union[int, Any]. Technically it just falls under the subclass rule stated above. (You could edit the docstring to make this the corollary.)
Sorry, something went wrong.
| @@ -499,22 +597,16 @@ def __new__(cls, name, bases, namespace, parameters=None, _root=False): | |||
| for t1 in params: | |||
| if t1 is Any: | |||
There was a problem hiding this comment.
I guess getting rid of this check should be sufficient to avoid simplifying away Any. (Also the comment above should be updated.)
Sorry, something went wrong.
| try: | ||
| return cached(*args, **kwds) | ||
| except TypeError: | ||
| pass # Do not duplicate real errors. |
There was a problem hiding this comment.
This comment is mysterious. I suppose you mean that we only catch TypeErrors so that other errors won't be masked by this. And I recall from a previous version that the issue here is that maybe an argument may not be hashable, and then we want the real error from the uncached function (I think). But TypeError would also be raised for various bugs in func (in general TypeError usually means there's a bug). In the end I think it's fine to just catch TypeError, because the uncached function should reproduce the error exactly. It would also be fine to catch Exception, but presumably unnecessary. So I'm just asking for a better comment. :-)
Sorry, something went wrong.
| """Optional type. | ||
|
|
||
| - You can use Optional[X] as a shorthand for Union[X, None]. | ||
| Optional[X] is equivalent to Union[X, type(None)]. |
There was a problem hiding this comment.
While type(None) is technically correct I'm not sure how much it adds to mention it here (especially since Union[x, None] and Union[x, type(None)] are always equivalent :-).
Sorry, something went wrong.
|
Feel free to iterate more on this! Especially the Union[Any, int] issue. But I figured we might as well take a checkpoint, so this is now merged (also into CPython 3.5, 3.6 and 3.7==default). I closed the three CPython bugs you mentioned. |
Sorry, something went wrong.
|
@gvanrossum This is a good catch, I didn't notice this while editing PEP 483. The point is that type is characterized by two sets: set of values and set of functions (methods), as mentioned in PEP 483. With such definition neither int is not a subtype of Any (since int has less methods) nor vice versa (since Any has more values). The fact that Any is at the same time at the top of type hierarchy (it has all values) and at its bottom (it has all methods) requires us to introduce two different concepts: subtype and compatibility. Actually, now I think that Any should be considered a subtype of object. Indeed, both have all values, but Any has more methods, therefore it is a subtype of object. This, in turn, means that Union[object, Any] should be simplified to just object. For example, if x has type Union[object, Any] then the only way to call x.lower() is when x is not object, but this is not possible: x: Union[object, Any]
x.lower() # this should be rejected
if not isinstance(x, object):
x.lower() # this should be allowed, but it is not reachableWhat do you think? (If it will help, the relation between object and Any is similar to relation between, e.g., Mapping and MutableMapping, they both have the same set of values, but MutableMapping has more methods, therefore it is a subtype of Mapping.) |
Sorry, something went wrong.
|
Good reasoning! I like talking about "has more methods" and "has more
values".
|
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
This PR:
The idea is to make most things not classes. Now _ForwardRef(), TypeVar(), Union[], Tuple[], Callable[] are not classes (i.e. new class objects are almost never created by typing).
Using isinstance() or issubclass() rises TypeError for almost everything. There are exceptions:
Finally, generics should be classes, to allow subclassing, but now the outcome of __getitem__ on classes is cached. I use an extended version of functools.lru_cache that allows fallback to non-cached version for unhashable arguments.
This is still WIP, since I did this only for Python 3.
@gvanrossum @JukkaL Please take a look.