summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsyt <devnull@localhost>2006-05-05 14:20:44 +0000
committersyt <devnull@localhost>2006-05-05 14:20:44 +0000
commita120206e378545016e1b480724ee9b53e875f85b (patch)
treeaf2c92d72b7651691de456057f8faede3b983daf
parenteea76f1da01a33dec2afc42119e001e4350aaea2 (diff)
downloadpylint-a120206e378545016e1b480724ee9b53e875f85b.tar.gz
* new W0212 message for access to protected member from client code
* new W0212 message for access to protected member from client code * new W0105 message extracted from W0104 (statement seems to have no effect) with string
-rw-r--r--ChangeLog5
-rw-r--r--README119
-rw-r--r--checkers/base.py10
-rw-r--r--checkers/classes.py53
-rw-r--r--test/input/func_base_stmt_without_effect.py3
-rw-r--r--test/input/func_block_disable_msg.py48
-rw-r--r--test/input/func_class_access_protected_members.py30
-rw-r--r--test/input/func_format.py2
-rw-r--r--test/input/func_w0233.py2
-rw-r--r--test/messages/func_base_stmt_without_effect.txt1
-rw-r--r--test/messages/func_class_access_protected_members.txt3
-rw-r--r--utils.py7
12 files changed, 145 insertions, 138 deletions
diff --git a/ChangeLog b/ChangeLog
index 43305a9..846d829 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,11 @@
ChangeLog for PyLint
====================
+ --
+ * new W0212 message for access to protected member from client code
+ * new W0105 message extracted from W0104 (statement seems to have no effect)
+ with string
+
2006-04-19 -- 0.11.0
* fix crash caused by the exceptions checker in some case
* fix some E1101 false positive with abstract method or classes defining
diff --git a/README b/README
index ea4565f..fc5aaba 100644
--- a/README
+++ b/README
@@ -4,129 +4,26 @@ README for PyLint
Dependencies
------------
Pylint requires the logilab-astng (version >= 0.14), logilab-common
-(version >= 0.13) and the optik (only for python < 2.3) packages.
+(version >= 0.13) and the optik (only for python < 2.3) packages.
+Pylint should be compatible with any python >= 2.2.
* http://www.logilab.org/projects/astng
* http://www.logilab.org/projects/common
* http://optik.sourceforge.net/
-Distributions
--------------
-The source tarball is available at ftp://ftp.logilab.fr/pub/pylint.
-
-You may apt-get a debian package by adding ::
-
- deb ftp://ftp.logilab.org/pub/debian unstable/
-
-to your /etc/apt/sources.list files. Pylint is also available in the standard Debian distribution
-
-Contributed RPM packages for pylint and logilab-common are available at
-ftp://ftp.nest.pld-linux.org/test .
-
-Pylint is also available in Gentoo, Fedora 4, Ubuntu, FreeBSD, Darwin.
-
-
Install
-------
From the source distribution, extract the tarball and run ::
python setup.py install
-For debian and rpm packages, use your usual tools according to your Linux
-distribution.
-
-Note for Windows users:
-On Windows, once you have installed pylint, the command line usage is
-pylint.bat [options] module_or_package
-
-But this will only work if pylint.bat is either in the current
-directory, or on your system path. (setup.py install install python.bat
-to the Scripts subdirectory of your Python installation -- e.g.
-C:\Python24\Scripts.) You can do any of the following to solve this:
-
-1. change to the appropriate directory before running pylint.bat
-
-2. add the Scripts directory to your path statement in your autoexec.bat
- file (this file is found in the root directory of your boot-drive)
-
-3. create a 'redirect' batch file in a directory actually on your
- systems path
-
-To effect (2), simply append the appropriate directory name to the PATH=
-statement in autoexec.bat. Be sure to use the Windows directory
-separator of ';' between entries. Then, once you have rebooted (this is
-necessary so that the new path statement will take effect when
-autoexec.bat is run), you will be able to invoke PyLint with
-pylint.bat on the command line.
-
-(3) is the best solution. Once done, you can call pylint at the command
-line without the .bat, just as do non-Windows users by typing: ::
-
- pylint [options] module_or_package
-
-To effect option (3), simply create a plain text file pylint.bat with
-the single line: ::
-
- C:\PythonDirectory\Scripts\pylint.bat
-
-(where PythonDirectory is replaced by the actual Python installation
-directory on your system -- e.g. C:\Python24\Scripts\pylint.bat).
-
-
-IDE integration
----------------
-
-Pylint is integrated in the following editors/IDEs:
-
- * emacs (of course)
-
- * eric3
-
- * eclipse (using the pydev_ plugin, see also http://msdl.cs.mcgill.ca/MSDL/people/denis/meetings/pythonDev)
-
-To use pylint from within vim, see http://www.gonzo.kiev.ua/projects/pylint.vim
-
-_pydev: http://pydev.sourceforge.net
-
-Some projects using Pylint
---------------------------
-
- * The CheeseCake kwalitee reporting tool uses pylint to analyze the source code.
-
-The following projects use pylint to help develop better code:
-
- * http://browsershots.org
-
- * OSAF Chandler (http://www.osafoundation.org/)
-
- * CPS (http://www.nuxeo.org)
-
- * Xen (http://www.xensource.com/)
-
- * pyxmpp (http://pyxmpp.jabberstudio.org/)
-
- * eXe (http://exelearning.org/)
-
- * PrimaGIS (http://www.primagis.org)
-
- * python-cdd (http://projetos.ossystems.com.br/python-cdd/)
-
- * CDSWare (http://cdsware.cern.ch/)
-
- * ASE (http://dcwww.camp.dtu.dk/campos/ASE/intro.html)
-
- * RunJob (http://projects.fnal.gov/runjob/)
-
- * Slugathon (http://slugathon.python-hosting.com/)
-
- * mercurial
-
- * Topographica (http://topographica.org/Home/index.html) (at least they intend to do so)
+You'll have to install dependancies in a similar way. For debian and
+rpm packages, use your usual tools according to your Linux distribution.
- * ERP5 (http://www.erp5.org/)
+More information about installation and available distribution format
+may be found in the user manual in the *doc* subdirectory.
- * many more...
Documentation
-------------
@@ -145,12 +42,10 @@ http://www.logilab.org/mailinglists/python_projects/mailinglist_register_form
Archives are available at
http://lists.logilab.org/pipermail/python-projects/
-If you prefer speaking french instead of english, you can use the
-generic forum-fr@logilab.org mailing list.
Contributors
------------
-* Sylvain Thenault: main author / maintainer
+* Sylvain Thénault: main author / maintainer
* Alexandre Fayolle: TkInter gui, documentation, debian support
* Brian van den Broek: windows installation documentation
* Amaury Forgeot d'Arc: patch to check names imported from a module
diff --git a/checkers/base.py b/checkers/base.py
index 7d852ea..8b103af 100644
--- a/checkers/base.py
+++ b/checkers/base.py
@@ -113,6 +113,11 @@ MSGS = {
'W0104': ('Statement seems to have no effect',
'Used when a statement doesn\'t have (or at least seems to) \
any effect.'),
+ 'W0105': ('String statement has no effect',
+ 'Used when a string is used as a statement (which of course \
+ has no effect). This is a particular case of W0104 with its \
+ own message so you can easily disable it if you\'re using \
+ those strings as documentation, instead of comments.'),
'W0122': ('Use of the exec statement',
'Used when you use the "exec" statement, to discourage its \
@@ -304,7 +309,10 @@ functions, methods
def visit_discard(self, node):
"""check for statement without effect"""
- if not isinstance(node.expr, astng.CallFunc):
+ if isinstance(node.expr, astng.Const) and \
+ isinstance(node.expr.value, basestring):
+ self.add_message('W0105', node=node)
+ elif not isinstance(node.expr, astng.CallFunc):
self.add_message('W0104', node=node)
def visit_function(self, node):
diff --git a/checkers/classes.py b/checkers/classes.py
index 7c0e551..3a9e468 100644
--- a/checkers/classes.py
+++ b/checkers/classes.py
@@ -26,25 +26,11 @@ from pylint.checkers import BaseChecker
from pylint.checkers.utils import overrides_a_method
MSGS = {
-## 'F0201': ('Unable to check method %r of interface %s',
-## 'Used when PyLint has been unable to fetch a
- ##method declared in \
-## an interface (either in the class or in the
- ##interface) and so to\
-## check its implementation.'),
'F0202': ('Unable to check methods signature (%s / %s)',
'Used when PyLint has been unable to check methods signature \
compatibility for an unexpected raison. Please report this kind \
if you don\'t make sense of it.'),
-## 'F0203': ('Unable to resolve %s',
-## 'Used when PyLint has been unable to resolve a name.'),
-## 'F0204': ('Name %s has not been resolved to a class as expected',
-## 'Used when PyLint try to resolve an ancestor class name but \
-## gets something else than a Class node.'),
-## 'E0201': ('Access to undefined member %r',
-## 'Used when an instance member not defined in the instance, its\
-## class or its ancestors is accessed.'),
'E0202': ('An attribute inherited from %s hide this method',
'Used when a class defines a method which is hiden by an \
instance attribute from an ancestor class.'),
@@ -55,12 +41,18 @@ MSGS = {
'Used when an instance attribute is defined outside the __init__\
method.'),
+ 'W0212': ('Access to a protected member %s of a client class', # E0214
+ 'Used when a protected member (i.e. class member with a name \
+ beginning with an underscore) is access outside the class or a \
+ descendant of the class where it\'s defined.'),
+
'E0211': ('Method has no argument',
'Used when a method which should have the bound instance as \
first argument has no argument defined.'),
'E0213': ('Method should have "self" as first argument',
'Used when a method has an attribute different the "self" as\
- first argument.'),
+ first argument. This is considered as an error since this is\
+ a soooo common convention that you should\'nt break it!'),
'C0202': ('Class method should have "cls" as first argument', # E0212
'Used when a class method has an attribute different than "cls"\
as first argument, to easily differentiate them from regular \
@@ -68,6 +60,7 @@ MSGS = {
'C0203': ('Metaclass method should have "mcs" as first argument', # E0214
'Used when a metaclass method has an attribute different the \
"mcs" as first argument.'),
+
'W0211': ('Static method with %r as first argument',
'Used when a static method has "self" or "cls" as first argument.'
),
@@ -257,13 +250,35 @@ instance attributes.'}
self.add_message('R0201', node=node)
def visit_getattr(self, node):
- """check if the name handle an access to a class member
- if so, register it
+ """check if the getattr is an access to a class member
+ if so, register it. Also check for access to protected
+ class member from outside its class (but ignore __special__
+ methods)
"""
+ attrname = node.attrname
if self._first_attrs and isinstance(node.expr, astng.Name) and \
node.expr.name == self._first_attrs[-1]:
- self._accessed[-1].setdefault(node.attrname, []).append(node)
-
+ self._accessed[-1].setdefault(attrname, []).append(node)
+ elif attrname[0] == '_' and not (attrname.startswith('__') and
+ attrname.endswith('__')):
+ # XXX move this in a reusable function
+ klass = node.frame()
+ while klass is not None and not isinstance(klass, astng.Class):
+ if klass.parent is None:
+ klass = None
+ else:
+ klass = klass.parent.frame()
+ # XXX infer to be more safe and less dirty ??
+ # in classes, check we are not getting a parent method
+ # through the class object or through super
+ if klass is None or not (
+ node.expr.as_string() in klass.basenames
+ or (isinstance(node.expr, astng.CallFunc)
+ and isinstance(node.expr.node, astng.Name)
+ and node.expr.node.name == 'super')):
+ self.add_message('W0212', node=node, args=attrname)
+
+
def visit_name(self, node):
"""check if the name handle an access to a class member
if so, register it
diff --git a/test/input/func_base_stmt_without_effect.py b/test/input/func_base_stmt_without_effect.py
index f282ff7..f9ff608 100644
--- a/test/input/func_base_stmt_without_effect.py
+++ b/test/input/func_base_stmt_without_effect.py
@@ -13,3 +13,6 @@ __revision__ <= 1
__revision__.lower() # ok
[i for i in __revision__] # ko
+
+
+"""inline doc string should use a separated message"""
diff --git a/test/input/func_block_disable_msg.py b/test/input/func_block_disable_msg.py
index f82fbd5..7de7acd 100644
--- a/test/input/func_block_disable_msg.py
+++ b/test/input/func_block_disable_msg.py
@@ -85,3 +85,51 @@ class Foo(object):
# no error
print self.bla
print self.blop
+
+
+class ClassLevelMessage(object):
+ """should'nt display to much attributes/not enough methods messages
+ """
+ # pylint: disable-msg=R0902,R0903
+
+ def __init__(self):
+ self.attr1 = 1
+ self.attr2 = 1
+ self.attr3 = 1
+ self.attr4 = 1
+ self.attr5 = 1
+ self.attr6 = 1
+ self.attr7 = 1
+ self.attr8 = 1
+ self.attr9 = 1
+ self.attr0 = 1
+
+ def too_complex_but_thats_ok(self, attr1, attr2):
+ """THIS Method has too much branchs but i don't care
+ """
+ # pylint: disable-msg=R0912
+ try:
+ attr3 = attr1+attr2
+ except ValueError:
+ attr3 = None
+ except:
+ print 'duh', self
+ if attr1:
+ for i in attr1:
+ if attr2:
+ print i
+ else:
+ print 'duh'
+ elif attr2:
+ for i in attr2:
+ if attr2:
+ print i
+ else:
+ print 'duh'
+ else:
+ for i in range(15):
+ if attr3:
+ print i
+ else:
+ print 'doh'
+
diff --git a/test/input/func_class_access_protected_members.py b/test/input/func_class_access_protected_members.py
new file mode 100644
index 0000000..f588953
--- /dev/null
+++ b/test/input/func_class_access_protected_members.py
@@ -0,0 +1,30 @@
+# pylint: disable-msg=R0903
+"""test external access to protected class members"""
+
+__revision__ = ''
+
+class MyClass:
+ """class docstring"""
+ _cls_protected = 5
+
+ def __init__(self, other):
+ """init"""
+ self._protected = 1
+ self.public = other
+
+
+ def test(self):
+ """test"""
+ self._protected += self._cls_protected
+ print self.public._haha
+
+ def clsmeth(cls):
+ """this is ok"""
+ print cls._cls_protected
+ clsmeth = classmethod(clsmeth)
+
+INST = MyClass()
+print INST.public
+print INST._protected
+print INST._cls_protected
+
diff --git a/test/input/func_format.py b/test/input/func_format.py
index da66d63..98a9f9a 100644
--- a/test/input/func_format.py
+++ b/test/input/func_format.py
@@ -1,4 +1,4 @@
-# pylint:disable-msg=C0103,W0104
+# pylint:disable-msg=C0103,W0104,W0105
"""Check format
"""
__revision__ = ''
diff --git a/test/input/func_w0233.py b/test/input/func_w0233.py
index 763ccf0..1d32b95 100644
--- a/test/input/func_w0233.py
+++ b/test/input/func_w0233.py
@@ -1,4 +1,4 @@
-# pylint: disable-msg=R0903
+# pylint: disable-msg=R0903,W0212
"""test for call to __init__ from a non ancestor class
"""
diff --git a/test/messages/func_base_stmt_without_effect.txt b/test/messages/func_base_stmt_without_effect.txt
index b50fd82..e1cce0c 100644
--- a/test/messages/func_base_stmt_without_effect.txt
+++ b/test/messages/func_base_stmt_without_effect.txt
@@ -1,3 +1,4 @@
W: 9: Statement seems to have no effect
W: 11: Statement seems to have no effect
W: 15: Statement seems to have no effect
+W: 18: String statement has no effect
diff --git a/test/messages/func_class_access_protected_members.txt b/test/messages/func_class_access_protected_members.txt
new file mode 100644
index 0000000..8c7bb02
--- /dev/null
+++ b/test/messages/func_class_access_protected_members.txt
@@ -0,0 +1,3 @@
+W: 19:MyClass.test: Access to a protected member _haha of a client class
+W: 28: Access to a protected member _protected of a client class
+W: 29: Access to a protected member _cls_protected of a client class
diff --git a/utils.py b/utils.py
index 5b39212..7389a2a 100644
--- a/utils.py
+++ b/utils.py
@@ -136,13 +136,12 @@ class MessagesHandlerMixIn:
msg_id = self.check_message_id(msg_id)
if scope == 'module':
assert line > 0
- if msg_id != 'I0011':
- self.add_message('I0011', line=line, args=msg_id)
- #self._module_msgs_state[msg_id] = False
try:
self._module_msgs_state[msg_id][line] = False
except KeyError:
self._module_msgs_state[msg_id] = {line: False}
+ if msg_id != 'I0011':
+ self.add_message('I0011', line=line, args=msg_id)
else:
msgs = self._msgs_state
@@ -157,11 +156,11 @@ class MessagesHandlerMixIn:
msg_id = self.check_message_id(msg_id)
if scope == 'module':
assert line > 0
- self.add_message('I0012', line=line, args=msg_id)
try:
self._module_msgs_state[msg_id][line] = True
except KeyError:
self._module_msgs_state[msg_id] = {line: True}
+ self.add_message('I0012', line=line, args=msg_id)
else:
msgs = self._msgs_state
msgs[msg_id] = True