Skip to content

Commit c40f0a0

Browse files
committed
Merge branch 'conditional-definition'
Work towards #649. Still only addresses a subset of issues.
2 parents cbd0300 + 0d2c577 commit c40f0a0

10 files changed

+388
-164
lines changed

mypy/checker.py

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
LITERAL_TYPE, BreakStmt, ContinueStmt, ComparisonExpr, StarExpr,
2222
YieldFromExpr, YieldFromStmt, NamedTupleExpr, SetComprehension,
2323
DictionaryComprehension, ComplexExpr, EllipsisExpr, TypeAliasExpr,
24-
RefExpr, YieldExpr, BackquoteExpr, CONTRAVARIANT, COVARIANT
24+
RefExpr, YieldExpr, BackquoteExpr, ImportFrom, ImportAll, ImportBase,
25+
CONTRAVARIANT, COVARIANT
2526
)
2627
from mypy.nodes import function_type, method_type, method_type_with_fallback
2728
from mypy import nodes
@@ -933,6 +934,25 @@ def check_compatibility(self, name: str, base1: TypeInfo,
933934
self.msg.base_class_definitions_incompatible(name, base1, base2,
934935
ctx)
935936

937+
def visit_import_from(self, node: ImportFrom) -> Type:
938+
self.check_import(node)
939+
940+
def visit_import_all(self, node: ImportAll) -> Type:
941+
self.check_import(node)
942+
943+
def check_import(self, node: ImportBase) -> Type:
944+
for assign in node.assignments:
945+
lvalue = assign.lvalues[0]
946+
lvalue_type, _, __ = self.check_lvalue(lvalue)
947+
if lvalue_type is None:
948+
# TODO: This is broken.
949+
lvalue_type = AnyType()
950+
message = '{} "{}"'.format(messages.INCOMPATIBLE_IMPORT_OF,
951+
cast(NameExpr, assign.rvalue).name)
952+
self.check_simple_assignment(lvalue_type, assign.rvalue, node,
953+
msg=message, lvalue_name='local name',
954+
rvalue_name='imported name')
955+
936956
#
937957
# Statements
938958
#
@@ -1040,9 +1060,7 @@ def check_multi_assignment(self, lvalues: List[Node],
10401060
context: Context,
10411061
infer_lvalue_type: bool = True,
10421062
msg: str = None) -> None:
1043-
"""Check the assignment of one rvalue to a number of lvalues
1044-
for example from a ListExpr or TupleExpr.
1045-
"""
1063+
"""Check the assignment of one rvalue to a number of lvalues."""
10461064

10471065
if not msg:
10481066
msg = messages.INCOMPATIBLE_TYPES_IN_ASSIGNMENT
@@ -1284,7 +1302,9 @@ def narrow_type_from_binder(self, expr: Node, known_type: Type) -> Type:
12841302

12851303
def check_simple_assignment(self, lvalue_type: Type, rvalue: Node,
12861304
context: Node,
1287-
msg: str = messages.INCOMPATIBLE_TYPES_IN_ASSIGNMENT) -> Type:
1305+
msg: str = messages.INCOMPATIBLE_TYPES_IN_ASSIGNMENT,
1306+
lvalue_name: str = 'variable',
1307+
rvalue_name: str = 'expression') -> Type:
12881308
if self.is_stub and isinstance(rvalue, EllipsisExpr):
12891309
# '...' is always a valid initializer in a stub.
12901310
return AnyType()
@@ -1293,7 +1313,8 @@ def check_simple_assignment(self, lvalue_type: Type, rvalue: Node,
12931313
if self.typing_mode_weak():
12941314
return rvalue_type
12951315
self.check_subtype(rvalue_type, lvalue_type, context, msg,
1296-
'expression has type', 'variable has type')
1316+
'{} has type'.format(rvalue_name),
1317+
'{} has type'.format(lvalue_name))
12971318
return rvalue_type
12981319

12991320
def check_indexed_assignment(self, lvalue: IndexExpr,

mypy/messages.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
"Generic type not valid as an expression any more (use '# type:' comment instead)"
6969
RETURN_TYPE_CANNOT_BE_CONTRAVARIANT = "Cannot use a contravariant type variable as return type"
7070
FUNCTION_PARAMETER_CANNOT_BE_COVARIANT = "Cannot use a covariant type variable as a parameter"
71+
INCOMPATIBLE_IMPORT_OF = "Incompatible import of"
7172

7273

7374
class MessageBuilder:

mypy/nodes.py

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ def get_line(self) -> int: pass
4343
UNBOUND_TVAR = 4 # type: int
4444
BOUND_TVAR = 5 # type: int
4545
TYPE_ALIAS = 6 # type: int
46+
# Placeholder for a name imported via 'from ... import'. Second phase of
47+
# semantic will replace this the actual imported reference. This is
48+
# needed so that we can detect whether a name has been imported during
49+
UNBOUND_IMPORTED = 7 # type: int
4650

4751

4852
LITERAL_YES = 2
@@ -171,6 +175,16 @@ def is_package_init_file(self) -> bool:
171175
class ImportBase(Node):
172176
"""Base class for all import statements."""
173177
is_unreachable = False
178+
# If an import replaces existing definitions, we construct dummy assignment
179+
# statements that assign the imported names to the names in the current scope,
180+
# for type checking purposes. Example:
181+
#
182+
# x = 1
183+
# from m import x <-- add assignment representing "x = m.x"
184+
assignments = None # type: List[AssignmentStmt]
185+
186+
def __init__(self) -> None:
187+
self.assignments = []
174188

175189

176190
class Import(ImportBase):
@@ -179,6 +193,7 @@ class Import(ImportBase):
179193
ids = None # type: List[Tuple[str, Optional[str]]] # (module id, as id)
180194

181195
def __init__(self, ids: List[Tuple[str, Optional[str]]]) -> None:
196+
super().__init__()
182197
self.ids = ids
183198

184199
def accept(self, visitor: NodeVisitor[T]) -> T:
@@ -191,6 +206,7 @@ class ImportFrom(ImportBase):
191206
names = None # type: List[Tuple[str, Optional[str]]] # Tuples (name, as name)
192207

193208
def __init__(self, id: str, relative: int, names: List[Tuple[str, Optional[str]]]) -> None:
209+
super().__init__()
194210
self.id = id
195211
self.names = names
196212
self.relative = relative
@@ -203,6 +219,7 @@ class ImportAll(ImportBase):
203219
"""from m import *"""
204220

205221
def __init__(self, id: str, relative: int) -> None:
222+
super().__init__()
206223
self.id = id
207224
self.relative = relative
208225

@@ -219,12 +236,13 @@ class FuncBase(SymbolNode):
219236
# If method, reference to TypeInfo
220237
info = None # type: TypeInfo
221238
is_property = False
239+
_fullname = None # type: str # Name with module prefix
222240

223241
@abstractmethod
224242
def name(self) -> str: pass
225243

226244
def fullname(self) -> str:
227-
return self.name()
245+
return self._fullname
228246

229247
def is_method(self) -> bool:
230248
return bool(self.info)
@@ -238,7 +256,6 @@ class OverloadedFuncDef(FuncBase):
238256
"""
239257

240258
items = None # type: List[Decorator]
241-
_fullname = None # type: str
242259

243260
def __init__(self, items: List['Decorator']) -> None:
244261
self.items = items
@@ -247,9 +264,6 @@ def __init__(self, items: List['Decorator']) -> None:
247264
def name(self) -> str:
248265
return self.items[1].func.name()
249266

250-
def fullname(self) -> str:
251-
return self._fullname
252-
253267
def accept(self, visitor: NodeVisitor[T]) -> T:
254268
return visitor.visit_overloaded_func_def(self)
255269

@@ -345,7 +359,6 @@ class FuncDef(FuncItem):
345359
This is a non-lambda function defined using 'def'.
346360
"""
347361

348-
_fullname = None # type: str # Name with module prefix
349362
is_decorated = False
350363
is_conditional = False # Defined conditionally (within block)?
351364
is_abstract = False
@@ -363,9 +376,6 @@ def __init__(self,
363376
def name(self) -> str:
364377
return self._name
365378

366-
def fullname(self) -> str:
367-
return self._fullname
368-
369379
def accept(self, visitor: NodeVisitor[T]) -> T:
370380
return visitor.visit_func_def(self)
371381

@@ -1650,11 +1660,19 @@ def __str__(self) -> str:
16501660

16511661

16521662
class SymbolTableNode:
1653-
# LDEF/GDEF/MDEF/UNBOUND_TVAR/TVAR/...
1663+
# Kind of node. Possible values:
1664+
# - LDEF: local definition (of any kind)
1665+
# - GDEF: global (module-level) definition
1666+
# - MDEF: class member definition
1667+
# - UNBOUND_TVAR: TypeVar(...) definition, not bound
1668+
# - TVAR: type variable in a bound scope (generic function / generic clas)
1669+
# - MODULE_REF: reference to a module
1670+
# - TYPE_ALIAS: type alias
1671+
# - UNBOUND_IMPORTED: temporary kind for imported names
16541672
kind = None # type: int
16551673
# AST node of definition (FuncDef/Var/TypeInfo/Decorator/TypeVarExpr,
16561674
# or None for a bound type variable).
1657-
node = None # type: SymbolNode
1675+
node = None # type: Optional[SymbolNode]
16581676
# Type variable id (for bound type variables only)
16591677
tvar_id = 0
16601678
# Module id (e.g. "foo.bar") or None

0 commit comments

Comments
 (0)