aboutsummaryrefslogtreecommitdiffstats
path: root/libbe
diff options
context:
space:
mode:
authorChris Ball <cjb@laptop.org>2009-07-13 11:45:40 -0400
committerChris Ball <cjb@laptop.org>2009-07-13 11:45:40 -0400
commit5e249abfee7273c79640c4211607a6b4bf7b374c (patch)
treed588c5c801e142b59af53b9f9c15e3f9e1982737 /libbe
parent64f62aa2bb841c483e8cb2b663434b3ad3038f4c (diff)
parent17adbfb1c04684b986bf2c97cc4fa5197198aadc (diff)
downloadbugseverywhere-5e249abfee7273c79640c4211607a6b4bf7b374c.tar.gz
Large merge from W. Trevor King. Highlights:
be show --only-raw-body be-mbox-to-xml be-xml-to-mbox be comment --xml be --dir
Diffstat (limited to 'libbe')
-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
16 files changed, 216 insertions, 86 deletions
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()