| # Copyright (c) 2010, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr> |
| # Copyright (c) 2012 FELD Boris <lothiraldan@gmail.com> |
| # Copyright (c) 2013-2018, 2020 Claudiu Popa <pcmanticore@gmail.com> |
| # Copyright (c) 2014 Google, Inc. |
| # Copyright (c) 2015-2016 Ceridwen <ceridwenv@gmail.com> |
| # Copyright (c) 2016 Jared Garst <jgarst@users.noreply.github.com> |
| # Copyright (c) 2017, 2019 Ćukasz Rogalski <rogalski.91@gmail.com> |
| # Copyright (c) 2017 Hugo <hugovk@users.noreply.github.com> |
| # Copyright (c) 2019 Ashley Whetter <ashley@awhetter.co.uk> |
| # Copyright (c) 2020-2021 hippo91 <guillaume.peillex@gmail.com> |
| # Copyright (c) 2020 David Gilman <davidgilman1@gmail.com> |
| # Copyright (c) 2021 Pierre Sassoulas <pierre.sassoulas@gmail.com> |
| # Copyright (c) 2021 Marc Mueller <30130371+cdce8p@users.noreply.github.com> |
| |
| # Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html |
| # For details: https://github.com/PyCQA/astroid/blob/main/LICENSE |
| |
| import unittest |
| from textwrap import dedent |
| |
| from astroid import nodes |
| from astroid.builder import AstroidBuilder, extract_node |
| from astroid.test_utils import require_version |
| |
| |
| class Python3TC(unittest.TestCase): |
| @classmethod |
| def setUpClass(cls): |
| cls.builder = AstroidBuilder() |
| |
| def test_starred_notation(self) -> None: |
| astroid = self.builder.string_build("*a, b = [1, 2, 3]", "test", "test") |
| |
| # Get the star node |
| node = next(next(next(astroid.get_children()).get_children()).get_children()) |
| |
| self.assertTrue(isinstance(node.assign_type(), nodes.Assign)) |
| |
| def test_yield_from(self) -> None: |
| body = dedent( |
| """ |
| def func(): |
| yield from iter([1, 2]) |
| """ |
| ) |
| astroid = self.builder.string_build(body) |
| func = astroid.body[0] |
| self.assertIsInstance(func, nodes.FunctionDef) |
| yieldfrom_stmt = func.body[0] |
| |
| self.assertIsInstance(yieldfrom_stmt, nodes.Expr) |
| self.assertIsInstance(yieldfrom_stmt.value, nodes.YieldFrom) |
| self.assertEqual(yieldfrom_stmt.as_string(), "yield from iter([1, 2])") |
| |
| def test_yield_from_is_generator(self) -> None: |
| body = dedent( |
| """ |
| def func(): |
| yield from iter([1, 2]) |
| """ |
| ) |
| astroid = self.builder.string_build(body) |
| func = astroid.body[0] |
| self.assertIsInstance(func, nodes.FunctionDef) |
| self.assertTrue(func.is_generator()) |
| |
| def test_yield_from_as_string(self) -> None: |
| body = dedent( |
| """ |
| def func(): |
| yield from iter([1, 2]) |
| value = yield from other() |
| """ |
| ) |
| astroid = self.builder.string_build(body) |
| func = astroid.body[0] |
| self.assertEqual(func.as_string().strip(), body.strip()) |
| |
| # metaclass tests |
| |
| def test_simple_metaclass(self) -> None: |
| astroid = self.builder.string_build("class Test(metaclass=type): pass") |
| klass = astroid.body[0] |
| |
| metaclass = klass.metaclass() |
| self.assertIsInstance(metaclass, nodes.ClassDef) |
| self.assertEqual(metaclass.name, "type") |
| |
| def test_metaclass_error(self) -> None: |
| astroid = self.builder.string_build("class Test(metaclass=typ): pass") |
| klass = astroid.body[0] |
| self.assertFalse(klass.metaclass()) |
| |
| def test_metaclass_imported(self) -> None: |
| astroid = self.builder.string_build( |
| dedent( |
| """ |
| from abc import ABCMeta |
| class Test(metaclass=ABCMeta): pass""" |
| ) |
| ) |
| klass = astroid.body[1] |
| |
| metaclass = klass.metaclass() |
| self.assertIsInstance(metaclass, nodes.ClassDef) |
| self.assertEqual(metaclass.name, "ABCMeta") |
| |
| def test_metaclass_multiple_keywords(self) -> None: |
| astroid = self.builder.string_build( |
| "class Test(magic=None, metaclass=type): pass" |
| ) |
| klass = astroid.body[0] |
| |
| metaclass = klass.metaclass() |
| self.assertIsInstance(metaclass, nodes.ClassDef) |
| self.assertEqual(metaclass.name, "type") |
| |
| def test_as_string(self) -> None: |
| body = dedent( |
| """ |
| from abc import ABCMeta |
| class Test(metaclass=ABCMeta): pass""" |
| ) |
| astroid = self.builder.string_build(body) |
| klass = astroid.body[1] |
| |
| self.assertEqual( |
| klass.as_string(), "\n\nclass Test(metaclass=ABCMeta):\n pass\n" |
| ) |
| |
| def test_old_syntax_works(self) -> None: |
| astroid = self.builder.string_build( |
| dedent( |
| """ |
| class Test: |
| __metaclass__ = type |
| class SubTest(Test): pass |
| """ |
| ) |
| ) |
| klass = astroid["SubTest"] |
| metaclass = klass.metaclass() |
| self.assertIsNone(metaclass) |
| |
| def test_metaclass_yes_leak(self) -> None: |
| astroid = self.builder.string_build( |
| dedent( |
| """ |
| # notice `ab` instead of `abc` |
| from ab import ABCMeta |
| |
| class Meta(metaclass=ABCMeta): pass |
| """ |
| ) |
| ) |
| klass = astroid["Meta"] |
| self.assertIsNone(klass.metaclass()) |
| |
| def test_parent_metaclass(self) -> None: |
| astroid = self.builder.string_build( |
| dedent( |
| """ |
| from abc import ABCMeta |
| class Test(metaclass=ABCMeta): pass |
| class SubTest(Test): pass |
| """ |
| ) |
| ) |
| klass = astroid["SubTest"] |
| self.assertTrue(klass.newstyle) |
| metaclass = klass.metaclass() |
| self.assertIsInstance(metaclass, nodes.ClassDef) |
| self.assertEqual(metaclass.name, "ABCMeta") |
| |
| def test_metaclass_ancestors(self) -> None: |
| astroid = self.builder.string_build( |
| dedent( |
| """ |
| from abc import ABCMeta |
| |
| class FirstMeta(metaclass=ABCMeta): pass |
| class SecondMeta(metaclass=type): |
| pass |
| |
| class Simple: |
| pass |
| |
| class FirstImpl(FirstMeta): pass |
| class SecondImpl(FirstImpl): pass |
| class ThirdImpl(Simple, SecondMeta): |
| pass |
| """ |
| ) |
| ) |
| classes = {"ABCMeta": ("FirstImpl", "SecondImpl"), "type": ("ThirdImpl",)} |
| for metaclass, names in classes.items(): |
| for name in names: |
| impl = astroid[name] |
| meta = impl.metaclass() |
| self.assertIsInstance(meta, nodes.ClassDef) |
| self.assertEqual(meta.name, metaclass) |
| |
| def test_annotation_support(self) -> None: |
| astroid = self.builder.string_build( |
| dedent( |
| """ |
| def test(a: int, b: str, c: None, d, e, |
| *args: float, **kwargs: int)->int: |
| pass |
| """ |
| ) |
| ) |
| func = astroid["test"] |
| self.assertIsInstance(func.args.varargannotation, nodes.Name) |
| self.assertEqual(func.args.varargannotation.name, "float") |
| self.assertIsInstance(func.args.kwargannotation, nodes.Name) |
| self.assertEqual(func.args.kwargannotation.name, "int") |
| self.assertIsInstance(func.returns, nodes.Name) |
| self.assertEqual(func.returns.name, "int") |
| arguments = func.args |
| self.assertIsInstance(arguments.annotations[0], nodes.Name) |
| self.assertEqual(arguments.annotations[0].name, "int") |
| self.assertIsInstance(arguments.annotations[1], nodes.Name) |
| self.assertEqual(arguments.annotations[1].name, "str") |
| self.assertIsInstance(arguments.annotations[2], nodes.Const) |
| self.assertIsNone(arguments.annotations[2].value) |
| self.assertIsNone(arguments.annotations[3]) |
| self.assertIsNone(arguments.annotations[4]) |
| |
| astroid = self.builder.string_build( |
| dedent( |
| """ |
| def test(a: int=1, b: str=2): |
| pass |
| """ |
| ) |
| ) |
| func = astroid["test"] |
| self.assertIsInstance(func.args.annotations[0], nodes.Name) |
| self.assertEqual(func.args.annotations[0].name, "int") |
| self.assertIsInstance(func.args.annotations[1], nodes.Name) |
| self.assertEqual(func.args.annotations[1].name, "str") |
| self.assertIsNone(func.returns) |
| |
| def test_kwonlyargs_annotations_supper(self) -> None: |
| node = self.builder.string_build( |
| dedent( |
| """ |
| def test(*, a: int, b: str, c: None, d, e): |
| pass |
| """ |
| ) |
| ) |
| func = node["test"] |
| arguments = func.args |
| self.assertIsInstance(arguments.kwonlyargs_annotations[0], nodes.Name) |
| self.assertEqual(arguments.kwonlyargs_annotations[0].name, "int") |
| self.assertIsInstance(arguments.kwonlyargs_annotations[1], nodes.Name) |
| self.assertEqual(arguments.kwonlyargs_annotations[1].name, "str") |
| self.assertIsInstance(arguments.kwonlyargs_annotations[2], nodes.Const) |
| self.assertIsNone(arguments.kwonlyargs_annotations[2].value) |
| self.assertIsNone(arguments.kwonlyargs_annotations[3]) |
| self.assertIsNone(arguments.kwonlyargs_annotations[4]) |
| |
| def test_annotation_as_string(self) -> None: |
| code1 = dedent( |
| """ |
| def test(a, b: int = 4, c=2, f: 'lala' = 4) -> 2: |
| pass""" |
| ) |
| code2 = dedent( |
| """ |
| def test(a: typing.Generic[T], c: typing.Any = 24) -> typing.Iterable: |
| pass""" |
| ) |
| for code in (code1, code2): |
| func = extract_node(code) |
| self.assertEqual(func.as_string(), code) |
| |
| def test_unpacking_in_dicts(self) -> None: |
| code = "{'x': 1, **{'y': 2}}" |
| node = extract_node(code) |
| self.assertEqual(node.as_string(), code) |
| assert isinstance(node, nodes.Dict) |
| keys = [key for (key, _) in node.items] |
| self.assertIsInstance(keys[0], nodes.Const) |
| self.assertIsInstance(keys[1], nodes.DictUnpack) |
| |
| def test_nested_unpacking_in_dicts(self) -> None: |
| code = "{'x': 1, **{'y': 2, **{'z': 3}}}" |
| node = extract_node(code) |
| self.assertEqual(node.as_string(), code) |
| |
| def test_unpacking_in_dict_getitem(self) -> None: |
| node = extract_node("{1:2, **{2:3, 3:4}, **{5: 6}}") |
| for key, expected in ((1, 2), (2, 3), (3, 4), (5, 6)): |
| value = node.getitem(nodes.Const(key)) |
| self.assertIsInstance(value, nodes.Const) |
| self.assertEqual(value.value, expected) |
| |
| def test_format_string(self) -> None: |
| code = "f'{greetings} {person}'" |
| node = extract_node(code) |
| self.assertEqual(node.as_string(), code) |
| |
| def test_underscores_in_numeral_literal(self) -> None: |
| pairs = [("10_1000", 101000), ("10_000_000", 10000000), ("0x_FF_FF", 65535)] |
| for value, expected in pairs: |
| node = extract_node(value) |
| inferred = next(node.infer()) |
| self.assertIsInstance(inferred, nodes.Const) |
| self.assertEqual(inferred.value, expected) |
| |
| def test_async_comprehensions(self) -> None: |
| async_comprehensions = [ |
| extract_node( |
| "async def f(): return __([i async for i in aiter() if i % 2])" |
| ), |
| extract_node( |
| "async def f(): return __({i async for i in aiter() if i % 2})" |
| ), |
| extract_node( |
| "async def f(): return __((i async for i in aiter() if i % 2))" |
| ), |
| extract_node( |
| "async def f(): return __({i: i async for i in aiter() if i % 2})" |
| ), |
| ] |
| non_async_comprehensions = [ |
| extract_node("async def f(): return __({i: i for i in iter() if i % 2})") |
| ] |
| |
| for comp in async_comprehensions: |
| self.assertTrue(comp.generators[0].is_async) |
| for comp in non_async_comprehensions: |
| self.assertFalse(comp.generators[0].is_async) |
| |
| @require_version("3.7") |
| def test_async_comprehensions_outside_coroutine(self): |
| # When async and await will become keywords, async comprehensions |
| # will be allowed outside of coroutines body |
| comprehensions = [ |
| "[i async for i in aiter() if condition(i)]", |
| "[await fun() async for fun in funcs]", |
| "{await fun() async for fun in funcs}", |
| "{fun: await fun() async for fun in funcs}", |
| "[await fun() async for fun in funcs if await smth]", |
| "{await fun() async for fun in funcs if await smth}", |
| "{fun: await fun() async for fun in funcs if await smth}", |
| "[await fun() async for fun in funcs]", |
| "{await fun() async for fun in funcs}", |
| "{fun: await fun() async for fun in funcs}", |
| "[await fun() async for fun in funcs if await smth]", |
| "{await fun() async for fun in funcs if await smth}", |
| "{fun: await fun() async for fun in funcs if await smth}", |
| ] |
| |
| for comp in comprehensions: |
| node = extract_node(comp) |
| self.assertTrue(node.generators[0].is_async) |
| |
| def test_async_comprehensions_as_string(self) -> None: |
| func_bodies = [ |
| "return [i async for i in aiter() if condition(i)]", |
| "return [await fun() for fun in funcs]", |
| "return {await fun() for fun in funcs}", |
| "return {fun: await fun() for fun in funcs}", |
| "return [await fun() for fun in funcs if await smth]", |
| "return {await fun() for fun in funcs if await smth}", |
| "return {fun: await fun() for fun in funcs if await smth}", |
| "return [await fun() async for fun in funcs]", |
| "return {await fun() async for fun in funcs}", |
| "return {fun: await fun() async for fun in funcs}", |
| "return [await fun() async for fun in funcs if await smth]", |
| "return {await fun() async for fun in funcs if await smth}", |
| "return {fun: await fun() async for fun in funcs if await smth}", |
| ] |
| for func_body in func_bodies: |
| code = dedent( |
| f""" |
| async def f(): |
| {func_body}""" |
| ) |
| func = extract_node(code) |
| self.assertEqual(func.as_string().strip(), code.strip()) |
| |
| |
| if __name__ == "__main__": |
| unittest.main() |