aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/comments/4be73baf-e46b-4acb-a58e-4719e57c550b/values19
-rw-r--r--.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/comments/d5ed4f87-f1a1-4138-b0ad-190e4a49d820/body1
-rw-r--r--.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/comments/d5ed4f87-f1a1-4138-b0ad-190e4a49d820/values11
-rw-r--r--.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/values2
-rw-r--r--.be/bugs/3613e6e9-db9e-4775-8914-f31f0b4b81ac/values2
-rw-r--r--.be/bugs/508ea95e-7bc6-4b9b-9e36-a3a87014423d/comments/1ba36272-7ae1-4f95-8002-7b45e62e6790/values25
-rw-r--r--.be/bugs/508ea95e-7bc6-4b9b-9e36-a3a87014423d/comments/e173c09a-1b3e-4d8a-a86a-6b8c94a76247/values19
-rw-r--r--.be/bugs/508ea95e-7bc6-4b9b-9e36-a3a87014423d/values2
-rw-r--r--.be/bugs/7ba4bc51-b251-483a-a67a-f1b89c83f6af/comments/db2c18d9-9573-4d68-88a5-ee47ed24b813/values19
-rw-r--r--.be/bugs/7ba4bc51-b251-483a-a67a-f1b89c83f6af/comments/ec16300f-529a-4492-8327-f9a72e4447c2/values19
-rw-r--r--.be/bugs/7ba4bc51-b251-483a-a67a-f1b89c83f6af/values2
-rw-r--r--.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/07fc448f-c42e-4846-929a-8924de485766/body8
-rw-r--r--.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/07fc448f-c42e-4846-929a-8924de485766/values11
-rw-r--r--.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/520a9829-8d90-43ce-be64-868b8321e5b0/body1
-rw-r--r--.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/520a9829-8d90-43ce-be64-868b8321e5b0/values11
-rw-r--r--.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/8b54e56e-c693-4594-998f-5bd6c1f385d7/body5
-rw-r--r--.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/8b54e56e-c693-4594-998f-5bd6c1f385d7/values11
-rw-r--r--.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/bb124fd9-08f5-4f82-a035-6355e8403075/body1
-rw-r--r--.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/bb124fd9-08f5-4f82-a035-6355e8403075/values11
-rw-r--r--.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/faa686bf-c0eb-48bf-8a0b-d9a2e02bd132/body5
-rw-r--r--.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/faa686bf-c0eb-48bf-8a0b-d9a2e02bd132/values8
-rw-r--r--.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/values17
-rw-r--r--.be/bugs/ecc91b94-7f3f-44a7-af58-03191d327a7f/values2
-rw-r--r--.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/028d2e8d-5b0f-4c43-a913-35a1709b2276/values19
-rw-r--r--.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/15602c0c-25e4-4c2c-9e24-79bdb90721b1/values19
-rw-r--r--.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/3f556a48-c538-4569-8609-3e829b561d78/values19
-rw-r--r--.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/f376debf-9f7e-4347-807f-00e7263487c7/body1
-rw-r--r--.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/f376debf-9f7e-4347-807f-00e7263487c7/values8
-rw-r--r--Makefile1
-rwxr-xr-xbe88
-rw-r--r--becommands/assign.py7
-rw-r--r--becommands/close.py1
-rw-r--r--becommands/comment.py59
-rw-r--r--becommands/diff.py1
-rw-r--r--becommands/init.py1
-rw-r--r--becommands/list.py4
-rw-r--r--becommands/merge.py1
-rw-r--r--becommands/new.py18
-rw-r--r--becommands/open.py1
-rw-r--r--becommands/remove.py1
-rw-r--r--becommands/set.py47
-rw-r--r--becommands/severity.py1
-rw-r--r--becommands/show.py49
-rw-r--r--becommands/status.py40
-rw-r--r--becommands/tag.py2
-rw-r--r--becommands/target.py5
-rw-r--r--libbe/arch.py1
-rw-r--r--libbe/beuuid.py1
-rw-r--r--libbe/bug.py17
-rw-r--r--libbe/bugdir.py3
-rw-r--r--libbe/bzr.py1
-rw-r--r--libbe/cmdutil.py67
-rw-r--r--libbe/comment.py152
-rw-r--r--libbe/config.py1
-rw-r--r--libbe/diff.py1
-rw-r--r--libbe/editor.py1
-rw-r--r--libbe/encoding.py1
-rw-r--r--libbe/mapfile.py5
-rw-r--r--libbe/plugin.py1
-rw-r--r--libbe/rcs.py1
-rw-r--r--libbe/settings_object.py26
-rw-r--r--libbe/utility.py23
-rwxr-xr-xtest_usage.sh6
-rwxr-xr-xxml/be-mbox-to-xml104
-rwxr-xr-xxml/be-xml-to-mbox157
65 files changed, 777 insertions, 397 deletions
diff --git a/.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/comments/4be73baf-e46b-4acb-a58e-4719e57c550b/values b/.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/comments/4be73baf-e46b-4acb-a58e-4719e57c550b/values
index a68a21d..4b8a017 100644
--- a/.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/comments/4be73baf-e46b-4acb-a58e-4719e57c550b/values
+++ b/.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/comments/4be73baf-e46b-4acb-a58e-4719e57c550b/values
@@ -1,21 +1,8 @@
+Content-type: text/plain
-
-Content-type=text/plain
-
-
-
-
-
-
-Date=Fri, 18 Apr 2008 11:21:03 +0000
-
-
-
-
-
-
-From=benf
+Date: Fri, 18 Apr 2008 11:21:03 +0000
+From: benf
diff --git a/.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/comments/d5ed4f87-f1a1-4138-b0ad-190e4a49d820/body b/.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/comments/d5ed4f87-f1a1-4138-b0ad-190e4a49d820/body
new file mode 100644
index 0000000..19b1cf5
--- /dev/null
+++ b/.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/comments/d5ed4f87-f1a1-4138-b0ad-190e4a49d820/body
@@ -0,0 +1 @@
+We could add this functionality to update_copyright.sh
diff --git a/.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/comments/d5ed4f87-f1a1-4138-b0ad-190e4a49d820/values b/.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/comments/d5ed4f87-f1a1-4138-b0ad-190e4a49d820/values
new file mode 100644
index 0000000..0b164a1
--- /dev/null
+++ b/.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/comments/d5ed4f87-f1a1-4138-b0ad-190e4a49d820/values
@@ -0,0 +1,11 @@
+Content-type: text/plain
+
+
+Date: Sat, 11 Jul 2009 14:08:45 +0000
+
+
+From: W. Trevor King <wking@drexel.edu>
+
+
+In-reply-to: 4be73baf-e46b-4acb-a58e-4719e57c550b
+
diff --git a/.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/values b/.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/values
index a2d65ff..420d967 100644
--- a/.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/values
+++ b/.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/values
@@ -7,7 +7,7 @@ creator: benf
severity: minor
-status: closed
+status: open
summary: Address is outdated for FSF offices
diff --git a/.be/bugs/3613e6e9-db9e-4775-8914-f31f0b4b81ac/values b/.be/bugs/3613e6e9-db9e-4775-8914-f31f0b4b81ac/values
index 9da9004..70ec5f5 100644
--- a/.be/bugs/3613e6e9-db9e-4775-8914-f31f0b4b81ac/values
+++ b/.be/bugs/3613e6e9-db9e-4775-8914-f31f0b4b81ac/values
@@ -4,7 +4,7 @@ creator: abentley
severity: minor
-status: closed
+status: fixed
summary: auto-add files to revision control
diff --git a/.be/bugs/508ea95e-7bc6-4b9b-9e36-a3a87014423d/comments/1ba36272-7ae1-4f95-8002-7b45e62e6790/values b/.be/bugs/508ea95e-7bc6-4b9b-9e36-a3a87014423d/comments/1ba36272-7ae1-4f95-8002-7b45e62e6790/values
index c8719a0..62b29d5 100644
--- a/.be/bugs/508ea95e-7bc6-4b9b-9e36-a3a87014423d/comments/1ba36272-7ae1-4f95-8002-7b45e62e6790/values
+++ b/.be/bugs/508ea95e-7bc6-4b9b-9e36-a3a87014423d/comments/1ba36272-7ae1-4f95-8002-7b45e62e6790/values
@@ -1,28 +1,11 @@
+Content-type: text/plain
+Date: Mon, 16 Jul 2007 15:23:47 +0000
-Content-type=text/plain
+From: abentley
-
-
-
-Date=Mon, 16 Jul 2007 15:23:47 +0000
-
-
-
-
-
-
-From=abentley
-
-
-
-
-
-
-In-reply-to=e173c09a-1b3e-4d8a-a86a-6b8c94a76247
-
-
+In-reply-to: e173c09a-1b3e-4d8a-a86a-6b8c94a76247
diff --git a/.be/bugs/508ea95e-7bc6-4b9b-9e36-a3a87014423d/comments/e173c09a-1b3e-4d8a-a86a-6b8c94a76247/values b/.be/bugs/508ea95e-7bc6-4b9b-9e36-a3a87014423d/comments/e173c09a-1b3e-4d8a-a86a-6b8c94a76247/values
index 96cc18c..e31b44b 100644
--- a/.be/bugs/508ea95e-7bc6-4b9b-9e36-a3a87014423d/comments/e173c09a-1b3e-4d8a-a86a-6b8c94a76247/values
+++ b/.be/bugs/508ea95e-7bc6-4b9b-9e36-a3a87014423d/comments/e173c09a-1b3e-4d8a-a86a-6b8c94a76247/values
@@ -1,21 +1,8 @@
+Content-type: text/plain
-
-Content-type=text/plain
-
-
-
-
-
-
-Date=Sun, 15 Jul 2007 13:34:52 +0000
-
-
-
-
-
-
-From=jelmer
+Date: Sun, 15 Jul 2007 13:34:52 +0000
+From: jelmer
diff --git a/.be/bugs/508ea95e-7bc6-4b9b-9e36-a3a87014423d/values b/.be/bugs/508ea95e-7bc6-4b9b-9e36-a3a87014423d/values
index 9b17373..c471b0f 100644
--- a/.be/bugs/508ea95e-7bc6-4b9b-9e36-a3a87014423d/values
+++ b/.be/bugs/508ea95e-7bc6-4b9b-9e36-a3a87014423d/values
@@ -4,7 +4,7 @@ creator: jelmer
severity: minor
-status: closed
+status: fixed
summary: should check not just EDITOR but also VISUAL.
diff --git a/.be/bugs/7ba4bc51-b251-483a-a67a-f1b89c83f6af/comments/db2c18d9-9573-4d68-88a5-ee47ed24b813/values b/.be/bugs/7ba4bc51-b251-483a-a67a-f1b89c83f6af/comments/db2c18d9-9573-4d68-88a5-ee47ed24b813/values
index 4cb1f35..59a3828 100644
--- a/.be/bugs/7ba4bc51-b251-483a-a67a-f1b89c83f6af/comments/db2c18d9-9573-4d68-88a5-ee47ed24b813/values
+++ b/.be/bugs/7ba4bc51-b251-483a-a67a-f1b89c83f6af/comments/db2c18d9-9573-4d68-88a5-ee47ed24b813/values
@@ -1,21 +1,8 @@
+Content-type: text/plain
-
-Content-type=text/plain
-
-
-
-
-
-
-Date=Thu, 24 Mar 2005 17:04:47 +0000
-
-
-
-
-
-
-From=abentley
+Date: Thu, 24 Mar 2005 17:04:47 +0000
+From: abentley
diff --git a/.be/bugs/7ba4bc51-b251-483a-a67a-f1b89c83f6af/comments/ec16300f-529a-4492-8327-f9a72e4447c2/values b/.be/bugs/7ba4bc51-b251-483a-a67a-f1b89c83f6af/comments/ec16300f-529a-4492-8327-f9a72e4447c2/values
index 51af41d..ddbdc15 100644
--- a/.be/bugs/7ba4bc51-b251-483a-a67a-f1b89c83f6af/comments/ec16300f-529a-4492-8327-f9a72e4447c2/values
+++ b/.be/bugs/7ba4bc51-b251-483a-a67a-f1b89c83f6af/comments/ec16300f-529a-4492-8327-f9a72e4447c2/values
@@ -1,21 +1,8 @@
+Content-type: text/plain
-
-Content-type=text/plain
-
-
-
-
-
-
-Date=Thu, 24 Mar 2005 13:05:13 +0000
-
-
-
-
-
-
-From=abentley
+Date: Thu, 24 Mar 2005 13:05:13 +0000
+From: abentley
diff --git a/.be/bugs/7ba4bc51-b251-483a-a67a-f1b89c83f6af/values b/.be/bugs/7ba4bc51-b251-483a-a67a-f1b89c83f6af/values
index 94a1f9f..e2a930c 100644
--- a/.be/bugs/7ba4bc51-b251-483a-a67a-f1b89c83f6af/values
+++ b/.be/bugs/7ba4bc51-b251-483a-a67a-f1b89c83f6af/values
@@ -4,7 +4,7 @@ creator: abentley
severity: serious
-status: closed
+status: fixed
summary: Add test cases
diff --git a/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/07fc448f-c42e-4846-929a-8924de485766/body b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/07fc448f-c42e-4846-929a-8924de485766/body
new file mode 100644
index 0000000..0598d70
--- /dev/null
+++ b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/07fc448f-c42e-4846-929a-8924de485766/body
@@ -0,0 +1,8 @@
+<type 'unicode'> <body>�</body>
+Traceback (most recent call last):
+ File "<string>", line 1, in <module>
+ File "/usr/lib/python2.5/xml/etree/ElementTree.py", line 963, in XML
+ parser.feed(text)
+ File "/usr/lib/python2.5/xml/etree/ElementTree.py", line 1245, in feed
+ self._parser.Parse(data, 0)
+UnicodeEncodeError: 'ascii' codec can't encode character u'\u1234' in position 6: ordinal not in range(128)
diff --git a/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/07fc448f-c42e-4846-929a-8924de485766/values b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/07fc448f-c42e-4846-929a-8924de485766/values
new file mode 100644
index 0000000..cd8d8b9
--- /dev/null
+++ b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/07fc448f-c42e-4846-929a-8924de485766/values
@@ -0,0 +1,11 @@
+Content-type: text/plain
+
+
+Date: Sun, 12 Jul 2009 11:34:22 +0000
+
+
+From: W. Trevor King <wking@drexel.edu>
+
+
+In-reply-to: faa686bf-c0eb-48bf-8a0b-d9a2e02bd132
+
diff --git a/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/520a9829-8d90-43ce-be64-868b8321e5b0/body b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/520a9829-8d90-43ce-be64-868b8321e5b0/body
new file mode 100644
index 0000000..397d4b6
--- /dev/null
+++ b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/520a9829-8d90-43ce-be64-868b8321e5b0/body
@@ -0,0 +1 @@
+It looks like etree wants a byte string, not unicode input
diff --git a/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/520a9829-8d90-43ce-be64-868b8321e5b0/values b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/520a9829-8d90-43ce-be64-868b8321e5b0/values
new file mode 100644
index 0000000..8bdaf52
--- /dev/null
+++ b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/520a9829-8d90-43ce-be64-868b8321e5b0/values
@@ -0,0 +1,11 @@
+Content-type: text/plain
+
+
+Date: Sun, 12 Jul 2009 11:42:16 +0000
+
+
+From: W. Trevor King <wking@drexel.edu>
+
+
+In-reply-to: faa686bf-c0eb-48bf-8a0b-d9a2e02bd132
+
diff --git a/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/8b54e56e-c693-4594-998f-5bd6c1f385d7/body b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/8b54e56e-c693-4594-998f-5bd6c1f385d7/body
new file mode 100644
index 0000000..ce2bb8d
--- /dev/null
+++ b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/8b54e56e-c693-4594-998f-5bd6c1f385d7/body
@@ -0,0 +1,5 @@
+For example, this works:
+
+python -c 'from xml.etree import ElementTree; a=u"<body>\u1234</body>"; print type(a), a; b=ElementTree.XML(a.encode("unicode_escape")); print type(b.text), unicode(b.text).decode("unicode_escape");'
+
+Ugly though :p. Ah well.
diff --git a/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/8b54e56e-c693-4594-998f-5bd6c1f385d7/values b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/8b54e56e-c693-4594-998f-5bd6c1f385d7/values
new file mode 100644
index 0000000..1784e0e
--- /dev/null
+++ b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/8b54e56e-c693-4594-998f-5bd6c1f385d7/values
@@ -0,0 +1,11 @@
+Content-type: text/plain
+
+
+Date: Sun, 12 Jul 2009 11:46:57 +0000
+
+
+From: W. Trevor King <wking@drexel.edu>
+
+
+In-reply-to: 520a9829-8d90-43ce-be64-868b8321e5b0
+
diff --git a/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/bb124fd9-08f5-4f82-a035-6355e8403075/body b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/bb124fd9-08f5-4f82-a035-6355e8403075/body
new file mode 100644
index 0000000..89a8f8d
--- /dev/null
+++ b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/bb124fd9-08f5-4f82-a035-6355e8403075/body
@@ -0,0 +1 @@
+That's with Python 2.5.2 and ElementTree "2326 2005-03-17 07:45:21Z fredrik"
diff --git a/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/bb124fd9-08f5-4f82-a035-6355e8403075/values b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/bb124fd9-08f5-4f82-a035-6355e8403075/values
new file mode 100644
index 0000000..cca07c3
--- /dev/null
+++ b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/bb124fd9-08f5-4f82-a035-6355e8403075/values
@@ -0,0 +1,11 @@
+Content-type: text/plain
+
+
+Date: Sun, 12 Jul 2009 11:37:55 +0000
+
+
+From: W. Trevor King <wking@drexel.edu>
+
+
+In-reply-to: 07fc448f-c42e-4846-929a-8924de485766
+
diff --git a/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/faa686bf-c0eb-48bf-8a0b-d9a2e02bd132/body b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/faa686bf-c0eb-48bf-8a0b-d9a2e02bd132/body
new file mode 100644
index 0000000..57e050d
--- /dev/null
+++ b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/faa686bf-c0eb-48bf-8a0b-d9a2e02bd132/body
@@ -0,0 +1,5 @@
+Isolated problem to:
+
+python -c 'from xml.etree import ElementTree; a=u"<body>\u1234</body>"; print type(a), a; b=ElementTree.XML(a);'
+
+Output attached below
diff --git a/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/faa686bf-c0eb-48bf-8a0b-d9a2e02bd132/values b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/faa686bf-c0eb-48bf-8a0b-d9a2e02bd132/values
new file mode 100644
index 0000000..e430ea0
--- /dev/null
+++ b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/faa686bf-c0eb-48bf-8a0b-d9a2e02bd132/values
@@ -0,0 +1,8 @@
+Content-type: text/plain
+
+
+Date: Sun, 12 Jul 2009 11:31:13 +0000
+
+
+From: W. Trevor King <wking@drexel.edu>
+
diff --git a/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/values b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/values
new file mode 100644
index 0000000..4bc81f5
--- /dev/null
+++ b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/values
@@ -0,0 +1,17 @@
+creator: W. Trevor King <wking@drexel.edu>
+
+
+reporter: W. Trevor King <wking@drexel.edu>
+
+
+severity: minor
+
+
+status: fixed
+
+
+summary: utf8 problems in xml parsing
+
+
+time: Sat, 11 Jul 2009 15:48:32 +0000
+
diff --git a/.be/bugs/ecc91b94-7f3f-44a7-af58-03191d327a7f/values b/.be/bugs/ecc91b94-7f3f-44a7-af58-03191d327a7f/values
index df99653..a82beb8 100644
--- a/.be/bugs/ecc91b94-7f3f-44a7-af58-03191d327a7f/values
+++ b/.be/bugs/ecc91b94-7f3f-44a7-af58-03191d327a7f/values
@@ -4,7 +4,7 @@ creator: abentley
severity: minor
-status: closed
+status: fixed
summary: no tests for missing $EDITOR
diff --git a/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/028d2e8d-5b0f-4c43-a913-35a1709b2276/values b/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/028d2e8d-5b0f-4c43-a913-35a1709b2276/values
index eb56317..d39c4a1 100644
--- a/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/028d2e8d-5b0f-4c43-a913-35a1709b2276/values
+++ b/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/028d2e8d-5b0f-4c43-a913-35a1709b2276/values
@@ -1,21 +1,8 @@
+Content-type: text/plain
-
-Content-type=text/plain
-
-
-
-
-
-
-Date=Tue, 25 Nov 2008 19:41:02 +0000
-
-
-
-
-
-
-From=W. Trevor King <wking@drexel.edu>
+Date: Tue, 25 Nov 2008 19:41:02 +0000
+From: W. Trevor King <wking@drexel.edu>
diff --git a/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/15602c0c-25e4-4c2c-9e24-79bdb90721b1/values b/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/15602c0c-25e4-4c2c-9e24-79bdb90721b1/values
index f976972..639fd4a 100644
--- a/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/15602c0c-25e4-4c2c-9e24-79bdb90721b1/values
+++ b/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/15602c0c-25e4-4c2c-9e24-79bdb90721b1/values
@@ -1,21 +1,8 @@
+Content-type: text/plain
-
-Content-type=text/plain
-
-
-
-
-
-
-Date=Tue, 25 Nov 2008 02:36:16 +0000
-
-
-
-
-
-
-From=W. Trevor King <wking@drexel.edu>
+Date: Tue, 25 Nov 2008 02:36:16 +0000
+From: W. Trevor King <wking@drexel.edu>
diff --git a/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/3f556a48-c538-4569-8609-3e829b561d78/values b/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/3f556a48-c538-4569-8609-3e829b561d78/values
index bf5085b..2821b2f 100644
--- a/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/3f556a48-c538-4569-8609-3e829b561d78/values
+++ b/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/3f556a48-c538-4569-8609-3e829b561d78/values
@@ -1,21 +1,8 @@
+Content-type: text/plain
-
-Content-type=text/plain
-
-
-
-
-
-
-Date=Tue, 25 Nov 2008 03:02:59 +0000
-
-
-
-
-
-
-From=W. Trevor King <wking@drexel.edu>
+Date: Tue, 25 Nov 2008 03:02:59 +0000
+From: W. Trevor King <wking@drexel.edu>
diff --git a/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/f376debf-9f7e-4347-807f-00e7263487c7/body b/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/f376debf-9f7e-4347-807f-00e7263487c7/body
new file mode 100644
index 0000000..b441da9
--- /dev/null
+++ b/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/f376debf-9f7e-4347-807f-00e7263487c7/body
@@ -0,0 +1 @@
+Test unicode �quotes�
diff --git a/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/f376debf-9f7e-4347-807f-00e7263487c7/values b/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/f376debf-9f7e-4347-807f-00e7263487c7/values
new file mode 100644
index 0000000..a67680d
--- /dev/null
+++ b/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/f376debf-9f7e-4347-807f-00e7263487c7/values
@@ -0,0 +1,8 @@
+Content-type: text/plain
+
+
+Date: Sat, 11 Jul 2009 18:28:57 +0000
+
+
+From: W. Trevor King <wking@drexel.edu>
+
diff --git a/Makefile b/Makefile
index 034f033..a63618c 100644
--- a/Makefile
+++ b/Makefile
@@ -29,6 +29,7 @@ MODULES += ${DOC_DIR}
RM = rm
+#PREFIX = /usr/local
PREFIX = ${HOME}
INSTALL_OPTIONS = "--prefix=${PREFIX}"
diff --git a/be b/be
index 2023daa..657f968 100755
--- a/be
+++ b/be
@@ -19,38 +19,66 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
+import os
import sys
+
from libbe import cmdutil, _version
-__doc__ == cmdutil.help()
+__doc__ = cmdutil.help()
+
+usage = "be [options] [command] [command_options ...] [command_args ...]"
-if len(sys.argv) == 1 or sys.argv[1] in ('--help', '-h'):
- print cmdutil.help()
-elif sys.argv[1] == '--complete':
- for command, module in cmdutil.iter_commands():
- print command
- print '\n'.join(["--help","--complete","--options","--version"])
-elif sys.argv[1] == '--version':
+parser = cmdutil.CmdOptionParser(usage)
+parser.command = "be"
+parser.add_option("--version", action="store_true", dest="version",
+ help="Print version string and exit.")
+parser.add_option("-d", "--dir", dest="dir", metavar="DIR",
+ help="Run this command from DIR instead of the current directory.")
+
+try:
+ options,args = parser.parse_args()
+ for option,value in cmdutil.option_value_pairs(options, parser):
+ if value == "--complete":
+ if option == "dir":
+ if len(args) == 0:
+ args = ["."]
+ paths = cmdutil.complete_path(args[0])
+ raise cmdutil.GetCompletions(paths)
+except cmdutil.GetHelp:
+ print cmdutil.help(parser=parser)
+ sys.exit(0)
+except cmdutil.GetCompletions, e:
+ print '\n'.join(e.completions)
+ sys.exit(0)
+
+if options.version == True:
print _version.version_info["revision_id"]
-else:
- try:
- try:
- sys.exit(cmdutil.execute(sys.argv[1], sys.argv[2:]))
- except KeyError, e:
- raise cmdutil.UserError("Unknown command \"%s\"" % e.args[0])
- except cmdutil.GetHelp:
- print cmdutil.help(sys.argv[1])
- sys.exit(0)
- except cmdutil.GetCompletions, e:
- print '\n'.join(e.completions)
- sys.exit(0)
- except cmdutil.UsageError, e:
- print "Invalid usage:", e
- print "\nArgs:", sys.argv[1:]
- print cmdutil.help(sys.argv[1])
- sys.exit(1)
- except cmdutil.UserError, e:
- print "ERROR:"
- print e
- sys.exit(1)
+ sys.exit(0)
+if options.dir != None:
+ os.chdir(options.dir)
+
+try:
+ if len(args) == 0:
+ raise cmdutil.UsageError, "must supply a command"
+ sys.exit(cmdutil.execute(args[0], args[1:]))
+except cmdutil.GetHelp:
+ print cmdutil.help(sys.argv[1])
+ sys.exit(0)
+except cmdutil.GetCompletions, e:
+ print '\n'.join(e.completions)
+ sys.exit(0)
+except cmdutil.UnknownCommand, e:
+ print e
+ sys.exit(1)
+except cmdutil.UsageError, e:
+ print "Invalid usage:", e
+ if len(args) == 0:
+ print cmdutil.help(parser=parser)
+ else:
+ print "\nArgs:", args
+ print cmdutil.help(sys.argv[1])
+ sys.exit(1)
+except cmdutil.UserError, e:
+ print "ERROR:"
+ print e
+ sys.exit(1)
diff --git a/becommands/assign.py b/becommands/assign.py
index 985cfdd..e6126b8 100644
--- a/becommands/assign.py
+++ b/becommands/assign.py
@@ -2,7 +2,6 @@
# Marien Zwart <marienz@gentoo.org>
# Thomas Gerigk <tgerigk@gmx.de>
# W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
@@ -18,7 +17,7 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""Assign an individual or group to fix a bug"""
-from libbe import cmdutil, bugdir, settings_object
+from libbe import cmdutil, bugdir
__desc__ = __doc__
def execute(args, test=False):
@@ -26,7 +25,7 @@ def execute(args, test=False):
>>> import os
>>> bd = bugdir.simple_bug_dir()
>>> os.chdir(bd.root)
- >>> bd.bug_from_shortname("a").assigned is settings_object.EMPTY
+ >>> bd.bug_from_shortname("a").assigned is None
True
>>> execute(["a"], test=True)
@@ -41,7 +40,7 @@ def execute(args, test=False):
>>> execute(["a","none"], test=True)
>>> bd._clear_bugs()
- >>> bd.bug_from_shortname("a").assigned is settings_object.EMPTY
+ >>> bd.bug_from_shortname("a").assigned is None
True
"""
parser = get_parser()
diff --git a/becommands/close.py b/becommands/close.py
index deaccce..ddffaa5 100644
--- a/becommands/close.py
+++ b/becommands/close.py
@@ -2,7 +2,6 @@
# Marien Zwart <marienz@gentoo.org>
# Thomas Gerigk <tgerigk@gmx.de>
# W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
diff --git a/becommands/comment.py b/becommands/comment.py
index 0b3a576..da82854 100644
--- a/becommands/comment.py
+++ b/becommands/comment.py
@@ -1,7 +1,6 @@
# Copyright (C) 2005-2009 Aaron Bentley and Panometrics, Inc.
# Chris Ball <cjb@laptop.org>
# W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
@@ -17,9 +16,13 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""Add a comment to a bug"""
-from libbe import cmdutil, bugdir, settings_object, editor
+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, test=False):
@@ -39,7 +42,7 @@ def execute(args, test=False):
True
>>> comment.time <= int(time.time())
True
- >>> comment.in_reply_to is settings_object.EMPTY
+ >>> comment.in_reply_to is None
True
>>> if 'EDITOR' in os.environ:
@@ -108,15 +111,59 @@ def execute(args, test=False):
if not body.endswith('\n'):
body+='\n'
- comment = parent.new_reply(body=body)
- if options.content_type != None:
- comment.content_type = options.content_type
+ if options.XML == False:
+ new = parent.new_reply(body=body)
+ if options.content_type != None:
+ new.content_type = options.content_type
+ 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)
bd.save()
def get_parser():
parser = cmdutil.CmdOptionParser("be comment ID [COMMENT]")
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/diff.py b/becommands/diff.py
index 2bdea93..aa782b4 100644
--- a/becommands/diff.py
+++ b/becommands/diff.py
@@ -1,6 +1,5 @@
# Copyright (C) 2005-2009 Aaron Bentley and Panometrics, Inc.
# W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
diff --git a/becommands/init.py b/becommands/init.py
index 390dd15..5d9ccda 100644
--- a/becommands/init.py
+++ b/becommands/init.py
@@ -1,6 +1,5 @@
# Copyright (C) 2005-2009 Aaron Bentley and Panometrics, Inc.
# W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
diff --git a/becommands/list.py b/becommands/list.py
index 443704b..76614a0 100644
--- a/becommands/list.py
+++ b/becommands/list.py
@@ -2,7 +2,6 @@
# Chris Ball <cjb@laptop.org>
# Oleg Romanyshyn <oromanyshyn@panoramicfeedback.com>
# W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
@@ -135,11 +134,12 @@ def execute(args, test=False):
return True
bugs = [b for b in bd if filter(b) ]
- if len(bugs) == 0:
+ if len(bugs) == 0 and options.xml == False:
print "No matching bugs found"
def list_bugs(cur_bugs, title=None, just_uuids=False, xml=False):
if xml == True:
+ print '<?xml version="1.0" encoding="%s" ?>' % bd.encoding
print "<bugs>"
if len(cur_bugs) > 0:
if title != None and xml == False:
diff --git a/becommands/merge.py b/becommands/merge.py
index 4bec6bf..3839774 100644
--- a/becommands/merge.py
+++ b/becommands/merge.py
@@ -1,5 +1,4 @@
# Copyright (C) 2008-2009 W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
diff --git a/becommands/new.py b/becommands/new.py
index 32e070a..ceb4949 100644
--- a/becommands/new.py
+++ b/becommands/new.py
@@ -1,6 +1,5 @@
# Copyright (C) 2005-2009 Aaron Bentley and Panometrics, Inc.
# W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
@@ -16,7 +15,7 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""Create a new bug"""
-from libbe import cmdutil, bugdir, settings_object
+from libbe import cmdutil, bugdir
__desc__ = __doc__
def execute(args, test=False):
@@ -36,7 +35,7 @@ def execute(args, test=False):
True
>>> print bug.severity
minor
- >>> bug.target == settings_object.EMPTY
+ >>> bug.target == None
True
"""
parser = get_parser()
@@ -45,14 +44,18 @@ def execute(args, test=False):
if len(args) != 1:
raise cmdutil.UsageError("Please supply a summary message")
bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test)
- bug = bd.new_bug(summary=args[0])
+ if args[0] == '-': # read summary from stdin
+ summary = sys.stdin.readline()
+ else:
+ summary = args[0]
+ bug = bd.new_bug(summary=summary.strip())
if options.reporter != None:
bug.reporter = options.reporter
else:
bug.reporter = bug.creator
if options.assigned != None:
bug.assigned = options.assigned
- elif bd.default_assignee != settings_object.EMPTY:
+ elif bd.default_assignee != None:
bug.assigned = bd.default_assignee
bd.save()
print "Created bug with ID %s" % bd.bug_shortname(bug)
@@ -66,8 +69,9 @@ def get_parser():
return parser
longhelp="""
-Create a new bug, with a new ID. The summary specified on the commandline
-is a string that describes the bug briefly.
+Create a new bug, with a new ID. The summary specified on the
+commandline is a string (only one line) that describes the bug briefly
+or "-", in which case the string will be read from stdin.
"""
def help():
diff --git a/becommands/open.py b/becommands/open.py
index f9abcbb..6d13af0 100644
--- a/becommands/open.py
+++ b/becommands/open.py
@@ -2,7 +2,6 @@
# Marien Zwart <marienz@gentoo.org>
# Thomas Gerigk <tgerigk@gmx.de>
# W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
diff --git a/becommands/remove.py b/becommands/remove.py
index 213a8d9..e20440b 100644
--- a/becommands/remove.py
+++ b/becommands/remove.py
@@ -1,5 +1,4 @@
# Copyright (C) 2008-2009 W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
diff --git a/becommands/set.py b/becommands/set.py
index e771018..fa431e9 100644
--- a/becommands/set.py
+++ b/becommands/set.py
@@ -3,7 +3,6 @@
# Marien Zwart <marienz@gentoo.org>
# Thomas Gerigk <tgerigk@gmx.de>
# W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
@@ -19,14 +18,15 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""Change tree settings"""
-from libbe import cmdutil, bugdir, settings_object
+import textwrap
+from libbe import cmdutil, bugdir, rcs, settings_object
__desc__ = __doc__
def _value_string(bd, setting):
val = bd.settings.get(setting, settings_object.EMPTY)
if val == settings_object.EMPTY:
default = getattr(bd, bd._setting_name_to_attr_name(setting))
- if default != settings_object.EMPTY:
+ if default not in [None, settings_object.EMPTY]:
val = "None (%s)" % default
else:
val = None
@@ -60,7 +60,9 @@ def execute(args, test=False):
elif len(args) == 1:
print _value_string(bd, args[0])
else:
- if args[1] != "none":
+ if args[1] == "none":
+ del bd.settings[args[0]]
+ else:
if args[0] not in bd.settings_properties:
msg = "Invalid setting %s\n" % args[0]
msg += 'Allowed settings:\n '
@@ -68,14 +70,36 @@ def execute(args, test=False):
raise cmdutil.UserError(msg)
old_setting = bd.settings.get(args[0])
setattr(bd, args[0], args[1])
- else:
- del bd.settings[args[0]]
bd.save()
def get_parser():
parser = cmdutil.CmdOptionParser("be set [NAME] [VALUE]")
return parser
+def get_bugdir_settings():
+ settings = []
+ for s in bugdir.BugDir.settings_properties:
+ settings.append(s)
+ settings.sort()
+ documented_settings = []
+ for s in settings:
+ set = getattr(bugdir.BugDir, s)
+ dstr = set.__doc__.strip()
+ # per-setting comment adjustments
+ if s == "rcs_name":
+ lines = dstr.split('\n')
+ while lines[0].startswith("This property defaults to") == False:
+ lines.pop(0)
+ assert len(lines) != None, \
+ "Unexpected rcs_name docstring:\n '%s'" % dstr
+ lines.insert(
+ 0, "The name of the revision control system to use.\n")
+ dstr = '\n'.join(lines)
+ doc = textwrap.wrap(dstr, width=70, initial_indent=' ',
+ subsequent_indent=' ')
+ documented_settings.append("%s\n%s" % (s, '\n'.join(doc)))
+ return documented_settings
+
longhelp="""
Show or change per-tree settings.
@@ -83,14 +107,11 @@ If name and value are supplied, the name is set to a new value.
If no value is specified, the current value is printed.
If no arguments are provided, all names and values are listed.
-Interesting settings are:
-rcs_name
- The name of the revision control system. "Arch" and "None" are supported.
-target
- The current development goal
-
To unset a setting, set it to "none".
-"""
+
+Allowed settings are:
+
+%s""" % ('\n'.join(get_bugdir_settings()),)
def help():
return get_parser().help_str() + longhelp
diff --git a/becommands/severity.py b/becommands/severity.py
index f8a0c02..36dea6e 100644
--- a/becommands/severity.py
+++ b/becommands/severity.py
@@ -2,7 +2,6 @@
# Marien Zwart <marienz@gentoo.org>
# Thomas Gerigk <tgerigk@gmx.de>
# W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
diff --git a/becommands/show.py b/becommands/show.py
index ff434ab..f700caa 100644
--- a/becommands/show.py
+++ b/becommands/show.py
@@ -3,7 +3,6 @@
# Thomas Gerigk <tgerigk@gmx.de>
# Thomas Habets <thomas@habets.pp.se>
# W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
@@ -19,6 +18,7 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""Show a particular bug"""
+import sys
from libbe import cmdutil, bugdir
__desc__ = __doc__
@@ -40,6 +40,7 @@ def execute(args, test=False):
Bug A
<BLANKLINE>
>>> execute (["--xml", "a"], test=True) # doctest: +ELLIPSIS
+ <?xml version="1.0" encoding="..." ?>
<bug>
<uuid>a</uuid>
<short-name>a</short-name>
@@ -53,27 +54,53 @@ def execute(args, test=False):
parser = get_parser()
options, args = parser.parse_args(args)
cmdutil.default_complete(options, args, parser,
- bugid_args={0: lambda bug : bug.active==True})
+ bugid_args={-1: lambda bug : bug.active==True})
if len(args) == 0:
raise cmdutil.UsageError
bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test)
- for bugid in args:
- bug = bd.bug_from_shortname(bugid)
- if options.dumpXML:
- print bug.xml(show_comments=True)
+ 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:
- print bug.string(show_comments=True)
- if bugid != args[-1]:
- print "" # add a blank line between bugs
+ bugname = shortname
+ is_comment = False
+ bug = bd.bug_from_shortname(bugname)
+ if is_comment == False:
+ if options.dumpXML:
+ print '<?xml version="1.0" encoding="%s" ?>' % bd.encoding
+ print bug.xml(show_comments=True)
+ else:
+ print bug.string(show_comments=True)
+ else:
+ comment = bug.comment_root.comment_from_shortname(
+ shortname, bug_shortname=bugname)
+ if options.dumpXML:
+ 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.dumpXML == False:
+ print "" # add a blank line between bugs/comments
def get_parser():
- parser = cmdutil.CmdOptionParser("be show [options] BUG-ID [BUG-ID ...]")
+ parser = cmdutil.CmdOptionParser("be show [options] ID [ID ...]")
parser.add_option("-x", "--xml", action="store_true",
dest='dumpXML', help="Dump as XML")
+ parser.add_option("--only-raw-body", action="store_true",
+ dest='only_raw_body', help="When printing only a single comment, just print it's body. This allows extraction of non-text content types.")
return parser
longhelp="""
-Show all information about a bug.
+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.
"""
def help():
diff --git a/becommands/status.py b/becommands/status.py
index d8bd4c4..882f7b3 100644
--- a/becommands/status.py
+++ b/becommands/status.py
@@ -1,5 +1,4 @@
# Copyright (C) 2008-2009 W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
@@ -56,24 +55,39 @@ def get_parser():
def help():
- longhelp=["""
-Show or change a bug's status.
-
-If no status is specified, the current value is printed. If a status
-is specified, it will be assigned to the bug.
-
-Status levels are:
-"""]
try: # See if there are any per-tree status configurations
bd = bugdir.BugDir(from_disk=True, manipulate_encodings=False)
except bugdir.NoBugDir, e:
pass # No tree, just show the defaults
longest_status_len = max([len(s) for s in bug.status_values])
- for status in bug.status_values :
+ active_statuses = []
+ for status in bug.active_status_values :
+ description = bug.status_description[status]
+ s = "%*s : %s" % (longest_status_len, status, description)
+ active_statuses.append(s)
+ inactive_statuses = []
+ for status in bug.inactive_status_values :
description = bug.status_description[status]
- s = "%*s : %s\n" % (longest_status_len, status, description)
- longhelp.append(s)
- longhelp = ''.join(longhelp)
+ s = "%*s : %s" % (longest_status_len, status, description)
+ inactive_statuses.append(s)
+ longhelp="""
+Show or change a bug's status.
+
+If no status is specified, the current value is printed. If a status
+is specified, it will be assigned to the bug.
+
+There are two classes of statuses, active and inactive, which are only
+important for commands like "be list" that show only active bugs by
+default.
+
+Active status levels are:
+ %s
+Inactive status levels are:
+ %s
+
+You can overide the list of allowed statuses on a per-repository basis.
+See "be set --help" for more details.
+""" % ('\n '.join(active_statuses), '\n '.join(inactive_statuses))
return get_parser().help_str() + longhelp
def complete(options, args, parser):
diff --git a/becommands/tag.py b/becommands/tag.py
index ab0324e..1b20ddb 100644
--- a/becommands/tag.py
+++ b/becommands/tag.py
@@ -102,7 +102,7 @@ def execute(args, test=False):
else: # add the tag
estrs.append(tag_string)
bug.extra_strings = estrs # reassign to notice change
- bug.save()
+ bd.save()
tags = []
for estr in bug.extra_strings:
diff --git a/becommands/target.py b/becommands/target.py
index 283998a..e2283f4 100644
--- a/becommands/target.py
+++ b/becommands/target.py
@@ -4,7 +4,6 @@
# Marien Zwart <marienz@gentoo.org>
# Thomas Gerigk <tgerigk@gmx.de>
# W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
@@ -20,7 +19,7 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""Show or change a bug's target for fixing"""
-from libbe import cmdutil, bugdir, settings_object
+from libbe import cmdutil, bugdir
__desc__ = __doc__
def execute(args, test=False):
@@ -56,7 +55,7 @@ def execute(args, test=False):
return
bug = bd.bug_from_shortname(args[0])
if len(args) == 1:
- if bug.target is None or bug.target is settings_object.EMPTY:
+ if bug.target is None:
print "No target assigned."
else:
print bug.target
diff --git a/libbe/arch.py b/libbe/arch.py
index 3051b34..8f7603b 100644
--- a/libbe/arch.py
+++ b/libbe/arch.py
@@ -2,7 +2,6 @@
# Ben Finney <ben+python@benfinney.id.au>
# James Rowe <jnrowe@ukfsn.org>
# W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
diff --git a/libbe/beuuid.py b/libbe/beuuid.py
index de67cb7..020ea9f 100644
--- a/libbe/beuuid.py
+++ b/libbe/beuuid.py
@@ -1,5 +1,4 @@
# Copyright (C) 2008-2009 W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
diff --git a/libbe/bug.py b/libbe/bug.py
index dfa49f2..8cb8a0a 100644
--- a/libbe/bug.py
+++ b/libbe/bug.py
@@ -1,7 +1,6 @@
# Copyright (C) 2008-2009 Chris Ball <cjb@laptop.org>
# Thomas Habets <thomas@habets.pp.se>
# W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
@@ -68,7 +67,7 @@ def load_severities(severity_def):
global severity_values
global severity_description
global severity_index
- if severity_def == settings_object.EMPTY:
+ if severity_def == None:
return
severity_values = tuple([val for val,description in severity_def])
severity_description = dict(severity_def)
@@ -88,9 +87,9 @@ def load_status(active_status_def, inactive_status_def):
global status_values
global status_description
global status_index
- if active_status_def == settings_object.EMPTY:
+ if active_status_def == None:
active_status_def = globals()["active_status_def"]
- if inactive_status_def == settings_object.EMPTY:
+ if inactive_status_def == None:
inactive_status_def = globals()["inactive_status_def"]
active_status_values = tuple([val for val,description in active_status_def])
inactive_status_values = tuple([val for val,description in inactive_status_def])
@@ -178,7 +177,7 @@ class Bug(settings_object.SavedSettingsObject):
def time_string(): return {}
def _get_time(self):
- if self.time_string == None or self.time_string == settings_object.EMPTY:
+ if self.time_string in [None, settings_object.EMPTY]:
return None
return utility.str_to_time(self.time_string)
def _set_time(self, value):
@@ -255,7 +254,7 @@ class Bug(settings_object.SavedSettingsObject):
def _setting_attr_string(self, setting):
value = getattr(self, setting)
- if value == settings_object.EMPTY:
+ if value in [None, settings_object.EMPTY]:
return ""
else:
return str(value)
@@ -283,7 +282,7 @@ class Bug(settings_object.SavedSettingsObject):
("summary", self.summary)]
ret = '<bug>\n'
for (k,v) in info:
- if v is not settings_object.EMPTY:
+ if v is not None:
ret += ' <%s>%s</%s>\n' % (k,xml.sax.saxutils.escape(v),k)
for estr in self.extra_strings:
ret += ' <extra-string>%s</extra-string>\n' % estr
@@ -477,8 +476,8 @@ def cmp_attr(bug_1, bug_2, attr, invert=False):
return 1
val_1 = getattr(bug_1, attr)
val_2 = getattr(bug_2, attr)
- if val_1 == settings_object.EMPTY: val_1 = None
- if val_2 == settings_object.EMPTY: val_2 = None
+ if val_1 == None: val_1 = None
+ if val_2 == None: val_2 = None
if invert == True :
return -cmp(val_1, val_2)
diff --git a/libbe/bugdir.py b/libbe/bugdir.py
index d4a39cb..fed9aa3 100644
--- a/libbe/bugdir.py
+++ b/libbe/bugdir.py
@@ -3,7 +3,6 @@
# Chris Ball <cjb@laptop.org>
# Oleg Romanyshyn <oromanyshyn@panoramicfeedback.com>
# W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
@@ -136,7 +135,7 @@ class BugDir (list, settings_object.SavedSettingsObject):
return settings_object.versioned_property(**kwargs)
@_versioned_property(name="target",
- doc="The current project development target")
+ doc="The current project development target.")
def target(): return {}
def _guess_encoding(self):
diff --git a/libbe/bzr.py b/libbe/bzr.py
index d73392a..56a1648 100644
--- a/libbe/bzr.py
+++ b/libbe/bzr.py
@@ -2,7 +2,6 @@
# Ben Finney <ben+python@benfinney.id.au>
# Marien Zwart <marienz@gentoo.org>
# W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
diff --git a/libbe/cmdutil.py b/libbe/cmdutil.py
index edc6442..7589241 100644
--- a/libbe/cmdutil.py
+++ b/libbe/cmdutil.py
@@ -1,7 +1,6 @@
# Copyright (C) 2005-2009 Aaron Bentley and Panometrics, Inc.
# Oleg Romanyshyn <oromanyshyn@panoramicfeedback.com>
# W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
@@ -16,6 +15,7 @@
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+import glob
import optparse
import os
from textwrap import TextWrapper
@@ -32,10 +32,10 @@ class UserError(Exception):
def __init__(self, msg):
Exception.__init__(self, msg)
-class UserErrorWrap(UserError):
- def __init__(self, exception):
- UserError.__init__(self, str(exception))
- self.exception = exception
+class UnknownCommand(UserError):
+ def __init__(self, cmd):
+ Exception.__init__(self, "Unknown command '%s'" % cmd)
+ self.cmd = cmd
class UsageError(Exception):
pass
@@ -56,24 +56,27 @@ def iter_commands():
def get_command(command_name):
"""Retrieves the module for a user command
- >>> get_command("asdf")
- Traceback (most recent call last):
- UserError: Unknown command asdf
+ >>> try:
+ ... get_command("asdf")
+ ... except UnknownCommand, e:
+ ... print e
+ Unknown command 'asdf'
>>> repr(get_command("list")).startswith("<module 'becommands.list' from ")
True
"""
cmd = plugin.get_plugin("becommands", command_name.replace("-", "_"))
if cmd is None:
- raise UserError("Unknown command %s" % command_name)
+ raise UnknownCommand(command_name)
return cmd
def execute(cmd, args):
enc = encoding.get_encoding()
- get_command(cmd).execute([a.decode(enc) for a in args])
+ cmd = get_command(cmd)
+ cmd.execute([a.decode(enc) for a in args])
return 0
-def help(cmd=None):
+def help(cmd=None, parser=None):
if cmd != None:
return get_command(cmd).help()
else:
@@ -82,17 +85,15 @@ def help(cmd=None):
cmdlist.append((name, module.__desc__))
longest_cmd_len = max([len(name) for name,desc in cmdlist])
ret = ["Bugs Everywhere - Distributed bug tracking",
- "",
- "usage: be [command] [command_options ...] [command_args ...]",
- "or: be help",
- "or: be help [command]",
- "",
- "Supported commands"]
+ "", "Supported commands"]
for name, desc in cmdlist:
numExtraSpaces = longest_cmd_len-len(name)
ret.append("be %s%*s %s" % (name, numExtraSpaces, "", desc))
-
- return "\n".join(ret)
+ ret.extend(["", "Run", " be help [command]", "for more information."])
+ longhelp = "\n".join(ret)
+ if parser == None:
+ return longhelp
+ return parser.help_str() + "\n" + longhelp
def completions(cmd):
parser = get_command(cmd).get_parser()
@@ -106,6 +107,13 @@ def raise_get_help(option, opt, value, parser):
def raise_get_completions(option, opt, value, parser):
print "got completion arg"
+ if hasattr(parser, "command") and parser.command == "be":
+ comps = []
+ for command, module in iter_commands():
+ comps.append(command)
+ for opt in parser.option_list:
+ comps.append(opt.get_opt_string())
+ raise GetCompletions(comps)
raise GetCompletions(completions(sys.argv[1]))
class CmdOptionParser(optparse.OptionParser):
@@ -141,7 +149,7 @@ def option_value_pairs(options, parser):
def default_complete(options, args, parser, bugid_args={}):
"""
- A dud complete implementation for becommands to that the
+ A dud complete implementation for becommands so that the
--complete argument doesn't cause any problems. Use this
until you've set up a command-specific complete function.
@@ -149,15 +157,25 @@ def default_complete(options, args, parser, bugid_args={}):
arguments taking bug shortnames and the values are functions for
filtering, since that's a common enough operation.
e.g. for "be open [options] BUGID"
- bugid_args = {0: lambda bug : bug.active == False}
+ bugid_args = {0: lambda bug : bug.active == False}
+ A positional argument of -1 specifies all remaining arguments
+ (e.g in the case of "be show BUGID BUGID ...").
"""
for option,value in option_value_pairs(options, parser):
if value == "--complete":
raise cmdutil.GetCompletions()
+ if len(bugid_args.keys()) > 0:
+ max_pos_arg = max(bugid_args.keys())
+ else:
+ max_pos_arg = -1
for pos,value in enumerate(args):
if value == "--complete":
+ filter = None
if pos in bugid_args:
filter = bugid_args[pos]
+ if pos > max_pos_arg and -1 in bugid_args:
+ filter = bugid_args[-1]
+ if filter != None:
bugshortnames = []
try:
bd = bugdir.BugDir(from_disk=True,
@@ -170,6 +188,13 @@ def default_complete(options, args, parser, bugid_args={}):
raise GetCompletions(bugshortnames)
raise GetCompletions()
+def complete_path(path):
+ """List possible path completions for path."""
+ comps = glob.glob(path+"*") + glob.glob(path+"/*")
+ if len(comps) == 1 and os.path.isdir(comps[0]):
+ comps.extend(glob.glob(comps[0]+"/*"))
+ return comps
+
def underlined(instring):
"""Produces a version of a string that is underlined with '='
diff --git a/libbe/comment.py b/libbe/comment.py
index 9d4f744..68deaf3 100644
--- a/libbe/comment.py
+++ b/libbe/comment.py
@@ -2,7 +2,6 @@
# Copyright (C) 2008-2009 Chris Ball <cjb@laptop.org>
# Thomas Habets <thomas@habets.pp.se>
# W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
@@ -18,12 +17,17 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA
-import email.mime.base, email.encoders
+import base64
import os
import os.path
+import sys
import time
+import types
+try: # import core module, Python >= 2.5
+ from xml.etree import ElementTree
+except ImportError: # look for non-core module
+ from elementtree import ElementTree
import xml.sax.saxutils
-import textwrap
import doctest
from beuuid import uuid_gen
@@ -43,16 +47,33 @@ class InvalidShortname(KeyError):
self.shortname = shortname
self.shortnames = shortnames
+class InvalidXML(ValueError):
+ def __init__(self, element, comment):
+ msg = "Invalid comment xml: %s\n %s\n" \
+ % (comment, ElementTree.tostring(element))
+ ValueError.__init__(self, msg)
+ self.element = element
+ self.comment = comment
+
+class MissingReference(ValueError):
+ def __init__(self, comment):
+ msg = "Missing reference to %s" % (comment.in_reply_to)
+ ValueError.__init__(self, msg)
+ self.reference = comment.in_reply_to
+ self.comment = comment
INVALID_UUID = "!!~~\n INVALID-UUID \n~~!!"
-def _list_to_root(comments, bug):
+def list_to_root(comments, bug, root=None,
+ ignore_missing_references=False):
"""
- Convert a raw list of comments to single (dummy) root comment. We
- use a dummy root comment, because there can be several comment
- threads rooted on the same parent bug. To simplify comment
- interaction, we condense these threads into a single thread with a
- Comment dummy root.
+ Convert a raw list of comments to single root comment. We use a
+ dummy root comment by default, because there can be several
+ comment threads rooted on the same parent bug. To simplify
+ comment interaction, we condense these threads into a single
+ thread with a Comment dummy root. Can also be used to append
+ a list of subcomments to a non-dummy root comment, so long as
+ all the new comments are descendants of the root comment.
No Comment method should use the dummy comment.
"""
@@ -61,17 +82,34 @@ def _list_to_root(comments, bug):
for comment in comments:
assert comment.uuid != None
uuid_map[comment.uuid] = comment
+ for comment in comments:
+ if comment.alt_id != None and comment.alt_id not in uuid_map:
+ uuid_map[comment.alt_id] = comment
+ if root == None:
+ root = Comment(bug, uuid=INVALID_UUID)
+ else:
+ uuid_map[root.uuid] = root
for comm in comments:
+ if comm.in_reply_to == INVALID_UUID:
+ comm.in_reply_to = None
rep = comm.in_reply_to
- if rep == None or rep == settings_object.EMPTY or rep == bug.uuid:
+ if rep == None or rep == bug.uuid:
root_comments.append(comm)
else:
parentUUID = comm.in_reply_to
- parent = uuid_map[parentUUID]
- parent.add_reply(comm)
- dummy_root = Comment(bug, uuid=INVALID_UUID)
- dummy_root.extend(root_comments)
- return dummy_root
+ try:
+ parent = uuid_map[parentUUID]
+ parent.add_reply(comm)
+ except KeyError, e:
+ if ignore_missing_references == True:
+ print >> sys.stderr, \
+ "Ignoring missing reference to %s" % parentUUID
+ comm.in_reply_to = None
+ root_comments.append(comm)
+ else:
+ raise MissingReference(comm)
+ root.extend(root_comments)
+ return root
def loadComments(bug, load_full=False):
"""
@@ -90,7 +128,7 @@ def loadComments(bug, load_full=False):
comm.load_settings()
dummy = comm.body # force the body to load
comments.append(comm)
- return _list_to_root(comments, bug)
+ return list_to_root(comments, bug)
def saveComments(bug):
path = bug.get_path("comments")
@@ -122,6 +160,10 @@ class Comment(Tree, settings_object.SavedSettingsObject):
kwargs["required_saved_properties"]=required_saved_properties
return settings_object.versioned_property(**kwargs)
+ @_versioned_property(name="Alt-id",
+ doc="Alternate ID for linking imported comments. Internally comments are linked (via In-reply-to) to the parent's UUID. However, these UUIDs are generated internally, so Alt-id is provided as a user-controlled linking target.")
+ def alt_id(): return {}
+
@_versioned_property(name="From",
doc="The author of the comment")
def From(): return {}
@@ -217,7 +259,7 @@ class Comment(Tree, settings_object.SavedSettingsObject):
def _setting_attr_string(self, setting):
value = getattr(self, setting)
- if value == settings_object.EMPTY:
+ if value in [None, settings_object.EMPTY]:
return ""
else:
return str(value)
@@ -248,8 +290,9 @@ class Comment(Tree, settings_object.SavedSettingsObject):
msg = email.mime.base.MIMEBase(maintype, subtype)
msg.set_payload(self.body or "")
email.encoders.encode_base64(msg)
- body = msg.as_string()
+ body = base64.encodestring(self.body or "")
info = [("uuid", self.uuid),
+ ("alt-id", self.alt_id),
("short-name", shortname),
("in-reply-to", self.in_reply_to),
("from", self._setting_attr_string("From")),
@@ -258,13 +301,77 @@ class Comment(Tree, settings_object.SavedSettingsObject):
("body", body)]
lines = ["<comment>"]
for (k,v) in info:
- if v not in [settings_object.EMPTY, None]:
+ if v != None:
lines.append(' <%s>%s</%s>' % (k,xml.sax.saxutils.escape(v),k))
lines.append("</comment>")
istring = ' '*indent
sep = '\n' + istring
return istring + sep.join(lines).rstrip('\n')
+ def from_xml(self, xml_string, verbose=True):
+ """
+ Note: If alt-id is not given, translates any <uuid> fields to
+ <alt-id> fields.
+ >>> commA = Comment(bug=None, body="Some\\ninsightful\\nremarks\\n")
+ >>> commA.uuid = "0123"
+ >>> commA.time_string = "Thu, 01 Jan 1970 00:00:00 +0000"
+ >>> xml = commA.xml(shortname="com-1")
+ >>> commB = Comment()
+ >>> commB.from_xml(xml)
+ >>> attrs=['uuid','alt_id','in_reply_to','From','time_string','content_type','body']
+ >>> for attr in attrs: # doctest: +ELLIPSIS
+ ... if getattr(commB, attr) != getattr(commA, attr):
+ ... estr = "Mismatch on %s: '%s' should be '%s'"
+ ... args = (attr, getattr(commB, attr), getattr(commA, attr))
+ ... print estr % args
+ Mismatch on uuid: '...' should be '0123'
+ Mismatch on alt_id: '0123' should be 'None'
+ >>> print commB.alt_id
+ 0123
+ >>> commA.From
+ >>> commB.From
+ """
+ if type(xml_string) == types.UnicodeType:
+ xml_string = xml_string.strip().encode("unicode_escape")
+ comment = ElementTree.XML(xml_string)
+ if comment.tag != "comment":
+ raise InvalidXML(comment, "root element must be <comment>")
+ tags=['uuid','alt-id','in-reply-to','from','date','content-type','body']
+ uuid = None
+ body = None
+ for child in comment.getchildren():
+ if child.tag == "short-name":
+ pass
+ elif child.tag in tags:
+ if child.text == None or len(child.text) == 0:
+ text = settings_object.EMPTY
+ else:
+ text = xml.sax.saxutils.unescape(child.text)
+ text = unicode(text).decode("unicode_escape").strip()
+ if child.tag == "uuid":
+ uuid = text
+ continue # don't set the bug's uuid tag.
+ if child.tag == "body":
+ body = text
+ continue # don't set the bug's body yet.
+ elif child.tag == 'from':
+ attr_name = "From"
+ elif child.tag == 'date':
+ attr_name = 'time_string'
+ else:
+ attr_name = child.tag.replace('-','_')
+ setattr(self, attr_name, text)
+ elif verbose == True:
+ print >> sys.stderr, "Ignoring unknown tag %s in %s" \
+ % (child.tag, comment.tag)
+ if self.alt_id == None and uuid not in [None, self.uuid]:
+ self.alt_id = uuid
+ if body != None:
+ if self.content_type.startswith("text/"):
+ self.body = body+"\n" # restore trailing newline
+ else:
+ self.body = base64.decodestring(body)
+
def string(self, indent=0, shortname=None):
"""
>>> comm = Comment(bug=None, body="Some\\ninsightful\\nremarks\\n")
@@ -287,13 +394,10 @@ class Comment(Tree, settings_object.SavedSettingsObject):
lines.append("From: %s" % (self._setting_attr_string("From")))
lines.append("Date: %s" % self.time_string)
lines.append("")
- #lines.append(textwrap.fill(self.body or "",
- # width=(79-indent)))
if self.content_type.startswith("text/"):
lines.extend((self.body or "").splitlines())
else:
lines.append("Content type %s not printable. Try XML output instead" % self.content_type)
- # some comments shouldn't be wrapped...
istring = ' '*indent
sep = '\n' + istring
@@ -335,9 +439,6 @@ class Comment(Tree, settings_object.SavedSettingsObject):
def save(self):
assert self.body != None, "Can't save blank comment"
- #if self.in_reply_to == None:
- # raise Exception, str(self)+'\n'+str(self.settings)+'\n'+str(self._settings_loaded)
- #assert self.in_reply_to != None, "Comment must be a reply to something"
self.save_settings()
self._set_comment_body(new=self.body, force=True)
@@ -362,7 +463,6 @@ class Comment(Tree, settings_object.SavedSettingsObject):
"""
reply = Comment(self.bug, body=body)
self.add_reply(reply)
- #raise Exception, "new reply added (%s),\n%s\n%s\n\t--%s--" % (body, self, reply, reply.in_reply_to)
return reply
def string_thread(self, string_method_name="string", name_map={},
diff --git a/libbe/config.py b/libbe/config.py
index 7f600a5..fafb4f0 100644
--- a/libbe/config.py
+++ b/libbe/config.py
@@ -1,6 +1,5 @@
# Copyright (C) 2005-2009 Aaron Bentley and Panometrics, Inc.
# W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
diff --git a/libbe/diff.py b/libbe/diff.py
index a349e14..13efc9f 100644
--- a/libbe/diff.py
+++ b/libbe/diff.py
@@ -1,6 +1,5 @@
# Copyright (C) 2005-2009 Aaron Bentley and Panometrics, Inc.
# W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
diff --git a/libbe/editor.py b/libbe/editor.py
index 6638ed9..4bab0fa 100644
--- a/libbe/editor.py
+++ b/libbe/editor.py
@@ -1,6 +1,5 @@
# Bugs Everywhere, a distributed bugtracker
# Copyright (C) 2008-2009 W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
diff --git a/libbe/encoding.py b/libbe/encoding.py
index bdac98e..84c360a 100644
--- a/libbe/encoding.py
+++ b/libbe/encoding.py
@@ -1,6 +1,5 @@
# Bugs Everywhere, a distributed bugtracker
# Copyright (C) 2008-2009 W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
diff --git a/libbe/mapfile.py b/libbe/mapfile.py
index 9ff2215..b183bfe 100644
--- a/libbe/mapfile.py
+++ b/libbe/mapfile.py
@@ -1,6 +1,5 @@
# Copyright (C) 2005-2009 Aaron Bentley and Panometrics, Inc.
# W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
@@ -68,9 +67,9 @@ def generate(map):
assert(':' not in key)
assert(len(key) > 0)
except AssertionError:
- raise IllegalKey(key.encode('string_escape'))
+ raise IllegalKey(unicode(key).encode('unicode_escape'))
if "\n" in map[key]:
- raise IllegalValue(map[key].encode('string_escape'))
+ raise IllegalValue(unicode(map[key]).encode('unicode_escape'))
lines = []
for key in keys:
diff --git a/libbe/plugin.py b/libbe/plugin.py
index 3c7c753..a21ba91 100644
--- a/libbe/plugin.py
+++ b/libbe/plugin.py
@@ -1,7 +1,6 @@
# Copyright (C) 2005-2009 Aaron Bentley and Panometrics, Inc.
# Marien Zwart <marienz@gentoo.org>
# W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
diff --git a/libbe/rcs.py b/libbe/rcs.py
index 2c416f4..844920a 100644
--- a/libbe/rcs.py
+++ b/libbe/rcs.py
@@ -3,7 +3,6 @@
# Ben Finney <ben+python@benfinney.id.au>
# Chris Ball <cjb@laptop.org>
# W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
diff --git a/libbe/settings_object.py b/libbe/settings_object.py
index 7326d1b..1dadd0a 100644
--- a/libbe/settings_object.py
+++ b/libbe/settings_object.py
@@ -125,17 +125,17 @@ def versioned_property(name, doc,
required_saved_properties.append(name)
def decorator(funcs):
fulldoc = doc
- if default != None:
+ if default != None or generator == None:
defaulting = defaulting_property(default=default, null=EMPTY,
default_mutable=mutable)
- fulldoc += "\n\nThis property defaults to %s" % default
+ fulldoc += "\n\nThis property defaults to %s." % default
if generator != None:
cached = cached_property(generator=generator, initVal=EMPTY,
mutable=mutable)
- fulldoc += "\n\nThis property is generated with %s" % generator
+ fulldoc += "\n\nThis property is generated with %s." % generator
if check_fn != None:
fn_checked = fn_checked_property(value_allowed_fn=check_fn)
- fulldoc += "\n\nThis property is checked with %s" % check_fn
+ fulldoc += "\n\nThis property is checked with %s." % check_fn
if allowed != None:
checked = checked_property(allowed=allowed)
fulldoc += "\n\nThe allowed values for this property are: %s." \
@@ -145,7 +145,7 @@ def versioned_property(name, doc,
settings = settings_property(name=name, null=UNPRIMED)
docp = doc_property(doc=fulldoc)
deco = hooked(primed(settings(docp(funcs))))
- if default != None:
+ if default != None or generator == None:
deco = defaulting(deco)
if generator != None:
deco = cached(deco)
@@ -235,7 +235,7 @@ class SavedSettingsObjectTests(unittest.TestCase):
# access missing setting
self.failUnless(t._settings_loaded == False, t._settings_loaded)
self.failUnless(len(t.settings) == 0, len(t.settings))
- self.failUnless(t.content_type == EMPTY, t.content_type)
+ self.failUnless(t.content_type == None, t.content_type)
# accessing t.content_type triggers the priming, which runs
# t._setup_saved_settings, which fills out t.settings with
# EMPTY data. t._settings_loaded is still false though, since
@@ -250,16 +250,16 @@ class SavedSettingsObjectTests(unittest.TestCase):
self.failUnless(t._settings_loaded == True, t._settings_loaded)
self.failUnless(t.settings["Content-type"] == EMPTY,
t.settings["Content-type"])
- self.failUnless(t.content_type == EMPTY, t.content_type)
+ self.failUnless(t.content_type == None, t.content_type)
self.failUnless(len(t.settings) == 1, len(t.settings))
self.failUnless(t.settings["Content-type"] == EMPTY,
t.settings["Content-type"])
# now we set a value
- t.content_type = None
- self.failUnless(t.settings["Content-type"] == None,
+ t.content_type = 5
+ self.failUnless(t.settings["Content-type"] == 5,
t.settings["Content-type"])
- self.failUnless(t.content_type == None, t.content_type)
- self.failUnless(t.settings["Content-type"] == None,
+ self.failUnless(t.content_type == 5, t.content_type)
+ self.failUnless(t.settings["Content-type"] == 5,
t.settings["Content-type"])
# now we set another value
t.content_type = "text/plain"
@@ -273,7 +273,7 @@ class SavedSettingsObjectTests(unittest.TestCase):
self.failUnless(t._settings_loaded == True, t._settings_loaded)
self.failUnless(t.settings["Content-type"] == EMPTY,
t.settings["Content-type"])
- self.failUnless(t.content_type == EMPTY, t.content_type)
+ self.failUnless(t.content_type == None, t.content_type)
self.failUnless(len(t.settings) == 1, len(t.settings))
self.failUnless(t.settings["Content-type"] == EMPTY,
t.settings["Content-type"])
@@ -376,7 +376,7 @@ class SavedSettingsObjectTests(unittest.TestCase):
t.load_settings()
self.failUnless(SAVES == [], SAVES)
self.failUnless(t._settings_loaded == True, t._settings_loaded)
- self.failUnless(t.list_type == EMPTY, t.list_type)
+ self.failUnless(t.list_type == None, t.list_type)
self.failUnless(SAVES == [
"'None' -> '<class 'libbe.settings_object.EMPTY'>'"
], SAVES)
diff --git a/libbe/utility.py b/libbe/utility.py
index 8a0f318..e16b94a 100644
--- a/libbe/utility.py
+++ b/libbe/utility.py
@@ -1,6 +1,5 @@
# Copyright (C) 2005-2009 Aaron Bentley and Panometrics, Inc.
# W. Trevor King <wking@drexel.edu>
-# <abentley@panoramicfeedback.com>
#
# 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
@@ -21,6 +20,7 @@ import os
import shutil
import tempfile
import time
+import types
import doctest
@@ -77,17 +77,34 @@ def time_to_str(time_val):
return time.strftime(RFC_2822_TIME_FMT, time.gmtime(time_val))
def str_to_time(str_time):
- """Convert an RFC 2822-fomatted string into a time falue.
+ """Convert an RFC 2822-fomatted string into a time value.
>>> str_to_time("Thu, 01 Jan 1970 00:00:00 +0000")
0
>>> q = time.time()
>>> str_to_time(time_to_str(q)) == int(q)
True
+ >>> str_to_time("Thu, 01 Jan 1970 00:00:00 -1000")
+ 36000
"""
- return calendar.timegm(time.strptime(str_time, RFC_2822_TIME_FMT))
+ timezone_str = str_time[-5:]
+ if timezone_str != "+0000":
+ str_time = str_time.replace(timezone_str, "+0000")
+ time_val = calendar.timegm(time.strptime(str_time, RFC_2822_TIME_FMT))
+ timesign = -int(timezone_str[0]+"1") # "+" -> time_val ahead of GMT
+ timezone_tuple = time.strptime(timezone_str[1:], "%H%M")
+ timezone = timezone_tuple.tm_hour*3600 + timezone_tuple.tm_min*60
+ return time_val + timesign*timezone
def handy_time(time_val):
return time.strftime("%a, %d %b %Y %H:%M", time.localtime(time_val))
+def time_to_gmtime(str_time):
+ """Convert an RFC 2822-fomatted string to a GMT string.
+ >>> time_to_gmtime("Thu, 01 Jan 1970 00:00:00 -1000")
+ 'Thu, 01 Jan 1970 10:00:00 +0000'
+ """
+ time_val = str_to_time(str_time)
+ return time_to_str(time_val)
+
suite = doctest.DocTestSuite()
diff --git a/test_usage.sh b/test_usage.sh
index b2e2cab..05832d3 100755
--- a/test_usage.sh
+++ b/test_usage.sh
@@ -124,7 +124,13 @@ BUGB=`echo "$OUT" | sed -n 's/Created bug with ID //p'`
be comment $BUGB "Blissfully unaware of a similar bug"
be merge $BUG $BUGB # join BUGB to BUG
be show $BUG # show bug details & comments
+# you can also export/import XML bugs/comments
+OUT=`be new 'yet more fun'`
+BUGC=`echo "$OUT" | sed -n 's/Created bug with ID //p'`
+be comment $BUGC "The ants go marching..."
+be show --xml $BUGC | be comment --xml ${BUG}:2 -
be remove $BUG # decide that you don't like that bug after all
+
cd /
rm -rf $TESTDIR
diff --git a/xml/be-mbox-to-xml b/xml/be-mbox-to-xml
new file mode 100755
index 0000000..9054cfd
--- /dev/null
+++ b/xml/be-mbox-to-xml
@@ -0,0 +1,104 @@
+#!/usr/bin/env python
+# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+Convert an mbox into xml suitable for imput into be.
+ $ cat mbox | be-mbox-to-xml | be comment --xml <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.
+"""
+
+import base64
+import email.utils
+from libbe.encoding import get_encoding, set_IO_stream_encodings
+from mailbox import mbox, Message # the mailbox people really want an on-disk copy
+from time import asctime, gmtime
+import types
+from xml.sax import make_parser
+from xml.sax.handler import ContentHandler
+from xml.sax.saxutils import escape
+
+DEFAULT_ENCODING = get_encoding()
+set_IO_stream_encodings(DEFAULT_ENCODING)
+
+def comment_message_to_xml(message, fields=None):
+ if fields == None:
+ fields = {}
+ new_fields = {}
+ new_fields[u'alt-id'] = message[u'message-id']
+ new_fields[u'in-reply-to'] = message[u'in-reply-to']
+ new_fields[u'from'] = message[u'from']
+ new_fields[u'date'] = message[u'date']
+ new_fields[u'content-type'] = message.get_content_type()
+ for k,v in new_fields.items():
+ if v != None and type(v) != types.UnicodeType:
+ fields[k] = unicode(v, encoding=DEFAULT_ENCODING)
+ elif v == None and k in fields:
+ new_fields[k] = fields[k]
+ for k,v in fields.items():
+ if k not in new_fields:
+ new_fields.k = fields[k]
+ fields = new_fields
+
+ if message.is_multipart():
+ ret = []
+ alt_id = fields[u'alt-id']
+ from_str = fields[u'from']
+ date = fields[u'date']
+ for m in message.walk():
+ if m == message:
+ continue
+ fields[u'from'] = from_str
+ fields[u'date'] = date
+ if len(ret) >= 0:
+ fields.pop(u'alt-id')
+ fields[u'in-reply-to'] = alt_id
+ ret.append(comment_message_to_xml(m, fields))
+ return u'\n'.join(ret)
+
+ charset = message.get_content_charset(DEFAULT_ENCODING).lower()
+ #assert charset == DEFAULT_ENCODING.lower(), \
+ # u"Unknown charset: %s" % charset
+
+ encoding = message[u'content-transfer-encoding'].lower()
+ body = message.get_payload(decode=True) # attempt to decode
+ assert body != None, "Unable to decode?"
+ if fields[u'content-type'].startswith(u"text/"):
+ body = unicode(body, encoding=charset).rstrip(u'\n')
+ else:
+ body = base64.encode(body)
+ fields[u'body'] = body
+ lines = [u"<comment>"]
+ for tag,body in fields.items():
+ if body != None:
+ ebody = escape(body)
+ lines.append(u" <%s>%s</%s>" % (tag, ebody, tag))
+ lines.append(u"</comment>")
+ return u'\n'.join(lines)
+
+def main(mbox_filename):
+ mb = mbox(mbox_filename)
+ print u'<?xml version="1.0" encoding="%s" ?>' % DEFAULT_ENCODING
+ print u"<comment-list>"
+ for message in mb:
+ print comment_message_to_xml(message)
+ print u"</comment-list>"
+
+
+if __name__ == "__main__":
+ import sys
+ main(sys.argv[1])
diff --git a/xml/be-xml-to-mbox b/xml/be-xml-to-mbox
index 80db634..b74a33d 100755
--- a/xml/be-xml-to-mbox
+++ b/xml/be-xml-to-mbox
@@ -26,14 +26,16 @@ followed by a blank line.
"""
#from mailbox import mbox, Message # the mailbox people really want an on-disk copy
+import codecs
import email.utils
-import types
-
from libbe.encoding import get_encoding, set_IO_stream_encodings
from libbe.utility import str_to_time as rfc2822_to_gmtime_integer
from time import asctime, gmtime
-from xml.sax import make_parser
-from xml.sax.handler import ContentHandler
+import types
+try: # import core module, Python >= 2.5
+ from xml.etree import ElementTree
+except ImportError: # look for non-core module
+ from elementtree import ElementTree
from xml.sax.saxutils import unescape
@@ -83,7 +85,7 @@ class Bug (LimitedAttrDict):
u"created",
u"summary",
u"comments",
- u"extra_strings"]
+ u"extra-strings"]
def print_to_mbox(self):
name,addr = email.utils.parseaddr(self["creator"])
print "From %s %s" % (addr, rfc2822_to_asctime(self["created"]))
@@ -96,28 +98,62 @@ class Bug (LimitedAttrDict):
print ""
print self["summary"]
print ""
- if len(self["extra_strings"]) > 0:
+ if "extra-strings" in self:
print "extra strings:\n ",
print '\n '.join(self["extra_strings"])
print ""
- for comment in self["comments"]:
- comment.print_to_mbox(self)
+ if "comments" in self:
+ for comment in self["comments"]:
+ comment.print_to_mbox(self)
+ def init_from_etree(self, element):
+ assert element.tag == "bug", element.tag
+ for field in element.getchildren():
+ text = unescape(unicode(field.text).decode("unicode_escape").strip())
+ if field.tag == "comment":
+ comm = Comment()
+ comm.init_from_etree(field)
+ if "comments" in self:
+ self["comments"].append(comm)
+ else:
+ self["comments"] = [comm]
+ elif field.tag == "extra-string":
+ if "extra-strings" in self:
+ self["extra-strings"].append(text)
+ else:
+ self["extra-strings"] = [text]
+ else:
+ self[field.tag] = text
class Comment (LimitedAttrDict):
_attrs = [u"uuid",
+ u"alt-id",
u"short-name",
u"in-reply-to",
u"from",
u"date",
u"content-type",
u"body"]
- def print_to_mbox(self, bug):
+ def print_to_mbox(self, bug=None):
+ if bug == None:
+ bug = Bug()
+ bug[u"uuid"] = u"no-uuid"
name,addr = email.utils.parseaddr(self["from"])
print "From %s %s" % (addr, rfc2822_to_asctime(self["date"]))
- print "Message-ID: <%s@%s>" % (self["uuid"], DEFAULT_DOMAIN)
+ if "uuid" in self: id = self["uuid"]
+ elif "alt-id" in self: id = self["alt-id"]
+ else: id = None
+ if id != None:
+ print "Message-ID: <%s@%s>" % (id, DEFAULT_DOMAIN)
print "Date: %s" % self["date"]
print "From: %s" % self["from"]
- print "Subject: %s: %s" % (self["short-name"], bug["summary"])
+ subject = ""
+ if "short-name" in self:
+ subject += self["short-name"]+u": "
+ if "summary" in bug:
+ subject += bug["summary"]
+ else:
+ subject += u"no-subject"
+ print "Subject: %s" % subject
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)
@@ -129,72 +165,41 @@ class Comment (LimitedAttrDict):
else: # content type and transfer encoding already in XML MIME output
print self["body"]
print ""
+ def init_from_etree(self, element):
+ 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
-class BE_list_handler (ContentHandler):
- def __init__(self):
- self.reset()
-
- def reset(self):
- self.bug = None
- self.comment = None
- self.extra_strings = None
- self.text_field = None
-
- def startElement(self, name, attributes):
- if name == "bug":
- assert self.bug == None, "Nested bugs?!"
- assert self.comment == None
- assert self.text_field == None
- self.bug = Bug(comments=[], extra_strings=[])
- elif name == "comment":
- assert self.bug != None, "<comment> not in <bug>?"
- assert self.comment == None, "Nested comments?!"
- assert self.text_field == None, "<comment> in text field %s?" % self.text_field
- self.comment = Comment()
- elif self.bug != None and self.comment == None:
- # parse bug text field
- self.text_field = name
- self.text_data = ""
- elif self.bug != None and self.comment != None:
- # parse comment text field
- self.text_field = name
- self.text_data = ""
-
- def endElement(self, name):
- if name == "bug":
- assert self.bug != None, "Invalid XML?"
- assert self.comment == None, "Invalid XML?"
- assert self.text_field == None, "Invalid XML?"
- self.bug.print_to_mbox()
- self.bug = None
- elif name == "comment":
- assert self.bug != None, "<comment> not in <bug>?"
- assert self.comment != None, "Invalid XML?"
- assert self.text_field == None, "<comment> in text field %s?" % self.text_field
- self.bug["comments"].append(self.comment)
- # comments printed by bug.print_to_mbox()
- self.comment = None
- elif self.bug != None and self.comment == None:
- # parse bug text field
- if self.text_field == "extra-string":
- self.bug["extra_strings"].append(unescape(self.text_data.strip()))
- else:
- self.bug[self.text_field] = unescape(self.text_data.strip())
- self.text_field = None
- self.text_data = None
- elif self.bug != None and self.comment != None:
- # parse comment text field
- self.comment[self.text_field] = unescape(self.text_data.strip())
- self.text_field = None
- self.text_data = None
-
- def characters(self, data):
- if self.text_field != None:
- self.text_data += data
+def print_to_mbox(element):
+ if element.tag == "bug":
+ b = Bug()
+ b.init_from_etree(element)
+ b.print_to_mbox()
+ elif element.tag == "comment":
+ 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()
if __name__ == "__main__":
import sys
-
- parser = make_parser()
- parser.setContentHandler(BE_list_handler())
- parser.parse(sys.stdin)
+
+ if len(sys.argv) == 1: # no filename given, use stdin
+ xml_unicode = sys.stdin.read()
+ else:
+ xml_unicode = codecs.open(sys.argv[1], "r", DEFAULT_ENCODING).read()
+ xml_str = xml_unicode.encode("unicode_escape").replace(r"\n", "\n")
+ elist = ElementTree.XML(xml_str)
+ print_to_mbox(elist)