-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Create a copy of TypeQuery specialized for bool #9604
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
Changes from 6 commits
cd17881
ab4f0c2
6423a02
0a838bd
f7129b2
d264791
da9dca0
65d37f6
900699d
f6c5c8e
de55732
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,7 @@ | |
from abc import abstractmethod | ||
from mypy.ordered_dict import OrderedDict | ||
from typing import Generic, TypeVar, cast, Any, List, Callable, Iterable, Optional, Set, Sequence | ||
from typing_extensions import Final | ||
from mypy_extensions import trait | ||
|
||
T = TypeVar('T') | ||
|
@@ -245,7 +246,8 @@ class TypeQuery(SyntheticTypeVisitor[T]): | |
"""Visitor for performing queries of types. | ||
|
||
strategy is used to combine results for a series of types, | ||
common use cases involve a boolean query using `any` or `all`. | ||
For cases involving a boolean query using `any` or `all`, the specialized | ||
TypeQueryBool is used. | ||
|
||
Note: this visitor keeps an internal state (tracks type aliases to avoid | ||
recursion), so it should *never* be re-used for querying different types, | ||
|
@@ -347,3 +349,121 @@ def query_types(self, types: Iterable[Type]) -> T: | |
self.seen_aliases.add(t) | ||
res.append(t.accept(self)) | ||
return self.strategy(res) | ||
|
||
|
||
class TypeQueryBool(SyntheticTypeVisitor[bool]): | ||
"""Specialized visitor for boolean strategies | ||
|
||
strategy is used to combine results for a series of types, | ||
Cases involve a boolean query using `any` or `all`. | ||
|
||
Note: this visitor keeps an internal state (tracks type aliases to avoid | ||
recursion), so it should *never* be re-used for querying different types, | ||
create a new visitor instance instead. | ||
|
||
# TODO: check that we don't have existing violations of this rule. | ||
""" | ||
STRATEGY_ANY = 0 # type: Final[int] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't it be just from typing_extensions import Final
class Test(object):
a = 1 # type: Final
b = 2 # type: Final[int]
reveal_type(Test.a) # Revealed type is 'Literal[1]?'
reveal_type(Test.b) # Revealed type is 'builtins.int' There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The doc says that if we omit the type, mypy infers it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure about the mypy team, but I personally prefer Literal, because it is more specific. And constants should be specific. 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I changed them to Final 👌 |
||
STRATEGY_ALL = 1 # type: Final[int] | ||
|
||
def __init__(self, strategy: int) -> None: | ||
# 0: any() | ||
vbarbaresi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# 1: all() | ||
self.strategy = strategy | ||
# Keep track of the type aliases already visited. This is needed to avoid | ||
# infinite recursion on types like A = Union[int, List[A]]. | ||
self.seen_aliases = set() # type: Set[TypeAliasType] | ||
|
||
def bool_strategy_empty(self) -> bool: | ||
if self.strategy == self.STRATEGY_ALL: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Try accessing the constants as |
||
return True | ||
return False | ||
|
||
def visit_unbound_type(self, t: UnboundType) -> bool: | ||
return self.query_types(t.args) | ||
|
||
def visit_type_list(self, t: TypeList) -> bool: | ||
return self.query_types(t.items) | ||
|
||
def visit_callable_argument(self, t: CallableArgument) -> bool: | ||
return t.typ.accept(self) | ||
|
||
def visit_any(self, t: AnyType) -> bool: | ||
return self.bool_strategy_empty() | ||
|
||
def visit_uninhabited_type(self, t: UninhabitedType) -> bool: | ||
return self.bool_strategy_empty() | ||
|
||
def visit_none_type(self, t: NoneType) -> bool: | ||
return self.bool_strategy_empty() | ||
|
||
def visit_erased_type(self, t: ErasedType) -> bool: | ||
return self.bool_strategy_empty() | ||
|
||
def visit_deleted_type(self, t: DeletedType) -> bool: | ||
return self.bool_strategy_empty() | ||
|
||
def visit_type_var(self, t: TypeVarType) -> bool: | ||
return self.query_types([t.upper_bound] + t.values) | ||
|
||
def visit_partial_type(self, t: PartialType) -> bool: | ||
return self.bool_strategy_empty() | ||
|
||
def visit_instance(self, t: Instance) -> bool: | ||
return self.query_types(t.args) | ||
|
||
def visit_callable_type(self, t: CallableType) -> bool: | ||
# FIX generics | ||
return self.query_types(t.arg_types + [t.ret_type]) | ||
|
||
def visit_tuple_type(self, t: TupleType) -> bool: | ||
return self.query_types(t.items) | ||
|
||
def visit_typeddict_type(self, t: TypedDictType) -> bool: | ||
return self.query_types(t.items.values()) | ||
|
||
def visit_raw_expression_type(self, t: RawExpressionType) -> bool: | ||
return self.bool_strategy_empty() | ||
|
||
def visit_literal_type(self, t: LiteralType) -> bool: | ||
return self.bool_strategy_empty() | ||
|
||
def visit_star_type(self, t: StarType) -> bool: | ||
return t.type.accept(self) | ||
|
||
def visit_union_type(self, t: UnionType) -> bool: | ||
return self.query_types(t.items) | ||
|
||
def visit_overloaded(self, t: Overloaded) -> bool: | ||
return self.query_types(t.items()) | ||
|
||
def visit_type_type(self, t: TypeType) -> bool: | ||
return t.item.accept(self) | ||
|
||
def visit_ellipsis_type(self, t: EllipsisType) -> bool: | ||
return self.bool_strategy_empty() | ||
|
||
def visit_placeholder_type(self, t: PlaceholderType) -> bool: | ||
return self.query_types(t.args) | ||
|
||
def visit_type_alias_type(self, t: TypeAliasType) -> bool: | ||
return get_proper_type(t).accept(self) | ||
|
||
def query_types(self, types: Iterable[Type]) -> bool: | ||
"""Perform a query for a list of types. | ||
|
||
Use the strategy to combine the results. | ||
Skip type aliases already visited types to avoid infinite recursion. | ||
""" | ||
for t in types: | ||
if isinstance(t, TypeAliasType): | ||
# Avoid infinite recursion for recursive type aliases. | ||
if t in self.seen_aliases: | ||
continue | ||
self.seen_aliases.add(t) | ||
res = t.accept(self) | ||
if res and self.strategy == self.STRATEGY_ANY: | ||
return True | ||
elif not res and self.strategy == self.STRATEGY_ALL: | ||
return False | ||
return self.strategy == self.STRATEGY_ALL |
+7 −0 | README.md | |
+1 −1 | stdlib/2/__builtin__.pyi | |
+1 −1 | stdlib/2and3/builtins.pyi | |
+13 −0 | stdlib/2and3/ftplib.pyi | |
+11 −6 | stdlib/2and3/logging/handlers.pyi | |
+1 −1 | stdlib/2and3/pstats.pyi | |
+2 −2 | stdlib/3/io.pyi | |
+0 −2 | tests/stubtest_whitelists/py36.txt | |
+0 −1 | tests/stubtest_whitelists/py39.txt |
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.
Related: #9602
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.
thanks for the headsup, I added the decorator for
allow_interpreted_subclasses=True