summaryrefslogtreecommitdiff
path: root/eggs/infrae.subversion-1.4.5-py2.6.egg/infrae/subversion/Common.py
blob: 234ef6b070b4b01ef895d09c581d1c6a389304f9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# Copyright (c) 2007-2008 Infrae. All rights reserved.
# $Id: Common.py 33243 2009-01-29 10:59:47Z sylvain $

from sets import Set            # For python 2.3 compatibility
import os.path
import os
import re


import zc.buildout


def ignoredFile(file):
    """Return true if the file should be ignored while checking for
    added/changed/modified files.
    """
    for suffix in ['.pyc', '.pyo', '.egg-info']:
        if file.endswith(suffix):
            return True
    return False


def reportInvalidFiles(path, name, badfiles):
    """Report invalid files.
    """
    badfiles = [file for file in badfiles if not ignoredFile(file)]
    if not badfiles:
        return
    raise ValueError("""\
In '%s':
local modifications detected while uninstalling %r: Uninstall aborted!

Please check for local modifications and make sure these are checked
in.

If you sure that these modifications can be ignored, remove the
checkout manually:

  rm -rf %s

Or if applicable, add the file to the 'svn:ignore' property of the
file's container directory.  Alternatively, add an ignore glob pattern
to your subversion client's 'global-ignores' configuration variable.
""" % (path, name, """
  rm -rf """.join(badfiles)))


def checkExistPath(path, warning=True):
    """Check that a path exist.
    """
    status = os.path.exists(path)
    if not status and warning:
        print "-------- WARNING --------"
        print "Directory %s have been removed." % os.path.abspath(path)
        print "Changes might be lost."
        print "-------- WARNING --------"
    return status


def checkAddedPaths(location, urls):
    """Check that no path have been added to that location.
    """
    current_paths = Set([os.path.join(location, s) for s in
                         os.listdir(location)])
    recipe_paths = Set(urls.keys())
    added_paths = list(current_paths - recipe_paths)
    for path in added_paths[:]:
        if path.endswith('.svn'):
            added_paths.remove(path)
    if added_paths:
        msg = "New path have been added to the location: %s."
        raise ValueError(msg % ', '.join(added_paths))


def prepareURLs(location, urls):
    """Given a list of urls/path, and a location, prepare a list of
    tuple with url, full path.
    """

    def prepareEntry(line):
        link, path = line.split()
        return os.path.join(location, path), link

    return dict([prepareEntry(l) for l in urls.splitlines() if l.strip()])


def extractNames(urls):
    """Return just the target names of the urls (used for egg names)"""

    def extractName(line):
        link, name = line.split()
        return name

    return [extractName(line) for line in urls.splitlines() if line.strip()]


class BaseRecipe(object):
    """infrae.subversion recipe. Base class.
    """

    def __init__(self, buildout, name, options):
        self.buildout = buildout
        self.name = name
        self.options = options
        # location is overridable if desired.
        location = options.get('location', None)
        if location:
            self.location = os.path.abspath(os.path.join(
                buildout['buildout']['directory'], location))
        else:
            self.location = os.path.join(
                buildout['buildout']['parts-directory'], self.name)
        options['location'] = self.location
        self.revisions = {} # Store revision information for each link
        self.updated = []   # Store updated links
        self.urls = prepareURLs(self.location, options['urls'])
        self.export = options.get('export')
        self.offline = buildout['buildout'].get('offline', 'false') == 'true'
        self.eggify = options.get('as_eggs', False)
        self.eggs = self.eggify and extractNames(options['urls']) or []
        self.newest = (
            not self.offline and
            buildout['buildout'].get('newest', 'true') == 'true'
            )
        self.verbose = buildout['buildout'].get('verbosity', 0)
        self.warning = not (options.get('no_warnings', 'false') == 'true')

    def _exportInformationToOptions(self):
        """Export revision and changed information to options.

        Options can only contains strings.
        """
        if self.options.get('export_info', False):
            self.options['updated'] = str('\n'.join(self.updated))
            str_revisions = ['%s %s' % r for r in self.revisions.items()
                             if r[1]]
            self.options['revisions'] = str('\n'.join(sorted(str_revisions)))
        # Always export egg list
        self.options['eggs'] = '\n'.join(sorted(self.eggs))

    def _updateAllRevisionInformation(self):
        """Update all revision information for defined urls.
        """
        for path, link in self.urls.items():
            if os.path.exists(path):
                self._updateRevisionInformation(link, path)

    def _updateRevisionInformation(self, link, revision):
        """Update revision information on a path.
        """
        old_revision = self.revisions.get(link, None)
        self.revisions[link] = revision
        if not (old_revision is None):
            self.updated.append(link)

    def _updatePath(self, link, path):
        """Update a single path.
        """
        raise NotImplementedError

    def _updateAllPaths(self):
        """Update the checkouts.
        """
        ignore = self.options.get('ignore_updates', False) or self.export

        num_release = re.compile('.*@[0-9]+$')
        for path, link in self.urls.items():
            if not checkExistPath(path, warning=self.warning):
                if self.verbose:
                    print "Entry %s missing, checkout a new version ..." % link
                self._installPath(link, path)
                continue

            if ignore:
                continue

            if num_release.match(link):
                if self.verbose:
                    print "Given num release for %s, skipping." % link
                continue

            if self.verbose:
                print "Updating %s" % path
            self._updatePath(link, path)

    def update(self):
        """Update the recipe.

        Does not update SVN path if the buildout is in offline mode,
        but still eggify and export information.
        """
        if self.newest:
            self._updateAllPaths()

        if self.eggify:
            self._eggify()
        self._exportInformationToOptions()
        return self.location

    def _installPath(self, link, path):
        """Checkout a single entry.
        """
        raise NotImplementedError

    def _installPathVerbose(self, link, path):
        """Checkout a single entry with verbose.
        """
        if self.verbose:
            print "%s %s to %s" % (self.export and 'Export' or 'Fetch',
                                   link, path)
        self._installPath(link, path)

    def _eggify(self):
        """Install everything as development eggs if eggs=true"""
        if self.eggify:
            target = self.buildout['buildout']['develop-eggs-directory']
            for path in self.urls.keys():
                # If we update the recipe, and we don't have newest,
                # and that some path have been deleted, all of them
                # might not be there.
                if checkExistPath(path, warning=self.warning):
                    zc.buildout.easy_install.develop(path, target)

    def install(self):
        """Checkout the checkouts.

        Fails if buildout is running in offline mode.
        """

        for path, link in self.urls.items():
            self._installPathVerbose(link, path)
        installed = [self.location]

        if self.eggify:
            self._eggify()
            # And also return the develop-eggs/*.egg-link files that are
            # ours so that an uninstall automatically zaps them.
            dev_dir = self.buildout['buildout']['develop-eggs-directory']
            egg_links = ['%s.egg-link' % egg for egg in self.eggs]
            egg_links = [os.path.join(dev_dir, link) for link in egg_links]
            installed += egg_links
        self._exportInformationToOptions()

        return installed