diff options
34 files changed, 525 insertions, 300 deletions
diff --git a/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/028d2e8d-5b0f-4c43-a913-35a1709b2276/body b/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/028d2e8d-5b0f-4c43-a913-35a1709b2276/body new file mode 100644 index 0000000..02bbe3a --- /dev/null +++ b/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/028d2e8d-5b0f-4c43-a913-35a1709b2276/body @@ -0,0 +1,5 @@ +Wrote/borrowed libbe/encoding.py. +Now the following works: + +python -c 'import libbe.encoding as e; print e.get_encoding(); e.set_IO_stream_encodings(e.get_encoding()) ;print u"\u2019"' | cat + diff --git a/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/028d2e8d-5b0f-4c43-a913-35a1709b2276/values b/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/028d2e8d-5b0f-4c43-a913-35a1709b2276/values new file mode 100644 index 0000000..eb56317 --- /dev/null +++ b/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/028d2e8d-5b0f-4c43-a913-35a1709b2276/values @@ -0,0 +1,21 @@ + + + +Content-type=text/plain + + + + + + +Date=Tue, 25 Nov 2008 19:41:02 +0000 + + + + + + +From=W. Trevor King <wking@drexel.edu> + + + diff --git a/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/values b/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/values index 40ac06d..e710d29 100644 --- a/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/values +++ b/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/values @@ -15,7 +15,7 @@ severity=minor -status=open +status=fixed @@ -33,9 +33,12 @@ else: except cmdutil.GetHelp: print cmdutil.help(sys.argv[1]) sys.exit(0) - except cmdutil.UsageError: + except cmdutil.UsageError, e: + print "Invalid usage:", e + print "\nArgs:", sys.argv[1:] print cmdutil.help(sys.argv[1]) sys.exit(1) except cmdutil.UserError, e: + print "ERROR:" print e sys.exit(1) diff --git a/becommands/assign.py b/becommands/assign.py index cb732b3..b00c5c2 100644 --- a/becommands/assign.py +++ b/becommands/assign.py @@ -18,7 +18,7 @@ from libbe import cmdutil, bugdir __desc__ = __doc__ -def execute(args): +def execute(args, test=False): """ >>> import os >>> bd = bugdir.simple_bug_dir() @@ -26,17 +26,17 @@ def execute(args): >>> bd.bug_from_shortname("a").assigned is None True - >>> execute(["a"]) + >>> execute(["a"], test=True) >>> bd._clear_bugs() >>> bd.bug_from_shortname("a").assigned == bd.user_id True - >>> execute(["a", "someone"]) + >>> execute(["a", "someone"], test=True) >>> bd._clear_bugs() >>> print bd.bug_from_shortname("a").assigned someone - >>> execute(["a","none"]) + >>> execute(["a","none"], test=True) >>> bd._clear_bugs() >>> bd.bug_from_shortname("a").assigned is None True @@ -44,11 +44,11 @@ def execute(args): options, args = get_parser().parse_args(args) assert(len(args) in (0, 1, 2)) if len(args) == 0: - raise cmdutil.UserError("Please specify a bug id.") + raise cmdutil.UsageError("Please specify a bug id.") if len(args) > 2: help() - raise cmdutil.UserError("Too many arguments.") - bd = bugdir.BugDir(from_disk=True) + raise cmdutil.UsageError("Too many arguments.") + bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) bug = bd.bug_from_shortname(args[0]) if len(args) == 1: bug.assigned = bd.user_id diff --git a/becommands/close.py b/becommands/close.py index 8d2ccdb..d125397 100644 --- a/becommands/close.py +++ b/becommands/close.py @@ -18,7 +18,7 @@ from libbe import cmdutil, bugdir __desc__ = __doc__ -def execute(args): +def execute(args, test=False): """ >>> from libbe import bugdir >>> import os @@ -26,18 +26,17 @@ def execute(args): >>> os.chdir(bd.root) >>> print bd.bug_from_shortname("a").status open - >>> execute(["a"]) + >>> execute(["a"], test=True) >>> bd._clear_bugs() >>> print bd.bug_from_shortname("a").status closed """ options, args = get_parser().parse_args(args) if len(args) == 0: - raise cmdutil.UserError("Please specify a bug id.") + raise cmdutil.UsageError("Please specify a bug id.") if len(args) > 1: - help() - raise cmdutil.UserError("Too many arguments.") - bd = bugdir.BugDir(from_disk=True) + raise cmdutil.UsageError("Too many arguments.") + bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) bug = bd.bug_from_shortname(args[0]) bug.status = "closed" bd.save() diff --git a/becommands/comment.py b/becommands/comment.py index 172f818..c9c6b41 100644 --- a/becommands/comment.py +++ b/becommands/comment.py @@ -15,16 +15,16 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """Add a comment to a bug""" -from libbe import cmdutil, bugdir, utility +from libbe import cmdutil, bugdir, editor import os __desc__ = __doc__ -def execute(args): +def execute(args, test=False): """ >>> import time >>> bd = bugdir.simple_bug_dir() >>> os.chdir(bd.root) - >>> execute(["a", "This is a comment about a"]) + >>> execute(["a", "This is a comment about a"], test=True) >>> bd._clear_bugs() >>> bug = bd.bug_from_shortname("a") >>> bug.load_comments() @@ -41,12 +41,12 @@ def execute(args): >>> if 'EDITOR' in os.environ: ... del os.environ["EDITOR"] - >>> execute(["b"]) + >>> execute(["b"], test=True) Traceback (most recent call last): UserError: No comment supplied, and EDITOR not specified. >>> os.environ["EDITOR"] = "echo 'I like cheese' > " - >>> execute(["b"]) + >>> execute(["b"], test=True) >>> bd._clear_bugs() >>> bug = bd.bug_from_shortname("b") >>> bug.load_comments() @@ -57,10 +57,9 @@ def execute(args): """ options, args = get_parser().parse_args(args) if len(args) == 0: - raise cmdutil.UserError("Please specify a bug or comment id.") + raise cmdutil.UsageError("Please specify a bug or comment id.") if len(args) > 2: - help() - raise cmdutil.UserError("Too many arguments.") + raise cmdutil.UsageError("Too many arguments.") shortname = args[0] if shortname.count(':') > 1: @@ -73,20 +72,20 @@ def execute(args): bugname = shortname is_reply = False - bd = bugdir.BugDir(from_disk=True) + bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) bug = bd.bug_from_shortname(bugname) bug.load_comments() if is_reply: - parent = bug.comment_root.comment_from_shortname(shortname, bug_shortname=bugname) + parent = bug.comment_root.comment_from_shortname(shortname, + bug_shortname=bugname) else: parent = bug.comment_root if len(args) == 1: try: - body = utility.editor_string("Please enter your comment above") - except utility.CantFindEditor: - raise cmdutil.UserError( - "No comment supplied, and EDITOR not specified.") + body = editor.editor_string("Please enter your comment above") + except editor.CantFindEditor, e: + raise cmdutil.UserError, "No comment supplied, and EDITOR not specified." if body is None: raise cmdutil.UserError("No comment entered.") body = body.decode('utf-8') diff --git a/becommands/diff.py b/becommands/diff.py index 862afc5..8714d77 100644 --- a/becommands/diff.py +++ b/becommands/diff.py @@ -20,7 +20,7 @@ from libbe import cmdutil, bugdir, diff import os __desc__ = __doc__ -def execute(args): +def execute(args, test=False): """ >>> import os >>> bd = bugdir.simple_bug_dir() @@ -31,7 +31,7 @@ def execute(args): >>> changed = bd.rcs.commit("Closed bug a") >>> os.chdir(bd.root) >>> if bd.rcs.versioned == True: - ... execute([original]) + ... execute([original], test=True) ... else: ... print "a:cm: Bug A\\nstatus: open -> closed\\n" Modified bug reports: @@ -45,16 +45,14 @@ def execute(args): if len(args) == 1: revision = args[0] if len(args) > 1: - help() - raise cmdutil.UserError("Too many arguments.") - bd = bugdir.BugDir(from_disk=True) + raise cmdutil.UsageError("Too many arguments.") + bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) if bd.rcs.versioned == False: print "This directory is not revision-controlled." else: old_bd = bd.duplicate_bugdir(revision) r,m,a = diff.diff(old_bd, bd) - diff.diff_report((r,m,a), bd) - # TODO, string return from diff report + print diff.diff_report((r,m,a), bd).encode(bd.encoding) bd.remove_duplicate_bugdir() def get_parser(): diff --git a/becommands/help.py b/becommands/help.py index bf0b4fc..1c99af5 100644 --- a/becommands/help.py +++ b/becommands/help.py @@ -21,20 +21,25 @@ __desc__ = __doc__ def execute(args): """ Print help of specified command. + >>> execute(["help"]) + Usage: be help [COMMAND] + <BLANKLINE> + Options: + -h, --help Print a help message + <BLANKLINE> + Print help for specified command or list of all commands. + <BLANKLINE> """ options, args = get_parser().parse_args(args) if len(args) > 1: - raise cmdutil.UserError("Too many arguments.") + raise cmdutil.UsageError("Too many arguments.") if len(args) == 0: print cmdutil.help() else: try: print cmdutil.help(args[0]) except AttributeError: - print "No help available" - - return - + print "No help available" def get_parser(): parser = cmdutil.CmdOptionParser("be help [COMMAND]") diff --git a/becommands/list.py b/becommands/list.py index 63e1cd6..c63039d 100644 --- a/becommands/list.py +++ b/becommands/list.py @@ -21,22 +21,21 @@ from libbe.bug import cmp_full, severity_values, status_values, \ import os __desc__ = __doc__ -def execute(args): +def execute(args, test=False): """ >>> import os >>> bd = bugdir.simple_bug_dir() >>> os.chdir(bd.root) - >>> execute([]) + >>> execute([], test=True) a:om: Bug A - >>> execute(["--status", "all"]) + >>> execute(["--status", "all"], test=True) a:om: Bug A b:cm: Bug B """ options, args = get_parser().parse_args(args) if len(args) > 0: - help() - raise cmdutil.UserError("Too many arguments.") - bd = bugdir.BugDir(from_disk=True) + raise cmdutil.UsageError("Too many arguments.") + bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) bd.load_all_bugs() # select status if options.status != None: diff --git a/becommands/merge.py b/becommands/merge.py index b079f2c..d1829d6 100644 --- a/becommands/merge.py +++ b/becommands/merge.py @@ -19,7 +19,7 @@ from libbe import cmdutil, bugdir import os, copy __desc__ = __doc__ -def execute(args): +def execute(args, test=False): """ >>> from libbe import utility >>> bd = bugdir.simple_bug_dir() @@ -38,7 +38,7 @@ def execute(args): >>> dummy.time = 2 >>> bd.save() >>> os.chdir(bd.root) - >>> execute(["a", "b"]) + >>> execute(["a", "b"], test=True) Merging bugs a and b >>> bd._clear_bugs() >>> a = bd.bug_from_shortname("a") @@ -61,30 +61,30 @@ def execute(args): Date: Thu, 01 Jan 1970 00:00:01 +0000 <BLANKLINE> Testing - --------- Comment --------- - Name: a:2 - From: wking <wking@thor.yang.physics.drexel.edu> - Date: Thu, 01 Jan 1970 00:00:02 +0000 + --------- Comment --------- + Name: a:2 + From: wking <wking@thor.yang.physics.drexel.edu> + Date: Thu, 01 Jan 1970 00:00:02 +0000 <BLANKLINE> - Testing... + Testing... --------- Comment --------- Name: a:3 From: wking <wking@thor.yang.physics.drexel.edu> Date: Thu, 01 Jan 1970 00:00:03 +0000 <BLANKLINE> Merged from bug b - --------- Comment --------- - Name: a:4 - From: wking <wking@thor.yang.physics.drexel.edu> - Date: Thu, 01 Jan 1970 00:00:01 +0000 + --------- Comment --------- + Name: a:4 + From: wking <wking@thor.yang.physics.drexel.edu> + Date: Thu, 01 Jan 1970 00:00:01 +0000 <BLANKLINE> - 1 2 - --------- Comment --------- - Name: a:5 - From: wking <wking@thor.yang.physics.drexel.edu> - Date: Thu, 01 Jan 1970 00:00:02 +0000 + 1 2 + --------- Comment --------- + Name: a:5 + From: wking <wking@thor.yang.physics.drexel.edu> + Date: Thu, 01 Jan 1970 00:00:02 +0000 <BLANKLINE> - 1 2 3 4 + 1 2 3 4 >>> b = bd.bug_from_shortname("b") >>> b.load_comments() >>> mergeB = b.comment_from_shortname(":3") @@ -105,12 +105,12 @@ def execute(args): Date: Thu, 01 Jan 1970 00:00:01 +0000 <BLANKLINE> 1 2 - --------- Comment --------- - Name: b:2 - From: wking <wking@thor.yang.physics.drexel.edu> - Date: Thu, 01 Jan 1970 00:00:02 +0000 + --------- Comment --------- + Name: b:2 + From: wking <wking@thor.yang.physics.drexel.edu> + Date: Thu, 01 Jan 1970 00:00:02 +0000 <BLANKLINE> - 1 2 3 4 + 1 2 3 4 --------- Comment --------- Name: b:3 From: wking <wking@thor.yang.physics.drexel.edu> @@ -122,12 +122,12 @@ def execute(args): """ options, args = get_parser().parse_args(args) if len(args) < 2: - raise cmdutil.UserError("Please two bug ids.") + raise cmdutil.UsageError("Please specify two bug ids.") if len(args) > 2: help() - raise cmdutil.UserError("Too many arguments.") + raise cmdutil.UsageError("Too many arguments.") - bd = bugdir.BugDir(from_disk=True) + bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) bugA = bd.bug_from_shortname(args[0]) bugA.load_comments() bugB = bd.bug_from_shortname(args[1]) diff --git a/becommands/new.py b/becommands/new.py index caa1549..bc17f83 100644 --- a/becommands/new.py +++ b/becommands/new.py @@ -18,14 +18,14 @@ from libbe import cmdutil, bugdir __desc__ = __doc__ -def execute(args): +def execute(args, test=False): """ >>> import os, time >>> from libbe import bug >>> bd = bugdir.simple_bug_dir() >>> os.chdir(bd.root) >>> bug.uuid_gen = lambda: "X" - >>> execute (["this is a test",]) + >>> execute (["this is a test",], test=True) Created bug with ID X >>> bd.load() >>> bug = bd.bug_from_uuid("X") @@ -40,8 +40,8 @@ def execute(args): """ options, args = get_parser().parse_args(args) if len(args) != 1: - raise cmdutil.UserError("Please supply a summary message") - bd = bugdir.BugDir(from_disk=True) + raise cmdutil.UsageError("Please supply a summary message") + bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) bug = bd.new_bug(summary=args[0]) bd.save() print "Created bug with ID %s" % bd.bug_shortname(bug) diff --git a/becommands/open.py b/becommands/open.py index 788a183..9a9667d 100644 --- a/becommands/open.py +++ b/becommands/open.py @@ -18,25 +18,24 @@ from libbe import cmdutil, bugdir __desc__ = __doc__ -def execute(args): +def execute(args, test=False): """ >>> import os >>> bd = bugdir.simple_bug_dir() >>> os.chdir(bd.root) >>> print bd.bug_from_shortname("b").status closed - >>> execute(["b"]) + >>> execute(["b"], test=True) >>> bd._clear_bugs() >>> print bd.bug_from_shortname("b").status open """ options, args = get_parser().parse_args(args) if len(args) == 0: - raise cmdutil.UserError("Please specify a bug id.") + raise cmdutil.UsageError, "Please specify a bug id." if len(args) > 1: - help() - raise cmdutil.UserError("Too many arguments.") - bd = bugdir.BugDir(from_disk=True) + raise cmdutil.UsageError, "Too many arguments." + bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) bug = bd.bug_from_shortname(args[0]) bug.status = "open" bd.save() diff --git a/becommands/remove.py b/becommands/remove.py index 8f7c2c6..386d9d4 100644 --- a/becommands/remove.py +++ b/becommands/remove.py @@ -18,7 +18,7 @@ from libbe import cmdutil, bugdir __desc__ = __doc__ -def execute(args): +def execute(args, test=False): """ >>> from libbe import mapfile >>> import os @@ -26,7 +26,7 @@ def execute(args): >>> os.chdir(bd.root) >>> print bd.bug_from_shortname("b").status closed - >>> execute (["b"]) + >>> execute (["b"], test=True) Removed bug b >>> bd._clear_bugs() >>> try: @@ -37,8 +37,8 @@ def execute(args): """ options, args = get_parser().parse_args(args) if len(args) != 1: - raise cmdutil.UserError("Please specify a bug id.") - bd = bugdir.BugDir(from_disk=True) + raise cmdutil.UsageError, "Please specify a bug id." + bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) bug = bd.bug_from_shortname(args[0]) bd.remove_bug(bug) bd.save() diff --git a/becommands/set.py b/becommands/set.py index 24011f0..3904262 100644 --- a/becommands/set.py +++ b/becommands/set.py @@ -18,25 +18,24 @@ from libbe import cmdutil, bugdir __desc__ = __doc__ -def execute(args): +def execute(args, test=False): """ >>> import os >>> bd = bugdir.simple_bug_dir() >>> os.chdir(bd.root) - >>> execute(["target"]) + >>> execute(["target"], test=True) None - >>> execute(["target", "tomorrow"]) - >>> execute(["target"]) + >>> execute(["target", "tomorrow"], test=True) + >>> execute(["target"], test=True) tomorrow - >>> execute(["target", "none"]) - >>> execute(["target"]) + >>> execute(["target", "none"], test=True) + >>> execute(["target"], test=True) None """ options, args = get_parser().parse_args(args) if len(args) > 2: - help() - raise cmdutil.UserError("Too many arguments.") - bd = bugdir.BugDir(from_disk=True) + raise cmdutil.UsageError, "Too many arguments" + bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) if len(args) == 0: keys = bd.settings.keys() keys.sort() diff --git a/becommands/set_root.py b/becommands/set_root.py index e17bd87..11f38b9 100644 --- a/becommands/set_root.py +++ b/becommands/set_root.py @@ -19,7 +19,7 @@ import os.path from libbe import cmdutil, bugdir __desc__ = __doc__ -def execute(args): +def execute(args, test=False): """ >>> from libbe import utility, rcs >>> import os @@ -29,7 +29,7 @@ def execute(args): ... except bugdir.NoBugDir, e: ... True True - >>> execute([dir.path]) + >>> execute([dir.path], test=True) No revision control detected. Directory initialized. >>> del(dir) @@ -40,34 +40,31 @@ def execute(args): >>> rcs.init('.') >>> print rcs.name Arch - >>> execute([]) + >>> execute([], test=True) Using Arch for revision control. Directory initialized. >>> rcs.cleanup() >>> try: - ... execute(['.']) + ... execute(['.'], test=True) ... except cmdutil.UserError, e: ... str(e).startswith("Directory already initialized: ") True - >>> execute(['/highly-unlikely-to-exist']) + >>> execute(['/highly-unlikely-to-exist'], test=True) Traceback (most recent call last): UserError: No such directory: /highly-unlikely-to-exist >>> os.chdir('/') """ options, args = get_parser().parse_args(args) if len(args) > 1: - print help() - raise cmdutil.UserError, "Too many arguments" + raise cmdutil.UsageError if len(args) == 1: basedir = args[0] else: basedir = "." - if os.path.exists(basedir) == False: - pass - #raise cmdutil.UserError, "No such directory: %s" % basedir try: - bd = bugdir.BugDir(basedir, from_disk=False, sink_to_existing_root=False, assert_new_BugDir=True) + bd = bugdir.BugDir(basedir, from_disk=False, sink_to_existing_root=False, assert_new_BugDir=True, + manipulate_encodings=not test) except bugdir.NoRootEntry: raise cmdutil.UserError("No such directory: %s" % basedir) except bugdir.AlreadyInitialized: diff --git a/becommands/severity.py b/becommands/severity.py index 3adefaa..3c856de 100644 --- a/becommands/severity.py +++ b/becommands/severity.py @@ -19,25 +19,24 @@ from libbe import cmdutil, bugdir from libbe.bug import severity_values, severity_description __desc__ = __doc__ -def execute(args): +def execute(args, test=False): """ >>> import os >>> bd = bugdir.simple_bug_dir() >>> os.chdir(bd.root) - >>> execute(["a"]) + >>> execute(["a"], test=True) minor - >>> execute(["a", "wishlist"]) - >>> execute(["a"]) + >>> execute(["a", "wishlist"], test=True) + >>> execute(["a"], test=True) wishlist - >>> execute(["a", "none"]) + >>> execute(["a", "none"], test=True) Traceback (most recent call last): UserError: Invalid severity level: none """ options, args = get_parser().parse_args(args) if len(args) not in (1,2): - print help() - return - bd = bugdir.BugDir(from_disk=True) + raise cmdutil.UsageError + bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) bug = bd.bug_from_shortname(args[0]) if len(args) == 1: print bug.severity @@ -46,7 +45,7 @@ def execute(args): bug.severity = args[1] except ValueError, e: if e.name != "severity": - raise + raise e raise cmdutil.UserError ("Invalid severity level: %s" % e.value) bd.save() diff --git a/becommands/show.py b/becommands/show.py index 45cd6ad..3bd7e63 100644 --- a/becommands/show.py +++ b/becommands/show.py @@ -18,12 +18,12 @@ from libbe import cmdutil, bugdir __desc__ = __doc__ -def execute(args): +def execute(args, test=False): """ >>> import os >>> bd = bugdir.simple_bug_dir() >>> os.chdir(bd.root) - >>> execute (["a",]) + >>> execute (["a",], test=True) ID : a Short name : a Severity : minor @@ -37,11 +37,11 @@ def execute(args): """ options, args = get_parser().parse_args(args) if len(args) == 0: - raise cmdutil.UserError("Please specify a bug id.") - bd = bugdir.BugDir(from_disk=True) + raise cmdutil.UsageError + bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) for bugid in args: bug = bd.bug_from_shortname(bugid) - print bug.string(show_comments=True).encode('utf-8') + print bug.string(show_comments=True) def get_parser(): parser = cmdutil.CmdOptionParser("be show BUG-ID [BUG-ID ...]") diff --git a/becommands/status.py b/becommands/status.py index a30b3d6..73d43f8 100644 --- a/becommands/status.py +++ b/becommands/status.py @@ -19,29 +19,28 @@ from libbe import cmdutil, bugdir from libbe.bug import status_values, status_description __desc__ = __doc__ -def execute(args): +def execute(args, test=False): """ >>> import os >>> bd = bugdir.simple_bug_dir() >>> os.chdir(bd.root) - >>> execute(["a"]) + >>> execute(["a"], test=True) open - >>> execute(["a", "closed"]) - >>> execute(["a"]) + >>> execute(["a", "closed"], test=True) + >>> execute(["a"], test=True) closed - >>> execute(["a", "none"]) + >>> execute(["a", "none"], test=True) Traceback (most recent call last): UserError: Invalid status: none """ options, args = get_parser().parse_args(args) if len(args) not in (1,2): - print help() - return - bd = bugdir.BugDir(from_disk=True) + raise cmdutil.UsageError + bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) bug = bd.bug_from_shortname(args[0]) if len(args) == 1: print bug.status - elif len(args) == 2: + else: try: bug.status = args[1] except ValueError, e: diff --git a/becommands/target.py b/becommands/target.py index dce100f..4371ef0 100644 --- a/becommands/target.py +++ b/becommands/target.py @@ -18,33 +18,32 @@ from libbe import cmdutil, bugdir __desc__ = __doc__ -def execute(args): +def execute(args, test=False): """ >>> import os >>> bd = bugdir.simple_bug_dir() >>> os.chdir(bd.root) - >>> execute(["a"]) + >>> execute(["a"], test=True) No target assigned. - >>> execute(["a", "tomorrow"]) - >>> execute(["a"]) + >>> execute(["a", "tomorrow"], test=True) + >>> execute(["a"], test=True) tomorrow - >>> execute(["a", "none"]) - >>> execute(["a"]) + >>> execute(["a", "none"], test=True) + >>> execute(["a"], test=True) No target assigned. """ options, args = get_parser().parse_args(args) - assert(len(args) in (0, 1, 2)) - if len(args) == 0: - print help() - return - bd = bugdir.BugDir(from_disk=True) + if len(args) not in (1, 2): + raise cmdutil.UsageError + bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) bug = bd.bug_from_shortname(args[0]) if len(args) == 1: if bug.target is None: print "No target assigned." else: print bug.target - elif len(args) == 2: + else: + assert len(args) == 2 if args[1] == "none": bug.target = None else: diff --git a/libbe/arch.py b/libbe/arch.py index fd953a4..1173535 100644 --- a/libbe/arch.py +++ b/libbe/arch.py @@ -14,10 +14,11 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +import codecs import os +import re import shutil import time -import re import unittest import doctest @@ -133,13 +134,16 @@ class Arch(RCS): """ tagpath = os.path.join(path, "{arch}", "=tagging-method") lines_out = [] - for line in file(tagpath, "rb"): - line.decode("utf-8") + f = codecs.open(tagpath, "r", self.encoding) + for line in f: if line.startswith("source "): lines_out.append("source ^[._=a-zA-X0-9].*$\n") else: lines_out.append(line) - file(tagpath, "wb").write("".join(lines_out).encode("utf-8")) + f.close() + f = codecs.open(tagpath, "w", self.encoding) + f.write("".join(lines_out)) + f.close() def _add_project_code(self, path): # http://mwolson.org/projects/GettingStartedWithArch.html @@ -215,7 +219,9 @@ class Arch(RCS): return [os.path.join(root, p) for p in inv_str.split('\n')] def _add_dir_rule(self, rule, dirname, root): inv_path = os.path.join(dirname, '.arch-inventory') - file(inv_path, "ab").write(rule) + f = codecs.open(inv_path, "a", self.encoding) + f.write(rule) + f.close() if os.path.realpath(inv_path) not in self._list_added(root): paranoid = self.paranoid self.paranoid = False @@ -233,12 +239,16 @@ class Arch(RCS): pass def _rcs_get_file_contents(self, path, revision=None): if revision == None: - return file(self._u_abspath(path), "rb").read() + return RCS._rcs_get_file_contents(self, path, revision) else: status,output,error = \ self._invoke_client("file-find", path, revision) - path = output.rstrip('\n') - return file(self._u_abspath(path), "rb").read() + relpath = output.rstrip('\n') + abspath = os.path.join(self.rootdir, relpath) + f = codecs.open(abspath, "r", self.encoding) + contents = f.read() + f.close() + return contents def _rcs_duplicate_repo(self, directory, revision=None): if revision == None: RCS._rcs_duplicate_repo(self, directory, revision) diff --git a/libbe/bug.py b/libbe/bug.py index 68cff7b..67051f2 100644 --- a/libbe/bug.py +++ b/libbe/bug.py @@ -182,7 +182,10 @@ class Bug(object): if show_comments == True: if self._comments_loaded == False: self.load_comments() - comout = self.comment_root.string_thread(auto_name_map=True, + # take advantage of the string_thread(auto_name_map=True) + # SIDE-EFFECT of sorting by bug time. + comout = self.comment_root.string_thread(flatten=False, + auto_name_map=True, bug_shortname=shortname) output = bugout + '\n' + comout.rstrip('\n') else : diff --git a/libbe/bugdir.py b/libbe/bugdir.py index 596922f..6bb6a43 100644 --- a/libbe/bugdir.py +++ b/libbe/bugdir.py @@ -24,8 +24,9 @@ import doctest import mapfile import bug -import utility import rcs +import encoding +import utility class NoBugDir(Exception): @@ -64,7 +65,9 @@ class MultipleBugMatches(ValueError): TREE_VERSION_STRING = "Bugs Everywhere Tree 1 0\n" -def setting_property(name, valid=None, doc=None): +def setting_property(name, valid=None, default=None, doc=None): + if default != None: + raise NotImplementedError def getter(self): value = self.settings.get(name) if valid is not None: @@ -88,7 +91,27 @@ def setting_property(name, valid=None, doc=None): class BugDir (list): """ - File-system access: + Sink to existing root + ====================== + + Consider the following usage case: + You have a bug directory rooted in + /path/to/source + by which I mean the '.be' directory is at + /path/to/source/.be + However, you're of in some subdirectory like + /path/to/source/GUI/testing + and you want to comment on a bug. Setting sink_to_root=True wen + you initialize your BugDir will cause it to search for the '.be' + file in the ancestors of the path you passed in as 'root'. + /path/to/source/GUI/testing/.be miss + /path/to/source/GUI/.be miss + /path/to/source/.be hit! + So it still roots itself appropriately without much work for you. + + File-system access + ================== + When rooted in non-bugdir directory, BugDirs live completely in memory until the first call to .save(). This creates a '.be' sub-directory containing configurations options, bugs, comments, @@ -98,12 +121,35 @@ class BugDir (list): will only load information from the file system when it loads new bugs/comments that it doesn't already have in memory, or when it explicitly asked to do so (e.g. .load() or __init__(from_disk=True)). + + Allow RCS initialization + ======================== + + This one is for testing purposes. Setting it to True allows the + BugDir to search for an installed RCS backend and initialize it in + the root directory. This is a convenience option for supporting + tests of versioning functionality (e.g. .duplicate_bugdir). + + Disable encoding manipulation + ============================= + + This one is for testing purposed. You might have non-ASCII + Unicode in your bugs, comments, files, etc. BugDir instances try + and support your preferred encoding scheme (e.g. "utf-8") when + dealing with stream and file input/output. For stream output, + this involves replacing sys.stdout and sys.stderr + (libbe.encode.set_IO_stream_encodings). However this messes up + doctest's output catching. In order to support doctest tests + using BugDirs, set manipulate_encodings=False, and stick to ASCII + in your tests. """ def __init__(self, root=None, sink_to_existing_root=True, assert_new_BugDir=False, allow_rcs_init=False, + manipulate_encodings=True, from_disk=False, rcs=None): list.__init__(self) self._save_user_id = False + self._manipulate_encodings = manipulate_encodings self.settings = {} if root == None: root = os.getcwd() @@ -131,7 +177,8 @@ class BugDir (list): """ if not os.path.exists(path): raise NoRootEntry(path) - versionfile = utility.search_parent_directories(path, os.path.join(".be", "version")) + versionfile=utility.search_parent_directories(path, + os.path.join(".be", "version")) if versionfile != None: beroot = os.path.dirname(versionfile) root = os.path.dirname(beroot) @@ -142,11 +189,11 @@ class BugDir (list): raise NoBugDir(path) return beroot - def get_version(self, path=None): - if self.rcs_name == None: - # Use a temporary RCS to check the version for the first time + def get_version(self, path=None, use_none_rcs=False): + if use_none_rcs == True: RCS = rcs.rcs_by_name("None") RCS.root(self.root) + RCS.encoding = encoding.get_encoding() else: RCS = self.rcs @@ -159,47 +206,65 @@ class BugDir (list): self.rcs.set_file_contents(self.get_path("version"), TREE_VERSION_STRING) - rcs_name = setting_property("rcs_name", - ("None", "bzr", "git", "Arch", "hg"), - doc= -"""The name of the current RCS. Kept seperate to make saving/loading -settings easy. Don't set this attribute. Set .rcs instead, and -.rcs_name will be automatically adjusted.""") - - _rcs = None + def _get_encoding(self): + if self._encoding == None: + return encoding.get_encoding() + else: + return self._encoding + def _set_encoding(self, new_encoding): + if new_encoding != None: + if encoding.known_encoding(new_encoding) == False: + raise InvalidValue("encoding", new_encoding) + self._encoding = new_encoding + if self._manipulate_encodings == True: + encoding.set_IO_stream_encodings(self.encoding) + if hasattr(self, "rcs"): + if self.rcs != None: + self.rcs.encoding = self.encoding + _encoding = setting_property("encoding", + doc= +"""The default input/output encoding to use (e.g. "utf-8"). +Dont' set this attribute, set .encoding instead.""") + encoding = property(_get_encoding, _set_encoding, doc= +"""The default input/output encoding to use (e.g. "utf-8").""") def _get_rcs(self): return self._rcs - def _set_rcs(self, new_rcs): if new_rcs == None: new_rcs = rcs.rcs_by_name("None") + new_rcs.encoding = self.encoding self._rcs = new_rcs new_rcs.root(self.root) self.rcs_name = new_rcs.name - + _rcs = None rcs = property(_get_rcs, _set_rcs, doc="A revision control system (RCS) instance") + rcs_name = setting_property("rcs_name", + ("None", "bzr", "git", "Arch", "hg"), + doc= +"""The name of the current RCS. Kept seperate to make saving/loading +settings easy. Don't set this attribute. Set .rcs instead, and +.rcs_name will be automatically adjusted.""") - _user_id = setting_property("user_id", doc= -"""The user's prefered name. Kept seperate to make saving/loading -settings easy. Don't set this attribute. Set .user_id instead, -and ._user_id will be automatically adjusted. This setting is -only saved if ._save_user_id == True""") def _get_user_id(self): if self._user_id == None and self.rcs != None: self._user_id = self.rcs.get_user_id() return self._user_id - def _set_user_id(self, user_id): if self.rcs != None: self.rcs.user_id = user_id self._user_id = user_id - user_id = property(_get_user_id, _set_user_id, doc= """The user's prefered name, e.g 'John Doe <jdoe@example.com>'. Note that the Arch RCS backend *enforces* ids with this format.""") + _user_id = setting_property("user_id", doc= +"""The user's prefered name. Kept seperate to make saving/loading +settings easy. Don't set this attribute. Set .user_id instead, +and ._user_id will be automatically adjusted. This setting is +only saved if ._save_user_id == True""") + target = setting_property("target", doc="The current project development target") @@ -231,7 +296,7 @@ that the Arch RCS backend *enforces* ids with this format.""") return new_rcs def load(self): - version = self.get_version() + version = self.get_version(use_none_rcs=True) if version != TREE_VERSION_STRING: raise NotImplementedError, \ "BugDir cannot handle version '%s' yet." % version @@ -241,6 +306,7 @@ that the Arch RCS backend *enforces* ids with this format.""") self.settings = self._get_settings(self.get_path("settings")) self.rcs = rcs.rcs_by_name(self.rcs_name) + self.encoding = self.encoding # setup encoding, IO_stream_encoding... if self.settings.get("user_id") != None: self.save_user_id() # was a user name in the settings file @@ -292,6 +358,8 @@ that the Arch RCS backend *enforces* ids with this format.""") if "user_id" in settings: settings = copy.copy(settings) del settings["user_id"] + if settings.get("encoding") == encoding.get_encoding(): + del settings["encoding"] # don't duplicate system default allow_no_rcs = not self.rcs.path_in_root(settings_path) # allow_no_rcs=True should only be for the special case of # configuring duplicate bugdir settings @@ -310,7 +378,7 @@ that the Arch RCS backend *enforces* ids with this format.""") duplicate_settings["user_id"] = self.user_id self._save_settings(duplicate_settings_path, duplicate_settings) - return BugDir(duplicate_path, from_disk=True) + return BugDir(duplicate_path, from_disk=True, manipulate_encodings=self._manipulate_encodings) def remove_duplicate_bugdir(self): self.rcs.remove_duplicate_repo() @@ -423,7 +491,8 @@ def simple_bug_dir(): """ dir = utility.Dir() assert os.path.exists(dir.path) - bugdir = BugDir(dir.path, sink_to_existing_root=False, allow_rcs_init=True) + bugdir = BugDir(dir.path, sink_to_existing_root=False, allow_rcs_init=True, + manipulate_encodings=False) bugdir._dir_ref = dir # postpone cleanup since dir.__del__() removes dir. bug_a = bugdir.new_bug("a", summary="Bug A") bug_a.creator = "John Doe <jdoe@example.com>" diff --git a/libbe/bzr.py b/libbe/bzr.py index a0ae715..38af6bb 100644 --- a/libbe/bzr.py +++ b/libbe/bzr.py @@ -55,7 +55,7 @@ class Bzr(RCS): pass def _rcs_get_file_contents(self, path, revision=None): if revision == None: - return file(os.path.join(self.rootdir, path), "rb").read() + return RCS._rcs_get_file_contents(self, path, revision) else: status,output,error = \ self._u_invoke_client("cat","-r",revision,path) diff --git a/libbe/cmdutil.py b/libbe/cmdutil.py index 6d7ab01..1a321e9 100644 --- a/libbe/cmdutil.py +++ b/libbe/cmdutil.py @@ -16,14 +16,14 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import optparse import os -import locale from textwrap import TextWrapper from StringIO import StringIO import doctest import bugdir import plugin -import utility +import encoding + class UserError(Exception): def __init__(self, msg): @@ -34,6 +34,13 @@ class UserErrorWrap(UserError): UserError.__init__(self, str(exception)) self.exception = exception +class GetHelp(Exception): + pass + +class UsageError(Exception): + pass + + def iter_commands(): for name, module in plugin.iter_plugins("becommands"): yield name.replace("_", "-"), module @@ -52,9 +59,11 @@ def get_command(command_name): raise UserError("Unknown command %s" % command_name) return cmd + def execute(cmd, args): - encoding = locale.getpreferredencoding() or 'ascii' - return get_command(cmd).execute([a.decode(encoding) for a in args]) + enc = encoding.get_encoding() + get_command(cmd).execute([a.decode(enc) for a in args]) + return 0 def help(cmd=None): if cmd != None: @@ -71,17 +80,8 @@ def help(cmd=None): ret.append("be %s%*s %s" % (name, numExtraSpaces, "", desc)) return "\n".join(ret) -class GetHelp(Exception): - pass - - -class UsageError(Exception): - pass - - def raise_get_help(option, opt, value, parser): raise GetHelp - class CmdOptionParser(optparse.OptionParser): def __init__(self, usage): diff --git a/libbe/comment.py b/libbe/comment.py index 579e294..bd085fa 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -243,11 +243,19 @@ class Comment(Tree): self.add_reply(reply) return reply - def string_thread(self, name_map={}, indent=0, + def string_thread(self, name_map={}, indent=0, flatten=True, auto_name_map=False, bug_shortname=None): """ Return a sting displaying a thread of comments. bug_shortname is only used if auto_name_map == True. + + SIDE-EFFECT: if auto_name_map==True, calls comment_shornames() + which will sort the tree by comment.time. Avoid by calling + name_map = {} + for shortname,comment in comm.comment_shortnames(bug_shortname): + name_map[comment.uuid] = shortname + comm.sort(key=lambda c : c.From) # your sort + comm.string_thread(name_map=name_map) >>> a = Comment(bug=None, uuid="a", body="Insightful remarks") >>> a.time = utility.str_to_time("Thu, 20 Nov 2008 01:00:00 +0000") @@ -261,7 +269,7 @@ class Comment(Tree): >>> d.uuid = "d" >>> d.time = utility.str_to_time("Thu, 20 Nov 2008 04:00:00 +0000") >>> a.sort(key=lambda comm : comm.time) - >>> print a.string_thread() + >>> print a.string_thread(flatten=True) --------- Comment --------- Name: a From: @@ -317,7 +325,7 @@ class Comment(Tree): for shortname,comment in self.comment_shortnames(bug_shortname): name_map[comment.uuid] = shortname stringlist = [] - for depth,comment in self.thread(flatten=True): + for depth,comment in self.thread(flatten=flatten): ind = 2*depth+indent if comment.uuid in name_map: sname = name_map[comment.uuid] diff --git a/libbe/config.py b/libbe/config.py index 79c0d6f..94c700e 100644 --- a/libbe/config.py +++ b/libbe/config.py @@ -15,30 +15,40 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import ConfigParser +import codecs +import locale import os.path +import sys import doctest +default_encoding = sys.getfilesystemencoding() or locale.getpreferredencoding() + def path(): """Return the path to the per-user config file""" return os.path.expanduser("~/.bugs_everywhere") -def set_val(name, value, section="DEFAULT"): +def set_val(name, value, section="DEFAULT", encoding=None): """Set a value in the per-user config file :param name: The name of the value to set :param value: The new value to set (or None to delete the value) :param section: The section to store the name/value in """ + if encoding == None: + encoding = default_encoding config = ConfigParser.ConfigParser() - config.read(path()) + f = codecs.open(path(), "r", encoding) + config.readfp(f, path()) + f.close() if value is not None: config.set(section, name, value) else: config.remove_option(section, name) - config.write(file(path(), "wb")) - pass + f = codecs.open(path(), "w", encoding) + config.write(f) + f.close() -def get_val(name, section="DEFAULT"): +def get_val(name, section="DEFAULT", encoding=None): """ Get a value from the per-user config file @@ -49,13 +59,17 @@ def get_val(name, section="DEFAULT"): True >>> set_val("junk", "random") >>> get_val("junk") - 'random' + u'random' >>> set_val("junk", None) >>> get_val("junk") is None True """ + if encoding == None: + encoding = default_encoding config = ConfigParser.ConfigParser() - config.read(path()) + f = codecs.open(path(), "r", encoding) + config.readfp(f, path()) + f.close() try: return config.get(section, name) except ConfigParser.NoOptionError: diff --git a/libbe/diff.py b/libbe/diff.py index 86a91ca..5fc0166 100644 --- a/libbe/diff.py +++ b/libbe/diff.py @@ -46,12 +46,13 @@ def diff_report(diff_data, bug_dir): added.sort(cmp_severity) removed.sort(cmp_severity) modified.sort(modified_cmp) - + lines = [] + if len(added) > 0: - print "New bug reports:" + lines.append("New bug reports:") for bug in added: - print bug.string(shortlist=True) - print "" + lines.extend(bug.string(shortlist=True).splitlines()) + lines.append("") if len(modified) > 0: printed = False @@ -61,15 +62,18 @@ def diff_report(diff_data, bug_dir): continue if not printed: printed = True - print "Modified bug reports:" - print change_str - print "" + lines.append("Modified bug reports:") + lines.extend(change_str.splitlines()) + if printed == True: + lines.append("") - if len(removed) > 0: - print "Removed bug reports:" + if len(removed) > 0: + lines.append("Removed bug reports:") for bug in removed: - print bug.string(shortlist=True) - print "" + lines.extend(bug.string(shortlist=True).splitlines()) + lines.append("") + + return '\n'.join(lines) def change_lines(old, new, attributes): change_list = [] diff --git a/libbe/editor.py b/libbe/editor.py new file mode 100644 index 0000000..4a63e5c --- /dev/null +++ b/libbe/editor.py @@ -0,0 +1,103 @@ +# Bugs Everywhere, a distributed bugtracker +# Copyright (C) 2005 Aaron Bentley and Panometrics, Inc. +# <abentley@panoramicfeedback.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA + +import codecs +import locale +import os +import sys +import tempfile +import doctest + +default_encoding = sys.getfilesystemencoding() or locale.getpreferredencoding() + +comment_marker = u"== Anything below this line will be ignored\n" + +class CantFindEditor(Exception): + def __init__(self): + Exception.__init__(self, "Can't find editor to get string from") + +def editor_string(comment=None, encoding=None): + """Invokes the editor, and returns the user_produced text as a string + + >>> if "EDITOR" in os.environ: + ... del os.environ["EDITOR"] + >>> if "VISUAL" in os.environ: + ... del os.environ["VISUAL"] + >>> editor_string() + Traceback (most recent call last): + CantFindEditor: Can't find editor to get string from + >>> os.environ["EDITOR"] = "echo bar > " + >>> editor_string() + u'bar\\n' + >>> os.environ["VISUAL"] = "echo baz > " + >>> editor_string() + u'baz\\n' + >>> del os.environ["EDITOR"] + >>> del os.environ["VISUAL"] + """ + if encoding == None: + encoding = default_encoding + for name in ('VISUAL', 'EDITOR'): + try: + editor = os.environ[name] + break + except KeyError: + pass + else: + raise CantFindEditor() + fhandle, fname = tempfile.mkstemp() + try: + if comment is not None: + os.write(fhandle, '\n'+comment_string(comment)) + os.close(fhandle) + oldmtime = os.path.getmtime(fname) + os.system("%s %s" % (editor, fname)) + f = codecs.open(fname, "r", encoding) + output = trimmed_string(f.read()) + f.close() + if output.rstrip('\n') == "": + output = None + finally: + os.unlink(fname) + return output + + +def comment_string(comment): + """ + >>> comment_string('hello') == comment_marker+"hello" + True + """ + return comment_marker + comment + + +def trimmed_string(instring): + """ + >>> trimmed_string("hello\\n"+comment_marker) + u'hello\\n' + >>> trimmed_string("hi!\\n" + comment_string('Booga')) + u'hi!\\n' + """ + out = [] + for line in instring.splitlines(True): + if line.startswith(comment_marker): + break + out.append(line) + return ''.join(out) + +suite = doctest.DocTestSuite() diff --git a/libbe/encoding.py b/libbe/encoding.py new file mode 100644 index 0000000..8b0ef73 --- /dev/null +++ b/libbe/encoding.py @@ -0,0 +1,53 @@ +# Bugs Everywhere, a distributed bugtracker +# Copyright (C) 2005 Aaron Bentley and Panometrics, Inc. +# <abentley@panoramicfeedback.com> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA +import codecs +import locale +import sys +import doctest + +def get_encoding(): + """ + Guess a useful input/output/filesystem encoding... Maybe we nees + seperate encodings for input/output and filessytem? Hmm... + """ + encoding = locale.getpreferredencoding() or sys.getdefaultencoding() + if sys.platform != 'win32' or sys.version_info[:2] > (2, 3): + encoding = locale.getlocale(locale.LC_TIME)[1] or encoding + # Python 2.3 on windows doesn't know about 'XYZ' alias for 'cpXYZ' + return encoding + +def known_encoding(encoding): + """ + >>> known_encoding("highly-unlikely-encoding") + False + >>> known_encoding(get_encoding()) + True + """ + try: + codecs.lookup(encoding) + return True + except LookupError: + return False + +def set_IO_stream_encodings(encoding): + sys.stdin = codecs.getreader(encoding)(sys.__stdin__) + sys.stdout = codecs.getwriter(encoding)(sys.__stdout__) + sys.stderr = codecs.getwriter(encoding)(sys.__stderr__) + +suite = doctest.DocTestSuite() diff --git a/libbe/git.py b/libbe/git.py index 046e72e..98fda2b 100644 --- a/libbe/git.py +++ b/libbe/git.py @@ -69,7 +69,7 @@ class Git(RCS): self._rcs_add(path) def _rcs_get_file_contents(self, path, revision=None): if revision == None: - return file(self._u_abspath(path), "rb").read() + return RCS._rcs_get_file_contents(self, path, revision) else: arg = "%s:%s" % (revision,path) status,output,error = self._u_invoke_client("show", arg) diff --git a/libbe/hg.py b/libbe/hg.py index 27cbb79..c00d7e2 100644 --- a/libbe/hg.py +++ b/libbe/hg.py @@ -58,14 +58,14 @@ class Hg(RCS): pass def _rcs_get_file_contents(self, path, revision=None): if revision == None: - return file(os.path.join(self.rootdir, path), "rb").read() + return RCS._rcs_get_file_contents(self, path, revision) else: status,output,error = \ self._u_invoke_client("cat","-r",revision,path) return output def _rcs_duplicate_repo(self, directory, revision=None): if revision == None: - RCS._rcs_duplicate_repo(self, directory, revision) + return RCS._rcs_duplicate_repo(self, directory, revision) else: self._u_invoke_client("archive", "--rev", revision, directory) def _rcs_commit(self, commitfile): diff --git a/libbe/rcs.py b/libbe/rcs.py index 3519c3d..786f9dd 100644 --- a/libbe/rcs.py +++ b/libbe/rcs.py @@ -15,13 +15,14 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA from subprocess import Popen, PIPE +import codecs import os import os.path -from socket import gethostname import re +from socket import gethostname +import shutil import sys import tempfile -import shutil import unittest import doctest @@ -77,8 +78,9 @@ class PathNotInRoot(Exception): self.root = root class NoSuchFile(Exception): - def __init__(self, pathname): - Exception.__init__(self, "No such file: %s" % pathname) + def __init__(self, pathname, root="."): + path = os.path.abspath(os.path.join(root, pathname)) + Exception.__init__(self, "No such file: %s" % path) def new(): @@ -97,12 +99,13 @@ class RCS(object): name = "None" client = "" # command-line tool for _u_invoke_client versioned = False - def __init__(self, paranoid=False): + def __init__(self, paranoid=False, encoding=sys.getdefaultencoding()): self.paranoid = paranoid self.verboseInvoke = False self.rootdir = None self._duplicateBasedir = None self._duplicateDirname = None + self.encoding = encoding def __del__(self): self.cleanup() @@ -171,15 +174,15 @@ class RCS(object): pass def _rcs_get_file_contents(self, path, revision=None): """ - Get the file contents as they were in a given revision. Don't - worry about decoding the contents, the RCS.get_file_contents() - method will handle that. - + Get the file contents as they were in a given revision. Revision==None specifies the current revision. """ assert revision == None, \ "The %s RCS does not support revision specifiers" % self.name - return file(os.path.join(self.rootdir, path), "rb").read() + f = codecs.open(os.path.join(self.rootdir, path), "r", self.encoding) + contents = f.read() + f.close() + return contents def _rcs_duplicate_repo(self, directory, revision=None): """ Get the repository as it was in a given revision. @@ -297,14 +300,18 @@ class RCS(object): relpath = self._u_rel_path(path) contents = self._rcs_get_file_contents(relpath,revision) else: - contents = file(path, "rb").read() - return contents.decode("utf-8") + f = codecs.open(path, "r", self.encoding) + contents = f.read() + f.close() + return contents def set_file_contents(self, path, contents, allow_no_rcs=False): """ Set the file contents under version control. """ add = not os.path.exists(path) - file(path, "wb").write(contents.encode("utf-8")) + f = codecs.open(path, "w", self.encoding) + f.write(contents) + f.close() if self._use_rcs(path, allow_no_rcs): if add: @@ -537,13 +544,13 @@ class RCS(object): Split the commitfile created in self.commit() back into summary and header lines. """ - f = file(commitfile, "rb") + f = codecs.open(commitfile, "r", self.encoding) summary = f.readline() body = f.read() body.lstrip('\n') if len(body) == 0: body = None - f.close + f.close() return (summary, body) diff --git a/libbe/utility.py b/libbe/utility.py index 2c77fcf..30240a9 100644 --- a/libbe/utility.py +++ b/libbe/utility.py @@ -15,10 +15,11 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA import calendar -import time +import codecs import os -import tempfile import shutil +import tempfile +import time import doctest @@ -87,73 +88,5 @@ def str_to_time(str_time): def handy_time(time_val): return time.strftime("%a, %d %b %Y %H:%M", time.localtime(time_val)) -class CantFindEditor(Exception): - def __init__(self): - Exception.__init__(self, "Can't find editor to get string from") - -def editor_string(comment=None): - - """Invokes the editor, and returns the user_produced text as a string - - >>> if "EDITOR" in os.environ: - ... del os.environ["EDITOR"] - >>> if "VISUAL" in os.environ: - ... del os.environ["VISUAL"] - >>> editor_string() - Traceback (most recent call last): - CantFindEditor: Can't find editor to get string from - >>> os.environ["EDITOR"] = "echo bar > " - >>> editor_string() - u'bar\\n' - >>> os.environ["VISUAL"] = "echo baz > " - >>> editor_string() - u'baz\\n' - >>> del os.environ["EDITOR"] - >>> del os.environ["VISUAL"] - """ - for name in ('VISUAL', 'EDITOR'): - try: - editor = os.environ[name] - break - except KeyError: - pass - else: - raise CantFindEditor() - fhandle, fname = tempfile.mkstemp() - try: - if comment is not None: - os.write(fhandle, '\n'+comment_string(comment)) - os.close(fhandle) - oldmtime = os.path.getmtime(fname) - os.system("%s %s" % (editor, fname)) - output = trimmed_string(file(fname, "rb").read().decode("utf-8")) - if output.rstrip('\n') == "": - output = None - finally: - os.unlink(fname) - return output - - -def comment_string(comment): - """ - >>> comment_string('hello') - '== Anything below this line will be ignored ==\\nhello' - """ - return '== Anything below this line will be ignored ==\n' + comment - - -def trimmed_string(instring): - """ - >>> trimmed_string("hello\\n== Anything below this line will be ignored") - 'hello\\n' - >>> trimmed_string("hi!\\n" + comment_string('Booga')) - 'hi!\\n' - """ - out = [] - for line in instring.splitlines(True): - if line.startswith('== Anything below this line will be ignored'): - break - out.append(line) - return ''.join(out) suite = doctest.DocTestSuite() |