8000 Support for functions producing generic functions by sixolet · Pull Request #3113 · python/mypy · GitHub
[go: up one dir, main page]

Skip to content

Support for functions producing generic functions #3113

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

Merged
merged 40 commits into from
Apr 21, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
679a172
Make it an error to use a class-attribute type var outside a type
sixolet Apr 1, 2017
74e9262
Fix the error message to handle more cases
sixolet Apr 1, 2017
5470c77
oops accidentally changed a file
sixolet Apr 1, 2017
12def8b
Typo fix
sixolet Apr 1, 2017
52782fe
more checking things in tests
sixolet Apr 1, 2017
0184595
Change error message
sixolet Apr 1, 2017
757a6b8
Change error message
sixolet Apr 1, 2017
57bca93
Make TypeQuery more general, handling nonboolean queries.
sixolet Mar 29, 2017
3b8bc23
Remove redundant empty check
sixolet Mar 29, 2017
82da335
Some types in the visitor were missing recursion
sixolet Mar 30, 2017
9f12644
Add ellipsis type handler to TypeQuery
sixolet Mar 30, 2017
8cf4fa6
Typechecks, but gives spurious error. Need to move variable binding?
sixolet Mar 30, 2017
2634277
Small change: Move tvar id generation nearer type analysis
sixolet Mar 30, 2017
30dba10
Even nearer
sixolet Mar 30, 2017
7b4f9f2
Replace the concept of BOUND_TVAR and UNBOUND_TVAR as mutative flags …
sixolet Mar 30, 2017
daf9592
Mid debugging gotta push now
sixolet Mar 30, 2017
826b9cf
Checkpoint. Most tests pass, classvar type aliases not yet
sixolet Mar 31, 2017
4141f94
forgot this file
sixolet Mar 31, 2017
58e3a2a
Fix aliasing to not bind type vars within a callable while aliasing
sixolet Mar 31, 2017
c061a09
removed some now-unused code
sixolet Apr 1, 2017
603013d
my own code review, first pass
sixolet Apr 1, 2017
c6fec63
Use type var query instead of another query fn
sixolet Apr 2, 2017
f46d52a
Tighten code
sixolet Apr 2, 2017
ea21337
fix some types
sixolet Apr 2, 2017
80d62a7
Use same type alias throughout
sixolet Apr 2, 2017
eaf8c0d
Make semanal tests pass. Delete one I think is wrong
sixolet Apr 2, 2017
5009a2b
Generator types for generators
sixolet Apr 2, 2017
7c64464
Lint and cleanups
sixolet Apr 3, 2017
a866d0f
Test for using generic function return as a decorator
sixolet Apr 11, 2017
9d5c630
Merge upstream
sixolet Apr 12, 2017
4683d70
Merge lint
sixolet Apr 12, 2017
cbb3cb0
Merged master; adapted code to use better TypeQuery
sixolet Apr 12, 2017
da0d936
More tests for nested and multi-type-var situations
sixolet Apr 12, 2017
edbecb0
Fixed type variable scoping rules to conform to PEP484 better
sixolet Apr 14, 2017
21d8b13
Move the typevars that are prohibited due to being bound by the class…
sixolet Apr 17, 2017
214aebc
More doc
sixolet Apr 17, 2017
5405646
Jukka comments
sixolet Apr 20, 2017
a25e926
one more
sixolet Apr 20, 2017
4aaab6c
get imports right
sixolet Apr 20, 2017
fd153ad
Merge branch 'master' into second-order-decorator
sixolet Apr 20, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Tighten code
  • Loading branch information
sixolet committed Apr 2, 2017
commit f46d52a9f8225ac4c84a866965f7a6542681c4c6
31 changes: 4 additions & 27 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
from mypy.nodes import implicit_module_attrs
from mypy.typeanal import (
TypeAnalyser, TypeAnalyserPass3, analyze_type_alias, no_subscript_builtin_alias,
TypeVariableQuery,
)
from mypy.exprtotype import expr_to_unanalyzed_type, TypeTranslationError
from mypy.sametypes import is_same_type
Expand Down Expand Up @@ -375,10 +376,8 @@ def update_function_type_variables(self, fun_type: CallableType, defn: Context)
Update the signature of defn to contain type variable definitions
if defn is generic.
"""

a = self.type_analyzer(tvar_scope=TypeVarScope(self.tvar_scope))
variables = a.bind_function_type_variables(fun_type, defn)
fun_type.variables = variables
fun_type.variables = a.bind_function_type_variables(fun_type, defn)

def visit_overloaded_func_def(self, defn: OverloadedFuncDef) -> None:
# OverloadedFuncDef refers to any legitimate situation where you have
Expand Down Expand Up @@ -503,7 +502,6 @@ def analyze_function(self, defn: FuncItem) -> None:
self.check_function_signature(defn)
if isinstance(defn, FuncDef):
defn.type = set_callable_name(defn.type, defn)

for arg in defn.arguments:
if arg.initializer:
arg.initializer.accept(self)
Expand Down Expand Up @@ -556,12 +554,6 @@ def check_classvar_in_signature(self, typ: Type) -> None:
# Show only one error per signature
break

def type_var_names(self) -> Set[str]:
if not self.type:
return set()
else:
return set(self.type.type_vars)

def check_function_signature(self, fdef: FuncItem) -> None:
sig = fdef.type
assert isinstance(sig, CallableType)
Expand Down Expand Up @@ -754,23 +746,8 @@ def get_all_bases_tvars(self, defn: ClassDef,
except TypeTranslationError:
# This error will be caught later.
continue
tvars.extend(self.get_tvars(base))
return self.remove_dups(tvars)

def get_tvars(self, tp: Type) -> List[Tuple[str, TypeVarExpr]]:
tvars = [] # type: List[Tuple[str, TypeVarExpr]]
if isinstance(tp, UnboundType):
tp_args = tp.args
elif isinstance(tp, TypeList):
tp_args = tp.items
else:
return tvars
for arg in tp_args:
tvar = self.analyze_unbound_tvar(arg)
if tvar:
tvars.append(tvar)
else:
tvars.extend(self.get_tvars(arg))
tvars.extend(base.accept(TypeVariableQuery(
self.lookup_qualified, self.tvar_scope)))
return self.remove_dups(tvars)

def remove_dups(self, tvars: List[T]) -> List[T]:
Expand Down
15 changes: 5 additions & 10 deletions mypy/tvar_scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@ class TypeVarScope:
def __init__(self, parent: Optional['TypeVarScope'] = None):
self.scope = {} # type: Dict[str, TypeVarDef]
self.parent = parent
if parent is None:
self.func_id = 0
self.class_id = 0
else:
self.func_id = 0
self.class_id = 0
if parent is not None:
self.func_id = parent.func_id
self.class_id = parent.class_id

Expand All @@ -32,10 +31,7 @@ def _bind(self, name: str, tvar_expr: TypeVarExpr, i: int):
self.scope[tvar_expr.fullname()] = tvar_def

def get_binding(self, item: Union[str, SymbolTableNode]):
if isinstance(item, SymbolTableNode):
fullname = item.fullname
else:
fullname = item
fullname = item.fullname if isinstance(item, SymbolTableNode) else item
if fullname in self.scope:
return self.scope[fullname]
elif self.parent is not None:
Expand All @@ -47,5 +43,4 @@ def __str__(self):
me = ", ".join('{}: {}`{}'.format(k, v.name, v.id) for k, v in self.scope.items())
if self.parent is None:
return me
else:
return "{} <- {}".format(str(self.parent), me)
return "{} <- {}".format(str(self.parent), me)
14 changes: 5 additions & 9 deletions mypy/typeanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@ def visit_unbound_type(self, t: UnboundType) -> Type:
not sym.normalized and not self.allow_unnormalized):
self.fail(no_subscript_builtin_alias(fullname), t)
if sym.kind == TVAR and self.tvar_scope.get_binding(sym) is not None:
# XXX: Don't fetch twice
tvar_def = self.tvar_scope.get_binding(sym)
if len(t.args) > 0:
self.fail('Type variable &quo 8000 t;{}" used with arguments'.format(
Expand Down Expand Up @@ -259,7 +258,7 @@ def get_type_var_names(self, tp: Type) -> List[str]:
"""
return [name for name, _
in tp.accept(TypeVariableQuery(self.lookup, self.tvar_scope,
include_callables=True,include_bound=True))]
include_callables=True, include_bound=True))]

def get_tvar_name(self, t: Type) -> Optional[str]:
if not isinstance(t, UnboundType):
Expand Down Expand Up @@ -411,11 +410,12 @@ def infer_type_variables(self,
names = [] # type: List[str]
tvars = [] # type: List[TypeVarExpr]
for arg in type.arg_types:
for name, tvar_expr in self.find_type_variables_in_type(arg, include_callables=True):
for name, tvar_expr in arg.accept(TypeVariableQuery(self.lookup, self.tvar_scope)):
if name not in names:
names.append(name)
tvars.append(tvar_expr)
for name, tvar_expr in self.find_type_variables_in_type(type.ret_type, include_callables=False):
for name, tvar_expr in type.ret_type.accept(
TypeVariableQuery(self.lookup, self.tvar_scope, include_callables=False)):
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other punchline of this diff: when you're finding the type variables that a function has, don't include the tvars you only find in a Callable in the return type. Those belong to that Callable

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add code comment mentioning what you said above.

if name not in names:
names.append(name)
tvars.append(tvar_expr)
Expand All @@ -441,10 +441,6 @@ def bind_function_type_variables(self, fun_type: CallableType, defn: Context) ->
def is_defined_type_var(self, tvar: str, context: Context) -> bool:
return self.tvar_scope.get_binding(self.lookup(tvar, context)) is not None

def find_type_variables_in_type(self, type: Type, *, include_callables: bool) -> List[Tuple[str, TypeVarExpr]]:
ret = type.accept(TypeVariableQuery(self.lookup, self.tvar_scope, include_callables))
return ret

def anal_array(self, a: List[Type], nested: bool = True) -> List[Type]:
res = [] # type: List[Type]
for t in a:
Expand Down Expand Up @@ -619,8 +615,8 @@ class TypeVariableQuery(TypeQuery[TypeVarList]):
def __init__(self,
lookup: Callable[[str, Context], SymbolTableNode],
4F4B scope: 'TypeVarScope',
include_callables: bool,
*,
include_callables: bool = True,
include_bound: bool = False):
self.include_callables = include_callables
self.lookup = lookup
Expand Down
0