~launchpad-pqm/launchpad/devel

9492.1.1 by Karl Fogel
Add utilities/formatdoctest.py and utilities/migrater/, both brought
1
#!/usr/bin/python
2
#
3
# Copyright (C) 2009 - Curtis Hovey <sinzui.is at verizon.net>
4
# This software is licensed under the GNU General Public License version 2.
5
#
6
# It comes from the Gedit Developer Plugins project (launchpad.net/gdp); see
7
# http://bazaar.launchpad.net/~sinzui/gdp/trunk/files/head%3A/plugins/gdp/ &
8
# http://bazaar.launchpad.net/%7Esinzui/gdp/trunk/annotate/head%3A/COPYING.
9
10
"""Find in files and replace strings in many files."""
11
12
import mimetypes
13
import os
14
import re
15
import sys
16
17
from optparse import OptionParser
18
19
20
mimetypes.init()
21
22
23
def extract_match(file_path, match_re, substitution=None):
24
    """Return a summary of matches in a file."""
25
    lines = []
26
    content = []
27
    match = None
28
    file_ = open(file_path, 'r')
29
    try:
30
        for lineno, line in enumerate(file_):
31
            match = match_re.search(line)
32
            if match:
33
                lines.append(
11716.1.3 by Curtis Hovey
Updated deglobber to convert multiline doctest imports so that normal single line
34
                    {'lineno': lineno + 1, 'text': line.strip(),
9492.1.1 by Karl Fogel
Add utilities/formatdoctest.py and utilities/migrater/, both brought
35
                     'match': match})
36
                if substitution is not None:
37
                    line = match_re.sub(substitution, line)
38
            if substitution is not None:
39
                content.append(line)
40
    finally:
41
        file_.close()
42
    if lines:
43
        if substitution is not None:
44
            file_ = open(file_path, 'w')
45
            try:
46
                file_.write(''.join(content))
47
            finally:
48
                file_.close()
11716.1.3 by Curtis Hovey
Updated deglobber to convert multiline doctest imports so that normal single line
49
        return {'file_path': file_path, 'lines': lines}
9492.1.1 by Karl Fogel
Add utilities/formatdoctest.py and utilities/migrater/, both brought
50
    return None
51
52
11716.1.3 by Curtis Hovey
Updated deglobber to convert multiline doctest imports so that normal single line
53
def find_matches(root_dir, file_pattern, match_pattern,
54
                 substitution=None, extract_match=extract_match):
55
    """Iterate a summary of matching lines in a file."""
56
    match_re = re.compile(match_pattern)
57
    for file_path in find_files(root_dir, file_pattern=file_pattern):
58
        summary = extract_match(
59
            file_path, match_re, substitution=substitution)
60
        if summary:
61
            yield summary
62
63
64
def find_files(root_dir, skip_dir_pattern='^[.]', file_pattern='.*'):
65
    """Iterate the matching files below a directory."""
66
    skip_dir_re = re.compile(r'^.*%s' % skip_dir_pattern)
67
    file_re = re.compile(r'^.*%s' % file_pattern)
68
    for path, subdirs, files in os.walk(root_dir):
69
        subdirs[:] = [dir_ for dir_ in subdirs
70
                      if skip_dir_re.match(dir_) is None]
71
        for file_ in files:
72
            file_path = os.path.join(path, file_)
73
            if os.path.islink(file_path):
74
                continue
75
            mime_type, encoding = mimetypes.guess_type(file_)
76
            if mime_type is None or 'text/' in mime_type:
77
                if file_re.match(file_path) is not None:
78
                    yield file_path
79
80
9492.1.1 by Karl Fogel
Add utilities/formatdoctest.py and utilities/migrater/, both brought
81
def get_option_parser():
82
    """Return the option parser for this program."""
83
    usage = "usage: %prog [options] root_dir file_pattern match"
84
    parser = OptionParser(usage=usage)
85
    parser.add_option(
86
        "-s", "--substitution", dest="substitution",
87
        help="The substitution string (may contain \\[0-9] match groups).")
88
    parser.set_defaults(substitution=None)
89
    return parser
90
91
92
def main(argv=None):
93
    """Run the command line operations."""
94
    if argv is None:
95
        argv = sys.argv
96
    parser = get_option_parser()
97
    (options, args) = parser.parse_args(args=argv[1:])
98
99
    root_dir = args[0]
100
    file_pattern = args[1]
101
    match_pattern = args[2]
102
    substitution = options.substitution
103
    print "Looking for [%s] in files like %s under %s:" % (
104
        match_pattern, file_pattern, root_dir)
105
    for summary in find_matches(
106
        root_dir, file_pattern, match_pattern, substitution=substitution):
107
        print "\n%(file_path)s" % summary
108
        for line in summary['lines']:
109
            print "    %(lineno)4s: %(text)s" % line
110
111
112
if __name__ == '__main__':
113
    sys.exit(main())