Release 0.14
[stgit] / stgit / utils.py
index 039b433..3a480c0 100644 (file)
@@ -1,8 +1,10 @@
 """Common utility functions
 """
 
-import errno, os, os.path, re, sys
+import errno, optparse, os, os.path, re, sys
+from stgit.exception import *
 from stgit.config import config
+from stgit.out import *
 
 __copyright__ = """
 Copyright (C) 2005, Catalin Marinas <catalin.marinas@gmail.com>
@@ -21,86 +23,6 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 """
 
-class MessagePrinter(object):
-    def __init__(self):
-        class Output(object):
-            def __init__(self, write, flush):
-                self.write = write
-                self.flush = flush
-                self.at_start_of_line = True
-                self.level = 0
-            def new_line(self):
-                """Ensure that we're at the beginning of a line."""
-                if not self.at_start_of_line:
-                    self.write('\n')
-                    self.at_start_of_line = True
-            def single_line(self, msg, print_newline = True,
-                            need_newline = True):
-                """Write a single line. Newline before and after are
-                separately configurable."""
-                if need_newline:
-                    self.new_line()
-                if self.at_start_of_line:
-                    self.write('  '*self.level)
-                self.write(msg)
-                if print_newline:
-                    self.write('\n')
-                    self.at_start_of_line = True
-                else:
-                    self.flush()
-                    self.at_start_of_line = False
-            def tagged_lines(self, tag, lines):
-                tag += ': '
-                for line in lines:
-                    self.single_line(tag + line)
-                    tag = ' '*len(tag)
-            def write_line(self, line):
-                """Write one line of text on a lines of its own, not
-                indented."""
-                self.new_line()
-                self.write('%s\n' % line)
-                self.at_start_of_line = True
-            def write_raw(self, string):
-                """Write an arbitrary string, possibly containing
-                newlines."""
-                self.new_line()
-                self.write(string)
-                self.at_start_of_line = string.endswith('\n')
-        self.__stdout = Output(sys.stdout.write, sys.stdout.flush)
-        if sys.stdout.isatty():
-            self.__out = self.__stdout
-        else:
-            self.__out = Output(lambda msg: None, lambda: None)
-    def stdout(self, line):
-        """Write a line to stdout."""
-        self.__stdout.write_line(line)
-    def stdout_raw(self, string):
-        """Write a string possibly containing newlines to stdout."""
-        self.__stdout.write_raw(string)
-    def info(self, *msgs):
-        for msg in msgs:
-            self.__out.single_line(msg)
-    def note(self, *msgs):
-        self.__out.tagged_lines('Notice', msgs)
-    def warn(self, *msgs):
-        self.__out.tagged_lines('Warning', msgs)
-    def error(self, *msgs):
-        self.__out.tagged_lines('Error', msgs)
-    def start(self, msg):
-        """Start a long-running operation."""
-        self.__out.single_line('%s ... ' % msg, print_newline = False)
-        self.__out.level += 1
-    def done(self, extramsg = None):
-        """Finish long-running operation."""
-        self.__out.level -= 1
-        if extramsg:
-            msg = 'done (%s)' % extramsg
-        else:
-            msg = 'done'
-        self.__out.single_line(msg, need_newline = False)
-
-out = MessagePrinter()
-
 def mkdir_file(filename, mode):
     """Opens filename with the given mode, creating the directory it's
     in if it doesn't already exist."""
@@ -245,7 +167,7 @@ def rename(basedir, file1, file2):
         # file1's parent dir may not be empty after move
         pass
 
-class EditorException(Exception):
+class EditorException(StgException):
     pass
 
 def call_editor(filename):
@@ -269,13 +191,16 @@ def call_editor(filename):
 
 def patch_name_from_msg(msg):
     """Return a string to be used as a patch name. This is generated
-    from the top line of the string passed as argument, and is at most
-    30 characters long."""
+    from the top line of the string passed as argument."""
     if not msg:
         return None
 
+    name_len = config.get('stgit.namelength')
+    if not name_len:
+        name_len = 30
+
     subject_line = msg.split('\n', 1)[0].lstrip().lower()
-    return re.sub('[\W]+', '-', subject_line).strip('-')[:30]
+    return re.sub('[\W]+', '-', subject_line).strip('-')[:name_len]
 
 def make_patch_name(msg, unacceptable, default_name = 'patch'):
     """Return a patch name generated from the given commit message,
@@ -290,3 +215,44 @@ def make_patch_name(msg, unacceptable, default_name = 'patch'):
             suffix += 1
         patchname = '%s-%d' % (patchname, suffix)
     return patchname
+
+# any and all functions are builtin in Python 2.5 and higher, but not
+# in 2.4.
+if not 'any' in dir(__builtins__):
+    def any(bools):
+        for b in bools:
+            if b:
+                return True
+        return False
+if not 'all' in dir(__builtins__):
+    def all(bools):
+        for b in bools:
+            if not b:
+                return False
+        return True
+
+def make_sign_options():
+    def callback(option, opt_str, value, parser, sign_str):
+        if parser.values.sign_str not in [None, sign_str]:
+            raise optparse.OptionValueError(
+                '--ack and --sign were both specified')
+        parser.values.sign_str = sign_str
+    return [optparse.make_option('--sign', action = 'callback',
+                                 callback = callback, dest = 'sign_str',
+                                 callback_args = ('Signed-off-by',),
+                                 help = 'add Signed-off-by line'),
+            optparse.make_option('--ack', action = 'callback',
+                                 callback = callback, dest = 'sign_str',
+                                 callback_args = ('Acked-by',),
+                                 help = 'add Acked-by line')]
+
+def add_sign_line(desc, sign_str, name, email):
+    if not sign_str:
+        return desc
+    sign_str = '%s: %s <%s>' % (sign_str, name, email)
+    if sign_str in desc:
+        return desc
+    desc = desc.rstrip()
+    if not any(s in desc for s in ['\nSigned-off-by:', '\nAcked-by:']):
+        desc = desc + '\n'
+    return '%s\n%s\n' % (desc, sign_str)