~launchpad-pqm/launchpad/devel

11692.6.1 by Curtis Hovey
Added deglobber.
1
#!/usr/bin/python
2
#locate all the bad imports in zcml
3
#create  a set
4
#for each item in set
5
#find true path
6
#replace all occurences.
7
11787.1.1 by Curtis Hovey
updated deglob to take command line args.
8
import os
9
import sys
10
11692.6.1 by Curtis Hovey
Added deglobber.
11
from find import find_matches
12
13
14
def get_interfaces(types=None, globs=None):
15
    interfaces = set()
16
    root = 'lib'
17
    if types is None:
18
        types = r'\.(zcml)'
19
    if globs is None:
20
        globs = r'\bcanonical\.launchpad\.interfaces.(I\w*)\b'
21
    for summary in find_matches(root, types, globs):
22
        for line in summary['lines']:
23
            interfaces.add(line['match'].group(1))
24
    return interfaces
25
26
27
def get_interface_modules(interfaces):
28
    interface_modules = {}
29
    root = 'lib'
30
    types = r'(errors|enums|interfaces).*\.py'
31
    interface_def = r'\bclass (%s)\b'
32
    for interface in interfaces:
33
        for summary in find_matches(root, types, interface_def % interface):
34
            # Chop lib/ and .py from the string and repace the slash with dot.
35
            module_ = summary['file_path'][4:-3].replace('/', '.')
36
            interface_modules[interface] = module_
37
            break
38
    return interface_modules
39
40
41
def update_zcml_globs_to_interfaces():
42
    root = 'lib'
43
    types = r'\.(zcml)'
44
    globs = r'\bcanonical\.launchpad\.interfaces.(I\w*)\b'
45
    interfaces = get_interfaces(types=types, glob_interface=globs)
46
    interface_modules = get_interface_modules(interfaces)
47
    glob_interface = r'\b(canonical\.launchpad\.interfaces.%s)\b'
48
    for interface, module_ in interface_modules.items():
49
        pattern = glob_interface % interface
50
        substitution = '%s.%s' % (module_, interface)
51
        for summary in find_matches(
52
            root, types, pattern, substitution=substitution):
53
            print "\n%(file_path)s" % summary
54
            for line in summary['lines']:
55
                print "    %(lineno)4s: %(text)s" % line
56
57
58
def update_doctest_globs_to_interfaces():
59
    root = 'lib'
60
    types = r'\.(txt)'
61
    globs = r'from \bcanonical\.launchpad\.interfaces import (\w+)$'
11716.1.2 by Curtis Hovey
Fixed typo.
62
    interfaces = get_interfaces(types=types, globs=globs)
11692.6.1 by Curtis Hovey
Added deglobber.
63
    interface_modules = get_interface_modules(interfaces)
64
    glob_interface = r'\b(from canonical\.launchpad\.interfaces import %s)\b'
65
    for interface, module_ in interface_modules.items():
66
        pattern = glob_interface % interface
67
        substitution = 'from %s import %s' % (module_, interface)
68
        for summary in find_matches(
11716.1.6 by Curtis Hovey
Converted glob imports in doctests to import for the true module.
69
            root, types, pattern, substitution=substitution):
11692.6.1 by Curtis Hovey
Added deglobber.
70
            print "\n%(file_path)s" % summary
71
            for line in summary['lines']:
72
                print "    %(lineno)4s: %(text)s" % line
73
74
11716.1.3 by Curtis Hovey
Updated deglobber to convert multiline doctest imports so that normal single line
75
def multiline_extract_match(file_path, match_re, substitution=None):
76
    """Return a summary of matches in a file."""
77
    lines = []
78
    content = []
79
    match = None
80
    file_ = open(file_path, 'r')
81
    in_match = False
82
    current_match = None
83
    try:
84
        for lineno, line in enumerate(file_):
85
            if in_match:
86
                identifiers = line.split(',')
87
                for idf in identifiers:
88
                    idf = idf.strip()
89
                    idf = idf.strip('...')
90
                    idf = idf.strip()
91
                    if idf.endswith(')'):
92
                        in_match = False
93
                        idf = idf[0:-1]
94
                    idf = idf.strip()
95
                    if idf == '':
96
                        continue
97
                    expanded_line = (
98
                        '%s %s\n' % (current_match.group(0), idf))
99
                    lines.append(
11716.1.4 by Curtis Hovey
improve report.
100
                        {'lineno': lineno + 1, 'text': expanded_line.strip(),
11716.1.3 by Curtis Hovey
Updated deglobber to convert multiline doctest imports so that normal single line
101
                         'match': None})
102
                    if substitution is not None:
103
                        content.append(expanded_line)
104
                    if not in_match:
105
                        current_match = None
106
                continue
107
            # Else check if this is the start of a multi-line.
108
            match = match_re.search(line)
109
            if match and line.strip().endswith('('):
110
                in_match = True
111
                current_match = match
112
                continue
113
            # Always append the non-matching lines to content to rebuild
114
            # the file.
115
            if substitution is not None:
116
                content.append(line)
117
    finally:
118
        file_.close()
119
    if lines:
120
        if substitution is not None:
121
            file_ = open(file_path, 'w')
122
            try:
123
                file_.write(''.join(content))
124
            finally:
125
                file_.close()
126
        return {'file_path': file_path, 'lines': lines}
127
    return None
128
129
130
def update_multi_doctest_globs_to_interfaces():
11716.1.5 by Curtis Hovey
deglob all doctests with multiline globs.
131
    root = 'lib'
11716.1.3 by Curtis Hovey
Updated deglobber to convert multiline doctest imports so that normal single line
132
    types = r'\.(txt)'
133
    pattern = r'[ ]+>>> from canonical\.launchpad\.interfaces import'
134
    substitution = True
135
    for summary in find_matches(
136
        root, types, pattern, substitution=substitution,
137
        extract_match=multiline_extract_match):
138
        print "\n%(file_path)s" % summary
139
        for line in summary['lines']:
140
            print "    %(lineno)4s: %(text)s" % line
141
142
11716.1.8 by Curtis Hovey
Save point for deglobber.
143
def normalize_doctest_imports(file_path, match_re, substitution=None):
144
    """Return a summary of matches in a file."""
145
    lines = []
146
    content = []
147
    match = None
148
    file_ = open(file_path, 'r')
149
    in_match = False
150
    imports = None
151
    try:
152
        for lineno, line in enumerate(file_):
153
            match = match_re.search(line)
154
            # Start match imports.
155
            if match and not in_match:
156
                in_match = True
157
                whitespace = match.group(1)
158
                imports = {}
159
                # Fall-through.
160
            # Store the collected imports.
161
            if match:
162
                module_ = match.group(2)
163
                if module_ not in imports:
164
                    imports[module_] = []
165
                imports[module_].append(match.group(3))
166
                continue
167
            # If the import section is passed, normalize the imports.
168
            if not match and in_match:
11716.1.9 by Curtis Hovey
Save point for deglobber.
169
                module_names = sorted(imports.keys())
170
                # Put zope modules first.
171
                zopes = list(module_names)
172
                zopes.reverse()
173
                for name in zopes:
174
                    if name.startswith('zope'):
175
                        module_names.remove(name)
176
                        module_names.insert(0, name)
177
                    else:
178
                        break
179
                for module_ in module_names:
11716.1.8 by Curtis Hovey
Save point for deglobber.
180
                    identifiers = sorted(imports[module_])
181
                    if len(identifiers) == 1:
182
                        expanded_line = (
183
                            '%s>>> from %s import %s\n' %
184
                            (whitespace, module_, identifiers[0]))
185
                    else:
186
                        continuation = ',\n%s...     ' % whitespace
187
                        idfs = continuation.join(identifiers)
188
                        expanded_line = (
11716.1.14 by Curtis Hovey
Fixed find and replace rules.
189
                            '%s>>> from %s import (%s%s%s)\n' %
11716.1.8 by Curtis Hovey
Save point for deglobber.
190
                            (whitespace, module_,
191
                             continuation[1:], idfs, continuation))
192
                    lines.append(
193
                        {'lineno': lineno + 1, 'text': expanded_line.strip(),
194
                         'match': None})
195
                    if substitution is not None:
196
                        content.append(expanded_line)
197
                # Clear imports.
198
                in_match = False
199
                imports = None
200
                # Append the current line.
201
                if substitution is not None:
202
                    content.append(line)
203
                continue
204
            # Always append the non-matching lines to content to rebuild
205
            # the file.
206
            if substitution is not None:
207
                content.append(line)
208
    finally:
209
        file_.close()
210
    if lines:
211
        if substitution is not None:
212
            file_ = open(file_path, 'w')
213
            try:
214
                file_.write(''.join(content))
215
            finally:
216
                file_.close()
217
        return {'file_path': file_path, 'lines': lines}
218
    return None
219
220
221
def normalize_all_doctest_imports():
11716.1.10 by Curtis Hovey
Save point for deglobber.
222
    root = 'lib'
11716.1.8 by Curtis Hovey
Save point for deglobber.
223
    types = r'\.(txt)'
11716.1.11 by Curtis Hovey
Save point for deglobber.
224
    pattern = r'^([ ]+)>>> from ([\w.]+) import ([\w.]+)$'
11716.1.8 by Curtis Hovey
Save point for deglobber.
225
    substitution = True
226
    for summary in find_matches(
227
        root, types, pattern, substitution=substitution,
228
        extract_match=normalize_doctest_imports):
229
        print "\n%(file_path)s" % summary
230
        for line in summary['lines']:
231
            print "    %(lineno)4s: %(text)s" % line
232
233
11787.1.1 by Curtis Hovey
updated deglob to take command line args.
234
def update_multi_python_globs_to_interfaces(root='lib', types='tests'):
235
    pattern=r'from canonical\.launchpad\.interfaces import'
11768.1.1 by Curtis Hovey
Updated debglobber to fix python tests.
236
    substitution = True
237
    for summary in find_matches(
238
        root, types, pattern, substitution=substitution,
239
        extract_match=multiline_extract_match):
240
        print "\n%(file_path)s" % summary
241
        for line in summary['lines']:
242
            print "    %(lineno)4s: %(text)s" % line
243
244
11787.1.1 by Curtis Hovey
updated deglob to take command line args.
245
def update_python_globs_to_interfaces(root='lib', types='tests'):
246
    update_multi_python_globs_to_interfaces(root=root, types=types)
11768.1.1 by Curtis Hovey
Updated debglobber to fix python tests.
247
    globs = r'from \bcanonical\.launchpad\.interfaces import (\w+)$'
248
    interfaces = get_interfaces(types=types, globs=globs)
249
    interface_modules = get_interface_modules(interfaces)
250
    glob_interface = r'\b(from canonical\.launchpad\.interfaces import %s)\b'
251
    for interface, module_ in interface_modules.items():
252
        pattern = glob_interface % interface
253
        substitution = 'from %s import %s' % (module_, interface)
254
        for summary in find_matches(
255
            root, types, pattern, substitution=substitution):
256
            print "\n%(file_path)s" % summary
257
            for line in summary['lines']:
258
                print "    %(lineno)4s: %(text)s" % line
259
260
11692.6.1 by Curtis Hovey
Added deglobber.
261
def main():
11787.1.1 by Curtis Hovey
updated deglob to take command line args.
262
    if len(sys.argv) != 3:
263
        print 'Usage: %s root_path file_test', os.path.basename(sys.argv[0])
264
        sys.exit(1)
265
    root = sys.argv[1]
266
    types = sys.argv[2]
267
    update_python_globs_to_interfaces(root, types)
11692.6.1 by Curtis Hovey
Added deglobber.
268
269
270
if __name__ == '__main__':
271
    main()