1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
# Copyright (c) 2016 Claudiu Popa <pcmanticore@gmail.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/master/COPYING.LESSER
"""Contains logic for retrieving special methods.
This implementation does not rely on the dot attribute access
logic, found in ``.getattr()``. The difference between these two
is that the dunder methods are looked with the type slots
(you can find more about these here
http://lucumr.pocoo.org/2014/8/16/the-python-i-would-like-to-see/)
As such, the lookup for the special methods is actually simpler than
the dot attribute access.
"""
import itertools
import astroid
from astroid import exceptions
from astroid import util
from astroid.interpreter import runtimeabc
from astroid.tree import treeabc
def _lookup_in_mro(node, name):
local_attrs = node.locals.get(name, [])
external_attrs = node.external_attrs.get(name, [])
attrs = itertools.chain(local_attrs, external_attrs)
nodes = itertools.chain.from_iterable(
itertools.chain(
ancestor.locals.get(name, []),
ancestor.external_attrs.get(name, [])
)
for ancestor in node.ancestors(recurs=True)
)
values = list(itertools.chain(attrs, nodes))
if not values:
raise exceptions.NotSupportedError
return values
@util.singledispatch
def lookup(node, name):
"""Lookup the given special method name in the given *node*
If the special method was found, then a list of attributes
will be returned. Otherwise, `astroid.NotSupportedError`
is going to be raised.
"""
raise exceptions.NotSupportedError
@lookup.register(treeabc.ClassDef)
def _(node, name):
metaclass = node.metaclass()
if metaclass is None:
raise exceptions.NotSupportedError
return _lookup_in_mro(metaclass, name)
@lookup.register(runtimeabc.Instance)
def _(node, name):
return _lookup_in_mro(node, name)
@lookup.register(runtimeabc.BuiltinInstance)
def _(node, name):
values = node.locals.get(name, [])
if not values:
raise exceptions.NotSupportedError
return values
|