Removing unstylish spaces

Alex cut_me_out at hotmail.com
Sat Jul 8 21:13:02 EDT 2000


Reading Code Complete and The Pragmatic Programmer at the same time can
make you do some silly things.  I learned recently that it's not stylish
to have a space between a function name and the opening parethesis of
its argument list, so I kludged something together to remove most of
them from my code.  I thought I would make it available here, in case
anyone else is so frivolously minded. :)

Alex.

#!/usr/bin/env python

'''Finds all *.py files beneath the cwd, looks in them for opening
parentheses preceded by a space, and removes the space if it thinks
it\'s part of a function call.

If you use it without backing your files up, you\'re an idiot.  That
said, it\'s pretty paranoid about trying not to stuff files up, and
will save a copy of file.py in file.py.bak, if it decides to modify
it.

You can do as you wish with this code, even attribute it to yourself,
if you\'re in a plagiaristic mood.'''

import os, string, re, shutil, sys

class File_getter:
    
    def __init__ (self, paths):
        self.filenames = []
        for path in paths:
            os.path.walk (os.path.expanduser (path), self.callback, None)
            
    def callback (self, dummy, directory, filenames):
        filenames = filter (re.compile ('\.py$').search, filenames)
        filenames = map (os.path.join, len (filenames) * [directory],
                         filenames)
        self.filenames.extend (filenames)

want_to_keep_the_space_p = re.compile (
    '(([^%s](' % (string.letters + '_') + \
    string.join (['if',     'and',    'or',   'else', 'assert',
                  'while',  'return', 'exec', 'del',  'for',
                  'not',    'raise',  'in',   'is',
                  '[-^<>=+*%~:]'],
                 '|') + \
    '))|[-^<>=+*%~:]|\d)$').search

assert     want_to_keep_the_space_p (')and')
assert     want_to_keep_the_space_p ('5 %')
assert not want_to_keep_the_space_p ('"%s" % str(4)')

def clean_brackets(filename):
    '''Find brackets proceeded by spaces in the python file filename,
    and remove the spaces unless they are proceeded by a keyword or symbol.
    '''
    
    file = open(filename).read()
    file_copied_p = None

    # ...first, get rid of confusing carriage returns
    file = string.replace (file, '\r\n', '\n')

    # Check that we don't get any false positives from an existing
    # syntax error.
    try:
        compile(file, 'test', 'exec')
    except SyntaxError: # Ignore file if we do.
        return None
    
    start = 0
    while 1:
        candidate = string.find(file, ' (', start)
        if candidate == -1: # No more found
            break

        # This could skip ahead two, if a space is removed.
        start = candidate + 1 

        # Check that the bracket is in a coding region of the file
        # by seeing if removing it induces a syntax error.
        assert file[candidate: candidate + 2] == ' ('
        mutated_file = file[:candidate] + file[candidate + 2:]
        try:
            compile(mutated_file, 'test', 'exec')
        except SyntaxError:

            # This is part of a coding region of the file, amend it.
            # in the rest of the loop.
            pass
        else:

            # Removal had no effect on syntactical correctness of the
            # file, it must be irrelevant.
            continue

        # If this is the first amendment to the file, take a copy of
        # it, in case this screws up.
        if not file_copied_p:
            shutil.copyfile(filename, filename + '.bak')
            file_copied_p = 1

        # Get the word/symbol prior to the bracket.
        # ...get the start of the current line
        line_start = string.rfind (file, '\n', 0, candidate)
        if line_start == -1: # No prior newline, must be on first line.
            line_start = 0
        else:                # Jump past the newline.
            line_start = line_start + 1
        # ...get the word
        words = string.split (file[line_start: candidate])
        if len (words) == 0: # Just whitespace prior to the bracket, ignore.
            continue
        prior_word = words[-1]

        if not want_to_keep_the_space_p (prior_word):
            assert file[candidate] == ' '
            file = file[:candidate] + file[candidate + 1:]

    if file_copied_p: # Must have been modified, too.  Save it.
        try:
            compile(file, 'test', 'exec')
        except SyntaxError:
            raise 'Stuffed up the syntax of ' + filename
        else:
            open(filename, 'w').write(file)
        
if __name__ == '__main__':
    agreement = 'Yes_I_want_to_run_this_poorly_understood_code_' \
                'over_all_python_files_below_my_cwd.'
    if (len(sys.argv) != 2) or (sys.argv[1] != agreement):
        print 'Usage:'
        print sys.argv[0], agreement
        sys.exit (1)

    for filename in File_getter([os.getcwd ()]).filenames:
        print filename
        clean_brackets(filename)
    



More information about the Python-list mailing list