summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog3
-rw-r--r--checkers/classes.py12
-rw-r--r--test/input/func_bad_context_manager.py53
-rw-r--r--test/messages/func_bad_context_manager.txt2
4 files changed, 70 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog
index c7a65af..f232a66 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,6 +2,9 @@ ChangeLog for Pylint
====================
--
+ * Add 'bad-context-manager' error, checking that '__exit__'
+ special method accepts the right number of arguments.
+
* Run pylint as a python module 'python -m pylint' (anatoly techtonik)
* Check for non-exception classes inside an except clause
diff --git a/checkers/classes.py b/checkers/classes.py
index 4c8022d..7ced271 100644
--- a/checkers/classes.py
+++ b/checkers/classes.py
@@ -152,6 +152,11 @@ MSGS = {
'non-iterator-returned',
'Used when an __iter__ method returns something which is not an \
iterable (i.e. has no `%s` method)' % NEXT_METHOD),
+ 'E0235': ('__exit__ must accept 3 arguments: type, value, traceback',
+ 'bad-context-manager',
+ 'Used when the __exit__ special method, belonging to a \
+ context manager, does not accept 3 arguments \
+ (type, value, traceback).')
}
@@ -325,6 +330,8 @@ a metaclass class method.'}
# check non-iterators in __iter__
if node.name == '__iter__':
self._check_iter(node)
+ elif node.name == '__exit__':
+ self._check_exit(node)
def _check_iter(self, node):
try:
@@ -344,6 +351,11 @@ a metaclass class method.'}
node=node)
break
+ def _check_exit(self, node):
+ positional = sum(1 for arg in node.args.args if arg.name != 'self')
+ if positional != 3 and not node.args.vararg:
+ self.add_message('bad-context-manager',
+ node=node)
def leave_function(self, node):
"""on method node, check if this method couldn't be a function
diff --git a/test/input/func_bad_context_manager.py b/test/input/func_bad_context_manager.py
new file mode 100644
index 0000000..620190d
--- /dev/null
+++ b/test/input/func_bad_context_manager.py
@@ -0,0 +1,53 @@
+"""Check that __exit__ special method accepts 3 arguments """
+
+# pylint: disable=too-few-public-methods, invalid-name
+
+__revision__ = 0
+
+class FirstGoodContextManager(object):
+ """ 3 arguments """
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, value, tb):
+ pass
+
+class SecondGoodContextManager(object):
+ """ 3 keyword arguments """
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type=None, value=None, tb=None):
+ pass
+
+class ThirdGoodContextManager(object):
+ """ 1 argument and variable arguments """
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, *args):
+ pass
+
+class FirstBadContextManager(object):
+ """ 1 argument """
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type):
+ pass
+
+class SecondBadContextManager(object):
+ """ Too many arguments """
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, value, tb, stack):
+ pass
+
+
+
diff --git a/test/messages/func_bad_context_manager.txt b/test/messages/func_bad_context_manager.txt
new file mode 100644
index 0000000..1111e6d
--- /dev/null
+++ b/test/messages/func_bad_context_manager.txt
@@ -0,0 +1,2 @@
+E: 40:FirstBadContextManager.__exit__: __exit__ must accept 3 arguments: type, value, traceback
+E: 49:SecondBadContextManager.__exit__: __exit__ must accept 3 arguments: type, value, traceback