# # # patch "tracvc/mtn/automate.py" # from [b9dea1f617b549efdb8bb2ceeb414fb5c31668d0] # to [9a1badc84e1e884634783dbe0ce71e67a237dd6d] # # patch "tracvc/mtn/backend.py" # from [59a61d9bd8479507a3c52ef03fe8cf219edb6ef6] # to [701b96d3eafa85ca1b48d6680eb5b0c967cc74de] # ============================================================ --- tracvc/mtn/automate.py b9dea1f617b549efdb8bb2ceeb414fb5c31668d0 +++ tracvc/mtn/automate.py 9a1badc84e1e884634783dbe0ce71e67a237dd6d @@ -33,7 +33,15 @@ from tracvc.mtn.cache import memoize from tracvc.mtn.util import add_slash, to_unicode, natsort_key from tracvc.mtn.cache import memoize +class AutomateException(Exception): + """Thrown when the status of an automate command + is not null, indicating that there is no valid result.""" + def __init__(self, errno): + Exception.__init__(self) + self.errno = errno + + class Automate(object): """General interface to the 'automate stdio' command.""" @@ -107,7 +115,9 @@ class Automate(object): self._write_cmd(cmd, args) status, result = self._get_result() self.lock.release() - return status, result + if status == 0: + return result + raise AutomateException(status) class MTN(object): @@ -125,63 +135,40 @@ class MTN(object): def leaves(self): """Returns a list containing the current leaves.""" - status, result = self.automate.command("leaves") - if status == 0: - return result.splitlines() - else: return [] + return self.automate.command("leaves").splitlines() def heads(self, name): """Returns a list containing the head revs of branch 'name'.""" - status, result = self.automate.command("heads", name) - if status == 0: - return result.splitlines() - else: return [] + return self.automate.command("heads", name).splitlines() def children(self, rev): """Returns a list of the children of rev.""" - status, result = self.automate.command("children", rev) - if status == 0: - return result.splitlines() - else: return [] + return self.automate.command("children", rev).splitlines() - @memoize(get_cachespec) + @memoize(get_cachespec) # IGNORE:E0602 def parents(self, rev): """Returns a list of the parents of rev.""" - status, result = self.automate.command("parents", rev) - if status == 0: - return result.splitlines() - else: return [] + return self.automate.command("parents", rev).splitlines() def ancestors(self, revs): """Returns a list of the ancestors of rev.""" - status, result = self.automate.command("ancestors", *revs) #IGNORE:W0142 - if status == 0: - return result.splitlines() - else: return [] + return self.automate.command("ancestors", *revs).splitlines() #IGNORE:W0142 def toposort(self, revs): """Sorts revisions topologically.""" - status, result = self.automate.command("toposort", *revs) #IGNORE:W0142 - if status == 0: - return result.splitlines() - else: return [] + return self.automate.command("toposort", *revs).splitlines() #IGNORE:W0142 def all_revs(self): """Returns a list of all revs in the repository.""" - status, result = self.automate.command("select", '') - if status == 0: - return result.splitlines() - else: return [] + return self.automate.command("select", '').splitlines() def roots(self): """Returns a list of all root revisions.""" + # FIXME: we should have an automate command for that. if self.roots_cache: return self.roots_cache - status, result = self.automate.command("graph") - if status != 0: - return [] roots = [] - for line in result.splitlines(): + for line in self.automate.command("graph").splitlines(): rev_and_parents = line.split(' ') if len(rev_and_parents) == 1: roots.append(rev_and_parents[0]) @@ -190,27 +177,22 @@ class MTN(object): def select(self, selector): """Returns a list of revisions selected by the selector.""" - status, result = self.automate.command("select", - selector.encode('utf-8')) - if status == 0: - return result.splitlines() - return [] + return self.automate.command("select", + selector.encode('utf-8')).splitlines() - @memoize(get_cachespec) + @memoize(get_cachespec) # IGNORE:E0602 def manifest(self, rev): """ Returns a processed manifest for rev. The manifest is a dictionary: path -> (kind, file_id, attrs), with kind being 'file' or 'dir', and attrs being a dictionary attr_name -> attr_value.""" - status, result = self.automate.command("get_manifest_of", rev) + raw_manifest = self.automate.command("get_manifest_of", rev) manifest = {} - if status != 0: - return manifest - # stanzas have variable length, trigger on next 'path' or eof + # stanzas have variable length, trigger on next 'path' ... path, kind, content, attrs = None, None, None, {} - for key, values in basic_io.items(result): + for key, values in basic_io.items(raw_manifest): if key == 'dir' or key == 'file': if path: manifest[path] = (kind, content, attrs) @@ -220,21 +202,19 @@ class MTN(object): content = values[0] elif key == 'attrs': attrs[to_unicode(values[0])] = to_unicode(values[1]) - if path: + if path: # ... or eof manifest[path] = (kind, content, attrs) return manifest - @memoize(get_cachespec) + @memoize(get_cachespec) # IGNORE:E0602 def certs(self, rev): """Returns a dictionary of certs for rev. There might be more than one cert of the same name, so their values are collected in a list.""" - status, result = self.automate.command("certs", rev) + raw_certs = self.automate.command("certs", rev) certs = {} - if status != 0: - return certs - - for key, values in basic_io.items(result): + + for key, values in basic_io.items(raw_certs): if key == 'name': name = to_unicode(values[0]) elif key == 'value': @@ -244,26 +224,22 @@ class MTN(object): def file(self, file_id): """Returns the file contents for a given file id.""" - status, result = self.automate.command("get_file", file_id) - if status == 0: - return result + return self.automate.command("get_file", file_id) - @memoize(get_cachespec) + @memoize(get_cachespec) # IGNORE:E0602 def file_length(self, file_id): """Return the file length.""" return len(self.file(file_id)) - @memoize(get_cachespec) + @memoize(get_cachespec) # IGNORE:E0602 def changesets(self, rev): """Parses a textual changeset into an instance of the Changeset class.""" - status, result = self.automate.command("get_revision", rev) - if status != 0: - return {} + raw_changesets = self.automate.command("get_revision", rev) + changesets = [] - oldpath = None - for key, values in basic_io.items(result): + for key, values in basic_io.items(raw_changesets): if key == 'old_revision': # start a new changeset changeset = Changeset(values[0]) @@ -294,10 +270,8 @@ class MTN(object): def branchnames(self): """Returns a list of branch names.""" - status, result = self.automate.command("branches") - if status == 0: - return map(to_unicode, result.splitlines()) #IGNORE:W0141 - else: return [] + return map(to_unicode, #IGNORE:W0141 + self.automate.command("branches").splitlines()) def branches(self): """Returns a list of (branch, oneoftheheads) tuples. Caveat: @@ -322,12 +296,10 @@ class MTN(object): def tags(self): """Returns a list of tags and their revs.""" - status, result = self.automate.command("tags") - if status != 0: - return [] + raw_tags = self.automate.command("tags") tags = [] - - for key, values in basic_io.items(result): + + for key, values in basic_io.items(raw_tags): if key == 'tag': tag = to_unicode(values[0]) elif key == 'revision': @@ -341,13 +313,10 @@ class MTN(object): the specified revision. Currently returns an empty list for directories.""" - status, result = self.automate.command("get_content_changed", - rev, path[1:]) - if status != 0: - return [] + raw_content_changed = self.automate.command("get_content_changed", + rev, path[1:]) revs = [] - - for key, values in basic_io.items(result): + for key, values in basic_io.items(raw_content_changed): if key == 'content_mark': revs.append(values[0]) return revs ============================================================ --- tracvc/mtn/backend.py 59a61d9bd8479507a3c52ef03fe8cf219edb6ef6 +++ tracvc/mtn/backend.py 701b96d3eafa85ca1b48d6680eb5b0c967cc74de @@ -34,7 +34,7 @@ from time import strptime from trac import __version__ as trac_version from pkg_resources import parse_version from time import strptime -from tracvc.mtn.automate import MTN +from tracvc.mtn.automate import MTN, AutomateException from tracvc.mtn.util import get_oldpath, get_parent import re @@ -47,7 +47,6 @@ REVID_RULE = re.compile(r'^[0-9a-f]{40}$ DATE_RULE = re.compile(r'\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}') REVID_RULE = re.compile(r'^[0-9a-f]{40}$') - class MonotoneConnector(Component): """ Provides this plugin's functionality. @@ -194,6 +193,10 @@ class MonotoneRepository(Repository): Represents a Monotone repository. """ + def close(self): + """Close the connection to the repository.""" + pass + def __init__(self, path, log, binary, cachespec, revpropspec = None): Repository.__init__(self, 'mtn:%s' % path, None, log) self.mtn = MTN(path, log, binary, cachespec) @@ -274,6 +277,8 @@ class MonotoneRepository(Repository): dates.sort() return leaves[dates[-1]] + # FIXME: for next_rev|previous_rev should not return parents or children, + # but next or previous revisions according to commit time def previous_rev(self, rev): """ Return the revision immediately preceding the specified revision. @@ -405,7 +410,10 @@ class MonotoneNode(Node): def __init__(self, mtn, rev, path, manifest = None): self.mtn = mtn - self.manifest = manifest or self.mtn.manifest(rev) + try: + self.manifest = manifest or self.mtn.manifest(rev) + except AutomateException: + raise NoSuchChangeset(rev) if not path in self.manifest: raise NoSuchNode(path, rev) @@ -497,7 +505,7 @@ class MonotoneChangeset(Changeset): """ Represents the set of changes in one revision. """ - + # changesets are retrieved via MonotoneRepository.get_changeset() def __init__(self, mtn, rev, revpropspec = None): self.certs = mtn.certs(rev) self.messages = self.certs.get('changelog', ['-'])