Skip to content

Commit 7d92e6c

Browse files
committed
Delay finding names of arg construcors
1 parent 729da57 commit 7d92e6c

File tree

8 files changed

+131
-120
lines changed

8 files changed

+131
-120
lines changed

extensions/mypy_extensions.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,20 +98,26 @@ class Point2D(TypedDict):
9898
# Return type that indicates a function does not return
9999
class NoReturn: pass
100100

101-
def Arg(name=None, typ=Any):
101+
102+
def Arg(typ=Any, name=None):
102103
return typ
103104

104-
def DefaultArg(name=None, typ=Any):
105+
106+
def DefaultArg(typ=Any, name=None):
105107
return typ
106108

107-
def NamedArg(name=None, typ=Any):
109+
110+
def NamedArg(typ=Any, name=None):
108111
return typ
109112

110-
def DefaultNamedArg(name=None, typ=Any):
113+
114+
def DefaultNamedArg(typ=Any, name=None):
111115
return typ
112116

117+
113118
def StarArg(typ=Any):
114119
return typ
115120

121+
116122
def KwArg(typ=Any):
117123
return typ

mypy/exprtotype.py

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -56,19 +56,18 @@ def expr_to_unanalyzed_type(expr: Expression) -> Type:
5656
elif isinstance(expr, ListExpr):
5757
types = [] # type: List[Type]
5858
names = [] # type: List[Optional[str]]
59-
kinds = [] # type: List[int]
59+
constructors = [] # type: List[UnboundArgumentConstructor]
6060
for it in expr.items:
6161
if isinstance(it, CallExpr):
62-
if not isinstance(it.callee, NameExpr):
63-
raise TypeTranslationError()
64-
arg_const = it.callee.name
65-
try:
66-
kind = ARG_KINDS_BY_CONSTRUCTOR[arg_const]
67-
except KeyError:
62+
callee = it.callee
63+
if isinstance(callee, NameExpr):
64+
constructor = UnboundArgumentConstructor(callee.name)
65+
elif isinstance(it.callee, MemberExpr):
66+
constructor = UnboundArgumentConstructor(get_member_expr_fullname(callee))
67+
else:
6868
raise TypeTranslationError()
6969
name = None
7070
typ = AnyType(implicit=True) # type: Type
71-
star = arg_const in STAR_ARG_CONSTRUCTORS
7271
for i, arg in enumerate(it.args):
7372
if it.arg_names[i] is not None:
7473
if it.arg_names[i] == "name":
@@ -79,21 +78,21 @@ def expr_to_unanalyzed_type(expr: Expression) -> Type:
7978
continue
8079
else:
8180
raise TypeTranslationError()
82-
elif i == 0 and not star:
83-
name = _extract_str(arg)
84-
elif i == 1 and not star or i == 0 and star:
81+
elif i == 0:
8582
typ = expr_to_unanalyzed_type(arg)
83+
elif i == 1:
84+
name = _extract_str(arg)
8685
else:
8786
raise TypeTranslationError()
8887
names.append(name)
8988
types.append(typ)
90-
kinds.append(kind)
89+
constructors.append(constructor)
9190
else:
9291
types.append(expr_to_unanalyzed_type(it))
9392
names.append(None)
94-
kinds.append(ARG_POS)
95-
return ArgumentList(types, names, kinds,
96-
line=expr.line, column=expr.column)
93+
constructors.append(UnboundArgumentConstructor(None))
94+
return ArgumentList(types, names, constructors,
95+
line=expr.line, column=expr.column)
9796
elif isinstance(expr, (StrExpr, BytesExpr, UnicodeExpr)):
9897
# Parse string literal type.
9998
try:

mypy/fastparse.py

Lines changed: 35 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -953,59 +953,51 @@ def translate_expr_list(self, l: Sequence[ast3.AST]) -> List[Type]:
953953
def translate_argument_list(self, l: Sequence[ast3.AST]) -> ArgumentList:
954954
types = [] # type: List[Type]
955955
names = [] # type: List[Optional[str]]
956-
kinds = [] # type: List[int]
956+
constructors = [] # type: List[UnboundArgumentConstructor]
957957
for e in l:
958+
constructor = UnboundArgumentConstructor(None)
958959
if isinstance(e, ast3.Call):
960+
name = None # type: Optional[str]
959961
# Parse the arg constructor
960962
f = e.func
961-
if not isinstance(f, ast3.Name):
962-
raise FastParserError("Expected arg constructor name",
963-
e.lineno, e.col_offset)
964-
try:
965-
kind = ARG_KINDS_BY_CONSTRUCTOR[f.id]
966-
star = f.id in STAR_ARG_CONSTRUCTORS
967-
except KeyError:
968-
raise FastParserError("Unknown argument constructor {}".format(f.id),
969-
f.lineno, f.col_offset)
963+
if isinstance(f, ast3.Name):
964+
constructor = UnboundArgumentConstructor(f.id, line=self.line)
965+
elif isinstance(f, ast3.NameConstant):
966+
constructor = UnboundArgumentConstructor(str(f.value), line=self.line)
967+
elif isinstance(f, ast3.Attribute):
968+
before_dot = self.visit(f.value)
969+
if isinstance(before_dot, UnboundType):
970+
constructor = UnboundArgumentConstructor(
971+
"{}.{}".format(before_dot.name, f.attr), line = self.line)
970972

971-
name = None # type: Optional[str]
972973
typ = AnyType(implicit=True) # type: Type
973-
for i, arg in enumerate(e.args):
974-
if i == 0 and not star:
975-
name = _extract_str(arg)
976-
elif i == 1 and not star or i == 0 and star:
977-
try:
978-
typ = self.visit(arg)
979-
except TypeCommentParseError:
980-
raise FastParserError("Bad type for callable argument",
981-
arg.lineno, arg.col_offset)
982-
else:
983-
raise FastParserError("Too many arguments for argument constructor",
984-
f.lineno, f.col_offset)
974+
if len(e.args) >= 1:
975+
typ = self.visit(e.args[0])
976+
if len(e.args) >= 2:
977+
name = self._extract_str(e.args[1])
978+
if len(e.args) > 2:
979+
self.fail("Too many arguments for argument constructor",
980+
f.lineno, getattr(f, 'col_offset', -1))
985981
for k in e.keywords:
986982
value = k.value
987983
if k.arg == "name" and not star:
988-
name = _extract_str(value)
984+
name = self._extract_str(value)
989985
elif k.arg == "typ":
990-
try:
991-
typ = self.visit(value)
992-
except TypeCommentParseError:
993-
raise FastParserError("Bad type for callable argument",
994-
value.lineno, value.col_offset)
986+
typ = self.visit(value)
995987
else:
996-
raise FastParserError(
988+
self.fail(
997989
'Unexpected argument "{}" for argument constructor'.format(k.arg),
998-
value.lineno, value.col_offset)
990+
value.lineno, getattr(value, 'col_offset', -1))
999991

1000992
types.append(typ)
1001993
names.append(name)
1002-
kinds.append(kind)
994+
constructors.append(constructor)
1003995
else:
1004996
types.append(self.visit(e))
1005997
names.append(None)
1006-
kinds.append(ARG_POS)
998+
constructors.append(constructor)
1007999

1008-
return ArgumentList(types, names, kinds, line=self.line)
1000+
return ArgumentList(types, names, constructors, line=self.line)
10091001

10101002
def visit_Name(self, n: ast3.Name) -> Type:
10111003
return UnboundType(n.id, line=self.line)
@@ -1060,25 +1052,12 @@ def visit_Ellipsis(self, n: ast3.Ellipsis) -> Type:
10601052
def visit_List(self, n: ast3.List) -> Type:
10611053
return self.translate_argument_list(n.elts)
10621054

1063-
1064-
class TypeCommentParseError(Exception):
1065-
def __init__(self, msg: str, lineno: int, offset: int) -> None:
1066-
self.msg = msg
1067-
self.lineno = lineno
1068-
self.offset = offset
1069-
1070-
1071-
class FastParserError(TypeCommentParseError):
1072-
pass
1073-
1074-
1075-
def _extract_str(arg: ast3.expr) -> Optional[str]:
1076-
if isinstance(arg, ast3.Name) and arg.id == 'None':
1077-
return None
1078-
elif isinstance(arg, ast3.NameConstant) and arg.value is None:
1079-
return None
1080-
elif isinstance(arg, ast3.Str):
1081-
return arg.s
1082-
else:
1083-
raise FastParserError("Bad type for name of argument",
1084-
arg.lineno, arg.col_offset)
1055+
def _extract_str(arg: ast3.expr) -> Optional[str]:
1056+
if isinstance(arg, ast3.Name) and arg.id == 'None':
1057+
return None
1058+
elif isinstance(arg, ast3.NameConstant) and arg.value is None:
1059+
return None
1060+
elif isinstance(arg, ast3.Str):
1061+
return arg.s
1062+
else:
1063+
self.fail("Bad type for name of argument", arg.lineno, arg.col_offset)

mypy/messages.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,8 @@ def format(self, typ: Type, verbosity: int = 0) -> str:
216216
else:
217217
arg_strings.append("{}({}, {})".format(
218218
constructor,
219-
repr(arg_name),
220-
strip_quotes(self.format(arg_type))))
219+
strip_quotes(self.format(arg_type)),
220+
repr(arg_name)))
221221

222222
return 'Callable[[{}], {}]'.format(", ".join(arg_strings), return_type)
223223
else:

mypy/semanal.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -766,8 +766,8 @@ def get_tvars(self, tp: Type) -> List[Tuple[str, TypeVarExpr]]:
766766
tvars = [] # type: List[Tuple[str, TypeVarExpr]]
767767
if isinstance(tp, UnboundType):
768768
tp_args = tp.args
769-
elif isinstance(tp, TypeList):
770-
tp_args = tp.items
769+
elif isinstance(tp, ArgumentList):
770+
tp_args = tp.types
771771
else:
772772
return tvars
773773
for arg in tp_args:

mypy/typeanal.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@
2929
'typing.Union',
3030
}
3131

32+
ARG_KINDS_BY_CONSTRUCTOR = {
33+
'mypy_extensions.Arg': nodes.ARG_POS,
34+
'mypy_extensions.DefaultArg': nodes.ARG_OPT,
35+
'mypy_extensions.NamedArg': nodes.ARG_NAMED,
36+
'mypy_extensions.DefaultNamedArg': nodes.ARG_NAMED_OPT,
37+
'mypy_extensions.StarArg': nodes.ARG_STAR,
38+
'mypy_extensions.KwArg': nodes.ARG_STAR2,
39+
}
40+
3241

3342
def analyze_type_alias(node: Expression,
3443
lookup_func: Callable[[str, Context], SymbolTableNode],
@@ -330,6 +339,27 @@ def visit_ellipsis_type(self, t: EllipsisType) -> Type:
330339
def visit_type_type(self, t: TypeType) -> Type:
331340
return TypeType(t.item.accept(self), line=t.line)
332341

342+
def argument_list_to_kinds(self, a: ArgumentList) -> List[int]:
343+
res = [] # type: List[int]
344+
for constructor in a.constructors:
345+
if constructor.name is None:
346+
res.append(nodes.ARG_POS)
347+
else:
348+
sym = self.lookup(constructor.name, constructor)
349+
if sym.node is None:
350+
assert sym.kind == UNBOUND_IMPORTED
351+
self.fail("Argument constructor not found: {}".format(constructor.name),
352+
constructor)
353+
res.append(nodes.ARG_POS)
354+
continue
355+
fullname = sym.node.fullname()
356+
if fullname not in ARG_KINDS_BY_CONSTRUCTOR:
357+
self.fail("Not an argument constructor: {}".format(fullname), constructor)
358+
res.append(nodes.ARG_POS)
359+
else:
360+
res.append(ARG_KINDS_BY_CONSTRUCTOR[fullname])
361+
return res
362+
333363
def analyze_callable_type(self, t: UnboundType) -> Type:
334364
fallback = self.builtin_type('builtins.function')
335365
if len(t.args) == 0:
@@ -344,10 +374,11 @@ def analyze_callable_type(self, t: UnboundType) -> Type:
344374
ret_type = t.args[1].accept(self)
345375
if isinstance(t.args[0], ArgumentList):
346376
# Callable[[ARG, ...], RET] (ordinary callable type)
377+
kinds = self.argument_list_to_kinds(t.args[0])
347378
args = t.args[0].types
348379
try:
349380
return CallableType(self.anal_array(args),
350-
t.args[0].kinds,
381+
kinds,
351382
t.args[0].names,
352383
ret_type=ret_type,
353384
fallback=fallback)

mypy/types.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -241,18 +241,18 @@ class ArgumentList(Type):
241241

242242
types = None # type: List[Type]
243243
names = None # type: List[Optional[str]]
244-
kinds = None # type: List[int]
244+
constructors = None # type: List[UnboundArgumentConstructor]
245245

246246
def __init__(self,
247247
types: List[Type],
248248
names: List[Optional[str]],
249-
kinds: List[int],
249+
constructors: List[UnboundArgumentConstructor],
250250
line: int = -1,
251251
column: int = -1) -> None:
252252
super().__init__(line, column)
253253
self.types = types
254254
self.names = names
255-
self.kinds = kinds
255+
self.constructors = constructors
256256

257257
def accept(self, visitor: 'TypeVisitor[T]') -> T:
258258
return visitor.visit_type_list(self)
@@ -261,19 +261,15 @@ def serialize(self) -> JsonDict:
261261
return {'.class': 'ArgumentList',
262262
'items': [t.serialize() for t in self.types],
263263
'names': self.names,
264-
'kinds': self.kinds}
264+
'constructors': self.constructors}
265265

266266
@classmethod
267267
def deserialize(cls, data: JsonDict) -> 'ArgumentList':
268268
assert data['.class'] == 'ArgumentList' or data['.class'] == 'TypeList'
269269
types = [Type.deserialize(t) for t in data['items']]
270270
names = cast(List[Optional[str]], data.get('names', [None] * len(types)))
271-
kinds = cast(List[int], data.get('kinds', [ARG_POS] * len(types)))
272-
return ArgumentList(
273-
types=[Type.deserialize(t) for t in data['items']],
274-
names=names,
275-
kinds=kinds,
276-
)
271+
constructors = [UnboundArgumentConstructor.deserialize(c) for c in data['constructors']]
272+
return ArgumentList(types=types, names=names, constructors=constructors)
277273

278274

279275
class AnyType(Type):
@@ -648,7 +644,7 @@ def __init__(self,
648644
self.special_sig = special_sig
649645
super().__init__(line, column)
650646

651-
def _process_names_on_init(self, arg_names):
647+
def _process_names_on_init(self, arg_names: Iterable[str]) -> None:
652648
seen = set() # type: Set[str]
653649
for name in arg_names:
654650
if name is None:
@@ -657,7 +653,7 @@ def _process_names_on_init(self, arg_names):
657653
raise ArgNameException('Duplicate argument name "{}"'.format(name))
658654
seen.add(name)
659655

660-
def _process_kinds_on_init(self, arg_kinds):
656+
def _process_kinds_on_init(self, arg_kinds: Iterable[int]) -> None:
661657
self.is_var_arg = False
662658
self.is_kw_arg = False
663659
seen_named = False

0 commit comments

Comments
 (0)