diff options
Diffstat (limited to 'libbe/ui/util')
-rw-r--r-- | libbe/ui/util/__init__.py | 1 | ||||
-rw-r--r-- | libbe/ui/util/editor.py | 116 | ||||
-rw-r--r-- | libbe/ui/util/pager.py | 65 | ||||
-rw-r--r-- | libbe/ui/util/user.py | 89 |
4 files changed, 271 insertions, 0 deletions
diff --git a/libbe/ui/util/__init__.py b/libbe/ui/util/__init__.py new file mode 100644 index 0000000..b98f164 --- /dev/null +++ b/libbe/ui/util/__init__.py @@ -0,0 +1 @@ +# Copyright diff --git a/libbe/ui/util/editor.py b/libbe/ui/util/editor.py new file mode 100644 index 0000000..1a10fa4 --- /dev/null +++ b/libbe/ui/util/editor.py @@ -0,0 +1,116 @@ +# Bugs Everywhere, a distributed bugtracker +# Copyright (C) 2008-2009 Gianluca Montecchi <gian@grys.it> +# W. Trevor King <wking@drexel.edu> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +""" +Define editor_string(), a function that invokes an editor to accept +user-produced text as a string. +""" + +import codecs +import locale +import os +import sys +import tempfile + +import libbe +import libbe.util.encoding + +if libbe.TESTING == True: + import doctest + + +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' + >>> os.environ["VISUAL"] = "echo 'baz\\n== Anything below this line will be ignored\\nHi' > " + >>> editor_string() + u'baz\\n' + >>> del os.environ["EDITOR"] + >>> del os.environ["VISUAL"] + """ + if encoding == None: + encoding = libbe.util.encoding.get_filesystem_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: + cstring = u'\n'+comment_string(comment) + os.write(fhandle, cstring.encode(encoding)) + os.close(fhandle) + oldmtime = os.path.getmtime(fname) + os.system("%s %s" % (editor, fname)) + output = libbe.util.encoding.get_file_contents( + fname, encoding=encoding, decode=True) + output = trimmed_string(output) + 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) + +if libbe.TESTING == True: + suite = doctest.DocTestSuite() diff --git a/libbe/ui/util/pager.py b/libbe/ui/util/pager.py new file mode 100644 index 0000000..1ddc3fa --- /dev/null +++ b/libbe/ui/util/pager.py @@ -0,0 +1,65 @@ +# Copyright (C) 2009 W. Trevor King <wking@drexel.edu> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +""" +Automatic pager for terminal output (a la Git). +""" + +import sys, os, select + +# see http://nex-3.com/posts/73-git-style-automatic-paging-in-ruby +def run_pager(paginate='auto'): + """ + paginate should be one of 'never', 'auto', or 'always'. + + usage: just call this function and continue using sys.stdout like + you normally would. + """ + if paginate == 'never' \ + or sys.platform == 'win32' \ + or not hasattr(sys.stdout, 'isatty') \ + or sys.stdout.isatty() == False: + return + + if paginate == 'auto': + if 'LESS' not in os.environ: + os.environ['LESS'] = '' # += doesn't work on undefined var + # don't page if the input is short enough + os.environ['LESS'] += ' -FRX' + if 'PAGER' in os.environ: + pager = os.environ['PAGER'] + else: + pager = 'less' + + read_fd, write_fd = os.pipe() + if os.fork() == 0: + # child process + os.close(read_fd) + os.close(0) + os.dup2(write_fd, 1) + os.close(write_fd) + if hasattr(sys.stderr, 'isatty') and sys.stderr.isatty() == True: + os.dup2(1, 2) + return + + # parent process, become pager + os.close(write_fd) + os.dup2(read_fd, 0) + os.close(read_fd) + + # Wait until we have input before we start the pager + select.select([0], [], []) + os.execlp(pager, pager) diff --git a/libbe/ui/util/user.py b/libbe/ui/util/user.py new file mode 100644 index 0000000..d6af89b --- /dev/null +++ b/libbe/ui/util/user.py @@ -0,0 +1,89 @@ +# Copyright + +""" +Tools for getting, setting, creating, and parsing the user's id. For +example, + 'John Doe <jdoe@example.com>' +Note that the Arch VCS backend *enforces* ids with this format. +""" + +import os +import re +from socket import gethostname + +import libbe +import libbe.storage.util.config + +def get_fallback_username(): + name = None + for env in ["LOGNAME", "USERNAME"]: + if os.environ.has_key(env): + name = os.environ[env] + break + assert name != None + return name + +def get_fallback_email(): + hostname = gethostname() + name = get_fallback_username() + return "%s@%s" % (name, hostname) + +def create_user_id(name, email=None): + """ + >>> create_user_id("John Doe", "jdoe@example.com") + 'John Doe <jdoe@example.com>' + >>> create_user_id("John Doe") + 'John Doe' + """ + assert len(name) > 0 + if email == None or len(email) == 0: + return name + else: + return "%s <%s>" % (name, email) + +def parse_user_id(value): + """ + >>> parse_user_id("John Doe <jdoe@example.com>") + ('John Doe', 'jdoe@example.com') + >>> parse_user_id("John Doe") + ('John Doe', None) + >>> try: + ... parse_user_id("John Doe <jdoe@example.com><what?>") + ... except AssertionError: + ... print "Invalid match" + Invalid match + """ + emailexp = re.compile("(.*) <([^>]*)>(.*)") + match = emailexp.search(value) + if match == None: + email = None + name = value + else: + assert len(match.groups()) == 3 + assert match.groups()[2] == "", match.groups() + email = match.groups()[1] + name = match.groups()[0] + assert name != None + assert len(name) > 0 + return (name, email) + +def get_user_id(storage=None): + """ + Sometimes the storage will also keep track of the user id (e.g. most VCSs). + """ + user = libbe.storage.util.config.get_val('user') + if user != None: + return user + if storage != None and hasattr(storage, 'get_user_id'): + user = storage.get_user_id() + if user != None: + return user + name = get_fallback_username() + email = get_fallback_email() + user = create_user_id(name, email) + return user + +def set_user_id(user_id): + """ + """ + user = libbe.storage.util.config.set_val('user', user_id) |