diff options
48 files changed, 823 insertions, 519 deletions
@@ -7,3 +7,4 @@ doc/libbe/ .be/id-cache libbe/_version.py *.egg-info +.idea/* diff --git a/build/lib/libbe/storage/util/config.py b/build/lib/libbe/storage/util/config.py new file mode 100644 index 0000000..34e17f4 --- /dev/null +++ b/build/lib/libbe/storage/util/config.py @@ -0,0 +1,128 @@ +# Copyright (C) 2005-2012 Aaron Bentley <abentley@panoramicfeedback.com> +# Chris Ball <cjb@laptop.org> +# Gianluca Montecchi <gian@grys.it> +# W. Trevor King <wking@tremily.us> +# +# This file is part of Bugs Everywhere. +# +# Bugs Everywhere 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. +# +# Bugs Everywhere 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 +# Bugs Everywhere. If not, see <http://www.gnu.org/licenses/>. + +"""Create, save, and load the per-user config file at :py:func:`path`. +""" + +import configparser +import codecs +import io +import os +import os.path + +import libbe +import libbe.util.encoding +if libbe.TESTING == True: + import doctest + + +default_encoding = libbe.util.encoding.get_text_file_encoding() +"""Default filesystem encoding. + +Initialized with :py:func:`libbe.util.encoding.get_text_file_encoding`. +""" + +def path(): + """Return the path to the per-user config file. + + Defaults to :file:`~/.config/bugs-everywhere`, but you can + override the directory with ``XDG_CONFIG_HOME`` from the `XDG Base + Directory Specification`_. You can also override the entire path + by setting the ``BE_CONFIG_PATH`` environment variable. + + .. _XDG Base Directory Specification: + http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + """ + default_dir = os.path.join('~', '.config') + dirname = os.path.expanduser( + os.environ.get('XDG_CONFIG_HOME', default_dir)) + default = os.path.join(dirname, 'bugs-everywhere') + return os.path.expanduser(os.environ.get('BE_CONFIG_PATH', default)) + +def set_val(name, value, section="DEFAULT", encoding=None): + """Set a value in the per-user config file. + + Parameters + ---------- + name : str + The name of the value to set. + value : str or None + The new value to set (or None to delete the value). + section : str + The section to store the name/value in. + encoding : str + The config file's encoding, defaults to :py:data:`default_encoding`. + """ + if encoding is None: + encoding = default_encoding + config = configparser.ConfigParser() + if os.path.exists(path()) == False: # touch file or config + open(path(), 'w').close() # read chokes on missing file + with io.open(path(), 'r', encoding) as f: + config.read_file(f, path()) + if value is not None: + config.set(section, name, value) + else: + config.remove_option(section, name) + f = codecs.open(path(), 'w', encoding) + config.write(f) + f.close() + +def get_val(name, section="DEFAULT", default=None, encoding=None): + """Get a value from the per-user config file + + Parameters + ---------- + name : str + The name of the value to set. + section : str + The section to store the name/value in. + default : + The value to return if `name` is not set. + encoding : str + The config file's encoding, defaults to :py:data:`default_encoding`. + + Examples + -------- + + >>> get_val("junk") is None + True + >>> set_val("junk", "random") + >>> get_val("junk") + u'random' + >>> set_val("junk", None) + >>> get_val("junk") is None + True + """ + if os.path.exists(path()): + if encoding is None: + encoding = default_encoding + config = configparser.ConfigParser() + with io.open(path(), 'r', encoding) as f: + config.read_file(f, path()) + try: + return config.get(section, name) + except configparser.NoOptionError: + return default + else: + return default + +if libbe.TESTING == True: + suite = doctest.DocTestSuite() diff --git a/interfaces/email/interactive/be-handle-mail b/interfaces/email/interactive/be-handle-mail index c5b87b1..d2805d5 100755 --- a/interfaces/email/interactive/be-handle-mail +++ b/interfaces/email/interactive/be-handle-mail @@ -50,9 +50,8 @@ executed, with the email's post-tag subject as the commit message. """ import codecs -import StringIO as StringIO +import doctest import email -from email.mime.multipart import MIMEMultipart import email.utils import os import os.path @@ -62,15 +61,15 @@ import sys import time import traceback import types -import doctest import unittest +from email.mime.multipart import MIMEMultipart -import libbe.bugdir import libbe.bug -import libbe.comment -import libbe.diff +import libbe.bugdir import libbe.command import libbe.command.subscribe as subscribe +import libbe.comment +import libbe.diff import libbe.storage import libbe.ui.command_line import libbe.util.encoding @@ -105,12 +104,14 @@ ALLOWED_COMMANDS = [u'assign', u'comment', u'commit', u'depend', u'diff', AUTOCOMMIT = True ENCODING = u'utf-8' -libbe.util.encoding.ENCODING = ENCODING # force default encoding +libbe.util.encoding.ENCODING = ENCODING # force default encoding + -class InvalidEmail (ValueError): +class InvalidEmail(ValueError): def __init__(self, msg, message): ValueError.__init__(self, message) self.msg = msg + def response(self): header = self.msg.response_header body = [u'Error processing email:\n', @@ -122,58 +123,70 @@ class InvalidEmail (ValueError): response.attach(self.msg.msg) ret = send_pgp_mime.attach_root(header, response) return ret + def response_body(self): - err_text = [unicode(self)] + err_text = [str(self)] return u'\n'.join(err_text) -class InvalidSubject (InvalidEmail): + +class InvalidSubject(InvalidEmail): def __init__(self, msg, message=None): - if message == None: + if message is None: message = u'Invalid subject' InvalidEmail.__init__(self, msg, message) + def response_body(self): - err_text = u'\n'.join([unicode(self), u'', + err_text = u'\n'.join([str(self), u'', u'full subject was:', self.msg.subject()]) return err_text -class InvalidPseudoHeader (InvalidEmail): + +class InvalidPseudoHeader(InvalidEmail): def response_body(self): err_text = [u'Invalid pseudo-header:\n', - unicode(self)] + str(self)] return u'\n'.join(err_text) -class InvalidCommand (InvalidEmail): + +class InvalidCommand(InvalidEmail): def __init__(self, msg, command, message=None): bigmessage = u'Invalid execution command "%s"' % command - if message != None: + if message is not None: bigmessage += u'\n%s' % message InvalidEmail.__init__(self, msg, bigmessage) self.command = command -class InvalidOption (InvalidCommand): + +class InvalidOption(InvalidCommand): def __init__(self, msg, option, message=None): - bigmessage = u'Invalid option "%s"' % (option) - if message != None: + bigmessage = u'Invalid option "%s"' % option + info = "" + command = "" + if message is not None: bigmessage += u'\n%s' % message InvalidCommand.__init__(self, msg, info, command, bigmessage) self.option = option -class NotificationFailed (Exception): + +class NotificationFailed(Exception): def __init__(self, msg): bigmessage = 'Notification failed: %s' % msg Exception.__init__(self, bigmessage) self.short_msg = msg -class ID (object): + +class ID(object): """ Sometimes you want to reference the output of a command that hasn't been executed yet. ID is there for situations like > a = Command(msg, "new", ["create a bug"]) > b = Command(msg, "comment", [ID(a), "and comment on it"]) """ + def __init__(self, command): self.command = command + def extract_id(self): if hasattr(self, 'cached_id'): return self._cached_id @@ -181,17 +194,19 @@ class ID (object): if self.command.command.name == u'new': regexp = re.compile(u'Created bug with ID (.*)') else: - raise NotImplementedError, self.command.command + raise NotImplementedError(self.command.command) match = regexp.match(self.command.stdout) assert len(match.groups()) == 1, str(match.groups()) self._cached_id = match.group(1) return self._cached_id + def __str__(self): if self.command.ret != 0: return '<id for %s>' % repr(self.command) return '<id %s>' % self.extract_id() -class Command (object): + +class Command(object): """ A libbe.command.Command handler. @@ -203,54 +218,60 @@ class Command (object): args: list of arguments to pass to the command stdin: if non-null, a string to pipe into the command's stdin """ + def __init__(self, msg, command, args=None, stdin=None): self.msg = msg - if args == None: + if args is None: self.args = [] else: self.args = args self.command = libbe.command.get_command_class(command_name=command)() - self.command._setup_io = lambda i_enc,o_enc : None + self.command._setup_io = lambda i_enc, o_enc: None self.ret = None self.stdin = stdin self.stdout = None + def __str__(self): return '<command: %s %s>' % (self.command, ' '.join([str(s) for s in self.args])) + def normalize_args(self): """ Expand any ID placeholders in self.args. """ - for i,arg in enumerate(self.args): + for i, arg in enumerate(self.args): if isinstance(arg, ID): self.args[i] = arg.extract_id() + def run(self): """ Attempt to execute the command whose info is given in the dictionary info. Returns the exit code, stdout, and stderr produced by the command. """ - if self.command.name in [None, u'']: # don't accept blank commands + if self.command.name in [None, u'']: # don't accept blank commands raise InvalidCommand(self.msg, self, 'Blank') elif self.command.name not in ALLOWED_COMMANDS: raise InvalidCommand(self.msg, self, 'Not allowed') - assert self.ret == None, u'running %s twice!' % unicode(self) + assert self.ret is None, u'running %s twice!' % str(self) self.normalize_args() UI.io.set_stdin(self.stdin) self.ret = libbe.ui.command_line.dispatch(UI, self.command, self.args) self.stdout = UI.io.get_stdout() return (self.ret, self.stdout) + def response_msg(self): - if self.ret == None: self.ret = -1 + if self.ret is None: self.ret = -1 response_body = [u'Results of running: (exit code %d)' % self.ret, - u' %s %s' % (self.command.name,u' '.join(self.args))] - if self.stdout != None and len(self.stdout) > 0: + u' %s %s' % (self.command.name, u' '.join(self.args))] + if self.stdout is not None and len(self.stdout) > 0: response_body.extend([u'', u'output:', u'', self.stdout]) - response_body.append(u'') # trailing endline + response_body.append(u'') # trailing endline response_generator = \ send_pgp_mime.PGPMimeMessageFactory(u'\n'.join(response_body)) return response_generator.plain() -class DiffTree (libbe.diff.DiffTree): + +class DiffTree(libbe.diff.DiffTree): """ In order to avoid tons of tiny MIMEText attachments, bug-level nodes set .add_child_text=True (in .join()), which is propogated @@ -275,66 +296,80 @@ class DiffTree (libbe.diff.DiffTree): bugdir/bugs/mod/a/comments/rem bugdir/bugs/mod/a/comments/mod """ + def report_or_none(self): report = self.report() - if report == None: + if report is None: return None payload = report.get_payload() - if payload == None or len(payload) == 0: + if payload is None or len(payload) == 0: return None return report + def report_string(self): report = self.report_or_none() - if report == None: + if report is None: return 'No changes' else: return send_pgp_mime.flatten(report, to_unicode=True) + def make_root(self): return MIMEMultipart() + def join(self, root, parent, data_part): if hasattr(parent, 'attach_child_text'): self.attach_child_text = True - if data_part != None: + if data_part is not None: send_pgp_mime.append_text(parent.data_mime_part, u'\n\n%s' % (data_part)) self.data_mime_part = parent.data_mime_part else: self.data_mime_part = None - if data_part != None: + if data_part is not None: self.data_mime_part = send_pgp_mime.encodedMIMEText(data_part) - if parent != None and parent.name in [u'new', u'rem', u'mod']: + if parent is not None and parent.name in [u'new', u'rem', u'mod']: self.attach_child_text = True - if data_part == None: # make blank data_mime_part for children's appends + if data_part is None: # make blank data_mime_part for children's appends self.data_mime_part = send_pgp_mime.encodedMIMEText(u'') - if self.data_mime_part != None: + if self.data_mime_part is not None: self.data_mime_part[u'Content-Description'] = self.name root.attach(self.data_mime_part) + def data_part(self, depth, indent=False): return libbe.diff.DiffTree.data_part(self, depth, indent=indent) -class Diff (libbe.diff.Diff): - def bug_add_string(self, bug): + +class Diff(libbe.diff.Diff): + @staticmethod + def bug_add_string(bug): return bug.string(show_comments=True) - def _comment_summary_string(self, comment): + + @staticmethod + def _comment_summary_string(comment): return comment.string() + def comment_add_string(self, comment): return self._comment_summary_string(comment) + def comment_rem_string(self, comment): return self._comment_summary_string(comment) -class Message (object): + +class Message(object): def __init__(self, email_text=None, disable_parsing=False): if disable_parsing == False: self.text = email_text - p=email.Parser.Parser() - self.msg=p.parsestr(self.text) - if LOGFILE != None: + p = email.Parser.Parser() + self.msg = p.parsestr(self.text) + if LOGFILE is not None: LOGFILE.write(u'handling %s\n' % self.author_addr()) LOGFILE.write(u'\n%s\n\n' % self.text) - self.confirm = True # enable/disable confirmation email + self.confirm = True # enable/disable confirmation email + def _yes_no(self, boolean): if boolean == True: return 'yes' return 'no' + def author_tuple(self): """ Extract and normalize the sender's email address. Returns a @@ -344,36 +379,44 @@ class Message (object): self._author_tuple_cache = \ send_pgp_mime.source_email(self.msg, return_realname=True) return self._author_tuple_cache + def author_addr(self): return email.utils.formataddr(self.author_tuple()) + def author_name(self): return self.author_tuple()[0] + def author_email(self): return self.author_tuple()[1] + def default_msg_attribute_access(self, attr_name, default=None): if attr_name in self.msg: return self.msg[attr_name] return default + def message_id(self, default=None): return self.default_msg_attribute_access('message-id', default=default) + def subject(self): if 'subject' not in self.msg: raise InvalidSubject(self, u'Email must contain a subject') return self.msg['subject'] + def _split_subject(self): """ Returns (tag, subject), with missing values replaced by None. """ if hasattr(self, '_split_subject_cache'): return self._split_subject_cache - args = self.subject().split(u']',1) + args = self.subject().split(u']', 1) if len(args) < 1: self._split_subject_cache = (None, None) elif len(args) < 2: - self._split_subject_cache = (args[0]+u']', None) + self._split_subject_cache = (args[0] + u']', None) else: - self._split_subject_cache = (args[0]+u']', args[1].strip()) + self._split_subject_cache = (args[0] + u']', args[1].strip()) return self._split_subject_cache + def _subject_tag_type(self): """ Parse subject tag, return (type, value), where type is one of @@ -381,7 +424,7 @@ class Message (object): in the case of "comment", in which case it's the bug ID/shortname. """ - tag,subject = self._split_subject() + tag, subject = self._split_subject() type = None value = None if tag == SUBJECT_TAG_NEW: @@ -394,21 +437,23 @@ class Message (object): type = u'comment' value = match.group(1) return (type, value) + def validate_subject(self): """ Validate the subject line. """ - tag,subject = self._split_subject() + tag, subject = self._split_subject() if not tag.startswith(SUBJECT_TAG_START): raise InvalidSubject( self, u'Subject must start with "%s"' % SUBJECT_TAG_START) - tag_type,value = self._subject_tag_type() - if tag_type == None: + tag_type, value = self._subject_tag_type() + if tag_type is None: raise InvalidSubject(self, u'Invalid tag "%s"' % tag) elif tag_type == u'new' and len(subject) == 0: raise InvalidSubject(self, u'Cannot create a bug with blank title') elif tag_type == u'comment' and len(value) == 0: raise InvalidSubject(self, u'Must specify a bug ID to comment') + def _get_bodies_and_mime_types(self): """ Traverse the email message returning (body, mime_type) for @@ -418,11 +463,12 @@ class Message (object): for part in self.msg.walk(): if part.is_multipart(): continue - body,mime_type=(part.get_payload(decode=True),part.get_content_type()) + body, mime_type = (part.get_payload(decode=True), part.get_content_type()) charset = part.get_content_charset(msg_charset).lower() if mime_type.startswith('text/'): - body = unicode(body, charset) # convert text types to unicode + body = str(body, charset) # convert text types to unicode yield (body, mime_type) + def _parse_body_pseudoheaders(self, body, required, optional, dictionary=None): """ @@ -432,17 +478,17 @@ class Message (object): like, you can initialize the dictionary with some defaults and pass your initialized dict in as dictionary. """ - if dictionary == None: + if dictionary is None: dictionary = {} body_lines = body.splitlines() - all = required+optional - for i,line in enumerate(body_lines): + all = required + optional + for i, line in enumerate(body_lines): line = line.strip() if len(line) == 0: break if ':' not in line: - raise InvalidPseudoheader(self, line) - key,value = line.split(':', 1) + raise InvalidPseudoHeader(self, line) + key, value = line.split(':', 1) value = value.strip() if key not in all: raise InvalidPseudoHeader(self, key) @@ -460,13 +506,15 @@ class Message (object): % u', '.join(missing)) remaining_body = u'\n'.join(body_lines[i:]).strip() return (remaining_body, dictionary) + def _strip_footer(self, body): body_lines = body.splitlines() - for i,line in enumerate(body_lines): + for i, line in enumerate(body_lines): if line.startswith(BREAK): break - i += 1 # increment past the current valid line. + i += 1 # increment past the current valid line. return u'\n'.join(body_lines[:i]).strip() + def parse(self): """ Parse the commands given in the email. Raises assorted @@ -474,7 +522,7 @@ class Message (object): otherwise returns a list of suggested commands to run. """ self.validate_subject() - tag_type,value = self._subject_tag_type() + tag_type, value = self._subject_tag_type() if tag_type == u'new': commands = self.parse_new() elif tag_type == u'comment': @@ -482,18 +530,19 @@ class Message (object): elif tag_type == u'control': commands = self.parse_control() else: - raise Exception, u'Unrecognized tag type "%s"' % tag_type + raise Exception(u'Unrecognized tag type "%s"' % tag_type) return commands + def parse_new(self): command = u'new' - tag,subject = self._split_subject() + tag, subject = self._split_subject() summary = subject options = {u'Reporter': self.author_addr(), u'Confirm': self._yes_no(self.confirm), u'Subscribe': 'no', } - body,mime_type = list(self._get_bodies_and_mime_types())[0] - comment_body,options = \ + body, mime_type = list(self._get_bodies_and_mime_types())[0] + comment_body, options = \ self._parse_body_pseudoheaders(body, NEW_REQUIRED_PSEUDOHEADERS, NEW_OPTIONAL_PSEUDOHEADERS, @@ -512,16 +561,16 @@ class Message (object): comment_body = self._strip_footer(comment_body) if len(comment_body) > 0: command = u'comment' - comment = u'Version: %s\n\n'%options[u'Version'] + comment_body + comment = u'Version: %s\n\n' % options[u'Version'] + comment_body args = [u'--author', self.author_addr(), u'--alt-id', self.message_id(), u'--content-type', mime_type] args.append(id) args.append(u'-') commands.append(Command(self, u'comment', args, stdin=comment)) - for key,value in options.items(): + for key, value in options.items(): if key in [u'Version', u'Reporter', u'Confirm']: - continue # we've already handled these options + continue # we've already handled these options command = key.lower() if key in [u'Depend', u'Tag', u'Target', u'Subscribe']: args = [id, value] @@ -533,23 +582,25 @@ class Message (object): args = ['--subscriber', self.author_addr(), id] commands.append(Command(self, command, args)) return commands + def parse_comment(self, bug_uuid): command = u'comment' bug_id = bug_uuid author = self.author_addr() alt_id = self.message_id() - body,mime_type = list(self._get_bodies_and_mime_types())[0] + body, mime_type = list(self._get_bodies_and_mime_types())[0] if mime_type == 'text/plain': body = self._strip_footer(body) content_type = mime_type args = [u'--author', author] - if alt_id != None: + if alt_id is not None: args.extend([u'--alt-id', alt_id]) args.extend([u'--content-type', content_type, bug_id, u'-']) commands = [Command(self, command, args, stdin=body)] return commands + def parse_control(self): - body,mime_type = list(self._get_bodies_and_mime_types())[0] + body, mime_type = list(self._get_bodies_and_mime_types())[0] commands = [] for line in body.splitlines(): line = line.strip() @@ -564,42 +615,46 @@ class Message (object): if type(line) == types.UnicodeType: # work around http://bugs.python.org/issue1170 for field in fields: - field = unicode(field, 'unicode escape') - command,args = (fields[0], fields[1:]) + field = str(field, 'unicode escape') + command, args = (fields[0], fields[1:]) commands.append(Command(self, command, args)) if len(commands) == 0: raise InvalidEmail(self, u'No commands in control email.') return commands + def run(self, repo='.'): self._begin_response() commands = self.parse() try: - for i,command in enumerate(commands): + for i, command in enumerate(commands): command.run() self._add_response(command.response_msg()) finally: if AUTOCOMMIT == True: - tag,subject = self._split_subject() + tag, subject = self._split_subject() self.commit_command = Command(self, 'commit', [subject]) self.commit_command.run() - if LOGFILE != None: + if LOGFILE is not None: LOGFILE.write(u'Autocommit:\n%s\n\n' % - send_pgp_mime.flatten(self.commit_command.response_msg(), - to_unicode=True)) + send_pgp_mime.flatten(self.commit_command.response_msg(), + to_unicode=True)) + def _begin_response(self): - tag,subject = self._split_subject() + tag, subject = self._split_subject() response_header = [u'From: %s' % THIS_ADDRESS, u'To: %s' % self.author_addr(), u'Date: %s' % libbe.util.utility.time_to_str(time.time()), - u'Subject: %s Re: %s'%(SUBJECT_TAG_RESPONSE,subject) + u'Subject: %s Re: %s' % (SUBJECT_TAG_RESPONSE, subject) ] - if self.message_id() != None: + if self.message_id() is not None: response_header.append(u'In-reply-to: %s' % self.message_id()) self.response_header = \ send_pgp_mime.header_from_text(text=u'\n'.join(response_header)) self._response_messages = [] + def _add_response(self, response_message): self._response_messages.append(response_message) + def response_email(self): assert len(self._response_messages) > 0 if len(self._response_messages) == 1: @@ -609,9 +664,10 @@ class Message (object): for message in self._response_messages: response_body.attach(message) return send_pgp_mime.attach_root(self.response_header, response_body) + def subscriber_emails(self, previous_revision=None): - if previous_revision == None: - if AUTOCOMMIT != True: # no way to tell what's changed + if previous_revision is None: + if AUTOCOMMIT != True: # no way to tell what's changed raise NotificationFailed('Autocommit dissabled') if len(self._response_messages) == 0: raise NotificationFailed('Initial email failed.') @@ -622,7 +678,7 @@ class Message (object): bd = UI.storage_callbacks.get_bugdir() writeable = bd.storage.writeable bd.storage.writeable = False - if bd.storage.versioned == False: # no way to tell what's changed + if bd.storage.versioned == False: # no way to tell what's changed bd.storage.writeable = writeable raise NotificationFailed('Not versioned') @@ -631,12 +687,12 @@ class Message (object): if len(subscribers) == 0: bd.storage.writeable = writeable return [] - for subscriber,subscriptions in subscribers.items(): + for subscriber, subscriptions in subscribers.items(): subscribers[subscriber] = [] - for id,types in subscriptions.items(): + for id, types in subscriptions.items(): for type in types: subscribers[subscriber].append( - libbe.diff.Subscription(id,type)) + libbe.diff.Subscription(id, type)) before_bd, after_bd = self._get_before_and_after_bugdirs(bd, previous_revision) diff = Diff(before_bd, after_bd) @@ -644,41 +700,43 @@ class Message (object): header = self._subscriber_header(bd, previous_revision) emails = [] - for subscriber,subscriptions in subscribers.items(): + for subscriber, subscriptions in subscribers.items(): header.replace_header('to', subscriber) report = diff.report_tree(subscriptions, diff_tree=DiffTree) root = report.report_or_none() - if root != None: + if root is not None: emails.append(send_pgp_mime.attach_root(header, root)) - if LOGFILE != None: + if LOGFILE is not None: LOGFILE.write(u'Preparing to notify %s of changes\n' % subscriber) bd.storage.writeable = writeable return emails + def _get_before_and_after_bugdirs(self, bd, previous_revision=None): - if previous_revision == None: + if previous_revision is None: commit_msg = self.commit_command.stdout assert commit_msg.startswith('Committed '), commit_msg after_revision = commit_msg[len('Committed '):] before_revision = bd.storage.revision_id(-2) else: before_revision = previous_revision - if before_revision == None: + if before_revision is None: # this commit was the initial commit before_bd = libbe.bugdir.BugDir(from_disk=False, manipulate_encodings=False) else: before_bd = libbe.bugdir.RevisionedBugDir(bd, before_revision) - #after_bd = bd.duplicate_bugdir(after_revision) - after_bd = bd # assume no changes since commit a few cycles ago + # after_bd = bd.duplicate_bugdir(after_revision) + after_bd = bd # assume no changes since commit a few cycles ago return (before_bd, after_bd) + def _subscriber_header(self, bd, previous_revision=None): root_dir = os.path.basename(bd.storage.repo) - if previous_revision == None: + if previous_revision is None: subject = 'Changes to %s on %s by %s' \ - % (root_dir, THIS_SERVER, self.author_addr()) + % (root_dir, THIS_SERVER, self.author_addr()) else: subject = 'Changes to %s on %s since revision %s' \ - % (root_dir, THIS_SERVER, previous_revision) + % (root_dir, THIS_SERVER, previous_revision) header = [u'From: %s' % THIS_ADDRESS, u'To: %s' % u'DUMMY-AUTHOR', u'Date: %s' % libbe.util.utility.time_to_str(time.time()), @@ -686,6 +744,7 @@ class Message (object): ] return send_pgp_mime.header_from_text(text=u'\n'.join(header)) + def generate_global_tags(tag_base=u'be-bug'): """ Generate a series of tags from a base tag string. @@ -699,6 +758,7 @@ def generate_global_tags(tag_base=u'be-bug'): SUBJECT_TAG_COMMENT = re.compile(u'\[%s:([\-0-9a-z/]*)]' % tag_base) SUBJECT_TAG_CONTROL = SUBJECT_TAG_RESPONSE + def open_logfile(logpath=None): """ If logpath=None, default to global LOGPATH. @@ -708,7 +768,7 @@ def open_logfile(logpath=None): Relative logpaths are expanded relative to _THIS_DIR """ global LOGPATH, LOGFILE - if logpath != None: + if logpath is not None: if logpath == u'-': LOGPATH = u'stderr' LOGFILE = sys.stderr @@ -719,14 +779,16 @@ def open_logfile(logpath=None): LOGPATH = logpath else: LOGPATH = os.path.join(_THIS_DIR, logpath) - if LOGFILE == None and LOGPATH != u'none': + if LOGFILE is None and LOGPATH != u'none': LOGFILE = codecs.open(LOGPATH, u'a+', - libbe.util.encoding.get_text_file_encoding()) + libbe.util.encoding.get_text_file_encoding()) + def close_logfile(): - if LOGFILE != None and LOGPATH not in [u'stderr', u'none']: + if LOGFILE is not None and LOGPATH not in [u'stderr', u'none']: LOGFILE.close() + def test(): result = unittest.TextTestRunner(verbosity=2).run(suite) num_errors = len(result.errors) @@ -734,11 +796,12 @@ def test(): num_bad = num_errors + num_failures return num_bad + def main(args): from optparse import OptionParser global AUTOCOMMIT, UI - usage='be-handle-mail [options]\n\n%s' % (__doc__) + usage = 'be-handle-mail [options]\n\n%s' % (__doc__) parser = OptionParser(usage=usage) parser.add_option('-r', '--repo', dest='repo', default=_THIS_DIR, metavar='REPO', @@ -762,17 +825,17 @@ def main(args): help='Run internal unit-tests and exit.') pargs = args - options,args = parser.parse_args(args[1:]) + options, args = parser.parse_args(args[1:]) if options.test == True: num_bad = test() if num_bad > 126: num_bad = 1 sys.exit(num_bad) - + AUTOCOMMIT = options.autocommit - if options.notify_since == None: + if options.notify_since is None: msg_text = sys.stdin.read() open_logfile(options.logfile) @@ -781,29 +844,29 @@ def main(args): io = libbe.command.StringInputOutput() UI = libbe.command.UserInterface(io, location=options.repo) - if options.notify_since != None: + if options.notify_since is not None: if options.subscribers == True: - if LOGFILE != None: + if LOGFILE is not None: LOGFILE.write(u'Checking for subscribers to notify since revision %s\n' % options.notify_since) try: m = Message(disable_parsing=True) emails = m.subscriber_emails(options.notify_since) - except NotificationFailed, e: - if LOGFILE != None: - LOGFILE.write(unicode(e) + u'\n') + except NotificationFailed as e: + if LOGFILE is not None: + LOGFILE.write(str(e) + u'\n') else: for msg in emails: if options.output == True: - print send_pgp_mime.flatten(msg, to_unicode=True) + print(send_pgp_mime.flatten(msg, to_unicode=True)) else: send_pgp_mime.mail(msg, send_pgp_mime.sendmail) close_logfile() UI.cleanup() sys.exit(0) - if len(msg_text.strip()) == 0: # blank email!? - if LOGFILE != None: + if len(msg_text.strip()) == 0: # blank email!? + if LOGFILE is not None: LOGFILE.write(u'Blank email!\n') close_logfile() UI.cleanup() @@ -811,10 +874,10 @@ def main(args): try: m = Message(msg_text) m.run() - except InvalidEmail, e: + except InvalidEmail as e: response = e.response() - except Exception, e: - if LOGFILE != None: + except Exception as e: + if LOGFILE is not None: LOGFILE.write(u'Uncaught exception:\n%s\n' % (e,)) traceback.print_tb(sys.exc_traceback, file=LOGFILE) close_logfile() @@ -824,28 +887,28 @@ def main(args): else: response = m.response_email() if options.output == True: - print send_pgp_mime.flatten(response, to_unicode=True) + print(send_pgp_mime.flatten(response, to_unicode=True)) elif m.confirm == True: - if LOGFILE != None: + if LOGFILE is not None: LOGFILE.write(u'Sending response to %s\n' % m.author_addr()) LOGFILE.write(u'\n%s\n\n' % send_pgp_mime.flatten(response, to_unicode=True)) send_pgp_mime.mail(response, send_pgp_mime.sendmail) else: - if LOGFILE != None: + if LOGFILE is not None: LOGFILE.write(u'Response declined by %s\n' % m.author_addr()) if options.subscribers == True: - if LOGFILE != None: + if LOGFILE is not None: LOGFILE.write(u'Checking for subscribers\n') try: emails = m.subscriber_emails() - except NotificationFailed, e: - if LOGFILE != None: - LOGFILE.write(unicode(e) + u'\n') + except NotificationFailed as e: + if LOGFILE is not None: + LOGFILE.write(str(e) + u'\n') else: for msg in emails: if options.output == True: - print send_pgp_mime.flatten(msg, to_unicode=True) + print(send_pgp_mime.flatten(msg, to_unicode=True)) else: send_pgp_mime.mail(msg, send_pgp_mime.sendmail) @@ -853,23 +916,28 @@ def main(args): m.commit_command.cleanup() UI.cleanup() -class GenerateGlobalTagsTestCase (unittest.TestCase): + +class GenerateGlobalTagsTestCase(unittest.TestCase): def setUp(self): super(GenerateGlobalTagsTestCase, self).setUp() self.save_global_tags() + def tearDown(self): self.restore_global_tags() super(GenerateGlobalTagsTestCase, self).tearDown() + def save_global_tags(self): self.saved_globals = [SUBJECT_TAG_BASE, SUBJECT_TAG_START, SUBJECT_TAG_RESPONSE, SUBJECT_TAG_NEW, SUBJECT_TAG_COMMENT, SUBJECT_TAG_CONTROL] + def restore_global_tags(self): global SUBJECT_TAG_BASE, SUBJECT_TAG_START, SUBJECT_TAG_RESPONSE, \ SUBJECT_TAG_NEW, SUBJECT_TAG_COMMENT, SUBJECT_TAG_CONTROL SUBJECT_TAG_BASE, SUBJECT_TAG_START, SUBJECT_TAG_RESPONSE, \ SUBJECT_TAG_NEW, SUBJECT_TAG_COMMENT, SUBJECT_TAG_CONTROL = \ self.saved_globals + def test_restore_global_tags(self): "Test global tag restoration by teardown function." global SUBJECT_TAG_BASE @@ -878,26 +946,32 @@ class GenerateGlobalTagsTestCase (unittest.TestCase): self.failUnlessEqual(SUBJECT_TAG_BASE, u'projectX-bug') self.restore_global_tags() self.failUnlessEqual(SUBJECT_TAG_BASE, u'be-bug') + def test_subject_tag_base(self): "Should set SUBJECT_TAG_BASE global correctly" generate_global_tags(u'projectX-bug') self.failUnlessEqual(SUBJECT_TAG_BASE, u'projectX-bug') + def test_subject_tag_start(self): "Should set SUBJECT_TAG_START global correctly" generate_global_tags(u'projectX-bug') self.failUnlessEqual(SUBJECT_TAG_START, u'[projectX-bug') + def test_subject_tag_response(self): "Should set SUBJECT_TAG_RESPONSE global correctly" generate_global_tags(u'projectX-bug') self.failUnlessEqual(SUBJECT_TAG_RESPONSE, u'[projectX-bug]') + def test_subject_tag_new(self): "Should set SUBJECT_TAG_NEW global correctly" generate_global_tags(u'projectX-bug') self.failUnlessEqual(SUBJECT_TAG_NEW, u'[projectX-bug:submit]') + def test_subject_tag_control(self): "Should set SUBJECT_TAG_CONTROL global correctly" generate_global_tags(u'projectX-bug') self.failUnlessEqual(SUBJECT_TAG_CONTROL, u'[projectX-bug]') + def test_subject_tag_comment(self): "Should set SUBJECT_TAG_COMMENT global correctly" generate_global_tags(u'projectX-bug') @@ -905,6 +979,7 @@ class GenerateGlobalTagsTestCase (unittest.TestCase): self.failUnlessEqual(len(m.groups()), 1) self.failUnlessEqual(m.group(1), u'abc/xyz-123') + unitsuite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) suite = unittest.TestSuite([unitsuite, doctest.DocTestSuite()]) diff --git a/interfaces/email/interactive/send_pgp_mime.py b/interfaces/email/interactive/send_pgp_mime.py index 42a0a8c..c70da23 100644 --- a/interfaces/email/interactive/send_pgp_mime.py +++ b/interfaces/email/interactive/send_pgp_mime.py @@ -128,7 +128,7 @@ def mail(msg, sendmail=None): the local host doesn't have an SMTP server set up for easy smtplib usage. """ - if sendmail != None: + if sendmail is not None: execute(sendmail, stdin=flatten(msg)) return None s = smtplib.SMTP() @@ -167,11 +167,11 @@ def guess_encoding(text): pass else: break - assert encoding != None + assert encoding is not None return encoding def encodedMIMEText(body, encoding=None): - if encoding == None: + if encoding is None: encoding = guess_encoding(body) if encoding == "us-ascii": return MIMEText(body) @@ -223,7 +223,7 @@ def replace(template, format_char, replacement_text): >>> replace('--textmode %?a?-u %a? %f', 'a', '') '--textmode %f' """ - if replacement_text == None: + if replacement_text is None: replacement_text = "" regexp = re.compile('%[?]'+format_char+'[?]([^?]*)[?]') if len(replacement_text) > 0: @@ -238,7 +238,7 @@ def flatten(msg, to_unicode=False): """ Produce flat text output from an email Message instance. """ - assert msg != None + assert msg is not None fp = StringIO() g = Generator(fp, mangle_from_=False) g.flatten(msg) @@ -392,9 +392,9 @@ class PGPMimeMessageFactory (object): body.add_header('Content-Disposition', 'inline') return body def passphrase_arg(self, passphrase=None): - if passphrase == None and PASSPHRASE != None: + if passphrase is None and PASSPHRASE is not None: passphrase = PASSPHRASE - if passphrase == None: + if passphrase is None: return (None,'') return (passphrase, pgp_stdin_passphrase_arg) def plain(self): @@ -415,7 +415,7 @@ class PGPMimeMessageFactory (object): bfile.flush() args = replace(pgp_sign_command, 'f', bfile.name) - if PGP_SIGN_AS == None: + if PGP_SIGN_AS is None: pgp_sign_as = '<%s>' % source_email(header) else: pgp_sign_as = PGP_SIGN_AS @@ -453,7 +453,7 @@ class PGPMimeMessageFactory (object): recipient_string = ' '.join(recipients) args = replace(pgp_encrypt_only_command, 'R', recipient_string) args = replace(args, 'f', bfile.name) - if PGP_SIGN_AS == None: + if PGP_SIGN_AS is None: pgp_sign_as = '<%s>' % source_email(header) else: pgp_sign_as = PGP_SIGN_AS @@ -493,7 +493,7 @@ class PGPMimeMessageFactory (object): recipient_string = ' '.join(recipients) args = replace(pgp_encrypt_only_command, 'R', recipient_string) args = replace(args, 'f', bfile.name) - if PGP_SIGN_AS == None: + if PGP_SIGN_AS is None: pgp_sign_as = '<%s>' % source_email(header) else: pgp_sign_as = PGP_SIGN_AS @@ -556,9 +556,9 @@ if __name__ == '__main__': stdin_used = False - if options.passphrase_file != None: + if options.passphrase_file is not None: PASSPHRASE = file(options.passphrase_file, 'r').read() - elif options.passphrase_fd != None: + elif options.passphrase_fd is not None: if options.passphrase_fd == 0: stdin_used = True PASSPHRASE = sys.stdin.read() @@ -573,25 +573,25 @@ if __name__ == '__main__': sys.exit(0) header = None - if options.header_filename != None: + if options.header_filename is not None: if options.header_filename == '-': assert stdin_used == False stdin_used = True header = sys.stdin.read() else: header = file(options.header_filename, 'r').read() - if header == None: + if header is None: raise Exception("missing header") headermsg = header_from_text(header) body = None - if options.body_filename != None: + if options.body_filename is not None: if options.body_filename == '-': assert stdin_used == False stdin_used = True body = sys.stdin.read() else: body = file(options.body_filename, 'r').read() - if body == None: + if body is None: raise Exception("missing body") m = PGPMimeMessageFactory(body) diff --git a/interfaces/web/web.py b/interfaces/web/web.py index ed87c78..ba68908 100644 --- a/interfaces/web/web.py +++ b/interfaces/web/web.py @@ -75,13 +75,13 @@ class WebInterface: if target != '': target = None if target == 'None' else target - if target == None: + if target is None: # Return all bugs that don't block any targets. return [bug for bug in bugs if not bug_target(self.bd, bug)] else: # Return all bugs that block the supplied target. targetbug = bug_from_target_summary(self.bd, target) - if targetbug == None: + if targetbug is None: return [] bugs = [bug for bug in get_blocked_by(self.bd, targetbug) if bug.active] @@ -188,7 +188,7 @@ class WebInterface: """The view that handles editing bug details.""" bug = self.bd.bug_from_uuid(id) - if summary != None: + if summary is not None: bug.summary = summary else: bug.status = status if status != 'None' else None diff --git a/libbe/bugdir.py b/libbe/bugdir.py index a5d384d..a694e89 100644 --- a/libbe/bugdir.py +++ b/libbe/bugdir.py @@ -133,8 +133,8 @@ class BugDir (list, settings_object.SavedSettingsObject): def inactive_status(): return {} def _extra_strings_check_fn(value): - return utility.iterable_full_of_strings(value, \ - alternative=settings_object.EMPTY) + return utility.iterable_full_of_strings(value, + alternative=settings_object.EMPTY) def _extra_strings_change_hook(self, old, new): self.extra_strings.sort() # to make merging easier self._prop_save_settings(old, new) @@ -175,7 +175,7 @@ class BugDir (list, settings_object.SavedSettingsObject): else: if self.uuid is None: self.uuid = libbe.util.id.uuid_gen() - if self.storage != None and self.storage.is_writeable(): + if self.storage is not None and self.storage.is_writeable(): self.save() # methods for saving/loading/accessing settings and properties. @@ -235,7 +235,7 @@ class BugDir (list, settings_object.SavedSettingsObject): def _refresh_uuid_cache(self): self._uuids_cache = set() # list bugs that are in storage - if self.storage != None and self.storage.is_readable(): + if self.storage is not None and self.storage.is_readable(): child_uuids = libbe.util.id.child_uuids( self.storage.children(self.id.storage())) for id in child_uuids: @@ -274,7 +274,7 @@ class BugDir (list, settings_object.SavedSettingsObject): if hasattr(self, '_uuids_cache') and bug.uuid in self._uuids_cache: self._uuids_cache.remove(bug.uuid) self.remove(bug) - if self.storage != None and self.storage.is_writeable(): + if self.storage is not None and self.storage.is_writeable(): bug.remove() def bug_from_uuid(self, uuid): @@ -752,12 +752,12 @@ if libbe.TESTING: bug_b.creator = 'Jane Doe <jdoe@example.com>' bug_b.time = 0 bug_b.status = 'closed' - if self.storage != None: + if self.storage is not None: self.storage.disconnect() # flush to storage self.storage.connect() def cleanup(self): - if self.storage != None: + if self.storage is not None: self.storage.writeable = True self.storage.disconnect() self.storage.destroy() @@ -765,7 +765,7 @@ if libbe.TESTING: self._dir_ref.cleanup() def flush_reload(self): - if self.storage != None: + if self.storage is not None: self.storage.disconnect() self.storage.connect() self._clear_bugs() @@ -885,7 +885,7 @@ if libbe.TESTING: self.storage.disconnect() self.storage.connect() def tearDown(self): - if self.storage != None: + if self.storage is not None: self.storage.disconnect() self.storage.destroy() self.dir.cleanup() diff --git a/libbe/command/base.py b/libbe/command/base.py index 806d880..b6a9e15 100644 --- a/libbe/command/base.py +++ b/libbe/command/base.py @@ -74,7 +74,7 @@ def get_command(command_name): >>> try: ... get_command('asdf') - ... except UnknownCommand, e: + ... except UnknownCommand as e: ... print(e) Unknown command 'asdf' (No module named asdf) @@ -99,7 +99,7 @@ def get_command_class(module=None, command_name=None): >>> repr(import_xml) "<class 'libbe.command.import_xml.Import_XML'>" """ - if module == None: + if module is None: module = get_command(command_name) try: cname = command_name.capitalize().replace('-', '_') @@ -121,7 +121,7 @@ def modname_to_command_name(modname): ... try: ... if issubclass(attr, Command): ... commands.append(attr) - ... except TypeError, e: + ... except TypeError as e: ... pass ... if len(commands) == 0: ... raise Exception('No Command classes in %s' % dir(mod)) @@ -163,7 +163,7 @@ class Argument (CommandInput): self.optional = optional self.repeatable = repeatable self.completion_callback = completion_callback - if self.metavar == None: + if self.metavar is None: self.metavar = self.name.upper() class Option (CommandInput): @@ -173,17 +173,17 @@ class Option (CommandInput): self.callback = callback self.short_name = short_name self.arg = arg - if self.arg == None and self.callback == None: + if self.arg is None and self.callback is None: # use an implicit boolean argument self.arg = Argument(name=self.name, help=self.help, default=False, type='bool') self.validate() def validate(self): - if self.arg == None: - assert self.callback != None, self.name + if self.arg is None: + assert self.callback is not None, self.name return - assert self.callback == None, '%s: %s' % (self.name, self.callback) + assert self.callback is None, '%s: %s' % (self.name, self.callback) assert self.arg.name == self.name, \ 'Name missmatch: %s != %s' % (self.arg.name, self.name) assert self.arg.optional == False, self.name @@ -208,13 +208,13 @@ class _DummyParser (optparse.OptionParser): # from libbe.ui.command_line.CmdOptionParser._add_option option.validate() long_opt = '--%s' % option.name - if option.short_name != None: + if option.short_name is not None: short_opt = '-%s' % option.short_name assert '_' not in option.name, \ 'Non-reconstructable option name %s' % option.name kwargs = {'dest':option.name.replace('-', '_'), 'help':option.help} - if option.arg == None or option.arg.type == 'bool': + if option.arg is None or option.arg.type == 'bool': kwargs['action'] = 'store_true' kwargs['metavar'] = None kwargs['default'] = False @@ -223,7 +223,7 @@ class _DummyParser (optparse.OptionParser): kwargs['action'] = 'store' kwargs['metavar'] = option.arg.metavar kwargs['default'] = option.arg.default - if option.short_name != None: + if option.short_name is not None: opt = optparse.Option(short_opt, long_opt, **kwargs) else: opt = optparse.Option(long_opt, **kwargs) @@ -291,7 +291,7 @@ class Command (object): pass else: params.pop('help') - if params['complete'] != None: + if params['complete'] is not None: pass else: params.pop('complete') @@ -303,16 +303,16 @@ class Command (object): return self.status def _parse_options_args(self, options=None, args=None): - if options == None: + if options is None: options = {} - if args == None: + if args is None: args = [] params = {} for option in self.options: assert option.name not in params, params[option.name] if option.name in options: params[option.name] = options.pop(option.name) - elif option.arg != None: + elif option.arg is not None: params[option.name] = option.arg.default else: # non-arg options are flags, set to default flag value params[option.name] = False @@ -385,13 +385,13 @@ class Command (object): return "A detailed help message." def complete(self, argument=None, fragment=None): - if argument == None: + if argument is None: ret = ['--%s' % o.name for o in self.options if o.name != 'complete'] - if len(self.args) > 0 and self.args[0].completion_callback != None: + if len(self.args) > 0 and self.args[0].completion_callback is not None: ret.extend(self.args[0].completion_callback(self, argument, fragment)) return ret - elif argument.completion_callback != None: + elif argument.completion_callback is not None: # finish a particular argument return argument.completion_callback(self, argument, fragment) return [] # the particular argument doesn't supply completion info @@ -413,7 +413,7 @@ class Command (object): >>> c = Command() >>> try: ... c._check_restricted_access(s, os.path.expanduser('~/.ssh/id_rsa')) - ... except UserError, e: + ... except UserError as e: ... assert str(e).startswith('file access restricted!'), str(e) ... print('we got the expected error') we got the expected error @@ -457,9 +457,9 @@ class StdInputOutput (InputOutput): InputOutput.__init__(self, stdin, stdout) def _get_io(self, input_encoding=None, output_encoding=None): - if input_encoding == None: + if input_encoding is None: input_encoding = libbe.util.encoding.get_input_encoding() - if output_encoding == None: + if output_encoding is None: output_encoding = libbe.util.encoding.get_output_encoding() stdin = codecs.getreader(input_encoding)(sys.stdin) stdin.encoding = input_encoding @@ -512,7 +512,7 @@ class UnconnectedStorageGetter (object): class StorageCallbacks (object): def __init__(self, location=None): - if location == None: + if location is None: location = '.' self.location = location self._get_unconnected_storage = UnconnectedStorageGetter(location) @@ -532,7 +532,7 @@ class StorageCallbacks (object): intended for the init command, which calls Storage.init(). """ if not hasattr(self, '_unconnected_storage'): - if self._get_unconnected_storage == None: + if self._get_unconnected_storage is None: raise NotImplementedError self._unconnected_storage = self._get_unconnected_storage() return self._unconnected_storage @@ -574,7 +574,7 @@ class StorageCallbacks (object): class UserInterface (object): def __init__(self, io=None, location=None): - if io == None: + if io is None: io = StringInputOutput() self.io = io self.storage_callbacks = StorageCallbacks(location) diff --git a/libbe/command/comment.py b/libbe/command/comment.py index bfd24fe..c579610 100644 --- a/libbe/command/comment.py +++ b/libbe/command/comment.py @@ -126,7 +126,7 @@ class Comment (libbe.command.Command): bugdir,bug,parent = ( libbe.command.util.bugdir_bug_comment_from_user_id( bugdirs, params['id'])) - if params['comment'] == None: + if params['comment'] is None: # try to launch an editor for comment-body entry try: if parent == bug.comment_root: @@ -144,7 +144,7 @@ class Comment (libbe.command.Command): if body is None: raise libbe.command.UserError('No comment entered.') elif params['comment'] == '-': # read body from stdin - binary = not (params['content-type'] == None + binary = not (params['content-type'] is None or params['content-type'].startswith("text/")) if not binary: body = self.stdin.read() @@ -156,12 +156,12 @@ class Comment (libbe.command.Command): body = params['comment'] if not body.endswith('\n'): body+='\n' - if params['author'] == None: + if params['author'] is None: params['author'] = self._get_user_id() new = parent.new_reply(body=body, content_type=params['content-type']) for key in ['alt-id', 'author']: - if params[key] != None: + if params[key] is not None: setattr(new, new._setting_name_to_attr_name(key), params[key]) if params['full-uuid']: comment_id = new.id.long_user() diff --git a/libbe/command/commit.py b/libbe/command/commit.py index 077980d..afe082d 100644 --- a/libbe/command/commit.py +++ b/libbe/command/commit.py @@ -71,10 +71,10 @@ class Commit (libbe.command.Command): summary = sys.stdin.readline() else: summary = params['summary'] - if summary == None and params['body'] == None: + if summary is None and params['body'] is None: params['body'] = 'EDITOR' storage = self._get_storage() - if params['body'] == None: + if params['body'] is None: body = None elif params['body'] == 'EDITOR': body = libbe.ui.util.editor.editor_string( @@ -83,8 +83,8 @@ class Commit (libbe.command.Command): self._check_restricted_access(storage, params['body']) body = libbe.util.encoding.get_file_contents( params['body'], decode=True) - if summary == None: # use the first body line as the summary - if body == None: + if summary is None: # use the first body line as the summary + if body is None: raise libbe.command.UserError( 'cannot commit without a summary') lines = body.splitlines() diff --git a/libbe/command/depend.py b/libbe/command/depend.py index 4cb0efd..fe02ce0 100644 --- a/libbe/command/depend.py +++ b/libbe/command/depend.py @@ -54,7 +54,7 @@ class Filter (object): else: target_bug = libbe.command.target.bug_target(bugdirs, bug) if self.target in ['none', None]: - if target_bug.summary != None: + if target_bug.summary is not None: return False else: if target_bug.summary != self.target: @@ -195,14 +195,14 @@ class Depend (libbe.command.Command): ]) def _run(self, **params): - if params['repair'] == True and params['bug-id'] != None: + if params['repair'] == True and params['bug-id'] is not None: raise libbe.command.UserError( 'No arguments with --repair calls.') - if params['repair'] == False and params['bug-id'] == None: + if params['repair'] == False and params['bug-id'] is None: raise libbe.command.UserError( 'Must specify either --repair or a BUG-ID') - if params['tree-depth'] != None \ - and params['blocking-bug-id'] != None: + if params['tree-depth'] is not None \ + and params['blocking-bug-id'] is not None: raise libbe.command.UserError( 'Only one bug id used in tree mode.') bugdirs = self._get_bugdirs() @@ -223,7 +223,7 @@ class Depend (libbe.command.Command): libbe.command.util.bugdir_bug_comment_from_user_id( bugdirs, params['bug-id'])) - if params['tree-depth'] != None: + if params['tree-depth'] is not None: dtree = DependencyTree(bugdirs, bugA, params['tree-depth'], filter) if len(dtree.blocked_by_tree()) > 0: print('%s blocked by:' % bugA.id.user(), file=self.stdout) @@ -241,7 +241,7 @@ class Depend (libbe.command.Command): % (' '*(depth), self.bug_string(node.bug, params))), file=self.stdout) return 0 - if params['blocking-bug-id'] != None: + if params['blocking-bug-id'] is not None: bugdirB,bugB,dummy_comment = ( libbe.command.util.bugdir_bug_comment_from_user_id( bugdirs, params['blocking-bug-id'])) diff --git a/libbe/command/diff.py b/libbe/command/diff.py index d8aea37..aca54c3 100644 --- a/libbe/command/diff.py +++ b/libbe/command/diff.py @@ -97,18 +97,18 @@ class Diff (libbe.command.Command): def diff(self, bugdir, subscriptions, params): - if params['repo'] == None: + if params['repo'] is None: if bugdir.storage.versioned == False: raise libbe.command.UserError( 'This repository is not revision-controlled.') - if params['revision'] == None: # get the most recent revision + if params['revision'] is None: # get the most recent revision params['revision'] = bugdir.storage.revision_id(-1) old_bd = libbe.bugdir.RevisionedBugDir(bugdir, params['revision']) else: old_storage = libbe.storage.get_storage(params['repo']) old_storage.connect() old_bd_current = libbe.bugdir.BugDir(old_storage, from_disk=True) - if params['revision'] == None: # use the current working state + if params['revision'] is None: # use the current working state old_bd = old_bd_current else: if old_bd_current.storage.versioned == False: @@ -127,7 +127,7 @@ class Diff (libbe.command.Command): print('\n'.join(uuids), file=self.stdout) else : rep = tree.report_string() - if rep != None: + if rep is not None: print(rep, file=self.stdout) return 0 diff --git a/libbe/command/due.py b/libbe/command/due.py index ddf111a..2f7d181 100644 --- a/libbe/command/due.py +++ b/libbe/command/due.py @@ -65,7 +65,7 @@ class Due (libbe.command.Command): bugdir,bug,comment = ( libbe.command.util.bugdir_bug_comment_from_user_id( bugdirs, params['bug-id'])) - if params['due'] == None: + if params['due'] is None: due_time = get_due(bug) if due_time is None: print('No due date assigned.', file=self.stdout) diff --git a/libbe/command/help.py b/libbe/command/help.py index 981ea1a..c7961ac 100644 --- a/libbe/command/help.py +++ b/libbe/command/help.py @@ -93,7 +93,7 @@ class Help (libbe.command.Command): ]) def _run(self, **params): - if params['topic'] == None: + if params['topic'] is None: if hasattr(self.ui, 'help'): print(self.ui.help().rstrip('\n'), file=self.stdout) elif params['topic'] in libbe.command.commands(command_names=True): diff --git a/libbe/command/html.py b/libbe/command/html.py index ed43808..5ec8691 100644 --- a/libbe/command/html.py +++ b/libbe/command/html.py @@ -100,21 +100,11 @@ class ServerApp (libbe.util.wsgi.WSGI_AppObject, self.logger.log( self.log_level, 'generate {} index file for {} bugs'.format( bug_type, len(bugs))) - template_info = { - 'title': self.title, - 'charset': 'UTF-8', - 'stylesheet': 'style.css', - 'header': self.header, - 'active_class': 'tab nsel', - 'inactive_class': 'tab nsel', - 'target_class': 'tab nsel', - 'bugs': bugs, - 'bug_entry': self.template.get_template('index_bug_entry.html'), - 'bug_dir': self.bug_dir, - 'index_file': self._index_file, - 'generation_time': self._generation_time(), - } - template_info['{}_class'.format(bug_type)] = 'tab sel' + template_info = {'title': self.title, 'charset': 'UTF-8', 'stylesheet': 'style.css', 'header': self.header, + 'active_class': 'tab nsel', 'inactive_class': 'tab nsel', 'target_class': 'tab nsel', + 'bugs': bugs, 'bug_entry': self.template.get_template('index_bug_entry.html'), + 'bug_dir': self.bug_dir, 'index_file': self._index_file, + 'generation_time': self._generation_time(), '{}_class'.format(bug_type): 'tab sel'} if bug_type == 'target': template = self.template.get_template('target_index.html') template_info['targets'] = [ @@ -314,7 +304,7 @@ class ServerApp (libbe.util.wsgi.WSGI_AppObject, return time.ctime() def _escape(self, string): - if string == None: + if string is None: return '' return xml.sax.saxutils.escape(string) diff --git a/libbe/command/import_xml.py b/libbe/command/import_xml.py index 391b2ed..42b9b49 100644 --- a/libbe/command/import_xml.py +++ b/libbe/command/import_xml.py @@ -102,7 +102,7 @@ class Import_XML (libbe.command.Command): bugdirs = self._get_bugdirs() writeable = storage.writeable storage.writeable = False - if params['root'] != None: + if params['root'] is not None: root_bugdir,root_bug,root_comment = ( libbe.command.util.bugdir_bug_comment_from_user_id( bugdirs, params['root'])) @@ -136,7 +136,7 @@ class Import_XML (libbe.command.Command): comms = [] for c in root_bug.comments(): comms.append(c.uuid) - if c.alt_id != None: + if c.alt_id is not None: comms.append(c.alt_id) if root_comment.uuid == libbe.comment.INVALID_UUID: root_text = root_bug.id.user() @@ -496,13 +496,13 @@ if libbe.TESTING == True: c1 = bugB.comment_from_uuid('c1') comments.remove(c1) self.assertTrue(c1.uuid == 'c1', c1.uuid) - self.assertTrue(c1.alt_id == None, c1.alt_id) + self.assertTrue(c1.alt_id is None, c1.alt_id) self.assertTrue(c1.author == 'Jane', c1.author) self.assertTrue(c1.body == 'So long\n', c1.body) c2 = bugB.comment_from_uuid('c2') comments.remove(c2) self.assertTrue(c2.uuid == 'c2', c2.uuid) - self.assertTrue(c2.alt_id == None, c2.alt_id) + self.assertTrue(c2.alt_id is None, c2.alt_id) self.assertTrue(c2.author == 'Jess', c2.author) self.assertTrue(c2.body == 'World\n', c2.body) c4 = comments[0] @@ -530,13 +530,13 @@ if libbe.TESTING == True: c1 = bugB.comment_from_uuid('c1') comments.remove(c1) self.assertTrue(c1.uuid == 'c1', c1.uuid) - self.assertTrue(c1.alt_id == None, c1.alt_id) + self.assertTrue(c1.alt_id is None, c1.alt_id) self.assertTrue(c1.author == 'Jane', c1.author) self.assertTrue(c1.body == 'Hello\n', c1.body) c2 = bugB.comment_from_uuid('c2') comments.remove(c2) self.assertTrue(c2.uuid == 'c2', c2.uuid) - self.assertTrue(c2.alt_id == None, c2.alt_id) + self.assertTrue(c2.alt_id is None, c2.alt_id) self.assertTrue(c2.author == 'Jess', c2.author) self.assertTrue(c2.body == 'World\n', c2.body) c4 = comments[0] @@ -566,13 +566,13 @@ if libbe.TESTING == True: c1 = bugB.comment_from_uuid('c1') comments.remove(c1) self.assertTrue(c1.uuid == 'c1', c1.uuid) - self.assertTrue(c1.alt_id == None, c1.alt_id) + self.assertTrue(c1.alt_id is None, c1.alt_id) self.assertTrue(c1.author == 'Jane', c1.author) self.assertTrue(c1.body == 'So long\n', c1.body) c2 = bugB.comment_from_uuid('c2') comments.remove(c2) self.assertTrue(c2.uuid == 'c2', c2.uuid) - self.assertTrue(c2.alt_id == None, c2.alt_id) + self.assertTrue(c2.alt_id is None, c2.alt_id) self.assertTrue(c2.author == 'Jess', c2.author) self.assertTrue(c2.body == 'World\n', c2.body) c4 = comments[0] @@ -601,13 +601,13 @@ if libbe.TESTING == True: c1 = bugB.comment_from_uuid('c1') comments.remove(c1) self.assertTrue(c1.uuid == 'c1', c1.uuid) - self.assertTrue(c1.alt_id == None, c1.alt_id) + self.assertTrue(c1.alt_id is None, c1.alt_id) self.assertTrue(c1.author == 'Jane', c1.author) self.assertTrue(c1.body == 'Hello\n', c1.body) c2 = bugB.comment_from_uuid('c2') comments.remove(c2) self.assertTrue(c2.uuid == 'c2', c2.uuid) - self.assertTrue(c2.alt_id == None, c2.alt_id) + self.assertTrue(c2.alt_id is None, c2.alt_id) self.assertTrue(c2.author == 'Jess', c2.author) self.assertTrue(c2.body == 'World\n', c2.body) c4 = comments[0] diff --git a/libbe/command/list.py b/libbe/command/list.py index 41e1bed..567e3fe 100644 --- a/libbe/command/list.py +++ b/libbe/command/list.py @@ -156,7 +156,7 @@ class List (libbe.command.Command): def _parse_params(self, bugdirs, params): cmp_list = [] - if params['sort'] != None: + if params['sort'] is not None: for cmp in params['sort'].split(','): if cmp not in AVAILABLE_IDXS: raise libbe.command.UserError( @@ -167,7 +167,7 @@ class List (libbe.command.Command): severity = parse_severity(params['severity'], important=params['important']) # select assigned - if params['assigned'] == None: + if params['assigned'] is None: if params['mine'] == True: assigned = [self._get_user_id()] else: @@ -178,7 +178,7 @@ class List (libbe.command.Command): for i in range(len(assigned)): if assigned[i] == '-': assigned[i] = params['user-id'] - if params['extra-strings'] == None: + if params['extra-strings'] is None: extra_strings_regexps = [] else: extra_strings_regexps = [re.compile(x) diff --git a/libbe/command/merge.py b/libbe/command/merge.py index 4ee67cf..5100803 100644 --- a/libbe/command/merge.py +++ b/libbe/command/merge.py @@ -170,7 +170,7 @@ class Merge (libbe.command.Command): for comment in newCommTree.traverse(): # all descendant comments comment.bug = bugA # uuids must be unique in storage - if comment.alt_id == None: + if comment.alt_id is None: comment.storage = None comment.alt_id = comment.uuid comment.storage = storage diff --git a/libbe/command/new.py b/libbe/command/new.py index 4a0288b..6a5ac1f 100644 --- a/libbe/command/new.py +++ b/libbe/command/new.py @@ -126,19 +126,19 @@ class New (libbe.command.Command): 'Ambiguous bugdir {}'.format(sorted(bugdirs.values()))) storage.writeable = False bug = bugdir.new_bug(summary=summary.strip()) - if params['creator'] != None: + if params['creator'] is not None: bug.creator = params['creator'] else: bug.creator = self._get_user_id() - if params['reporter'] != None: + if params['reporter'] is not None: bug.reporter = params['reporter'] else: bug.reporter = bug.creator - if params['assigned'] != None: + if params['assigned'] is not None: bug.assigned = _parse_assigned(self, params['assigned']) - if params['status'] != None: + if params['status'] is not None: bug.status = params['status'] - if params['severity'] != None: + if params['severity'] is not None: bug.severity = params['severity'] storage.writeable = True bug.save() diff --git a/libbe/command/serve_commands.py b/libbe/command/serve_commands.py index dbdb76b..1541d12 100644 --- a/libbe/command/serve_commands.py +++ b/libbe/command/serve_commands.py @@ -202,7 +202,7 @@ if libbe.TESTING: ('Content-Type', 'application/octet-stream' ) in self.response_headers, self.response_headers) - self.assertTrue(self.exc_info == None, self.exc_info) + self.assertTrue(self.exc_info is None, self.exc_info) # TODO: integration tests on ServeCommands? unitsuite =unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) diff --git a/libbe/command/set.py b/libbe/command/set.py index 9ec7e2c..698e657 100644 --- a/libbe/command/set.py +++ b/libbe/command/set.py @@ -83,7 +83,7 @@ class Set (libbe.command.Command): else: raise libbe.command.UserError( 'Ambiguous bugdir {}'.format(sorted(bugdirs.values()))) - if params['setting'] == None: + if params['setting'] is None: keys = bugdir.settings_properties keys.sort() for key in keys: @@ -94,7 +94,7 @@ class Set (libbe.command.Command): msg += 'Allowed settings:\n ' msg += '\n '.join(bugdir.settings_properties) raise libbe.command.UserError(msg) - if params['value'] == None: + if params['value'] is None: print(_value_string(bugdir, params['setting'])) else: if params['value'] == 'none': @@ -181,7 +181,7 @@ def get_bugdir_settings(): lines = dstr.split('\n') while lines[0].startswith('This property defaults to') == False: lines.pop(0) - assert len(lines) != None, \ + assert len(lines) is not None, \ 'Unexpected vcs_name docstring:\n "%s"' % dstr lines.insert( 0, 'The name of the revision control system to use.\n') diff --git a/libbe/command/show.py b/libbe/command/show.py index c08fd1f..2e7d59e 100644 --- a/libbe/command/show.py +++ b/libbe/command/show.py @@ -166,7 +166,7 @@ def _xml_footer(): return ['</be-xml>'] def output(bugdirs, ids, encoding, as_xml=True, with_comments=True): - if ids == None or len(ids) == 0: + if ids is None or len(ids) == 0: ids = [] for bugdir in list(bugdirs.values()): bugdir.load_all_bugs() diff --git a/libbe/command/subscribe.py b/libbe/command/subscribe.py index 45cd0dd..b6f9cf0 100644 --- a/libbe/command/subscribe.py +++ b/libbe/command/subscribe.py @@ -127,17 +127,17 @@ class Subscribe (libbe.command.Command): if params['list-all'] == True: assert len(params['id']) == 0, params['id'] subscriber = params['subscriber'] - if subscriber == None: + if subscriber is None: subscriber = self._get_user_id() if params['unsubscribe'] == True: - if params['servers'] == None: + if params['servers'] is None: params['servers'] = 'INVALID' - if params['types'] == None: + if params['types'] is None: params['types'] = 'INVALID' else: - if params['servers'] == None: + if params['servers'] is None: params['servers'] = '*' - if params['types'] == None: + if params['types'] is None: params['types'] = 'all' servers = params['servers'].split(',') types = params['types'].split(',') @@ -247,7 +247,7 @@ def _get_subscriber(extra_strings, subscriber, type_root): def subscribe(extra_strings, subscriber, types, servers, type_root): args = _get_subscriber(extra_strings, subscriber, type_root) - if args == None: # no match + if args is None: # no match extra_strings.append(_generate_string(subscriber, types, servers)) return extra_strings # Alter matched string @@ -270,7 +270,7 @@ def subscribe(extra_strings, subscriber, types, servers, type_root): def unsubscribe(extra_strings, subscriber, types, servers, type_root): args = _get_subscriber(extra_strings, subscriber, type_root) - if args == None: # no match + if args is None: # no match return extra_strings # pass # Remove matched string i,s,ts,srvs = args diff --git a/libbe/command/tag.py b/libbe/command/tag.py index 51e2abe..bbffe09 100644 --- a/libbe/command/tag.py +++ b/libbe/command/tag.py @@ -104,9 +104,9 @@ class Tag (libbe.command.Command): ]) def _run(self, **params): - if params['id'] == None and params['list'] == False: + if params['id'] is None and params['list'] == False: raise libbe.command.UserError('Please specify a bug id.') - if params['id'] != None and params['list'] == True: + if params['id'] is not None and params['list'] == True: raise libbe.command.UserError( 'Do not specify a bug id with the --list option.') bugdirs = self._get_bugdirs() diff --git a/libbe/command/target.py b/libbe/command/target.py index b13647e..eb11c11 100644 --- a/libbe/command/target.py +++ b/libbe/command/target.py @@ -90,10 +90,10 @@ class Target (libbe.command.Command): def _run(self, **params): if params['resolve'] == False: - if params['id'] == None: + if params['id'] is None: raise libbe.command.UserError('Please specify a bug id.') else: - if params['target'] != None: + if params['target'] is not None: raise libbe.command.UserError('Too many arguments') params['target'] = params.pop('id') bugdirs = self._get_bugdirs() @@ -106,7 +106,7 @@ class Target (libbe.command.Command): raise libbe.command.UserError( 'Ambiguous bugdir {}'.format(sorted(bugdirs.values()))) bug = bug_from_target_summary(bugdirs, bugdir, params['target']) - if bug == None: + if bug is None: print('No target assigned.', file=self.stdout) else: print(bug.id.long_user(), file=self.stdout) @@ -114,9 +114,9 @@ class Target (libbe.command.Command): bugdir,bug,comment = ( libbe.command.util.bugdir_bug_comment_from_user_id( bugdirs, params['id'])) - if params['target'] == None: + if params['target'] is None: target = bug_target(bugdirs, bug) - if target == None: + if target is None: print('No target assigned.', file=self.stdout) else: print(target.summary, file=self.stdout) @@ -157,8 +157,8 @@ by UUID), try """ def bug_from_target_summary(bugdirs, bugdir, summary=None): - if summary == None: - if bugdir.target == None: + if summary is None: + if bugdir.target is None: return None else: return bugdir.bug_from_uuid(bugdir.target) @@ -196,7 +196,7 @@ def remove_target(bugdirs, bug): def add_target(bugdirs, bugdir, bug, summary): target = bug_from_target_summary(bugdirs, bugdir, summary) - if target == None: + if target is None: target = bugdir.new_bug(summary=summary) target.severity = 'target' libbe.command.depend.add_block(target, bug) diff --git a/libbe/command/util.py b/libbe/command/util.py index 49f3490..632e22c 100644 --- a/libbe/command/util.py +++ b/libbe/command/util.py @@ -38,7 +38,7 @@ def complete_command(command, argument, fragment=None): def comp_path(fragment=None): """List possible path completions for fragment.""" - if fragment == None: + if fragment is None: fragment = '.' comps = glob.glob(fragment+'*') + glob.glob(fragment+'/*') if len(comps) == 1 and os.path.isdir(comps[0]): @@ -64,14 +64,14 @@ def assignees(bugdirs): for bugdir in list(bugdirs.values()): bugdir.load_all_bugs() ret.update(set([bug.assigned for bug in bugdir - if bug.assigned != None])) + if bug.assigned is not None])) return list(ret) def complete_assigned(command, argument, fragment=None): return assignees(command._get_bugdirs()) def complete_extra_strings(command, argument, fragment=None): - if fragment == None: + if fragment is None: return [] return [fragment] @@ -88,7 +88,7 @@ def complete_bug_comment_id(command, argument, fragment=None, import libbe.bugdir import libbe.util.id bugdirs = command._get_bugdirs() - if fragment == None or len(fragment) == 0: + if fragment is None or len(fragment) == 0: fragment = '/' try: p = libbe.util.id.parse_user(bugdirs, fragment) @@ -101,7 +101,7 @@ def complete_bug_comment_id(command, argument, fragment=None, except libbe.util.id.NoIDMatches: return [] except libbe.util.id.MultipleIDMatches as e: - if e.common == None: + if e.common is None: # choose among bugdirs return e.matches common = e.common @@ -109,7 +109,7 @@ def complete_bug_comment_id(command, argument, fragment=None, root,residual = libbe.util.id.residual(common, fragment) p = libbe.util.id.parse_user(bugdirs, e.common) bug = None - if matches == None: # fragment was complete, get a list of children uuids + if matches is None: # fragment was complete, get a list of children uuids if p['type'] == 'bugdir': bugdir = bugdirs[p['bugdir']] matches = bugdir.uuids() @@ -131,11 +131,11 @@ def complete_bug_comment_id(command, argument, fragment=None, if comments == False: return[fragment] bugdir = bugdirs[p['bugdir']] - if bug == None: + if bug is None: bug = bugdir.bug_from_uuid(p['bug']) child_fn = bug.comment_from_uuid elif p['type'] == 'comment': - assert matches == None, matches + assert matches is None, matches return [fragment] possible = [] common += '/' @@ -180,7 +180,7 @@ def select_values(string, possible_values, name="unkown"): ['abc', 'def', 'hij'] """ possible_values = list(possible_values) # don't alter the original - if string == None: + if string is None: pass elif string.startswith('-'): blacklisted_values = set(string[1:].split(',')) diff --git a/libbe/diff.py b/libbe/diff.py index 595d512..f7ddfbe 100644 --- a/libbe/diff.py +++ b/libbe/diff.py @@ -256,7 +256,7 @@ class DiffTree (libbe.util.tree.Tree): pass else: self.join(root, parent, data_part) - if data_part != None: + if data_part is not None: depth += 1 for child in self: root = child.report(root, self, depth) @@ -264,7 +264,7 @@ class DiffTree (libbe.util.tree.Tree): def make_root(self): return [] def join(self, root, parent, data_part): - if data_part != None: + if data_part is not None: root.append(data_part) def data_part(self, depth, indent=True): if self.data is None: @@ -452,9 +452,9 @@ class Diff (object): if BUGDIR_TYPE_ALL in bugdir_types \ or BUGDIR_TYPE_MOD in bugdir_types \ or uuid in subscribed_bugs: - if old_bug.storage != None and old_bug.storage.is_readable(): + if old_bug.storage is not None and old_bug.storage.is_readable(): old_bug.load_comments() - if new_bug.storage != None and new_bug.storage.is_readable(): + if new_bug.storage is not None and new_bug.storage.is_readable(): new_bug.load_comments() if old_bug != new_bug: modified.append((old_bug, new_bug)) @@ -534,7 +534,7 @@ class Diff (object): for p in properties] return self._attribute_changes(old, new, attributes) def _bugdir_attribute_changes(self): - return self._settings_properties_attribute_changes( \ + return self._settings_properties_attribute_changes( self.old_bugdir, self.new_bugdir) def _bug_attribute_changes(self, old, new): return self._settings_properties_attribute_changes(old, new) @@ -558,8 +558,8 @@ class Diff (object): root = self._cached_full_report bugdir_types = [s.type for s in subscriptions if s.id == BUGDIR_ID] subscribed_bugs = [s.id for s in subscriptions - if BUG_TYPE_ALL.has_descendant( \ - s.type, match_self=True)] + if BUG_TYPE_ALL.has_descendant( + s.type, match_self=True)] selected_by_bug = [node.name for node in root.child_by_path('bugdir/bugs')] if BUGDIR_TYPE_ALL in bugdir_types: diff --git a/libbe/storage/base.py b/libbe/storage/base.py index 09e176b..3ee056d 100644 --- a/libbe/storage/base.py +++ b/libbe/storage/base.py @@ -569,6 +569,7 @@ class VersionedStorage (Storage): if TESTING: + # noinspection PyBroadException class StorageTestCase (unittest.TestCase): """Test cases for Storage class.""" @@ -1131,11 +1132,11 @@ if TESTING: storage_testcase_classes = [ c for c in ( ob for ob in list(globals().values()) if isinstance(ob, type)) - if ((issubclass(c, StorageTestCase) \ - and c.Class == Storage) + if ((issubclass(c, StorageTestCase) + and c.Class == Storage) or - (issubclass(c, VersionedStorageTestCase) \ - and c.Class == VersionedStorage))] + (issubclass(c, VersionedStorageTestCase) + and c.Class == VersionedStorage))] for base_class in storage_testcase_classes: testcase_class_name = storage_class.__name__ + base_class.__name__ diff --git a/libbe/storage/util/config.py b/libbe/storage/util/config.py index f53f5b2..c6546a0 100644 --- a/libbe/storage/util/config.py +++ b/libbe/storage/util/config.py @@ -69,7 +69,7 @@ def set_val(name, value, section="DEFAULT", encoding=None): encoding : str The config file's encoding, defaults to :py:data:`default_encoding`. """ - if encoding == None: + if encoding is None: encoding = default_encoding config = configparser.ConfigParser() if os.path.exists(path()) == False: # touch file or config @@ -112,7 +112,7 @@ def get_val(name, section="DEFAULT", default=None, encoding=None): True """ if os.path.exists(path()): - if encoding == None: + if encoding is None: encoding = default_encoding config = configparser.ConfigParser() f = codecs.open(path(), 'r', encoding) diff --git a/libbe/storage/util/properties.py b/libbe/storage/util/properties.py index 6a2b964..4489a44 100644 --- a/libbe/storage/util/properties.py +++ b/libbe/storage/util/properties.py @@ -42,50 +42,53 @@ import copy import types import libbe + if libbe.TESTING: import unittest -class ValueCheckError (ValueError): +class ValueCheckError(ValueError): def __init__(self, name, value, allowed): - action = "in" # some list of allowed values - if type(allowed) == types.FunctionType: - action = "allowed by" # some allowed-value check function + action = "in" # some list of allowed values + if isinstance(allowed, types.FunctionType): + action = "allowed by" # some allowed-value check function msg = "%s not %s %s for %s" % (value, action, allowed, name) ValueError.__init__(self, msg) self.name = name self.value = value self.allowed = allowed + def Property(funcs): """ End a chain of property decorators, returning a property. """ - args = {} - args["fget"] = funcs.get("fget", None) - args["fset"] = funcs.get("fset", None) - args["fdel"] = funcs.get("fdel", None) - args["doc"] = funcs.get("doc", None) - - #print("Creating a property with") - #for key, val in args.items(): print(key, value) + args = {"fget": funcs.get("fget", None), "fset": funcs.get("fset", None), "fdel": funcs.get("fdel", None), + "doc": funcs.get("doc", None)} + + # print("Creating a property with") + # for key, val in args.items(): print(key, value) return property(**args) + def doc_property(doc=None): """ Add a docstring to a chain of property decorators. """ + def decorator(funcs=None): """ Takes either a dict of funcs {"fget":fnX, "fset":fnY, ...} or a function fn() returning such a dict. """ if hasattr(funcs, "__call__"): - funcs = funcs() # convert from function-arg to dict + funcs = funcs() # convert from function-arg to dict funcs["doc"] = doc return funcs + return decorator + def local_property(name, null=None, mutable_null=False): """ Define get/set access to per-parent-instance local storage. Uses @@ -95,11 +98,13 @@ def local_property(name, null=None, mutable_null=False): If mutable_null == True, we only release deepcopies of the null to the outside world. """ + def decorator(funcs): if hasattr(funcs, "__call__"): funcs = funcs() fget = funcs.get("fget", None) fset = funcs.get("fset", None) + def _fget(self): if fget is not None: fget(self) @@ -109,40 +114,49 @@ def local_property(name, null=None, mutable_null=False): ret_null = null value = getattr(self, "_%s_value" % name, ret_null) return value + def _fset(self, value): setattr(self, "_%s_value" % name, value) if fset is not None: fset(self, value) + funcs["fget"] = _fget funcs["fset"] = _fset funcs["name"] = name return funcs + return decorator + def settings_property(name, null=None): """ Similar to local_property, except where local_property stores the value in instance._<name>_value, settings_property stores the value in instance.settings[name]. """ + def decorator(funcs): if hasattr(funcs, "__call__"): funcs = funcs() fget = funcs.get("fget", None) fset = funcs.get("fset", None) + def _fget(self): if fget is not None: fget(self) value = self.settings.get(name, null) return value + def _fset(self, value): self.settings[name] = value if fset is not None: fset(self, value) + funcs["fget"] = _fget funcs["fset"] = _fset funcs["name"] = name return funcs + return decorator @@ -158,22 +172,30 @@ def settings_property(name, null=None): # True def _hash_mutable_value(value): return repr(value) + + def _init_mutable_property_cache(self): if not hasattr(self, "_mutable_property_cache_hash"): # first call to _fget for any mutable property self._mutable_property_cache_hash = {} self._mutable_property_cache_copy = {} + + def _set_cached_mutable_property(self, cacher_name, property_name, value): _init_mutable_property_cache(self) self._mutable_property_cache_hash[(cacher_name, property_name)] = \ _hash_mutable_value(value) self._mutable_property_cache_copy[(cacher_name, property_name)] = \ copy.deepcopy(value) + + def _get_cached_mutable_property(self, cacher_name, property_name, default=None): _init_mutable_property_cache(self) if (cacher_name, property_name) not in self._mutable_property_cache_copy: return default return self._mutable_property_cache_copy[(cacher_name, property_name)] + + def _eq_cached_mutable_property(self, cacher_name, property_name, value, default=None): _init_mutable_property_cache(self) if (cacher_name, property_name) not in self._mutable_property_cache_hash: @@ -194,12 +216,14 @@ def defaulting_property(default=None, null=None, null should never escape to the outside world, so don't worry about it being a mutable. """ + def decorator(funcs): if hasattr(funcs, "__call__"): funcs = funcs() fget = funcs.get("fget") fset = funcs.get("fset") name = funcs.get("name", "<unknown>") + def _fget(self): value = fget(self) if value == null: @@ -208,63 +232,81 @@ def defaulting_property(default=None, null=None, else: return default return value + def _fset(self, value): if value == default: value = null fset(self, value) + funcs["fget"] = _fget funcs["fset"] = _fset return funcs + return decorator + def fn_checked_property(value_allowed_fn): """ Define allowed values for get/set access to a property. """ + def decorator(funcs): if hasattr(funcs, "__call__"): funcs = funcs() fget = funcs.get("fget") fset = funcs.get("fset") name = funcs.get("name", "<unknown>") + def _fget(self): value = fget(self) - if value_allowed_fn(value) != True: + if not value_allowed_fn(value): raise ValueCheckError(name, value, value_allowed_fn) return value + def _fset(self, value): - if value_allowed_fn(value) != True: + if not value_allowed_fn(value): raise ValueCheckError(name, value, value_allowed_fn) fset(self, value) + funcs["fget"] = _fget funcs["fset"] = _fset return funcs + return decorator -def checked_property(allowed=[]): + +def checked_property(allowed=None): """ Define allowed values for get/set access to a property. """ + def decorator(funcs): if hasattr(funcs, "__call__"): funcs = funcs() fget = funcs.get("fget") fset = funcs.get("fset") name = funcs.get("name", "<unknown>") + def _fget(self): value = fget(self) if value not in allowed: raise ValueCheckError(name, value, allowed) return value + def _fset(self, value): if value not in allowed: raise ValueCheckError(name, value, allowed) fset(self, value) + funcs["fget"] = _fget funcs["fset"] = _fset return funcs + if allowed is None: + allowed = [] + return decorator + def cached_property(generator, initVal=None, mutable=False): """ Allow caching of values generated by generator(instance), where @@ -291,11 +333,13 @@ def cached_property(generator, initVal=None, mutable=False): generator is called whenever the cached value would otherwise be used. """ + def decorator(funcs): if hasattr(funcs, "__call__"): funcs = funcs() fget = funcs.get("fget") name = funcs.get("name", "<unknown>") + def _fget(self): cache = getattr(self, "_%s_cache" % name, True) value = fget(self) @@ -309,10 +353,13 @@ def cached_property(generator, initVal=None, mutable=False): else: value = generator(self) return value + funcs["fget"] = _fget return funcs + return decorator + def primed_property(primer, initVal=None, unprimeableVal=None): """ Just like a cached_property, except that instead of returning a @@ -326,14 +373,17 @@ def primed_property(primer, initVal=None, unprimeableVal=None): whenever ._<name>_prime is True, or is False or missing and value == initVal. """ + def decorator(funcs): if hasattr(funcs, "__call__"): funcs = funcs() fget = funcs.get("fget") name = funcs.get("name", "<unknown>") + def _fget(self): prime = getattr(self, "_%s_prime" % name, False) - if prime == False: + value = None + if not prime: value = fget(self) if prime == True or (prime == False and value == initVal): primer(self) @@ -341,10 +391,13 @@ def primed_property(primer, initVal=None, unprimeableVal=None): if prime == False and value == initVal: return unprimeableVal return value + funcs["fget"] = _fget return funcs + return decorator + def change_hook_property(hook, mutable=False, default=None): """Call the function `hook` whenever a value different from the current value is set. @@ -373,40 +426,46 @@ def change_hook_property(hook, mutable=False, default=None): `hook(instance, old_value, new_value)`, where `instance` is a reference to the class instance to which this property belongs. """ + def decorator(funcs): if hasattr(funcs, "__call__"): funcs = funcs() fget = funcs.get("fget") fset = funcs.get("fset") name = funcs.get("name", "<unknown>") - def _fget(self, new_value=None, from_fset=False): # only used if mutable == True + + def _fget(self, new_value=None, from_fset=False): # only used if mutable == True if from_fset: - value = new_value # compare new value with cached + value = new_value # compare new value with cached else: - value = fget(self) # compare current value with cached + value = fget(self) # compare current value with cached if _eq_cached_mutable_property(self, "change hook property", name, value, default) != 0: # there has been a change, cache new value old_value = _get_cached_mutable_property(self, "change hook property", name, default) _set_cached_mutable_property(self, "change hook property", name, value) - if from_fset: # return previously cached value + if from_fset: # return previously cached value value = old_value - else: # the value changed while we weren't looking + else: # the value changed while we weren't looking hook(self, old_value, value) return value + def _fset(self, value): - if mutable: # get cached previous value + if mutable: # get cached previous value old_value = _fget(self, new_value=value, from_fset=True) else: old_value = fget(self) fset(self, value) if value != old_value: hook(self, old_value, value) + if mutable: funcs["fget"] = _fget funcs["fset"] = _fset return funcs + return decorator + if libbe.TESTING: class DecoratorTests(unittest.TestCase): def testLocalDoc(self): @@ -415,40 +474,48 @@ if libbe.TESTING: @doc_property("A fancy property") def x(): return {} + self.assertTrue(Test.x.__doc__ == "A fancy property", Test.x.__doc__) + def testLocalProperty(self): class Test(object): @Property @local_property(name="LOCAL") def x(): return {} + t = Test() - self.assertTrue(t.x == None, str(t.x)) - t.x = 'z' # the first set initializes ._LOCAL_value + self.assertTrue(t.x is None, str(t.x)) + t.x = 'z' # the first set initializes ._LOCAL_value self.assertTrue(t.x == 'z', str(t.x)) self.assertTrue("_LOCAL_value" in dir(t), dir(t)) self.assertTrue(t._LOCAL_value == 'z', t._LOCAL_value) + def testSettingsProperty(self): class Test(object): @Property @settings_property(name="attr") def x(): return {} + def __init__(self): self.settings = {} + t = Test() - self.assertTrue(t.x == None, str(t.x)) - t.x = 'z' # the first set initializes ._LOCAL_value + self.assertTrue(t.x is None, str(t.x)) + t.x = 'z' # the first set initializes ._LOCAL_value self.assertTrue(t.x == 'z', str(t.x)) self.assertTrue("attr" in t.settings, t.settings) self.assertTrue(t.settings["attr"] == 'z', t.settings["attr"]) + def testDefaultingLocalProperty(self): class Test(object): @Property @defaulting_property(default='y', null='x') @local_property(name="DEFAULT", null=5) def x(): return {} + t = Test() self.assertTrue(t.x == 5, str(t.x)) t.x = 'x' @@ -459,14 +526,17 @@ if libbe.TESTING: self.assertTrue(t.x == 'z', str(t.x)) t.x = 5 self.assertTrue(t.x == 5, str(t.x)) + def testCheckedLocalProperty(self): class Test(object): @Property @checked_property(allowed=['x', 'y', 'z']) @local_property(name="CHECKED") def x(): return {} + def __init__(self): self._CHECKED_value = 'x' + t = Test() self.assertTrue(t.x == 'x', str(t.x)) try: @@ -475,6 +545,7 @@ if libbe.TESTING: except ValueCheckError as e: pass self.assertTrue(type(e) == ValueCheckError, type(e)) + def testTwoCheckedLocalProperties(self): class Test(object): @Property @@ -486,9 +557,11 @@ if libbe.TESTING: @checked_property(allowed=['a', 'b', 'c']) @local_property(name="A") def a(): return {} + def __init__(self): self._A_value = 'a' self._X_value = 'x' + t = Test() try: t.x = 'a' @@ -508,14 +581,17 @@ if libbe.TESTING: t.a = 'a' t.a = 'b' t.a = 'c' + def testFnCheckedLocalProperty(self): class Test(object): @Property - @fn_checked_property(lambda v : v in ['x', 'y', 'z']) + @fn_checked_property(lambda v: v in ['x', 'y', 'z']) @local_property(name="CHECKED") def x(): return {} + def __init__(self): self._CHECKED_value = 'x' + t = Test() self.assertTrue(t.x == 'x', str(t.x)) try: @@ -524,57 +600,65 @@ if libbe.TESTING: except ValueCheckError as e: pass self.assertTrue(type(e) == ValueCheckError, type(e)) + def testCachedLocalProperty(self): class Gen(object): def __init__(self): self.i = 0 + def __call__(self, owner): self.i += 1 return self.i + class Test(object): @Property @cached_property(generator=Gen(), initVal=None) @local_property(name="CACHED") def x(): return {} + t = Test() self.assertFalse("_CACHED_cache" in dir(t), - getattr(t, "_CACHED_cache", None)) + getattr(t, "_CACHED_cache", None)) self.assertTrue(t.x == 1, t.x) self.assertTrue(t.x == 1, t.x) self.assertTrue(t.x == 1, t.x) t.x = 8 self.assertTrue(t.x == 8, t.x) self.assertTrue(t.x == 8, t.x) - t._CACHED_cache = False # Caching is off, but the stored value - val = t.x # is 8, not the initVal (None), so we - self.assertTrue(val == 8, val) # get 8. - t._CACHED_value = None # Now we've set the stored value to None - val = t.x # so future calls to fget (like this) - self.assertTrue(val == 2, val) # will call the generator every time... + t._CACHED_cache = False # Caching is off, but the stored value + val = t.x # is 8, not the initVal (None), so we + self.assertTrue(val == 8, val) # get 8. + t._CACHED_value = None # Now we've set the stored value to None + val = t.x # so future calls to fget (like this) + self.assertTrue(val == 2, val) # will call the generator every time... val = t.x self.assertTrue(val == 3, val) val = t.x self.assertTrue(val == 4, val) - t._CACHED_cache = True # We turn caching back on, and get - self.assertTrue(t.x == 1, str(t.x)) # the original cached value. - del t._CACHED_cached_value # Removing that value forces a - self.assertTrue(t.x == 5, str(t.x)) # single cache-regenerating call - self.assertTrue(t.x == 5, str(t.x)) # to the genenerator, after which - self.assertTrue(t.x == 5, str(t.x)) # we get the new cached value. + t._CACHED_cache = True # We turn caching back on, and get + self.assertTrue(t.x == 1, str(t.x)) # the original cached value. + del t._CACHED_cached_value # Removing that value forces a + self.assertTrue(t.x == 5, str(t.x)) # single cache-regenerating call + self.assertTrue(t.x == 5, str(t.x)) # to the genenerator, after which + self.assertTrue(t.x == 5, str(t.x)) # we get the new cached value. + def testPrimedLocalProperty(self): class Test(object): def prime(self): self.settings["PRIMED"] = self.primeVal + @Property @primed_property(primer=prime, initVal=None, unprimeableVal=2) @settings_property(name="PRIMED") def x(): return {} + def __init__(self): - self.settings={} + self.settings = {} self.primeVal = "initialized" + t = Test() self.assertFalse("_PRIMED_prime" in dir(t), - getattr(t, "_PRIMED_prime", None)) + getattr(t, "_PRIMED_prime", None)) self.assertTrue(t.x == "initialized", t.x) t.x = 1 self.assertTrue(t.x == 1, t.x) @@ -590,6 +674,7 @@ if libbe.TESTING: t.x = None t.primeVal = None self.assertTrue(t.x == 2, t.x) + def testChangeHookLocalProperty(self): class Test(object): def _hook(self, old, new): @@ -600,16 +685,18 @@ if libbe.TESTING: @change_hook_property(_hook) @local_property(name="HOOKED") def x(): return {} + t = Test() t.x = 1 - self.assertTrue(t.old == None, t.old) + self.assertTrue(t.old is None, t.old) self.assertTrue(t.new == 1, t.new) t.x = 1 - self.assertTrue(t.old == None, t.old) + self.assertTrue(t.old is None, t.old) self.assertTrue(t.new == 1, t.new) t.x = 2 self.assertTrue(t.old == 1, t.old) self.assertTrue(t.new == 2, t.new) + def testChangeHookMutableProperty(self): class Test(object): def _hook(self, old, new): @@ -621,10 +708,11 @@ if libbe.TESTING: @change_hook_property(_hook, mutable=True) @local_property(name="HOOKED") def x(): return {} + t = Test() t.hook_calls = 0 t.x = [] - self.assertTrue(t.old == None, t.old) + self.assertTrue(t.old is None, t.old) self.assertTrue(t.new == [], t.new) self.assertTrue(t.hook_calls == 1, t.hook_calls) a = t.x @@ -650,19 +738,20 @@ if libbe.TESTING: self.assertTrue(t.old == [], t.old) self.assertTrue(t.new == [5], t.new) self.assertTrue(t.hook_calls == 4, t.hook_calls) - t.x.append(6) # this append(6) is not noticed yet + t.x.append(6) # this append(6) is not noticed yet self.assertTrue(t.old == [], t.old) - self.assertTrue(t.new == [5,6], t.new) + self.assertTrue(t.new == [5, 6], t.new) self.assertTrue(t.hook_calls == 4, t.hook_calls) # this append(7) is not noticed, but the t.x get causes the # append(6) to be noticed t.x.append(7) self.assertTrue(t.old == [5], t.old) - self.assertTrue(t.new == [5,6,7], t.new) + self.assertTrue(t.new == [5, 6, 7], t.new) self.assertTrue(t.hook_calls == 5, t.hook_calls) - a = t.x # now the append(7) is noticed - self.assertTrue(t.old == [5,6], t.old) - self.assertTrue(t.new == [5,6,7], t.new) + a = t.x # now the append(7) is noticed + self.assertTrue(t.old == [5, 6], t.old) + self.assertTrue(t.new == [5, 6, 7], t.new) self.assertTrue(t.hook_calls == 6, t.hook_calls) + suite = unittest.TestLoader().loadTestsFromTestCase(DecoratorTests) diff --git a/libbe/storage/util/settings_object.py b/libbe/storage/util/settings_object.py index d872f49..8981e35 100644 --- a/libbe/storage/util/settings_object.py +++ b/libbe/storage/util/settings_object.py @@ -57,7 +57,7 @@ class EMPTY (_Token): def prop_save_settings(self, old, new): """The default action undertaken when a property changes. """ - if self.storage != None and self.storage.is_writeable(): + if self.storage is not None and self.storage.is_writeable(): self.save_settings() def prop_load_settings(self): @@ -68,7 +68,7 @@ def prop_load_settings(self): `._setup_saved_settings()` internally. If `.storage` is inaccessible, don't do anything. """ - if self.storage != None and self.storage.is_readable(): + if self.storage is not None and self.storage.is_readable(): self.load_settings() # Some name-mangling routines for pretty printing setting names @@ -160,18 +160,18 @@ def versioned_property(name, doc, required_saved_properties.append(name) def decorator(funcs): fulldoc = doc - if default != None or generator == None: + if default is not None or generator is None: defaulting = defaulting_property(default=default, null=EMPTY, mutable_default=mutable) fulldoc += "\n\nThis property defaults to %s." % default - if generator != None: + if generator is not None: cached = cached_property(generator=generator, initVal=EMPTY, mutable=mutable) fulldoc += "\n\nThis property is generated with %s." % generator - if check_fn != None: + if check_fn is not None: fn_checked = fn_checked_property(value_allowed_fn=check_fn) fulldoc += "\n\nThis property is checked with %s." % check_fn - if allowed != None: + if allowed is not None: checked = checked_property(allowed=allowed) fulldoc += "\n\nThe allowed values for this property are: %s." \ % (', '.join(allowed)) @@ -182,13 +182,13 @@ 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 or generator == None: + if default is not None or generator is None: deco = defaulting(deco) - if generator != None: + if generator is not None: deco = cached(deco) - if check_fn != None: + if check_fn is not None: deco = fn_checked(deco) - if allowed != None: + if allowed is not None: deco = checked(deco) return Property(deco) return decorator @@ -233,7 +233,7 @@ class SavedSettingsObject(object): Sets up a settings dict loaded from storage. Fills in all missing settings entries with EMPTY. """ - if settings == None: + if settings is None: settings = {} for property in self.settings_properties: if property not in self.settings \ @@ -274,7 +274,7 @@ class SavedSettingsObject(object): def clear_cached_setting(self, setting=None): "If setting=None, clear *all* cached settings" - if setting != None: + if setting is not None: if hasattr(self, "_%s_cached_value" % setting): delattr(self, "_%s_cached_value" % setting) else: @@ -342,20 +342,20 @@ if libbe.TESTING == True: # accessing t.content_type triggers the priming, but # t.storage.is_readable() == False, so nothing happens. t.storage.readable = False - self.assertTrue(t.content_type == None, t.content_type) + self.assertTrue(t.content_type is None, t.content_type) self.assertTrue(t.settings == {}, t.settings) self.assertTrue(len(t.settings) == 0, len(t.settings)) - self.assertTrue(t.content_type == None, t.content_type) + self.assertTrue(t.content_type is None, t.content_type) # accessing t.content_type triggers the priming again, and # now that t.storage.is_readable() == True, this fills out # t.settings with EMPTY data. At this point there should # be one load and no saves. t.storage.readable = True - self.assertTrue(t.content_type == None, t.content_type) + self.assertTrue(t.content_type is None, t.content_type) self.assertTrue(len(t.settings) == 1, len(t.settings)) self.assertTrue(t.settings["Content-type"] == EMPTY, t.settings["Content-type"]) - self.assertTrue(t.content_type == None, t.content_type) + self.assertTrue(t.content_type is None, t.content_type) self.assertTrue(t.load_count == 1, t.load_count) self.assertTrue(len(t.storage) == 0, len(t.storage)) # an explicit call to load settings forces a reload, @@ -364,7 +364,7 @@ if libbe.TESTING == True: self.assertTrue(len(t.settings) == 1, len(t.settings)) self.assertTrue(t.settings["Content-type"] == EMPTY, t.settings["Content-type"]) - self.assertTrue(t.content_type == None, t.content_type) + self.assertTrue(t.content_type is None, t.content_type) self.assertTrue(t.load_count == 2, t.load_count) self.assertTrue(len(t.storage) == 0, len(t.storage)) # now we set a value @@ -400,7 +400,7 @@ if libbe.TESTING == True: t.content_type = EMPTY self.assertTrue(t.settings["Content-type"] == EMPTY, t.settings["Content-type"]) - self.assertTrue(t.content_type == None, t.content_type) + self.assertTrue(t.content_type is None, t.content_type) self.assertTrue(len(t.settings) == 1, len(t.settings)) self.assertTrue(t.settings["Content-type"] == EMPTY, t.settings["Content-type"]) @@ -592,7 +592,7 @@ if libbe.TESTING == True: def list_type(): return {} t = Test() self.assertTrue(len(t.storage) == 0, len(t.storage)) - self.assertTrue(t.list_type == None, t.list_type) + self.assertTrue(t.list_type is None, t.list_type) self.assertTrue(len(t.storage) == 0, len(t.storage)) self.assertTrue(t.settings["List-type"]==EMPTY, t.settings["List-type"]) @@ -614,6 +614,6 @@ if libbe.TESTING == True: {'List-type':[5]}], t.storage) - unitsuite = unittest.TestLoader().loadTestsFromTestCase( \ + unitsuite = unittest.TestLoader().loadTestsFromTestCase( SavedSettingsObjectTests) suite = unittest.TestSuite([unitsuite, doctest.DocTestSuite()]) diff --git a/libbe/storage/util/upgrade.py b/libbe/storage/util/upgrade.py index 12d177a..6b278f1 100644 --- a/libbe/storage/util/upgrade.py +++ b/libbe/storage/util/upgrade.py @@ -117,7 +117,7 @@ class Upgrader (object): self.repo = repo vcs_name = self._get_vcs_name() - if vcs_name == None: + if vcs_name is None: vcs_name = 'None' self.vcs = libbe.storage.vcs.vcs_by_name(vcs_name) self.vcs.repo = self.repo diff --git a/libbe/storage/vcs/base.py b/libbe/storage/vcs/base.py index 0521e90..813905c 100644 --- a/libbe/storage/vcs/base.py +++ b/libbe/storage/vcs/base.py @@ -430,7 +430,7 @@ class VCS (libbe.storage.base.VersionedStorage): Get the file contents as they were in a given revision. Revision==None specifies the current revision. """ - if revision != None: + if revision is not None: raise libbe.storage.base.InvalidRevision( 'The %s VCS does not support revision specifiers' % self.name) path = os.path.join(self.repo, path) @@ -553,7 +553,7 @@ class VCS (libbe.storage.base.VersionedStorage): -1 """ if not hasattr(self, '_parsed_version') \ - or self._parsed_version == None: + or self._parsed_version is None: num_part = self.version().split(' ')[0] self._parsed_version = [] for num in num_part.split('.'): @@ -598,7 +598,7 @@ class VCS (libbe.storage.base.VersionedStorage): return -1 def installed(self): - if self.version() != None: + if self.version() is not None: return True return False @@ -769,7 +769,7 @@ class VCS (libbe.storage.base.VersionedStorage): self._cached_path_id.remove_id(id) def _ancestors(self, id=None, revision=None): - if id==None: + if id is None: path = self.be_dir else: path = self.path(id, revision, relpath=False) @@ -794,7 +794,7 @@ class VCS (libbe.storage.base.VersionedStorage): self._u_rel_path(path), revision) listdir = lambda path : self._vcs_listdir( self._u_rel_path(path), revision) - if id==None: + if id is None: path = self.be_dir else: path = self.path(id, revision, relpath=False) @@ -813,7 +813,7 @@ class VCS (libbe.storage.base.VersionedStorage): if c is None: continue cpath = os.path.join(path, c) children[i] = self._u_path_to_id(cpath) - return [c for c in children if c != None] + return [c for c in children if c is not None] def _get(self, id, default=libbe.util.InvalidObject, revision=None): try: @@ -1017,7 +1017,7 @@ class VCS (libbe.storage.base.VersionedStorage): '/a.b/c/.be' """ if root is None: - assert self.repo != None, "VCS not rooted" + assert self.repo is not None, "VCS not rooted" root = self.repo return os.path.abspath(os.path.join(root, path)) @@ -1138,7 +1138,7 @@ if libbe.TESTING: dp = os.path.realpath(self.dirname) vcs_name = self.Class.name self.assertTrue( - dp == rp or rp == None, + dp == rp or rp is None, "%(vcs_name)s VCS root in wrong dir (%(dp)s %(rp)s)" % vars()) class VCS_get_user_id_TestCase(VCSTestCase): @@ -1151,7 +1151,7 @@ if libbe.TESTING: if user_id is None: return name,email = libbe.ui.util.user.parse_user_id(user_id) - if email != None: + if email is not None: self.assertTrue('@' in email, email) def make_vcs_testcase_subclasses(vcs_class, namespace): diff --git a/libbe/storage/vcs/bzr.py b/libbe/storage/vcs/bzr.py index 6da299a..fa5b879 100644 --- a/libbe/storage/vcs/bzr.py +++ b/libbe/storage/vcs/bzr.py @@ -64,7 +64,7 @@ class Bzr(base.VCS): self.versioned = True def _vcs_version(self): - if bzrlib == None: + if bzrlib is None: return None return bzrlib.__version__ @@ -77,7 +77,7 @@ class Bzr(base.VCS): return c.username() def _vcs_detect(self, path): - if self._u_search_parent_directories(path, '.bzr') != None : + if self._u_search_parent_directories(path, '.bzr') is not None: return True return False @@ -135,7 +135,7 @@ class Bzr(base.VCS): pass def _parse_revision_string(self, revision=None): - if revision == None: + if revision is None: return revision rev_opt = bzrlib.option.Option.OPTIONS['revision'] try: @@ -145,7 +145,7 @@ class Bzr(base.VCS): return rev_spec def _vcs_get_file_contents(self, path, revision=None): - if revision == None: + if revision is None: return base.VCS._vcs_get_file_contents(self, path, revision) path = os.path.join(self.repo, path) revision = self._parse_revision_string(revision) diff --git a/libbe/storage/vcs/darcs.py b/libbe/storage/vcs/darcs.py index 12eabf4..07ddb9e 100644 --- a/libbe/storage/vcs/darcs.py +++ b/libbe/storage/vcs/darcs.py @@ -178,7 +178,7 @@ class Darcs(base.VCS): if revision is None: return base.VCS._vcs_get_file_contents(self, path, revision) if self.version_cmp(2, 0, 0) == 1: - status,output,error = self._u_invoke_client( \ + status,output,error = self._u_invoke_client( 'show', 'contents', '--patch', revision, path) return output # Darcs versions < 2.0.0pre2 lack the 'show contents' command @@ -209,7 +209,7 @@ class Darcs(base.VCS): if self.version_cmp(2, 3, 1) == 1: # Sun Nov 15 20:32:06 EST 2009 thomashartman1@gmail.com # * add versioned show files functionality (darcs show files -p 'some patch') - status,output,error = self._u_invoke_client( \ + status,output,error = self._u_invoke_client( 'show', 'files', '--no-files', '--patch', revision) children = output.rstrip('\n').splitlines() rpath = '.' @@ -227,7 +227,7 @@ class Darcs(base.VCS): # Wed Dec 9 05:42:21 EST 2009 Luca Molteni <volothamp@gmail.com> # * resolve issue835 show file with file directory arguments path = path.rstrip(os.path.sep) - status,output,error = self._u_invoke_client( \ + status,output,error = self._u_invoke_client( 'show', 'files', '--patch', revision, path) files = output.rstrip('\n').splitlines() if path == '.': @@ -243,7 +243,7 @@ class Darcs(base.VCS): def _vcs_commit(self, commitfile, allow_empty=False): id = self.get_user_id() - if id == None or '@' not in id: + if id is None or '@' not in id: id = '%s <%s@invalid.com>' % (id, id) args = ['record', '--all', '--author', id, '--logfile', commitfile] status,output,error = self._u_invoke_client(*args) diff --git a/libbe/storage/vcs/git.py b/libbe/storage/vcs/git.py index 9a15c92..d8a966d 100644 --- a/libbe/storage/vcs/git.py +++ b/libbe/storage/vcs/git.py @@ -191,7 +191,7 @@ class PygitGit(base.VCS): return eobj def _vcs_get_file_contents(self, path, revision=None): - if revision == None: + if revision is None: return base.VCS._vcs_get_file_contents(self, path, revision) else: blob = self._git_get_object(path=path, revision=revision) @@ -311,7 +311,7 @@ class ExecGit (PygitGit): return None # Git has no infomation def _vcs_detect(self, path): - if self._u_search_parent_directories(path, '.git') != None : + if self._u_search_parent_directories(path, '.git') is not None: return True return False @@ -339,7 +339,7 @@ class ExecGit (PygitGit): if not os.path.isdir(self._u_abspath(path)): self._u_invoke_client('rm', '-f', path) def _vcs_get_file_contents(self, path, revision=None): - if revision == None: + if revision is None: return base.VCS._vcs_get_file_contents(self, path, revision) else: arg = '%s:%s' % (revision,path) diff --git a/libbe/storage/vcs/hg.py b/libbe/storage/vcs/hg.py index 0ebdfb0..6198bc8 100644 --- a/libbe/storage/vcs/hg.py +++ b/libbe/storage/vcs/hg.py @@ -74,7 +74,7 @@ class Hg(base.VCS): self.__updated = [] # work around http://mercurial.selenic.com/bts/issue618 def _vcs_version(self): - if version == None: + if version is None: return None return version() @@ -106,7 +106,7 @@ class Hg(base.VCS): def _vcs_detect(self, path): """Detect whether a directory is revision-controlled using Mercurial""" - if self._u_search_parent_directories(path, '.hg') != None: + if self._u_search_parent_directories(path, '.hg') is not None: return True return False @@ -131,7 +131,7 @@ class Hg(base.VCS): self.__updated.append(path) # work around http://mercurial.selenic.com/bts/issue618 def _vcs_get_file_contents(self, path, revision=None): - if revision == None: + if revision is None: return base.VCS._vcs_get_file_contents(self, path, revision) else: return self._u_invoke_client('cat', '-r', revision, path) diff --git a/libbe/storage/vcs/monotone.py b/libbe/storage/vcs/monotone.py index 1d74c5b..4eaced8 100644 --- a/libbe/storage/vcs/monotone.py +++ b/libbe/storage/vcs/monotone.py @@ -86,7 +86,7 @@ class Monotone (base.VCS): -1 """ if not hasattr(self, '_parsed_version') \ - or self._parsed_version == None: + or self._parsed_version is None: self._parsed_version = [int(x) for x in self.version().split('.')] for current,other in zip(self._parsed_version, args): c = cmp(current,other) @@ -129,7 +129,7 @@ class Monotone (base.VCS): return None # Monotone has no infomation def _vcs_detect(self, path): - if self._u_search_parent_directories(path, '_MTN') != None : + if self._u_search_parent_directories(path, '_MTN') is not None: return True return False @@ -153,11 +153,11 @@ class Monotone (base.VCS): """Invoke the client on our branch. """ arglist = [] - if self._db_path != None: + if self._db_path is not None: arglist.extend(['--db', self._db_path]) - if self._key != None: + if self._key is not None: arglist.extend(['--key', self._key]) - if self._key_dir != None: + if self._key_dir is not None: arglist.extend(['--keydir', self._key_dir]) arglist.extend(args) args = tuple(arglist) diff --git a/libbe/ui/command_line.py b/libbe/ui/command_line.py index b649e24..3b8089f 100644 --- a/libbe/ui/command_line.py +++ b/libbe/ui/command_line.py @@ -59,13 +59,13 @@ class CmdOptionParser(optparse.OptionParser): option.validate() self._option_by_name[option.name] = option long_opt = '--%s' % option.name - if option.short_name != None: + if option.short_name is not None: short_opt = '-%s' % option.short_name assert '_' not in option.name, \ 'Non-reconstructable option name %s' % option.name kwargs = {'dest':option.name.replace('-', '_'), 'help':option.help} - if option.arg == None: # a callback option + if option.arg is None: # a callback option kwargs['action'] = 'callback' kwargs['callback'] = self.callback elif option.arg.type == 'bool': @@ -77,7 +77,7 @@ class CmdOptionParser(optparse.OptionParser): kwargs['action'] = 'store' kwargs['metavar'] = option.arg.metavar kwargs['default'] = option.arg.default - if option.short_name != None: + if option.short_name is not None: opt = optparse.Option(short_opt, long_opt, **kwargs) else: opt = optparse.Option(long_opt, **kwargs) @@ -95,7 +95,7 @@ class CmdOptionParser(optparse.OptionParser): for name,value in list(options.items()): argument = None option = self._option_by_name[name] - if option.arg != None: + if option.arg is not None: argument = option.arg if value == '--complete': fragment = None @@ -108,7 +108,7 @@ class CmdOptionParser(optparse.OptionParser): name = args[i-1][2:] if name == option.name: break - elif option.short_name != None \ + elif option.short_name is not None \ and args[i-1].startswith('-') \ and args[i-1].endswith(option.short_name): break @@ -163,7 +163,7 @@ class CmdOptionParser(optparse.OptionParser): def complete(self, argument=None, fragment=None): comps = self.command.complete(argument, fragment) - if fragment != None: + if fragment is not None: comps = [c for c in comps if c.startswith(fragment)] if len(comps) > 0: print('\n'.join(comps), file=self.command.stdout) @@ -258,7 +258,7 @@ class BE (libbe.command.Command): cmdlist = [] for name in libbe.command.commands(): Class = libbe.command.get_command_class(command_name=name) - assert hasattr(Class, '__doc__') and Class.__doc__ != None, \ + assert hasattr(Class, '__doc__') and Class.__doc__ is not None, \ 'Command class %s missing docstring' % Class cmdlist.append((Class.name, Class.__doc__.splitlines()[0])) cmdlist.sort() diff --git a/libbe/ui/util/editor.py b/libbe/ui/util/editor.py index 8412ff6..5ab519f 100644 --- a/libbe/ui/util/editor.py +++ b/libbe/ui/util/editor.py @@ -64,14 +64,14 @@ def editor_string(comment=None, encoding=None): >>> del os.environ["EDITOR"] >>> del os.environ["VISUAL"] """ - if encoding == None: + if encoding is None: encoding = libbe.util.encoding.get_text_file_encoding() editor = None for name in ('VISUAL', 'EDITOR'): if name in os.environ and os.environ[name] != '': editor = os.environ[name] break - if editor == None: + if editor is None: raise CantFindEditor() fhandle, fname = tempfile.mkstemp() try: diff --git a/libbe/ui/util/user.py b/libbe/ui/util/user.py index 4a4e7f1..1d7e874 100644 --- a/libbe/ui/util/user.py +++ b/libbe/ui/util/user.py @@ -92,7 +92,7 @@ def create_user_id(name, email=None): parse_user_id : inverse """ assert len(name) > 0 - if email == None or len(email) == 0: + if email is None or len(email) == 0: return name else: return formataddr((name, email)) @@ -137,11 +137,11 @@ def get_user_id(storage=None): configured it directly. """ user = libbe.storage.util.config.get_val('user') - if user != None: + if user is not None: return user - if storage != None and hasattr(storage, 'get_user_id'): + if storage is not None and hasattr(storage, 'get_user_id'): user = storage.get_user_id() - if user != None: + if user is not None: return user name = get_fallback_fullname() email = get_fallback_email() diff --git a/libbe/util/encoding.py b/libbe/util/encoding.py index c5e242b..8a3b53e 100644 --- a/libbe/util/encoding.py +++ b/libbe/util/encoding.py @@ -45,18 +45,18 @@ def get_encoding(): Guess a useful input/output/filesystem encoding... Maybe we need seperate encodings for input/output and filesystem? Hmm... """ - if ENCODING != None: + if ENCODING is not None: return ENCODING encoding = locale.getpreferredencoding() or sys.getdefaultencoding() return encoding def get_input_encoding(): - if INPUT_ENCODING != None: + if INPUT_ENCODING is not None: return INPUT_ENCODING return sys.__stdin__.encoding or get_encoding() def get_output_encoding(): - if OUTPUT_ENCODING != None: + if OUTPUT_ENCODING is not None: return OUTPUT_ENCODING return sys.__stdout__.encoding or get_encoding() @@ -83,7 +83,7 @@ def known_encoding(encoding): def get_file_contents(path, mode='r', encoding=None, decode=False): if decode == True: - if encoding == None: + if encoding is None: encoding = get_text_file_encoding() f = codecs.open(path, mode, encoding) else: @@ -94,7 +94,7 @@ def get_file_contents(path, mode='r', encoding=None, decode=False): def set_file_contents(path, contents, mode='w', encoding=None): if type(contents) == str: - if encoding == None: + if encoding is None: encoding = get_text_file_encoding() f = codecs.open(path, mode, encoding) else: diff --git a/libbe/util/id.py b/libbe/util/id.py index 078dd3f..f4d19ae 100644 --- a/libbe/util/id.py +++ b/libbe/util/id.py @@ -156,7 +156,7 @@ class NoIDMatches (KeyError): self.possible_ids = possible_ids self.msg = msg def __str__(self): - if self.msg == None: + if self.msg is None: return 'No id matches %s.\n%s' % (self.id, self.possible_ids) return self.msg @@ -175,7 +175,7 @@ class InvalidIDStructure (KeyError): self.id = id self.msg = msg def __str__(self): - if self.msg == None: + if self.msg is None: return 'Invalid id structure "%s"' % self.id return self.msg @@ -188,7 +188,7 @@ def _assemble(args, check_length=False): """ args = list(args) for i,arg in enumerate(args): - if arg == None: + if arg is None: args[i] = '' id = '/'.join(args) if check_length == True: @@ -271,7 +271,7 @@ def _expand(truncated_id, common, other_ids): other_ids = list(other_ids) if len(other_ids) == 0: raise NoIDMatches(truncated_id, other_ids) - if truncated_id == None: + if truncated_id is None: if len(other_ids) == 1: return other_ids[0] raise MultipleIDMatches(truncated_id, common, other_ids) @@ -377,7 +377,7 @@ class ID (object): def user(self): ids = [] for o in self._ancestors(): - if o == None: + if o is None: ids.append(None) else: ids.append(_truncate(o.uuid, o.sibling_uuids())) @@ -480,7 +480,7 @@ class IDreplacer (object): def __call__(self, match): ids = [] for m in match.groups(): - if m == None: + if m is None: m = '' ids.append(m) try: @@ -577,7 +577,7 @@ def _parse_user(id): ret = {} args = _split(id, check_length=True) for i,(type,arg) in enumerate(zip(HIERARCHY, args)): - if arg != None and len(arg) == 0: + if arg is not None and len(arg) == 0: raise InvalidIDStructure( id, 'Invalid %s part %d "%s" of id "%s"' % (type, i, arg, id)) ret['type'] = type @@ -609,7 +609,7 @@ if libbe.TESTING == True: def __init__(self, uuid, parent=None, siblings=[]): self.uuid = uuid self._siblings = siblings - if parent == None: + if parent is None: type_i = 0 else: assert parent.type in HIERARCHY, parent diff --git a/libbe/util/subproc.py b/libbe/util/subproc.py index 5412b08..912433f 100644 --- a/libbe/util/subproc.py +++ b/libbe/util/subproc.py @@ -54,7 +54,7 @@ def invoke(args, stdin=None, stdout=PIPE, stderr=PIPE, expect=(0,), unicode_output == True, convert stdout and stdin strings to unicode before returing them. """ - if cwd == None: + if cwd is None: cwd = '.' if isinstance(shell, (str,)): list_args = ' '.split(args) # sloppy, but just for logging @@ -81,11 +81,11 @@ def invoke(args, stdin=None, stdout=PIPE, stderr=PIPE, expect=(0,), stdout,stderr = q.communicate(input=stdin) status = q.wait() if unicode_output == True: - if encoding == None: + if encoding is None: encoding = get_encoding() - if stdout != None: + if stdout is not None: stdout = str(stdout, encoding) - if stderr != None: + if stderr is not None: stderr = str(stderr, encoding) libbe.LOG.debug('{0}\n{1}{2}'.format(status, stdout, stderr)) else: diff --git a/libbe/util/wsgi.py b/libbe/util/wsgi.py index cfa43be..9f22596 100644 --- a/libbe/util/wsgi.py +++ b/libbe/util/wsgi.py @@ -46,7 +46,7 @@ try: import cherrypy.wsgiserver except ImportError: cherrypy = None -if cherrypy != None: +if cherrypy is not None: try: # CherryPy >= 3.2 import cherrypy.wsgiserver.ssl_builtin except ImportError: # CherryPy <= 3.1.X @@ -239,9 +239,9 @@ class WSGI_Object (object): req_uri += '?' + environ['QUERY_STRING'] start = time.localtime() if time.daylight: - offset = time.altzone / 60 / 60 * -100 + offset = time.altzone // 60 // 60 * -100 else: - offset = time.timezone / 60 / 60 * -100 + offset = time.timezone // 60 // 60 * -100 if offset >= 0: offset = '+{:04d}'.format(offset) elif offset < 0: @@ -800,7 +800,7 @@ if libbe.TESTING: ('Content-Type','text/plain'), ('X-Dummy-Header','Dummy Value')], self.caller.response_headers) - self.assertTrue(self.caller.exc_info == None, self.caller.exc_info) + self.assertTrue(self.caller.exc_info is None, self.caller.exc_info) def test_log_request(self): self.app.log_request( diff --git a/misc/xml/be-mail-to-xml b/misc/xml/be-mail-to-xml index 6155baa..6071c74 100755 --- a/misc/xml/be-mail-to-xml +++ b/misc/xml/be-mail-to-xml @@ -27,19 +27,21 @@ followed by a blank line. import base64 import email.utils -from libbe.util.encoding import get_output_encoding -from libbe.util.utility import time_to_str -import mailbox # the mailbox people really want an on-disk copy +import mailbox # the mailbox people really want an on-disk copy import optparse -from time import asctime, gmtime, mktime import types +from time import mktime from xml.sax.saxutils import escape -BREAK = u'--' # signature separator +from libbe.util.encoding import get_output_encoding +from libbe.util.utility import time_to_str + +BREAK = u'--' # signature separator DEFAULT_ENCODING = get_output_encoding() KNOWN_IDS = [] + def normalize_email_address(address): """ Standardize whitespace, etc. @@ -49,6 +51,7 @@ def normalize_email_address(address): return None return addr + def normalize_RFC_2822_date(date): """ Some email clients write non-RFC 2822-compliant date tags like: @@ -57,60 +60,59 @@ def normalize_RFC_2822_date(date): to deal with such inconsistencies. """ time_tuple = email.utils.parsedate(date) - assert time_tuple != None, \ + assert time_tuple is not None, \ 'unparsable date: "%s"' % date return time_to_str(mktime(time_tuple)) + def strip_footer(body): body_lines = body.splitlines() - for i,line in enumerate(body_lines): + for i, line in enumerate(body_lines): if line.startswith(BREAK): break - i += 1 # increment past the current valid line. + i += 1 # increment past the current valid line. return u'\n'.join(body_lines[:i]).strip() + def comment_message_to_xml(message, fields=None): - if fields == None: + if fields is 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'author'] = normalize_email_address(message[u'from']) - new_fields[u'date'] = message[u'date'] - if new_fields[u'date'] != None: + new_fields = {u'alt-id': message[u'message-id'], u'in-reply-to': message[u'in-reply-to'], + u'author': normalize_email_address(message[u'from']), u'date': message[u'date']} + if new_fields[u'date'] is not None: new_fields[u'date'] = normalize_RFC_2822_date(new_fields[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: + for k, v in new_fields.items(): + if v is not None and type(v) != types.UnicodeType: + fields[k] = str(v, encoding=DEFAULT_ENCODING) + elif v is None and k in fields: new_fields[k] = fields[k] - for k,v in fields.items(): + for k, v in fields.items(): if k not in new_fields: new_fields.k = fields[k] fields = new_fields - if fields[u'in-reply-to'] == None: - if message[u'references'] != None: + if fields[u'in-reply-to'] is None: + if message[u'references'] is not None: refs = message[u'references'].split() - for ref in refs: # search for a known reference id. + for ref in refs: # search for a known reference id. if ref in KNOWN_IDS: fields[u'in-reply-to'] = ref break - if fields[u'in-reply-to'] == None and len(refs) > 0: - fields[u'in-reply-to'] = refs[0] # default to the first - else: # check for mutliple in-reply-to references. + if fields[u'in-reply-to'] is None and len(refs) > 0: + fields[u'in-reply-to'] = refs[0] # default to the first + else: # check for mutliple in-reply-to references. refs = fields[u'in-reply-to'].split() found_ref = False - for ref in refs: # search for a known reference id. + for ref in refs: # search for a known reference id. if ref in KNOWN_IDS: fields[u'in-reply-to'] = ref found_ref = True break if found_ref == False and len(refs) > 0: - fields[u'in-reply-to'] = refs[0] # default to the first + fields[u'in-reply-to'] = refs[0] # default to the first - if fields[u'alt-id'] != None: + if fields[u'alt-id'] is not None: KNOWN_IDS.append(fields[u'alt-id']) if message.is_multipart(): @@ -123,51 +125,56 @@ def comment_message_to_xml(message, fields=None): continue fields[u'author'] = from_str fields[u'date'] = date - if len(ret) > 0: # we've added one part already - fields.pop(u'alt-id') # don't pass alt-id to other parts - fields[u'in-reply-to'] = alt_id # others respond to first + if len(ret) > 0: # we've added one part already + fields.pop(u'alt-id') # don't pass alt-id to other parts + fields[u'in-reply-to'] = alt_id # others respond to first 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(), \ + # assert charset == DEFAULT_ENCODING.lower(), \ # u"Unknown charset: %s" % charset - if message[u'content-transfer-encoding'] == None: + if message[u'content-transfer-encoding'] is None: encoding = DEFAULT_ENCODING else: encoding = message[u'content-transfer-encoding'].lower() - body = message.get_payload(decode=True) # attempt to decode - assert body != None, "Unable to decode?" + body = message.get_payload(decode=True) # attempt to decode + assert body is not None, "Unable to decode?" if fields[u'content-type'].startswith(u"text/"): - body = strip_footer(unicode(body, encoding=charset)) + body = strip_footer(str(body, encoding=charset)) else: - body = base64.encode(body) + body = base64.encodebytes(body) fields[u'body'] = body lines = [u"<comment>"] - for tag,body in fields.items(): - if body != None: + for tag, body in fields.items(): + if body is not None: ebody = escape(body) lines.append(u" <%s>%s</%s>" % (tag, ebody, tag)) lines.append(u"</comment>") return u'\n'.join(lines) + def main(argv): parser = optparse.OptionParser(usage='%prog [options] mailbox') formats = ['mbox', 'Maildir', 'MH', 'Babyl', 'MMDF'] parser.add_option('-f', '--format', type='choice', dest='format', help="Select the mailbox format from %s. See the mailbox module's documention for descriptions of these formats." \ - % ', '.join(formats), + % ', '.join(formats), default='mbox', choices=formats) - options,args = parser.parse_args(argv) + options, args = parser.parse_args(argv) mailbox_file = args[1] reader = getattr(mailbox, options.format) mb = reader(mailbox_file, factory=None) - print u'<?xml version="1.0" encoding="%s" ?>' % DEFAULT_ENCODING - print u"<be-xml>" + print + u'<?xml version="1.0" encoding="%s" ?>' % DEFAULT_ENCODING + print + u"<be-xml>" for message in mb: - print comment_message_to_xml(message) - print u"</be-xml>" + print + comment_message_to_xml(message) + print + u"</be-xml>" if __name__ == "__main__": diff --git a/misc/xml/be-xml-to-mbox b/misc/xml/be-xml-to-mbox index 0151792..48454f9 100755 --- a/misc/xml/be-xml-to-mbox +++ b/misc/xml/be-xml-to-mbox @@ -27,23 +27,20 @@ Messages begin with a a From_ line, followed by RFC 822 email, followed by a blank line. """ -#from mailbox import mbox, Message # the mailbox people really want an on-disk copy +# from mailbox import mbox, Message # the mailbox people really want an on-disk copy import email.utils from libbe.util.encoding import get_output_encoding from libbe.util.utility import str_to_time as rfc2822_to_gmtime_integer from time import asctime, gmtime 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.etree import ElementTree from xml.sax.saxutils import unescape - DEFAULT_DOMAIN = "invalid.com" DEFAULT_EMAIL = "dummy@" + DEFAULT_DOMAIN DEFAULT_ENCODING = get_output_encoding() + def rfc2822_to_asctime(rfc2822_string): """Convert an RFC 2822-fomatted string into a asctime string. >>> rfc2822_to_asctime("Thu, 01 Jan 1970 00:00:00 +0000") @@ -53,27 +50,32 @@ def rfc2822_to_asctime(rfc2822_string): return asctime(gmtime(0)) return asctime(gmtime(rfc2822_to_gmtime_integer(rfc2822_string))) -class LimitedAttrDict (dict): + +class LimitedAttrDict(dict): """ Dict with error checking, to avoid invalid bug/comment fields. """ - _attrs = [] # override with list of valid attribute names + _attrs = [] # override with list of valid attribute names + def __init__(self, **kwargs): dict.__init__(self) - for key,value in kwargs.items(): + for key, value in kwargs.items(): self[key] = value + def __setitem__(self, key, item): self._validate_key(key) dict.__setitem__(self, key, item) + def _validate_key(self, key): if key in self._attrs: return elif type(key) not in types.StringTypes: - raise TypeError, "Invalid attribute type %s for '%s'" % (type(key), key) + raise TypeError("Invalid attribute type %s for '%s'" % (type(key), key)) else: - raise ValueError, "Invalid attribute name '%s'" % key + raise ValueError("Invalid attribute name '%s'" % key) + -class Bug (LimitedAttrDict): +class Bug(LimitedAttrDict): _attrs = [u"uuid", u"short-name", u"severity", @@ -85,32 +87,34 @@ class Bug (LimitedAttrDict): u"summary", u"comments", u"extra-strings"] + def print_to_mbox(self): if "creator" in self: # otherwise, probably a `be show` uuid-only bug to avoid # root comments. - name,addr = email.utils.parseaddr(self["creator"]) - print "From %s %s" % (addr, rfc2822_to_asctime(self["created"])) - print "Message-id: <%s@%s>" % (self["uuid"], DEFAULT_DOMAIN) - print "Date: %s" % self["created"] - print "From: %s" % self["creator"] - print "Content-Type: %s; charset=%s" \ - % ("text/plain", DEFAULT_ENCODING) - print "Content-Transfer-Encoding: 8bit" - print "Subject: %s: %s" % (self["short-name"], self["summary"]) + name, addr = email.utils.parseaddr(self["creator"]) + print("From %s %s" % (addr, rfc2822_to_asctime(self["created"]))) + print("Message-id: <%s@%s>" % (self["uuid"], DEFAULT_DOMAIN)) + print("Date: %s" % self["created"]) + print("From: %s" % self["creator"]) + print("Content-Type: %s; charset=%s" + % ("text/plain", DEFAULT_ENCODING)) + print("Content-Transfer-Encoding: 8bit") + print("Subject: %s: %s" % (self["short-name"], self["summary"])) if "extra-strings" in self: for estr in self["extra-strings"]: - print "X-Extra-String: %s" % estr - print "" - print self["summary"] - print "" + print("X-Extra-String: %s" % estr) + print() + print(self["summary"]) + print() if "comments" in self: for comment in self["comments"]: - comment.print_to_mbox(self) + 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()) + text = unescape(bytes(field.text).decode("unicode_escape").strip()) if field.tag == "comment": comm = Comment() comm.init_from_etree(field) @@ -126,12 +130,14 @@ class Bug (LimitedAttrDict): else: self[field.tag] = text + def wrap_id(id): if "@" not in id: return "<%s@%s>" % (id, DEFAULT_DOMAIN) return id -class Comment (LimitedAttrDict): + +class Comment(LimitedAttrDict): _attrs = [u"uuid", u"alt-id", u"short-name", @@ -141,49 +147,54 @@ class Comment (LimitedAttrDict): u"content-type", u"body", u"extra-strings"] + def print_to_mbox(self, bug=None): - if bug == None: + if bug is None: bug = Bug() bug[u"uuid"] = u"no-uuid" - name,addr = email.utils.parseaddr(self["author"]) - print "From %s %s" % (addr, rfc2822_to_asctime(self["date"])) - 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" % wrap_id(id) + name, addr = email.utils.parseaddr(self["author"]) + print("From %s %s" % (addr, rfc2822_to_asctime(self["date"]))) + if "uuid" in self: + id = self["uuid"] + elif "alt-id" in self: + id = self["alt-id"] + else: + id = None + if id is not None: + print("Message-id: %s" % wrap_id(id)) if "alt-id" in self: - print "Alt-id: %s" % wrap_id(self["alt-id"]) - print "Date: %s" % self["date"] - print "From: %s" % self["author"] + print("Alt-id: %s" % wrap_id(self["alt-id"])) + print("Date: %s" % self["date"]) + print("From: %s" % self["author"]) subject = "" if "short-name" in self: - subject += self["short-name"]+u": " + subject += self["short-name"] + u": " if "summary" in bug: subject += bug["summary"] else: subject += u"no-subject" - print "Subject: %s" % subject + print("Subject: %s" % subject) if "in-reply-to" not in self.keys(): self["in-reply-to"] = bug["uuid"] - print "In-Reply-To: %s" % wrap_id(self["in-reply-to"]) + print("In-Reply-To: %s" % wrap_id(self["in-reply-to"])) if "extra-strings" in self: for estr in self["extra-strings"]: - print "X-Extra-String: %s" % estr + print("X-Extra-String: %s" % estr) if self["content-type"].startswith("text/"): - print "Content-Transfer-Encoding: 8bit" - print "Content-Type: %s; charset=%s" \ - % (self["content-type"], DEFAULT_ENCODING) + print("Content-Transfer-Encoding: 8bit") + print("Content-Type: %s; charset=%s" + % (self["content-type"], DEFAULT_ENCODING)) else: - print "Content-Transfer-Encoding: base64" - print "Content-Type: %s;" % (self["content-type"]) - print "" - print self["body"] - print "" + print("Content-Transfer-Encoding: base64") + print("Content-Type: %s;" % (self["content-type"])) + print() + 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()) + text = unescape(bytes(field.text).decode("unicode_escape").strip()) if field.tag == "extra-string": if "extra-strings" in self: self["extra-strings"].append(text) @@ -191,9 +202,10 @@ class Comment (LimitedAttrDict): self["extra-strings"] = [text] else: if field.tag == "body": - text+="\n" + text += "\n" self[field.tag] = text + def print_to_mbox(element): if element.tag == "bug": b = Bug() @@ -207,14 +219,15 @@ def print_to_mbox(element): for elt in element.getchildren(): print_to_mbox(elt) + if __name__ == "__main__": import codecs import sys - + sys.stdin = codecs.getreader(DEFAULT_ENCODING)(sys.stdin) sys.stdout = codecs.getwriter(DEFAULT_ENCODING)(sys.stdout) - if len(sys.argv) == 1: # no filename given, use 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() @@ -52,7 +52,7 @@ def python_tree(root_path='libbe', root_modname='libbe'): stack.append(c) else: continue - if f.parent == None: + if f.parent is None: f.modname = root_modname else: f.modname = f.parent.modname + '.' + f.name diff --git a/update-copyright b/update-copyright -Subproject cd7751d74780f3c73d843b46bff1182eae36411 +Subproject e7724a0017849f130444e7dfc396c875809e306 |