On 25.1.2011 17:37, Jean-Michel Pichavant wrote:
> James Lingard wrote:
>> On Mon, Jan 24, 2011 at 6:14 PM, Sylvain Thénault
>> <[email protected]
>> <mailto:[email protected]>> wrote:
>>
>> On 24 janvier 14:57, James Lingard wrote:
>> > I would like the ability to disable the W0612 warning for
>> variables created
>> > as the result of a sequence unpacking operation. For example,
>> after:
>> >
>> > ( a, b, c, d, e ) = mytuple
>> >
>> > pylint will currently complain about all the unused variables.
>>
>> [snip] You could argue that it's better to have the variables names there, as
>> documentation for what the other components of the tuple are.
> This is a very good point. I switch to pylint style however, because it's
> really
> important to get no warning/errors at all, otherwise you're good for permanent
> analysis of your logs.
>
> Your original request make sense though. Don't know how difficult it would be
> to
> implement this in pylint.
>
I'm using modified version of pylint 0.20.0 which has something like this.
That is, I've added a separate warning for unused variables from repeated
tuple un/packing. This allows me do things like
mytuple = a, b, c, d, e
...
a, b, c, d, e = mytuple
in the same module without provoking W0612 (but just my new warning, which I
normally ignore). Since repeated identical un/packing is needed, W0612 should
still be generated when variable name list from tuple unpacking does not match
any other in the same module. In other words,
as, b, c, d, e = mytuple
should generate regular unused variable warning for all variables from that
unpacking, not just 'as'.
Diff attached. I have other modifications in pylint (should be stripped from
the diff), so line numbers probably won't match.
Best Regards,
Jaakko Salli
diff -u -r pylint-orig\checkers\variables.py pylint\checkers\variables.py
--- pylint-orig\checkers\variables.py Tue Mar 23 12:39:48 2010
+++ pylint\checkers\variables.py Fri Mar 26 12:36:45 2010
@@ -77,6 +77,9 @@
'W0614': ('Unused import %s from wildcard import',
'Used when an imported module or variable is not used from a \
\'from X import *\' style import.'),
+ 'W0617': ('Unused variable %r from repeated tuple unpack',
+ 'Used when a variable, that was unpacked from a large tuple \
+ defined multiple times in the module, is defined but not used'),
'W0621': ('Redefining name %r from outer scope (line %s)',
'Used when a variable\'s name hide a name defined in the outer \
@@ -126,6 +133,21 @@
self._checking_mod_attr = None
self._vars = None
+ # deferred unused variable warnings, resolved on module leave. List
+ # consists of (name, stmt) tuples.
+ self._deferred_unused = []
+
+ # keys are tuples of local var names. Values are sets of
+ # (lineno, varname) tuples - ie. first tuple of this layout
+ # encountered.
+ self._tuple_packings_by_tpl = {}
+
+ # dict with keys as line numbers where identical tuple packs/unpacks
+ # have occurred. Values are sets of (lineno, varname) tuples, and
+ # as such identify exactly the tuple referred to by the key line
+ # number.
+ self._tuple_packing_by_lineno = {}
+
def visit_module(self, node):
"""visit module : update consumption analysis variable
checks globals doesn't overrides builtins
@@ -154,6 +176,34 @@
self.add_message('W0614', args=name, node=stmt)
else:
self.add_message('W0611', args=name, node=stmt)
+
+ #
+ # check for unused variables from large tuple unpacks
+
+ # first collect all tuple cases_sets which are fully unused -
+ # for those we will issue W0612 as usual.
+ all_unused_cases = set()
+ for name, stmt in self._deferred_unused:
+ all_unused_cases.add((stmt.lineno, name))
+
+ tuple_packing_by_lineno = self._tuple_packing_by_lineno
+ bad_linenos_set_ids = set()
+
+ for cases_set in tuple_packing_by_lineno.values():
+ if cases_set.issubset(all_unused_cases):
+ #print 'Added bad set: %s'%cases_set
+ bad_linenos_set_ids.add(id(cases_set))
+
+ for name, stmt in self._deferred_unused:
+ ln = stmt.lineno
+ p_cases_set = tuple_packing_by_lineno.get(ln, None)
+ #print '%i: %s'%(ln, name)
+ #print ' p_cases_set = %s'%p_cases_set
+ if p_cases_set is None or id(p_cases_set) in bad_linenos_set_ids:
+ self.add_message('W0612', args=name, node=stmt)
+ else:
+ self.add_message('W0617', args=name, node=stmt)
+
del self._to_consume
del self._vars
@@ -246,7 +296,27 @@
continue
self.add_message('W0613', args=name, node=stmt)
else:
- self.add_message('W0612', args=name, node=stmt)
+ # if this was in unused variable in tuple unpacking, then
+ # defer the 'unused variable' msg.
+ s_parent = stmt.parent
+
+ # AssTuple is converted to Tuple in Pylin 0.18.1+
+ if isinstance(s_parent, astng.Tuple):
+ #print dir(s_parent)
+ tuple_nodes = list(s_parent.get_children())
+ #raise AssertionError
+ #print '---'
+ #print s_parent.ass_type()
+ #print s_parent.assigned_stmts()
+ #print s_parent.statement()
+ #print(tuple_nodes)
+ else:
+ tuple_nodes = ()
+
+ if len(tuple_nodes) > 2:
+ self._deferred_unused.append((name, stmt))
+ else:
+ self.add_message('W0612', args=name, node=stmt)
def visit_global(self, node):
"""check names imported exists in the global scope"""
@@ -409,6 +524,19 @@
or name in self.config.additional_builtins):
self.add_message('E0602', args=name, node=node)
+
+ def visit_tuple(self, node):
+ """record tuples"""
+ self._register_tuple_packing(list(node.get_children()))
+
+ def visit_asstuple(self, node):
+ """record tuples"""
+ print('WARNING', node.__class__.__name__)
+ self._register_tuple_packing(list(node.get_children()))
+
def visit_import(self, node):
"""check modules attribute accesses"""
for name, _ in node.names:
@@ -460,6 +588,44 @@
## def leave_default(self, node):
## """by default, reset the _checking_mod_attr attribute"""
## self._checking_mod_attr = None
+
+ def _register_tuple_packing(self, tuple_nodes):
+ """registers a valid tuple packing layout from pack or unpack
+ operation.
+ """
+ # we are only interested of tuples larger than 2
+ if len(tuple_nodes) < 2:
+ return
+
+ names = []
+ cases = []
+
+ for tnode in tuple_nodes:
+ # all must be names of locals
+ if not isinstance(tnode, astng.Name) and \
+ not isinstance(tnode, astng.AssName):
+ return
+
+ name = tnode.name
+ names.append(name)
+
+ lineno = tnode.lineno
+ if not (lineno is None):
+ cases.append((lineno, name))
+
+ names = tuple(names)
+ cases = frozenset(cases)
+
+ tuple_packings = self._tuple_packings_by_tpl
+ if not (names in tuple_packings):
+ # First occurrence of pack/unpack
+ tuple_packings[names] = cases
+ else:
+ # Second or futher occurrence of same pack/unpack
+ identical_tuple_pack_linenos = self._tuple_packing_by_lineno
+ for cases_set in (tuple_packings[names], cases):
+ for i, name in cases_set:
+ identical_tuple_pack_linenos[i] = cases_set
def _check_module_attrs(self, node, module, module_names):
"""check that module_names (list of string) are accessible through the
_______________________________________________
Python-Projects mailing list
[email protected]
http://lists.logilab.org/mailman/listinfo/python-projects