8000 Make Statement a proper subclass of Node by elazarg · Pull Request #2121 · python/mypy · GitHub
[go: up one dir, main page]

Skip to content

Make Statement a proper subclass of Node #2121

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

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions mypy/fastparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,9 @@
UnaryExpr, FuncExpr, ComparisonExpr,
StarExpr, YieldFromExpr, NonlocalDecl, DictionaryComprehension,
SetComprehension, ComplexExpr, EllipsisExpr, YieldExpr, Argument,
AwaitExpr,
ARG_POS, ARG_OPT, ARG_STAR, ARG_NAMED, ARG_STAR2
)
AwaitExpr, ARG_POS, ARG_OPT, ARG_STAR, ARG_NAMED, ARG_STAR2, Statement)
from mypy.types import (
Type, CallableType, FunctionLike, AnyType, UnboundType, TupleType, TypeList, EllipsisType,
Type, CallableType, AnyType, UnboundType, TupleType, TypeList, EllipsisType,
)
from mypy import defaults
from mypy import experiments
Expand Down Expand Up @@ -128,6 +126,9 @@ def visit_NoneType(self, n: Any) -> Optional[Node]:
def visit_list(self, l: Sequence[ast35.AST]) -> List[Node]:
return [self.visit(e) for e in l]

def visit_statement_list(self, l: Sequence[ast35.AST]) -> List[Statement]:
return [self.visit(e) for e in l]

op_map = {
ast35.Add: '+',
ast35.Sub: '-',
Expand Down Expand Up @@ -176,12 +177,13 @@ def from_comp_operator(self, op: ast35.cmpop) -> str:
def as_block(self, stmts: List[ast35.stmt], lineno: int) -> Block:
b = None
if stmts:
b = Block(self.fix_function_overloads(self.visit_list(stmts)))
stmt_list = self.visit_statement_list(stmts)
b = Block(self.fix_function_overloads(stmt_list))
b.set_line(lineno)
return b

def fix_function_overloads(self, stmts: List[Node]) -> List[Node]:
ret = [] # type: List[Node]
def fix_function_overloads(self, stmts: List[Statement]) -> List[Statement]:
ret = [] # type: List[Statement]
current_overload = []
current_overload_name = None
# mypy doesn't actually check that the decorator is literally @overload
Expand Down Expand Up @@ -225,7 +227,7 @@ def translate_module_id(self, id: str) -> str:
return id

def visit_Module(self, mod: ast35.Module) -> Node:
body = self.fix_function_overloads(self.visit_list(mod.body))
body = self.fix_function_overloads(self.visit_statement_list(mod.body))

return MypyFile(body,
self.imports,
Expand Down
22 changes: 11 additions & 11 deletions mypy/fastparse2.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,12 @@
TupleExpr, GeneratorExpr, ListComprehension, ListExpr, ConditionalExpr,
DictExpr, SetExpr, NameExpr, IntExpr, StrExpr, BytesExpr, UnicodeExpr,
FloatExpr, CallExpr, SuperExpr, MemberExpr, IndexExpr, SliceExpr, OpExpr,
UnaryExpr, FuncExpr, ComparisonExpr,
StarExpr, YieldFromExpr, NonlocalDecl, DictionaryComprehension,
SetComprehension, ComplexExpr, EllipsisExpr, YieldExpr, Argument,
AwaitExpr, Expression,
ARG_POS, ARG_OPT, ARG_STAR, ARG_NAMED, ARG_STAR2
UnaryExpr, FuncExpr, ComparisonExpr, DictionaryComprehension,
SetComprehension, ComplexExpr, EllipsisExpr, YieldExpr, Argument, Expression,
ARG_POS, ARG_OPT, ARG_STAR, ARG_NAMED, ARG_STAR2, Statement
)
from mypy.types import (
Type, CallableType, FunctionLike, AnyType, UnboundType, TupleType, TypeList, EllipsisType,
Type, CallableType, AnyType, UnboundType,
)
from mypy import defaults
from mypy import experiments
Expand All @@ -44,7 +42,6 @@
try:
from typed_ast import ast27
from typed_ast import ast35
from typed_ast import conversions
except ImportError:
if sys.version_info.minor > 2:
print('You must install the typed_ast package before you can run mypy'
Expand Down Expand Up @@ -147,6 +144,9 @@ def visit_NoneType(self, n: Any) -> Optional[Node]:
def visit_list(self, l: Sequence[ast27.AST]) -> List[Node]:
return [self.visit(e) for e in l]

def visit_statement_list(self, l: Sequence[ast27.AST]) -> List[Statement]:
return [self.visit(e) for e in l]

op_map = {
ast27.Add: '+',
ast27.Sub: '-',
Expand Down Expand Up @@ -194,12 +194,12 @@ def from_comp_operator(self, op: ast27.cmpop) -> str:
def as_block(self, stmts: List[ast27.stmt], lineno: int) -> Block:
b = None
if stmts:
b = Block(self.fix_function_overloads(self.visit_list(stmts)))
b = Block(self.fix_function_overloads(self.visit_statement_list(stmts)))
b.set_line(lineno)
return b

def fix_function_overloads(self, stmts: List[Node]) -> List[Node]:
ret = [] # type: List[Node]
def fix_function_overloads(self, stmts: List[Statement]) -> List[Statement]:
ret = [] # type: List[Statement]
current_overload = []
current_overload_name = None
# mypy doesn't actually check that the decorator is literally @overload
Expand Down Expand Up @@ -243,7 +243,7 @@ def translate_module_id(self, id: str) -> str:
return id

def visit_Module(self, mod: ast27.Module) -> Node:
body = self.fix_function_overloads(self.visit_list(mod.body))
body = self.fix_function_overloads(self.visit_statement_list(mod.body))

return MypyFile(body,
self.imports,
Expand Down
19 changes: 13 additions & 6 deletions mypy/nodes.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
"""Abstract syntax tree node classes (i.e. parse tree)."""

import os
import re
from abc import abstractmethod, ABCMeta
from abc import abstractmethod

from typing import (
Any, TypeVar, List, Tuple, cast, Set, Dict, Union, Optional
Expand Down Expand Up @@ -117,13 +116,21 @@ def accept(self, visitor: NodeVisitor[T]) -> T:
raise RuntimeError('Not implemented')


# These are placeholders for a future refactoring; see #1783.
# For now they serve as (unchecked) documentation of what various
# This is a placeholder for a future refactoring; see #1783.
# For now it serves as (unchecked) documentation of what various
# fields of Node subtypes are expected to contain.
Statement = Node
Expression = Node


class Statement(Node):
pass


# For now, only Var is a declaration
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious what other uses you are planning? I am hesitant to add scaffolding for things we might need in the future. In practice the future always evolves in an unanticipated direction.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure it's needed; the motivation was that Var is not a category like Statement and Expression. But now I see that Argument should be together with Var too (wherever that might be) and PEP-526 might add more.

It feels like it's appropriate here, but I don't have a real Argument to support this feeling.

class Declaration(Node):
pass


class SymbolNode(Node):
# Nodes that can be stored in a symbol table.

Expand Down Expand Up @@ -572,7 +579,7 @@ def deserialize(cls, data: JsonDict) -> 'Decorator':
return dec


class Var(SymbolNode, Statement):
class Var(SymbolNode, Declaration):
"""A variable.

It can refer to global/local variable or a data attribute.
Expand Down
31 changes: 16 additions & 15 deletions mypy/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
UnicodeLit, FloatLit, Op, Indent, Keyword, Punct, LexError, ComplexLit,
EllipsisToken
)
import mypy.types
from mypy.nodes import (
MypyFile, Import, Node, ImportAll, ImportFrom, FuncDef, OverloadedFuncDef,
ClassDef, Decorator, Block, Var, OperatorAssignmentStmt,
Expand All @@ -27,14 +26,14 @@
UnaryExpr, FuncExpr, PrintStmt, ImportBase, ComparisonExpr,
StarExpr, YieldFromExpr, NonlocalDecl, DictionaryComprehension,< A92E /span>
SetComprehension, ComplexExpr, EllipsisExpr, YieldExpr, ExecStmt, Argument,
BackquoteExpr
BackquoteExpr, Statement
)
from mypy import defaults
from mypy import nodes
from mypy.errors import Errors, CompileError
from mypy.types import Type, CallableType, AnyType, UnboundType
from mypy.parsetype import (
parse_type, parse_types, parse_signature, TypeParseError, parse_str_as_signature
parse_type, parse_types, parse_signature, TypeParseError
)
from mypy.options import Options

Expand Down Expand Up @@ -235,7 +234,7 @@ def translate_module_id(self, id: str) -> str:
return 'builtins'
return id

def parse_import_from(self) -> Node:
def parse_import_from(self) -> Statement:
self.expect('from')

# Build the list of beginning relative tokens.
Expand Down Expand Up @@ -318,8 +317,8 @@ def parse_qualified_name(self) -> str:

# Parsing global definitions

def parse_defs(self) -> List[Node]:
defs = [] # type: List[Node]
def parse_defs(self) -> List[Statement]:
defs = [] # type: List[Statement]
while not self.eof():
try:
defn, is_simple = self.parse_statement()
Expand Down Expand Up @@ -394,7 +393,7 @@ def parse_class_keywords(self) -> Optional[str]:
def parse_super_type(self) -> Node:
return self.parse_expression(precedence[','])

def parse_decorated_function_or_class(self) -> Node:
def parse_decorated_function_or_class(self) -> Statement:
decorators = []
no_type_checks = False
while self.current_str() == '@':
Expand Down Expand Up @@ -868,7 +867,7 @@ def parse_block(self, allow_type: bool = False) -> Tuple[Block, Type]:
brk = self.expect_break()
type = self.parse_type_comment(brk, signature=True)
self.expect_indent()
stmt_list = [] # type: List[Node]
stmt_list = [] # type: List[Statement]
while (not isinstance(self.current(), Dedent) and
not isinstance(self.current(), Eof)):
try:
Expand All @@ -886,7 +885,7 @@ def parse_block(self, allow_type: bool = False) -> Tuple[Block, Type]:
node.set_line(colon)
return node, type

def try_combine_overloads(self, s: Node, stmt: List[Node]) -> bool:
def try_combine_overloads(self, s: Node, stmt: List[Statement]) -> bool:
if isinstance(s, Decorator) and stmt:
fdef = s
n = fdef.func.name()
Expand All @@ -898,8 +897,8 @@ def try_combine_overloads(self, s: Node, stmt: List[Node]) -> bool:
return True
return False

def parse_statement(self) -> Tuple[Node, bool]:
stmt = None # type: Node
def parse_statement(self) -> Tuple[Statement, bool]:
stmt = None # type: Statement
t = self.current()
ts = self.current_str()
is_simple = True # Is this a non-block statement?
Expand Down Expand Up @@ -964,7 +963,7 @@ def parse_statement(self) -> Tuple[Node, bool]:
stmt.set_line(t)
return stmt, is_simple

def parse_expression_or_assignment(self) -> Node:
def parse_expression_or_assignment(self) -> Statement:
expr = self.parse_expression(star_expr_allowed=True)
if self.current_str() == '=':
return self.parse_assignment(expr)
Expand All @@ -978,7 +977,7 @@ def parse_expression_or_assignment(self) -> Node:
# Expression statement.
return ExpressionStmt(expr)

def parse_assignment(self, lvalue: Any) -> Node:
def parse_assignment(self, lvalue: Any) -> AssignmentStmt:
"""Parse an assignment statement.

Assume that lvalue has been parsed already, and the current token is '='.
Expand Down Expand Up @@ -1184,7 +1183,7 @@ def parse_if_stmt(self) -> IfStmt:
else:
return None

def parse_try_stmt(self) -> Node:
def parse_try_stmt(self) -> Statement:
self.expect('try')
body, _ = self.parse_block()
is_error = False
Expand Down Expand Up @@ -1813,7 +1812,9 @@ def parse_lambda_expr(self) -> FuncExpr:

expr = self.parse_expression(precedence[','])

nodes = [ReturnStmt(expr).set_line(lambda_tok)]
ret = ReturnStmt(expr)
ret.set_line(lambda_tok)
nodes = [ret] # type: List[Statement]
# Potentially insert extra assignment statements to the beginning of the
# body, used to decompose Python 2 tuple arguments.
nodes[:0] = extra_stmts
Expand Down
11 changes: 7 additions & 4 deletions mypy/treetransform.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
ComparisonExpr, TempNode, StarExpr,
YieldFromExpr, NamedTupleExpr, NonlocalDecl, SetComprehension,
DictionaryComprehension, ComplexExpr, TypeAliasExpr, EllipsisExpr,
YieldExpr, ExecStmt, Argument, BackquoteExpr, AwaitExpr,
YieldExpr, ExecStmt, Argument, BackquoteExpr, AwaitExpr, Statement
)
from mypy.types import Type, FunctionLike, Instance
from mypy.types import Type, FunctionLike
from mypy.traverser import TraverserVisitor
from mypy.visitor import NodeVisitor

Expand Down Expand Up @@ -57,7 +57,7 @@ def __init__(self) -> None:

def visit_mypy_file(self, node: MypyFile) -> Node:
# NOTE: The 'names' and 'imports' instance variables will be empty!
new = MypyFile(self.nodes(node.defs), [], node.is_bom,
new = MypyFile(self.statements(node.defs), [], node.is_bom,
ignored_lines=set(node.ignored_lines))
new._name = node._name
new._fullname = node._fullname
Expand Down Expand Up @@ -200,7 +200,7 @@ def visit_nonlocal_decl(self, node: NonlocalDecl) -> Node:
return NonlocalDecl(node.names[:])

def visit_block(self, node: Block) -> Block:
return Block(self.nodes(node.body))
return Block(self.statements(node.body))

def visit_decorator(self, node: Decorator) -> Decorator:
# Note that a Decorator must be transformed to a Decorator.
Expand Down Expand Up @@ -524,6 +524,9 @@ def optional_block(self, block: Block) -> Block:
def nodes(self, nodes: List[Node]) -> List[Node]:
return [self.node(node) for node in nodes]

def statements(self, nodes: List[Statement]) -> List[Statement]:
return [cast(Statement, self.node(node)) for node in nodes]

def optional_nodes(self, nodes: List[Node]) -> List[Node]:
return [self.optional_node(node) for node in nodes]

Expand Down
0