""" Tests for `api.names`. """ from textwrap import dedent import pytest def _assert_definition_names(definitions, names): assert [d.name for d in definitions] == names def _check_names(get_names, source, names): definitions = get_names(dedent(source)) _assert_definition_names(definitions, names) return definitions def test_get_definitions_flat(get_names): _check_names(get_names, """ import module class Class: pass def func(): pass data = None """, ['module', 'Class', 'func', 'data']) def test_dotted_assignment(get_names): _check_names(get_names, """ x = Class() x.y.z = None """, ['x', 'z']) # TODO is this behavior what we want? def test_multiple_assignment(get_names): _check_names(get_names, "x = y = None", ['x', 'y']) def test_multiple_imports(get_names): _check_names(get_names, """ from module import a, b from another_module import * """, ['a', 'b']) def test_nested_definitions(get_names): definitions = _check_names(get_names, """ class Class: def f(): pass def g(): pass """, ['Class']) subdefinitions = definitions[0].defined_names() _assert_definition_names(subdefinitions, ['f', 'g']) assert [d.full_name for d in subdefinitions] == ['__main__.Class.f', '__main__.Class.g'] def test_nested_class(get_names): definitions = _check_names(get_names, """ class L1: class L2: class L3: def f(): pass def f(): pass def f(): pass def f(): pass """, ['L1', 'f']) subdefs = definitions[0].defined_names() subsubdefs = subdefs[0].defined_names() _assert_definition_names(subdefs, ['L2', 'f']) _assert_definition_names(subsubdefs, ['L3', 'f']) _assert_definition_names(subsubdefs[0].defined_names(), ['f']) def test_class_fields_with_all_scopes_false(get_names): definitions = _check_names(get_names, """ from module import f g = f(f) class C: h = g def foo(x=a): bar = x return bar """, ['f', 'g', 'C', 'foo']) C_subdefs = definitions[-2].defined_names() foo_subdefs = definitions[-1].defined_names() _assert_definition_names(C_subdefs, ['h']) _assert_definition_names(foo_subdefs, ['x', 'bar']) def test_async_stmt_with_all_scopes_false(get_names): definitions = _check_names(get_names, """ from module import f import asyncio g = f(f) class C: h = g def __init__(self): pass async def __aenter__(self): pass def foo(x=a): bar = x return bar async def async_foo(duration): async def wait(): await asyncio.sleep(100) for i in range(duration//100): await wait() return duration//100*100 async with C() as cinst: d = cinst """, ['f', 'asyncio', 'g', 'C', 'foo', 'async_foo', 'cinst', 'd']) C_subdefs = definitions[3].defined_names() foo_subdefs = definitions[4].defined_names() async_foo_subdefs = definitions[5].defined_names() cinst_subdefs = definitions[6].defined_names() _assert_definition_names(C_subdefs, ['h', '__init__', '__aenter__']) _assert_definition_names(foo_subdefs, ['x', 'bar']) _assert_definition_names(async_foo_subdefs, ['duration', 'wait', 'i']) # We treat d as a name outside `async with` block _assert_definition_names(cinst_subdefs, []) def test_follow_imports(get_names): # github issue #344 imp = get_names('import datetime')[0] assert imp.name == 'datetime' datetime_names = [str(d.name) for d in imp.defined_names()] assert 'timedelta' in datetime_names def test_names_twice(get_names): code = dedent(''' def lol(): pass ''') defs = get_names(code) assert defs[0].defined_names() == [] def test_simple_name(get_names): defs = get_names('foo', references=True) assert not defs[0]._name.infer() def test_no_error(get_names): code = dedent(""" def foo(a, b): if a == 10: if b is None: print("foo") a = 20 """) func_name, = get_names(code) a, b, a20 = func_name.defined_names() assert a.name == 'a' assert b.name == 'b' assert a20.name == 'a' assert a20.goto() == [a20] @pytest.mark.parametrize( 'code, index, is_side_effect', [ ('x', 0, False), ('x.x', 0, False), ('x.x', 1, False), ('x.x = 3', 0, False), ('x.x = 3', 1, True), ('def x(x): x.x = 3', 1, False), ('def x(x): x.x = 3', 3, True), ('import sys; sys.path', 0, False), ('import sys; sys.path', 1, False), ('import sys; sys.path', 2, False), ('import sys; sys.path = []', 2, True), ] ) def test_is_side_effect(get_names, code, index, is_side_effect): names = get_names(code, references=True, all_scopes=True) assert names[index].is_side_effect() == is_side_effect def test_no_defined_names(get_names): definition, = get_names("x = (1, 2)") assert not definition.defined_names()