Skip to content

Commit f799055

Browse files
committed
Fix more typeshed circular dependency issues.
Fixes pytype test failures seen in python/typeshed#7676 by doing more extensive re-resolving of external types upon encountering circular imports. This also lets us remove a previous hack that we put in to partially work around this problem. PiperOrigin-RevId: 444427690
1 parent d8f5a92 commit f799055

File tree

3 files changed

+11
-19
lines changed

3 files changed

+11
-19
lines changed

pytype/load_pytd.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -312,8 +312,7 @@ def resolve_builtin_types(self, mod_ast, *, lookup_ast=None):
312312
visitors.ExpandCompatibleBuiltins(self.builtins_ast))
313313
return mod_ast
314314

315-
def resolve_external_types(self, mod_ast, module_map, aliases, *,
316-
mod_name=None):
315+
def resolve_external_types(self, mod_ast, module_map, aliases, *, mod_name):
317316
name = mod_name or mod_ast.name
318317
try:
319318
mod_ast = mod_ast.Visit(visitors.LookupExternalTypes(
@@ -616,10 +615,15 @@ def finish_and_verify_ast(self, mod_ast):
616615
if mod_ast:
617616
try:
618617
self._resolver.verify(mod_ast)
619-
except BadDependencyError:
618+
except (BadDependencyError, visitors.ContainerError):
620619
# In the case of a circular import, an external type may be left
621-
# unresolved. As long as the module containing the unresolved type does
622-
# not also contain a circular import, an extra lookup should resolve it.
620+
# unresolved, so we re-resolve lookups in this module and its direct
621+
# dependencies. Technically speaking, we should re-resolve all
622+
# transitive imports, but lookups are expensive.
623+
dependencies = self._resolver.collect_dependencies(mod_ast)
624+
for k in dependencies:
625+
self._modules[k].ast = self._resolve_external_types(
626+
self._modules[k].ast)
623627
mod_ast = self._resolve_external_types(mod_ast)
624628
self._resolver.verify(mod_ast)
625629
return mod_ast

pytype/pytd/pytd.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -644,10 +644,7 @@ def IsContainer(t):
644644
for p in t.bases:
645645
if isinstance(p, GenericType):
646646
base = p.base_type
647-
# We need to check for Generic and Protocol again here because base may
648-
# not yet have been resolved to a ClassType.
649-
if (base.name in ('typing.Generic', 'typing.Protocol') or
650-
isinstance(base, ClassType) and IsContainer(base.cls)):
647+
if isinstance(base, ClassType) and IsContainer(base.cls):
651648
return True
652649
return False
653650

pytype/pytd/visitors.py

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1594,16 +1594,7 @@ def EnterGenericType(self, node):
15941594
raise ContainerError("Name %s must be defined as a TypeVar" % t.name)
15951595
elif not isinstance(node, (pytd.CallableType, pytd.TupleType)):
15961596
actual_param_count = len(node.parameters)
1597-
if actual_param_count and not base_type.cls.template:
1598-
# This AdjustTypeParameters() call is needed because we validate nodes
1599-
# before their type parameters have been adjusted in some circular
1600-
# import cases. The result of this adjustment is not saved because it
1601-
# may not be accurate if the container is only partially resolved, but
1602-
# it's good enough to avoid some spurious container validation errors.
1603-
cls = base_type.cls.Visit(AdjustTypeParameters())
1604-
else:
1605-
cls = base_type.cls
1606-
max_param_count = len(cls.template)
1597+
max_param_count = len(base_type.cls.template)
16071598
if actual_param_count > max_param_count:
16081599
raise ContainerError(
16091600
"Too many parameters on %s: expected %s, got %s" % (

0 commit comments

Comments
 (0)