aboutsummaryrefslogtreecommitdiffstats
path: root/libbe/ui/util
diff options
context:
space:
mode:
Diffstat (limited to 'libbe/ui/util')
-rw-r--r--libbe/ui/util/__init__.py1
-rw-r--r--libbe/ui/util/editor.py116
-rw-r--r--libbe/ui/util/pager.py65
-rw-r--r--libbe/ui/util/user.py89
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)