source: tailor/vcpx/repository.py @ 1150

Revision 1150, 14.0 KB checked in by lele@…, 7 years ago (diff)

Remove final slashes from the CVS module name

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        if self.delay_before_apply:
96            self.delay_before_apply = float(self.delay_before_apply)
97        self.encoding = cget(self.name, 'encoding')
98        if not self.encoding:
99            self.encoding = getpreferredencoding()
100        self.encoding_errors_policy = cget(self.name,
101                                           'encoding-errors-policy', 'strict')
102
103    def _validateConfiguration(self):
104        """
105        Validate the configuration, possibly altering/completing it.
106        """
107
108        if self.EXECUTABLE:
109            from os import getenv, pathsep
110            from os.path import isabs, exists, join
111            from vcpx.config import ConfigurationError
112            from sys import platform
113
114            if isabs(self.EXECUTABLE):
115                ok = exists(self.EXECUTABLE)
116            else:
117                ok = False
118                mswindows = (platform == "win32")
119                for path in getenv('PATH').split(pathsep):
120                    if exists(join(path, self.EXECUTABLE)):
121                        ok = True
122                    elif mswindows:
123                        for ext in ['.exe', '.bat']:
124                            if exists(join(path, self.EXECUTABLE + ext)):
125                                self.EXECUTABLE += ext
126                                ok = True
127                                break
128                    if ok:
129                        break
130            if not ok:
131                self.log.critical("Cannot find external command %r",
132                                  self.EXECUTABLE)
133                raise ConfigurationError("The command %r used "
134                                         "by %r does not exist in %r!" %
135                                         (self.EXECUTABLE, self.name,
136                                          getenv('PATH')))
137
138    def encode(self, s):
139        """
140        If `s` is an unicode object, encode it in the the right charset.
141        Return a standard Python string.
142        """
143
144        if isinstance(s, unicode):
145            return s.encode(self.encoding, self.encoding_errors_policy)
146        else:
147            return s
148
149    def workingDir(self):
150        """
151        Return an instance of the specific WorkingDir for this kind of
152        repository.
153        """
154
155        from source import InvocationError
156
157        wdname = self.kind.capitalize() + 'WorkingDir'
158        modname = 'vcpx.' + self.kind
159        try:
160            wdmod = __import__(modname, globals(), locals(), [wdname])
161            workingdir = getattr(wdmod, wdname)
162        except SyntaxError, e:
163            self.log.exception("Cannot import %r from %r", wdname, modname)
164            raise InvocationError("Cannot import %r: %s" % (wdname, e))
165        except (AttributeError, ImportError), e:
166            self.log.critical("Cannot import %r from %r", wdname, modname)
167            if self.kind == 'bzr':
168                from sys import version_info
169                if version_info < (2,4):
170                    self.log.warning("Bazaar-NG backend requires Python 2.4")
171            raise InvocationError("%r is not a known VCS kind: %s" %
172                                  (self.kind, e))
173
174        return workingdir(self)
175
176    def command(self, *args, **kwargs):
177        """
178        Return the base external command, a sequence suitable to be used
179        to init an ExternalCommand instance.
180
181        This return None if the backend uses a different way to execute
182        its actions.
183        """
184
185        executable = kwargs.get('executable', self.EXECUTABLE)
186        if executable:
187            cmd = [executable]
188            cmd.extend(args)
189            return cmd
190
191
192class ArxRepository(Repository):
193    METADIR = '_arx'
194
195    def _load(self, project):
196        Repository._load(self, project)
197        self.EXECUTABLE = project.config.get(self.name, 'arx-command', 'arx')
198
199
200class BzrRepository(Repository):
201    METADIR = '.bzr'
202
203    def _load(self, project):
204        Repository._load(self, project)
205        ppath = project.config.get(self.name, 'python-path')
206        if ppath:
207            from sys import path
208
209            if ppath not in path:
210                path.insert(0, ppath)
211
212
213class CdvRepository(Repository):
214    METADIR = '.cdv'
215
216    def _load(self, project):
217        Repository._load(self, project)
218        self.EXECUTABLE = project.config.get(self.name, 'cdv-command', 'cdv')
219
220
221class CgRepository(Repository):
222    METADIR = '.git'
223
224    def _load(self, project):
225        Repository._load(self, project)
226        self.EXECUTABLE = project.config.get(self.name, 'cg-command', 'cg')
227
228
229class CvsRepository(Repository):
230    METADIR = 'CVS'
231
232    def _load(self, project):
233        Repository._load(self, project)
234        self.EXECUTABLE = project.config.get(self.name, 'cvs-command', 'cvs')
235        self.tag_entries = project.config.get(self.name, 'tag-entries', 'True')
236        self.freeze_keywords = project.config.get(self.name, 'freeze-keywords', 'False')
237
238    def _validateConfiguration(self):
239        from os.path import split
240        from config import ConfigurationError
241
242        Repository._validateConfiguration(self)
243
244        if not self.module and self.repository:
245            self.module = split(self.repository)[1]
246
247        if not self.module:
248            self.log.critical('Missing module information in %r', self.name)
249            raise ConfigurationError("Must specify a repository and maybe "
250                                     "a module also")
251
252        if self.module.endswith('/'):
253            self.log.debug("Removing final slash from %r in %r",
254                           self.module, self.name)
255            self.module = self.module.rstrip('/')
256
257
258class CvspsRepository(CvsRepository):
259    def command(self, *args, **kwargs):
260        if kwargs.get('cvsps', False):
261            kwargs['executable'] = self.__cvsps
262        return CvsRepository.command(self, *args, **kwargs)
263
264    def _load(self, project):
265        CvsRepository._load(self, project)
266        self.__cvsps = project.config.get(self.name, 'cvsps-command', 'cvsps')
267
268
269class DarcsRepository(Repository):
270    METADIR = '_darcs'
271
272    def _load(self, project):
273        Repository._load(self, project)
274        cget = project.config.get
275        self.EXECUTABLE = cget(self.name, 'darcs-command', 'darcs')
276        self.use_look_for_adds = cget(self.name, 'look-for-adds', 'False')
277
278    def command(self, *args, **kwargs):
279        if args[0] == 'record' and self.use_look_for_adds:
280            args = args + ('--look-for-adds',)
281        return Repository.command(self, *args, **kwargs)
282
283
284class GitRepository(Repository):
285    METADIR = '.git'
286
287    def _load(self, project):
288        Repository._load(self, project)
289        self.EXECUTABLE = project.config.get(self.name, 'git-command', 'git')
290
291
292class HgRepository(Repository):
293    METADIR = '.hg'
294
295    def _load(self, project):
296        Repository._load(self, project)
297        ppath = project.config.get(self.name, 'python-path')
298        if ppath:
299            from sys import path
300
301            if ppath not in path:
302                path.insert(0, ppath)
303        self.EXTRA_METADIRS = ['.hgtags']
304
305    def _validateConfiguration(self):
306        """
307        Mercurial expects all data to be in utf-8, so we disallow other encodings
308        """
309        Repository._validateConfiguration(self)
310
311        if self.encoding.upper() != 'UTF-8':
312            self.log.warning("Forcing UTF-8 encoding instead of " + self.encoding)
313            self.encoding = 'UTF-8'
314
315class MonotoneRepository(Repository):
316    METADIR = '_MTN'
317
318    def _load(self, project):
319        Repository._load(self, project)
320        cget = project.config.get
321        self.EXECUTABLE = cget(self.name, 'monotone-command', 'mtn')
322        self.keyid = cget(self.name, 'keyid')
323        self.passphrase = cget(self.name, 'passphrase')
324        self.keyfile = cget(self.name, 'keyfile')
325        self.keygenid = cget(self.name, 'keygenid')
326        self.custom_lua = cget(self.name, 'custom_lua')
327
328
329class SvnRepository(Repository):
330    METADIR = '.svn'
331
332    def command(self, *args, **kwargs):
333        if kwargs.get('svnadmin', False):
334            kwargs['executable'] = self.__svnadmin
335        return Repository.command(self, *args, **kwargs)
336
337    def _load(self, project):
338        Repository._load(self, project)
339        cget = project.config.get
340        self.EXECUTABLE = cget(self.name, 'svn-command', 'svn')
341        self.__svnadmin = cget(self.name, 'svnadmin-command', 'svnadmin')
342        self.use_propset = cget(self.name, 'use-propset', False)
343        self.filter_badchars = cget(self.name, 'filter-badchars', False)
344        self.use_limit = cget(self.name, 'use-limit', True)
345        self.trust_root = cget(self.name, 'trust-root', False)
346        self.ignore_externals = cget(self.name, 'ignore-externals', True)
347
348    def _validateConfiguration(self):
349        from vcpx.config import ConfigurationError
350
351        Repository._validateConfiguration(self)
352
353        if not self.repository:
354            self.log.critical('Missing repository information in %r', self.name)
355            raise ConfigurationError("Must specify the root of the "
356                                     "Subversion repository used "
357                                     "as %s with the option "
358                                     "'repository'" % self.which)
359        elif self.repository.endswith('/'):
360            self.log.debug("Removing final slash from %r in %r",
361                           self.repository, self.name)
362            self.repository = self.repository.rstrip('/')
363
364        if not self.module:
365            self.log.critical('Missing module information in %r', self.name)
366            raise ConfigurationError("Must specify the path within the "
367                                     "Subversion repository as 'module'")
368
369        if self.module == '.':
370            self.log.warning("Replacing '.' with '/' in module name in %r",
371                             self.name)
372            self.module = '/'
373        elif not self.module.startswith('/'):
374            self.log.debug("Prepending '/' to module %r in %r",
375                           self.module, self.name)
376            self.module = '/' + self.module
377
378    def workingDir(self):
379        wd = Repository.workingDir(self)
380        wd.USE_PROPSET = self.use_propset
381        return wd
382
383
384class TlaRepository(Repository):
385    METADIR = '{arch}'
386
387    def _load(self, project):
388        Repository._load(self, project)
389        self.EXECUTABLE = project.config.get(self.name, 'tla-command', 'tla')
390        self.IGNORE_IDS = project.config.get(self.name, 'ignore-ids', False)
391        if self.IGNORE_IDS:
392            self.EXTRA_METADIRS = ['.arch-ids']
393
394
395class BazRepository(TlaRepository):
396    def _load(self, project):
397        TlaRepository._load(self, project)
398        self.EXECUTABLE = project.config.get(self.name, 'baz-command', 'baz')
399
400    def command(self, *args, **kwargs):
401        if args:
402            if args[0] == 'tree-lint':
403                args = list(args)
404                args[0] = 'lint'
405            elif args[0] == 'missing' and args[1] == '-f':
406                args = list(args)
407                del args[1]
408        return TlaRepository.command(self, *args, **kwargs)
Note: See TracBrowser for help on using the repository browser.