-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Better callable: Callable[[Arg('x', int), VarArg(str)], int] now a thing you can do
#2607
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
Changes from 1 commit
249d70f
7ecdcc1
066bd5e
213944b
0e19070
3f2f617
0b69630
f4ccf92
967bb5a
bb5134e
d4a83e1
54a5da9
e79c527
52ffe5c
06416f7
398fbad
2c9ce02
51c6f56
5e679a3
0926fe9
288a8be
6e67ab2
97a859b
1c7d4c6
f153850
be954f5
07ae917
1b97362
552f49e
f2e3663
27e2a9d
793a663
3d212b3
0780149
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
…, visitor stuff
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,9 +5,10 @@ | |
| ListExpr, StrExpr, BytesExpr, UnicodeExpr, EllipsisExpr, CallExpr, | ||
| ARG_POS, ARG_NAMED, get_member_expr_fullname | ||
| ) | ||
| from mypy.sharedparse import ARG_KINDS_BY_CONSTRUCTOR, STAR_ARG_CONSTRUCTORS | ||
| from mypy.fastparse import parse_type_comment | ||
| from mypy.types import Type, UnboundType, ArgumentList, EllipsisType, AnyType, Optional | ||
| from mypy.types import ( | ||
| Type, UnboundType, ArgumentList, EllipsisType, AnyType, Optional, CallableArgument, | ||
| ) | ||
|
|
||
|
|
||
| class TypeTranslationError(Exception): | ||
|
|
@@ -53,49 +54,32 @@ def expr_to_unanalyzed_type(expr: Expression) -> Type: | |
| return base | ||
| else: | ||
| raise TypeTranslationError() | ||
| elif isinstance(expr, ListExpr): | ||
| types = [] # type: List[Type] | ||
| names = [] # type: List[Optional[str]] | ||
| kinds = [] # type: List[int] | ||
| for it in expr.items: | ||
| if isinstance(it, CallExpr): | ||
| if not isinstance(it.callee, NameExpr): | ||
| raise TypeTranslationError() | ||
| arg_const = it.callee.name | ||
| try: | ||
| kind = ARG_KINDS_BY_CONSTRUCTOR[arg_const] | ||
| except KeyError: | ||
| elif isinstance(expr, CallExpr): | ||
|
||
| if not isinstance(expr.callee, NameExpr): | ||
|
||
| raise TypeTranslationError() | ||
| arg_const = expr.callee.name | ||
| name = None | ||
| typ = AnyType(implicit=True) # type: Type | ||
| for i, arg in enumerate(expr.args): | ||
| if expr.arg_names[i] is not None: | ||
| if expr.arg_names[i] == "name": | ||
| name = _extract_str(arg) | ||
| continue | ||
| elif expr.arg_names[i] == "typ": | ||
| typ = expr_to_unanalyzed_type(arg) | ||
| continue | ||
| else: | ||
| raise TypeTranslationError() | ||
| name = None | ||
| typ = AnyType(implicit=True) # type: Type | ||
| star = arg_const in STAR_ARG_CONSTRUCTORS | ||
| for i, arg in enumerate(it.args): | ||
| if it.arg_names[i] is not None: | ||
| if it.arg_names[i] == "name": | ||
| name = _extract_str(arg) | ||
| continue | ||
| elif it.arg_names[i] == "typ": | ||
| typ = expr_to_unanalyzed_type(arg) | ||
| continue | ||
| else: | ||
| raise TypeTranslationError() | ||
| elif i == 0 and not star: | ||
| name = _extract_str(arg) | ||
| elif i == 1 and not star or i == 0 and star: | ||
| typ = expr_to_unanalyzed_type(arg) | ||
| else: | ||
| raise TypeTranslationError() | ||
| names.append(name) | ||
| types.append(typ) | ||
| kinds.append(kind) | ||
| elif i == 0: | ||
| typ = expr_to_unanalyzed_type(arg) | ||
| elif i == 1: | ||
| name = _extract_str(arg) | ||
| else: | ||
| types.append(expr_to_unanalyzed_type(it)) | ||
| names.append(None) | ||
| kinds.append(ARG_POS) | ||
| def fail(message, Expression): | ||
| raise TypeTranslationError(message) | ||
| return ArgumentList(types, names, kinds, | ||
| line=expr.line, column=expr.column) | ||
| raise TypeTranslationError() | ||
| return CallableArgument(typ, name, arg_const, expr.line, expr.column) | ||
| elif isinstance(expr, ListExpr): | ||
| return ArgumentList([expr_to_unanalyzed_type(it) for it in expr.items], | ||
| line=expr.line, column=expr.column) | ||
| elif isinstance(expr, (StrExpr, BytesExpr, UnicodeExpr)): | ||
| # Parse string literal type. | ||
| try: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,7 +4,6 @@ | |
| from typing import Tuple, Union, TypeVar, Callable, Sequence, Optional, Any, cast, List, Set | ||
| from mypy.sharedparse import ( | ||
| special_function_elide_names, argument_elide_name, | ||
| ARG_KINDS_BY_CONSTRUCTOR, STAR_ARG_CONSTRUCTORS, | ||
| ) | ||
| from mypy.nodes import ( | ||
| MypyFile, Node, ImportBase, Import, ImportAll, ImportFrom, FuncDef, | ||
|
|
@@ -25,6 +24,7 @@ | |
| ) | ||
| from mypy.types import ( | ||
| Type, CallableType, AnyType, UnboundType, TupleType, ArgumentList, EllipsisType, | ||
| CallableArgument, | ||
| ) | ||
| from mypy import defaults | ||
| from mypy import experiments | ||
|
|
@@ -153,9 +153,6 @@ def __init__(self, | |
| def fail(self, msg: str, line: int, column: int) -> None: | ||
| self.errors.report(line, column, msg) | ||
|
|
||
| def fail_ast(self, msg: str, n: ast3.AST) -> None: | ||
| self.fail(msg, n.lineno, n.col_offset) | ||
|
|
||
| def generic_visit(self, node: ast3.AST) -> None: | ||
| raise RuntimeError('AST node not implemented: ' + str(type(node))) | ||
|
|
||
|
|
@@ -449,18 +446,12 @@ def make_argument(arg: ast3.arg, default: Optional[ast3.expr], kind: int) -> Arg | |
| new_args.append(make_argument(args.kwarg, None, ARG_STAR2)) | ||
| names.append(args.kwarg) | ||
|
|
||
| check_arg_names([name.arg for name in names], names, self.fail_ast) | ||
| def fail_arg(msg: str, arg: ast3.arg): | ||
| self.fail(msg, arg.lineno, arg.col_offset) | ||
|
|
||
| return new_args | ||
| check_arg_names([name.arg for name in names], names, fail_arg) | ||
|
|
||
| def stringify_name(self, n: ast3.AST) -> str: | ||
| if isinstance(n, ast3.Name): | ||
| return n.id | ||
| elif isinstance(n, ast3.Attribute): | ||
| sv = self.stringify_name(n.value) | ||
| if sv is not None: | ||
| return "{}.{}".format(sv, n.attr) | ||
| return None # Can't do it. | ||
| return new_args | ||
|
|
||
| # ClassDef(identifier name, | ||
| # expr* bases, | ||
|
|
@@ -473,7 +464,7 @@ def visit_ClassDef(self, n: ast3.ClassDef) -> ClassDef: | |
| metaclass_arg = find(lambda x: x.arg == 'metaclass', n.keywords) | ||
| metaclass = None | ||
| if metaclass_arg: | ||
| metaclass = self.stringify_name(metaclass_arg.value) | ||
| metaclass = stringify_name(metaclass_arg.value) | ||
| if metaclass is None: | ||
| metaclass = '<error>' # To be reported later | ||
|
|
||
|
|
@@ -964,6 +955,21 @@ class TypeConverter(ast3.NodeTransformer): # type: ignore # typeshed PR #931 | |
| def __init__(self, errors: Errors, line: int = -1) -> None: | ||
| self.errors = errors | ||
| self.line = line | ||
| self.node_stack = [] # type: List[ast3.AST] | ||
|
|
||
| def visit(self, node): | ||
| """Modified visit -- keep track of the stack of nodes""" | ||
| self.node_stack.append(node) | ||
| try: | ||
| return super().visit(node) | ||
| finally: | ||
| self.node_stack.pop() | ||
|
|
||
| def parent(self) -> ast3.AST: | ||
| """Return the AST node above the one we are processing""" | ||
| if len(self.node_stack) < 2: | ||
| return None | ||
| return self.node_stack[-2] | ||
|
|
||
| def fail(self, msg: str, line: int, column: int) -> None: | ||
| self.errors.report(line, column, msg) | ||
|
|
@@ -984,63 +990,50 @@ def visit_NoneType(self, n: Any) -> Type: | |
| def translate_expr_list(self, l: Sequence[ast3.AST]) -> List[Type]: | ||
| return [self.visit(e) for e in l] | ||
|
|
||
| def visit_Call(self, e: ast3.Call) -> Type: | ||
| # Parse the arg constructor | ||
| if not isinstance(self.parent(), ast3.List): | ||
| return self.generic_visit(e) | ||
| f = e.func | ||
| constructor = stringify_name(f) | ||
| if not constructor: | ||
| self.fail("Expected arg constructor name", e.lineno, e.col_offset) | ||
| constructor = "BadArgConstructor" | ||
| name = None # type: Optional[str] | ||
| typ = AnyType(implicit=True) # type: Type | ||
| for i, arg in enumerate(e.args): | ||
| if i == 0: | ||
| typ = self.visit(arg) | ||
| elif i == 1: | ||
| name = self._extract_str(arg) | ||
| else: | ||
| self.fail("Too many arguments for argument constructor", | ||
| f.lineno, f.col_offset) | ||
| for k in e.keywords: | ||
| value = k.value | ||
| if k.arg == "name": | ||
| name = self._extract_str(value) | ||
| elif k.arg == "typ": | ||
|
||
| typ = self.visit(value) | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Make an error if we re-set |
||
| else: | ||
| self.fail( | ||
| 'Unexpected argument "{}" for argument constructor'.format(k.arg), | ||
| value.lineno, value.col_offset) | ||
| return CallableArgument(typ, name, constructor, e.lineno, e.col_offset) | ||
|
|
||
|
|
||
| def translate_argument_list(self, l: Sequence[ast3.AST]) -> ArgumentList: | ||
| types = [] # type: List[Type] | ||
| names = [] # type: List[Optional[str]] | ||
| kinds = [] # type: List[int] | ||
| for e in l: | ||
| if isinstance(e, ast3.Call): | ||
| # Parse the arg constructor | ||
| f = e.func | ||
| if not isinstance(f, ast3.Name): | ||
| raise FastParserError("Expected arg constructor name", | ||
| e.lineno, e.col_offset) | ||
| try: | ||
| kind = ARG_KINDS_BY_CONSTRUCTOR[f.id] | ||
| star = f.id in STAR_ARG_CONSTRUCTORS | ||
| except KeyError: | ||
| self.fail("Unknown argument constructor {}".format(f.id), | ||
| f.lineno, f.col_offset) | ||
| kind = ARG_POS | ||
| star = False | ||
|
|
||
| name = None # type: Optional[str] | ||
| typ = AnyType(implicit=True) # type: Type | ||
| for i, arg in enumerate(e.args): | ||
| if i == 0 and not star: | ||
| name = self._extract_str(arg) | ||
| elif i == 1 and not star or i == 0 and star: | ||
| typ = self.visit(arg) | ||
| else: | ||
| self.fail("Too many arguments for argument constructor", | ||
| f.lineno, f.col_offset) | ||
| for k in e.keywords: | ||
| value = k.value | ||
| if k.arg == "name" and not star: | ||
| name = self._extract_str(value) | ||
| elif k.arg == "typ": | ||
| typ = self.visit(value) | ||
| else: | ||
| self.fail( | ||
| 'Unexpected argument "{}" for argument constructor'.format(k.arg), | ||
| value.lineno, value.col_offset) | ||
|
|
||
| types.append(typ) | ||
| names.append(name) | ||
| kinds.append(kind) | ||
| else: | ||
| types.append(self.visit(e)) | ||
| names.append(None) | ||
| kinds.append(ARG_POS) | ||
|
|
||
| return ArgumentList(types, names, kinds, line=self.line) | ||
| return ArgumentList([self.visit(e) for e in l], line=self.line) | ||
|
|
||
| def _extract_str(self, n: ast3.AST) -> str: | ||
| def _extract_str(self, n: ast3.expr) -> str: | ||
|
||
| if isinstance(n, ast3.Str): | ||
| return n.s.strip() | ||
| elif isinstance(n, ast3.NameConstant) and str(n.value) == 'None': | ||
| return None | ||
| self.fail('Expected string literal for argument name, got "{}"'.format(n), n.lineno, n.col_offset) | ||
| self.fail('Expected string literal for argument name, got {}'.format(type(n).__name__), self.line, 0) | ||
| return None | ||
|
|
||
| def visit_Name(self, n: ast3.Name) -> Type: | ||
|
|
@@ -1096,3 +1089,12 @@ def visit_Ellipsis(self, n: ast3.Ellipsis) -> Type: | |
| def visit_List(self, n: ast3.List) -> Type: | ||
| l = len(n.elts) | ||
| return self.translate_argument_list(n.elts) | ||
|
|
||
| def stringify_name(n: ast3.AST) -> Optional[str]: | ||
| if isinstance(n, ast3.Name): | ||
| return n.id | ||
| elif isinstance(n, ast3.Attribute): | ||
| sv = stringify_name(n.value) | ||
| if sv is not None: | ||
| return "{}.{}".format(sv, n.attr) | ||
| return None # Can't do it. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,7 +11,7 @@ | |
| from mypy.types import ( | ||
| CallableType, EllipsisType, Instance, Overloaded, TupleType, TypedDictType, | ||
| ArgumentList, TypeVarType, UnboundType, UnionType, TypeVisitor, | ||
| TypeType | ||
| TypeType, CallableArgument, | ||
|
||
| ) | ||
| from mypy.visitor import NodeVisitor | ||
|
|
||
|
|
@@ -177,8 +177,8 @@ def visit_callable_type(self, ct: CallableType) -> None: | |
| val.accept(self) | ||
| v.upper_bound.accept(self) | ||
|
|
||
| def visit_ellipsis_type(self, e: EllipsisType) -> None: | ||
| pass # Nothing to descend into. | ||
| # def visit_ellipsis_type(self, e: EllipsisType) -> None: | ||
| # pass # Nothing to descend into. | ||
|
|
||
| def visit_overloaded(self, t: Overloaded) -> None: | ||
| for ct in t.items(): | ||
|
|
@@ -210,9 +210,12 @@ def visit_typeddict_type(self, tdt: TypedDictType) -> None: | |
| if tdt.fallback is not None: | ||
| tdt.fallback.accept(self) | ||
|
|
||
| def visit_type_list(self, tl: ArgumentList) -> None: | ||
| for t in tl.types: | ||
| t.accept(self) | ||
| # def visit_type_list(self, tl: ArgumentList) -> None: | ||
| # for t in tl.items: | ||
| # t.accept(self) | ||
|
|
||
| # def visit_callable_argument(self, t: CallableArgument) -> None: | ||
| # t.typ.accept(self) | ||
|
|
||
| def visit_type_var(self, tvt: TypeVarType) -> None: | ||
| if tvt.values: | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add comment about why it makes sense to just return
typhere and elsewhere in this file.