~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 2009 Canonical Ltd.  This software is licensed under the
4
# GNU Affero General Public License version 3 (see the file LICENSE).
5
6
"""Move a python module in the tree.
7
8
It uses bzr mv to rename the module and will try to find all imports.
9
10
rename-module.py src_file+ target
11
12
Both files must be under lib/.
13
14
If more than one src files is given, target must be a directory.
15
"""
16
17
__metaclass__ = type
18
19
__all__ = [
20
    'bzr_add',
21
    'bzr_has_filename',
22
    'bzr_move_file',
23
    'bzr_remove_file',
24
    'rename_module',
25
    'update_references',
26
    ]
27
28
import os
29
import sys
30
31
from bzrlib import workingtree
32
from find import find_matches
14612.2.6 by William Grant
utilities
33
from utils import (
34
    fail,
35
    log,
36
    )
9492.1.1 by Karl Fogel
Add utilities/formatdoctest.py and utilities/migrater/, both brought
37
38
39
def file2module(module_file):
40
    """From a filename, return the python module name."""
41
    start_path = 'lib' + os.path.sep
42
    assert module_file.startswith(start_path), (
43
        "File should start with lib: %s" % module_file)
44
    assert module_file.endswith('.py'), (
45
        "File should end with .py: %s" % module_file)
46
    return module_file[len(start_path):-3].replace(os.path.sep, '.')
47
48
49
# Cache the working tree for speed.
50
_wt = workingtree.WorkingTree.open('.')
51
14565.2.7 by Curtis Hovey
Always fix references in ./scripts and ./database
52
9492.1.1 by Karl Fogel
Add utilities/formatdoctest.py and utilities/migrater/, both brought
53
def bzr_move_file(src_file, target):
54
    """Move or rename a versioned file or directory."""
55
    if os.path.isdir(target):
56
        _wt.move([src_file], target)
57
    else:
58
        _wt.rename_one(src_file, target)
59
    log('    Renamed %s => %s', src_file, target)
60
61
62
def bzr_add(paths):
63
    "Version a list of paths."
64
    _wt.add(paths)
65
14565.2.7 by Curtis Hovey
Always fix references in ./scripts and ./database
66
9492.1.1 by Karl Fogel
Add utilities/formatdoctest.py and utilities/migrater/, both brought
67
def bzr_remove_file(filename):
68
    """Remove a versioned file."""
69
    _wt.remove([filename], keep_files=False, force=True)
70
71
72
def bzr_has_filename(file_path):
73
    """Is the file versioned?"""
74
    _wt.has_filename(file_path)
75
76
77
def rename_module(src_file, target_file):
78
    """Renamed  a versioned module and update all references to it."""
79
    # Move the file using bzr.
80
    bzr_move_file(src_file, target_file)
81
    if not src_file.endswith('.py'):
82
        # It's not a module, so don't try to update imports of it.
83
        return
84
    source_module = file2module(src_file)
85
    target_module = file2module(target_file)
86
    update_references(source_module, target_module)
87
88
89
def update_references(source_module, target_module):
90
    """Update references to the source module.
91
92
    :param src_module: a string describing the old module name. May contain
93
        RE patterns
94
    :param target_module: a string representing the new module name. May
95
        contain RE groups.
96
    """
97
    source = r'\b%s\b' % source_module.replace('.', '\\.')
98
    target = target_module
14565.2.7 by Curtis Hovey
Always fix references in ./scripts and ./database
99
    root_dirs = [
100
        'cronscripts', 'scripts', 'database', 'lib/canonical', 'lib/lp']
9492.1.1 by Karl Fogel
Add utilities/formatdoctest.py and utilities/migrater/, both brought
101
    file_pattern = '\.(py|txt|zcml)$'
102
    print "    Updating references:"
103
    for root_dir in root_dirs:
104
        for summary in find_matches(
105
            root_dir, file_pattern, source, substitution=target):
106
            print "        * %(file_path)s" % summary
107
108
109
def main():
110
    """Rename a module and update all references to it."""
111
    if len(sys.argv) < 3:
112
        fail('Usage: %s src_file+ target', os.path.basename(sys.argv[0]))
113
    src_files = sys.argv[1:-1]
114
    target = sys.argv[-1]
115
116
    if os.path.exists(target) and not os.path.isdir(target):
117
        fail('Destination file "%s" already exists.', target)
118
    if not target.startswith('lib'):
119
        fail('Destination file "%s" must be under lib.', target)
120
    if len(src_files) > 1 and not os.path.isdir(target):
121
        fail('Destination must be a directory.')
122
123
    for src_file in src_files:
124
        if not os.path.exists(src_file):
125
            log('Source file "%s" doesn\'t exists. Skipping', src_file)
126
            continue
127
        if not src_file.startswith('lib'):
128
            log('Source file "%s" must be under lib. Skipping', src_file)
129
            continue
130
131
        if os.path.isdir(target):
132
            target_file = os.path.join(target, os.path.basename(src_file))
133
        else:
134
            target_file = target
135
136
        rename_module(src_file, target_file)
137
138
139
if __name__ == '__main__':
140
    main()