From 1bbf068f28a6c05da563bc1224a4456f635227a4 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 5 Dec 2009 01:00:35 -0500 Subject: `be diff` raises UsageError if required revision control not possible. It had previously printed an message and exitted without error. --- becommands/diff.py | 71 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/becommands/diff.py b/becommands/diff.py index 8e6c0f8..f581ace 100644 --- a/becommands/diff.py +++ b/becommands/diff.py @@ -63,42 +63,45 @@ def execute(args, manipulate_encodings=True, restrict_file_access=False): bd = bugdir.BugDir(from_disk=True, manipulate_encodings=manipulate_encodings) if bd.vcs.versioned == False: - print "This directory is not revision-controlled." + raise cmdutil.UsageError("This directory is not revision-controlled.") + if options.dir == None: + if revision == None: # get the most recent revision + revision = bd.vcs.revision_id(-1) + old_bd = bd.duplicate_bugdir(revision) else: - if options.dir == None: - if revision == None: # get the most recent revision - revision = bd.vcs.revision_id(-1) - old_bd = bd.duplicate_bugdir(revision) + cwd = os.getcwd() + os.chdir(options.dir) + old_bd_current = bugdir.BugDir(from_disk=True, + manipulate_encodings=False) + if revision == None: # use the current working state + old_bd = old_bd_current else: - cwd = os.getcwd() - os.chdir(options.dir) - old_bd_current = bugdir.BugDir(from_disk=True, manipulate_encodings=False) - if revision == None: # use the current working state - old_bd = old_bd_current - else: - old_bd = old_bd_current.duplicate_bugdir(revision) - os.chdir(cwd) - d = diff.Diff(old_bd, bd) - tree = d.report_tree() + if old_bd_current.vcs.versioned == False: + raise cmdutil.UsageError("%s is not revision-controlled." + % options.dir) + old_bd = old_bd_current.duplicate_bugdir(revision) + os.chdir(cwd) + d = diff.Diff(old_bd, bd) + tree = d.report_tree() - uuids = [] - if options.all == True: - options.new = options.modified = options.removed = True - if options.new == True: - uuids.extend([c.name for c in tree.child_by_path("/bugs/new")]) - if options.modified == True: - uuids.extend([c.name for c in tree.child_by_path("/bugs/mod")]) - if options.removed == True: - uuids.extend([c.name for c in tree.child_by_path("/bugs/rem")]) - if (options.new or options.modified or options.removed) == True: - print "\n".join(uuids) - else : - rep = tree.report_string() - if rep != None: - print rep - bd.remove_duplicate_bugdir() - if options.dir != None and revision != None: - old_bd_current.remove_duplicate_bugdir() + uuids = [] + if options.all == True: + options.new = options.modified = options.removed = True + if options.new == True: + uuids.extend([c.name for c in tree.child_by_path("/bugs/new")]) + if options.modified == True: + uuids.extend([c.name for c in tree.child_by_path("/bugs/mod")]) + if options.removed == True: + uuids.extend([c.name for c in tree.child_by_path("/bugs/rem")]) + if (options.new or options.modified or options.removed) == True: + print "\n".join(uuids) + else : + rep = tree.report_string() + if rep != None: + print rep + bd.remove_duplicate_bugdir() + if options.dir != None and revision != None: + old_bd_current.remove_duplicate_bugdir() def get_parser(): parser = cmdutil.CmdOptionParser("be diff [options] REVISION") @@ -128,7 +131,7 @@ For Arch your specifier must be a fully-qualified revision name. Besides the standard summary output, you can use the options to output UUIDS for the different categories. This output can be used as the -input to 'be show' to get and understanding of the current status. +input to 'be show' to get an understanding of the current status. """ def help(): -- cgit From 281e98e998b4a1ec550c6702aee0eead003905be Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 5 Dec 2009 01:30:30 -0500 Subject: Replaced `be diff` options --new, --removed, --modified, and --all with --uuids. We'll be adding a --subscribe option which will select the bugs/changes we're interested in, which deprecates the selection portion of the old options. The new --uuids just selects the "bug uuid" output over the default "change summary" output. --- becommands/diff.py | 29 +++++++---------------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/becommands/diff.py b/becommands/diff.py index f581ace..aebbfdb 100644 --- a/becommands/diff.py +++ b/becommands/diff.py @@ -84,16 +84,11 @@ def execute(args, manipulate_encodings=True, restrict_file_access=False): d = diff.Diff(old_bd, bd) tree = d.report_tree() - uuids = [] - if options.all == True: - options.new = options.modified = options.removed = True - if options.new == True: - uuids.extend([c.name for c in tree.child_by_path("/bugs/new")]) - if options.modified == True: - uuids.extend([c.name for c in tree.child_by_path("/bugs/mod")]) - if options.removed == True: - uuids.extend([c.name for c in tree.child_by_path("/bugs/rem")]) - if (options.new or options.modified or options.removed) == True: + if options.uuids == True: + uuids = [] + bugs = tree.child_by_path("/bugs") + for bug_type in bugs: + uuids.extend([bug.name for bug in bug_type]) print "\n".join(uuids) else : rep = tree.report_string() @@ -105,20 +100,10 @@ def execute(args, manipulate_encodings=True, restrict_file_access=False): def get_parser(): parser = cmdutil.CmdOptionParser("be diff [options] REVISION") - # boolean options - bools = (("n", "new", "Print UUIDS for new bugs"), - ("m", "modified", "Print UUIDS for modified bugs"), - ("r", "removed", "Print UUIDS for removed bugs"), - ("a", "all", "Print UUIDS for all changed bugs")) - for s in bools: - attr = s[1].replace('-','_') - short = "-%c" % s[0] - long = "--%s" % s[1] - help = s[2] - parser.add_option(short, long, action="store_true", - default=False, dest=attr, help=help) parser.add_option("-d", "--dir", dest="dir", metavar="DIR", help="Compare with repository in DIR instead of the current directory.") + parser.add_option("-u", "--uuids", action="store_true", dest="uuids", + help="Only print the bug UUIDS.", default=False) return parser longhelp=""" -- cgit From 129c100046231ed15d2f16eaa90b5c01e41a442c Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 5 Dec 2009 01:58:41 -0500 Subject: Moved subscription types from becommands/subscribe.py to libbe/diff.py. --- becommands/subscribe.py | 105 ++++++++++------------------ interfaces/email/interactive/be-handle-mail | 8 +-- libbe/diff.py | 46 ++++++++++++ 3 files changed, 86 insertions(+), 73 deletions(-) diff --git a/becommands/subscribe.py b/becommands/subscribe.py index 051341b..4220c37 100644 --- a/becommands/subscribe.py +++ b/becommands/subscribe.py @@ -14,47 +14,12 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """(Un)subscribe to change notification""" -from libbe import cmdutil, bugdir, tree +from libbe import cmdutil, bugdir, tree, diff import os, copy __desc__ = __doc__ TAG="SUBSCRIBE:" -class SubscriptionType (tree.Tree): - """ - Trees of subscription types to allow users to select exactly what - notifications they want to subscribe to. - """ - def __init__(self, type_name, *args, **kwargs): - tree.Tree.__init__(self, *args, **kwargs) - self.type = type_name - def __str__(self): - return self.type - def __repr__(self): - return "" % str(self) - def string_tree(self, indent=0): - lines = [] - for depth,node in self.thread(): - lines.append("%s%s" % (" "*(indent+2*depth), node)) - return "\n".join(lines) - -BUGDIR_TYPE_NEW = SubscriptionType("new") -BUGDIR_TYPE_ALL = SubscriptionType("all", [BUGDIR_TYPE_NEW]) - -# same name as BUGDIR_TYPE_ALL for consistency -BUG_TYPE_ALL = SubscriptionType(str(BUGDIR_TYPE_ALL)) - -INVALID_TYPE = SubscriptionType("INVALID") - -class InvalidType (ValueError): - def __init__(self, type_name, type_root): - msg = "Invalid type %s for tree:\n%s" \ - % (type_name, type_root.string_tree(4)) - ValueError.__init__(self, msg) - self.type_name = type_name - self.type_root = type_root - - def execute(args, manipulate_encodings=True, restrict_file_access=False): """ >>> bd = bugdir.SimpleBugDir() @@ -127,19 +92,19 @@ def execute(args, manipulate_encodings=True, restrict_file_access=False): types = options.types.split(",") if len(args) == 0 or args[0] == "DIR": # directory-wide subscriptions - type_root = BUGDIR_TYPE_ALL + type_root = diff.BUGDIR_TYPE_ALL entity = bd entity_name = "bug directory" else: # bug-specific subscriptions - type_root = BUG_TYPE_ALL + type_root = diff.BUG_TYPE_ALL bug = bd.bug_from_shortname(args[0]) entity = bug entity_name = bug.uuid if options.list_all == True: entity_name = "anything in the bug directory" - types = [type_from_name(name, type_root, default=INVALID_TYPE, - default_ok=options.unsubscribe) + types = [diff.type_from_name(name, type_root, default=diff.INVALID_TYPE, + default_ok=options.unsubscribe) for name in types] estrs = entity.extra_strings if options.list == True or options.list_all == True: @@ -210,8 +175,8 @@ if you're just hacking away on your private repository, you'll known what's changed ;). This command just (un)sets the appropriate subscriptions, and leaves it up to each interface to perform the notification. -""" % (BUG_TYPE_ALL.string_tree(6), BUGDIR_TYPE_ALL.string_tree(6), - BUGDIR_TYPE_ALL) +""" % (diff.BUG_TYPE_ALL.string_tree(6), diff.BUGDIR_TYPE_ALL.string_tree(6), + diff.BUGDIR_TYPE_ALL) def help(): return get_parser().help_str() + longhelp @@ -227,7 +192,7 @@ def _parse_string(string, type_root): assert string.startswith(TAG), string string = string[len(TAG):] subscriber,types,servers = string.split("\t") - types = [type_from_name(name, type_root) for name in types.split(",")] + types = [diff.type_from_name(name, type_root) for name in types.split(",")] return (subscriber,types,servers.split(",")) def _get_subscriber(extra_strings, subscriber, type_root): @@ -240,16 +205,6 @@ def _get_subscriber(extra_strings, subscriber, type_root): # functions exposed to other modules -def type_from_name(name, type_root, default=None, default_ok=False): - if name == str(type_root): - return type_root - for t in type_root.traverse(): - if name == str(t): - return t - if default_ok: - return default - raise InvalidType(name, type_root) - def subscribe(extra_strings, subscriber, types, servers, type_root): args = _get_subscriber(extra_strings, subscriber, type_root) if args == None: # no match @@ -311,17 +266,22 @@ def get_subscribers(extra_strings, type, server, type_root, >>> def sgs(*args, **kwargs): ... return sorted(get_subscribers(*args, **kwargs)) >>> es = [] - >>> es = subscribe(es, "John Doe ", [BUGDIR_TYPE_ALL], ["a.com"], BUGDIR_TYPE_ALL) - >>> es = subscribe(es, "Jane Doe ", [BUGDIR_TYPE_NEW], ["*"], BUGDIR_TYPE_ALL) - >>> sgs(es, BUGDIR_TYPE_ALL, "a.com", BUGDIR_TYPE_ALL) + >>> es = subscribe(es, "John Doe ", [diff.BUGDIR_TYPE_ALL], + ... ["a.com"], diff.BUGDIR_TYPE_ALL) + >>> es = subscribe(es, "Jane Doe ", [diff.BUGDIR_TYPE_NEW], + ... ["*"], diff.BUGDIR_TYPE_ALL) + >>> sgs(es, diff.BUGDIR_TYPE_ALL, "a.com", diff.BUGDIR_TYPE_ALL) ['John Doe '] - >>> sgs(es, BUGDIR_TYPE_ALL, "a.com", BUGDIR_TYPE_ALL, match_descendant_types=True) + >>> sgs(es, diff.BUGDIR_TYPE_ALL, "a.com", diff.BUGDIR_TYPE_ALL, + ... match_descendant_types=True) ['Jane Doe ', 'John Doe '] - >>> sgs(es, BUGDIR_TYPE_ALL, "b.net", BUGDIR_TYPE_ALL, match_descendant_types=True) + >>> sgs(es, diff.BUGDIR_TYPE_ALL, "b.net", diff.BUGDIR_TYPE_ALL, + ... match_descendant_types=True) ['Jane Doe '] - >>> sgs(es, BUGDIR_TYPE_NEW, "a.com", BUGDIR_TYPE_ALL) + >>> sgs(es, diff.BUGDIR_TYPE_NEW, "a.com", diff.BUGDIR_TYPE_ALL) ['Jane Doe '] - >>> sgs(es, BUGDIR_TYPE_NEW, "a.com", BUGDIR_TYPE_ALL, match_ancestor_types=True) + >>> sgs(es, diff.BUGDIR_TYPE_NEW, "a.com", diff.BUGDIR_TYPE_ALL, + ... match_ancestor_types=True) ['Jane Doe ', 'John Doe '] """ for string in extra_strings: @@ -360,9 +320,12 @@ def get_bugdir_subscribers(bugdir, server): >>> bd = bugdir.SimpleBugDir(sync_with_disk=False) >>> a = bd.bug_from_shortname("a") - >>> bd.extra_strings = subscribe(bd.extra_strings, "John Doe ", [BUGDIR_TYPE_ALL], ["a.com"], BUGDIR_TYPE_ALL) - >>> bd.extra_strings = subscribe(bd.extra_strings, "Jane Doe ", [BUGDIR_TYPE_NEW], ["*"], BUGDIR_TYPE_ALL) - >>> a.extra_strings = subscribe(a.extra_strings, "John Doe ", [BUG_TYPE_ALL], ["a.com"], BUG_TYPE_ALL) + >>> bd.extra_strings = subscribe(bd.extra_strings, "John Doe ", + ... [diff.BUGDIR_TYPE_ALL], ["a.com"], diff.BUGDIR_TYPE_ALL) + >>> bd.extra_strings = subscribe(bd.extra_strings, "Jane Doe ", + ... [diff.BUGDIR_TYPE_NEW], ["*"], diff.BUGDIR_TYPE_ALL) + >>> a.extra_strings = subscribe(a.extra_strings, "John Doe ", + ... [diff.BUG_TYPE_ALL], ["a.com"], diff.BUG_TYPE_ALL) >>> subscribers = get_bugdir_subscribers(bd, "a.com") >>> subscribers["Jane Doe "]["DIR"] [] @@ -375,14 +338,18 @@ def get_bugdir_subscribers(bugdir, server): >>> bd.cleanup() """ subscribers = {} - for sub in get_subscribers(bugdir.extra_strings, BUGDIR_TYPE_ALL, server, - BUGDIR_TYPE_ALL, match_descendant_types=True): - i,s,ts,srvs = _get_subscriber(bugdir.extra_strings,sub,BUGDIR_TYPE_ALL) + for sub in get_subscribers(bugdir.extra_strings, diff.BUGDIR_TYPE_ALL, + server, diff.BUGDIR_TYPE_ALL, + match_descendant_types=True): + i,s,ts,srvs = _get_subscriber(bugdir.extra_strings, sub, + diff.BUGDIR_TYPE_ALL) subscribers[sub] = {"DIR":ts} for bug in bugdir: - for sub in get_subscribers(bug.extra_strings, BUG_TYPE_ALL, server, - BUG_TYPE_ALL, match_descendant_types=True): - i,s,ts,srvs = _get_subscriber(bug.extra_strings,sub,BUG_TYPE_ALL) + for sub in get_subscribers(bug.extra_strings, diff.BUG_TYPE_ALL, + server, diff.BUG_TYPE_ALL, + match_descendant_types=True): + i,s,ts,srvs = _get_subscriber(bug.extra_strings, sub, + diff.BUG_TYPE_ALL) if sub in subscribers: subscribers[sub][bug.uuid] = ts else: diff --git a/interfaces/email/interactive/be-handle-mail b/interfaces/email/interactive/be-handle-mail index 3b321cf..952c16d 100755 --- a/interfaces/email/interactive/be-handle-mail +++ b/interfaces/email/interactive/be-handle-mail @@ -685,21 +685,21 @@ class Message (object): ordered_subscriptions.extend(subscriptions.items()) for id,types in ordered_subscriptions: if id == "DIR": - if subscribe.BUGDIR_TYPE_ALL in types: + if libbe.diff.BUGDIR_TYPE_ALL in types: parts.append(diff_tree.report_or_none()) break # we've attached everything, so stop checking. - if subscribe.BUGDIR_TYPE_NEW in types: + if libbe.diff.BUGDIR_TYPE_NEW in types: new = diff_tree.child_by_path("/bugs/new") parts.append(new.report_or_none()) continue # move on to next id # if we get this far, id refers to a bug. - assert types == [subscribe.BUG_TYPE_ALL], types + assert types == [libbe.diff.BUG_TYPE_ALL], types if id not in bug_index: continue # no changes here, move on to next id type,bug_root = bug_index[id] if type == "added" \ and "DIR" in subscriptions \ - and subscriptions["DIR"] == subscribe.BUGDIR_TYPE_NEW: + and subscriptions["DIR"] == libbe.diff.BUGDIR_TYPE_NEW: # this info already attached at the DIR level continue # move on to next id parts.append(bug_root.report_or_none()) diff --git a/libbe/diff.py b/libbe/diff.py index c25f7a7..66d9f4b 100644 --- a/libbe/diff.py +++ b/libbe/diff.py @@ -27,6 +27,52 @@ if libbe.TESTING == True: import doctest +class SubscriptionType (tree.Tree): + """ + Trees of subscription types to allow users to select exactly what + notifications they want to subscribe to. + """ + def __init__(self, type_name, *args, **kwargs): + tree.Tree.__init__(self, *args, **kwargs) + self.type = type_name + def __str__(self): + return self.type + def __repr__(self): + return "" % str(self) + def string_tree(self, indent=0): + lines = [] + for depth,node in self.thread(): + lines.append("%s%s" % (" "*(indent+2*depth), node)) + return "\n".join(lines) + +BUGDIR_TYPE_NEW = SubscriptionType("new") +BUGDIR_TYPE_ALL = SubscriptionType("all", [BUGDIR_TYPE_NEW]) + +# same name as BUGDIR_TYPE_ALL for consistency +BUG_TYPE_ALL = SubscriptionType(str(BUGDIR_TYPE_ALL)) + +INVALID_TYPE = SubscriptionType("INVALID") + +class InvalidType (ValueError): + def __init__(self, type_name, type_root): + msg = "Invalid type %s for tree:\n%s" \ + % (type_name, type_root.string_tree(4)) + ValueError.__init__(self, msg) + self.type_name = type_name + self.type_root = type_root + +def type_from_name(name, type_root, default=None, default_ok=False): + if name == str(type_root): + return type_root + for t in type_root.traverse(): + if name == str(t): + return t + if default_ok: + return default + raise InvalidType(name, type_root) + + + class DiffTree (tree.Tree): """ A tree holding difference data for easy report generation. -- cgit From 1f59c7ef9019879d0b5e407492e4a6e04c5a29cc Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 5 Dec 2009 03:22:25 -0500 Subject: Added subscriptions option to diff.Diff.report_tree(). Also added diff.BUGDIR_ID to avoid lots of magic 'DIR' definitions, and added diff.Subscription class to make the old (id, type) tuples a bit more elegant. --- becommands/subscribe.py | 5 ++- libbe/diff.py | 97 +++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 80 insertions(+), 22 deletions(-) diff --git a/becommands/subscribe.py b/becommands/subscribe.py index 4220c37..3f4998e 100644 --- a/becommands/subscribe.py +++ b/becommands/subscribe.py @@ -163,7 +163,7 @@ you of changes, although there is no way to guarantee this behavior. Available TYPES: For bugs: %s - For DIR : + For %s: %s For unsubscription, any listed SERVERS and TYPES are removed from your @@ -175,7 +175,8 @@ if you're just hacking away on your private repository, you'll known what's changed ;). This command just (un)sets the appropriate subscriptions, and leaves it up to each interface to perform the notification. -""" % (diff.BUG_TYPE_ALL.string_tree(6), diff.BUGDIR_TYPE_ALL.string_tree(6), +""" % (diff.BUG_TYPE_ALL.string_tree(6), diff.BUGDIR_ID, + diff.BUGDIR_TYPE_ALL.string_tree(6), diff.BUGDIR_TYPE_ALL) def help(): diff --git a/libbe/diff.py b/libbe/diff.py index 66d9f4b..b5384a8 100644 --- a/libbe/diff.py +++ b/libbe/diff.py @@ -19,6 +19,7 @@ """Compare two bug trees.""" import difflib +import types import libbe from libbe import bugdir, bug, settings_object, tree @@ -45,6 +46,7 @@ class SubscriptionType (tree.Tree): lines.append("%s%s" % (" "*(indent+2*depth), node)) return "\n".join(lines) +BUGDIR_ID = "DIR" BUGDIR_TYPE_NEW = SubscriptionType("new") BUGDIR_TYPE_ALL = SubscriptionType("all", [BUGDIR_TYPE_NEW]) @@ -71,7 +73,32 @@ def type_from_name(name, type_root, default=None, default_ok=False): return default raise InvalidType(name, type_root) - +class Subscription (object): + """ + >>> subscriptions = [Subscription('XYZ', 'all', type_root=BUG_TYPE_ALL), + ... Subscription('DIR', 'new', type_root=BUGDIR_TYPE_ALL), + ... Subscription('ABC', BUG_TYPE_ALL),] + >>> print sorted(subscriptions) + [, , ] + """ + def __init__(self, id, subscription_type, **kwargs): + if type(subscription_type) in types.StringTypes: + subscription_type = type_from_name(subscription_type, **kwargs) + self.id = id + self.type = subscription_type + def __cmp__(self, other): + for attr in 'id', 'type': + value = cmp(getattr(self, attr), getattr(other, attr)) + if value != 0: + if self.id == BUGDIR_ID: + return -1 + elif other.id == BUGDIR_ID: + return 1 + return value + def __str__(self): + return str(self.type) + def __repr__(self): + return "" % (self.id, self.type) class DiffTree (tree.Tree): """ @@ -233,6 +260,19 @@ class Diff (object): New comments: from John Doe on Thu, 01 Jan 1970 00:00:00 +0000 I'm closing this bug... + + You can also limit the report generation by providing a list of + subscriptions. + + >>> subscriptions = [Subscription('DIR', BUGDIR_TYPE_NEW), + ... Subscription('b', BUG_TYPE_ALL)] + >>> r = d.report_tree(subscriptions) + >>> print r.report_string() + New bugs: + c:om: Bug C + Removed bugs: + b:cm: Bug B + >>> bd.cleanup() """ def __init__(self, old_bugdir, new_bugdir): @@ -241,7 +281,7 @@ class Diff (object): # data assembly methods - def _changed_bugs(self): + def _changed_bugs(self, subscriptions): """ Search for differences in all bugs between .old_bugdir and .new_bugdir. Returns @@ -250,33 +290,48 @@ class Diff (object): removed bugs respectively. modified_bugs is a list of (old_bug,new_bug) pairs. """ - if hasattr(self, "__changed_bugs"): - return self.__changed_bugs + bugdir_types = [s.type for s in subscriptions if s.id == BUGDIR_ID] + if BUGDIR_TYPE_ALL in bugdir_types: + new_uuids = list(self.new_bugdir.uuids()) + old_uuids = list(self.old_bugdir.uuids()) + elif BUGDIR_TYPE_NEW in bugdir_types: + new_uuids = list(self.new_bugdir.uuids()) + old_uuids = [] + subscribed_bugs = [s.id for s in subscriptions + if BUG_TYPE_ALL.has_descendant( \ + s.type, match_self=True)] + new_uuids.extend([s for s in subscribed_bugs + if self.new_bugdir.has_bug(s)]) + new_uuids = sorted(set(new_uuids)) + old_uuids.extend([s for s in subscribed_bugs + if self.old_bugdir.has_bug(s)]) + old_uuids = sorted(set(old_uuids)) added = [] removed = [] modified = [] - for uuid in self.new_bugdir.uuids(): + for uuid in new_uuids: new_bug = self.new_bugdir.bug_from_uuid(uuid) try: old_bug = self.old_bugdir.bug_from_uuid(uuid) except KeyError: added.append(new_bug) - else: + continue + if BUGDIR_TYPE_ALL in bugdir_types \ + or uuid in subscribed_bugs: if old_bug.sync_with_disk == True: old_bug.load_comments() if new_bug.sync_with_disk == True: new_bug.load_comments() if old_bug != new_bug: modified.append((old_bug, new_bug)) - for uuid in self.old_bugdir.uuids(): + for uuid in old_uuids: if not self.new_bugdir.has_bug(uuid): old_bug = self.old_bugdir.bug_from_uuid(uuid) removed.append(old_bug) added.sort() removed.sort() modified.sort(self._bug_modified_cmp) - self.__changed_bugs = (added, modified, removed) - return self.__changed_bugs + return (added, modified, removed) def _bug_modified_cmp(self, left, right): return cmp(left[1], right[1]) def _changed_comments(self, old, new): @@ -348,25 +403,28 @@ class Diff (object): # report generation methods - def report_tree(self, diff_tree=DiffTree): + def report_tree(self, subscriptions=None, diff_tree=DiffTree): """ Pretty bare to make it easy to adjust to specific cases. You can pass in a DiffTree subclass via diff_tree to override the default report assembly process. """ - if hasattr(self, "__report_tree"): - return self.__report_tree + if subscriptions == None: + subscriptions = [Subscription(BUGDIR_ID, BUGDIR_TYPE_ALL)] bugdir_settings = sorted(self.new_bugdir.settings_properties) bugdir_settings.remove("vcs_name") # tweaked by bugdir.duplicate_bugdir root = diff_tree("bugdir") - bugdir_attribute_changes = self._bugdir_attribute_changes() - if len(bugdir_attribute_changes) > 0: - bugdir = diff_tree("settings", bugdir_attribute_changes, - self.bugdir_attribute_change_string) - root.append(bugdir) + bugdir_subscriptions = [s.type for s in subscriptions + if s.id == BUGDIR_ID] + if BUGDIR_TYPE_ALL in bugdir_subscriptions: + bugdir_attribute_changes = self._bugdir_attribute_changes() + if len(bugdir_attribute_changes) > 0: + bugdir = diff_tree("settings", bugdir_attribute_changes, + self.bugdir_attribute_change_string) + root.append(bugdir) bug_root = diff_tree("bugs") root.append(bug_root) - add,mod,rem = self._changed_bugs() + add,mod,rem = self._changed_bugs(subscriptions) bnew = diff_tree("new", "New bugs:", requires_children=True) bug_root.append(bnew) for bug in add: @@ -416,8 +474,7 @@ class Diff (object): self.comment_body_change_string) c.append(cbody) cr.extend([cnew, crem, cmod]) - self.__report_tree = root - return self.__report_tree + return root # change data -> string methods. # Feel free to play with these in subclasses. -- cgit From f7f0d9c959aee752298fdfe7a73939cf4c09fda5 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 5 Dec 2009 03:32:06 -0500 Subject: Adjusted diff.Subscription.__init__() to guess type_root if required. --- libbe/diff.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/libbe/diff.py b/libbe/diff.py index b5384a8..73db13d 100644 --- a/libbe/diff.py +++ b/libbe/diff.py @@ -75,13 +75,18 @@ def type_from_name(name, type_root, default=None, default_ok=False): class Subscription (object): """ - >>> subscriptions = [Subscription('XYZ', 'all', type_root=BUG_TYPE_ALL), - ... Subscription('DIR', 'new', type_root=BUGDIR_TYPE_ALL), + >>> subscriptions = [Subscription('XYZ', 'all'), + ... Subscription('DIR', 'new'), ... Subscription('ABC', BUG_TYPE_ALL),] >>> print sorted(subscriptions) [, , ] """ def __init__(self, id, subscription_type, **kwargs): + if 'type_root' not in kwargs: + if id == BUGDIR_ID: + kwargs['type_root'] = BUGDIR_TYPE_ALL + else: + kwargs['type_root'] = BUG_TYPE_ALL if type(subscription_type) in types.StringTypes: subscription_type = type_from_name(subscription_type, **kwargs) self.id = id -- cgit From 06f4869e74ed800156fb4c46116741a17fc04ef1 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 5 Dec 2009 03:53:26 -0500 Subject: Added BUGDIR_TYPE_MOD and BUGDIR_TYPE_REM to libbe.diff. Now you can subscribe to only hear about modified bugs or only about removed bugs. Kindof odd for a general subscription, but possibly useful as an argument to the upcoming `be diff --subscribe`, e.g. be diff --subscribe DIR:mod which would replace the old be diff --modified --- libbe/diff.py | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/libbe/diff.py b/libbe/diff.py index 73db13d..3122fe8 100644 --- a/libbe/diff.py +++ b/libbe/diff.py @@ -48,7 +48,10 @@ class SubscriptionType (tree.Tree): BUGDIR_ID = "DIR" BUGDIR_TYPE_NEW = SubscriptionType("new") -BUGDIR_TYPE_ALL = SubscriptionType("all", [BUGDIR_TYPE_NEW]) +BUGDIR_TYPE_MOD = SubscriptionType("mod") +BUGDIR_TYPE_REM = SubscriptionType("rem") +BUGDIR_TYPE_ALL = SubscriptionType("all", + [BUGDIR_TYPE_NEW, BUGDIR_TYPE_MOD, BUGDIR_TYPE_REM]) # same name as BUGDIR_TYPE_ALL for consistency BUG_TYPE_ALL = SubscriptionType(str(BUGDIR_TYPE_ALL)) @@ -296,12 +299,16 @@ class Diff (object): (old_bug,new_bug) pairs. """ bugdir_types = [s.type for s in subscriptions if s.id == BUGDIR_ID] - if BUGDIR_TYPE_ALL in bugdir_types: - new_uuids = list(self.new_bugdir.uuids()) - old_uuids = list(self.old_bugdir.uuids()) - elif BUGDIR_TYPE_NEW in bugdir_types: - new_uuids = list(self.new_bugdir.uuids()) - old_uuids = [] + new_uuids = [] + old_uuids = [] + for bd_type in [BUGDIR_TYPE_ALL, BUGDIR_TYPE_NEW, BUGDIR_TYPE_MOD]: + if bd_type in bugdir_types: + new_uuids = list(self.new_bugdir.uuids()) + break + for bd_type in [BUGDIR_TYPE_ALL, BUGDIR_TYPE_REM]: + if bd_type in bugdir_types: + old_uuids = list(self.old_bugdir.uuids()) + break subscribed_bugs = [s.id for s in subscriptions if BUG_TYPE_ALL.has_descendant( \ s.type, match_self=True)] @@ -319,9 +326,13 @@ class Diff (object): try: old_bug = self.old_bugdir.bug_from_uuid(uuid) except KeyError: - added.append(new_bug) + if BUGDIR_TYPE_ALL in bugdir_types \ + or BUGDIR_TYPE_NEW in bugdir_types \ + or uuid in subscribed_bugs: + added.append(new_bug) continue if BUGDIR_TYPE_ALL in bugdir_types \ + or BUGDIR_TYPE_MOD in bugdir_types \ or uuid in subscribed_bugs: if old_bug.sync_with_disk == True: old_bug.load_comments() -- cgit From e95de5d97dc05ce5dbb9a553d5e42e437ceccbbf Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 5 Dec 2009 03:55:55 -0500 Subject: Added --subscribe option to `be diff` --- becommands/diff.py | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/becommands/diff.py b/becommands/diff.py index aebbfdb..5a94462 100644 --- a/becommands/diff.py +++ b/becommands/diff.py @@ -40,15 +40,18 @@ def execute(args, manipulate_encodings=True, restrict_file_access=False): Changed bug settings: status: open -> closed >>> if bd.vcs.versioned == True: - ... execute(["--modified", original], manipulate_encodings=False) + ... execute(["--subscribe", "DIR:mod", "--uuids", original], + ... manipulate_encodings=False) ... else: ... print "a" a >>> if bd.vcs.versioned == False: ... execute([original], manipulate_encodings=False) ... else: - ... print "This directory is not revision-controlled." - This directory is not revision-controlled. + ... raise cmdutil.UsageError('This directory is not revision-controlled.') + Traceback (most recent call last): + ... + UsageError: This directory is not revision-controlled. >>> bd.cleanup() """ parser = get_parser() @@ -59,11 +62,22 @@ def execute(args, manipulate_encodings=True, restrict_file_access=False): if len(args) == 1: revision = args[0] if len(args) > 1: - raise cmdutil.UsageError("Too many arguments.") + raise cmdutil.UsageError('Too many arguments.') + if options.subscribe == None: + subscriptions = [diff.Subscription(diff.BUGDIR_ID, + diff.BUGDIR_TYPE_ALL)] + else: + subscriptions = [] + for subscription in options.subscribe.split(','): + fields = subscription.split(':') + if len(fields) != 2: + raise cmdutil.UsageError('Invalid subscription "%s", should be ID:TYPE') + id,type = fields + subscriptions.append(diff.Subscription(id, type)) bd = bugdir.BugDir(from_disk=True, manipulate_encodings=manipulate_encodings) if bd.vcs.versioned == False: - raise cmdutil.UsageError("This directory is not revision-controlled.") + raise cmdutil.UsageError('This directory is not revision-controlled.') if options.dir == None: if revision == None: # get the most recent revision revision = bd.vcs.revision_id(-1) @@ -77,19 +91,19 @@ def execute(args, manipulate_encodings=True, restrict_file_access=False): old_bd = old_bd_current else: if old_bd_current.vcs.versioned == False: - raise cmdutil.UsageError("%s is not revision-controlled." + raise cmdutil.UsageError('%s is not revision-controlled.' % options.dir) old_bd = old_bd_current.duplicate_bugdir(revision) os.chdir(cwd) d = diff.Diff(old_bd, bd) - tree = d.report_tree() + tree = d.report_tree(subscriptions) if options.uuids == True: uuids = [] - bugs = tree.child_by_path("/bugs") + bugs = tree.child_by_path('/bugs') for bug_type in bugs: uuids.extend([bug.name for bug in bug_type]) - print "\n".join(uuids) + print '\n'.join(uuids) else : rep = tree.report_string() if rep != None: @@ -102,6 +116,8 @@ def get_parser(): parser = cmdutil.CmdOptionParser("be diff [options] REVISION") parser.add_option("-d", "--dir", dest="dir", metavar="DIR", help="Compare with repository in DIR instead of the current directory.") + parser.add_option("-s", "--subscribe", dest="subscribe", metavar="SUBSCRIPTION", + help="Only print changes matching SUBSCRIPTION, subscription is a comma-separ\ated list of ID:TYPE tuples. See `be subscribe --help` for descriptions of ID and TYPE.") parser.add_option("-u", "--uuids", action="store_true", dest="uuids", help="Only print the bug UUIDS.", default=False) return parser -- cgit From fbb8504a6c0438e90b046e44a60608159f4e3f63 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 5 Dec 2009 04:11:39 -0500 Subject: Created diff.subscriptions_from_string() --- becommands/diff.py | 16 +++++----------- libbe/diff.py | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/becommands/diff.py b/becommands/diff.py index 5a94462..e2ff052 100644 --- a/becommands/diff.py +++ b/becommands/diff.py @@ -63,17 +63,11 @@ def execute(args, manipulate_encodings=True, restrict_file_access=False): revision = args[0] if len(args) > 1: raise cmdutil.UsageError('Too many arguments.') - if options.subscribe == None: - subscriptions = [diff.Subscription(diff.BUGDIR_ID, - diff.BUGDIR_TYPE_ALL)] - else: - subscriptions = [] - for subscription in options.subscribe.split(','): - fields = subscription.split(':') - if len(fields) != 2: - raise cmdutil.UsageError('Invalid subscription "%s", should be ID:TYPE') - id,type = fields - subscriptions.append(diff.Subscription(id, type)) + try: + subscriptions = diff.subscriptions_from_string( + options.subscribe) + except ValueError, e: + raise cmdutil.UsageError(e.msg) bd = bugdir.BugDir(from_disk=True, manipulate_encodings=manipulate_encodings) if bd.vcs.versioned == False: diff --git a/libbe/diff.py b/libbe/diff.py index 3122fe8..e947021 100644 --- a/libbe/diff.py +++ b/libbe/diff.py @@ -108,6 +108,29 @@ class Subscription (object): def __repr__(self): return "" % (self.id, self.type) +def subscriptions_from_string(string=None, subscription_sep=',', id_sep=':'): + """ + >>> subscriptions_from_string(None) + [] + >>> subscriptions_from_string('DIR:new,DIR:rem,ABC:all,XYZ:all') + [, , , ] + >>> subscriptions_from_string('DIR::new') + Traceback (most recent call last): + ... + ValueError: Invalid subscription "DIR::new", should be ID:TYPE + """ + if string == None: + return [Subscription(BUGDIR_ID, BUGDIR_TYPE_ALL)] + subscriptions = [] + for subscription in string.split(','): + fields = subscription.split(':') + if len(fields) != 2: + raise ValueError('Invalid subscription "%s", should be ID:TYPE' + % subscription) + id,type = fields + subscriptions.append(Subscription(id, type)) + return subscriptions + class DiffTree (tree.Tree): """ A tree holding difference data for easy report generation. -- cgit From 3cf0394832176a18f658ef3a89521bcccd57cb9e Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 5 Dec 2009 04:21:04 -0500 Subject: More 'DIR'->diff.BUGDIR_ID updates --- becommands/diff.py | 4 ++-- becommands/subscribe.py | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/becommands/diff.py b/becommands/diff.py index e2ff052..2cff537 100644 --- a/becommands/diff.py +++ b/becommands/diff.py @@ -40,7 +40,7 @@ def execute(args, manipulate_encodings=True, restrict_file_access=False): Changed bug settings: status: open -> closed >>> if bd.vcs.versioned == True: - ... execute(["--subscribe", "DIR:mod", "--uuids", original], + ... execute(["--subscribe", "%(bugdir_id)s:mod", "--uuids", original], ... manipulate_encodings=False) ... else: ... print "a" @@ -53,7 +53,7 @@ def execute(args, manipulate_encodings=True, restrict_file_access=False): ... UsageError: This directory is not revision-controlled. >>> bd.cleanup() - """ + """ % {'bugdir_id':diff.BUGDIR_ID} parser = get_parser() options, args = parser.parse_args(args) cmdutil.default_complete(options, args, parser) diff --git a/becommands/subscribe.py b/becommands/subscribe.py index 3f4998e..19aac53 100644 --- a/becommands/subscribe.py +++ b/becommands/subscribe.py @@ -91,7 +91,7 @@ def execute(args, manipulate_encodings=True, restrict_file_access=False): servers = options.servers.split(",") types = options.types.split(",") - if len(args) == 0 or args[0] == "DIR": # directory-wide subscriptions + if len(args) == 0 or args[0] == diff.BUGDIR_ID: # directory-wide subscriptions type_root = diff.BUGDIR_TYPE_ALL entity = bd entity_name = "bug directory" @@ -314,7 +314,7 @@ def get_bugdir_subscribers(bugdir, server): Returns a dict of dicts: subscribers[user][id] = types where id is either a bug.uuid (in the case of a bug subscription) - or "DIR" (in the case of a bugdir subscription). + or "%(bugdir_id)s" (in the case of a bugdir subscription). Only checks bugs that are currently in memory, so you might want to call bugdir.load_all_bugs() first. @@ -328,16 +328,16 @@ def get_bugdir_subscribers(bugdir, server): >>> a.extra_strings = subscribe(a.extra_strings, "John Doe ", ... [diff.BUG_TYPE_ALL], ["a.com"], diff.BUG_TYPE_ALL) >>> subscribers = get_bugdir_subscribers(bd, "a.com") - >>> subscribers["Jane Doe "]["DIR"] + >>> subscribers["Jane Doe "]["%(bugdir_id)s"] [] - >>> subscribers["John Doe "]["DIR"] + >>> subscribers["John Doe "]["%(bugdir_id)s"] [] >>> subscribers["John Doe "]["a"] [] >>> get_bugdir_subscribers(bd, "b.net") - {'Jane Doe ': {'DIR': []}} + {'Jane Doe ': {'%(bugdir_id)s': []}} >>> bd.cleanup() - """ + """ % {'bugdir_id':diff.BUGDIR_ID} subscribers = {} for sub in get_subscribers(bugdir.extra_strings, diff.BUGDIR_TYPE_ALL, server, diff.BUGDIR_TYPE_ALL, -- cgit From 167a8d2ae883fecf4e9d375e333e406dc723ef3b Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 5 Dec 2009 05:38:48 -0500 Subject: Added libbe.diff.Diff.full_report() for speed with several subscription lists. Now report_tree() returns an appropriately .masked version of the cached full report, which is much faster than recomputing a new diff tree from scratch. Also fixed bug in libbe.diff.DiffTree.report() where .requires_children=True was exposing nodes with children, when it should (and now does) only expose nodes with _unmasked_ children. --- libbe/diff.py | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/libbe/diff.py b/libbe/diff.py index e947021..fb2b249 100644 --- a/libbe/diff.py +++ b/libbe/diff.py @@ -215,7 +215,8 @@ class DiffTree (tree.Tree): if self.masked == True: return None data_part = self.data_part(depth) - if self.requires_children == True and len(self) == 0: + if self.requires_children == True \ + and len([c for c in self if c.masked == False]) == 0: pass else: self.join(root, parent, data_part) @@ -304,6 +305,20 @@ class Diff (object): Removed bugs: b:cm: Bug B + While sending subscriptions to report_tree() makes the report + generation more efficient (because you may not need to compare + _all_ the bugs, etc.), sometimes you will have several sets of + subscriptions. In that case, it's better to run full_report() + first, and then use report_tree() to avoid redundant comparisons. + + >>> d.full_report() + >>> print d.report_tree([subscriptions[0]]).report_string() + New bugs: + c:om: Bug C + >>> print d.report_tree([subscriptions[1]]).report_string() + Removed bugs: + b:cm: Bug B + >>> bd.cleanup() """ def __init__(self, old_bugdir, new_bugdir): @@ -442,12 +457,62 @@ class Diff (object): # report generation methods - def report_tree(self, subscriptions=None, diff_tree=DiffTree): + def full_report(self, diff_tree=DiffTree): + """ + Generate a full report for efficiency if you'll be using + .report_tree() with several sets of subscriptions. + """ + self._cached_full_report = self.report_tree(diff_tree=diff_tree, + allow_cached=False) + self._cached_full_report_diff_tree = diff_tree + def _sub_report(self, subscriptions): + """ + Return ._cached_full_report masked for subscriptions. + """ + root = self._cached_full_report + bugdir_types = [s.type for s in subscriptions if s.id == BUGDIR_ID] + subscribed_bugs = [s.id for s in subscriptions + if BUG_TYPE_ALL.has_descendant( \ + s.type, match_self=True)] + selected_by_bug = [node.name + for node in root.child_by_path('bugdir/bugs')] + if BUGDIR_TYPE_ALL in bugdir_types: + for node in root.traverse(): + node.masked = False + selected_by_bug = [] + else: + node = root.child_by_path('bugdir/settings') + node.masked = True + for name,type in (('new', BUGDIR_TYPE_NEW), + ('mod', BUGDIR_TYPE_MOD), + ('rem', BUGDIR_TYPE_REM)): + if type in bugdir_types: + bugs = root.child_by_path('bugdir/bugs/%s' % name) + for bug_node in bugs: + for node in bug_node.traverse(): + node.masked = False + selected_by_bug.remove(name) + for name in selected_by_bug: + bugs = root.child_by_path('bugdir/bugs/%s' % name) + for bug_node in bugs: + if bug_node.name in subscribed_bugs: + for node in bug_node.traverse(): + node.masked = False + else: + for node in bug_node.traverse(): + node.masked = True + return root + def report_tree(self, subscriptions=None, diff_tree=DiffTree, + allow_cached=True): """ Pretty bare to make it easy to adjust to specific cases. You can pass in a DiffTree subclass via diff_tree to override the default report assembly process. """ + if allow_cached == True \ + and hasattr(self, '_cached_full_report') \ + and diff_tree == self._cached_full_report_diff_tree: + return self._sub_report(subscriptions) if subscriptions == None: subscriptions = [Subscription(BUGDIR_ID, BUGDIR_TYPE_ALL)] bugdir_settings = sorted(self.new_bugdir.settings_properties) -- cgit From cc58188259e36193c3174fbb55e37c790382a7ea Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 5 Dec 2009 07:41:50 -0500 Subject: Use new libbe.diff.Diff.report_tree(subscriptions) in be-handle-mail. This makes Message.subscriber_emails() much cleaner. Also fix libbe.diff.Diff._sub_report() to handle missing 'bugdir/settings'. Added libbe.diff.SubscriptionType.__cmp__ so that SubscriptionType('all') == SubscriptionType('all') This is important when comparing the types returned by becommands.subscribe.get_bugdir_subscribers() with the libbe.diff.*_TYPE_* types. --- interfaces/email/interactive/be-handle-mail | 67 ++++++++--------------------- libbe/diff.py | 13 ++++-- 2 files changed, 26 insertions(+), 54 deletions(-) diff --git a/interfaces/email/interactive/be-handle-mail b/interfaces/email/interactive/be-handle-mail index 952c16d..10f6884 100755 --- a/interfaces/email/interactive/be-handle-mail +++ b/interfaces/email/interactive/be-handle-mail @@ -306,6 +306,8 @@ class DiffTree (libbe.diff.DiffTree): """ def report_or_none(self): report = self.report() + if report == None: + return None payload = report.get_payload() if payload == None or len(payload) == 0: return None @@ -315,7 +317,7 @@ class DiffTree (libbe.diff.DiffTree): if report == None: return "No changes" else: - return send_pgp_mime.flatten(self.report(), to_unicode=True) + return send_pgp_mime.flatten(report, to_unicode=True) def make_root(self): return MIMEMultipart() def join(self, root, parent, data_part): @@ -658,64 +660,29 @@ class Message (object): bd.load_all_bugs() subscribers = subscribe.get_bugdir_subscribers(bd, THIS_SERVER) - if len(subscribers) == 0: - return [] + return [] + for subscriber,subscriptions in subscribers.items(): + subscribers[subscriber] = [] + for id,types in subscriptions.items(): + for type in types: + subscribers[subscriber].append( + libbe.diff.Subscription(id,type)) before_bd, after_bd = self._get_before_and_after_bugdirs(bd, previous_revision) diff = Diff(before_bd, after_bd) - diff_tree = diff.report_tree(diff_tree=DiffTree) - bug_index = {} - for child in diff_tree.child_by_path("/bugs/new"): - bug_index[child.name] = ("added", child) - for child in diff_tree.child_by_path("/bugs/mod"): - bug_index[child.name] = ("modified", child) - for child in diff_tree.child_by_path("/bugs/rem"): - bug_index[child.name] = ("removed", child) + diff.full_report(diff_tree=DiffTree) header = self._subscriber_header(bd, previous_revision) emails = [] for subscriber,subscriptions in subscribers.items(): header.replace_header("to", subscriber) - parts = [] - if "DIR" in subscriptions: # make sure we check the DIR level first - ordered_subscriptions = [("DIR", subscriptions.pop("DIR"))] - else: - ordered_subscriptions = [] - ordered_subscriptions.extend(subscriptions.items()) - for id,types in ordered_subscriptions: - if id == "DIR": - if libbe.diff.BUGDIR_TYPE_ALL in types: - parts.append(diff_tree.report_or_none()) - break # we've attached everything, so stop checking. - if libbe.diff.BUGDIR_TYPE_NEW in types: - new = diff_tree.child_by_path("/bugs/new") - parts.append(new.report_or_none()) - continue # move on to next id - # if we get this far, id refers to a bug. - assert types == [libbe.diff.BUG_TYPE_ALL], types - if id not in bug_index: - continue # no changes here, move on to next id - type,bug_root = bug_index[id] - if type == "added" \ - and "DIR" in subscriptions \ - and subscriptions["DIR"] == libbe.diff.BUGDIR_TYPE_NEW: - # this info already attached at the DIR level - continue # move on to next id - parts.append(bug_root.report_or_none()) - parts = [p for p in parts if p != None] - if len(parts) == 0: - continue # no email to this subscriber - elif len(parts) == 1: - root = parts[0] - else: # join subscription parts into a single body - root = MIMEMultipart() - root[u"Content-Description"] = u"Multiple subscription trees." - for part in parts: - root.attach(part) - emails.append(send_pgp_mime.attach_root(header, root)) - if LOGFILE != None: - LOGFILE.write(u"Preparing to notify %s of changes\n" % subscriber) + report = diff.report_tree(subscriptions, diff_tree=DiffTree) + root = report.report_or_none() + if root != None: + emails.append(send_pgp_mime.attach_root(header, root)) + if LOGFILE != None: + LOGFILE.write(u"Preparing to notify %s of changes\n" % subscriber) return emails def _get_before_and_after_bugdirs(self, bd, previous_revision=None): if previous_revision == None: diff --git a/libbe/diff.py b/libbe/diff.py index fb2b249..46b8bda 100644 --- a/libbe/diff.py +++ b/libbe/diff.py @@ -38,6 +38,8 @@ class SubscriptionType (tree.Tree): self.type = type_name def __str__(self): return self.type + def __cmp__(self, other): + return cmp(self.type, other.type) def __repr__(self): return "" % str(self) def string_tree(self, indent=0): @@ -222,8 +224,8 @@ class DiffTree (tree.Tree): self.join(root, parent, data_part) if data_part != None: depth += 1 - for child in self: - child.report(root, self, depth) + for child in self: + root = child.report(root, self, depth) return root def make_root(self): return [] @@ -481,8 +483,11 @@ class Diff (object): node.masked = False selected_by_bug = [] else: - node = root.child_by_path('bugdir/settings') - node.masked = True + try: + node = root.child_by_path('bugdir/settings') + node.masked = True + except KeyError: + pass for name,type in (('new', BUGDIR_TYPE_NEW), ('mod', BUGDIR_TYPE_MOD), ('rem', BUGDIR_TYPE_REM)): -- cgit From e260fa7ed1e501404c75cdbe3d7461f29cd6c3e1 Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 5 Dec 2009 08:08:09 -0500 Subject: Adjust libbe.diff.DiffTree to fix failed doctest. ====================================================================== FAIL: Doctest: libbe.diff.DiffTree ---------------------------------------------------------------------- Traceback (most recent call last): File "/usr/lib/python2.5/doctest.py", line 2128, in runTest raise self.failureException(self.format_failure(new.getvalue())) AssertionError: Failed doctest test for libbe.diff.DiffTree File "/home/wking/src/fun/be/be.diff-subscribe/libbe/diff.py", line 136, in DiffTree ---------------------------------------------------------------------- File "/home/wking/src/fun/be/be.diff-subscribe/libbe/diff.py", line 172, in libbe.diff.DiffTree Failed example: print bugdir.report_string() Exception raised: Traceback (most recent call last): File "/usr/lib/python2.5/doctest.py", line 1228, in __run compileflags, 1) in test.globs File "", line 1, in print bugdir.report_string() File "/home/wking/src/fun/be/be.diff-subscribe/libbe/diff.py", line 213, in report_string return "\n".join(self.report()) TypeError --- libbe/diff.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/libbe/diff.py b/libbe/diff.py index 46b8bda..b3cd6bc 100644 --- a/libbe/diff.py +++ b/libbe/diff.py @@ -210,12 +210,15 @@ class DiffTree (tree.Tree): raise KeyError, "%s doesn't match '%s'" % (names, self.name) raise KeyError, "%s points to child not in %s" % (names, [c.name for c in self]) def report_string(self): - return "\n".join(self.report()) + report = self.report() + if report == None: + return '' + return '\n'.join(report) def report(self, root=None, parent=None, depth=0): if root == None: root = self.make_root() if self.masked == True: - return None + return root data_part = self.data_part(depth) if self.requires_children == True \ and len([c for c in self if c.masked == False]) == 0: -- cgit From d3122f5c72cc0a0c345bf0bd545f9e3217ca934f Mon Sep 17 00:00:00 2001 From: "W. Trevor King" Date: Sat, 5 Dec 2009 08:15:52 -0500 Subject: Updated NEWS --- NEWS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/NEWS b/NEWS index 669bed2..21a0140 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,11 @@ +December 5, 2009 + * changes to `be diff` + * exits with an error if required revision control is not possible. + Previously it printed a message, but exitted with status 0. + * removed options --new, --removed, --modified, --all + * added options --uuids, --subscribe + * assorted cleanups and bugfixes + December 4, 2009 * new commands: email-bugs -- cgit