Package pybaz :: Module _builtin
[frames] | no frames]

Source Code for Module pybaz._builtin

   1  # arch-tag: david@allouche.net - 2003-11-17 15:23:30 332029000 
   2  # Copyright (C) 2003 David Allouche <david@allouche.net> 
   3  # 
   4  #    This program is free software; you can redistribute it and/or modify 
   5  #    it under the terms of the GNU General Public License as published by 
   6  #    the Free Software Foundation; either version 2 of the License, or 
   7  #    (at your option) any later version. 
   8  # 
   9  #    This program is distributed in the hope that it will be useful, 
  10  #    but WITHOUT ANY WARRANTY; without even the implied warranty of 
  11  #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  12  #    GNU General Public License for more details. 
  13  # 
  14  #    You should have received a copy of the GNU General Public License 
  15  #    along with this program; if not, write to the Free Software 
  16  #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
  17   
  18  """ 
  19  Internal module providing top-level pybaz package names 
  20   
  21  This module implements the top-level public interface for the 
  22  pybaz_ package. But for convenience reasons the author prefers 
  23  to store this code in a file separate from ``__init__.py``. 
  24   
  25  .. _pybaz: pybaz-module.html 
  26   
  27  This module is strictly internal and should never be used. 
  28   
  29  :var backend: See `pybaz.backend` 
  30   
  31  :var _arch: Internal deprecated interface to the backend 
  32  """ 
  33   
  34  ### Package docstring ### 
  35   
  36  import os 
  37  import shutil 
  38  import itertools 
  39  import re 
  40  from sets import ImmutableSet 
  41  import errors 
  42  import util 
  43  from pathname import PathName, FileName, DirName 
  44  from deprecation import deprecated_usage, deprecated_callable 
  45  from _escaping import * 
  46  from _output import * 
  47  from _location import * 
  48   
  49  __all__ = [] 
  50   
51 -def public(*names):
52 __all__.extend(names)
53 54 55 ### Backend abstraction and compatibility 56
57 -class _BackendProxy(object):
58 """Internal hack for compatibility with _arch"""
59 - def __getattribute__(self, name):
60 return getattr(backend._module, name)
61 - def __setattr__(self, name, value):
62 setattr(backend._module, name, value)
63 64 _arch = _BackendProxy() 65 66 from backends import commandline 67 68 backend = commandline.default_backend() 69 70 public('backend') 71 72 73 ### Factory 74 75 public('Factory', 'factory') 76
77 -def _check_param_type(name, value, type):
78 if not isinstance(value, type): 79 if type.__module__ == '__builtin__': 80 typename = type.__name__ 81 else: 82 typename = type.__module__ + '.' + type.__name__ 83 raise TypeError("%s must be a %s, but was: %r" 84 % (name, typename, value))
85 86
87 -class Factory(object):
88 89 """Abstract factory for objects created by the public interface. 90 91 Eventually, it will possible to alter the type all objects created by 92 PyBaz, system-wide (by assigning to `pybaz.factory`) or locally (by using a 93 factory attribute in instances). 94 95 Currently, this is only used internally to localise the cyclic dependencies 96 between various internal modules. 97 """ 98
99 - def Archive(self, name):
100 """Create an Archive. 101 102 :param name: archive name 103 :type name: str 104 :rtype: `Archive` 105 """ 106 _check_param_type('name', name, str) 107 return Archive(name)
108
109 - def isArchive(self, obj):
110 """Does the object implement the Archive interface? 111 112 :rtype: bool 113 """ 114 return isinstance(obj, Archive)
115
116 - def Version(self, name):
117 """Create a Version. 118 119 :param name: fully qualified name of the version. 120 :type name: str 121 :rtype: `Version` 122 """ 123 _check_param_type('name', name, str) 124 return Version(name)
125
126 - def isVersion(self, obj):
127 """Does the object implement the Version interface? 128 129 :rtype: bool 130 """ 131 return isinstance(obj, Version)
132
133 - def Revision(self, name):
134 """Create a Revision. 135 136 :param name: fully qualified name of the revision 137 :type name: str 138 :rtype: `Revision` 139 """ 140 _check_param_type('name', name, str) 141 return Revision(name)
142
143 - def isRevision(self, obj):
144 """Does the object implement the Revision interface? 145 146 :rtype: bool 147 """ 148 return isinstance(obj, Revision)
149
150 - def isSourceTree(self, obj):
151 """Does the object implement the SourceTree interface? 152 153 :rtype: bool 154 """ 155 return isinstance(obj, ArchSourceTree)
156
157 - def ArchiveLocation(self, url):
158 """Create an ArchiveLocation. 159 160 :type url: str 161 """ 162 _check_param_type('url', url, str) 163 return ArchiveLocation(url)
164
165 - def isArchiveLocation(self, obj):
166 """Does the object implement the ArchiveLocation interface? 167 168 :rtype: bool 169 """ 170 return isinstance(obj, ArchiveLocation)
171 172 173 factory = Factory() 174 175 176 ### Internal helpers for namespace sanity checks 177
178 -class _unsafe(tuple):
179 """Used internally to disable namespace sanity checks"""
180 181
182 -def _archive_param(archive):
183 """Internal argument checking utility""" 184 if isinstance(archive, Archive): 185 return archive.name 186 else: 187 if not NameParser.is_archive_name(archive): 188 raise errors.NamespaceError(archive, 'archive name') 189 return archive
190
191 -def _version_param(vsn):
192 """Internal argument checking utility""" 193 if isinstance(vsn, Version): 194 return vsn.fullname 195 else: 196 p = NameParser(vsn) 197 if not p.has_archive() or not p.is_version(): 198 raise errors.NamespaceError(vsn, 'fully-qualified version') 199 return vsn
200
201 -def _revision_param(rev):
202 """Internal argument checking utility.""" 203 if isinstance(rev, Revision): 204 return rev.fullname 205 else: 206 p = NameParser(rev) 207 if not p.has_archive() or not p.has_patchlevel(): 208 raise errors.NamespaceError(rev, 'fully-qualified revision') 209 return rev
210
211 -def _version_revision_param(vsn_rev):
212 """Internal argument checking utility""" 213 if isinstance(vsn_rev, BranchItem): 214 return vsn_rev.fullname 215 else: 216 p = NameParser(vsn_rev) 217 if not p.has_archive() or not p.has_version(): 218 raise errors.NamespaceError(vsn_rev, 219 'fully-qualified version or revision') 220 return vsn_rev
221
222 -def _package_revision_param(pkg_rev):
223 """Internal argument checking utility""" 224 if isinstance(pkg_rev, CategoryItem): 225 return pkg_rev.fullname 226 else: 227 p = NameParser(pkg_rev) 228 if not p.has_archive() or not p.has_package(): 229 raise errors.NamespaceError(pkg_rev, 230 'fully-qualified package or revision') 231 return pkg_rev
232
233 -def _check_version_param(param, name):
234 """Internal argument checking utility""" 235 if not isinstance(param, Version): 236 raise TypeError("Parameter \"%r\" must be a Version, but was: %r" \ 237 % (name, param))
238
239 -def _check_revision_param(param, name):
240 """Internal argument checking utility""" 241 if not isinstance(param, Revision): 242 raise TypeError("Parameter \"%s\" must be a Revision" 243 " but was: %r" % (name, param))
244 245 246 ### Base class for all namespace classes ### 247 248 public('NamespaceObject') 249
250 -class NamespaceObject(object):
251 252 """Base class for all archive objects.""" 253
254 - def get_fullname(self):
255 """Deprecated 256 257 Fully qualified name of this namespace object. 258 259 :rtype: str 260 :see: `NamespaceObject.fullname` 261 """ 262 raise NotImplementedError
263 264 fullname = property(get_fullname, doc = """ 265 Fully qualfied name of this namespace object. 266 267 :type: str 268 """) 269
270 - def exists(self):
271 """Does this namespace exists? 272 273 Within the Arch model, history cannot be changed: created archive 274 entries cannot be deleted. However, it is possible to ``unregister`` an 275 archive, or to find references to archives whose location is not known. 276 Thus, existence cannot always be decided. Testing for the existence of 277 a name in a non-registered archive raises 278 `errors.ArchiveNotRegistered`. 279 280 :return: whether this namespace object exists. 281 :rtype: bool 282 :raise errors.ArchiveNotRegistered: the archive name is not registered, 283 so existence cannot be decided. 284 :raise errors.ExecProblem: there was a problem accessing the archive. 285 """ 286 raise NotImplementedError
287
288 - def __repr__(self):
289 """Fully-qualified name in angle brackets. 290 291 :rtype: str 292 """ 293 return '%s.%s(%r)' % ( 294 self.__class__.__module__, self.__class__.__name__, self.fullname)
295
296 - def __str__(self):
297 """Fully-qualified name. 298 299 Returns the value of the fullname attribute. 300 301 :rtype: str 302 """ 303 return self.fullname
304
305 - def __eq__(self, x):
306 """Compare types and fully-qualified names. 307 308 :return: wether objects have the same types and names. 309 :rtype: bool 310 """ 311 return type(self) is type(x) \ 312 and self.__class__ is x.__class__ \ 313 and self.fullname == x.fullname
314
315 - def __ne__(self, x):
316 """Compare types and fully-qualified names. 317 318 :return: whether objects have different types or names. 319 :rtype: bool 320 """ 321 return type(self) is not type(x) \ 322 or self.__class__ is not x.__class__ \ 323 or self.fullname != x.fullname
324 325 326 ### Mixins for archive iteration aspect ### 327 328 public( 329 'RevisionIterable', 330 'VersionIterable', 331 'BranchIterable', 332 'CategoryIterable', 333 ) 334
335 -class RevisionIterable(NamespaceObject):
336 337 """Abstract class for namespace classes above Revision. 338 339 RevisionIterable provides features which are common to all objects 340 containing revisions. 341 """ 342
343 - def iter_revisions(self, reverse=False):
344 """Iterate over archive revisions. 345 346 :param reverse: reverse order, recent revisions first. 347 :type reverse: bool 348 :return: all existing revisions in this namespace. 349 :rtype: iterable of `Revision` 350 351 :precondition: `self.exists()` returns ``True``. 352 """ 353 raise NotImplementedError
354
355 - def iter_library_revisions(self, reverse=False):
356 """Iterate over library revisions. 357 358 :param reverse: reverse order, recent revisions first. 359 :type reverse: bool 360 :return: revisions in this namespace which are present in the 361 revision library. 362 :rtype: iterable of `Revision` 363 """ 364 raise NotImplementedError
365 366
367 -class VersionIterable(RevisionIterable):
368 369 """Abstract class for archive classes above Version. 370 371 VersionIterable provides features which are common to all objects 372 containing versions. 373 """ 374
375 - def iter_versions(self, reverse=False):
376 """Iterate over archive versions. 377 378 :param reverse: reverse order, higher versions first. 379 :type reverse: bool 380 :return: all existing versions in this namespace. 381 :rtype: iterable of `Version` 382 383 :precondition: `self.exists()` returns ``True``. 384 """ 385 raise NotImplementedError
386
387 - def iter_library_versions(self, reverse=False):
388 """Iterate over library revisions. 389 390 :param reverse: reverse order, higher versions first. 391 :type reverse: bool 392 :return: versions in this namespace which are present in the 393 revision library. 394 :rtype: iterable of `Version` 395 """ 396 raise NotImplementedError
397
398 - def iter_revisions(self, reverse=False):
399 for v in self.iter_versions(reverse): 400 for r in v.iter_revisions(reverse): yield r
401
402 - def iter_library_revisions(self, reverse=False):
403 for v in self.iter_library_versions(reverse): 404 for r in v.iter_library_revisions(reverse): yield r
405 406
407 -class BranchIterable(VersionIterable):
408 409 """Base class for archive classes above Branch. 410 411 BranchIterable provides features which are common to all objects 412 containing branches. 413 """ 414
415 - def iter_branches(self):
416 """Iterate over archive branches. 417 418 :return: all existing branches in this namespace. 419 :rtype: iterable of `Branch` 420 :precondition: `self.exists()` returns ``True``. 421 """ 422 raise NotImplementedError
423
424 - def iter_library_branches(self):
425 """Iterate over library branches. 426 427 :return: branches in this namespace which are present in the 428 revision library. 429 :rtype: iterable of `Branch` 430 """ 431 raise NotImplementedError
432
433 - def iter_versions(self, reverse=False):
434 for b in self.iter_branches(): 435 for v in b.iter_versions(reverse): yield v
436
437 - def iter_library_versions(self, reverse=False):
438 for b in self.iter_library_branches(): 439 for v in b.iter_library_versions(reverse): yield v
440 441
442 -class CategoryIterable(BranchIterable):
443 444 """Base class for Archive. 445 446 CategoryIterable provides features for the aspect of Archive wich 447 relates to its containing other archive objects. 448 """ 449
450 - def iter_categories(self):
451 """Iterate over archive categories. 452 453 :return: all existing categories in this namespace. 454 :rtype: iterable of `Category` 455 :precondition: `self.exists()` returns ``True``. 456 """ 457 raise NotImplementedError
458
459 - def iter_library_categories(self):
460 """Iterate over library categories. 461 462 :return: categories in this namespace which are present in the 463 revision library. 464 :rtype: iterable of `Category` 465 """ 466 raise NotImplementedError
467
468 - def iter_branches(self):
469 for c in self.iter_categories(): 470 for b in c.iter_branches(): yield b
471
472 - def iter_library_branches(self):
473 for c in self.iter_library_categories(): 474 for b in c.iter_library_branches(): yield b
475 476 477 ### Base classes for archive containment aspect ### 478 479 public( 480 'ArchiveItem', 481 'CategoryItem', 482 'BranchItem', 483 'VersionItem', 484 ) 485
486 -class ArchiveItem(NamespaceObject):
487 488 """Base class for all archive components classes. 489 490 ArchiveItem provides features common to all objects which are 491 structural components of Archive. 492 """ 493
494 - def __init__(self, archive, nonarch):
495 self._archive = archive 496 self._nonarch = nonarch
497
498 - def get_archive(self):
499 """Deprecated. 500 501 Archive which contains this namespace object. 502 503 :rtype: `Archive` 504 :see: `ArchiveItem.archive` 505 """ 506 deprecated_callable(self.get_archive, (type(self), 'archive')) 507 return self.archive
508
509 - def _get_archive(self):
510 return Archive(_unsafe((self._archive,)))
511 512 archive = property(_get_archive, doc=""" 513 Archive which contains this namespace object. 514 515 :type: `Archive` 516 """) 517
518 - def get_nonarch(self):
519 """Deprecated. 520 521 Non-arch part of this namespace name. 522 523 :rtype: str 524 :see: `ArchiveItem.nonarch` 525 """ 526 deprecated_callable(self.get_nonarch, (type(self), 'nonarch')) 527 return self.nonarch
528
529 - def _get_nonarch(self):
530 return self._nonarch
531 532 nonarch = property(_get_nonarch, doc=""" 533 Non-arch part of this namespace name. 534 535 :type: str 536 """) 537
538 - def get_fullname(self):
539 deprecated_callable(self.get_fullname, (type(self), 'fullname')) 540 return self.fullname
541
542 - def _get_fullname(self):
543 return '%s/%s' % (self._archive, self._nonarch)
544 545 fullname = property(_get_fullname, doc=NamespaceObject.fullname.__doc__)
546 547
548 -class CategoryItem(ArchiveItem):
549 550 """Base class for archive classes below Category. 551 552 CategoryItem provides features common to all objects which are 553 structural components of Category. 554 """ 555
556 - def get_category(self):
557 """Deprecated. 558 559 Category which contains this namespace object. 560 561 :rtype: `Category` 562 :see: `CategoryItem.category` 563 """ 564 deprecated_callable(self.get_category, (type(self), 'category')) 565 return self.category
566
567 - def _get_category(self):
568 return Category(_unsafe((self._archive, self._nonarch.split('--')[0])))
569 570 category = property(_get_category, doc=""" 571 Category which contains this object. 572 573 :type: `Category` 574 """)
575 576
577 -class BranchItem(CategoryItem):
578 579 """Base class for archive classes Version and Revision. 580 581 BranchItem provides features common to all objects which are 582 structural components of Branch. 583 """ 584
585 - def get_branch(self):
586 """Deprecated. 587 588 Branch which contains this object. 589 590 :rtype: `Branch` 591 :see: `BranchItem.branch` 592 """ 593 deprecated_callable(self.get_branch, (type(self), 'branch')) 594 return self.branch
595
596 - def _get_branch(self):
597 assert isinstance(self, Version) 598 package = '--'.join(self._nonarch.split('--')[0:-1]) 599 return Branch(_unsafe((self._archive, package)))
600 601 branch = property(_get_branch, doc=""" 602 Branch which contains this namespace object. 603 604 :type: `Branch` 605 """)
606 607
608 -class VersionItem(BranchItem):
609 610 """Base class for Revision. 611 612 VersionItem provides features for the aspect of Revision which 613 relate to its containment within other archive objects. 614 """ 615
616 - def __init__(self, archive, version, patchlevel):
617 BranchItem.__init__(self, archive, "%s--%s" % (version, patchlevel)) 618 self._version, self._patchlevel = version, patchlevel
619
620 - def get_branch(self):
621 deprecated_callable(self.get_branch, (type(self), 'branch')) 622 return self.branch
623
624 - def _get_branch(self):
625 assert isinstance(self, Revision) 626 package = '--'.join(self._version.split('--')[0:-1]) 627 return Branch(_unsafe((self._archive, package)))
628 629 branch = property(_get_branch, doc=BranchItem.branch.__doc__) 630
631 - def get_version(self):
632 """Deprecated. 633 634 Version which contains this revision. 635 636 :rtype: `Version` 637 :see: `VersionItem.version` 638 """ 639 deprecated_callable(self.get_version, (type(self), 'version')) 640 return self.version
641
642 - def _get_version(self):
643 return Version(_unsafe((self._archive, self._version)))
644 645 version = property(_get_version, doc=""" 646 Version which contains this revision. 647 648 :type: `Version` 649 """) 650
651 - def get_patchlevel(self):
652 """Deprecated. 653 654 Patch-level part of this object's name. 655 656 :rtype: str 657 :see: `VersionItem.patchlevel` 658 """ 659 deprecated_callable(self.get_patchlevel, (type(self), 'patchlevel')) 660 return self.patchlevel
661
662 - def _get_patchlevel(self):
663 return self._patchlevel
664 665 patchlevel = property(_get_patchlevel, doc=""" 666 Patch-level part of this object's name. 667 668 :type: str 669 """)
670 671 672 ### Mixins for misc. features of archive objects ### 673 674 public('Setupable', 'Package') 675
676 -class Setupable(ArchiveItem):
677 678 """Base class for container archive objects.""" 679
680 - def setup(self):
681 """Deprecated. 682 683 PyBaz 1.1 and later do archive-setup implicitly. 684 """ 685 deprecated_callable(self.setup, because= 686 'pybaz 1.1 and later do archive-setup implicitly.')
687 688
689 -class Package(Setupable, RevisionIterable):
690 691 """Base class for ordered container archive objects.""" 692
693 - def as_revision(self):
694 """Deprecated. 695 696 Latest revision in this package. 697 698 :rtype: `Revision` 699 :precondition: `self.exists()` returns ``True`` 700 :precondition: `self.iter_revisions()` yields at least one object. 701 :raises StopIteration: this package contains no revision 702 :see: `Package.latest_revision` 703 """ 704 deprecated_callable(self.as_revision, self.latest_revision) 705 return self.iter_revisions(reverse=True).next()
706
707 - def latest_revision(self):
708 """Latest revision in this package. 709 710 :rtype: `Revision` 711 :precondition: `self.exists()` returns ``True`` 712 :precondition: `self.iter_revisions()` yields at least one object. 713 :raises ValueError: the archive is not registered, or this 714 package does not exist, or it contains no revision. 715 """ 716 try: 717 return self.iter_revisions(reverse=True).next() 718 except errors.ExecProblem: 719 try: 720 self_exists = self.exists() 721 except errors.ArchiveNotRegistered: 722 raise ValueError('Archive is not registered: %s' 723 % self.archive) 724 if not self_exists: 725 raise ValueError('Package does not exist: %s' % self) 726 raise 727 except StopIteration: 728 raise ValueError('Package contains no revision: %s' % self)
729 730 731 ### Archive classes ### 732 733 public('Archive', 'Category', 'Branch', 'Version', 'Revision') 734 735
736 -class Archive(CategoryIterable):
737 738 """Arch archive namespace object. 739 740 In the Arch revision control system, archives are the units of 741 storage. They store revisions organized in categories, branches 742 and versions, and are associated to a `name` and a `location`. 743 744 :see: `Category`, `Branch`, `Version`, `Revision` 745 """ 746
747 - def __init__(self, name):
748 """Create an archive object from its registered name. 749 750 :param name: archive name, like "jdoe@example.com--2003" 751 :type name: str 752 :raise errors.NamespaceError: invalid archive name. 753 """ 754 if isinstance(name, Archive): 755 name = name.__name 756 if isinstance(name, _unsafe): 757 assert len(name) == 1 758 name = name[0] 759 elif not NameParser.is_archive_name(name): 760 raise errors.NamespaceError(name, 'archive name') 761 self.__name = name 762 self._impl = _archive_impl()
763
764 - def get_name(self):
765 """Deprecated. 766 767 Logical name of the archive. 768 769 :rtype: str 770 :see: `Archive.name` 771 """ 772 deprecated_callable(Archive.get_name, (Archive, 'name')) 773 return self.name
774
775 - def _get_name(self):
776 return self.__name
777 778 name = property(_get_name, doc=""" 779 Logical name of the archive. 780 781 :type: str 782 """) 783
784 - def get_fullname(self):
785 deprecated_callable(Archive.get_fullname, (Archive, 'fullname')) 786 return self.name
787 788 fullname = property(_get_name, doc=NamespaceObject.fullname.__doc__) 789
790 - def get_location(self):
791 """Deprecated. 792 793 URI of the archive, specifies location and access method. 794 795 :rtype: str 796 :see: `Archive.all_locations` 797 """ 798 deprecated_callable(Archive.get_location, Archive.all_locations) 799 return self.location
800
801 - def _get_location(self):
802 deprecated_callable((Archive, 'location'), Archive.all_locations) 803 status, location = backend.status_one_cmd( 804 ('whereis-archive', self.name), (0,1)) 805 if status == 0: 806 return location 807 else: 808 return None
809 810 location = property(_get_location, doc=""" 811 Deprecated. 812 813 For example 'http://ddaa.net/arch/2004', or 814 'sftp://user@sourcecontrol.net/public_html/2004'. 815 816 :see: `Archive.all_locations` 817 :type: str 818 """) 819
820 - def all_locations(self):
821 """All registered locations for this archive. 822 823 :rtype: list of `ArchiveLocation` 824 """ 825 args = ['whereis-archive', '--all-locations', self.name] 826 try: 827 raw_locations = [factory.ArchiveLocation(url) 828 for url in backend.sequence_cmd(args)] 829 except errors.ExecProblem: 830 if not self.is_registered(): 831 return [] 832 else: 833 raise 834 # may report duplicate locations with old-style registrations 835 unique_locations = [] 836 for location in raw_locations: 837 if location not in unique_locations: 838 unique_locations.append(location) 839 return unique_locations
840
841 - def _meta_info(self, key):
842 archive = self.name 843 args = self._impl.archive_meta_info_args(self.name, key) 844 status, output = backend.status_one_cmd(args, expected=(0, 1)) 845 if status != 0: 846 raise KeyError( 847 "No such meta-info in archive %s: %s" % (archive, key)) 848 return output
849
850 - def _has_meta_info(self, key):
851 try: 852 self._meta_info(key) 853 return True 854 except KeyError: 855 return False
856
857 - def get_is_mirror(self):
858 """Deprecated. 859 860 Is this archive registration a mirror? 861 862 :see: `ArchiveLocation._meta_info_present` 863 :rtype: bool 864 """ 865 deprecated_callable(Archive.get_is_mirror, 866 ArchiveLocation._meta_info_present) 867 return self.is_mirror
868
869 - def _get_is_mirror(self):
870 deprecated_callable((Archive, 'is_mirror'), 871 ArchiveLocation._meta_info_present) 872 return self._has_meta_info('mirror')
873 874 is_mirror = property(_get_is_mirror, doc=""" 875 Deprecated. 876 877 :see: `ArchiveLocation._meta_info_present` 878 :type: bool 879 """) 880
881 - def get_official_name(self):
882 """Deprecated. 883 884 Official archive name of this archive registration. 885 886 :see: `ArchiveLocation._meta_info_present` 887 :rtype: str 888 """ 889 deprecated_callable(Archive.get_official_name, 890 ArchiveLocation._meta_info_present) 891 return self.official_name
892
893 - def _get_official_name(self):
894 deprecated_callable((Archive, 'official_name'), 895 ArchiveLocation._meta_info_present) 896 return self._meta_info('name')
897 898 official_name = property(_get_official_name, doc=""" 899 Deprecated. 900 901 :see: `ArchiveLocation._meta_info_present` 902 :type: str 903 """) 904
905 - def get_is_signed(self):
906 """Deprecated. 907 908 Is the archive GPG-signed? 909 910 :see: `ArchiveLocation._meta_info_present` 911 :rtype: bool 912 """ 913 deprecated_callable(Archive.get_is_signed, 914 ArchiveLocation._meta_info_present) 915 return self.is_signed
916
917 - def _get_is_signed(self):
918 deprecated_callable((Archive, 'is_signed'), 919 ArchiveLocation._meta_info_present) 920 return self._has_meta_info('signed-archive')
921 922 is_signed = property(_get_is_signed, doc=""" 923 Deprecated. 924 925 :see: `ArchiveLocation._meta_info_present` 926 :type: bool 927 """) 928
929 - def get_has_listings(self):
930 """Deprecated. 931 932 Does the archive provide .listing file for http access? 933 934 :see: `ArchiveLocation._meta_info_present` 935 :rtype: bool 936 """ 937 deprecated_callable(Archive.get_has_listings, 938 ArchiveLocation._meta_info_present) 939 return self.has_listings
940
941 - def _get_has_listings(self):
942 deprecated_callable((Archive, 'has_listings'), 943 ArchiveLocation._meta_info_present) 944 return self._has_meta_info('http-blows')
945 946 has_listings = property(_get_has_listings, doc=""" 947 Deprecated. 948 949 :see: `ArchiveLocation._meta_info_present` 950 :type: bool 951 """) 952
953 - def _get_version_string(self):
954 deprecated_callable((Archive, 'version_string'), 955 ArchiveLocation._version_string) 956 location = '/'.join((self.location, '.archive-version')) 957 import urllib 958 version_file = urllib.urlopen(location) 959 try: 960 ret = version_file.read() 961 finally: 962 version_file.close() 963 return ret.rstrip()
964 965 version_string = property(_get_version_string, doc=""" 966 Deprecated. 967 968 Contents of the ``.archive-version`` file at the root of the archive. 969 970 :see: `ArchiveLocation._version_string` 971 :type: str 972 """) 973
974 - def _is_tla_format(self):
975 expected = ('Hackerlab arch archive directory, format version 2.', 976 'Hackerlab arch archive directory, format version 1.') 977 return self.version_string in expected
978
979 - def _is_baz_format(self):
980 expected = 'Bazaar archive format 1 0' 981 return self.version_string == expected
982
983 - def __getitem__(self, category):
984 """Instanciate a Category belonging to this archive. 985 986 :param category: unqualified category name 987 :type category: str 988 :rtype: `Category` 989 """ 990 if not NameParser.is_category_name(category): 991 raise errors.NamespaceError(category, 'unqualified category name') 992 return Category(_unsafe((self.name, category)))
993
994 - def exists(self):
995 if self.is_registered(): 996 return True 997 else: 998 raise errors.ArchiveNotRegistered(self.name)
999
1000 - def is_registered(self):
1001 """Is this archive registered? 1002 1003 :return: Whether the location associated to this registration name is 1004 known. 1005 :rtype: bool 1006 :see: `register_archive`, `Archive.unregister` 1007 """ 1008 for archive in iter_archives(): 1009 if archive.name == self.name: 1010 return True 1011 else: 1012 return False
1013
1014 - def iter_categories(self):
1015 args = self._impl.iter_categories_args(self.name) 1016 for c in backend.sequence_cmd(args): 1017 yield Category(_unsafe((self.name, c)))
1018
1019 - def iter_location_versions(self, location):
1020 """Versions present in the specified archive location. 1021 1022 :warning: This is a temporary facility that does no sanity checking. It 1023 will be removed shortly after bound namespace objects are properly 1024 implemented. 1025 """ 1026 for category in backend.sequence_cmd(['categories', location.url]): 1027 category_url = location.url + '/' + category 1028 for branch in backend.sequence_cmd(['branches', category_url]): 1029 branch_url = location.url + '/' + branch 1030 for version in backend.sequence_cmd(['versions', branch_url]): 1031 yield Version(_unsafe((self.name, version)))
1032
1033 - def iter_location_revisions(self, location):
1034 """Revisions present in the specified archive location. 1035 1036 :warning: This is a temporary facility that does no sanity checking. It 1037 will be removed shortly after bound namespace objects are properly 1038 implemented. 1039 """ 1040 for version in self.iter_location_versions(location): 1041 for revision in version.iter_location_revisions(location): 1042 yield revision
1043
1044 - def iter_library_categories(self):
1045 for c in _arch.library_categories(self.name): 1046 yield Category(_unsafe((self.name, c)))
1047
1048 - def get_categories(self):
1049 """Deprecated. 1050 1051 Categories in this archive. 1052 1053 :rtype: tuple of `Category` 1054 :see: `iter_categories` 1055 """ 1056 deprecated_callable(Archive.get_categories, Archive.iter_categories) 1057 return tuple(self.iter_categories())
1058
1059 - def _get_categories(self):
1060 deprecated_callable((Archive, 'categories'), Archive.iter_categories) 1061 return tuple(self.iter_categories())
1062 1063 categories = property(_get_categories, doc=""" 1064 Deprecated. 1065 1066 Categories in this archive. 1067 1068 :type: tuple of `Category` 1069 :see: `iter_categories` 1070 """) 1071
1072 - def get_library_categories(self):
1073 """Deprecated. 1074 1075 Categories in this archive present in the library. 1076 1077 :rtype: tuple of `Category` 1078 :see: `iter_library_categories` 1079 """ 1080 deprecated_callable(Archive.get_library_categories, 1081 Archive.iter_library_categories) 1082 return tuple(self.iter_library_categories())
1083
1084 - def _get_library_categories(self):
1085 deprecated_callable((Archive, 'library_categories'), 1086 Archive.iter_library_categories) 1087 return tuple(self.iter_library_categories())
1088 1089 library_categories = property(_get_library_categories, doc=""" 1090 Deprecated. 1091 1092 Categories in this archive present in the library. 1093 1094 :type; tuple of `Category` 1095 :see: `iter_library_categories` 1096 """) 1097
1098 - def unregister(self):
1099 """Unregister this archive. 1100 1101 :precondition: `self.is_registered()` 1102 :postcondition: not `self.is_registered()` 1103 :see: `register_archive` 1104 """ 1105 backend.null_cmd(('register-archive', '--delete', self.name))
1106
1107 - def make_mirror(self, name, location, signed=False, listing=False, 1108 tla=False):
1109 """Deprecated. 1110 1111 :see: `ArchiveLocation.create_mirror` 1112 1113 :param name: name of the new mirror (for example 1114 'david@allouche.net--2003b-MIRROR'). 1115 :type name: str 1116 :param location: writeable URI were to create the archive mirror. 1117 :type location: str 1118 :param signed: create GPG signatures for the mirror contents 1119 :type signed: bool 1120 :param listing: maintains ''.listing'' files to enable HTTP access. 1121 :type listing: bool 1122 :param tla: create a tla archive instead of a baz archive. 1123 :type tla: bool 1124 1125 :return: object for the newly created archive mirror. 1126 :rtype: `Archive` 1127 1128 :precondition: `self.is_registered()` 1129 :precondition: ``name`` is not a registered archive name 1130 :precondition: ``location`` does not exist and can be created 1131 :postcondition: Archive(name).is_registered() 1132 1133 :raise errors.NamespaceError: ``name`` is not a valid archive name. 1134 """ 1135 deprecated_callable(Archive.make_mirror, ArchiveLocation.create_mirror) 1136 a = Archive(name) # check archive name validity first 1137 official_name = self.official_name 1138 self._impl.make_mirror(official_name, name, location, 1139 signed, listing, tla) 1140 return a
1141
1142 - def mirror(self, limit=None, fromto=None, 1143 no_cached=False, cached_tags=False):
1144 """Deprecated. 1145 1146 :see: `ArchiveLocation.make_mirrorer` 1147 1148 :param limit: restrict mirrorring to those archive items. All items 1149 must belong to this archive. 1150 :type limit: iterable of at least one ArchiveItem or str 1151 1152 :param fromto: update the mirror specified by the second item with the 1153 contents of the archive specified by the first item. 1154 :type fromto: sequence of exactly two Archive or str. 1155 1156 :precondition: If ``fromto`` is provided, both items must be registered 1157 archives names whose official name is this archive. 1158 1159 :param no_cached: do not copy cached revisions. 1160 :type no_cached: bool 1161 1162 :param cached_tags: copy only cachedrevs for tags to other archives. 1163 :type cached_tags: bool 1164 """ 1165 deprecated_callable(Archive.mirror, ArchiveLocation.make_mirrorer) 1166 assert no_cached + cached_tags < 2 1167 assert limit is None or len(limit) > 0 1168 if fromto is None: 1169 from_, to = self.name, None 1170 else: 1171 from_, to = self._impl.mirror_fromto(fromto) 1172 args = ['archive-mirror'] 1173 if no_cached: 1174 args.append('--no-cached') 1175 if cached_tags: 1176 args.append('--cached-tags') 1177 args.append(from_) 1178 if to is not None: 1179 args.append(to) 1180 if limit is not None: 1181 args.extend([self._archive_limit_param(L) for L in limit]) 1182 backend.null_cmd(args)
1183
1184 - def _archive_limit_param(self, item):
1185 """Internal argument checking utility""" 1186 if isinstance(item, ArchiveItem): 1187 if self != item.archive: 1188 message = "%s is not an item of %s" % (item.fullname, self.name) 1189 raise ValueError(message) 1190 return item.nonarch 1191 else: 1192 p = NameParser(item) 1193 if not p.has_category(): 1194 raise errors.NamespaceError(item, 'archive limit') 1195 if not p.has_archive(): 1196 return item 1197 elif self.name != p.get_archive(): 1198 message = "%s is not an item of %s" % (item.fullname, self.name) 1199 raise ValueError(message) 1200 else: 1201 return p.get_nonarch()
1202 1203
1204 -def _archive_impl():
1205 if backend.version.release < (1, 3, 0): 1206 return _Archive_Baz_1_0 1207 else: 1208 return _Archive_Baz_1_3
1209 1210
1211 -class _Archive_Baz_1_0(object):
1212
1213 - def archive_meta_info_args(archive, key):
1214 return ('archive-meta-info', '--archive', archive, key)
1215 1216 archive_meta_info_args = staticmethod(archive_meta_info_args) 1217
1218 - def register_archive(name, location):
1219 args = ['register-archive'] 1220 if name is not None: 1221 args.append(name) 1222 args.append(location) 1223 backend.null_cmd(args) 1224 if name is not None: 1225 return Archive(_unsafe((name,))) 1226 else: 1227 for archive in iter_archives(): 1228 if archive.location == location: 1229 return archive 1230 raise AssertionError, 'not reached'
1231 1232 register_archive = staticmethod(register_archive) 1233
1234 - def make_mirror(master, name, location, signed, listing, tla):
1235 args = ['make-archive', '--mirror', master] 1236 if signed: 1237 args.append('--signed') 1238 if listing: 1239 args.append('--listing') 1240 if tla: 1241 args.append('--tla') 1242 args.extend((name, location)) 1243 backend.null_cmd(args)
1244 1245 make_mirror = staticmethod(make_mirror) 1246
1247 - def iter_categories_args(name):
1248 return ('categories', '--archive', name)
1249 1250 iter_categories_args = staticmethod(iter_categories_args) 1251
1252 - def _registered_archive_param(archive):
1253 """Internal argument checking utility""" 1254 archive = _archive_param(archive) 1255 if not Archive(_unsafe((archive,))).is_registered(): 1256 raise errors.ArchiveNotRegistered(archive) 1257 return archive
1258 1259 _registered_archive_param = staticmethod(_registered_archive_param) 1260
1261 - def mirror_fromto(fromto):
1262 return [_Archive_Baz_1_0._registered_archive_param(A) for A in fromto]
1263 1264 mirror_fromto = staticmethod(mirror_fromto)
1265 1266
1267 -class _Archive_Baz_1_3(object):
1268
1269 - def archive_meta_info_args(archive, key):
1270 return ('archive-meta-info', archive + '/' + key)
1271 1272 archive_meta_info_args = staticmethod(archive_meta_info_args) 1273
1274 - def _is_url(location):
1275 match = re.match('[^/]+://', location) 1276 return match is not None
1277 1278 _is_url = staticmethod(_is_url) 1279
1280 - def _absolutize(cls, location):
1281 if cls._is_url(location): 1282 return location 1283 return os.path.abspath(location)
1284 1285 _absolutize = classmethod(_absolutize) 1286
1287 - def _canonical_location_unregister(official_name, absolute_location):
1288 # Figure out the full location by examining the registration and 1289 # unregister the mirror location in the process. The location must be 1290 # unregistered because we will create an old-style registration and it 1291 # should not be redundant. 1292 args = ['whereis-archive', '--all-locations', official_name] 1293 import sets 1294 locations_with = sets.Set(backend.sequence_cmd(args)) 1295 args = ['register-archive', '--delete', absolute_location] 1296 backend.null_cmd(args) 1297 if len(locations_with) == 1: 1298 difference = locations_with 1299 else: 1300 args = ['whereis-archive', '--all-locations', official_name] 1301 locations_without = sets.Set(backend.sequence_cmd(args)) 1302 difference = locations_with - locations_without 1303 assert len(difference) == 1 1304 canonical_location = difference.pop().rstrip('\n') 1305 return canonical_location
1306 1307 _canonical_location_unregister = staticmethod( 1308 _canonical_location_unregister) 1309
1310 - def _register_name(name, canonical_location):
1311 home = DirName(os.environ['HOME']) 1312 locations_dir = home/'.arch-params'/'=locations' 1313 if not os.path.isdir(locations_dir): 1314 os.mkdir(locations_dir) 1315 print >> open(locations_dir/name, 'w'), canonical_location
1316 1317 _register_name = staticmethod(_register_name) 1318
1319 - def _location_meta_info(location, label):
1320 args = ['archive-meta-info', location + '/' + label] 1321 return backend.text_cmd(args).strip()
1322 1323 _location_meta_info = staticmethod(_location_meta_info) 1324
1325 - def register_archive(cls, name, location):
1326 absolute_location = cls._absolutize(location) 1327 backend.null_cmd(['register-archive', location]) 1328 official_name = cls._location_meta_info(absolute_location, 'name') 1329 if name is None: 1330 return Archive(_unsafe((official_name,))) 1331 if name != official_name: 1332 # given name is not official name, create registered name 1333 canonical_loc = cls._canonical_location_unregister( 1334 official_name, absolute_location) 1335 cls._register_name(name, canonical_loc) 1336 return Archive(_unsafe((name,)))
1337 1338 register_archive = classmethod(register_archive) 1339
1340 - def make_mirror(cls, master, name, location, signed, listing, tla):
1341 absolute_location = cls._absolutize(location) 1342 _Archive_Baz_1_0.make_mirror(master, name, absolute_location, 1343 signed, listing, tla) 1344 canonical_location = cls._canonical_location_unregister( 1345 master, absolute_location) 1346 cls._register_name(name, canonical_location)
1347 1348 make_mirror = classmethod(make_mirror) 1349
1350 - def iter_categories_args(name):
1351 return ('categories', name)
1352 1353 iter_categories_args = staticmethod(iter_categories_args) 1354
1355 - def mirror_fromto(fromto):
1356 return [Archive(_unsafe((A,))).location 1357 for A in _Archive_Baz_1_0.mirror_fromto(fromto)]
1358 1359 mirror_fromto = staticmethod(mirror_fromto)
1360 1361
1362 -class Category(Setupable, BranchIterable):
1363 1364 """Arch category namespace object. 1365 1366 :see: `Archive`, `Branch`, `Version`, `Revision` 1367 """ 1368
1369 - def __init__(self, name):
1370 """Create a category object from its name. 1371 1372 :param name: fully-qualified category name, like 1373 "jdoe@example.com/frob" 1374 :type name: str 1375 :raise errors.NamespaceError: ``name`` is not a valid category name. 1376 """ 1377 if isinstance(name, Category): 1378 name = (name._archive, name._nonarch) 1379 elif not isinstance(name, _unsafe): 1380 p = NameParser(name) 1381 if not p.has_archive() or not p.is_category(): 1382 raise errors.NamespaceError(name, 'fully-qualified category') 1383 name = (p.get_archive(), p.get_nonarch()) 1384 ArchiveItem.__init__(self, *name)
1385
1386 - def __getitem__(self, b):
1387 """Instanciate a branch belonging to this category. 1388 1389 For example ``Category('jdoe@example.com/frob')['devel']`` is 1390 equivalent to ``Branch('jdoe@example.com/frob--devel')``. 1391 1392 :param b: branch id. 1393 :type b: str 1394 :rtype: `Category` 1395 1396 :raise NamespaceError: argument is not a valid branch id. 1397 """ 1398 if b is not None and not NameParser.is_branch_name(b): 1399 raise errors.NamespaceError(b, 'unqualified branch name') 1400 if b is None: # nameless branch 1401 return Branch(_unsafe((self._archive, self._nonarch))) 1402 else: # named branch 1403 return Branch(_unsafe((self._archive, 1404 "%s--%s" % (self._nonarch, b))))
1405
1406 - def exists(self):
1407 if not self.archive.is_registered(): 1408 raise errors.ArchiveNotRegistered(self.archive) 1409 for category in self.archive.iter_categories(): 1410 if category.fullname == self.fullname: 1411 return True 1412 else: 1413 return False
1414
1415 - def iter_branches(self):
1416 for b in backend.sequence_cmd(('branches', self.fullname)): 1417 yield Branch(_unsafe((self._archive, b)))
1418
1419 - def iter_library_branches(self):
1420 for b in _arch.library_branches(self.fullname): 1421 yield Branch(_unsafe((self._archive, b)))
1422
1423 - def get_branches(self):
1424 """Deprecated. 1425 1426 Branches in this category. 1427 1428 :rtype: tuple of `Branch` 1429 :see: `iter_branches` 1430 """ 1431 deprecated_callable(self.get_branches, self.iter_branches) 1432 return tuple(self.iter_branches())
1433
1434 - def _get_branches(self):
1435 deprecated_callable((type(self), 'branches'), self.iter_branches) 1436 return tuple(self.iter_branches())
1437 1438 branches = property(_get_branches, doc=""" 1439 Deprecated. 1440 1441 Branches in this category. 1442 1443 :type: tuple of `Branch` 1444 :see: `Category.iter_branches` 1445 """) 1446
1447 - def get_library_branches(self):
1448 """Deprecated. 1449 1450 Branches in this category present in the library. 1451 1452 :rtype: tuple of `Branch` 1453 :see: `iter_library_branches` 1454 """ 1455 deprecated_callable(self.get_library_branches, 1456 self.iter_library_branches) 1457 return tuple(self.iter_library_branches())
1458
1459 - def _get_library_branches(self):
1460 deprecated_callable((Category, 'library_branches'), 1461 self.iter_library_branches) 1462 return tuple(self.iter_library_branches())
1463 1464 library_branches = property(_get_library_branches, doc=""" 1465 Deprecated. 1466 1467 Branches in this category present in the library. 1468 1469 :type: tuple of `Branch` 1470 :see: `Category.iter_branches` 1471 """)
1472 1473
1474 -class Branch(CategoryItem, Package, VersionIterable):
1475 1476 """Arch branch namespace object. 1477 1478 :see: `Archive`, `Category`, `Version`, `Revision` 1479 """ 1480
1481 - def __init__(self, name):
1482 """Create a Branch object from its name. 1483 1484 :param name: fully-qualified branch name, like 1485 "jdoe@example.com--2004/frob--devo" or 1486 "jdoe@example.com--2004/frob". 1487 :type name: str 1488 :raise errors.NamespaceError: ``name`` is not a valid branch name. 1489 """ 1490 if isinstance(name, Branch): 1491 name = (name._archive, name._nonarch) 1492 elif not isinstance(name, _unsafe): 1493 p = NameParser(name) 1494 if not p.has_archive() or not p.is_package(): 1495 raise errors.NamespaceError(name, 'fully-qualified branch') 1496 assert p.is_package() 1497 name = (p.get_archive(), p.get_nonarch()) 1498 CategoryItem.__init__(self, *name)
1499
1500 - def __getitem__(self, v):
1501 """Instanciate a version belonging to this branch. 1502 1503 For example ``Branch('jdoe@example.com/frob--devel')['0']`` is 1504 equivalent to ``Branch('jdoe@example.com/frob--devel--0')``. 1505 1506 :param v: branch id. 1507 :type v: str 1508 :rtype: `Version` 1509 1510 :raise NamespaceError: argument is not a valid version id. 1511 """ 1512 if not NameParser.is_version_id(v): 1513 raise errors.NamespaceError(v, 'version id') 1514 return Version(_unsafe((self._archive, "%s--%s" % (self._nonarch, v))))
1515
1516 - def exists(self):
1517 if not self.category.exists(): 1518 return False 1519 for branch in self.category.iter_branches(): 1520 if branch.fullname == self.fullname: 1521 return True 1522 else: 1523 return False
1524
1525 - def iter_versions(self, reverse=False):
1526 args = ['versions'] 1527 if reverse: 1528 args.append('--reverse') 1529 args.append(self.fullname) 1530 for v in backend.sequence_cmd(args): 1531 yield Version(_unsafe((self._archive, v)))
1532
1533 - def iter_library_versions(self, reverse=False):
1534 for v in _arch.library_versions(self.fullname, reverse): 1535 yield Version(_unsafe((self._archive, v)))
1536
1537 - def get_versions(self, reverse=False):
1538 """Deprecated. 1539 1540 Versions in this branch. 1541 1542 :rtype: tuple of `Version` 1543 :see: `iter_versions` 1544 """ 1545 deprecated_callable(self.get_versions, self.iter_versions) 1546 return tuple(self.iter_versions(reverse))
1547
1548 - def _get_versions(self):
1549 deprecated_callable((Branch, 'versions'), self.iter_versions) 1550 return tuple(self.iter_versions(reverse=False))
1551 1552 versions = property(_get_versions, doc=""" 1553 Deprecated. 1554 1555 Versions in this branch. 1556 1557 :type: tuple of `Version` 1558 :see: `iter_versions` 1559 """) 1560
1561 - def get_library_versions(self, reverse=False):
1562 """Deprecated. 1563 1564 Versions in this branch present in the library. 1565 1566 :rtype: tuple of `Version` 1567 :see: `iter_library_versions` 1568 """ 1569 deprecated_callable(self.get_library_versions, 1570 self.iter_library_versions) 1571 return tuple(self.iter_library_versions(reverse))
1572
1573 - def _get_library_versions(self):
1574 deprecated_callable((Branch, 'library_versions'), 1575 self.iter_library_versions) 1576 return tuple(self.iter_library_versions(reverse=False))
1577 1578 library_versions = property(_get_library_versions, doc=""" 1579 Deprecated. 1580 1581 Versions in this branch present in the library. 1582 1583 :type: tuple of `Version` 1584 :see: `iter_library_versions` 1585 """) 1586
1587 - def as_version(self):
1588 """Deprecated. 1589 1590 Latest version in this branch. 1591 1592 :rtype: `Version` 1593 :precondition: `self.exists()` returns ``True`` 1594 :precondition: `self.iter_versions` yields at least one object. 1595 :raise IndexError: this branch is empty. 1596 :see: `latest_version` 1597 """ 1598 deprecated_callable(self.as_version, self.latest_version) 1599 return list(self.iter_versions(reverse=True))[0]
1600
1601 - def latest_version(self):
1602 """Latest version in this branch. 1603 1604 :rtype: `Version` 1605 :precondition: `self.exists()` returns ``True`` 1606 :precondition: `self.iter_versions` yields at least one object. 1607 :raise ValueError: the archive is not registered, or this branch does 1608 not exist, or it contains no version. 1609 """ 1610 try: 1611 return self.iter_versions(reverse=True).next() 1612 except errors.ExecProblem: 1613 try: 1614 self_exists = self.exists() 1615 except errors.ArchiveNotRegistered: 1616 raise ValueError('Archive is not registered: %s' 1617 % self.archive) 1618 if not self_exists: 1619 raise ValueError('Branch does not exist: %s' % self) 1620 raise 1621 except StopIteration: 1622 raise ValueError('Branch contains no version: %s' % self)
1623 1624 1625
1626 -class Version(BranchItem, Package, RevisionIterable):
1627 1628 """Arch version namespace object. 1629 1630 :see: `Archive`, `Category`, `Branch`, `Revision` 1631 """ 1632
1633 - def __init__(self, name):
1634 """Create a Version object from its name. 1635 1636 :param name: fully-qualified version name, like 1637 "jdoe@example.com--2004/frob--devo--1.2". 1638 :type name: str 1639 1640 :note: Nameless branches have no "branch" part in their name. 1641 """ 1642 if isinstance(name, Version): 1643 name = (name._archive, name._nonarch) 1644 elif not isinstance(name, _unsafe): 1645 p = NameParser(name) 1646 if not p.has_archive() or not p.is_version(): 1647 raise errors.NamespaceError(name, 'fully-qualified version') 1648 name = (p.get_archive(), p.get_nonarch()) 1649 BranchItem.__init__(self, *name) 1650 self._impl = _Namespace_Baz_1_0
1651
1652 - def __getitem__(self, idx):
1653 """Instanciate a revision belonging to this version. 1654 1655 Given a string, instanciate the revision of this version with the given 1656 patchlevel. For example 1657 ``Version('jdoe@example.com/frob--devel--0')['patch-1']`` is equivalent 1658 to ``Revision('jdoe@example.com/frob--devel--0--patch-1')``. 1659 1660 Given an integer, instanciate the *existing* revision of this version 1661 with the given index. For example, if ``version`` contains at least one 1662 revision, ``version[0]`` is equivalent to ``version['base-0']``, and 1663 ``version[-1]`` is equivalent to ``version.latest_revision()``. 1664 1665 :param idx: patch level, or revision number 1666 :type idx: str, int 1667 :rtype: `Revision` 1668 1669 :raise NamespaceError: argument is a string not a valid version 1670 patchlevel. 1671 :raise ValueError: argument is an integer and the version contains no 1672 revision with that index. 1673 """ 1674 if isinstance(idx, str): 1675 return self._getitem_str(idx) 1676 if isinstance(idx, int): 1677 return self._getitem_int(idx) 1678 else: 1679 raise TypeError( 1680 'Version indices must be str or int, but was %r' % (idx,))
1681
1682 - def _getitem_str(self, lvl):
1683 if not NameParser.is_patchlevel(lvl): 1684 raise errors.NamespaceError(lvl, 'unqualified patchlevel') 1685 return Revision(_unsafe((self._archive, self._nonarch, lvl)))
1686
1687 - def _getitem_int(self, idx):
1688 if idx < 0: 1689 reverse = True 1690 decounter = - idx - 1 1691 else: 1692 reverse = False 1693 decounter = idx 1694 assert decounter >= 0 1695 rev_iter = self.iter_revisions(reverse) 1696 try: 1697 while decounter > 0: 1698 decounter -= 1 1699 rev_iter.next() 1700 return rev_iter.next() 1701 except StopIteration: 1702 raise IndexError( 1703 'Version index out of bounds: %r[%d]' % (self, idx))
1704
1705 - def as_version(self):
1706 """Deprecated. 1707 1708 This version. 1709 1710 :rtype: `Version` 1711 """ 1712 deprecated_callable(self.as_version, because='Foolish consistency.') 1713 return self
1714
1715 - def exists(self):
1716 if not self.branch.exists(): 1717 return False 1718 for version in self.branch.iter_versions(): 1719 if version.fullname == self.fullname: 1720 return True 1721 else: 1722 return False
1723
1724 - def get(self, dir, link=False):
1725 """Construct a project tree for this version. 1726 1727 Extract the latest revision for this version from the archive. That is 1728 a shortcut for ``version.latest_revision.get(dir)``. It can be 1729 susceptible to race conditions when a concurrent transaction occurs on 1730 the same version, yielding a latter revision that what you may have 1731 meant. 1732 1733 :param dir: path of the project tree to create. Must not 1734 already exist. 1735 :type dir: str 1736 :param link: hardlink files to revision library instead of copying 1737 :type link: bool 1738 :return: newly created project tree. 1739 :rtype: `WorkingTree` 1740 """ 1741 args = self._impl.get_args(self, dir, link) 1742 backend.null_cmd(args) 1743 return WorkingTree(dir)
1744
1745 - def iter_revisions(self, reverse=False):
1746 args = ['revisions'] 1747 if reverse: 1748 args.append('--reverse') 1749 args.append(self.fullname) 1750 for lvl in backend.sequence_cmd(args): 1751 yield Revision(_unsafe((self._archive, self._nonarch, lvl)))
1752
1753 - def iter_location_revisions(self, location):
1754 """Revisions present in this version at specified archive location. 1755 1756 :warning: This is a temporary facility that does no sanity checking. It 1757 will be removed shortly after bound namespace objects are properly 1758 implemented. 1759 """ 1760 version_url = location.url + '/' + self.nonarch 1761 for level in backend.sequence_cmd(['revisions', version_url]): 1762 yield Revision(_unsafe((self._archive, self._nonarch, level)))
1763
1764 - def iter_library_revisions(self, reverse=False):
1765 for lvl in _arch.library_revisions(self.fullname, reverse): 1766 yield Revision(_unsafe((self._archive, self._nonarch, lvl)))
1767
1768 - def get_revisions(self, reverse=False):
1769 """Deprecated. 1770 1771 Revisions in this version. 1772 1773 :rtype: tuple of `Revision` 1774 :see: `iter_revisions` 1775 """ 1776 deprecated_callable(self.get_revisions, self.iter_revisions) 1777 return tuple(self.iter_revisions(reverse))
1778
1779 - def _get_revisions(self):
1780 deprecated_callable((Version, 'revisions'), self.iter_revisions) 1781 return tuple(self.iter_revisions(reverse=False))
1782 1783 revisions = property(_get_revisions, doc=""" 1784 Deprecated. 1785 1786 Revisions in this version. 1787 1788 :type: tuple of `Revision` 1789 :see: `iter_revisions` 1790 """) 1791
1792 - def get_library_revisions(self, reverse=False):
1793 """Deprecated. 1794 1795 Revisions in this version present in the library. 1796 1797 :rtype: tuple of `Revision` 1798 :see: `iter_library_revisions` 1799 """ 1800 deprecated_callable(self.get_library_revisions, 1801 self.iter_library_revisions) 1802 return tuple(self.iter_library_revisions(reverse))
1803
1804 - def _get_library_revisions(self):
1805 deprecated_callable((Version, 'library_revisions'), 1806 self.iter_library_revisions) 1807 return tuple(self.iter_library_revisions(reverse=False))
1808 1809 library_revisions = property(_get_library_revisions, doc=""" 1810 Deprecated. 1811 1812 Revisions in this version present in the library. 1813 1814 :type: tuple of `Revision` 1815 :see: `iter_library_revisions` 1816 """) 1817
1818 - def iter_merges(self, other=None, reverse=False, metoo=True):
1819 """Iterate over merge points in this version. 1820 1821 This method is mostly useful to save multiple invocations of 1822 the command-line tool and multiple connection to a remote 1823 archives when building an ancestry graph. Ideally, it would 1824 not be present and the desired merge graph traversal would be 1825 done using the new_patches and merges properties of Patchlog 1826 objects. 1827 1828 :param other: list merges with that version. 1829 :type other: `Version` 1830 :param reverse: reverse order, recent revisions first. 1831 :type reverse: bool 1832 :param metoo: do not report the presence of a patch within itself 1833 :type metoo: bool 1834 1835 :return: Iterator of tuples (R, T) where R are revisions in this 1836 version and T are iterable of revisions in the ``other`` version. 1837 :rtype: iterable of `Revision` 1838 """ 1839 if other is None: othername = None 1840 else: othername = other.fullname 1841 flow = _arch.iter_merges(self.fullname, othername, reverse, metoo) 1842 last_target = None 1843 sources = [] 1844 for target, source in flow: 1845 if last_target is None: 1846 last_target = target 1847 if target == last_target: 1848 sources.append(source) 1849 else: 1850 yield self[last_target], itertools.imap(Revision, sources) 1851 last_target = target 1852 sources = [source] 1853 yield self[last_target], itertools.imap(Revision, sources)
1854
1855 - def iter_cachedrevs(self):
1856 """Iterate over the cached revisions in this version. 1857 1858 :rtype: iterator of `Revision` 1859 """ 1860 for rev in _arch.cachedrevs(self.fullname): 1861 lvl = rev.split('--')[-1] 1862 yield self[lvl]
1863 1864
1865 -class Revision(VersionItem):
1866 1867 """Arch revision namespace object. 1868 1869 :see: `Archive`, `Category`, `Branch`, `Version` 1870 :group Libray Methods: library_add, library_remove, library_find 1871 :group History Methods: get_ancestor, get_previous, iter_ancestors 1872 """ 1873
1874 - def __init__(self, name):
1875 """Create a Revision object from its name. 1876 1877 :param name: fully-qualified revision, like 1878 "jdoe@example.com--2004/frob--devo--1.2--patch-2". 1879 :type name: str 1880 1881 :note: Nameless branches have no "branch" part in their name. 1882 """ 1883 if isinstance(name, Revision): 1884 name = (name._archive, name._version, name._patchlevel) 1885 elif not isinstance(name, _unsafe): 1886 p = NameParser(name) 1887 if not p.has_archive() or not p.has_patchlevel(): 1888 raise errors.NamespaceError(name, 'fully-qualified revision') 1889 name = (p.get_archive(), p.get_package_version(), 1890 p.get_patchlevel()) 1891 VersionItem.__init__(self, *name) 1892 self.__patchlog = Patchlog(self) 1893 self._impl = _revision_impl()
1894
1895 - def as_revision(self):
1896 """Deprecated 1897 1898 Returns this revision. For consistency with `Package.as_revision()`. 1899 1900 :rtype: `Revision` 1901 """ 1902 deprecated_callable(self.as_revision, because='Foolish consistency.') 1903 return self
1904
1905 - def exists(self):
1906 try: 1907 for revision in self.version.iter_revisions(): 1908 if revision.fullname == self.fullname: 1909 return True 1910 else: 1911 return False 1912 except Exception: 1913 if self.version.exists(): 1914 raise 1915 else: 1916 return False
1917
1918 - def get(self, dir, link=False):
1919 """Construct a project tree for this revision. 1920 1921 Extract this revision from the archive. 1922 1923 :param dir: path of the project tree to create. Must not 1924 already exist. 1925 :type dir: str 1926 :param link: hardlink files to revision library instead of copying 1927 :type link: bool 1928 :return: newly created project tree. 1929 :rtype: `WorkingTree` 1930 """ 1931 args = self._impl.get_args(self, dir, link) 1932 backend.null_cmd(args) 1933 return WorkingTree(dir)
1934
1935 - def get_patch(self, dir):
1936 """Fetch the changeset associated to this revision. 1937 1938 :param dir: name of the changeset directory to create. Must 1939 not already exist. 1940 :type dir: str 1941 :return: changeset associated to this revision. 1942 :rtype: `Changeset` 1943 """ 1944 _arch.get_patch(self.fullname, dir) 1945 return Changeset(dir)
1946
1947 - def get_patchlog(self):
1948 """Deprecated. 1949 1950 Patchlog associated to this revision. 1951 1952 :rtype: `Patchlog` 1953 :see: `Revision.patchlog` 1954 """ 1955 deprecated_callable(self.get_patchlog, (Revision, 'patchlog')) 1956 return self.patchlog
1957
1958 - def _get_patchlog(self):
1959 return self.__patchlog
1960 1961 patchlog = property(_get_patchlog, doc=""" 1962 Patchlog associated to this revision. 1963 1964 The `Patchlog` object is created in `__init__`, since log parsing is 1965 deferred that has little overhead and avoid parsing the log for a given 1966 revision several times. The patchlog data is read from the archive. 1967 1968 :type: `Patchlog` 1969 :see: `ArchSourceTree.iter_logs` 1970 """) 1971
1972 - def make_continuation(self, target):
1973 """Create a continuation of this revision in the target version. 1974 1975 :param target: version to create a continuation into. If it does not 1976 exist yet, it is created. 1977 :type target: Version 1978 """ 1979 _check_version_param(target, 'target') 1980 target_name = target.fullname 1981 _arch.tag(self.fullname, target_name)
1982
1983 - def library_add(self):
1984 """Add this revision to the library. 1985 1986 :postcondition: self in self.version.iter_library_revisions() 1987 """ 1988 _arch.library_add(self.fullname)
1989
1990 - def library_remove(self):
1991 """Remove this revision from the library. 1992 1993 :precondition: self in self.version.iter_library_revisions() 1994 :postcondition: self not in self.version.iter_library_revisions() 1995 """ 1996 _arch.library_remove(self.fullname)
1997
1998 - def library_find(self):
1999 """The copy of this revision in the library. 2000 2001 :rtype: `LibraryTree` 2002 :precondition: self in self.version.iter_library_revisions() 2003 """ 2004 return LibraryTree(_arch.library_find(self.fullname))
2005 2006 #def library_file(self, file): 2007 # raise NotImplementedError, "library_file is not yet implemented" 2008
2009 - def get_ancestor(self):
2010 """Deprecated. 2011 2012 Parent revision. 2013 2014 :return: 2015 - The previous namespace revision, if this revision is regular 2016 commit. 2017 - The tag origin, if this revision is a continuation 2018 - ``None`` if this revision is an import. 2019 2020 :rtype: `Revision` or None 2021 :see: `Revision.ancestor` 2022 """ 2023 deprecated_callable(self.get_ancestor, (Revision, 'ancestor')) 2024 return self.ancestor
2025
2026 - def _get_ancestor(self):
2027 args = ['ancestry-graph', '--immediate', self.fullname] 2028 # ancestry-graph --immediate always gives a fully qualified revision id 2029 revision_id = backend.one_cmd(args) 2030 if revision_id == '(null)': 2031 return None 2032 return Revision(revision_id)
2033 2034 ancestor = property(_get_ancestor, doc=""" 2035 Parent revision. 2036 2037 - The previous namespace revision, if this revision is regular commit. 2038 - The tag origin, if this revision is a continuation 2039 - ``None`` if this revision is an import. 2040 2041 :type: `Revision` or None 2042 """) 2043
2044 - def get_previous(self):
2045 """Deprecated. 2046 2047 Previous namespace revision. 2048 2049 :return: the previous revision in the same version, or None if this 2050 revision is a ``base-0``. 2051 :rtype: `Revision` or None 2052 :see: `Revision.previous` 2053 """ 2054 deprecated_callable(self.get_previous, (Revision, 'previous')) 2055 return self.previous
2056
2057 - def _get_previous(self):
2058 args = ['ancestry-graph', '--previous', self.fullname] 2059 previous = backend.one_cmd(args) 2060 if previous in ('(null)', '<<<no previous revision>>>'): 2061 return None 2062 revision_id = self._impl.revision_from_previous(self.archive.name, 2063 previous) 2064 return Revision(revision_id)
2065 2066 previous = property(_get_previous, doc=""" 2067 Previous namespace revision. 2068 2069 The previous revision in the same version, or None if this revision is a 2070 ``base-0``. 2071 2072 :type: `Revision` or None 2073 """) 2074
2075 - def iter_ancestors(self, metoo=False):
2076 """Ancestor revisions. 2077 2078 :param metoo: yield ``self`` as the first revision. 2079 :type metoo: bool 2080 :return: all the revisions in that line of development. 2081 :rtype: iterable of `Revision` 2082 """ 2083 args = ['ancestry-graph'] 2084 args.append(self.fullname) 2085 lines = iter(backend.sequence_cmd(args)) 2086 if not metoo: 2087 lines.next() # the first revision is self 2088 for line in lines: 2089 ancestor, merge = line.split('\t') 2090 yield Revision(ancestor)
2091
2092 - def cache(self, cache=None):
2093 """Cache a full source tree for this revision in its archive. 2094 2095 :param cache: cache root for trees with pristines. 2096 :type cache: bool 2097 """ 2098 _arch.cacherev(self.fullname, cache)
2099
2100 - def uncache(self):
2101 """Remove the cached tree of this revision from its archive.""" 2102 _arch.uncacherev(self.fullname)
2103
2104 - def _tla_file_url(self, name):
2105 return '/'.join([self.archive.location, self.category.nonarch, 2106 self.branch.nonarch, self.version.nonarch, 2107 self.patchlevel, name])
2108
2109 - def _baz_file_url(self, name):
2110 return '/'.join([self.archive.location, self.version.nonarch, 2111 self.patchlevel, name])
2112
2113 - def _file_url(self, name):
2114 if self.archive._is_tla_format(): 2115 return self._tla_file_url(name) 2116 if self.archive._is_baz_format(): 2117 return self._baz_file_url(name) 2118 raise AssertionError, \ 2119 'did not recognize archive version of %s' % self.archive
2120 2121 _checksum_regex = re.compile( 2122 "^Signature-for: (.*)/(.*)\n" 2123 "([a-zA-Z0-9]+ [^/\\s]+ [a-fA-F0-9]+\n)*", 2124 re.MULTILINE) 2125
2126 - def _parse_checksum(self, text):
2127 match = self._checksum_regex.search(text) 2128 checksum_body = match.group() 2129 checksum_lines = checksum_body.strip().split('\n') 2130 checksum_words = map(lambda x: x.split(), checksum_lines) 2131 assert checksum_words[0] == ['Signature-for:', self.fullname] 2132 del checksum_words[0] 2133 checksums = {} 2134 for algo, name, value in checksum_words: 2135 name_sums = checksums.setdefault(name, dict()) 2136 name_sums[algo] = value 2137 return checksums
2138
2139 - def iter_files(self):
2140 """Files stored in the archive for that revision. 2141 2142 :rtype: iterable of `RevisionFile` 2143 """ 2144 import urllib 2145 for checksum_name, can_fail in \ 2146 (('checksum', False), ('checksum.cacherev', True)): 2147 try: 2148 checksum_file = urllib.urlopen(self._file_url(checksum_name)) 2149 except IOError: 2150 if can_fail: continue 2151 else: raise 2152 try: 2153 checksum_data = checksum_file.read() 2154 finally: 2155 checksum_file.close() 2156 yield RevisionFile(self, checksum_name, {}) 2157 checksums = self._parse_checksum(checksum_data) 2158 for name, name_sums in checksums.items(): 2159 yield RevisionFile(self, name, name_sums)
2160
2161 - def apply(self, tree, reverse=False):
2162 """Replay this revision on this tree. Raise on conflict. 2163 2164 :param tree: the tree to apply changes to. 2165 :type tree: `WorkingTree` 2166 :param reverse: invert the meaning of the changeset; adds 2167 become deletes, etc. 2168 :type reverse: bool 2169 :raise errors.ChangesetConflict: a conflict occured while replaying the 2170 revision. 2171 """ 2172 _check_working_tree_param(tree, 'tree') 2173 args = self._impl.replay_args(self, tree, reverse) 2174 status = backend.status_cmd(args, expected=(0,1)) 2175 if status == 1: 2176 raise errors.ChangesetConflict(tree, self)
2177 2178
2179 -class _Namespace_Baz_1_0(object):
2180
2181 - def replay_args(rev, tree, reverse):
2182 args = ['replay', '--dir', str(tree)] 2183 if reverse: 2184 args.append('--reverse') 2185 args.append(rev.fullname) 2186 return args
2187 2188 replay_args = staticmethod(replay_args) 2189
2190 - def get_args(revision, dir, link=False):
2191 args = ['get'] 2192 if link: 2193 args.append('--link') 2194 args.extend((revision.fullname, str(dir))) 2195 return args
2196 2197 get_args = staticmethod(get_args)
2198 2199
2200 -def _revision_impl():
2201 if backend.version.release < (1, 4, 0): 2202 return _Revision_Baz_1_0 2203 else: 2204 return _Revision_Baz_1_4
2205 2206
2207 -class _Revision_Baz_1_0(_Namespace_Baz_1_0):
2208
2209 - def revision_from_previous(archive, previous):
2210 return archive + '/' + previous
2211 2212 revision_from_previous = staticmethod(revision_from_previous)
2213 2214
2215 -class _Revision_Baz_1_4(_Namespace_Baz_1_0):
2216
2217 - def revision_from_previous(archive, previous):
2218 return previous
2219 2220 revision_from_previous = staticmethod(revision_from_previous)
2221 2222 2223 public('RevisionFile') 2224
2225 -class RevisionFile(object):
2226 2227 """File component of an archived revision. 2228 2229 :ivar revision: revision this file belongs to. 2230 :type revision: `Revision` 2231 :ivar name: name of that file. 2232 :type name: str 2233 :ivar checksums: dictionnary whose keys are checksum algorithms 2234 (e.g. ``"md5"``, ``"sha1"``) and whose values are checksum 2235 values (strings of hexadecimal digits). 2236 :type checksums: dict of str to str 2237 """ 2238
2239 - def __init__(self, revision, name, checksums):
2240 self.revision = revision 2241 self.name = name 2242 self.checksums = checksums
2243
2244 - def _get_data(self):
2245 import urllib 2246 my_file = urllib.urlopen(self.revision._file_url(self.name)) 2247 try: 2248 ret = my_file.read() 2249 finally: 2250 my_file.close() 2251 return ret
2252 2253 data = property(_get_data, 2254 """Content of of that file. 2255 2256 :type: str 2257 """)
2258 2259 2260 ### Patch logs ### 2261 2262 public('Patchlog') 2263 2264 from _patchlog import Patchlog 2265 2266 public('LogMessage') 2267 2268 from _logmessage import LogMessage 2269 2270 2271 ### Source trees ### 2272 2273 public( 2274 'init_tree', 2275 'in_source_tree', 2276 'tree_root', 2277 ) 2278
2279 -def init_tree(directory, version=None, nested=False):
2280 """Initialize a new project tree. 2281 2282 :param directory: directory to initialize as a source tree. 2283 :type directory: str 2284 :param version: if given, set the the ``tree-version`` and create an empty 2285 log version. 2286 :type version: `Version` 2287 :param nested: if true, the command will succeed even if 'directory' 2288 is already within a source tree. 2289 :type nested: bool 2290 :return: source tree object for the given directory. 2291 :rtype: `WorkingTree` 2292 """ 2293 if version is not None: 2294 version = _version_param(version) 2295 _arch.init_tree(directory, version, nested) 2296 return WorkingTree(directory)
2297 2298
2299 -def in_source_tree(directory=None):
2300 """Is directory inside a Arch source tree? 2301 2302 :param directory: test if that directory is in an Arch source tree. 2303 :type directory: str 2304 :return: whether this directory is inside an Arch source tree. 2305 :rtype: bool 2306 2307 :warning: omitting the ``directory`` argument is deprecated. 2308 """ 2309 if directory is None: 2310 deprecated_usage( 2311 in_source_tree, "Argument defaults to current direcotry.") 2312 directory = '.' 2313 return _arch.in_tree(directory)
2314 2315
2316 -def tree_root(directory=None):
2317 """SourceTree containing the given directory. 2318 2319 :param directory: give the ``tree-root`` of this directory. Specify "." to 2320 get the ``tree-root`` of the current directory. 2321 :type directory: str 2322 :return: source tree containing ``directory``. 2323 :rtype: `ArchSourceTree` 2324 2325 :warning: omitting the ``directory`` argument is deprecated. 2326 """ 2327 if directory is None: 2328 deprecated_usage(tree_root, "Argument defaults to current directory.") 2329 directory = '.' 2330 root = _arch.tree_root(directory) 2331 # XXX use SourceTree to instanciate either WorkingTree or LibraryTree 2332 return SourceTree(root)
2333 2334 2335 public('SourceTree', 2336 'ForeignTree', 2337 'ArchSourceTree', 2338 'LibraryTree', 2339 'WorkingTree') 2340
2341 -class SourceTree(DirName):
2342 2343 """Abstract base class for `ForeignTree` and `ArchSourceTree`.""" 2344
2345 - def __new__(cls, root=None):
2346 """Create a source tree object for the given root path. 2347 2348 `ForeignTree` if root does not point to a Arch source tree. 2349 `LibraryTree` if root is a tree in the revision library. `WorkingTree` 2350 if root is a Arch source tree outside of the revision library. 2351 2352 If root is omitted, use the tree-root of the current working directory. 2353 """ 2354 __pychecker__ = 'no-returnvalues' 2355 # checking root actually is a root done by LibraryTree and WorkingTree 2356 if cls is not SourceTree: 2357 assert root is not None 2358 return DirName.__new__(cls, root) 2359 else: 2360 if not root: 2361 root = _arch.tree_root(os.getcwd()) 2362 root = DirName(root).realpath() 2363 if _is_tree_root(root): 2364 for revlib in _arch.iter_revision_libraries(): 2365 if root.startswith(revlib): 2366 return LibraryTree(root) 2367 return WorkingTree(root) 2368 else: 2369 assert not _arch.in_tree(root) 2370 return ForeignTree(root)
2371 2372
2373 -class ForeignTree(SourceTree):
2374 """Generic source tree without Arch support. 2375 2376 Unlike Arch source trees, the root may not even exist. 2377 """
2378 - def __init__(self, root): pass
2379 2380
2381 -def _is_tree_root(dir):
2382 """Return True if dir is a Arch source tree.""" 2383 return os.path.isdir(os.path.join(dir, '{arch}'))
2384 2385
2386 -class ArchSourceTree(SourceTree):
2387 2388 """Abstract base class for Arch source trees.""" 2389
2390 - def get_tree_version(self):
2391 """Default version of the tree, also called tree-version. 2392 2393 :raise errors.TreeVersionError: no valid default version set. 2394 :raise IOError: unable to read the ++default-version file. 2395 """ 2396 data = self._read_tree_version() 2397 if data is None: 2398 raise errors.TreeVersionError(self) 2399 try: 2400 return Version(data) 2401 except errors.NamespaceError: 2402 raise errors.TreeVersionError(self, data)
2403 tree_version = property(get_tree_version) 2404
2405 - def _read_tree_version(self):
2406 path = os.path.join(self, '{arch}', '++default-version') 2407 try: 2408 return open(path, 'r').read().rstrip('\n') 2409 except IOError: 2410 if not os.path.exists(vsn_name): 2411 return None 2412 raise
2413
2414 - def _get_tree_revision(self):
2415 """Revision of the last patchlog for the tree-version. 2416 2417 :raise errors.TreeVersionError: no valid default version set. 2418 :raise IOError: unable to read the ++default-version file. 2419 """ 2420 reverse_logs = self.iter_logs(reverse=True) 2421 try: last_log = reverse_logs.next() 2422 except StopIteration: 2423 raise RuntimeError('no logs for the tree-version') 2424 return last_log.revision
2425 tree_revision = property(_get_tree_revision) 2426
2427 - def check_is_tree_root(self):
2428 if not _is_tree_root(self): 2429 raise errors.SourceTreeError(str(self)) 2430 assert os.path.isdir(self)
2431
2432 - def get_tagging_method(self):
2433 return _arch.tagging_method(self)
2434 tagging_method = property(get_tagging_method) 2435 2436
2437 - def iter_inventory(self, source=False, precious=False, backups=False, 2438 junk=False, unrecognized=False, trees=False, 2439 directories=False, files=False, both=False, 2440 names=False, limit=None):
2441 """Tree inventory. 2442 2443 The kind of files looked for is specified by setting to 2444 ``True`` exactly one of the following keyword arguments: 2445 2446 ``source``, ``precious``, ``backups``, ``junk``, 2447 ``unrecognized``, ``trees``. 2448 2449 If the ``trees`` argument is not set, whether files, directory 2450 or both should be listed is specified by setting to ``True`` 2451 exactly one of the following keyword arguments: 2452 2453 ``directories``, ``files``, ``both``. 2454 2455 :keyword source: list source files only. 2456 :keyword precious: list precious files only. 2457 :keyword backups: list backup files only. 2458 :keyword junk: list junk files only. 2459 :keyword unrecognized: list unrecognized files only. 2460 :keyword trees: list nested trees only. If this is true, the 2461 iterator wil only yield `ArchSourceTree` objects. 2462 2463 :keyword directories: list directories only, 2464 yield only `FileName` objects. 2465 :keyword files: list files only, yield only `DirName` objects. 2466 :keyword both: list both files and directories, 2467 yield both FileName and `DirName` objects. 2468 2469 :keyword name: do inventory as if the id-tagging-method was 2470 set to ``names``. That is useful to ignore 2471 ``untagged-source`` classification. 2472 2473 :keyword limit: restrict the inventory to this directory. Must 2474 be the name of a directory relative to the tree root. 2475 2476 :rtype: iterable of `FileName`, `DirName`, `ArchSourceTree` 2477 according to the arguments. 2478 """ 2479 __pychecker__ = 'maxargs=12' 2480 name_filter = self.__inventory_filter(trees, directories, files, both) 2481 name_iterator = self.__inventory_helper( 2482 ['inventory'], source, precious, backups, junk, unrecognized, 2483 trees, directories, files, both, names, limit) 2484 # using a generator here would delay argument checking 2485 return itertools.imap(name_filter, name_iterator)
2486
2487 - def iter_inventory_ids(self,source=False, precious=False, backups=False, 2488 junk=False, unrecognized=False, trees=False, 2489 directories=False, files=False, both=False, 2490 limit=None):
2491 """Tree inventory with file ids. 2492 2493 :see: `ArchSourceTree.iter_inventory` 2494 2495 :rtype: iterable of tuples ``(id, item)``, where ``item`` is 2496 `FileName`, `DirName`, `ArchSourceTree` according to the 2497 arguments and ``id`` is the associated inventory id. 2498 """ 2499 __pychecker__ = 'maxargs=11' 2500 name_filter = self.__inventory_filter(trees, directories, files, both) 2501 def id_name_filter(line): 2502 name, id_ = line.split('\t') 2503 return id_, name_filter(name)
2504 id_name_iterator = self.__inventory_helper( 2505 ['inventory', '--ids'], source, precious, backups, junk, 2506 unrecognized, trees, directories, files, both, 2507 names=False, limit=limit) 2508 # using a generator here would delay argument checking 2509 return itertools.imap(id_name_filter, id_name_iterator)
2510
2511 - def __inventory_filter(self, trees, directories, files, both):
2512 types = trees, directories, files, both 2513 assert 1 == sum([bool(t) for t in types]) 2514 trees, directories, files, both = types 2515 if trees: 2516 return self.__inventory_filter_trees 2517 if directories: 2518 return self.__inventory_filter_directories 2519 if files: 2520 return self.__inventory_filter_files 2521 if both: 2522 return self.__inventory_filter_both 2523 raise AssertionError('unreachable')
2524
2525 - def __inventory_filter_trees(self, t):
2526 return SourceTree(self/name_unescape(t))
2527
2528 - def __inventory_filter_directories(self, d):
2529 return DirName(name_unescape(d))
2530
2531 - def __inventory_filter_files(self, f):
2532 return FileName(name_unescape(f))
2533
2534 - def __inventory_filter_both(self, f):
2535 __pychecker__ = 'no-returnvalues' 2536 f = name_unescape(f) 2537 if os.path.isfile(self/f): 2538 return FileName(f) 2539 elif os.path.islink(self/f): 2540 return FileName(f) 2541 elif os.path.isdir(self/f): 2542 return DirName(f) 2543 else: 2544 raise AssertionError("neither file, link, nor dir: %r" % (self/f,))
2545
2546 - def __inventory_helper(self, opts, 2547 source, precious, backups, junk, unrecognized,trees, 2548 directories, files, both, names, limit):
2549 __pychecker__ = 'maxargs=13' 2550 # Asserts there is only one class and one type 2551 classes = source, precious, backups, junk, unrecognized, trees 2552 classes = [bool(X) for X in classes] 2553 assert 1 == sum(classes) 2554 class_opts = ['--source', '--precious', '--backups', '--junk', 2555 '--unrecognized', '--trees'] 2556 types = directories, files, both 2557 types = [bool(X) for X in types] 2558 assert 1 == sum(types) + bool(trees) 2559 type_opts = ['--directories', '--files', '--both'] 2560 for flag, opt in zip(classes + types, class_opts + type_opts): 2561 if flag: 2562 opts.append(opt) 2563 if names: 2564 opts.append('--names') 2565 if limit is not None: 2566 self._check_relname_param(limit, 'limit') 2567 opts.append(str(limit)) 2568 return backend.sequence_cmd(opts, chdir=str(self))
2569 2570
2571 - def _check_relname_param(self, param, name):
2572 """Internal argument checking utility""" 2573 if not isinstance(param, basestring): 2574 exc_type = TypeError 2575 elif os.path.isabs(param): 2576 exc_type = ValueError 2577 else: 2578 return 2579 raise exc_type("Parameter \"%s\" must be a relative path (string)" 2580 " but was: %r" % (name, param))
2581
2582 - def inventory(self, *args, **kw):
2583 """Deprecated. 2584 2585 Inventory of the source tree as a list. 2586 2587 :see: `iter_inventory` 2588 """ 2589 # deprecated_callable(self.inventory, self.iter_inventory) 2590 return list (self.iter_inventory(*args, **kw))
2591
2592 - def get_tree(self):
2593 return util.sorttree(self.inventory(source=True, both=True))
2594
2595 - def iter_log_versions(self, limit=None, reverse=False):
2596 """Iterate over versions this tree has a log-version for. 2597 2598 :param limit: only iterate log-versions in this namespace. 2599 :type limit: `Archive`, `Category`, `Branch`, `Version` 2600 :param reverse: yield log-versions in reverse order. 2601 :type reverse: bool 2602 """ 2603 kwargs = {} 2604 if isinstance(limit, Archive): 2605 kwargs['archive'] = limit.name 2606 elif isinstance(limit, (Category, Branch, Version)): 2607 kwargs['archive'] = limit.archive.name 2608 if isinstance(limit, Category): kwargs['category'] = limit.nonarch 2609 elif isinstance(limit, Branch): kwargs['branch'] = limit.nonarch 2610 elif isinstance(limit, Version): kwargs['version'] = limit.nonarch 2611 elif limit is not None and not isinstance(limit, Archive): 2612 raise TypeError("Expected Archive, Category, Branch or Version" 2613 " but got: %r" % limit) 2614 for vsn in _arch.iter_log_versions(self, reverse=reverse, **kwargs): 2615 yield Version(vsn)
2616
2617 - def iter_logs(self, version=None, reverse=False):
2618 """Iterate over patchlogs present in this tree. 2619 2620 :param version: list patchlogs from this version. Defaults to 2621 the tree-version. 2622 :type version: `Version` 2623 :param reverse: iterate more recent logs first. 2624 :type reverse: bool 2625 :return: patchlogs from ``version``. 2626 :rtype: iterator of `Patchlog`. 2627 :raise errors.TreeVersionError: no valid default version set. 2628 :raise IOError: unable to read the ++default-version file. 2629 """ 2630 if version is None: 2631 version = self.tree_version.fullname 2632 else: 2633 version = _version_param(version) 2634 for rvsn in _arch.iter_log_ls(self, version, reverse=reverse): 2635 yield Patchlog(_unsafe((rvsn,)), tree=self)
2636
2637 - def get_tag(self, name):
2638 """Read a file id set by an explicit id tag or tagline. 2639 2640 FIXME: update docstring when support for pybaz<1.3 is dropped, as this 2641 release fixed "baz id" to report name-based ids correctly. 2642 2643 :param name: name of a source file relative to the tree-root. 2644 :type name: str 2645 :return: file id if the file has an explicit id or a tagline. 2646 :rtype: str 2647 """ 2648 absname = str(self/name) 2649 if not os.path.exists(absname) and not os.path.islink(absname): 2650 raise IOError(2, 'No such file or directory', absname) 2651 args = ('id', absname) 2652 status, output = backend.status_one_cmd(args, expected=(0,1)) 2653 if status == 1: 2654 return None 2655 return output.split('\t')[1]
2656
2657 - def file_find(self, name, revision):
2658 """Find a pristine copy of a file for a given revision. 2659 2660 Will create the requested revision in the library or in the 2661 current tree pristines if needed. 2662 2663 :param name: name of the file. 2664 :type name: str 2665 :param revision: revision to look for the file into. 2666 :type revision: `Revision` 2667 :return: absolute path name of a pristine copy of the file. 2668 :rtype: `pathname.PathName` 2669 :raise errors.MissingFileError: file is not source or is not 2670 present in the specified revision. 2671 """ 2672 revision = _revision_param(revision) 2673 result = _arch.file_find(self, name, revision) 2674 if result is None: 2675 raise errors.MissingFileError(self, name, revision) 2676 # XXX Work around relative path when using pristines 2677 result = os.path.join(self, result) 2678 return PathName(result)
2679 2680
2681 -class LibraryTree(ArchSourceTree):
2682 2683 """Read-only Arch source tree.""" 2684
2685 - def __init__(self, root):
2686 """Create a LibraryTree object with the given root path. 2687 2688 Root must be a directory containing a Arch source tree in the 2689 revision library. 2690 """ 2691 ArchSourceTree.__init__(self, root) 2692 self.check_is_tree_root()
2693 2694
2695 -class WorkingTree(ArchSourceTree):
2696 2697 """Working source tree, Arch source tree which can be modified.""" 2698
2699 - def __init__(self, root):
2700 """Create WorkingTree object with the given root path. 2701 2702 Root must be a directory containing a valid Arch source tree 2703 outside of the revision library. 2704 """ 2705 ArchSourceTree.__init__(self, root) 2706 self.check_is_tree_root() 2707 self._impl = _workingtree_impl()
2708
2709 - def sync_tree(self, revision):
2710 """Adds the patchlogs in the given revision to the current tree. 2711 2712 Create a temporary source tree for ``revision``, then add all the 2713 patchlogs present in that tree to the current tree. No content 2714 is touched besides the patchlogs. 2715 2716 :param revision: revision to synchronize with. 2717 :type revision: `Version`, `Revision` or str 2718 :raise errors.NamespaceError: ``revision`` is not a valid 2719 version or revision name. 2720 """ 2721 revision = _version_revision_param(revision) 2722 _arch.sync_tree(self, revision)
2723
2724 - def set_tree_version(self, version):
2725 version = _version_param(version) 2726 version_path = os.path.join(str(self), '{arch}', '++default-version') 2727 print >> open(version_path, 'w'), version
2728
2729 - def resolved(self, all=False):
2730 _arch.resolved(self, all)
2731 2732 tree_version = property(ArchSourceTree.get_tree_version, set_tree_version) 2733
2734 - def has_changes(self):
2735 """Are there uncommited changes is this source tree? 2736 2737 :rtype: bool 2738 """ 2739 return self._impl.has_changes(str(self))
2740
2741 - def changes(self, revision=None, output=None):
2742 """Uncommited changes in this tree. 2743 2744 :param revision: produce the changeset between this revision 2745 and the tree. If ``revision`` is ``None``, use the 2746 tree-revision. 2747 :type revision: `Revision`, None 2748 :param output: absolute path of the changeset to produce. 2749 :type output: string 2750 :return: changeset between ``revision`` and the tree. 2751 :rtype: Changeset 2752 :raise errors.TreeVersionError: no valid default version set. 2753 :raise IOError: unable to read the ++default-version file. 2754 """ 2755 if revision is None: revision = self.tree_revision 2756 _check_str_param(output, 'output') 2757 _check_revision_param(revision, 'revision') 2758 return delta(revision, self, output)
2759
2760 - def star_merge(self, from_=None, reference=None, 2761 forward=False, diff3=False):
2762 """Merge mutually merged branches. 2763 2764 :bug: if the merge causes a conflict, a RuntimeError is 2765 raised. You should not rely on this behaviour as it is 2766 likely to change in the future. If you want to support 2767 conflicting merges, use `iter_star_merge` instead. 2768 2769 :param `from_`: branch to merge changes from, ``None`` means the 2770 ``tree-version``. 2771 :type `from_`: None, `Version`, `Revision`, or str 2772 :param reference: reference version for the merge, ``None`` 2773 means the ``tree-version``. 2774 :type reference: None, `Version` or str 2775 :param forward: ignore already applied patch hunks. 2776 :type forward: bool 2777 :param diff3: produce inline conflict markers instead of 2778 ``.rej`` files. 2779 :type diff3: bool 2780 :raise errors.NamespaceError: ``from_`` or ``reference`` is 2781 not a valid version or revision name. 2782 """ 2783 if from_ is not None: 2784 from_ = _version_revision_param(from_) 2785 if reference is not None: 2786 reference = _version_revision_param(reference) 2787 if forward: 2788 deprecated_usage(WorkingTree.star_merge, ( 2789 "forward=True has always been a no-op.")) 2790 args = self._impl.star_merge_args(diff3) 2791 args.extend(('--dir', str(self))) 2792 if reference is not None: 2793 args.extend(('--reference', reference)) 2794 if from_ is not None: 2795 args.append(from_) 2796 # yes, we want to raise if there was a conflict. 2797 backend.null_cmd(args)
2798
2799 - def iter_star_merge(self, from_=None, reference=None, 2800 forward=False, diff3=False):
2801 """Merge mutually merged branches. 2802 2803 :param `from_`: branch to merge changes from, ``None`` means the 2804 ``tree-version``. 2805 :type `from_`: None, `Version`, `Revision`, or str 2806 :param reference: reference version for the merge, ``None`` 2807 means the ``tree-version``. 2808 :type reference: None, `Version` or str 2809 :param forward: ignore already applied patch hunks. 2810 :type forward: bool 2811 :param diff3: produce inline conflict markers instead of 2812 ``.rej`` files. 2813 :type diff3: bool 2814 :raise errors.NamespaceError: ``from_`` or ``reference`` is 2815 not a valid version or revision name. 2816 :rtype: `ChangesetApplication` 2817 """ 2818 # the --changes option must be supported by another method 2819 # which returns a Changeset. 2820 if from_ is not None: 2821 from_ = _version_revision_param(from_) 2822 if reference is not None: 2823 reference = _version_revision_param(reference) 2824 if forward: 2825 deprecated_usage(WorkingTree.iter_star_merge, ( 2826 "forward=True has always been a no-op.")) 2827 args = self._impl.star_merge_args(diff3) 2828 args.extend(('--dir', str(self))) 2829 if reference is not None: 2830 args.extend(('--reference', reference)) 2831 if from_ is not None: 2832 args.append(from_) 2833 merge = backend.sequence_cmd(args, expected=(0,1)) 2834 return ChangesetApplication(merge)
2835
2836 - def undo(self, revision=None, output=None, quiet=False, throw_away=False):
2837 """Undo and save changes in a project tree. 2838 2839 Remove local changes since revision and optionally save them 2840 as a changeset. 2841 2842 :keyword revision: revision to revert to. Default to the last 2843 revision of the tree-version for which a patchlog is present. 2844 :type revision: `Revision`, str 2845 :keyword output: name of the output changeset directory. Must 2846 not already exist. Default to an automatic ,,undo-N name 2847 in the working tree. 2848 :type output: str 2849 :keyword quiet: OBSOLETE. Incremental output is always discarded. 2850 :type quiet: bool 2851 :keyword throw_away: discard the output changeset and return 2852 ``None``. Must not be used at the same time as ``output``. 2853 :type throw_away: bool 2854 :return: changeset restoring the undone changes, 2855 or None if ``throw_away``. 2856 :rtype: `Changeset`, None 2857 """ 2858 assert sum(map(bool, (output, throw_away))) < 2 2859 if output is None: 2860 output = util.new_numbered_name(self, ',,undo-') 2861 if revision is not None: 2862 revision = _revision_param(revision) 2863 _arch.undo(self, revision, output, quiet, throw_away) 2864 if throw_away: return None 2865 return Changeset(output)
2866
2867 - def redo(self, patch=None, keep=False, quiet=False):
2868 """Redo changes in a project tree. 2869 2870 Apply patch to the project tree and delete patch. 2871 2872 If patch is provided, it must be a Changeset object. Else, the highest 2873 numbered ,,undo-N directory in the project tree root is used. 2874 2875 If keep is true, the patch directory is not deleted. 2876 """ 2877 _arch.redo(self, patch, keep, quiet)
2878
2879 - def set_tagging_method(self, method):
2880 _arch.set_tagging_method(self, method)
2881 tagging_method = property(ArchSourceTree.get_tagging_method, 2882 set_tagging_method) 2883
2884 - def add_tag(self, file):
2885 _arch.add(self/file)
2886
2887 - def move_tag(self, src, dest):
2888 _arch.move(self/src, self/dest)
2889
2890 - def move_file(self, src, dest):
2891 # FIXME: move explicit tag if present 2892 dest = PathName(dest) 2893 assert os.path.exists((self/dest).dirname()) 2894 os.rename(self/src, self/dest)
2895
2896 - def delete(self, file):
2897 fullfile = self/file 2898 if os.path.isfile(fullfile): 2899 if _arch.has_explicit_id(fullfile): 2900 _arch.delete(fullfile) 2901 os.unlink(fullfile) 2902 elif os.path.isdir(fullfile): 2903 shutil.rmtree(fullfile)
2904
2905 - def del_file(self, file):
2906 fullfile = self/file 2907 if os.path.isfile(fullfile): 2908 os.unlink(fullfile) 2909 else: 2910 shutil.rmtree(fullfile)
2911
2912 - def del_tag(self, file):
2913 if not os.path.islink(self/file): 2914 assert os.path.exists(self/file) 2915 _arch.delete(self/file)
2916
2917 - def import_(self, log=None):
2918 """Archive a full-source base-0 revision. 2919 2920 If log is specified, it must be a LogMessage object or a file 2921 name as a string. If omitted, the default log message file of 2922 the tree is used. 2923 2924 The --summary, --log-message and --setup options to tla are 2925 mere CLI convenience features and are not directly supported. 2926 """ 2927 if isinstance(log, LogMessage): 2928 log.save() 2929 log = log.name 2930 assert log is None or isinstance(log, str) 2931 if log is not None: 2932 log = os.path.abspath(log) 2933 self._impl.import_(str(self), log)
2934
2935 - def commit(self, log=None, strict=False, seal=False, fix=False, 2936 out_of_date_ok=False, file_list=None, version=None, 2937 just_commit=False):
2938 """Archive a changeset-based revision. 2939 2940 :keyword version: version in which to commit the revision. 2941 Defaults to the `tree_version`. 2942 :type version: `Version`, str 2943 :keyword log: Log message for this revision. Defaults to the log 2944 message file of the tree-version (the file created by 2945 ``tla make-log``. 2946 :type log: `LogMessage` 2947 :param strict: perform a strict tree-lint before commiting. 2948 :type strict: bool 2949 :param seal: create a ``version-0`` revision. 2950 :type seal: bool 2951 :param fix: create a ``versionfix`` revision. 2952 :type fix: bool 2953 :param out_of_date_ok: commit even if the tree is out of 2954 date. 2955 :type out_of_date_ok: bool 2956 :param file_list: Only commit changes to those files, 2957 specified relative to the tree-root. 2958 :type file_list: iterable of str, with at least one item. 2959 :param just_commit: only create new revision, do not add ancillary data 2960 like cachedrevs or ancestry files. 2961 :type just_commit: bool 2962 2963 The --summary and --log-message options to tla are mere CLI 2964 convenience features and are not directly supported. 2965 2966 :see: `WorkingTree.iter_commit` 2967 """ 2968 for unused in self.iter_commit( 2969 log, strict, seal, fix, out_of_date_ok, file_list, version, 2970 just_commit): 2971 pass
2972
2973 - def log_for_merge(self):
2974 """Standard arch log of newly merged patches. 2975 2976 :rtype: str 2977 """ 2978 return _arch.log_for_merge(self)
2979 2980
2981 - def iter_commit(self, log=None, strict=False, seal=False, fix=False, 2982 out_of_date_ok=False, file_list=None, version=None, 2983 just_commit=False, stderr_too=False):
2984 """Archive a changeset-based revision, returning an iterator. 2985 2986 2987 :keyword version: version in which to commit the revision. 2988 Defaults to the `tree_version`. 2989 :type version: `Version`, str 2990 :keyword log: Log message for this revision. Defaults to the log 2991 message file of the tree-version (the file created by 2992 ``tla make-log``. 2993 :type log: `LogMessage` 2994 :keyword strict: perform a strict tree-lint before commiting. 2995 :type strict: bool 2996 :keyword seal: create a ``version-0`` revision. 2997 :type seal: bool 2998 :keyword fix: create a ``versionfix`` revision. 2999 :type fix: bool 3000 :keyword out_of_date_ok: commit even if the tree is out of 3001 date. 3002 :type out_of_date_ok: bool 3003 :keyword file_list: Only commit changes to those files, 3004 specified relative to the tree-root. 3005 :type file_list: iterable of str, with at least one item. 3006 :param just_commit: only create new revision, do not add ancillary data 3007 like cachedrevs or ancestry files. 3008 :type just_commit: bool 3009 :param stderr_too: iterate over stderr output as well as stdout. 3010 :type stderr_too: bool 3011 :rtype: iterator of `TreeChange`, `Chatter` or str 3012 3013 :warning: ``stderr_too=True`` is only supported with the 3014 PyArchSpawningStrategy. Using it will cause a ArgumentError with the 3015 TwistedSpawningStrategy. That will be fixed when the process handling 3016 subsystem is replaced by Gnarly. 3017 3018 The --summary and --log-message options to tla are mere CLI 3019 convenience features and are not directly supported. 3020 3021 :see: `WorkingTree.commit` 3022 """ 3023 # XXX stderr_too=True will fail with the TwistedSpawningStrategy. That 3024 # XXX will be fixed when the process handling subsystem is replaced by 3025 # XXX Gnarly. -- David Allouche 2005-05-27 3026 args = ['commit'] 3027 if just_commit: 3028 args.extend(self._impl.commit_just_commit) 3029 if log is not None: 3030 if isinstance(log, LogMessage): 3031 log.save() 3032 log = log.name 3033 assert isinstance(log, str) 3034 log = os.path.abspath(log) 3035 args.extend(('--log', log)) 3036 if strict: 3037 args.append('--strict') 3038 assert not (seal and fix) 3039 if seal: 3040 args.append('--seal') 3041 if fix: 3042 args.append('--fix') 3043 if out_of_date_ok: 3044 args.append('--out-of-date-ok') 3045 self._maybe_commit_version(version, args) 3046 if file_list is not None: 3047 file_list = tuple(file_list) 3048 assert 0 < len(file_list) 3049 args.append('--') 3050 args.extend(map(str, file_list)) 3051 iterator = backend.sequence_cmd(args, chdir=str(self), 3052 stderr_too=stderr_too) 3053 return classify_changeset_creation(iterator)
3054
3055 - def _maybe_commit_version(self, version, args):
3056 """Add a version to the commit command line if needed""" 3057 if version is None: 3058 # raises on bad versions 3059 self.tree_version.fullname 3060 return 3061 version = _version_param(version) 3062 args.extend(self._impl.commit_version_args(version))
3063
3064 - def log_message(self, create=True):
3065 """Default log-message object used by import and commit. 3066 3067 If `create` is False, and the standard log file does not already 3068 exists, return None. If `create` is True, use ``tla make-log`` if 3069 needed. 3070 """ 3071 path = self._message_path() 3072 if not os.path.exists(path): 3073 if not create: return None 3074 _arch.make_log(self) 3075 return LogMessage(path)
3076
3077 - def _message_path(self):
3078 version = self.tree_version 3079 return self / FileName('++log.%s--%s' 3080 % (version.nonarch, version.archive.name))
3081
3082 - def add_log_version(self, version):
3083 """Add a patch log version to the project tree.""" 3084 version = _version_param(version) 3085 _arch.add_log_version(self, version)
3086
3087 - def remove_log_version(self, version):
3088 """Remove a patch log version from the project tree.""" 3089 version = _version_param(version) 3090 _arch.remove_log_version(self, version)
3091
3092 - def replay(self):
3093 """Replay changesets into this working tree.""" 3094 _arch.replay(self)
3095
3096 - def update(self):
3097 """Apply delta of new revisions in the archive. 3098 3099 Apply delta(A,B) on this working tree, where A and B are both 3100 revisions of the tree version, A is the latest whose patchlog 3101 is present in the tree and B is the latest present in the 3102 archive. 3103 """ 3104 _arch.update(self)
3105
3106 - def iter_pristines(self):
3107 """Pristines present in that source tree. 3108 3109 :return: Revisions which have pristine trees in that source tree. 3110 :rtype: iterable of `Revision` 3111 """ 3112 for rvsn in _arch.iter_pristines(self): 3113 yield Revision(rvsn)
3114
3115 - def add_pristine(self, revision):
3116 """Ensure that the project tree has a particular pristine revision. 3117 3118 :param revision: revision to add a pristine for. 3119 :type revision: `Revision` 3120 """ 3121 revision = _revision_param(revision) 3122 _arch.add_pristine(self, revision)
3123
3124 - def find_pristine(self, revision):
3125 """Path to a pristine tree for the given revision. 3126 3127 :param revision: find a pristine for that revision. 3128 :type revision: `Revision` 3129 :rtype: `ArchSourceTree` 3130 :raise errors.NoPristineFoundError: no pristine tree was found 3131 for the given revision. 3132 """ 3133 if revision not in self.iter_pristines(): 3134 raise errors.NoPristineFoundError(self, revision) 3135 revision = _revision_param(revision) 3136 return ArchSourceTree(_arch.find_pristine(self, revision))
3137 3138 3139 # This is crufty. Implementations are not flyweights.
3140 -def _workingtree_impl():
3141 if backend.version.release < (1, 1, 0): 3142 return _WorkingTree_Baz_1_0 3143 elif backend.version.release == (1, 2, 0): 3144 return _WorkingTree_Baz_2_0_BUG 3145 elif backend.version.release < (1, 4, 0): 3146 return _WorkingTree_Baz_1_1 3147 else: 3148 return _WorkingTree_Baz_1_4
3149 3150
3151 -class _WorkingTree_Baz_1_0(object):
3152
3153 - def import_(tree, log):
3154 args = ['import', '--dir', tree, '--setup'] 3155 if log is not None: 3156 args.extend(('--log', log)) 3157 backend.null_cmd(args)
3158 3159 import_ = staticmethod(import_) 3160
3161 - def has_changes(tree):
3162 args = ('changes', '--dir', tree, '--quiet') 3163 return 1 == backend.status_cmd(args, expected=(0,1))
3164 3165 has_changes = staticmethod(has_changes) 3166 3167 commit_just_commit = [] 3168
3169 - def star_merge_args(diff3):
3170 args = ['star-merge'] 3171 if diff3: 3172 args.append('--three-way') 3173 return args
3174 3175 star_merge_args = staticmethod(star_merge_args) 3176
3177 - def commit_version_args(version):
3178 return [version]
3179 3180 commit_version_args = staticmethod(commit_version_args)
3181 3182
3183 -class _WorkingTree_Baz_1_1(_WorkingTree_Baz_1_0):
3184
3185 - def import_(tree, log):
3186 args = ['import', '--dir', tree] 3187 if log is not None: 3188 args.extend(('--log', log)) 3189 backend.null_cmd(args)
3190 3191 import_ = staticmethod(import_) 3192
3193 - def has_changes(tree):
3194 args = ('diff', '--dir', tree, '--quiet', '--summary') 3195 return 1 == backend.status_cmd(args, expected=(0,1))
3196 3197 has_changes = staticmethod(has_changes) 3198 3199 commit_just_commit = ['--just-commit'] 3200
3201 - def star_merge_args(diff3):
3202 args = ['merge', '--star-merge'] 3203 if not diff3: 3204 args.append('--two-way') 3205 return args
3206 3207 star_merge_args = staticmethod(star_merge_args)
3208 3209
3210 -class _WorkingTree_Baz_2_0_BUG(_WorkingTree_Baz_1_1):
3211
3212 - def import_(tree, log):
3213 # baz-1.2 has a bug preventing use of the --dir option 3214 args = ['import'] 3215 if log is not None: 3216 args.extend(('--log', log)) 3217 backend.null_cmd(args, chdir=tree)
3218 3219 import_ = staticmethod(import_)
3220 3221
3222 -class _WorkingTree_Baz_1_4(_WorkingTree_Baz_1_1):
3223
3224 - def commit_version_args(version):
3225 return ['--branch', version]
3226 3227 commit_version_args = staticmethod(commit_version_args)
3228 3229 3230 ### Changesets ### 3231 3232 from _changeset import * 3233 3234 public('Changeset') 3235 public('ChangesetApplication', 'ChangesetCreation') 3236 public('delta', 'iter_delta') 3237 3238 public('changeset') 3239
3240 -def changeset(orig, mod, dest):
3241 """Deprecated. 3242 3243 :see: `delta` 3244 :rtype: `Changeset` 3245 """ 3246 deprecated_callable(changeset, delta) 3247 return delta(ArchSourceTree(orig), ArchSourceTree(mod), dest)
3248 3249
3250 -def _check_str_param(param, name):
3251 """Internal argument checking utility""" 3252 if not isinstance(param, basestring): 3253 raise TypeError("Parameter \"%s\" must be a string" 3254 " but was: %r" % (name, param))
3255
3256 -def _check_working_tree_param(param, name):
3257 """Internal argument checking utility""" 3258 if not isinstance(param, WorkingTree): 3259 raise TypeError("Parameter \"%s\" must be a WorkingTree" 3260 " but was: %r" % (name, param))
3261 3262 3263 ### Top level archive functions ### 3264 3265 public('my_id', 'set_my_id', 'make_archive', 'register_archive') 3266 3267 from _my_id import my_id 3268 from _my_id import set_my_id 3269
3270 -def make_archive(name, location, signed=False, listing=False, tla=False):
3271 """Deprecated. 3272 3273 :see: `ArchiveLocation.create_master` 3274 3275 :param name: archive name (e.g. "david@allouche.net--2003b"). 3276 :type name: `Archive` or str 3277 :param location: URL of the archive 3278 :type location: str 3279 :param signed: create GPG signatures for the archive contents. 3280 :type signed: bool 3281 :param listing: maintains ''.listing'' files to enable HTTP access. 3282 :type listing: bool 3283 :param tla: create a tla archive instead of a baz archive. 3284 :type tla: bool 3285 3286 :return: an `Archive` instance for the given name. 3287 :rtype: `Archive` 3288 3289 :raise errors.NamespaceError: ``name`` is not a valid archive name. 3290 """ 3291 deprecated_callable(make_archive, ArchiveLocation.create_master) 3292 name = _archive_param(name) 3293 archive = Archive(_unsafe((name,))) 3294 params = ArchiveLocationParams() 3295 params.signed = signed 3296 params.listing = listing 3297 if tla: 3298 params.tla_format() 3299 location = ArchiveLocation(location) 3300 location.create_master(archive, params) 3301 return archive
3302
3303 -def register_archive(name, location):
3304 """Deprecated. 3305 3306 :see: `ArchiveLocation.register` 3307 3308 :param name: archive name, or None to use the official name stored in the 3309 archive. 3310 :type name: str, None 3311 :param location: URL of the archive. 3312 :type location: str 3313 :return: newly registered archive. 3314 :rtype: `Archive`. 3315 """ 3316 deprecated_callable(register_archive, ArchiveLocation.register) 3317 if name is not None: 3318 name = _archive_param(name) 3319 if Archive(name).is_registered(): 3320 raise ValueError('%r is already a registered name' % (name,)) 3321 3322 _check_str_param(location, 'location') 3323 return _archive_impl().register_archive(name, location)
3324 3325 3326 public('iter_archives', 'archives') 3327
3328 -def iter_archives():
3329 """Iterate over registered archives. 3330 3331 :return: all registered archives. 3332 :rtype: iterable of `Archive` 3333 """ 3334 for n in backend.sequence_cmd(('archives', '--names')): 3335 yield Archive(_unsafe((n,)))
3336 3337
3338 -def archives():
3339 """Deprecated. 3340 3341 List of registered archives. 3342 3343 :rtype: sequence of `Archive` 3344 :see: `iter_archives` 3345 """ 3346 deprecated_callable(archives, iter_archives) 3347 return list(iter_archives())
3348 3349 3350 public('iter_library_archives', 'library_archives') 3351
3352 -def iter_library_archives():
3353 """Iterate over archives present in the revision library. 3354 3355 :returns: all archives which are present in the revision library. 3356 :rtype: iterable of `Archive` 3357 """ 3358 for n in _arch.library_archives(): 3359 yield Archive(_unsafe((n,)))
3360 3361
3362 -def library_archives():
3363 """Deprecated. 3364 3365 List of archives present in the revision library. 3366 3367 :rtype: sequence of `Archive` 3368 :see: `iter_library_archives` 3369 """ 3370 deprecated_callable(library_archives, iter_library_archives) 3371 return list(iter_library_archives())
3372 3373 3374 public('default_archive') 3375
3376 -def default_archive():
3377 """Default Archive object or None. 3378 3379 :return: the default archive, or None. 3380 :rtype: `Archive`, None 3381 """ 3382 name = _arch.default_archive() 3383 if name is None: 3384 return None 3385 else: 3386 return Archive(_unsafe((name,)))
3387 3388 3389 public('make_continuation') 3390
3391 -def make_continuation(source_revision, tag_version):
3392 """Deprecated. 3393 3394 :see: `Revision.make_continuation` 3395 """ 3396 deprecated_callable(make_continuation, Revision.make_continuation) 3397 source_revision = _version_revision_param(source_revision) 3398 tag_version = _version_param(tag_version) 3399 _arch.tag(source_revision, tag_version)
3400 3401 public('get', 'get_patch') 3402
3403 -def get(revision, dir, link=False):
3404 """ Construct a project tree for a revision. 3405 3406 :rtype: `WorkingTree` 3407 :see: `Revision.get` 3408 """ 3409 # FIXME: fragile, code duplication 3410 revision = _package_revision_param(revision) 3411 args = ['get'] 3412 if link: 3413 args.append('--link') 3414 args.extend((revision, str(dir))) 3415 backend.null_cmd(args) 3416 return WorkingTree(dir)
3417
3418 -def get_patch(revision, dir):
3419 """Deprecated. 3420 3421 :rtype: `Changeset` 3422 :see: `Revision.get_patch` 3423 """ 3424 deprecated_callable(get_patch, Revision.get_patch) 3425 revision = _revision_param(revision) 3426 _arch.get_patch(revision, dir) 3427 return Changeset(dir)
3428 3429 3430 public( 3431 'iter_revision_libraries', 3432 'register_revision_library', 3433 'unregister_revision_library', 3434 ) 3435
3436 -def iter_revision_libraries():
3437 """Iterate over registered revision library directories. 3438 3439 :return: directory names of all registered revision libraries. 3440 :rtype: iterable of str 3441 """ 3442 return _arch.iter_revision_libraries()
3443
3444 -def register_revision_library(dirname):
3445 """Register an existing revision library directory. 3446 3447 :param dirname: absolute path name of existing user-writable directory. 3448 :type dirname: str 3449 :todo: create_revision_library which abstracts out revlib construction. 3450 :postcondition: ``dirname`` is present in `iter_revision_libraries` output. 3451 """ 3452 if not os.path.isabs(dirname): 3453 raise ValueError, "not an absolute path: %r" % dirname 3454 if not os.path.isdir(dirname): 3455 raise ValueError, "directory does not exist: %r" % dirname 3456 _arch.register_revision_library(dirname)
3457
3458 -def unregister_revision_library(dirname):
3459 """Unregister a revision library directory. 3460 3461 :param dirname: registered revision library directory. 3462 :type dirname: str 3463 :todo: delete_revision_library which abstracts out revlib destruction. 3464 :precondition: ``dirname`` is present in `iter_revision_libraries` output. 3465 :postcondition: ``dirname`` is not listed by `iter_revision_libraries`. 3466 """ 3467 if not os.path.isabs(dirname): 3468 raise ValueError, "not an absolute path: %r" % dirname 3469 _arch.unregister_revision_library(dirname)
3470 3471 3472 ### Parsing Arch names ### 3473 3474 public('NameParser') 3475 3476 from _nameparser import NameParser 3477 3478 3479 ### Searching in Archives ### 3480 3481 public( 3482 'filter_archive_logs', 3483 'filter_revisions', 3484 'grep_summary', 3485 'grep_summary_interactive', 3486 'suspected_move', 3487 'revisions_merging', 3488 'temphack', 3489 'revision_which_created', 3490 'last_revision', 3491 'map_name_id', 3492 ) 3493 3494 from _deprecated_helpers import * 3495