1
# arch-tag: 27f07153-69b6-4ddb-b704-eaabcca57e3e
2
# Author: Rob Weir <rob.weir@canonical.com>
3
# Copyright (C) 2004 Canonical Software
5
from pybaz import NameParser
7
from zope.interface import implements, classProvides
8
from canonical.launchpad.interfaces import INamespaceObject, ISourceTreeAPI, \
9
ISourceTreeFactory, IFileName, \
10
IArchiveItem, ICategoryItem, \
11
IRevisionFactory, IPatchlog, \
12
ICategoryFactory, ICategory, \
13
ILogMessage, IArchSourceTree, \
14
IDirName, IPathNameFactory, \
15
IArchiveLocation, IArchive, \
16
IBranchItem, IVersionItem, \
17
IBranch, IBranchFactory, \
18
ISetupable, IPackage, \
28
from canonical.launchpad import database
29
from canonical.lp.dbschema import ArchArchiveType
33
###############################################################################
36
class NamespaceObject(object):
38
Implement canonical.launchpad.interfaces.INamespaceObject against the
42
implements(INamespaceObject)
44
def __init__(self, fullname):
45
"""Set our own name"""
46
self._fullname = fullname
51
# Fullname is the *fully-qualified name of the object*
52
# eg rob@bah/baz--bor--0 is the fullname of a Version
53
fullname = property(fullname)
58
"""Are we not equal to x?"""
59
return x is not None \
60
and (id(x) == id(self)
61
or (self._eq_interface.providedBy(x)
62
and self.fullname == x.fullname))
65
"""Are we not equal to x?"""
66
return not self.__eq__(x)
69
###############################################################################
70
### Iteration base classes
72
class RevisionIterable(object):
74
implements(IRevisionIterable)
76
def iter_revisions(self, reverse=False):
77
raise NotImplementedError, "Not implemented yet"
79
def iter_library_revisions(self, reverse=False):
80
raise NotImplementedError, "Not implemented yet"
83
class VersionIterable(RevisionIterable):
85
implements(IVersionIterable)
87
def iter_versions(self, reverse=False):
88
raise NotImplementedError, "Not implemented yet"
90
def iter_library_versions(self, reverse=False):
91
raise NotImplementedError, "Not implemented yet"
94
class BranchIterable(VersionIterable):
96
implements(IBranchIterable)
98
def iter_branches(self):
99
raise NotImplementedError, "Not implemented yet"
101
def iter_library_branches(self):
102
raise NotImplementedError, "Not implemented yet"
105
class CategoryIterable(BranchIterable):
107
implements(ICategoryIterable)
109
def iter_categories(self):
110
raise NotImplementedError, "Not implemented yet"
112
def iter_library_categories(self):
113
raise NotImplementedError, "Not implemented yet"
116
###############################################################################
117
### Containement base classes
119
class ArchiveItem(NamespaceObject):
121
implements(IArchiveItem)
124
pass # Not implement yet
126
archive = property(archive)
129
pass # Not implement yet
131
nonarch = property(nonarch)
134
class CategoryItem(ArchiveItem):
136
implements(ICategoryItem)
139
ArchiveItem.__init__(self)
140
self._category = "category"
143
return self._category
145
category = property(category)
148
class BranchItem(CategoryItem):
150
implements(IBranchItem)
153
CategoryItem.__init__(self)
154
self._branch = "branch"
159
branch = property(branch)
162
class VersionItem(BranchItem):
164
implements(IVersionItem)
167
BranchItem.__init__(self)
173
version = property(version)
176
###############################################################################
177
### Misc base classes
179
class Setupable(ArchiveItem):
181
implements(ISetupable)
184
raise NotImplemented, "Not implemented yet"
187
class Package(Setupable, RevisionIterable):
192
raise NotImplemented, "Not implemented yet"
195
###############################################################################
196
### Concrete Namespace classes
199
class Archives(object, UserDict.DictMixin):
200
"""I am a collection of all the archives available to the system"""
201
implements(IArchiveCollection)
203
def __getitem__(self, key):
204
"""retrieve an archive"""
205
Archive._validate_name(key)
206
return self.getMapper().findByName(key)
208
def __setitem__(self, key, value):
209
"""Do not add an archive, just fail!"""
210
raise TypeError, ("Archives does not setitem. "
211
"Use the 'create' method instead")
213
def __delitem__(self, key):
214
"""Do not delete an archive, just fail!"""
215
raise TypeError, ("Archives does not delitem.")
218
"""return the current mapper"""
219
return database.ArchiveMapper()
222
return [archive.name for archive in
223
self.getMapper().findByMatchingName('%')]
225
def create(self, name, locations=[]):
226
"""create an archive"""
227
Archive._validate_name(name)
228
archive=Archive(name)
229
self.getMapper().insert(archive)
230
# TODO populate the archive locations too.
234
class ArchiveLocationRegistry(object):
235
"""I'm a list of the registered locations for an archive"""
237
def __init__(self, archive):
238
self._mapper = database.ArchiveLocationMapper()
239
self._archive = archive
241
def _createLocation(self, url, type):
242
"""Add a location to the database"""
243
location = ArchiveLocation(self._archive, url, type)
244
self._mapper.insertLocation(location)
247
def createReadWriteTargetLocation(self, url):
248
"""Create a ArchiveLocation for a read/write mirror"""
249
return self._createLocation(url, ArchArchiveType.READWRITE)
251
def createReadOnlyTargetLocation(self, url):
252
"""Create a ArchiveLocation for a read-only archive"""
253
return self._createLocation(url, ArchArchiveType.READONLY)
255
def createMirrorTargetLocation(self, url):
256
"""Create a ArchiveLocation for a mirror target"""
257
return self._createLocation(url, ArchArchiveType.MIRRORTARGET)
259
def existsLocation(self, location):
260
"""Is location in the db?"""
261
return self._mapper.locationExists(location)
263
def _get(self, type):
264
"""Return all locations"""
265
return self._mapper.get(self._archive, type=type)
267
def _remove(self, type, name):
268
raise NotImplemented, "Not implemented yet"
270
def getMirrorTargetLocations(self):
271
return self._get(ArchArchiveType.MIRRORTARGET)
273
def getReadOnlyLocations(self):
274
return self._get(ArchArchiveType.READONLY)
276
def getReadWriteLocations(self):
277
return self._get(ArchArchiveType.READWRITE)
279
def removeMirrorTarget(self, location):
280
raise NotImplemented, "Not implemented yet"
282
def removeReadonlyTarget(self, location):
283
raise NotImplemented, "Not implemented yet"
285
def removeReadWriteTarget(self, location):
286
raise NotImplemented, "Not implemented yet"
289
class ArchiveLocation(object):
290
"""I'm a single location of an archive"""
292
implements(IArchiveLocation)
294
def __init__(self, archive, url, type):
295
"""Create us, given an Archive and a url string"""
296
self._mapper = database.ArchiveLocationMapper()
298
self._archive = archive
302
"""My registered location"""
308
"""The archive I belong to"""
311
archive = property(archive)
314
"""Am I GPG-signed?"""
315
return self._mapper.isItSigned(self)
317
GPGSigned = property(GPGSigned)
319
def unregister(self):
320
"""Delete me from the database"""
321
raise NotImplementedError, "Not implemented yet!"
324
class Archive(NamespaceObject, CategoryIterable):
325
"""A database & file store backed arch Archive"""
328
_eq_interface = IArchive
330
def __init__(self, name):
331
"""Create a new Archive object from the named archive."""
332
NamespaceObject.__init__(self, name)
334
self._mapper = database.ArchiveMapper()
336
def _validate_name(archive_name):
337
"""Raise a NamespaceError if the given archive name is invalid."""
338
from pybaz import NameParser
339
if not NameParser.is_archive_name(archive_name):
340
raise NamespaceError("invalid archive name: %s" % archive_name)
341
_validate_name = staticmethod(_validate_name)
344
# If it didn't exist, it would be a MissingArchive
348
"""return the archive name"""
351
name = property(name)
354
# ArchiveLocationRegistry objects know about different types of locations.
355
return ArchiveLocationRegistry(self)
357
location = property(location)
359
def __getitem__(self, category_name):
360
"""Instanciate a Category object belonging to that archive.
362
:param category_name: unqualified category name.
363
:type category_name: str
366
if not NameParser.is_category_name(category_name):
367
raise NamespaceError('invalid category name: %s' % category_name)
368
category = Category(category_name, self)
369
mapper = database.CategoryMapper()
370
if not mapper.exists(category):
371
return MissingCategory(category_name, self)
375
def is_registered(self):
376
"""Is there an archive location associated with the archive?"""
377
# if we have at least one mention in the archivelocation table
378
location = self.location
379
if len(location.getMirrorTargetLocations()) + \
380
len(location.getReadOnlyLocations()) + \
381
len(location.getReadWriteLocations()) > 0:
386
def unregister(self):
387
"""The location method should """
388
raise RuntimeError("This is old and broken, do not use it.")
390
def make_mirror(self, name, location, signed=False, listing=False):
391
raise NotImplementedError, "Not implemented yet!"
393
def create_category(self, name):
394
"""Creata a category object, inserting it if it doesn't exist."""
395
mapper = database.CategoryMapper()
396
category = Category(name, self)
397
mapper.insert(category)
401
class MissingArchive(Archive):
402
"""I am a Special Case for missing archives"""
404
def __init__(self, name):
405
"""Create a new Archive object from the named archive."""
407
Archive.__init__(self, name)
409
def __getitem__(self, category_name):
410
"""Raise a TypeError."""
411
raise TypeError, "MissingCategory cannot getitem."""
417
class Category(Setupable, BranchIterable):
418
"""FS & DB backed Category object"""
419
classProvides(ICategoryFactory)
420
implements(ICategory)
421
_eq_interface = ICategory
423
def __init__(self, name, archive):
424
from pybaz import NameParser
425
self._archive = archive
428
self._fullname = self._archive.name + "/" + self._name
430
self._mapper = database.CategoryMapper()
433
def __getitem__(self, name):
434
"""Return the asked-for branch object"""
435
branch = Branch(name, self)
436
mapper = database.BranchMapper()
437
if not mapper.exists(branch):
438
return MissingBranch(name, self)
445
archive = property(archive)
448
"""The non-archive part of the name"""
451
nonarch = property(nonarch)
456
name = property(name)
462
self._mapper.insert(self)
464
def create_branch(self, name):
465
"""Creata a branch object, inserting it if it doesn't exist."""
466
mapper = database.BranchMapper()
467
branch = Branch(name, self)
468
mapper.insert(branch)
471
class MissingCategory(Category):
472
def __init__(self, name, archive):
473
Category.__init__(self, name, archive)
478
class Branch(CategoryItem, Package, VersionIterable):
479
"""DB-backed version of arch.Branch"""
482
classProvides(IBranchFactory)
483
_eq_interface = IBranch
485
def __init__(self, name, category):
486
# NamespaceObject.__init__(self, category.fullname + "--" + name)
487
self._category = category
489
self._fullname = category.fullname + "--" + name
494
def __getitem__(self, name):
495
"""Return the asked-for version object"""
496
version = Version(name, self)
497
mapper = database.VersionMapper()
498
if not mapper.exists(version):
499
return MissingVersion(name, self)
501
version._sqlobject_branch = mapper.findByName("%s--%s" % (self.fullname, name))
505
"""Get the last version from this branch"""
506
raise NotImplementedError, "Not implemented yet!"
510
return self._category
512
category = property(category)
515
# Our name. eg if self.fullname = "rob@bah/baz--bah", self.name = "bah".
518
name = property(name)
520
def create_version(self, name):
521
"""Creata a version object, inserting it if it doesn't exist."""
522
mapper = database.VersionMapper()
523
version = Version(name, self)
524
version._sqlobject_branch = mapper.insert(version)
527
class MissingBranch(Branch):
528
"""A branch that doesn't exist"""
533
class Version(BranchItem, Package, RevisionIterable):
534
"""Implementats canonical.launchpad.interfaces.IVersion, backed by the db"""
537
_eq_interface = IVersion
539
def __init__(self, name, branch):
540
NamespaceObject.__init__(self, branch.fullname + "--" + name)
542
self._branch = branch
547
def __getitem__(self, key):
548
"""Return the asked-for revision object"""
549
revision = Revision(key, self)
550
mapper = database.RevisionMapper()
551
if not mapper.exists(revision):
552
return MissingRevision(key, self)
554
revision.get_changeset()
557
def iter_cachedrevs(self):
558
raise NotImplementedError, "Not implemented yet!"
560
def iter_merges(self, other, reverse=False):
561
raise NotImplementedError, "Not implemented yet!"
564
return self._branch.category
566
category = property(category)
571
branch = property(branch)
576
name = property(name)
578
def create_revision(self, name):
579
"""Creata a Revision object, inserting it if it doesn't exist."""
580
mapper = database.RevisionMapper()
581
revision = Revision(name, self)
582
mapper.insert(revision)
585
class MissingVersion(Version):
586
"""A Version that doesn't exist."""
591
class Revision(VersionItem):
592
"""A Revision object backed by the database"""
594
implements(IRevision)
595
classProvides(IRevisionFactory)
596
_eq_interface = IRevision
598
def __init__(self, name, version):
599
NamespaceObject.__init__(self, version.fullname + "--" + name)
601
self._version = version
602
self._patchlog = None
608
def set_patchlog(self, patchlog):
609
self._patchlog = patchlog
610
mapper = database.RevisionMapper()
611
mapper.update_log(self, patchlog.summary)
614
return self._patchlog
616
patchlog = property(patchlog)
618
def get_changeset(self):
619
mapper = database.RevisionMapper()
620
self._cset = mapper.changeset(self)
622
def set_changeset(self, cset):
628
changeset = property(changeset)
631
pass # Not implemented yet
633
ancestor = property(ancestor)
636
pass # Not implemented yet
638
previous = property(previous)
640
def library_add(self):
641
raise NotImplementedError, "Not implemented yet!"
643
def library_remove(self):
644
raise NotImplementedError, "Not implemented yet!"
646
def library_find(self):
647
raise NotImplementedError, "Not implemented yet!"
649
def iter_ancestors(self, metoo=False):
650
raise NotImplementedError, "Not implemented yet!"
652
def cache(self, cache=None):
653
raise NotImplementedError, "Not implemented yet!"
656
raise NotImplementedError, "Not implemented yet!"
659
return self.version.branch.category
661
category = property(category)
664
return self.version.branch
666
branch = property(branch)
671
version = property(version)
673
def patchlevel(self):
676
patchlevel = property(patchlevel)
681
name = property(name)
684
class MissingRevision(Revision):
685
def __init__(self, name, version):
686
Revision.__init__(self, name, version)
691
class PatchlogFactory(object):
693
def __call__(revision, tree=None, fromlib=False):
694
raise NotImplementedError, "Not implemented yet!"
696
implements(IPatchlog)
699
pass # Not implemented yet
701
revision = property(revision)
704
pass # Not implemented yet
706
summary = property(summary)
708
def description(self):
709
pass # Not implemented yet
711
description = property(description)
714
pass # Not implemented yet
716
date = property(date)
719
pass # Not implemented yet
721
creator = property(creator)
723
def continuation(self):
724
pass # Not implemented yet
726
continuation = property(continuation)
728
def new_patches(self):
729
pass # Not implemented yet
731
new_patches = property(new_patches)
733
def merged_patches(self):
734
pass # Not implemented yet
736
merged_patches = property(merged_patches)
739
pass # Not implemented yet
741
new_files = property(new_files)
743
def modified_files(self):
744
pass # Not implemented yet
746
modified_files = property(modified_files)
748
def removed_files(self):
749
pass # Not implemented yet
751
removed_files = property(removed_files)
753
def __getitem__(self, header):
754
raise NotImplementedError, "Not implemented yet!"
757
class LogMessageFactory(object):
759
implements(ILogMessageFactory)
762
raise NotImplementedError, "Not implemented yet!"
765
class LogMessage(object):
767
implements(ILogMessage)
770
pass # Not implemented yet
772
name = property(name)
774
def description(self):
775
pass # Not implemented yet
777
description = property(description)
780
raise NotImplementedError, "Not implemented yet!"
783
raise NotImplementedError, "Not implemented yet!"
786
raise NotImplementedError, "Not implemented yet!"
788
def __getitem__(self, header):
789
raise NotImplementedError, "Not implemented yet!"
791
def __setitem__(self, header, text):
792
raise NotImplementedError, "Not implemented yet!"
795
class PathNameFactory(object):
797
implements(IPathNameFactory)
799
def __call__(path='.'):
800
raise NotImplementedError, "Not implemented yet!"
803
class PathName(object):
805
implements(IPathName)
807
def __div__(self, path):
808
raise NotImplementedError, "Not implemented yet!"
811
raise NotImplementedError, "Not implemented yet!"
814
raise NotImplementedError, "Not implemented yet!"
817
raise NotImplementedError, "Not implemented yet!"
820
raise NotImplementedError, "Not implemented yet!"
823
raise NotImplementedError, "Not implemented yet!"
826
class DirName(PathName):
830
class FileName(PathName):
831
implements(IFileName)
835
class SourceTreeAPI(object):
837
implements(ISourceTreeAPI)
839
def init_tree(self, directory, version=None, nested=False):
840
raise NotImplementedError, "Not implemented yet!"
842
def in_source_tree(self, directory=None):
843
raise NotImplementedError, "Not implemented yet!"
845
def tree_root(directory=None):
846
raise NotImplementedError, "Not implemented yet!"
849
class SourceTreeFactory(object):
851
implements(ISourceTreeFactory)
853
def __call_(root=None):
854
raise NotImplementedError, "Not implemented yet!"
857
class ArchSourceTree(DirName):
859
implements(IArchSourceTree)
861
def tree_version(self):
862
pass # Not implemented yet
864
tree_version = property(tree_version)
866
def tagging_method(self):
867
pass # Not implemented yet
869
tagging_method = property(tagging_method)