[Python-ideas] Should a single/double quote followed by a left parenthesis raise SyntaxError?

Jacob Niehus jacob.niehus at gmail.com
Wed Jul 15 17:40:59 CEST 2015


I recently forgot the '%' between a format string and its tuple of
values and got "TypeError: 'str' object is not callable." The error
makes sense, of course, because function calling has higher precedence
than anything else in the expression, so while:

>>> '%s' 'abc'

yields '%sabc',

>>> '%s' ('abc')

yields a TypeError.

My question is whether this should be caught as a syntax error instead
of a runtime error. I can't think of any possible case where a
single/double quote followed by '(' (optionally separated by whitespace)
would not raise an exception when the '(' is not in a string or comment.
I even wrote a script (below) to check every Python file on my computer
for occurrences of the regular expression r'[''"]\s*\(' occurring
outside of a string or comment. The sole result of my search, a file in
a wx library, is a definite runtime error by inspection:

    raise IndexError, "Index out of range: %d > %d" (idx, len(self._items))

Can anyone think of a counter-example?

-Jake

--------------------------------------------------------------------------------

#!/usr/bin/env python2
import re
import sys
import tokenize
from py_compile import PyCompileError, compile

ttypes = {v: k for k, v in tokenize.__dict__.items()
          if isinstance(v, int) and k == k.upper()}

for filename in sys.argv[1:]:
    lines = file(filename, 'r').readlines()
    matches = []
    for lnum, line in enumerate(lines, 1):
        for match in re.finditer(r'[''"]\s*\(', line):
            matches.append((lnum, match.span(), match.group(0)))

    try:
        assert(matches)
        compile(filename, doraise=True)
    except (AssertionError, PyCompileError, IOError):
        continue

    matchdict = {k: [] for k in [m[0] for m in matches]}
    for match in matches:
        matchdict[match[0]].append(match)

    with open(filename, 'r') as f:
        gen = tokenize.generate_tokens(f.readline)
        for ttype, tstr, (srow, scol), (erow, ecol), line in gen:
            if srow == erow:
                for mrow, (mscol, mecol), mstr in matchdict.get(srow, []):
                    pcols = [mscol + i for i, x in enumerate(mstr) if x == '(']
                    for p in pcols:
                        if (p in range(scol, ecol) and
                                ttypes[ttype] not in ['COMMENT', 'STRING']):
                            print filename
                            print srow
                            print ttypes[ttype]


More information about the Python-ideas mailing list