aboutsummaryrefslogtreecommitdiffstats
path: root/becommands
diff options
context:
space:
mode:
Diffstat (limited to 'becommands')
-rw-r--r--becommands/assign.py4
-rw-r--r--becommands/close.py4
-rw-r--r--becommands/comment.py82
-rw-r--r--becommands/commit.py6
-rw-r--r--becommands/depend.py6
-rw-r--r--becommands/diff.py2
-rw-r--r--becommands/email_bugs.py237
-rw-r--r--becommands/help.py2
-rw-r--r--becommands/html.py2
-rw-r--r--becommands/import_xml.py267
-rw-r--r--becommands/init.py16
-rw-r--r--becommands/list.py2
-rw-r--r--becommands/merge.py6
-rw-r--r--becommands/new.py2
-rw-r--r--becommands/open.py4
-rw-r--r--becommands/remove.py4
-rw-r--r--becommands/set.py2
-rw-r--r--becommands/severity.py4
-rw-r--r--becommands/show.py158
-rw-r--r--becommands/status.py4
-rw-r--r--becommands/subscribe.py2
-rw-r--r--becommands/tag.py4
-rw-r--r--becommands/target.py4
23 files changed, 666 insertions, 158 deletions
diff --git a/becommands/assign.py b/becommands/assign.py
index fbef281..2c78f69 100644
--- a/becommands/assign.py
+++ b/becommands/assign.py
@@ -21,7 +21,7 @@
from libbe import cmdutil, bugdir
__desc__ = __doc__
-def execute(args, manipulate_encodings=True):
+def execute(args, manipulate_encodings=True, restrict_file_access=False):
"""
>>> import os
>>> bd = bugdir.SimpleBugDir()
@@ -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..a14cea8 100644
--- a/becommands/close.py
+++ b/becommands/close.py
@@ -21,7 +21,7 @@
from libbe import cmdutil, bugdir
__desc__ = __doc__
-def execute(args, manipulate_encodings=True):
+def execute(args, manipulate_encodings=True, restrict_file_access=False):
"""
>>> from libbe import bugdir
>>> import os
@@ -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..8e899ce 100644
--- a/becommands/comment.py
+++ b/becommands/comment.py
@@ -19,20 +19,16 @@
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):
+def execute(args, manipulate_encodings=True, restrict_file_access=False):
"""
>>> import time
>>> bd = bugdir.SimpleBugDir()
>>> 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/commit.py b/becommands/commit.py
index dc70e7e..39d1e2e 100644
--- a/becommands/commit.py
+++ b/becommands/commit.py
@@ -18,9 +18,9 @@ from libbe import cmdutil, bugdir, editor, vcs
import sys
__desc__ = __doc__
-def execute(args, manipulate_encodings=True):
+def execute(args, manipulate_encodings=True, restrict_file_access=False):
"""
- >>> import os, time
+ >>> import os
>>> from libbe import bug
>>> bd = bugdir.SimpleBugDir()
>>> os.chdir(bd.root)
@@ -49,6 +49,8 @@ def execute(args, manipulate_encodings=True):
elif options.body == "EDITOR":
body = editor.editor_string("Please enter your commit message above")
else:
+ if restrict_file_access == True:
+ cmdutil.restrict_file_access(bd, options.body)
body = bd.vcs.get_file_contents(options.body, allow_no_vcs=True)
try:
revision = bd.vcs.commit(summary, body=body,
diff --git a/becommands/depend.py b/becommands/depend.py
index f52527e..f50d693 100644
--- a/becommands/depend.py
+++ b/becommands/depend.py
@@ -35,7 +35,7 @@ class BrokenLink (Exception):
self.blocking_bug = blocking_bug
-def execute(args, manipulate_encodings=True):
+def execute(args, manipulate_encodings=True, restrict_file_access=False):
"""
>>> from libbe import utility
>>> bd = bugdir.SimpleBugDir()
@@ -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/diff.py b/becommands/diff.py
index e71da9b..6477934 100644
--- a/becommands/diff.py
+++ b/becommands/diff.py
@@ -21,7 +21,7 @@ from libbe import cmdutil, bugdir, diff
import os
__desc__ = __doc__
-def execute(args, manipulate_encodings=True):
+def execute(args, manipulate_encodings=True, restrict_file_access=False):
"""
>>> import os
>>> bd = bugdir.SimpleBugDir()
diff --git a/becommands/email_bugs.py b/becommands/email_bugs.py
new file mode 100644
index 0000000..d0366df
--- /dev/null
+++ b/becommands/email_bugs.py
@@ -0,0 +1,237 @@
+# 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.
+"""Email specified bugs in a be-handle-mail compatible format."""
+
+import copy
+from cStringIO import StringIO
+from email import Message
+from email.mime.text import MIMEText
+from email.generator import Generator
+import sys
+import time
+
+from libbe import cmdutil, bugdir
+from libbe.subproc import invoke
+from libbe.utility import time_to_str
+from libbe.vcs import detect_vcs, installed_vcs
+import show
+
+__desc__ = __doc__
+
+sendmail='/usr/sbin/sendmail -t'
+
+def execute(args, manipulate_encodings=True, restrict_file_access=False):
+ """
+ >>> import os
+ >>> from libbe import bug
+ >>> bd = bugdir.SimpleBugDir()
+ >>> bd.encoding = 'utf-8'
+ >>> os.chdir(bd.root)
+ >>> import email.charset as c
+ >>> c.add_charset('utf-8', c.SHORTEST, c.QP, 'utf-8')
+ >>> execute(["-o", "--to", "a@b.com", "--from", "b@c.edu", "a", "b"],
+ ... manipulate_encodings=False) # doctest: +ELLIPSIS
+ Content-Type: text/xml; charset="utf-8"
+ MIME-Version: 1.0
+ Content-Transfer-Encoding: quoted-printable
+ From: b@c.edu
+ To: a@b.com
+ Date: ...
+ Subject: [be-bug:xml] Updates to a, b
+ <BLANKLINE>
+ <?xml version=3D"1.0" encoding=3D"utf-8" ?>
+ <be-xml>
+ <version>
+ <tag>...</tag>
+ <branch-nick>...</branch-nick>
+ <revno>...</revno>
+ <revision-id>...
+ </version>
+ <bug>
+ <uuid>a</uuid>
+ <short-name>a</short-name>
+ <severity>minor</severity>
+ <status>open</status>
+ <creator>John Doe &lt;jdoe@example.com&gt;</creator>
+ <created>Thu, 01 Jan 1970 00:00:00 +0000</created>
+ <summary>Bug A</summary>
+ </bug>
+ <bug>
+ <uuid>b</uuid>
+ <short-name>b</short-name>
+ <severity>minor</severity>
+ <status>closed</status>
+ <creator>Jane Doe &lt;jdoe@example.com&gt;</creator>
+ <created>Thu, 01 Jan 1970 00:00:00 +0000</created>
+ <summary>Bug B</summary>
+ </bug>
+ </be-xml>
+ >>> bd.cleanup()
+
+ Note that the '=3D' bits in
+ <?xml version=3D"1.0" encoding=3D"utf-8" ?>
+ are the way quoted-printable escapes '='.
+
+ The unclosed <revision-id>... is because revision ids can be long
+ enough to cause line wraps, and we want to ensure we match even if
+ the closing </revision-id> is split by the wrapping.
+ """
+ parser = get_parser()
+ options, args = parser.parse_args(args)
+ cmdutil.default_complete(options, args, parser,
+ bugid_args={-1: lambda bug : bug.active==True})
+ if len(args) == 0:
+ raise cmdutil.UsageError
+ bd = bugdir.BugDir(from_disk=True,
+ manipulate_encodings=manipulate_encodings)
+ xml = show.output(args, bd, as_xml=True, with_comments=True)
+ subject = options.subject
+ if subject == None:
+ subject = '[be-bug:xml] Updates to %s' % ', '.join(args)
+ submit_email = TextEmail(to_address=options.to_address,
+ from_address=options.from_address,
+ subject=subject,
+ body=xml,
+ encoding=bd.encoding,
+ subtype='xml')
+ if options.output == True:
+ print submit_email
+ else:
+ submit_email.send()
+
+def get_parser():
+ parser = cmdutil.CmdOptionParser("be email-bugs [options] ID [ID ...]")
+ parser.add_option("-t", "--to", metavar="EMAIL", dest="to_address",
+ help="Submission email address (%default)",
+ default="be-devel@bugseverywhere.org")
+ parser.add_option("-f", "--from", metavar="EMAIL", dest="from_address",
+ help="Senders email address, overriding auto-generated default",
+ default=None)
+ parser.add_option("-s", "--subject", metavar="STRING", dest="subject",
+ help="Subject line, overriding auto-generated default. If you use this option, remember that be-handle-mail probably want something like '[be-bug:xml] ...'",
+ default=None)
+ parser.add_option('-o', '--output', dest='output', action='store_true',
+ help="Don't mail the generated message, print it to stdout instead. Useful for testing functionality.")
+ return parser
+
+longhelp="""
+Email specified bugs in a be-handle-mail compatible format. This is
+the prefered method for reporting bugs if you did not install bzr by
+branching a bzr repository.
+
+If you _did_ install bzr by branching a bzr repository, we suggest you
+commit any new bug information with
+ bzr commit --message "Reported bug in demuxulizer"
+and then email a bzr merge directive with
+ bzr send --mail-to "be-devel@bugseverywhere.org"
+rather than using this command.
+"""
+
+def help():
+ return get_parser().help_str() + longhelp
+
+class TextEmail (object):
+ """
+ Make it very easy to compose and send single-part text emails.
+ >>> msg = TextEmail(to_address='Monty <monty@a.com>',
+ ... from_address='Python <python@b.edu>',
+ ... subject='Parrots',
+ ... header={'x-special-header':'your info here'},
+ ... body="Remarkable bird, id'nit, squire?\\nLovely plumage!")
+ >>> print msg # doctest: +ELLIPSIS
+ Content-Type: text/plain; charset="utf-8"
+ MIME-Version: 1.0
+ Content-Transfer-Encoding: base64
+ From: Python <python@b.edu>
+ To: Monty <monty@a.com>
+ Date: ...
+ Subject: Parrots
+ x-special-header: your info here
+ <BLANKLINE>
+ UmVtYXJrYWJsZSBiaXJkLCBpZCduaXQsIHNxdWlyZT8KTG92ZWx5IHBsdW1hZ2Uh
+ <BLANKLINE>
+ >>> import email.charset as c
+ >>> c.add_charset('utf-8', c.SHORTEST, c.QP, 'utf-8')
+ >>> print msg # doctest: +ELLIPSIS
+ Content-Type: text/plain; charset="utf-8"
+ MIME-Version: 1.0
+ Content-Transfer-Encoding: quoted-printable
+ From: Python <python@b.edu>
+ To: Monty <monty@a.com>
+ Date: ...
+ Subject: Parrots
+ x-special-header: your info here
+ <BLANKLINE>
+ Remarkable bird, id'nit, squire?
+ Lovely plumage!
+ """
+ def __init__(self, to_address, from_address=None, subject=None,
+ header=None, body=None, encoding='utf-8', subtype='plain'):
+ self.to_address = to_address
+ self.from_address = from_address
+ if self.from_address == None:
+ self.from_address = self._guess_from_address()
+ self.subject = subject
+ self.header = header
+ if self.header == None:
+ self.header = {}
+ self.body = body
+ self.encoding = encoding
+ self.subtype = subtype
+ def _guess_from_address(self):
+ vcs = detect_vcs('.')
+ if vcs.name == "None":
+ vcs = installed_vcs()
+ return vcs.get_user_id()
+ def encoded_MIME_body(self):
+ return MIMEText(self.body.encode(self.encoding),
+ self.subtype,
+ self.encoding)
+ def message(self):
+ response = self.encoded_MIME_body()
+ response['From'] = self.from_address
+ response['To'] = self.to_address
+ response['Date'] = time_to_str(time.time())
+ response['Subject'] = self.subject
+ for k,v in self.header.items():
+ response[k] = v
+ return response
+ def flatten(self, to_unicode=False):
+ """
+ This is a simplified version of send_pgp_mime.flatten().
+ """
+ fp = StringIO()
+ g = Generator(fp, mangle_from_=False)
+ g.flatten(self.message())
+ text = fp.getvalue()
+ if to_unicode == True:
+ encoding = msg.get_content_charset() or "utf-8"
+ text = unicode(text, encoding=encoding)
+ return text
+ def __str__(self):
+ return self.flatten()
+ def __unicode__(self):
+ return self.flatten(to_unicode=True)
+ def send(self, sendmail=None):
+ """
+ This is a simplified version of send_pgp_mime.mail().
+
+ Send an email Message instance on its merry way by shelling
+ out to the user specified sendmail.
+ """
+ if sendmail == None:
+ sendmail = SENDMAIL
+ invoke(sendmail, stdin=self.flatten())
diff --git a/becommands/help.py b/becommands/help.py
index c12c56a..99ab8c4 100644
--- a/becommands/help.py
+++ b/becommands/help.py
@@ -20,7 +20,7 @@
from libbe import cmdutil, utility
__desc__ = __doc__
-def execute(args, manipulate_encodings=False):
+def execute(args, manipulate_encodings=True, restrict_file_access=False):
"""
Print help of specified command (the manipulate_encodings argument
is ignored).
diff --git a/becommands/html.py b/becommands/html.py
index b0640da..622a531 100644
--- a/becommands/html.py
+++ b/becommands/html.py
@@ -21,7 +21,7 @@ import xml.sax.saxutils, htmlentitydefs
__desc__ = __doc__
-def execute(args, manipulate_encodings=True):
+def execute(args, manipulate_encodings=True, restrict_file_access=False):
"""
>>> import os
>>> bd = bugdir.SimpleBugDir()
diff --git a/becommands/import_xml.py b/becommands/import_xml.py
new file mode 100644
index 0000000..a74d329
--- /dev/null
+++ b/becommands/import_xml.py
@@ -0,0 +1,267 @@
+# 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, restrict_file_access=False):
+ """
+ >>> 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:
+ if restrict_file_access == True:
+ cmdutil.restrict_file_access(bd, options.body)
+ 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
+ if len(root_comments) > 0:
+ if croot_bug == None:
+ raise UserError(
+ '--comment-root option is required for your root comments:\n%s'
+ % '\n\n'.join([c.string() for c in 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:
+ if croot_bug != None:
+ 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>
+ <branch-nick>be</branch-nick>
+ <revno>446</revno>
+ <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` 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 48f > 48f.xml
+ user$ cat 48f.xml | mail -s "Demuxulizer bug xml" devs@b.com
+or equivalently (with a slightly fancier be-handle-mail compatible
+email):
+ user$ be email-bugs 48f
+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/init.py b/becommands/init.py
index 275dd77..7d6d475 100644
--- a/becommands/init.py
+++ b/becommands/init.py
@@ -20,7 +20,7 @@ import os.path
from libbe import cmdutil, bugdir
__desc__ = __doc__
-def execute(args, manipulate_encodings=True):
+def execute(args, manipulate_encodings=True, restrict_file_access=False):
"""
>>> from libbe import utility, vcs
>>> import os
@@ -37,14 +37,14 @@ def execute(args, manipulate_encodings=True):
>>> dir = utility.Dir()
>>> os.chdir(dir.path)
- >>> vcs = vcs.installed_vcs()
- >>> vcs.init('.')
- >>> print vcs.name
- Arch
- >>> execute([], manipulate_encodings=False)
- Using Arch for revision control.
+ >>> _vcs = vcs.installed_vcs()
+ >>> _vcs.init('.')
+ >>> _vcs.name in vcs.VCS_ORDER
+ True
+ >>> execute([], manipulate_encodings=False) # doctest: +ELLIPSIS
+ Using ... for revision control.
Directory initialized.
- >>> vcs.cleanup()
+ >>> _vcs.cleanup()
>>> try:
... execute(['--root', '.'], manipulate_encodings=False)
diff --git a/becommands/list.py b/becommands/list.py
index 14e387b..4711789 100644
--- a/becommands/list.py
+++ b/becommands/list.py
@@ -26,7 +26,7 @@ __desc__ = __doc__
AVAILABLE_CMPS = [fn[4:] for fn in dir(bug) if fn[:4] == 'cmp_']
AVAILABLE_CMPS.remove("attr") # a cmp_* template.
-def execute(args, manipulate_encodings=True):
+def execute(args, manipulate_encodings=True, restrict_file_access=False):
"""
>>> import os
>>> bd = bugdir.SimpleBugDir()
diff --git a/becommands/merge.py b/becommands/merge.py
index bc18479..31c781f 100644
--- a/becommands/merge.py
+++ b/becommands/merge.py
@@ -19,7 +19,7 @@ from libbe import cmdutil, bugdir
import os, copy
__desc__ = __doc__
-def execute(args, manipulate_encodings=True):
+def execute(args, manipulate_encodings=True, restrict_file_access=False):
"""
>>> from libbe import utility
>>> bd = bugdir.SimpleBugDir()
@@ -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/new.py b/becommands/new.py
index 30a7d28..92d61e4 100644
--- a/becommands/new.py
+++ b/becommands/new.py
@@ -20,7 +20,7 @@ from libbe import cmdutil, bugdir
import sys
__desc__ = __doc__
-def execute(args, manipulate_encodings=True):
+def execute(args, manipulate_encodings=True, restrict_file_access=False):
"""
>>> import os, time
>>> from libbe import bug
diff --git a/becommands/open.py b/becommands/open.py
index c9e55a2..c2c15e2 100644
--- a/becommands/open.py
+++ b/becommands/open.py
@@ -21,7 +21,7 @@
from libbe import cmdutil, bugdir
__desc__ = __doc__
-def execute(args, manipulate_encodings=True):
+def execute(args, manipulate_encodings=True, restrict_file_access=False):
"""
>>> import os
>>> bd = bugdir.SimpleBugDir()
@@ -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..e4f0065 100644
--- a/becommands/remove.py
+++ b/becommands/remove.py
@@ -18,7 +18,7 @@
from libbe import cmdutil, bugdir
__desc__ = __doc__
-def execute(args, manipulate_encodings=True):
+def execute(args, manipulate_encodings=True, restrict_file_access=False):
"""
>>> from libbe import mapfile
>>> import os
@@ -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/set.py b/becommands/set.py
index e5cab8d..c8c5a2c 100644
--- a/becommands/set.py
+++ b/becommands/set.py
@@ -32,7 +32,7 @@ def _value_string(bd, setting):
val = None
return str(val)
-def execute(args, manipulate_encodings=True):
+def execute(args, manipulate_encodings=True, restrict_file_access=False):
"""
>>> import os
>>> bd = bugdir.SimpleBugDir()
diff --git a/becommands/severity.py b/becommands/severity.py
index e987760..524976b 100644
--- a/becommands/severity.py
+++ b/becommands/severity.py
@@ -21,7 +21,7 @@
from libbe import cmdutil, bugdir, bug
__desc__ = __doc__
-def execute(args, manipulate_encodings=True):
+def execute(args, manipulate_encodings=True, restrict_file_access=False):
"""
>>> import os
>>> bd = bugdir.SimpleBugDir()
@@ -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..557c63a 100644
--- a/becommands/show.py
+++ b/becommands/show.py
@@ -17,12 +17,12 @@
# 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.
-"""Show a particular bug"""
+"""Show a particular bug, comment, or combination of both."""
import sys
-from libbe import cmdutil, bugdir
+from libbe import cmdutil, bugdir, comment, version, _version
__desc__ = __doc__
-def execute(args, manipulate_encodings=True):
+def execute(args, manipulate_encodings=True, restrict_file_access=False):
"""
>>> import os
>>> bd = bugdir.SimpleBugDir()
@@ -41,15 +41,23 @@ def execute(args, manipulate_encodings=True):
<BLANKLINE>
>>> execute (["--xml", "a"], manipulate_encodings=False) # doctest: +ELLIPSIS
<?xml version="1.0" encoding="..." ?>
- <bug>
- <uuid>a</uuid>
- <short-name>a</short-name>
- <severity>minor</severity>
- <status>open</status>
- <creator>John Doe &lt;jdoe@example.com&gt;</creator>
- <created>...</created>
- <summary>Bug A</summary>
- </bug>
+ <be-xml>
+ <version>
+ <tag>...</tag>
+ <branch-nick>...</branch-nick>
+ <revno>...</revno>
+ <revision-id>...</revision-id>
+ </version>
+ <bug>
+ <uuid>a</uuid>
+ <short-name>a</short-name>
+ <severity>minor</severity>
+ <status>open</status>
+ <creator>John Doe &lt;jdoe@example.com&gt;</creator>
+ <created>Thu, 01 Jan 1970 00:00:00 +0000</created>
+ <summary>Bug A</summary>
+ </bug>
+ </be-xml>
>>> bd.cleanup()
"""
parser = get_parser()
@@ -60,38 +68,18 @@ def execute(args, manipulate_encodings=True):
raise cmdutil.UsageError
bd = bugdir.BugDir(from_disk=True,
manipulate_encodings=manipulate_encodings)
- 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:
- if options.XML:
- print bug.xml(show_comments=options.comments)
- else:
- print bug.string(show_comments=options.comments)
- else:
- comment = bug.comment_root.comment_from_shortname(
- shortname, bug_shortname=bugname)
- if options.XML:
- print comment.xml(shortname=shortname)
- else:
- if len(args) == 1 and options.only_raw_body == True:
- sys.__stdout__.write(comment.body)
- else:
- print comment.string(shortname=shortname)
- if shortname != args[-1] and options.XML == False:
- print "" # add a blank line between bugs/comments
+
+ if options.only_raw_body == True:
+ if len(args) != 1:
+ raise cmdutil.UsageError(
+ 'only one ID accepted with --only-raw-body')
+ bug,comment = cmdutil.bug_comment_from_id(bd, args[0])
+ if comment == bug.comment_root:
+ raise cmdutil.UsageError(
+ "--only-raw-body requires a comment ID, not '%s'" % args[0])
+ sys.__stdout__.write(comment.body)
+ sys.exit(0)
+ print output(args, bd, as_xml=options.XML, with_comments=options.comments)
def get_parser():
parser = cmdutil.CmdOptionParser("be show [options] ID [ID ...]")
@@ -108,9 +96,87 @@ def get_parser():
longhelp="""
Show all information about the bugs or comments whose IDs are given.
-It's probably not a good idea to mix bug and comment IDs in a single
-call, but you're free to do so if you like.
+Without the --xml flag set, it's probably not a good idea to mix bug
+and comment IDs in a single call, but you're free to do so if you
+like. With the --xml flag set, there will never be any root comments,
+so mix and match away (the bug listings for directly requested
+comments will be restricted to the bug uuid and the requested
+comment(s)).
+
+Directly requested comments will be grouped by their parent bug and
+placed at the end of the output, so the ordering may not match the
+order of the listed IDs.
"""
def help():
return get_parser().help_str() + longhelp
+
+def _sort_ids(ids, with_comments=True):
+ bugs = []
+ root_comments = {}
+ for id in ids:
+ bugname,commname = cmdutil.parse_id(id)
+ if commname == None:
+ bugs.append(bugname)
+ elif with_comments == True:
+ if bugname not in root_comments:
+ root_comments[bugname] = [commname]
+ else:
+ root_comments[bugname].append(commname)
+ for bugname in root_comments.keys():
+ assert bugname not in bugs, \
+ "specifically requested both '%s%s' and '%s'" \
+ % (bugname, root_comments[bugname][0], bugname)
+ return (bugs, root_comments)
+
+def _xml_header(encoding):
+ lines = ['<?xml version="1.0" encoding="%s" ?>' % encoding,
+ '<be-xml>',
+ ' <version>',
+ ' <tag>%s</tag>' % version.version()]
+ for tag in ['branch-nick', 'revno', 'revision-id']:
+ value = _version.version_info[tag.replace('-', '_')]
+ lines.append(' <%s>%s</%s>' % (tag, value, tag))
+ lines.append(' </version>')
+ return lines
+
+def _xml_footer():
+ return ['</be-xml>']
+
+def output(ids, bd, as_xml=True, with_comments=True):
+ bugs,root_comments = _sort_ids(ids, with_comments)
+ lines = []
+ if as_xml:
+ lines.extend(_xml_header(bd.encoding))
+ else:
+ spaces_left = len(ids) - 1
+ for bugname in bugs:
+ bug = cmdutil.bug_from_id(bd, bugname)
+ if as_xml:
+ lines.append(bug.xml(indent=2, show_comments=with_comments))
+ else:
+ lines.append(bug.string(show_comments=with_comments))
+ if spaces_left > 0:
+ spaces_left -= 1
+ lines.append('') # add a blank line between bugs/comments
+ for bugname,comments in root_comments.items():
+ bug = cmdutil.bug_from_id(bd, bugname)
+ if as_xml:
+ lines.extend([' <bug>', ' <uuid>%s</uuid>' % bug.uuid])
+ for commname in comments:
+ try:
+ comment = bug.comment_root.comment_from_shortname(commname)
+ except comment.InvalidShortname, e:
+ raise UserError(e.message)
+ if as_xml:
+ lines.append(comment.xml(indent=4, shortname=bugname))
+ else:
+ lines.append(comment.string(shortname=shortname))
+ if spaces_left > 0:
+ spaces_left -= 1
+ lines.append('') # add a blank line between bugs/comments
+ if as_xml:
+ lines.append('</bug>')
+ if as_xml:
+ lines.extend(_xml_footer())
+ return '\n'.join(lines)
diff --git a/becommands/status.py b/becommands/status.py
index 827c7ce..fd31c97 100644
--- a/becommands/status.py
+++ b/becommands/status.py
@@ -18,7 +18,7 @@
from libbe import cmdutil, bugdir, bug
__desc__ = __doc__
-def execute(args, manipulate_encodings=True):
+def execute(args, manipulate_encodings=True, restrict_file_access=False):
"""
>>> import os
>>> bd = bugdir.SimpleBugDir()
@@ -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/subscribe.py b/becommands/subscribe.py
index 0a23057..051341b 100644
--- a/becommands/subscribe.py
+++ b/becommands/subscribe.py
@@ -55,7 +55,7 @@ class InvalidType (ValueError):
self.type_root = type_root
-def execute(args, manipulate_encodings=True):
+def execute(args, manipulate_encodings=True, restrict_file_access=False):
"""
>>> bd = bugdir.SimpleBugDir()
>>> bd.set_sync_with_disk(True)
diff --git a/becommands/tag.py b/becommands/tag.py
index 31b43ba..e22cb70 100644
--- a/becommands/tag.py
+++ b/becommands/tag.py
@@ -19,7 +19,7 @@ from libbe import cmdutil, bugdir
import os, copy
__desc__ = __doc__
-def execute(args, manipulate_encodings=True):
+def execute(args, manipulate_encodings=True, restrict_file_access=False):
"""
>>> from libbe import utility
>>> bd = bugdir.SimpleBugDir()
@@ -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..efb2479 100644
--- a/becommands/target.py
+++ b/becommands/target.py
@@ -22,7 +22,7 @@
from libbe import cmdutil, bugdir
__desc__ = __doc__
-def execute(args, manipulate_encodings=True):
+def execute(args, manipulate_encodings=True, restrict_file_access=False):
"""
>>> import os
>>> bd = bugdir.SimpleBugDir()
@@ -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."