3RNN/Lib/site-packages/pasta/base/scope_test.py
2024-05-26 19:49:15 +02:00

468 lines
14 KiB
Python

# coding=utf-8
"""Tests for scope."""
# Copyright 2017 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import ast
import textwrap
import unittest
from pasta.base import ast_utils
from pasta.base import scope
from pasta.base import test_utils
class ScopeTest(test_utils.TestCase):
def test_top_level_imports(self):
self.maxDiff = None
source = textwrap.dedent("""\
import aaa
import bbb, ccc.ddd
import aaa.bbb.ccc
from eee import fff
from ggg.hhh import iii, jjj
""")
tree = ast.parse(source)
nodes = tree.body
node_1_aaa = nodes[0].names[0]
node_2_bbb = nodes[1].names[0]
node_2_ccc_ddd = nodes[1].names[1]
node_3_aaa_bbb_ccc = nodes[2].names[0]
node_4_eee = nodes[3]
node_4_fff = nodes[3].names[0]
node_5_ggg_hhh = nodes[4]
node_5_iii = nodes[4].names[0]
node_5_jjj = nodes[4].names[1]
s = scope.analyze(tree)
self.assertItemsEqual(
s.names.keys(), {
'aaa', 'bbb', 'ccc', 'fff', 'iii', 'jjj'
})
self.assertItemsEqual(
s.external_references.keys(), {
'aaa', 'bbb', 'ccc', 'ccc.ddd', 'aaa.bbb', 'aaa.bbb.ccc', 'eee',
'eee.fff', 'ggg', 'ggg.hhh', 'ggg.hhh.iii', 'ggg.hhh.jjj'
})
self.assertItemsEqual(s.external_references['aaa'], [
scope.ExternalReference('aaa', node_1_aaa, s.names['aaa']),
scope.ExternalReference('aaa', node_3_aaa_bbb_ccc, s.names['aaa']),
])
self.assertItemsEqual(s.external_references['bbb'], [
scope.ExternalReference('bbb', node_2_bbb, s.names['bbb']),
])
self.assertItemsEqual(s.external_references['ccc'], [
scope.ExternalReference('ccc', node_2_ccc_ddd, s.names['ccc']),
])
self.assertItemsEqual(s.external_references['ccc.ddd'], [
scope.ExternalReference('ccc.ddd', node_2_ccc_ddd,
s.names['ccc'].attrs['ddd']),
])
self.assertItemsEqual(s.external_references['aaa.bbb'], [
scope.ExternalReference('aaa.bbb', node_3_aaa_bbb_ccc,
s.names['aaa'].attrs['bbb']),
])
self.assertItemsEqual(s.external_references['aaa.bbb.ccc'], [
scope.ExternalReference('aaa.bbb.ccc', node_3_aaa_bbb_ccc,
s.names['aaa'].attrs['bbb'].attrs['ccc']),
])
self.assertItemsEqual(s.external_references['eee'], [
scope.ExternalReference('eee', node_4_eee, None),
])
self.assertItemsEqual(s.external_references['eee.fff'], [
scope.ExternalReference('eee.fff', node_4_fff, s.names['fff']),
])
self.assertItemsEqual(s.external_references['ggg'], [
scope.ExternalReference('ggg', node_5_ggg_hhh, None),
])
self.assertItemsEqual(s.external_references['ggg.hhh'], [
scope.ExternalReference('ggg.hhh', node_5_ggg_hhh, None),
])
self.assertItemsEqual(s.external_references['ggg.hhh.iii'], [
scope.ExternalReference('ggg.hhh.iii', node_5_iii, s.names['iii']),
])
self.assertItemsEqual(s.external_references['ggg.hhh.jjj'], [
scope.ExternalReference('ggg.hhh.jjj', node_5_jjj, s.names['jjj']),
])
self.assertIs(s.names['aaa'].definition, node_1_aaa)
self.assertIs(s.names['bbb'].definition, node_2_bbb)
self.assertIs(s.names['ccc'].definition, node_2_ccc_ddd)
self.assertIs(s.names['fff'].definition, node_4_fff)
self.assertIs(s.names['iii'].definition, node_5_iii)
self.assertIs(s.names['jjj'].definition, node_5_jjj)
self.assertItemsEqual(s.names['aaa'].reads, [node_3_aaa_bbb_ccc])
for ref in {'bbb', 'ccc', 'fff', 'iii', 'jjj'}:
self.assertEqual(s.names[ref].reads, [], 'Expected no reads for %s' % ref)
def test_if_nested_imports(self):
source = textwrap.dedent("""\
if a:
import aaa
elif b:
import bbb
else:
import ccc
""")
tree = ast.parse(source)
nodes = tree.body
node_aaa, node_bbb, node_ccc = ast_utils.find_nodes_by_type(tree, ast.alias)
s = scope.analyze(tree)
self.assertItemsEqual(s.names.keys(), {'aaa', 'bbb', 'ccc', 'a', 'b'})
self.assertItemsEqual(s.external_references.keys(), {'aaa', 'bbb', 'ccc'})
self.assertEqual(s.names['aaa'].definition, node_aaa)
self.assertEqual(s.names['bbb'].definition, node_bbb)
self.assertEqual(s.names['ccc'].definition, node_ccc)
self.assertIsNone(s.names['a'].definition)
self.assertIsNone(s.names['b'].definition)
for ref in {'aaa', 'bbb', 'ccc'}:
self.assertEqual(s.names[ref].reads, [],
'Expected no reads for %s' % ref)
def test_try_nested_imports(self):
source = textwrap.dedent("""\
try:
import aaa
except:
import bbb
finally:
import ccc
""")
tree = ast.parse(source)
nodes = tree.body
node_aaa, node_bbb, node_ccc = ast_utils.find_nodes_by_type(tree, ast.alias)
s = scope.analyze(tree)
self.assertItemsEqual(s.names.keys(), {'aaa', 'bbb', 'ccc'})
self.assertItemsEqual(s.external_references.keys(), {'aaa', 'bbb', 'ccc'})
self.assertEqual(s.names['aaa'].definition, node_aaa)
self.assertEqual(s.names['bbb'].definition, node_bbb)
self.assertEqual(s.names['ccc'].definition, node_ccc)
for ref in {'aaa', 'bbb', 'ccc'}:
self.assertEqual(s.names[ref].reads, [],
'Expected no reads for %s' % ref)
def test_functiondef_nested_imports(self):
source = textwrap.dedent("""\
def foo(bar):
import aaa
""")
tree = ast.parse(source)
nodes = tree.body
node_aaa = ast_utils.find_nodes_by_type(tree, ast.alias)[0]
s = scope.analyze(tree)
self.assertItemsEqual(s.names.keys(), {'foo'})
self.assertItemsEqual(s.external_references.keys(), {'aaa'})
def test_classdef_nested_imports(self):
source = textwrap.dedent("""\
class Foo():
import aaa
""")
tree = ast.parse(source)
nodes = tree.body
node_aaa = nodes[0].body[0].names[0]
s = scope.analyze(tree)
self.assertItemsEqual(s.names.keys(), {'Foo'})
self.assertItemsEqual(s.external_references.keys(), {'aaa'})
def test_multilevel_import_reads(self):
source = textwrap.dedent("""\
import aaa.bbb.ccc
aaa.bbb.ccc.foo()
""")
tree = ast.parse(source)
nodes = tree.body
node_ref = nodes[1].value.func.value
s = scope.analyze(tree)
self.assertItemsEqual(s.names.keys(), {'aaa'})
self.assertItemsEqual(s.external_references.keys(),
{'aaa', 'aaa.bbb', 'aaa.bbb.ccc'})
self.assertItemsEqual(s.names['aaa'].reads, [node_ref.value.value])
self.assertItemsEqual(s.names['aaa'].attrs['bbb'].reads, [node_ref.value])
self.assertItemsEqual(s.names['aaa'].attrs['bbb'].attrs['ccc'].reads,
[node_ref])
def test_import_reads_in_functiondef(self):
source = textwrap.dedent("""\
import aaa
@aaa.x
def foo(bar):
return aaa
""")
tree = ast.parse(source)
nodes = tree.body
return_value = nodes[1].body[0].value
decorator = nodes[1].decorator_list[0].value
s = scope.analyze(tree)
self.assertItemsEqual(s.names.keys(), {'aaa', 'foo'})
self.assertItemsEqual(s.external_references.keys(), {'aaa'})
self.assertItemsEqual(s.names['aaa'].reads, [decorator, return_value])
def test_import_reads_in_classdef(self):
source = textwrap.dedent("""\
import aaa
@aaa.x
class Foo(aaa.Bar):
pass
""")
tree = ast.parse(source)
nodes = tree.body
node_aaa = nodes[0].names[0]
decorator = nodes[1].decorator_list[0].value
base = nodes[1].bases[0].value
s = scope.analyze(tree)
self.assertItemsEqual(s.names.keys(), {'aaa', 'Foo'})
self.assertItemsEqual(s.external_references.keys(), {'aaa'})
self.assertItemsEqual(s.names['aaa'].reads, [decorator, base])
def test_import_masked_by_function_arg(self):
source = textwrap.dedent("""\
import aaa
def foo(aaa=aaa):
return aaa
""")
tree = ast.parse(source)
nodes = tree.body
argval = nodes[1].args.defaults[0]
s = scope.analyze(tree)
self.assertItemsEqual(s.names.keys(), {'aaa', 'foo'})
self.assertItemsEqual(s.external_references.keys(), {'aaa'})
self.assertItemsEqual(s.names['aaa'].reads, [argval])
def test_import_masked_by_assign(self):
source = textwrap.dedent("""\
import aaa
def foo():
aaa = 123
return aaa
aaa
""")
tree = ast.parse(source)
nodes = tree.body
node_aaa = nodes[2].value
s = scope.analyze(tree)
self.assertItemsEqual(s.names.keys(), {'aaa', 'foo'})
self.assertItemsEqual(s.external_references.keys(), {'aaa'})
self.assertItemsEqual(s.names['aaa'].reads, [node_aaa])
def test_import_in_decortator(self):
source = textwrap.dedent("""\
import aaa
@aaa.wrapper
def foo(aaa=1):
pass
""")
tree = ast.parse(source)
nodes = tree.body
decorator = nodes[1].decorator_list[0].value
s = scope.analyze(tree)
self.assertItemsEqual(s.names.keys(), {'aaa', 'foo'})
self.assertItemsEqual(s.external_references.keys(), {'aaa'})
self.assertItemsEqual(s.names['aaa'].reads, [decorator])
@test_utils.requires_features('type_annotations')
def test_import_in_return_type(self):
source = textwrap.dedent("""\
import aaa
def foo() -> aaa.Foo:
pass
""")
tree = ast.parse(source)
nodes = tree.body
func = nodes[1]
s = scope.analyze(tree)
self.assertItemsEqual(s.names.keys(), {'aaa', 'foo'})
self.assertItemsEqual(s.external_references.keys(), {'aaa'})
self.assertItemsEqual(s.names['aaa'].reads, [func.returns.value])
@test_utils.requires_features('type_annotations')
def test_import_in_argument_type(self):
source = textwrap.dedent("""\
import aaa
def foo(bar: aaa.Bar):
pass
""")
tree = ast.parse(source)
nodes = tree.body
func = nodes[1]
s = scope.analyze(tree)
self.assertItemsEqual(s.names.keys(), {'aaa', 'foo'})
self.assertItemsEqual(s.external_references.keys(), {'aaa'})
self.assertItemsEqual(s.names['aaa'].reads,
[func.args.args[0].annotation.value])
def test_import_attribute_references(self):
source = textwrap.dedent("""\
import aaa.bbb.ccc, ddd.eee
aaa.x()
aaa.bbb.y()
aaa.bbb.ccc.z()
""")
tree = ast.parse(source)
nodes = tree.body
call1 = nodes[1].value.func.value
call2 = nodes[2].value.func.value
call3 = nodes[3].value.func.value
s = scope.analyze(tree)
self.assertItemsEqual(s.names.keys(), {'aaa', 'ddd'})
self.assertItemsEqual(s.external_references.keys(),
{'aaa', 'aaa.bbb', 'aaa.bbb.ccc', 'ddd', 'ddd.eee'})
self.assertItemsEqual(s.names['aaa'].reads,
[call1, call2.value, call3.value.value])
self.assertItemsEqual(s.names['aaa'].attrs['bbb'].reads,
[call2, call3.value])
self.assertItemsEqual(s.names['aaa'].attrs['bbb'].attrs['ccc'].reads,
[call3])
def test_lookup_scope(self):
src = textwrap.dedent("""\
import a
def b(c, d, e=1):
class F(d):
g = 1
return c
""")
t = ast.parse(src)
import_node, func_node = t.body
class_node, return_node = func_node.body
sc = scope.analyze(t)
import_node_scope = sc.lookup_scope(import_node)
self.assertIs(import_node_scope.node, t)
self.assertIs(import_node_scope, sc)
self.assertItemsEqual(import_node_scope.names, ['a', 'b'])
func_node_scope = sc.lookup_scope(func_node)
self.assertIs(func_node_scope.node, func_node)
self.assertIs(func_node_scope.parent_scope, sc)
self.assertItemsEqual(func_node_scope.names, ['c', 'd', 'e', 'F'])
class_node_scope = sc.lookup_scope(class_node)
self.assertIs(class_node_scope.node, class_node)
self.assertIs(class_node_scope.parent_scope, func_node_scope)
self.assertItemsEqual(class_node_scope.names, ['g'])
return_node_scope = sc.lookup_scope(return_node)
self.assertIs(return_node_scope.node, func_node)
self.assertIs(return_node_scope, func_node_scope)
self.assertItemsEqual(return_node_scope.names, ['c', 'd', 'e', 'F'])
self.assertIs(class_node_scope.lookup_scope(func_node),
func_node_scope)
self.assertIsNone(sc.lookup_scope(ast.Name(id='foo')))
def test_class_methods(self):
source = textwrap.dedent("""\
import aaa
class C:
def aaa(self):
return aaa
def bbb(self):
return aaa
""")
tree = ast.parse(source)
importstmt, classdef = tree.body
method_aaa, method_bbb = classdef.body
s = scope.analyze(tree)
self.assertItemsEqual(s.names.keys(), {'aaa', 'C'})
self.assertItemsEqual(s.external_references.keys(), {'aaa'})
self.assertItemsEqual(s.names['aaa'].reads,
[method_aaa.body[0].value, method_bbb.body[0].value])
# TODO: Test references to C.aaa, C.bbb once supported
def test_vararg_kwarg_references_in_function_body(self):
source = textwrap.dedent("""\
def aaa(bbb, *ccc, **ddd):
ccc
ddd
eee(ccc, ddd)
""")
tree = ast.parse(source)
funcdef, call = tree.body
ccc_expr, ddd_expr = funcdef.body
sc = scope.analyze(tree)
func_scope = sc.lookup_scope(funcdef)
self.assertIn('ccc', func_scope.names)
self.assertItemsEqual(func_scope.names['ccc'].reads, [ccc_expr.value])
self.assertIn('ddd', func_scope.names)
self.assertItemsEqual(func_scope.names['ddd'].reads, [ddd_expr.value])
def suite():
result = unittest.TestSuite()
result.addTests(unittest.makeSuite(ScopeTest))
return result
if __name__ == '__main__':
unittest.main()