aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorW. Trevor King <wking@drexel.edu>2009-11-21 10:53:04 -0500
committerW. Trevor King <wking@drexel.edu>2009-11-21 10:53:04 -0500
commit3b168403ff5e50d767476c4c0f037d1841bb2bf9 (patch)
tree1929369aed3b5b4b0b6c87b27c92732798d0714a
parent75fedab07f9e566ca1c984051d7deece4d910e2c (diff)
downloadbugseverywhere-3b168403ff5e50d767476c4c0f037d1841bb2bf9.tar.gz
Broke `be comment --xml` out and extended into `be import-xml`.
It should currently do everything that `be comment --xml` did, but it still has a way to go before it lives up to it's longhelp string, mostly figuring out bug/comment merging. The allowed XML format also changed a bit, becoming a bit more structured. cmdutil.bug_from_shortname() renamed to cmdutil.bug_from_id(). New functions cmdutil.parse_id() and cmdutil.bug_comment_from_id(). Additional doctests in libbe.comment.Comment.comment_shortnames() to show example output if bug_shortname==None. Brought be-xml-to-mbox and be-mbox-to-xml up to speed on the current <be-xml>-rooted format. * Added <extra-string> handling to their comment handling. * Moved extra strings from email bodies to X-Extra-String headers (some comment bodies are not text, and we should keep the estr location consistent between bugs and comments.)
-rw-r--r--becommands/assign.py2
-rw-r--r--becommands/close.py2
-rw-r--r--becommands/comment.py80
-rw-r--r--becommands/depend.py4
-rw-r--r--becommands/email_bugs.py2
-rw-r--r--becommands/import_xml.py257
-rw-r--r--becommands/merge.py4
-rw-r--r--becommands/open.py2
-rw-r--r--becommands/remove.py2
-rw-r--r--becommands/severity.py2
-rw-r--r--becommands/show.py21
-rw-r--r--becommands/status.py2
-rw-r--r--becommands/tag.py2
-rw-r--r--becommands/target.py2
-rwxr-xr-xinterfaces/xml/be-mbox-to-xml8
-rwxr-xr-xinterfaces/xml/be-xml-to-mbox42
-rw-r--r--libbe/cmdutil.py59
-rw-r--r--libbe/comment.py6
18 files changed, 373 insertions, 126 deletions
diff --git a/becommands/assign.py b/becommands/assign.py
index fbef281..31aa607 100644
--- a/becommands/assign.py
+++ b/becommands/assign.py
@@ -57,7 +57,7 @@ def execute(args, manipulate_encodings=True):
raise cmdutil.UsageError("Too many arguments.")
bd = bugdir.BugDir(from_disk=True,
manipulate_encodings=manipulate_encodings)
- bug = cmdutil.bug_from_shortname(bd, args[0])
+ bug = cmdutil.bug_from_id(bd, args[0])
bug = bd.bug_from_shortname(args[0])
if len(args) == 1:
bug.assigned = bd.user_id
diff --git a/becommands/close.py b/becommands/close.py
index 2cdcb59..d65c4e0 100644
--- a/becommands/close.py
+++ b/becommands/close.py
@@ -45,7 +45,7 @@ def execute(args, manipulate_encodings=True):
raise cmdutil.UsageError("Too many arguments.")
bd = bugdir.BugDir(from_disk=True,
manipulate_encodings=manipulate_encodings)
- bug = cmdutil.bug_from_shortname(bd, args[0])
+ bug = cmdutil.bug_from_id(bd, args[0])
bug.status = "closed"
bd.save()
diff --git a/becommands/comment.py b/becommands/comment.py
index dad32dd..a600e9f 100644
--- a/becommands/comment.py
+++ b/becommands/comment.py
@@ -19,10 +19,6 @@
from libbe import cmdutil, bugdir, comment, editor
import os
import sys
-try: # import core module, Python >= 2.5
- from xml.etree import ElementTree
-except ImportError: # look for non-core module
- from elementtree import ElementTree
__desc__ = __doc__
def execute(args, manipulate_encodings=True):
@@ -32,7 +28,7 @@ def execute(args, manipulate_encodings=True):
>>> os.chdir(bd.root)
>>> execute(["a", "This is a comment about a"], manipulate_encodings=False)
>>> bd._clear_bugs()
- >>> bug = cmdutil.bug_from_shortname(bd, "a")
+ >>> bug = cmdutil.bug_from_id(bd, "a")
>>> bug.load_comments(load_full=False)
>>> comment = bug.comment_root[0]
>>> print comment.body
@@ -54,7 +50,7 @@ def execute(args, manipulate_encodings=True):
>>> os.environ["EDITOR"] = "echo 'I like cheese' > "
>>> execute(["b"], manipulate_encodings=False)
>>> bd._clear_bugs()
- >>> bug = cmdutil.bug_from_shortname(bd, "b")
+ >>> bug = cmdutil.bug_from_id(bd, "b")
>>> bug.load_comments(load_full=False)
>>> comment = bug.comment_root[0]
>>> print comment.body
@@ -71,25 +67,10 @@ def execute(args, manipulate_encodings=True):
raise cmdutil.UsageError("Too many arguments.")
shortname = args[0]
- if shortname.count(':') > 1:
- raise cmdutil.UserError("Invalid id '%s'." % shortname)
- elif shortname.count(':') == 1:
- # Split shortname generated by Comment.comment_shortnames()
- bugname = shortname.split(':')[0]
- is_reply = True
- else:
- bugname = shortname
- is_reply = False
bd = bugdir.BugDir(from_disk=True,
manipulate_encodings=manipulate_encodings)
- bug = cmdutil.bug_from_shortname(bd, bugname)
- bug.load_comments(load_full=False)
- if is_reply:
- parent = bug.comment_root.comment_from_shortname(shortname,
- bug_shortname=bugname)
- else:
- parent = bug.comment_root
+ bug, parent = cmdutil.bug_comment_from_id(bd, shortname)
if len(args) == 1: # try to launch an editor for comment-body entry
try:
@@ -118,51 +99,11 @@ def execute(args, manipulate_encodings=True):
if not body.endswith('\n'):
body+='\n'
- if options.XML == False:
- new = parent.new_reply(body=body, content_type=options.content_type)
- if options.author != None:
- new.author = options.author
- if options.alt_id != None:
- new.alt_id = options.alt_id
- else: # import XML comment [list]
- # read in the comments
- str_body = body.encode("unicode_escape").replace(r'\n', '\n')
- comment_list = ElementTree.XML(str_body)
- if comment_list.tag not in ["bug", "comment-list"]:
- raise comment.InvalidXML(
- comment_list, "root element must be <bug> or <comment-list>")
- new_comments = []
- ids = []
- for c in bug.comment_root.traverse():
- ids.append(c.uuid)
- if c.alt_id != None:
- ids.append(c.alt_id)
- for child in comment_list.getchildren():
- if child.tag == "comment":
- new = comment.Comment(bug)
- new.from_xml(unicode(ElementTree.tostring(child)).decode("unicode_escape"))
- if new.alt_id in ids:
- raise cmdutil.UserError(
- "Clashing comment alt_id: %s" % new.alt_id)
- ids.append(new.uuid)
- if new.alt_id != None:
- ids.append(new.alt_id)
- if new.in_reply_to == None:
- new.in_reply_to = parent.uuid
- new_comments.append(new)
- else:
- print >> sys.stderr, "Ignoring unknown tag %s in %s" \
- % (child.tag, comment_list.tag)
- try:
- comment.list_to_root(new_comments,bug,root=parent, # link new comments
- ignore_missing_references=options.ignore_missing_references)
- except comment.MissingReference, e:
- raise cmdutil.UserError(e)
- # Protect against programmer error causing data loss:
- kids = [c.uuid for c in parent.traverse()]
- for nc in new_comments:
- assert nc.uuid in kids, "%s wasn't added to %s" % (nc.uuid, parent.uuid)
- nc.save()
+ new = parent.new_reply(body=body, content_type=options.content_type)
+ if options.author != None:
+ new.author = options.author
+ if options.alt_id != None:
+ new.alt_id = options.alt_id
def get_parser():
parser = cmdutil.CmdOptionParser("be comment ID [COMMENT]")
@@ -172,11 +113,6 @@ def get_parser():
help="Set an alternate comment ID", default=None)
parser.add_option("-c", "--content-type", metavar="MIME", dest="content_type",
help="Set comment content-type (e.g. text/plain)", default=None)
- parser.add_option("-x", "--xml", action="store_true", default=False,
- dest='XML', help="Use COMMENT to specify an XML comment description rather than the comment body. The root XML element should be either <bug> or <comment-list> with one or more <comment> children. The syntax for the <comment> elements should match that generated by 'be show --xml COMMENT-ID'. Unrecognized tags are ignored. Missing tags are left at the default value. The comment UUIDs are always auto-generated, so if you set a <uuid> field, but no <alt-id> field, your <uuid> will be used as the comment's <alt-id>. An exception is raised if <alt-id> conflicts with an existing comment.")
- parser.add_option("-i", "--ignore-missing-references", action="store_true",
- dest="ignore_missing_references",
- help="For XML import, if any comment's <in-reply-to> refers to a non-existent comment, ignore it (instead of raising an exception).")
return parser
longhelp="""
diff --git a/becommands/depend.py b/becommands/depend.py
index f52527e..044aaff 100644
--- a/becommands/depend.py
+++ b/becommands/depend.py
@@ -95,7 +95,7 @@ def execute(args, manipulate_encodings=True):
for blockee,blocker in fixed])
return 0
- bugA = cmdutil.bug_from_shortname(bd, args[0])
+ bugA = cmdutil.bug_from_id(bd, args[0])
if options.tree_depth != None:
dtree = DependencyTree(bd, bugA, options.tree_depth)
@@ -112,7 +112,7 @@ def execute(args, manipulate_encodings=True):
return 0
if len(args) == 2:
- bugB = cmdutil.bug_from_shortname(bd, args[1])
+ bugB = cmdutil.bug_from_id(bd, args[1])
if options.remove == True:
remove_block(bugA, bugB)
else: # add the dependency
diff --git a/becommands/email_bugs.py b/becommands/email_bugs.py
index 9aeccec..c188332 100644
--- a/becommands/email_bugs.py
+++ b/becommands/email_bugs.py
@@ -89,7 +89,7 @@ def execute(args, manipulate_encodings=True):
lines = [u'<?xml version="1.0" encoding="%s" ?>' % bd.encoding,
u'<bugs>']
for shortname in args:
- bug = cmdutil.bug_from_shortname(bd, shortname)
+ bug = cmdutil.bug_from_id(bd, shortname)
lines.append(bug.xml(show_comments=True))
lines.append(u'</bugs>')
subject = options.subject
diff --git a/becommands/import_xml.py b/becommands/import_xml.py
new file mode 100644
index 0000000..d28e97a
--- /dev/null
+++ b/becommands/import_xml.py
@@ -0,0 +1,257 @@
+# Copyright (C) 2009 W. Trevor King <wking@drexel.edu>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+"""Import comments and bugs from XML"""
+from libbe import cmdutil, bugdir, bug, comment, utility
+from becommands.comment import complete
+import os
+import sys
+try: # import core module, Python >= 2.5
+ from xml.etree import ElementTree
+except ImportError: # look for non-core module
+ from elementtree import ElementTree
+__desc__ = __doc__
+
+def execute(args, manipulate_encodings=True):
+ """
+ >>> import time
+ >>> import StringIO
+ >>> bd = bugdir.SimpleBugDir()
+ >>> os.chdir(bd.root)
+ >>> orig_stdin = sys.stdin
+ >>> sys.stdin = StringIO.StringIO("<be-xml><comment><uuid>c</uuid><body>This is a comment about a</body></comment></be-xml>")
+ >>> execute(["-c", "a", "-"], manipulate_encodings=False)
+ >>> sys.stdin = orig_stdin
+ >>> bd._clear_bugs()
+ >>> bug = cmdutil.bug_from_id(bd, "a")
+ >>> bug.load_comments(load_full=False)
+ >>> comment = bug.comment_root[0]
+ >>> print comment.body
+ This is a comment about a
+ <BLANKLINE>
+ >>> comment.author == bd.user_id
+ True
+ >>> comment.time <= int(time.time())
+ True
+ >>> comment.in_reply_to is None
+ True
+ >>> bd.cleanup()
+ """
+ parser = get_parser()
+ options, args = parser.parse_args(args)
+ complete(options, args, parser)
+ if len(args) < 1:
+ raise cmdutil.UsageError("Please specify an XML file.")
+ if len(args) > 1:
+ raise cmdutil.UsageError("Too many arguments.")
+ filename = args[0]
+
+ bd = bugdir.BugDir(from_disk=True,
+ manipulate_encodings=manipulate_encodings)
+ if options.comment_root != None:
+ croot_bug,croot_comment = \
+ cmdutil.bug_comment_from_id(bd, options.comment_root)
+ else:
+ croot_bug,croot_comment = (None, None)
+
+ if filename == '-':
+ xml = sys.stdin.read()
+ else:
+ xml = bd.vcs.get_file_contents(filename, allow_no_vcs=True)
+ str_xml = xml.encode('unicode_escape').replace(r'\n', '\n')
+ # unicode read + encode to string so we know the encoding,
+ # which might not be given (?) in a binary string read?
+
+ # parse the xml
+ root_bugs = []
+ root_comments = []
+ version = {}
+ be_xml = ElementTree.XML(str_xml)
+ if be_xml.tag != 'be-xml':
+ raise utility.InvalidXML(
+ 'import-xml', be_xml, 'root element must be <be-xml>')
+ for child in be_xml.getchildren():
+ if child.tag == 'bug':
+ new = bug.Bug(bugdir=bd)
+ new.from_xml(unicode(ElementTree.tostring(child)).decode('unicode_escape'))
+ root_bugs.append(new)
+ elif child.tag == 'comment':
+ new = comment.Comment(croot_bug)
+ new.from_xml(unicode(ElementTree.tostring(child)).decode("unicode_escape"))
+ root_comments.append(new)
+ elif child.tag == 'version':
+ for gchild in child.getchildren():
+ if child.tag in ['tag', 'nick', 'revision', 'revision-id']:
+ text = xml.sax.saxutils.unescape(child.text)
+ text = text.decode('unicode_escape').strip()
+ version[child.tag] = text
+ else:
+ print >> sys.stderr, 'ignoring unknown tag %s in %s' \
+ % (gchild.tag, child.tag)
+ else:
+ print >> sys.stderr, 'ignoring unknown tag %s in %s' \
+ % (child.tag, comment_list.tag)
+
+ # merge the new root_comments
+ croot_cids = []
+ for c in croot_bug.comment_root.traverse():
+ croot_cids.append(c.uuid)
+ if c.alt_id != None:
+ croot_cids.append(c.alt_id)
+ for new in root_comments:
+ if new.alt_id in croot_cids:
+ raise cmdutil.UserError(
+ 'clashing comment alt_id: %s' % new.alt_id)
+ croot_cids.append(new.uuid)
+ if new.alt_id != None:
+ croot_cids.append(new.alt_id)
+ if new.in_reply_to == None:
+ new.in_reply_to = croot_comment.uuid
+ try:
+ # link new comments
+ comment.list_to_root(root_comments,croot_bug,root=croot_comment,
+ ignore_missing_references= \
+ options.ignore_missing_references)
+ except comment.MissingReference, e:
+ raise cmdutil.UserError(e)
+
+ # merge the new croot_bugs
+ for new in root_bugs:
+ bd.append(new)
+
+ # protect against programmer error causing data loss:
+ comms = [c.uuid for c in croot_comment.traverse()]
+ for new in root_comments:
+ assert new.uuid in comms, \
+ "comment %s wasn't added to %s" % (new.uuid, croot_comment.uuid)
+ for new in root_bugs:
+ assert bd.has_bug(new.uuid), \
+ "bug %s wasn't added" % (new.uuid)
+
+ # save new information
+ for new in root_comments:
+ new.save()
+ for new in root_bugs:
+ new.save()
+
+def get_parser():
+ parser = cmdutil.CmdOptionParser("be import-xml XMLFILE")
+ parser.add_option("-i", "--ignore-missing-references", action="store_true",
+ dest="ignore_missing_references", default=False,
+ help="If any comment's <in-reply-to> refers to a non-existent comment, ignore it (instead of raising an exception).")
+ parser.add_option("-a", "--add-only", action='store_true',
+ dest="add_only", default=False,
+ help="If any bug or comment listed in the XML file already exists in the bug repository, do not alter the repository version.")
+ parser.add_option("-c", "--comment-root", dest="comment_root",
+ help="Supply a bug or comment ID as the root of any <comment> elements that are direct children of the <be-xml> element. If any such <comment> elements exist, you are required to set this option.")
+ return parser
+
+longhelp="""
+Import comments and bugs from XMLFILE. If XMLFILE is '-', the file is
+read from stdin.
+
+This command provides a fallback mechanism for passing bugs between
+repositories, in case the repositories VCSs are incompatible. If the
+VCSs are compatible, it's better to use their builtin merge/push/pull
+to share this information, as that will preserve a more detailed
+history.
+
+The XML file should be formatted similarly to
+ <be-xml>
+ <version>
+ <tag>1.0.0</tag>
+ <nick>be</nick>
+ <revision>446</revision>
+ <revision-id>a@b.com-20091119214553-iqyw2cpqluww3zna</revision-id>
+ <version>
+ <bug>
+ ...
+ <comment>...</comment>
+ <comment>...</comment>
+ </bug>
+ <bug>...</bug>
+ <comment>...</comment>
+ <comment>...</comment>
+ </be-xml>
+where the ellipses mark output commpatible with Bug.xml() and
+Comment.xml(). Take a look at the output of `be show --xml --version`
+for some explicit examples. Unrecognized tags are ignored. Missing
+tags are left at the default value. The version tag is not required,
+but is strongly recommended.
+
+The bug and comment UUIDs are always auto-generated, so if you set a
+<uuid> field, but no <alt-id> field, your <uuid> will be used as the
+comment's <alt-id>. An exception is raised if <alt-id> conflicts with
+an existing comment. Bugs do not have a permantent alt-id, so they
+the <uuid>s you specify are not saved. The <uuid>s _are_ used to
+match agains prexisting bug and comment uuids, and comment alt-ids,
+and fields explicitly given in the XML file will replace old versions
+unless the --add-only flag.
+
+*.extra_strings recieves special treatment, and if --add-only is not
+set, the resulting list concatenates both source lists and removes
+repeats.
+
+Here's an example of import activity:
+ Repository
+ bug (uuid=B, author=John, status=open)
+ estr (don't forget your towel)
+ estr (helps with space travel)
+ com (uuid=C1, author=Jane, body=Hello)
+ com (uuid=C2, author=Jess, body=World)
+ XML
+ bug (uuid=B, status=fixed)
+ estr (don't forget your towel)
+ estr (watch out for flying dolphins)
+ com (uuid=C1, body=So long)
+ com (uuid=C3, author=Jed, body=And thanks)
+ Result
+ bug (uuid=B, author=John, status=fixed)
+ estr (don't forget your towel)
+ estr (helps with space travel)
+ estr (watch out for flying dolphins)
+ com (uuid=C1, author=Jane, body=So long)
+ com (uuid=C2, author=Jess, body=World)
+ com (uuid=C4, alt-id=C3, author=Jed, body=And thanks)
+ Result, with --add-only
+ bug (uuid=B, author=John, status=open)
+ estr (don't forget your towel)
+ estr (helps with space travel)
+ com (uuid=C1, author=Jane, body=Hello)
+ com (uuid=C2, author=Jess, body=World)
+ com (uuid=C4, alt-id=C3, author=Jed, body=And thanks)
+
+Examples:
+
+Import comments (e.g. emails from an mbox) and append to bug XYZ
+ $ be-mbox-to-xml mail.mbox | be import-xml --c XYZ -
+Or you can append those emails underneath the prexisting comment XYZ-3
+ $ be-mbox-to-xml mail.mbox | be import-xml --c XYZ-3 -
+
+User creates a new bug
+ user$ be new "The demuxulizer is broken"
+ Created bug with ID 48f
+ user$ be comment 48f
+ <Describe bug>
+ ...
+User exports bug as xml and emails it to the developers
+ user$ be show --xml --version 48f > 48f.xml
+ user$ cat 48f.xml | mail -s "Demuxulizer bug xml" devs@b.com
+Devs recieve email, and save it's contents as demux-bug.xml
+ dev$ cat demux-bug.xml | be import-xml -
+"""
+
+def help():
+ return get_parser().help_str() + longhelp
diff --git a/becommands/merge.py b/becommands/merge.py
index bc18479..766af56 100644
--- a/becommands/merge.py
+++ b/becommands/merge.py
@@ -137,9 +137,9 @@ def execute(args, manipulate_encodings=True):
bd = bugdir.BugDir(from_disk=True,
manipulate_encodings=manipulate_encodings)
- bugA = cmdutil.bug_from_shortname(bd, args[0])
+ bugA = cmdutil.bug_from_id(bd, args[0])
bugA.load_comments()
- bugB = cmdutil.bug_from_shortname(bd, args[1])
+ bugB = cmdutil.bug_from_id(bd, args[1])
bugB.load_comments()
mergeA = bugA.new_comment("Merged from bug %s" % bugB.uuid)
newCommTree = copy.deepcopy(bugB.comment_root)
diff --git a/becommands/open.py b/becommands/open.py
index c9e55a2..1b4c23e 100644
--- a/becommands/open.py
+++ b/becommands/open.py
@@ -44,7 +44,7 @@ def execute(args, manipulate_encodings=True):
raise cmdutil.UsageError, "Too many arguments."
bd = bugdir.BugDir(from_disk=True,
manipulate_encodings=manipulate_encodings)
- bug = cmdutil.bug_from_shortname(bd, args[0])
+ bug = cmdutil.bug_from_id(bd, args[0])
bug.status = "open"
def get_parser():
diff --git a/becommands/remove.py b/becommands/remove.py
index 0e61362..d265e5c 100644
--- a/becommands/remove.py
+++ b/becommands/remove.py
@@ -44,7 +44,7 @@ def execute(args, manipulate_encodings=True):
raise cmdutil.UsageError, "Please specify a bug id."
bd = bugdir.BugDir(from_disk=True,
manipulate_encodings=manipulate_encodings)
- bug = cmdutil.bug_from_shortname(bd, args[0])
+ bug = cmdutil.bug_from_id(bd, args[0])
bd.remove_bug(bug)
print "Removed bug %s" % bug.uuid
diff --git a/becommands/severity.py b/becommands/severity.py
index e987760..f42f740 100644
--- a/becommands/severity.py
+++ b/becommands/severity.py
@@ -43,7 +43,7 @@ def execute(args, manipulate_encodings=True):
raise cmdutil.UsageError
bd = bugdir.BugDir(from_disk=True,
manipulate_encodings=manipulate_encodings)
- bug = cmdutil.bug_from_shortname(bd, args[0])
+ bug = cmdutil.bug_from_id(bd, args[0])
if len(args) == 1:
print bug.severity
elif len(args) == 2:
diff --git a/becommands/show.py b/becommands/show.py
index 94e16bf..4f8c30d 100644
--- a/becommands/show.py
+++ b/becommands/show.py
@@ -63,24 +63,17 @@ def execute(args, manipulate_encodings=True):
if options.XML:
print '<?xml version="1.0" encoding="%s" ?>' % bd.encoding
for shortname in args:
- if shortname.count(':') > 1:
- raise cmdutil.UserError("Invalid id '%s'." % shortname)
- elif shortname.count(':') == 1:
- # Split shortname generated by Comment.comment_shortnames()
- bugname = shortname.split(':')[0]
- is_comment = True
- else:
- bugname = shortname
- is_comment = False
- if is_comment == True and options.comments == False:
- continue
- bug = cmdutil.bug_from_shortname(bd, bugname)
- if is_comment == False:
+ bugname,commname = cmdutil.parse_id(shortname)
+ if commname == None: # bug shortname
+ bug = cmdutil.bug_from_id(bd, shortname)
if options.XML:
print bug.xml(show_comments=options.comments)
else:
print bug.string(show_comments=options.comments)
- else:
+ elif options.comments == False:
+ continue
+ else: # comment shortname
+ bug,comment = cmdutil.bug_comment_from_id(shortname)
comment = bug.comment_root.comment_from_shortname(
shortname, bug_shortname=bugname)
if options.XML:
diff --git a/becommands/status.py b/becommands/status.py
index 827c7ce..bf66c26 100644
--- a/becommands/status.py
+++ b/becommands/status.py
@@ -40,7 +40,7 @@ def execute(args, manipulate_encodings=True):
raise cmdutil.UsageError
bd = bugdir.BugDir(from_disk=True,
manipulate_encodings=manipulate_encodings)
- bug = cmdutil.bug_from_shortname(bd, args[0])
+ bug = cmdutil.bug_from_id(bd, args[0])
if len(args) == 1:
print bug.status
else:
diff --git a/becommands/tag.py b/becommands/tag.py
index 31b43ba..e6debb4 100644
--- a/becommands/tag.py
+++ b/becommands/tag.py
@@ -95,7 +95,7 @@ def execute(args, manipulate_encodings=True):
if len(tags) > 0:
print '\n'.join(tags)
return
- bug = cmdutil.bug_from_shortname(bd, args[0])
+ bug = cmdutil.bug_from_id(bd, args[0])
if len(args) == 2:
given_tag = args[1]
estrs = bug.extra_strings
diff --git a/becommands/target.py b/becommands/target.py
index 7e41451..672eb06 100644
--- a/becommands/target.py
+++ b/becommands/target.py
@@ -55,7 +55,7 @@ def execute(args, manipulate_encodings=True):
if target and isinstance(target,str):
print target
return
- bug = cmdutil.bug_from_shortname(bd, args[0])
+ bug = cmdutil.bug_from_id(bd, args[0])
if len(args) == 1:
if bug.target is None:
print "No target assigned."
diff --git a/interfaces/xml/be-mbox-to-xml b/interfaces/xml/be-mbox-to-xml
index a740117..3af2978 100755
--- a/interfaces/xml/be-mbox-to-xml
+++ b/interfaces/xml/be-mbox-to-xml
@@ -15,8 +15,8 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""
-Convert an mbox into xml suitable for imput into be.
- $ cat mbox | be-mbox-to-xml | be comment --xml <ID> -
+Convert an mbox into xml suitable for input into be.
+ $ be-mbox-to-xml file.mbox | be import-xml -c <ID> -
mbox is a flat-file format, consisting of a series of messages.
Messages begin with a a From_ line, followed by RFC 822 email,
followed by a blank line.
@@ -140,10 +140,10 @@ def comment_message_to_xml(message, fields=None):
def main(mbox_filename):
mb = mbox(mbox_filename)
print u'<?xml version="1.0" encoding="%s" ?>' % DEFAULT_ENCODING
- print u"<comment-list>"
+ print u"<be-xml>"
for message in mb:
print comment_message_to_xml(message)
- print u"</comment-list>"
+ print u"</be-xml>"
if __name__ == "__main__":
diff --git a/interfaces/xml/be-xml-to-mbox b/interfaces/xml/be-xml-to-mbox
index 7960d56..ecc6327 100755
--- a/interfaces/xml/be-xml-to-mbox
+++ b/interfaces/xml/be-xml-to-mbox
@@ -88,19 +88,18 @@ class Bug (LimitedAttrDict):
def print_to_mbox(self):
name,addr = email.utils.parseaddr(self["creator"])
print "From %s %s" % (addr, rfc2822_to_asctime(self["created"]))
- print "Message-ID: <%s@%s>" % (self["uuid"], DEFAULT_DOMAIN)
+ print "Message-id: <%s@%s>" % (self["uuid"], DEFAULT_DOMAIN)
print "Date: %s" % self["created"]
print "From: %s" % self["creator"]
print "Content-Type: %s; charset=%s" % ("text/plain", DEFAULT_ENCODING)
print "Content-Transfer-Encoding: 8bit"
print "Subject: %s: %s" % (self["short-name"], self["summary"])
+ if "extra-strings" in self:
+ for estr in self["extra_strings"]:
+ print "X-Extra-String: %s" % estr
print ""
print self["summary"]
print ""
- if "extra-strings" in self:
- print "extra strings:\n ",
- print '\n '.join(self["extra_strings"])
- print ""
if "comments" in self:
for comment in self["comments"]:
comment.print_to_mbox(self)
@@ -131,7 +130,8 @@ class Comment (LimitedAttrDict):
u"author",
u"date",
u"content-type",
- u"body"]
+ u"body",
+ u"extra-strings"]
def print_to_mbox(self, bug=None):
if bug == None:
bug = Bug()
@@ -142,7 +142,7 @@ class Comment (LimitedAttrDict):
elif "alt-id" in self: id = self["alt-id"]
else: id = None
if id != None:
- print "Message-ID: <%s@%s>" % (id, DEFAULT_DOMAIN)
+ print "Message-id: <%s@%s>" % (id, DEFAULT_DOMAIN)
print "Date: %s" % self["date"]
print "From: %s" % self["author"]
subject = ""
@@ -156,6 +156,9 @@ class Comment (LimitedAttrDict):
if "in-reply-to" not in self.keys():
self["in-reply-to"] = bug["uuid"]
print "In-Reply-To: <%s@%s>" % (self["in-reply-to"], DEFAULT_DOMAIN)
+ if "extra-strings" in self:
+ for estr in self["extra_strings"]:
+ print "X-Extra-String: %s" % estr
if self["content-type"].startswith("text/"):
print "Content-Transfer-Encoding: 8bit"
print "Content-Type: %s; charset=%s" % (self["content-type"], DEFAULT_ENCODING)
@@ -168,9 +171,15 @@ class Comment (LimitedAttrDict):
assert element.tag == "comment", element.tag
for field in element.getchildren():
text = unescape(unicode(field.text).decode("unicode_escape").strip())
- if field.tag == "body":
- text+="\n"
- self[field.tag] = text
+ if field.tag == "extra-string":
+ if "extra-strings" in self:
+ self["extra-strings"].append(text)
+ else:
+ self["extra-strings"] = [text]
+ else:
+ if field.tag == "body":
+ text+="\n"
+ self[field.tag] = text
def print_to_mbox(element):
if element.tag == "bug":
@@ -181,16 +190,9 @@ def print_to_mbox(element):
c = Comment()
c.init_from_etree(element)
c.print_to_mbox()
- elif element.tag in ["bugs", "bug-list"]:
- for b_elt in element.getchildren():
- b = Bug()
- b.init_from_etree(b_elt)
- b.print_to_mbox()
- elif element.tag in ["comments", "comment-list"]:
- for c_elt in element.getchildren():
- c = Comment()
- c.init_from_etree(c_elt)
- c.print_to_mbox()
+ elif element.tag in ["be-xml"]:
+ for elt in element.getchildren():
+ print_to_mbox(elt)
if __name__ == "__main__":
import sys
diff --git a/libbe/cmdutil.py b/libbe/cmdutil.py
index f1c8acd..96430eb 100644
--- a/libbe/cmdutil.py
+++ b/libbe/cmdutil.py
@@ -30,10 +30,10 @@ import sys
import doctest
import bugdir
+import comment
import plugin
import encoding
-
class UserError(Exception):
def __init__(self, msg):
Exception.__init__(self, msg)
@@ -213,16 +213,69 @@ def underlined(instring):
return "%s\n%s" % (instring, "="*len(instring))
-def bug_from_shortname(bdir, shortname):
+def parse_id(id):
+ """
+ Return (bug_id, comment_id) tuple.
+ Basically inverts Comment.comment_shortnames()
+ >>> parse_id('XYZ')
+ ('XYZ', None)
+ >>> parse_id('XYZ:123')
+ ('XYZ', ':123')
+ >>> parse_id('')
+ Traceback (most recent call last):
+ ...
+ UserError: invalid id ''.
+ >>> parse_id('::')
+ Traceback (most recent call last):
+ ...
+ UserError: invalid id '::'.
+ """
+ if len(id) == 0:
+ raise UserError("invalid id '%s'." % id)
+ if id.count(':') > 1:
+ raise UserError("invalid id '%s'." % id)
+ elif id.count(':') == 1:
+ # Split shortname generated by Comment.comment_shortnames()
+ bug_id,comment_id = id.split(':')
+ comment_id = ':'+comment_id
+ else:
+ bug_id = id
+ comment_id = None
+ return (bug_id, comment_id)
+
+def bug_from_id(bdir, id):
"""
Exception translation for the command-line interface.
+ id can be either the bug shortname or the full uuid.
"""
try:
- bug = bdir.bug_from_shortname(shortname)
+ bug = bdir.bug_from_shortname(id)
except (bugdir.MultipleBugMatches, bugdir.NoBugMatches), e:
raise UserError(e.message)
return bug
+def bug_comment_from_id(bdir, id):
+ """
+ Return (bug,comment) tuple matching shortname. id can be either
+ the bug/comment shortname or the full uuid. If there is no
+ comment part to the id, the returned comment is the bug's
+ .comment_root.
+ """
+ bug_id,comment_id = parse_id(id)
+ try:
+ bug = bdir.bug_from_shortname(bug_id)
+ except (bugdir.MultipleBugMatches, bugdir.NoBugMatches), e:
+ raise UserError(e.message)
+ if comment_id == None:
+ comm = bug.comment_root
+ else:
+ #bug.load_comments(load_full=False)
+ try:
+ comm = bug.comment_root.comment_from_shortname(comment_id)
+ except comment.InvalidShortname, e:
+ raise UserError(e.message)
+ return (bug, comm)
+
def _test():
import doctest
import sys
diff --git a/libbe/comment.py b/libbe/comment.py
index 678d8ba..5cc43c4 100644
--- a/libbe/comment.py
+++ b/libbe/comment.py
@@ -641,6 +641,12 @@ class Comment(Tree, settings_object.SavedSettingsObject):
bug-1:2 b
bug-1:3 c
bug-1:4 d
+ >>> for id,name in a.comment_shortnames():
+ ... print id, name.uuid
+ :1 a
+ :2 b
+ :3 c
+ :4 d
"""
if bug_shortname == None:
bug_shortname = ""