source: tailor/vcpx/repository.py @ 1039

Revision 1039, 13.1 KB checked in by lele@…, 7 years ago (diff)

Cosmetic whitespace

Line 
1# -*- mode: python; coding: utf-8 -*-
2# :Progetto: vcpx -- Configuration details about known repository kinds
3# :Creato:   gio 04 ago 2005 13:32:55 CEST
4# :Autore:   Lele Gaifax <lele@nautilus.homeip.net>
5# :Licenza:  GNU General Public License
6#
7
8"""
9This module holds a simple abstraction of what a repository is for
10Tailor purposes.
11"""
12
13__docformat__ = 'reStructuredText'
14
15REPO_DESCRIPTION = """\
16 Repository: %s
17       Kind: %s"""
18
19class Repository(object):
20    """
21    Collector for the configuration of a single repository.
22    """
23    METADIR = None
24    EXTRA_METADIRS = []
25    EXECUTABLE = None
26
27    def __new__(klass, name, project, which):
28        """
29        Return the right subclass for kind, if it exists.
30        """
31
32        kind = name[:name.index(':')]
33        subclass = klass
34        subclassname = kind.capitalize() + 'Repository'
35        if subclassname in globals():
36            subclass = globals()[subclassname]
37        instance = super(Repository, klass).__new__(subclass, name,
38                                                    project, which)
39        instance.kind = kind
40        return instance
41
42    def __init__(self, name, project, which):
43        """
44        Initialize a new instance of Repository, with a `name` and
45        associated to given `project`; `which` is either "source"
46        or "target".
47        """
48
49        from logging import getLogger
50        from weakref import ref
51
52        self.name = name
53        self.which = which
54        self.projectref = ref(project)
55        self._load(project)
56        self.log = getLogger('tailor.vcpx.%s.%s' % (self.__class__.__name__,
57                                                    which))
58        self._validateConfiguration()
59
60    def __str__(self):
61
62        s = REPO_DESCRIPTION % (self.repository, self.kind)
63        if self.module:
64            s += "\n     Module: %s" % self.module
65        return s
66
67    def _load(self, project):
68        """
69        Load the configuration for this repository.
70
71        The two main and mandatory attributes, ``repository`` and ``module``
72        can be specified either on the specific slot in the config file, or
73        as ``source-repository`` (or ``target-repository``) in its [DEFAULT]
74        section.
75
76        If the configuration does not specify a specific ``root-directory``
77        take the one from the project.
78        """
79
80        from os.path import split, expanduser
81        from locale import getpreferredencoding
82
83        cget = project.config.get
84        self.repository = cget(self.name, 'repository') or \
85                          cget(self.name, '%s-repository' % self.which)
86        if self.repository:
87            self.repository = expanduser(self.repository)
88        self.module = cget(self.name, 'module') or \
89                      cget(self.name, '%s-module' % self.which)
90        self.rootdir = cget(self.name, 'root-directory',
91                            vars={'root-directory': project.rootdir})
92        self.subdir = cget(self.name, 'subdir',
93                           vars={'subdir': project.subdir})
94        self.delay_before_apply = cget(self.name, 'delay-before-apply')
95        self.encoding = cget(self.name, 'encoding')
96        if not self.encoding:
97            self.encoding = getpreferredencoding()
98        self.encoding_errors_policy = cget(self.name,
99                                           'encoding-errors-policy', 'strict')
100
101    def _validateConfiguration(self):
102        """
103        Validate the configuration, possibly altering/completing it.
104        """
105
106        if self.EXECUTABLE:
107            from os import getenv, pathsep
108            from os.path import isabs, exists, join
109            from vcpx.config import ConfigurationError
110            from sys import platform
111
112            if isabs(self.EXECUTABLE):
113                ok = exists(self.EXECUTABLE)
114            else:
115                ok = False
116                mswindows = (platform == "win32")
117                for path in getenv('PATH').split(pathsep):
118                    if exists(join(path, self.EXECUTABLE)):
119                        ok = True
120                    elif mswindows:
121                        for ext in ['.exe', '.bat']:
122                            if exists(join(path, self.EXECUTABLE + ext)):
123                                self.EXECUTABLE += ext
124                                ok = True
125                                break
126                    if ok:
127                        break
128            if not ok:
129                self.log.critical("Cannot find external command %r",
130                                  self.EXECUTABLE)
131                raise ConfigurationError("The command %r used "
132                                         "by %r does not exist in %r!" %
133                                         (self.EXECUTABLE, self.name,
134                                          getenv('PATH')))
135
136    def encode(self, s):
137        """
138        If `s` is an unicode object, encode it in the the right charset.
139        Return a standard Python string.
140        """
141
142        if isinstance(s, unicode):
143            return s.encode(self.encoding, self.encoding_errors_policy)
144        else:
145            return s
146
147    def workingDir(self):
148        """
149        Return an instance of the specific WorkingDir for this kind of
150        repository.
151        """
152
153        from source import InvocationError
154
155        wdname = self.kind.capitalize() + 'WorkingDir'
156        modname = 'vcpx.' + self.kind
157        try:
158            wdmod = __import__(modname, globals(), locals(), [wdname])
159            workingdir = getattr(wdmod, wdname)
160        except SyntaxError, e:
161            self.log.exception("Cannot import %r from %r", wdname, modname)
162            raise InvocationError("Cannot import %r: %s" % (wdname, e))
163        except (AttributeError, ImportError), e:
164            self.log.critical("Cannot import %r from %r", wdname, modname)
165            if self.kind == 'bzr':
166                from sys import version_info
167                if version_info < (2,4):
168                    self.log.warning("Bazaar-NG backend requires Python 2.4")
169            raise InvocationError("%r is not a known VCS kind: %s" %
170                                  (self.kind, e))
171
172        return workingdir(self)
173
174    def command(self, *args, **kwargs):
175        """
176        Return the base external command, a sequence suitable to be used
177        to init an ExternalCommand instance.
178
179        This return None if the backend uses a different way to execute
180        its actions.
181        """
182
183        executable = kwargs.get('executable', self.EXECUTABLE)
184        if executable:
185            cmd = [executable]
186            cmd.extend(args)
187            return cmd
188
189
190class ArxRepository(Repository):
191    METADIR = '_arx'
192
193    def _load(self, project):
194        Repository._load(self, project)
195        self.EXECUTABLE = project.config.get(self.name, 'arx-command', 'arx')
196
197
198class BzrRepository(Repository):
199    METADIR = '.bzr'
200
201    def _load(self, project):
202        Repository._load(self, project)
203        ppath = project.config.get(self.name, 'python-path')
204        if ppath:
205            from sys import path
206
207            if ppath not in path:
208                path.insert(0, ppath)
209
210
211class CdvRepository(Repository):
212    METADIR = '.cdv'
213
214    def _load(self, project):
215        Repository._load(self, project)
216        self.EXECUTABLE = project.config.get(self.name, 'cdv-command', 'cdv')
217
218
219class CgRepository(Repository):
220    METADIR = '.git'
221
222    def _load(self, project):
223        Repository._load(self, project)
224        self.EXECUTABLE = project.config.get(self.name, 'cg-command', 'cg')
225
226
227class CvsRepository(Repository):
228    METADIR = 'CVS'
229
230    def _load(self, project):
231        Repository._load(self, project)
232        self.EXECUTABLE = project.config.get(self.name, 'cvs-command', 'cvs')
233        self.tag_entries = project.config.get(self.name, 'tag-entries', 'True')
234
235    def _validateConfiguration(self):
236        from os.path import split
237        from config import ConfigurationError
238
239        Repository._validateConfiguration(self)
240
241        if not self.module and self.repository:
242            self.module = split(self.repository)[1]
243
244        if not self.module:
245            self.log.critical('Missing module information')
246            raise ConfigurationError("Must specify a repository and maybe "
247                                     "a module also")
248
249
250class CvspsRepository(CvsRepository):
251    def command(self, *args, **kwargs):
252        if kwargs.get('cvsps', False):
253            kwargs['executable'] = self.__cvsps
254        return CvsRepository.command(self, *args, **kwargs)
255
256    def _load(self, project):
257        CvsRepository._load(self, project)
258        self.__cvsps = project.config.get(self.name, 'cvsps-command', 'cvsps')
259        self.tag_entries = project.config.get(self.name, 'tag-entries', 'True')
260
261
262class DarcsRepository(Repository):
263    METADIR = '_darcs'
264
265    def _load(self, project):
266        Repository._load(self, project)
267        self.EXECUTABLE = project.config.get(self.name, 'darcs-command', 'darcs')
268
269
270class GitRepository(Repository):
271    METADIR = '.git'
272
273    def _load(self, project):
274        Repository._load(self, project)
275        self.EXECUTABLE = project.config.get(self.name, 'git-command', 'git')
276
277
278class HgRepository(Repository):
279    METADIR = '.hg'
280
281    def _load(self, project):
282        Repository._load(self, project)
283        self.EXECUTABLE = project.config.get(self.name, 'hg-command', 'hg')
284        self.EXTRA_METADIRS = ['.hgtags']
285
286
287class HglibRepository(Repository):
288    METADIR = '.hg'
289
290    def _load(self, project):
291        Repository._load(self, project)
292        ppath = project.config.get(self.name, 'python-path')
293        if ppath:
294            from sys import path
295
296            if ppath not in path:
297                path.insert(0, ppath)
298        self.EXTRA_METADIRS = ['.hgtags']
299
300
301class MonotoneRepository(Repository):
302    METADIR = 'MT'
303
304    def _load(self, project):
305        Repository._load(self, project)
306        cget = project.config.get
307        self.EXECUTABLE = cget(self.name, 'monotone-command', 'monotone')
308        self.keyid = cget(self.name, 'keyid')
309        self.passphrase = cget(self.name, 'passphrase')
310        self.keyfile = cget(self.name, 'keyfile')
311        self.keygenid = cget(self.name, 'keygenid')
312        self.custom_lua = cget(self.name, 'custom_lua')
313
314
315class SvnRepository(Repository):
316    METADIR = '.svn'
317
318    def command(self, *args, **kwargs):
319        if kwargs.get('svnadmin', False):
320            kwargs['executable'] = self.__svnadmin
321        return Repository.command(self, *args, **kwargs)
322
323    def _load(self, project):
324        Repository._load(self, project)
325        cget = project.config.get
326        self.EXECUTABLE = cget(self.name, 'svn-command', 'svn')
327        self.__svnadmin = cget(self.name, 'svnadmin-command', 'svnadmin')
328        self.use_propset = cget(self.name, 'use-propset', False)
329        self.filter_badchars = cget(self.name, 'filter-badchars', False)
330        self.use_limit = cget(self.name, 'use-limit', True)
331        self.trust_root = cget(self.name, 'trust-root', False)
332
333    def _validateConfiguration(self):
334        from vcpx.config import ConfigurationError
335
336        Repository._validateConfiguration(self)
337
338        if not self.repository:
339            self.log.critical('Missing repository information')
340            raise ConfigurationError("Must specify the root of the "
341                                     "Subversion repository used "
342                                     "as %s with the option "
343                                     "'repository'" % self.which)
344
345        if not self.module:
346            self.log.critical('Missing module information')
347            raise ConfigurationError("Must specify the path within the "
348                                     "Subversion repository as 'module'")
349
350        if self.module == '.':
351            self.log.warning("Replacing '.' with '/' in module name")
352            self.module = '/'
353        elif not self.module.startswith('/'):
354            self.log.debug("Prepending '/' to module %r", self.module)
355            self.module = '/' + self.module
356
357    def workingDir(self):
358        wd = Repository.workingDir(self)
359        wd.USE_PROPSET = self.use_propset
360        return wd
361
362
363class SvndumpRepository(Repository):
364
365    def _validateConfiguration(self):
366        Repository._validateConfiguration(self)
367
368        if self.module and self.module.startswith('/'):
369            self.log.debug("Removing starting '/' from module %r", self.module)
370            self.module = self.module[1:]
371        if self.module and not self.module.endswith('/'):
372            self.module = self.module+'/'
373
374
375class TlaRepository(Repository):
376    METADIR = '{arch}'
377
378    def _load(self, project):
379        Repository._load(self, project)
380        self.EXECUTABLE = project.config.get(self.name, 'tla-command', 'tla')
381        self.IGNORE_IDS = project.config.get(self.name, 'ignore-ids', False)
382        if self.IGNORE_IDS:
383            self.EXTRA_METADIRS = ['.arch-ids']
384
385
386class BazRepository(TlaRepository):
387    def _load(self, project):
388        TlaRepository._load(self, project)
389        self.EXECUTABLE = project.config.get(self.name, 'baz-command', 'baz')
390
391    def command(self, *args, **kwargs):
392        if args:
393            if args[0] == 'tree-lint':
394                args[0] = 'lint'
395        return TlaRepository.command(self, *args, **kwargs)
Note: See TracBrowser for help on using the repository browser.