summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFELD Boris <lothiraldan@gmail.com>2012-09-19 14:39:05 +0200
committerFELD Boris <lothiraldan@gmail.com>2012-09-19 14:39:05 +0200
commit10e92212351ce62e213c068b99833cd573df7ce1 (patch)
treed0691f95160ba4a6d9aaa634e02e17d628ef95ce
parent44db90cd6c79d9b25d55f30b5576a05d2c4d79f2 (diff)
downloadpylint-10e92212351ce62e213c068b99833cd573df7ce1.tar.gz
Add test and code for handling __all__ with pylint. Closes #4685.
-rw-r--r--ChangeLog9
-rw-r--r--checkers/variables.py18
-rw-r--r--test/input/func_all.py45
-rw-r--r--test/messages/func_all.txt4
4 files changed, 71 insertions, 5 deletions
diff --git a/ChangeLog b/ChangeLog
index e45a3ed..d23d5b6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -5,12 +5,15 @@ ChangeLog for PyLint
* #100707: check for boolop being used as exception class, introducing
new W0711 message (patch by Tim Hatch)
+ * #4014: improve checking of metaclass methods first args, introducing
+ new C0204 message (patch by lothiraldan@gmail.com finalized by sthenault)
+
+ * #4685: check for consistency of a module's __all__ variable,
+ introducing new E0603 message
+
* #100654: fix grammatical error for W0332 message (using 'l' as
long int identifier)
- * #4014: Improve checking of metaclass methods first args, introducing
- new C0204 message (patch by lothiraldan@gmail.com finalized by sthenault)
-
* fix cross-interpreter issue (non compatible access to __builtins__)
* stop including tests files in distribution, they causes crash when
diff --git a/checkers/variables.py b/checkers/variables.py
index f0abd1b..c6da73a 100644
--- a/checkers/variables.py
+++ b/checkers/variables.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2003-2011 LOGILAB S.A. (Paris, FRANCE).
+# Copyright (c) 2003-2012 LOGILAB S.A. (Paris, FRANCE).
# http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This program is free software; you can redistribute it and/or modify it under
@@ -58,6 +58,8 @@ MSGS = {
assignment.'),
'E0602': ('Undefined variable %r',
'Used when an undefined variable is accessed.'),
+ 'E0603': ('Undefined variable name %r in __all__',
+ 'Used when an undefined variable name is referenced in __all__.'),
'E0611': ('No name %r in module %r',
'Used when a name cannot be found in a module.'),
@@ -105,6 +107,7 @@ class VariablesChecker(BaseChecker):
* undefined variables
* redefinition of variable from builtins or from an outer scope
* use of variable before assignment
+ * __all__ consistency
"""
__implements__ = IASTNGChecker
@@ -152,6 +155,17 @@ builtins. Remember that you should avoid to define new builtins when possible.'
"""
assert len(self._to_consume) == 1
not_consumed = self._to_consume.pop()[0]
+ # attempt to check for __all__ if defined
+ if '__all__' in node.locals:
+ assigned = node.igetattr('__all__').next()
+ for elt in getattr(assigned, 'elts', ()):
+ elt_name = elt.value
+ # If elt is in not_consumed, remove it from not_consumed
+ if elt_name in not_consumed:
+ del not_consumed[elt_name]
+ continue
+ if elt_name not in node.locals:
+ self.add_message('E0603', args=elt_name, node=elt)
# don't check unused imports in __init__ files
if not self.config.init_import and node.package:
return
@@ -375,7 +389,7 @@ builtins. Remember that you should avoid to define new builtins when possible.'
def visit_assname(self, node):
if isinstance(node.ass_type(), astng.AugAssign):
self.visit_name(node)
-
+
def visit_delname(self, node):
self.visit_name(node)
diff --git a/test/input/func_all.py b/test/input/func_all.py
new file mode 100644
index 0000000..e9f1fc8
--- /dev/null
+++ b/test/input/func_all.py
@@ -0,0 +1,45 @@
+"""Test Pylint's use of __all__.
+
+* NonExistant is not defined in this module, and it is listed in
+ __all__. An error is expected.
+
+* This module imports path and republished it in __all__. No errors
+ are expected.
+"""
+# pylint: disable=R0903,R0201,W0612
+
+__revision__ = 0
+
+from os import path
+
+__all__ = [
+ 'Dummy',
+ 'NonExistant',
+ 'path',
+ 'func',
+ 'inner',
+ 'InnerKlass']
+
+
+class Dummy(object):
+ """A class defined in this module."""
+ pass
+
+DUMMY = Dummy()
+
+def function():
+ """Function docstring
+ """
+ pass
+
+function()
+
+class Klass(object):
+ """A klass which contains a function"""
+ def func(self):
+ """A klass method"""
+ inner = None
+
+ class InnerKlass(object):
+ """A inner klass"""
+ pass
diff --git a/test/messages/func_all.txt b/test/messages/func_all.txt
new file mode 100644
index 0000000..ba0e855
--- /dev/null
+++ b/test/messages/func_all.txt
@@ -0,0 +1,4 @@
+E: 17: Undefined variable name 'NonExistant' in __all__
+E: 19: Undefined variable name 'func' in __all__
+E: 20: Undefined variable name 'inner' in __all__
+E: 21: Undefined variable name 'InnerKlass' in __all__