Basic implementation support for namedexpr, fixes #1647

This commit is contained in:
Dave Halter
2020-08-05 10:12:47 +02:00
parent 76c0c373da
commit abf63d73d3
6 changed files with 55 additions and 3 deletions

View File

@@ -16,6 +16,7 @@ Unreleased
- Functions with ``@property`` now return ``property`` instead of ``function`` - Functions with ``@property`` now return ``property`` instead of ``function``
in ``Name().type`` in ``Name().type``
- Started using annotations - Started using annotations
- Better support for the walrus operator
- Project attributes are now read accessible - Project attributes are now read accessible
This is likely going to be the last minor release before 1.0. This is likely going to be the last minor release before 1.0.

View File

@@ -170,6 +170,8 @@ class InferenceState:
return tree_name_to_values(self, context, name) return tree_name_to_values(self, context, name)
elif type_ == 'param': elif type_ == 'param':
return context.py__getattribute__(name.value, position=name.end_pos) return context.py__getattribute__(name.value, position=name.end_pos)
elif type_ == 'namedexpr_test':
return context.infer_node(def_)
else: else:
result = follow_error_node_imports_if_possible(context, name) result = follow_error_node_imports_if_possible(context, name)
if result is not None: if result is not None:

View File

@@ -753,6 +753,8 @@ def tree_name_to_values(inference_state, context, tree_name):
types = NO_VALUES types = NO_VALUES
elif typ == 'del_stmt': elif typ == 'del_stmt':
types = NO_VALUES types = NO_VALUES
elif typ == 'namedexpr_test':
types = infer_node(context, node)
else: else:
raise ValueError("Should not happen. type: %s" % typ) raise ValueError("Should not happen. type: %s" % typ)
return types return types

View File

@@ -90,7 +90,7 @@ def get_flow_branch_keyword(flow_node, node):
first_leaf = child.get_first_leaf() first_leaf = child.get_first_leaf()
if first_leaf in _FLOW_KEYWORDS: if first_leaf in _FLOW_KEYWORDS:
keyword = first_leaf keyword = first_leaf
return 0 return None
def clean_scope_docstring(scope_node): def clean_scope_docstring(scope_node):
@@ -239,7 +239,7 @@ def get_parent_scope(node, include_flows=False):
return None # It's a module already. return None # It's a module already.
while True: while True:
if is_scope(scope) or include_flows and isinstance(scope, tree.Flow): if is_scope(scope):
if scope.type in ('classdef', 'funcdef', 'lambdef'): if scope.type in ('classdef', 'funcdef', 'lambdef'):
index = scope.children.index(':') index = scope.children.index(':')
if scope.children[index].start_pos >= node.start_pos: if scope.children[index].start_pos >= node.start_pos:
@@ -251,6 +251,14 @@ def get_parent_scope(node, include_flows=False):
scope = scope.parent scope = scope.parent
continue continue
return scope return scope
elif include_flows and isinstance(scope, tree.Flow):
# The cursor might be on `if foo`, so the parent scope will not be
# the if, but the parent of the if.
if not (scope.type == 'if_stmt'
and any(n.start_pos <= node.start_pos < n.end_pos
for n in scope.get_test_nodes())):
return scope
scope = scope.parent scope = scope.parent

View File

@@ -5,7 +5,7 @@
x = 3 x = 3
if NOT_DEFINED: if NOT_DEFINED:
x = '' x = ''
#? 6 int() #? 6 int() str()
elif x: elif x:
pass pass
else: else:

View File

@@ -1,3 +1,6 @@
# For assignment expressions / named expressions / walrus operators / whatever
# they are called.
# python >= 3.8 # python >= 3.8
b = (a:=1, a) b = (a:=1, a)
@@ -11,3 +14,39 @@ b = ('':=1,)
#? int() #? int()
b[0] b[0]
def test_assignments():
match = ''
#? str()
match
#? 8 int()
if match := 1:
#? int()
match
#? int()
match
def test_assignments2():
class Foo:
match = ''
#? str()
Foo.match
#? 13 int()
if Foo.match := 1:
#? str()
Foo.match
#? str()
Foo.match
#?
y
#? 16 str()
if y := Foo.match:
#? str()
y
#? str()
y
#? 8 str()
if z := Foo.match:
pass