Skip to content
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

Bug 905 #913

Closed
wants to merge 23 commits into from
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a91c3af
Adds inference support for all typing types that are defined through …
hippo91 Feb 27, 2021
8ebc1e0
Instead of creating a new class (by the mean of TYPING_TYPE_TEMPLATE)…
hippo91 Feb 27, 2021
feaddca
Adds doc and type hints
hippo91 Feb 27, 2021
a53ee8d
Adds a unit test for inferring _alias function of the typing module
hippo91 Feb 27, 2021
63c3d96
Formatting according to black
hippo91 Feb 27, 2021
4fbfd10
Adds an entry
hippo91 Feb 27, 2021
b9a649f
Removes useless import
hippo91 Feb 27, 2021
6173010
Update pre-commit config
cdce8p Feb 22, 2021
b20cc9c
Update black version - tox
cdce8p Feb 22, 2021
04b0481
Fix end of files
cdce8p Feb 22, 2021
fe8587b
Fix trailing whitespaces
cdce8p Feb 22, 2021
4037368
Fix black issues
cdce8p Feb 22, 2021
b71974c
Adds inference support for all typing types that are defined through …
hippo91 Feb 27, 2021
ce5f01c
Instead of creating a new class (by the mean of TYPING_TYPE_TEMPLATE)…
hippo91 Feb 27, 2021
bf08bc1
Adds doc and type hints
hippo91 Feb 27, 2021
af0c14c
Adds a unit test for inferring _alias function of the typing module
hippo91 Feb 27, 2021
5fcb911
Formatting according to black
hippo91 Feb 27, 2021
a580ce5
Adds an entry
hippo91 Feb 27, 2021
e556215
Removes useless import
hippo91 Feb 27, 2021
b9c86d3
Takes into account @Pierre-Sassoulas remarks
hippo91 Feb 28, 2021
20a7b65
Enable _alias mocking and testing only if python version is at least 3.7
hippo91 Feb 28, 2021
3dcb5fb
Reformat (black)
hippo91 Feb 28, 2021
9694507
Merge branch 'bug_905' of https://github.com/hippo91/astroid into bug…
hippo91 Feb 28, 2021
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
8 changes: 8 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ Release Date: TBA

Closes #895 #899

* Fix the `Duplicates found in MROs` false positive.

Closes #905
Closes PyCQA/pylint#4093
Closes PyCQA/pylint#4131
Closes PyCQA/pylint#4145
Closes PyCQA/pylint#3247
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Closes PyCQA/pylint#4093
Closes PyCQA/pylint#4131
Closes PyCQA/pylint#4145
Closes PyCQA/pylint#3247
Closes PyCQA/pylint#2717
Closes PyCQA/pylint#3247
Closes PyCQA/pylint#4093
Closes PyCQA/pylint#4131
Closes PyCQA/pylint#4145

Copied from the PR description + sorted


What's New in astroid 2.5?
============================
Release Date: 2021-02-15
Expand Down
46 changes: 46 additions & 0 deletions astroid/brain/brain_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@
UseInferenceDefault,
extract_node,
inference_tip,
node_classes,
nodes,
context,
InferenceError,
)
import astroid


TYPING_NAMEDTUPLE_BASENAMES = {"NamedTuple", "typing.NamedTuple"}
Expand Down Expand Up @@ -85,6 +88,48 @@ def infer_typing_attr(node, context=None):
return node.infer(context=context)


GET_ITEM_TEMPLATE = """
@classmethod
def __getitem__(cls, value):
return cls
"""

Copy link
Member

@cdce8p cdce8p Feb 27, 2021

Choose a reason for hiding this comment

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

Suggested change
ABC_METACLASS_TEMPLATE = """
from abc import ABCMeta
ABCMeta
"""


def _looks_like_typing_alias(node: nodes.Call) -> bool:
"""
Returns True if the node corresponds to a call to _alias function.
For example :

MutableSet = _alias(collections.abc.MutableSet, T)

:param node: call node
"""
if isinstance(node, nodes.Call) and isinstance(node.func, nodes.Name):
if node.func.name == "_alias" and isinstance(node.args[0], nodes.Attribute):
return True
return False
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
if isinstance(node, nodes.Call) and isinstance(node.func, nodes.Name):
if node.func.name == "_alias" and isinstance(node.args[0], nodes.Attribute):
return True
return False
return (
isinstance(node, nodes.Call) and
isinstance(node.func, nodes.Name) and
node.func.name == "_alias" and
isinstance(node.args[0], nodes.Attribute)
)

Copy link
Member

Choose a reason for hiding this comment

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

It might make sense to take a look at #908 after that. I updated the pre-commit config to use a current version of black.

Copy link
Member

Choose a reason for hiding this comment

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

I merged #908, a rebase is probably necessary. There are a lot of pre-commit check that are in pylint and not in astroid, I might update astroid configuration once we release 2.7.2, upgraded black is enough for now.



def infer_typing_alias(
node: nodes.Call, context: context.InferenceContext = None
) -> node_classes.NodeNG:
"""
Infers the call to _alias function

:param node: call node
:param context: inference context
"""
if not isinstance(node, nodes.Call):
return
res = next(node.args[0].infer(context=context))
#  Needs to mock the __getitem__ class method so that
#  MutableSet[T] is acceptable
func_to_add = extract_node(GET_ITEM_TEMPLATE)
if res.metaclass():
res.metaclass().locals["__getitem__"] = [func_to_add]
Comment on lines +132 to +133
Copy link
Member

@cdce8p cdce8p Feb 27, 2021

Choose a reason for hiding this comment

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

Suggested change
if res.metaclass():
res.metaclass().locals["__getitem__"] = [func_to_add]
if res != astroid.Uninferable:
if not res.metaclass():
res._metaclass = extract_node(ABC_METACLASS_TEMPLATE)
res.metaclass().locals["__getitem__"] = [func_to_add]
else:
return

return res


MANAGER.register_transform(
nodes.Call,
inference_tip(infer_typing_typevar_or_newtype),
Expand All @@ -93,3 +138,4 @@ def infer_typing_attr(node, context=None):
MANAGER.register_transform(
nodes.Subscript, inference_tip(infer_typing_attr), _looks_like_typing_subscript
)
MANAGER.register_transform(nodes.Call, infer_typing_alias, _looks_like_typing_alias)
35 changes: 35 additions & 0 deletions tests/unittest_brain.py
Original file line number Diff line number Diff line change
Expand Up @@ -1188,6 +1188,41 @@ def test_typing_namedtuple_dont_crash_on_no_fields(self):
inferred = next(node.infer())
self.assertIsInstance(inferred, astroid.Instance)

def test_typing_alias_type(self):
"""
Test that the type aliased thanks to typing._alias function are
correctly inferred.
"""
node = builder.extract_node(
"""
from typing import TypeVar, MutableSet

T = TypeVar("T")
MutableSet[T]

class V(MutableSet[T]):
pass
"""
)
inferred = next(node.infer())
mro_entries = list(inferred.mro())
self.assertIsInstance(mro_entries[0], astroid.ClassDef)
self.assertEqual(mro_entries[0].name, "V")
self.assertIsInstance(mro_entries[1], astroid.ClassDef)
self.assertEqual(mro_entries[1].name, "MutableSet")
self.assertIsInstance(mro_entries[2], astroid.ClassDef)
self.assertEqual(mro_entries[2].name, "Set")
self.assertIsInstance(mro_entries[3], astroid.ClassDef)
self.assertEqual(mro_entries[3].name, "Collection")
self.assertIsInstance(mro_entries[4], astroid.ClassDef)
self.assertEqual(mro_entries[4].name, "Sized")
self.assertIsInstance(mro_entries[5], astroid.ClassDef)
self.assertEqual(mro_entries[5].name, "Iterable")
self.assertIsInstance(mro_entries[6], astroid.ClassDef)
self.assertEqual(mro_entries[6].name, "Container")
self.assertIsInstance(mro_entries[7], astroid.ClassDef)
self.assertEqual(mro_entries[7].name, "object")


class ReBrainTest(unittest.TestCase):
def test_regex_flags(self):
Expand Down