Skip to content

Commit

Permalink
Untangle most of the big import cycle (#7397)
Browse files Browse the repository at this point in the history
Currently mypy has a 47 module import cycle, which is not great.

This untangles things, leaving the following cycles:
 * Size 2: mypy.build, mypy.semanal_main
 * Size 2: mypy.plugin, mypy.interpreted_plugin
 * Size 4: mypy.checker, mypy.checkexpr, mypy.checkmember, mypy.checkstrformat. The type checker itself. This could be untangled by having the sub-checkers operate on an interface of the checker, like the semantic analyzer does.
 * Size 5: mypy.nodes, mypy.types, mypy.type_visitor, mypy.visitors, mypy.strconv. This can't really be untangled.
 * Size 14: mypy.messages, mypy.expandtype, mypy.erasetype, mypy.typevars, mypy.maptype, mypy.typeops, mypy.sametypes, mypy.subtypes, mypy.constraints, mypy.applytype, mypy.join, mypy.meet, mypy.solve, mypy.infer. The "typeops tangle". Untangling a little further is probably possible but will require some thought and actual logic changes to code. Messages can definitely be removed.

The untangling done here is pretty simple and consists of two main parts:
 * Remove `mypy.types`'s dependency on the type ops tangle by moving all of its functions that depend on it into `mypy.typeops`.
 * Remove the dependency of the type ops tangle on the type checker by moving some functions from `mypy.checkmember` to `mypy.typeops`.

Fixes #93. Fixes #6329.
  • Loading branch information
msullivan authored Aug 27, 2019
1 parent ddec163 commit 5cd1dab
Show file tree
Hide file tree
Showing 20 changed files with 482 additions and 435 deletions.
4 changes: 2 additions & 2 deletions mypy/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
from mypy.nodes import MypyFile, ImportBase, Import, ImportFrom, ImportAll, SymbolTable
from mypy.semanal_pass1 import SemanticAnalyzerPreAnalysis
from mypy.semanal import SemanticAnalyzer
from mypy.semanal_main import semantic_analysis_for_scc
import mypy.semanal_main
from mypy.checker import TypeChecker
from mypy.indirection import TypeIndirectionVisitor
from mypy.errors import Errors, CompileError, ErrorInfo, report_internal_error
Expand Down Expand Up @@ -2961,7 +2961,7 @@ def process_stale_scc(graph: Graph, scc: List[str], manager: BuildManager) -> No
# SemanticAnalyzerPass2.add_builtin_aliases for details.
typing_mod = graph['typing'].tree
assert typing_mod, "The typing module was not parsed"
semantic_analysis_for_scc(graph, scc, manager.errors)
mypy.semanal_main.semantic_analysis_for_scc(graph, scc, manager.errors)

# Track what modules aren't yet done so we can finish them as soon
# as possible, saving memory.
Expand Down
42 changes: 23 additions & 19 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
Type, AnyType, CallableType, FunctionLike, Overloaded, TupleType, TypedDictType,
Instance, NoneType, strip_type, TypeType, TypeOfAny,
UnionType, TypeVarId, TypeVarType, PartialType, DeletedType, UninhabitedType, TypeVarDef,
true_only, false_only, function_type, is_named_instance, union_items, TypeQuery, LiteralType,
function_type, is_named_instance, union_items, TypeQuery, LiteralType,
is_optional, remove_optional, TypeTranslator, StarType, get_proper_type, ProperType,
get_proper_types, is_literal_type
)
Expand All @@ -45,8 +45,12 @@
)
import mypy.checkexpr
from mypy.checkmember import (
map_type_from_supertype, bind_self, erase_to_bound, type_object_type,
analyze_descriptor_access,
analyze_descriptor_access, type_object_type,
)
from mypy.typeops import (
map_type_from_supertype, bind_self, erase_to_bound, make_simplified_union,
erase_def_to_union_or_bound, erase_to_union_or_bound,
true_only, false_only,
)
from mypy import message_registry
from mypy.subtypes import (
Expand Down Expand Up @@ -1539,7 +1543,7 @@ def get_op_other_domain(self, tp: FunctionLike) -> Optional[Type]:
raw_items = [self.get_op_other_domain(it) for it in tp.items()]
items = [it for it in raw_items if it]
if items:
return UnionType.make_simplified_union(items)
return make_simplified_union(items)
return None
else:
assert False, "Need to check all FunctionLike subtypes here"
Expand Down Expand Up @@ -1973,7 +1977,7 @@ def check_assignment(self, lvalue: Lvalue, rvalue: Expression, infer_lvalue_type
if not self.current_node_deferred:
# Partial type can't be final, so strip any literal values.
rvalue_type = remove_instance_last_known_values(rvalue_type)
inferred_type = UnionType.make_simplified_union(
inferred_type = make_simplified_union(
[rvalue_type, NoneType()])
self.set_inferred_type(var, lvalue, inferred_type)
else:
Expand Down Expand Up @@ -2417,7 +2421,7 @@ def check_multi_assignment_from_union(self, lvalues: List[Expression], rvalue: E
rv_type=item, undefined_rvalue=True)
for t, lv in zip(transposed, self.flatten_lvalues(lvalues)):
t.append(self.type_map.pop(lv, AnyType(TypeOfAny.special_form)))
union_types = tuple(UnionType.make_simplified_union(col) for col in transposed)
union_types = tuple(make_simplified_union(col) for col in transposed)
for expr, items in assignments.items():
# Bind a union of types collected in 'assignments' to every expression.
if isinstance(expr, StarExpr):
Expand All @@ -2432,8 +2436,8 @@ def check_multi_assignment_from_union(self, lvalues: List[Expression], rvalue: E

types, declared_types = zip(*clean_items)
self.binder.assign_type(expr,
UnionType.make_simplified_union(list(types)),
UnionType.make_simplified_union(list(declared_types)),
make_simplified_union(list(types)),
make_simplified_union(list(declared_types)),
False)
for union, lv in zip(union_types, self.flatten_lvalues(lvalues)):
# Properly store the inferred types.
Expand Down Expand Up @@ -2856,9 +2860,9 @@ def try_infer_partial_type_from_indexed_assignment(
# TODO: Don't infer things twice.
key_type = self.expr_checker.accept(lvalue.index)
value_type = self.expr_checker.accept(rvalue)
full_key_type = UnionType.make_simplified_union(
full_key_type = make_simplified_union(
[key_type, var.type.inner_types[0]])
full_value_type = UnionType.make_simplified_union(
full_value_type = make_simplified_union(
[value_type, var.type.inner_types[1]])
if (is_valid_inferred_type(full_key_type) and
is_valid_inferred_type(full_value_type)):
Expand Down Expand Up @@ -3176,7 +3180,7 @@ def check_except_handler_test(self, n: Expression) -> Type:

all_types.append(exc_type)

return UnionType.make_simplified_union(all_types)
return make_simplified_union(all_types)

def get_types_from_except_handler(self, typ: Type, n: Expression) -> List[Type]:
"""Helper for check_except_handler_test to retrieve handler types."""
Expand Down Expand Up @@ -3524,7 +3528,7 @@ def partition_by_callable(self, typ: Type,
# do better.
# If it is possible for the false branch to execute, return the original
# type to avoid losing type information.
callables, uncallables = self.partition_by_callable(typ.erase_to_union_or_bound(),
callables, uncallables = self.partition_by_callable(erase_to_union_or_bound(typ),
unsound_partition)
uncallables = [typ] if len(uncallables) else []
return callables, uncallables
Expand Down Expand Up @@ -4087,7 +4091,7 @@ def conditional_type_map(expr: Expression,
if it was not the proposed type, if any. None means bot, {} means top"""
if proposed_type_ranges:
proposed_items = [type_range.item for type_range in proposed_type_ranges]
proposed_type = UnionType.make_simplified_union(proposed_items)
proposed_type = make_simplified_union(proposed_items)
if current_type:
if isinstance(proposed_type, AnyType):
# We don't really know much about the proposed type, so we shouldn't
Expand Down Expand Up @@ -4170,7 +4174,7 @@ def builtin_item_type(tp: Type) -> Optional[Type]:
return tp.args[0]
elif isinstance(tp, TupleType) and all(not isinstance(it, AnyType)
for it in get_proper_types(tp.items)):
return UnionType.make_simplified_union(tp.items) # this type is not externally visible
return make_simplified_union(tp.items) # this type is not externally visible
elif isinstance(tp, TypedDictType):
# TypedDict always has non-optional string keys. Find the key type from the Mapping
# base class.
Expand Down Expand Up @@ -4221,7 +4225,7 @@ def or_conditional_maps(m1: TypeMap, m2: TypeMap) -> TypeMap:
for n1 in m1:
for n2 in m2:
if literal_hash(n1) == literal_hash(n2):
result[n1] = UnionType.make_simplified_union([m1[n1], m2[n2]])
result[n1] = make_simplified_union([m1[n1], m2[n2]])
return result


Expand Down Expand Up @@ -4435,7 +4439,7 @@ def overload_can_never_match(signature: CallableType, other: CallableType) -> bo
# the below subtype check and (surprisingly?) `is_proper_subtype(Any, Any)`
# returns `True`.
# TODO: find a cleaner solution instead of this ad-hoc erasure.
exp_signature = expand_type(signature, {tvar.id: tvar.erase_to_union_or_bound()
exp_signature = expand_type(signature, {tvar.id: erase_def_to_union_or_bound(tvar)
for tvar in signature.variables})
assert isinstance(exp_signature, CallableType)
return is_callable_compatible(exp_signature, other,
Expand Down Expand Up @@ -4689,7 +4693,7 @@ class Status(Enum):

if isinstance(typ, UnionType):
items = [try_expanding_enum_to_union(item, target_fullname) for item in typ.items]
return UnionType.make_simplified_union(items)
return make_simplified_union(items)
elif isinstance(typ, Instance) and typ.type.is_enum and typ.type.fullname() == target_fullname:
new_items = []
for name, symbol in typ.type.names.items():
Expand All @@ -4704,7 +4708,7 @@ class Status(Enum):
# only using CPython, but we might as well for the sake of full correctness.
if sys.version_info < (3, 7):
new_items.sort(key=lambda lit: lit.value)
return UnionType.make_simplified_union(new_items)
return make_simplified_union(new_items)
else:
return typ

Expand All @@ -4716,7 +4720,7 @@ def coerce_to_literal(typ: Type) -> ProperType:
typ = get_proper_type(typ)
if isinstance(typ, UnionType):
new_items = [coerce_to_literal(item) for item in typ.items]
return UnionType.make_simplified_union(new_items)
return make_simplified_union(new_items)
elif isinstance(typ, Instance) and typ.last_known_value:
return typ.last_known_value
else:
Expand Down
Loading

0 comments on commit 5cd1dab

Please sign in to comment.