Refactor _check_protected_attribute_access by extracting independent part in utils functions. Improve is_super_call docstring.

authorSylvain Th?nault <sylvain.thenault@logilab.fr>
changeset21a44efb3abf
branchdefault
phasepublic
hiddenno
parent revision#40de65ba89b2 Add checking for protected attribute assignement, closes #7394.
child revision#e0547797db67 Closes #93591: Correctly emit W0623 on multiple assignment of unpackable exceptions
files modified by this revision
checkers/classes.py
checkers/utils.py
# HG changeset patch
# User Sylvain Thénault <sylvain.thenault@logilab.fr>
# Date 1339073644 -7200
# Thu Jun 07 14:54:04 2012 +0200
# Node ID 21a44efb3abf371ec2611dac307b1f599b72359e
# Parent 40de65ba89b2179f6eef826197f4adde6edb69e0
Refactor _check_protected_attribute_access by extracting independent part in utils functions. Improve is_super_call docstring.

diff --git a/checkers/classes.py b/checkers/classes.py
@@ -21,11 +21,11 @@
1  from logilab.astng import YES, Instance, are_exclusive, AssAttr
2 
3  from pylint.interfaces import IASTNGChecker
4  from pylint.checkers import BaseChecker
5  from pylint.checkers.utils import (PYMETHODS, overrides_a_method,
6 -    check_messages, is_attr_private)
7 +    check_messages, is_attr_private, is_attr_protected, node_frame_class)
8 
9  def class_is_abstract(node):
10      """return true if the given class node should be considered as an abstract
11      class
12      """
@@ -332,20 +332,13 @@
13          * Klass2._attr inside "Klass" class when Klass2 is a base class of
14              Klass.
15          '''
16          attrname = node.attrname
17 
18 -        if attrname[0] == '_' and not attrname == '_' and not (
19 -                attrname.startswith('__') and attrname.endswith('__')):
20 -
21 -            klass = node.frame()
22 +        if is_attr_protected(attrname):
23 
24 -            while klass is not None and not isinstance(klass, astng.Class):
25 -                if klass.parent is None:
26 -                    klass = None
27 -                else:
28 -                    klass = klass.parent.frame()
29 +            klass = node_frame_class(node)
30 
31              # XXX infer to be more safe and less dirty ??
32              # in classes, check we are not getting a parent method
33              # through the class object or through super
34              callee = node.expr.as_string()
diff --git a/checkers/utils.py b/checkers/utils.py
@@ -320,10 +320,38 @@
35              elif c != '%':
36                  num_args += 1
37          i += 1
38      return keys, num_args
39 
40 +def is_attr_protected(attrname):
41 +    """return True if attribute name is protected (start with _ and some other
42 +    details), False otherwise.
43 +    """
44 +    return attrname[0] == '_' and not attrname == '_' and not (
45 +             attrname.startswith('__') and attrname.endswith('__'))
46 +
47 +def node_frame_class(node):
48 +    """return klass node for a method node (or a staticmethod or a
49 +    classmethod), return null otherwise
50 +    """
51 +    klass = node.frame()
52 +
53 +    while klass is not None and not isinstance(klass, astng.Class):
54 +        if klass.parent is None:
55 +            klass = None
56 +        else:
57 +            klass = klass.parent.frame()
58 +
59 +    return klass
60 +
61 +def is_super_call(expr):
62 +    """return True if expression node is a function call and if function name
63 +    is super. Check before that you're in a method.
64 +    """
65 +    return (isinstance(expr, astng.CallFunc) and
66 +        isinstance(expr.func, astng.Name) and
67 +        expr.func.name == 'super')
68  def is_attr_private(attrname):
69      """Check that attribute name is private (at least two leading underscores,
70      at most one trailing underscore)
71      """
72      regex = re.compile('^_{2,}.*[^_]+_?$')