Online Shopping : Computers : Programming : Languages : Python : Snippets

+ Search
Add Entry AlertManage Folder Edit Entry Add page to
Did You Find This Entry Useful?

2 of 2 people (100%) answered Yes
Recently 1 of 1 people (100%) answered Yes

Entry -- search for multi-argument .append() calls

Mar 27th, 2009 09:10
hindusthan jobs, engatoo engatoo, games games, John Martin, Nathan Wallace, Hans Nowak, Snippet 367, Tim Peters

Packages: miscellaneous.version_dependent
From:           	"Tim Peters" <[email protected]>
To:             	"Python mailing list" <[email protected]>
Subject:        	RE: Multi-argument append() is illegal
Date sent:      	Tue, 29 Feb 2000 04:25:03 -0500
[Guido van Rossum]
> I've noticed that there is some code out there that creates a list of
> tuples and uses code like list.append(a,b,c) to add the tuple (a,b,c)
> to the list.  According to the documentation, this is illegal:
> append() only takes a single argument, and one should write
> list.append((a,b,c)).  However, the actual append() implementation
> didn't mind, and implemented list.append(a,b,c) as
> list.append((a,b,c)).  Many people are using this even though it's
> never been documented.
> I am going to rectify this in Python 1.6 -- people coming from other
> languages might well expect list.append(a, b, c) to mean the same as
> list.append(a); list.append(b); list.append(c), and it's always been
> my philosophy to make ambiguous syntax illegal rather than to pick 
> interpretation randomly.
Before anyone starts <wink>, protesting this appears to be as futile as
griping about whitespace:  The Dictator Has Spoken here.  It's been 
forever and needs to get fixed.
> ...
> You can also grep through your sources for a pattern like
> "\. *append *\(.*," -- which doesn't find every occurrence, but is
> a good starting point.
Actually, it's a terrible starting point:  the sheer quantity of "false
hits" will drive you mad.  For example, note that the one above 
matches your
.append()s again *after* you've fixed them(!).  I've been thru this a 
times in my own code over the years, and never dreamed up a regexp that
didn't leave me with hours of manual drudge.  You really need to 
account for
bracket nesting levels to avoid spurious hits on "deep" commas, and to
ignore commas in strings and trailing comments, and to span lines too 
avoid missing the
So, Python to the rescue.  What appears to be a very capable checker is
attached.  It's pretty slow, but not achingly so, and you shouldn't 
need to
use it often.  Read the docstring at the top for cautions.  A copy of 
(or a fixed version, if this proves to have bugs) may show up in the 
distribution.  BTW, there's no chance of talking me into trying to 
this to edit your files for you, so it's a good thing you didn't ask
parsing-is-better-than-wild-ass-guessing-ly y'rs  - tim
#! /usr/bin/env python
# Released to the public domain, by Tim Peters, 28 February 2000.
""" -- search for multi-argument .append() calls.
Usage:  specify one or more file or directory paths:
    checkappend [-v] file_or_dir [file_or_dir] ...
Each file_or_dir is checked for multi-argument .append() calls.  When
a directory, all .py files in the directory, and recursively in its
subdirectories, are checked.
Use -v for status msgs.  Use -vv for more status msgs.
In the absence of -v, the only output is pairs of the form
    line containing the suspicious append
Note that this finds multi-argument append calls regardless of whether
they're attached to list objects.  If a module defines a class with an
append method that takes more than one argument, calls to that method
will be listed.
Note that this will not find multi-argument list.append calls made via 
bound method object.  For example, this is not caught:
    somelist = []
    push = somelist.append
    push(1, 2, 3)
__version__ = 1, 0, 0
import os
import sys
import string
import getopt
import tokenize
verbose = 0
def errprint(*args):
    msg = string.join(args)
def main():
    args = sys.argv[1:]
    global verbose
        opts, args = getopt.getopt(sys.argv[1:], "v")
    except getopt.error, msg:
        errprint(msg + "\n\n" + __doc__)
    for opt, optarg in opts:
        if opt == '-v':
            verbose = verbose + 1
    if not args:
    for arg in args:
def check(file):
    if os.path.isdir(file) and not os.path.islink(file):
        if verbose:
            print "%s: listing directory" % `file`
        names = os.listdir(file)
        for name in names:
            fullname = os.path.join(file, name)
            if ((os.path.isdir(fullname) and
                 not os.path.islink(fullname))
                or os.path.normcase(name[-3:]) == ".py"):
        f = open(file)
    except IOError, msg:
        errprint("%s: I/O Error: %s" % (`file`, str(msg)))
    if verbose > 1:
        print "checking", `file`, "..."
    ok = AppendChecker(file, f).run()
    if verbose and ok:
        print "%s: Clean bill of health." % `file`
 FIND_STMT]   = range(5)
class AppendChecker:
    def __init__(self, fname, file):
        self.fname = fname
        self.file = file
        self.state = FIND_DOT
        self.nerrors = 0
    def run(self):
            tokenize.tokenize(self.file.readline, self.tokeneater)
        except tokenize.TokenError, msg:
            errprint("%s: Token Error: %s" % (`self.fname`, str(msg)))
            self.nerrors = self.nerrors + 1
        return self.nerrors == 0
    def tokeneater(self, type, token, start, end, line,
                JUNK=(tokenize.COMMENT, tokenize.NL),
        state = self.state
        if type in JUNK:
        elif state is FIND_DOT:
            if type is OP and token == ".":
                state = FIND_APPEND
        elif state is FIND_APPEND:
            if type is NAME and token == "append":
                self.line = line
                self.lineno = start[0]
                state = FIND_LPAREN
                state = FIND_DOT
        elif state is FIND_LPAREN:
            if type is OP and token == "(":
                self.level = 1
                state = FIND_COMMA
                state = FIND_DOT
        elif state is FIND_COMMA:
            if type is OP:
                if token in ("(", "{", "["):
                    self.level = self.level + 1
                elif token in (")", "}", "]"):
                    self.level = self.level - 1
                    if self.level == 0:
                        state = FIND_DOT
                elif token == "," and self.level == 1:
                    self.nerrors = self.nerrors + 1
                    print "%s(%d):\n%s" % (self.fname, self.lineno,
                    # don't gripe about this stmt again
                    state = FIND_STMT
        elif state is FIND_STMT:
            if type is NEWLINE:
                state = FIND_DOT
            raise SystemError("unknown internal state '%s'" % `state`)
        self.state = state
if __name__ == '__main__':
# end of file