-################################################################
-# lilylib.py -- options and stuff
-#
-# source file of the GNU LilyPond music typesetter
+# This file is part of LilyPond, the GNU music typesetter.
#
-# (c) 1998--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
-# Jan Nieuwenhuizen <janneke@gnu.org>
-
-### subst:\(^\|[^._a-z]\)\(abspath\|identify\|warranty\|progress\|warning\|error\|exit\|getopt_args\|option_help_str\|options_help_str\|help\|setup_temp\|read_pipe\|system\|cleanup_temp\|strip_extension\|cp_to_dir\|mkdir_p\|init\) *(
-### replace:\1ly.\2 (
-
-### subst: \(help_summary\|keep_temp_dir_p\|option_definitions\|original_dir\|program_name\|pseudo_filter_p\|temp_dir\|verbose_p\)
+# Copyright (C) 1998--2015 Han-Wen Nienhuys <hanwen@xs4all.nl>
+# Jan Nieuwenhuizen <janneke@gnu.org>
+#
+# LilyPond 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 3 of the License, or
+# (at your option) any later version.
+#
+# LilyPond 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 LilyPond. If not, see <http://www.gnu.org/licenses/>.
import __main__
+import glob
+import os
+import re
import shutil
-import string
import sys
-import tempfile
-import glob
+import optparse
+import time
################################################################
# Users of python modules should include this snippet
# and customize variables below.
-# We'll suffer this path init stuff as long as we don't install our
-# python packages in <prefix>/lib/pythonx.y (and don't kludge around
-# it as we do with teTeX on Red Hat Linux: set some environment var
-# (PYTHONPATH) in profile)
-
-# If set, LILYPONDPREFIX must take prevalence
-# if datadir is not set, we're doing a build and LILYPONDPREFIX
-
-import getopt
-import os
-import sys
-datadir = '@local_lilypond_datadir@'
-if not os.path.isdir (datadir):
- datadir = '@lilypond_datadir@'
-if os.environ.has_key ('LILYPONDPREFIX') :
- datadir = os.environ['LILYPONDPREFIX']
- while datadir[-1] == os.sep:
- datadir= datadir[:-1]
-
-sys.path.insert (0, os.path.join (datadir, 'python'))
-
-
-
-# Customize these
-if __name__ == '__main__':
- import lilylib as ly
- global _;_=ly._
- global re;re = ly.re
-
- # lilylib globals
- program_name = 'unset'
- pseudo_filter_p = 0
- original_dir = os.getcwd ()
- temp_dir = os.path.join (original_dir, '%s.dir' % program_name)
- keep_temp_dir_p = 0
- verbose_p = 0
- help_summary = _ ("lilylib module")
-
- option_definitions = [
- ('', 'h', 'help', _ ("print this help")),
- ]
-
- from lilylib import *
-################################################################
-
-# Handle bug in Python 1.6-2.1
-#
-# there are recursion limits for some patterns in Python 1.6 til 2.1.
-# fix this by importing pre instead. Fix by Mats.
-
-if float (sys.version[0:3]) <= 2.1:
- try:
- import pre
- re = pre
- del pre
- except ImportError:
- import re
-else:
- import re
-
-# Attempt to fix problems with limited stack size set by Python!
-# Sets unlimited stack size. Note that the resource module only
-# is available on UNIX.
-try:
- import resource
- resource.setrlimit (resource.RLIMIT_STACK, (-1, -1))
-except:
- pass
+# Python 2.5 only accepts strings with proper Python internal encoding
+# (i.e. ASCII or Unicode) when writing to stdout/stderr, so we must
+# use ugettext iso gettext, and encode the string when writing to
+# stdout/stderr
localedir = '@localedir@'
try:
- import gettext
- gettext.bindtextdomain ('lilypond', localedir)
- gettext.textdomain ('lilypond')
- _ = gettext.gettext
+ import gettext
+ t = gettext.translation ('lilypond', localedir)
+ _ = t.ugettext
+ ungettext = t.ungettext
except:
- def _ (s):
- return s
+ def _ (s):
+ return s
+ def ungettext (s, p, n):
+ if n == 1:
+ return s
+ return p
underscore = _
-def identify (port):
- port.write ('%s (GNU LilyPond) %s\n' % (__main__.program_name, __main__.program_version))
+# Urg, Python 2.4 does not define stderr/stdout encoding
+# Maybe guess encoding from LANG/LC_ALL/LC_CTYPE?
+
+reload (sys)
+sys.setdefaultencoding ('utf-8')
+import codecs
+sys.stdout = codecs.getwriter ('utf8') (sys.stdout)
+sys.stderr = codecs.getwriter ('utf8') (sys.stderr)
+
+def encoded_write(f, s):
+ f.write (s.encode (f.encoding or 'utf-8', 'replace'))
+
+# ugh, Python 2.5 optparse requires Unicode strings in some argument
+# functions, and refuse them in some other places
+def display_encode (s):
+ return s.encode (sys.stderr.encoding or 'utf-8', 'replace')
+
+# Lilylib globals.
+program_version = '@TOPLEVEL_VERSION@'
+program_name = os.path.basename (sys.argv[0])
+
+
+# Check if program_version contains @ characters. This will be the case if
+# the .py file is called directly while building the lilypond documentation.
+# If so, try to check for the env var LILYPOND_VERSION, which is set by our
+# makefiles and use its value.
+at_re = re.compile (r'@')
+if at_re.match (program_version):
+ if os.environ.has_key('LILYPOND_VERSION'):
+ program_version = os.environ['LILYPOND_VERSION']
+ else:
+ program_version = "unknown"
+
+
+# Logging framework: We have the following output functions:
+# error
+# warning
+# progress
+# debug
+
+loglevels = {"NONE":0, "ERROR":1, "WARN":2, "BASIC":3, "PROGRESS":4, "INFO":5, "DEBUG":6}
+
+loglevel = loglevels["PROGRESS"]
+
+def set_loglevel (l):
+ global loglevel
+ newlevel = loglevels.get (l, -1)
+ if newlevel > 0:
+ debug_output (_ ("Setting loglevel to %s") % l)
+ loglevel = newlevel
+ else:
+ error (_ ("Unknown or invalid loglevel '%s'") % l)
+
+
+def handle_loglevel_option (option, opt_str, value, parser, *args):
+ if value:
+ set_loglevel (value);
+ elif args:
+ set_loglevel (args[0]);
+
+def is_loglevel (l):
+ global loglevel
+ return loglevel >= loglevels[l];
+
+def is_verbose ():
+ return is_loglevel ("DEBUG")
+
+def stderr_write (s):
+ encoded_write (sys.stderr, s)
+
+def print_logmessage (level, s, fullmessage = True, newline = True):
+ if (is_loglevel (level)):
+ if fullmessage:
+ stderr_write (program_name + ": " + s + '\n')
+ elif newline:
+ stderr_write (s + '\n')
+ else:
+ stderr_write (s)
-def warranty ():
- identify (sys.stdout)
- sys.stdout.write ('\n')
- sys.stdout.write (_ ("Copyright (c) %s by") % '1998--2005')
- sys.stdout.write ('\n')
- map (lambda x: sys.stdout.write (' %s\n' % x), __main__.copyright)
- sys.stdout.write ('\n')
- sys.stdout.write (_ ("Distributed under terms of the GNU General Public License."))
- sys.stdout.write ('\n')
- sys.stdout.write (_ ("It comes with NO WARRANTY."))
- sys.stdout.write ('\n')
-
-def progress (s):
- sys.stderr.write (s)
+def error (s):
+ print_logmessage ("ERROR", _ ("error: %s") % s);
def warning (s):
- sys.stderr.write (__main__.program_name + ": " + _ ("warning: %s") % s + '\n')
+ print_logmessage ("WARN", _ ("warning: %s") % s);
-def error (s):
- sys.stderr.write (__main__.program_name + ": " + _ ("error: %s") % s + '\n')
-
-def exit (i):
- if __main__.verbose_p:
- raise _ ('Exiting (%d)...') % i
- else:
- sys.exit (i)
-
-def getopt_args (opts):
- '''Construct arguments (LONG, SHORT) for getopt from list of options.'''
- short = ''
- long = []
- for o in opts:
- if o[1]:
- short = short + o[1]
- if o[0]:
- short = short + ':'
- if o[2]:
- l = o[2]
- if o[0]:
- l = l + '='
- long.append (l)
- return (short, long)
-
-def option_help_str (o):
- '''Transform one option description (4-tuple ) into neatly formatted string'''
- sh = ' '
- if o[1]:
- sh = '-%s' % o[1]
-
- sep = ' '
- if o[1] and o[2]:
- sep = ', '
-
- long = ''
- if o[2]:
- long= '--%s' % o[2]
-
- arg = ''
- if o[0]:
- if o[2]:
- arg = '='
- arg = arg + o[0]
- return ' ' + sh + sep + long + arg
-
-
-def options_help_str (opts):
- '''Convert a list of options into a neatly formatted string'''
- w = 0
- strs =[]
- helps = []
-
- for o in opts:
- s = option_help_str (o)
- strs.append ((s, o[3]))
- if len (s) > w:
- w = len (s)
-
- str = ''
- for s in strs:
- first = 1
- for ss in re.split ('\n\s*', s[1]):
- if first:
- str = str + '%s%s%s\n' \
- % (s[0], ' ' * (w - len (s[0]) + 3), ss)
- first = 0
- else:
- str = str + '%s%s\n' \
- % (' ' * (w + 3), ss)
- return str
-
-def help ():
- ls = [(_ ("Usage: %s [OPTIONS]... FILE") % __main__.program_name),
- ('\n\n'),
- (__main__.help_summary),
- ('\n\n'),
- (_ ("Options:")),
- ('\n'),
- (options_help_str (__main__.option_definitions)),
- ('\n\n'),
- (_ ("Report bugs to %s.") % 'bug-lilypond@gnu.org'),
- ('\n')]
- map (sys.stdout.write, ls)
-
-def lilypond_version (binary):
- p = read_pipe ('%s --version ' % binary)
-
- ls = p.split ('\n')
- v= '<not found>'
- for l in ls:
- m = re.search ('GNU LilyPond ([0-9a-z.]+)', p)
- if m:
- v = m.group (1)
-
- return v
-
-def lilypond_version_check (binary, req):
- if req[0] <> '@' :
- v = lilypond_version (binary)
- if v <> req:
- error (_("Binary %s has version %s, looking for version %s") % \
- (binary, v, req))
- sys.exit (1)
-
-
-def setup_temp ():
-
- ''' Create a temporary directory, and return its name. '''
-
- if not __main__.keep_temp_dir_p:
- __main__.temp_dir = tempfile.mktemp (__main__.program_name)
- try:
- os.mkdir (__main__.temp_dir, 0700)
- except OSError:
- pass
-
- return __main__.temp_dir
+def basic_progress (s):
+ print_logmessage ("BASIC", s);
-def command_name (cmd):
- # Strip all stuf after command,
- # deal with "((latex ) >& 1 ) .." too
- cmd = re.match ('([\(\)]*)([^\\\ ]*)', cmd).group (2)
- return os.path.basename (cmd)
-
-def error_log (name):
- name = re.sub('[^a-z]','x', name)
- return tempfile.mktemp ('%s.errorlog' % name)
-
-def read_pipe (cmd, mode = 'r'):
-
-
- redirect = ''
- error_log_file = ''
- if __main__.verbose_p:
- progress (_ ("Opening pipe `%s\'") % cmd)
- else:
- error_log_file = error_log (command_name (cmd))
- redirect = ' 2>%s' % error_log_file
-
- pipe = os.popen (cmd + redirect, mode)
- output = pipe.read ()
- status = pipe.close ()
- # successful pipe close returns 'None'
- if not status:
- status = 0
- signal = 0x0f & status
- exit_status = status >> 8
-
- if status:
- error (_ ("`%s\' failed (%d)") % (cmd, exit_status))
-
- if not __main__.verbose_p:
- contents = open (error_log_file).read ()
- if contents:
- error (_ ("The error log is as follows:"))
- sys.stderr.write (contents)
-
- # Ugh. code dup
- if error_log_file:
- os.unlink (error_log_file)
-
- exit (1)
-
- if __main__.verbose_p:
- progress ('\n')
-
- if error_log_file:
- os.unlink (error_log_file)
-
- return output
-
-def system (cmd, ignore_error = 0, progress_p = 0):
-
- '''System CMD. If IGNORE_ERROR, do not complain when CMD
-returns non zero. If PROGRESS_P, always show progress.
-
-RETURN VALUE
-
-Exit status of CMD '''
-
- name = command_name (cmd)
- error_log_file = ''
-
- if __main__.verbose_p:
- progress_p = 1
- progress (_ ("Invoking `%s\'") % cmd)
- else:
- progress ( _("Running %s...") % name)
-
- redirect = ''
- if not progress_p:
- error_log_file = error_log (name)
- redirect = ' 1>/dev/null 2>' + error_log_file
- elif __main__.pseudo_filter_p:
- redirect = ' 1>/dev/null'
-
- status = os.system (cmd + redirect)
- signal = 0x0f & status
- exit_status = status >> 8
-
- if status:
-
- exit_type = 'status %d' % exit_status
- if signal:
- exit_type = 'signal %d' % signal
-
- msg = _ ("`%s\' failed (%s)") % (name, exit_type)
- if ignore_error:
- if __main__.verbose_p:
- warning (msg + ' ' + _ ("(ignored)"))
- else:
- error (msg)
- if not progress_p and error_log_file:
- error (_ ("The error log is as follows:"))
- sys.stderr.write (open (error_log_file).read ())
- if error_log_file:
- os.unlink (error_log_file)
- exit (1)
-
- if error_log_file:
- os.unlink (error_log_file)
- progress ('\n')
- return status
-
-def cleanup_temp ():
- if not __main__.keep_temp_dir_p:
- if __main__.verbose_p:
- progress (_ ("Cleaning %s...") % __main__.temp_dir)
- shutil.rmtree (__main__.temp_dir)
- if __main__.verbose_p:
- progress ('\n')
+def progress (s, fullmessage = False, newline = True):
+ print_logmessage ("PROGRESS", s, fullmessage, newline);
+def debug_output (s, fullmessage = False, newline = True):
+ print_logmessage ("DEBUG", s, fullmessage, newline);
-def strip_extension (f, ext):
- (p, e) = os.path.splitext (f)
- if e == ext:
- e = ''
- return p + e
-def cp_to_dir (pattern, dir):
- "Copy files matching re PATTERN from cwd to DIR"
-
- # Duh. Python style portable: cp *.EXT OUTDIR
- # system ('cp *.%s %s' % (ext, outdir), 1)
+def require_python_version ():
+ if sys.hexversion < 0x02040000:
+ error ("Python 2.4 or newer is required to run this program.\n\
+Please upgrade Python from http://python.org/download/, and if you use MacOS X,\n\
+please read 'Setup for MacOS X' in Application Usage.")
+ os.system ("open http://python.org/download/")
+ sys.exit (2)
- files = filter (lambda x, p=pattern: re.match (p, x), os.listdir ('.'))
- map (lambda x, d=dir: shutil.copy2 (x, os.path.join (d, x)), files)
+# A modified version of the commands.mkarg(x) that always uses
+# double quotes (since Windows can't handle the single quotes)
+# and escapes the characters \, $, ", and ` for unix shells.
+def mkarg(x):
+ if os.name == 'nt':
+ return ' "%s"' % x
+ s = ' "'
+ for c in x:
+ if c in '\\$"`':
+ s = s + '\\'
+ s = s + c
+ s = s + '"'
+ return s
+def command_name (cmd):
+ # Strip all stuf after command,
+ # deal with "((latex ) >& 1 ) .." too
+ cmd = re.match ('([\(\)]*)([^\\\ ]*)', cmd).group (2)
+ return os.path.basename (cmd)
+
+def subprocess_system (cmd,
+ ignore_error=False,
+ progress_p=True,
+ be_verbose=False,
+ redirect_output=False,
+ log_file=None):
+ import subprocess
+
+ show_progress= progress_p
+ name = command_name (cmd)
+ error_log_file = ''
+
+ if redirect_output:
+ progress (_ ("Processing %s.ly") % log_file)
+ else:
+ if be_verbose:
+ show_progress = 1
+ progress (_ ("Invoking `%s\'") % cmd)
+ else:
+ progress ( _("Running %s...") % name)
+
+ stdout_setting = None
+ stderr_setting = None
+ if not show_progress:
+ stdout_setting = subprocess.PIPE
+
+ if redirect_output:
+ stderr_filename = log_file + '.log'
+ stderr_setting = open(stderr_filename, 'w')
+
+ proc = subprocess.Popen (cmd,
+ shell=True,
+ universal_newlines=True,
+ stdout=stdout_setting,
+ stderr=stderr_setting)
+
+ log = ''
+
+ if redirect_output:
+ while proc.poll()==None:
+ time.sleep(0.01)
+ retval = proc.returncode
+ stderr_setting.close()
+ else:
+ if show_progress:
+ retval = proc.wait()
+ else:
+ log = proc.communicate ()
+ retval = proc.returncode
+
+ if retval:
+ print >>sys.stderr, 'command failed:', cmd
+ if retval < 0:
+ print >>sys.stderr, "Child was terminated by signal", -retval
+ elif retval > 0:
+ print >>sys.stderr, "Child returned", retval
+
+ if ignore_error:
+ print >>sys.stderr, "Error ignored by lilylib"
+ else:
+ if not show_progress:
+ print log[0]
+ print log[1]
+ sys.exit (1)
+
+ return abs (retval)
+
+def ossystem_system (cmd,
+ ignore_error=False,
+ progress_p=True,
+ be_verbose=False,
+ redirect_output=False,
+ log_file=None):
+
+
+ name = command_name (cmd)
+ if be_verbose:
+ show_progress = 1
+ progress (_ ("Invoking `%s\'") % cmd)
+ else:
+ progress ( _("Running %s...") % name)
+
+ retval = os.system (cmd)
+ if retval:
+ print >>sys.stderr, 'command failed:', cmd
+ if retval < 0:
+ print >>sys.stderr, "Child was terminated by signal", -retval
+ elif retval > 0:
+ print >>sys.stderr, "Child returned", retval
+
+ if ignore_error:
+ print >>sys.stderr, "Error ignored"
+ else:
+ sys.exit (1)
+
+ return abs (retval)
+
+
+system = subprocess_system
+if sys.platform == 'mingw32':
+
+ ## subprocess x-compile doesn't work.
+ system = ossystem_system
+
+def strip_extension (f, ext):
+ (p, e) = os.path.splitext (f)
+ if e == ext:
+ e = ''
+ return p + e
+
+
+def search_exe_path (name):
+ p = os.environ['PATH']
+ exe_paths = p.split (':')
+ for e in exe_paths:
+ full = os.path.join (e, name)
+ if os.path.exists (full):
+ return full
+ return None
-# Python < 1.5.2 compatibility
-#
-# On most platforms, this is equivalent to
-#`normpath(join(os.getcwd()), PATH)'. *Added in Python version 1.5.2*
-
-if os.path.__dict__.has_key ('abspath'):
- abspath = os.path.abspath
-else:
- def abspath (path):
- return os.path.normpath (os.path.join (os.getcwd (), path))
-
-if os.__dict__.has_key ('makedirs'):
- makedirs = os.makedirs
-else:
- def makedirs (dir, mode=0777):
- system ('mkdir -p %s' % dir)
-
-
-def mkdir_p (dir, mode=0777):
- if not os.path.isdir (dir):
- makedirs (dir, mode)
-
-
-environment = {}
-
-# tex needs lots of memory, more than it gets by default on Debian
-non_path_environment = {
- 'extra_mem_top' : '1000000',
- 'extra_mem_bottom' : '1000000',
- 'pool_size' : '250000',
-}
-
-def setup_environment ():
- global environment
-
- kpse = read_pipe ('kpsexpand \$TEXMF')
- texmf = re.sub ('[ \t\n]+$','', kpse)
- type1_paths = read_pipe ('kpsewhich -expand-path=\$T1FONTS')
-
- environment = {
- # TODO: * prevent multiple addition.
- # * clean TEXINPUTS, MFINPUTS, TFMFONTS,
- # as these take prevalence over $TEXMF
- # and thus may break tex run?
-
- 'TEXMF' : "{%s,%s}" % (datadir, texmf) ,
-
- # 'GS_FONTPATH' : type1_paths,
- # 'GS_LIB' : datadir + '/ps',
- 'GS_FONTPATH' : "",
- 'GS_LIB' : "",
- }
-
- # $TEXMF is special, previous value is already taken care of
- if os.environ.has_key ('TEXMF'):
- del os.environ['TEXMF']
-
- for key in environment.keys ():
- val = environment[key]
- if os.environ.has_key (key):
- val = os.environ[key] + os.pathsep + val
- os.environ[key] = val
-
- for key in non_path_environment.keys ():
- val = non_path_environment[key]
- os.environ[key] = val
def print_environment ():
- for (k,v) in os.environ.items ():
- sys.stderr.write ("%s=\"%s\"\n" % (k, v))
-
-BOUNDING_BOX_RE = '^%%BoundingBox: (-?[0-9]+) (-?[0-9]+) (-?[0-9]+) (-?[0-9]+)'
-def get_bbox (filename):
- bbox = filename + '.bbox'
- ## -sOutputFile does not work with bbox?
- cmd = 'gs -sDEVICE=bbox -q -dNOPAUSE %s -c showpage -c quit 2>%s' % \
- (filename, bbox)
- system (cmd, progress_p = 1)
- box = open (bbox).read ()
- m = re.match (BOUNDING_BOX_RE, box)
- gr = []
- if m:
- gr = map (string.atoi, m.groups ())
-
- return gr
-
-
-def make_ps_images (ps_name, resolution = 90, papersize = "a4",
- rename_page1_p = 0):
- ## FIXME
- ## have better algorithm for deciding when to crop page,
- ## and when to show full page.
- ## Better than what, and why?
-
- base = re.sub (r'\.e?ps', '', ps_name)
- header = open (ps_name).read (1024)
-
- png1 = base + '.png'
- pngn = base + '-page%d.png'
- output_file = pngn
- multi_page = re.search ('\n%%Pages: ', header)
-
- if not multi_page:
- cmd = r'''gs\
- -dEPSCrop\
- -dGraphicsAlphaBits=4\
- -dNOPAUSE\
- -dTextAlphaBits=4\
- -sDEVICE=pnggray\
- -sOutputFile='%(output_file)s'\
- -sPAPERSIZE=%(papersize)s\
- -q\
- -r%(resolution)d\
- '%(ps_name)s'\
- -c showpage\
- -c quit ''' % vars ()
- else:
- cmd = r'''gs\
- -s\
- -dGraphicsAlphaBits=4\
- -dNOPAUSE\
- -dTextAlphaBits=4\
- -sDEVICE=pnggray\
- -sOutputFile='%(output_file)s'\
- -sPAPERSIZE=%(papersize)s\
- -q\
- -r%(resolution)d\
- '%(ps_name)s'\
- -c quit''' % vars ()
-
- remove = glob.glob (png1) + glob.glob (base + '-page*.png')
- map (os.unlink, remove)
-
- status = system (cmd)
- signal = 0xf & status
- exit_status = status >> 8
-
- if status:
- remove = glob.glob (png1) + glob.glob (base + '-page*.png')
- map (os.unlink, remove)
- error (_ ("%s exited with status: %d" % ('GS', status))
- exit (1)
-
- if rename_page1_p or not multi_page:
- os.rename (pngn % 1, png1)
- files = glob.glob (png1) + glob.glob (re.sub ('%d', '*', output_file))
- return files
+ for (k,v) in os.environ.items ():
+ sys.stderr.write ("%s=\"%s\"\n" % (k, v))
+
+class NonDentedHeadingFormatter (optparse.IndentedHelpFormatter):
+ def format_heading(self, heading):
+ if heading:
+ return heading[0].upper() + heading[1:] + ':\n'
+ return ''
+ def format_option_strings(self, option):
+ sep = ' '
+ if option._short_opts and option._long_opts:
+ sep = ','
+
+ metavar = ''
+ if option.takes_value():
+ metavar = '=%s' % option.metavar or option.dest.upper()
+
+ return "%3s%s %s%s" % (" ".join (option._short_opts),
+ sep,
+ " ".join (option._long_opts),
+ metavar)
+
+ # Only use one level of indentation (even for groups and nested groups),
+ # since we don't indent the headeings, either
+ def indent(self):
+ self.current_indent = self.indent_increment
+ self.level += 1
+ def dedent(self):
+ self.level -= 1
+ if self.level <= 0:
+ self.current_indent = ''
+ self.level = 0;
+
+ def format_usage(self, usage):
+ return _("Usage: %s") % usage + '\n'
+
+ def format_description(self, description):
+ return description
+
+class NonEmptyOptionParser (optparse.OptionParser):
+ "A subclass of OptionParser that gobbles empty string arguments."
+
+ def parse_args (self, args=None, values=None):
+ options, args = optparse.OptionParser.parse_args (self, args, values)
+ return options, filter (None, args)
+
+def get_option_parser (*args, **kwargs):
+ p = NonEmptyOptionParser (*args, **kwargs)
+ p.formatter = NonDentedHeadingFormatter ()
+ p.formatter.set_parser (p)
+ return p