From e3c683f84345051612f670d2faade7ef638b406a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Tue, 24 Mar 2009 20:05:54 +0100 Subject: Added build_manpage support. Moved all the startup logic from portato.py to portato/__init__.py --- build_manpage.py | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++++ portato.py | 90 +----------------------------------- portato/__init__.py | 110 +++++++++++++++++++++++++++++++++++++++---- setup.cfg | 3 ++ setup.py | 5 +- 5 files changed, 240 insertions(+), 99 deletions(-) create mode 100644 build_manpage.py create mode 100644 setup.cfg diff --git a/build_manpage.py b/build_manpage.py new file mode 100644 index 0000000..8bdbf2f --- /dev/null +++ b/build_manpage.py @@ -0,0 +1,131 @@ +# -*- coding: utf-8 -*- + +# +# This file is copied from http://andialbrecht.wordpress.com/2009/03/17/creating-a-man-page-with-distutils-and-optparse/ +# + +"""build_manpage command -- Generate man page from setup()""" + +import datetime +from distutils.command.build import build +from distutils.core import Command +from distutils.errors import DistutilsOptionError +import optparse + +class build_manpage(Command): + + description = 'Generate man page from setup().' + + user_options = [ + ('output=', 'O', 'output file'), + ('parser=', None, 'module path to optparser (e.g. mymod:func)'), + ] + + def initialize_options(self): + self.output = None + self.parser = None + + def finalize_options(self): + if self.output is None: + raise DistutilsOptionError('\'output\' option is required') + if self.parser is None: + raise DistutilsOptionError('\'parser\' option is required') + mod_name, func_name = self.parser.split(':') + fromlist = mod_name.split('.') + try: + mod = __import__(mod_name, fromlist=fromlist) + self._parser = getattr(mod, func_name)() + except ImportError, err: + raise + self._parser.formatter = ManPageFormatter() + self._parser.formatter.set_parser(self._parser) + self.announce('Writing man page %s' % self.output) + self._today = datetime.date.today() + + def _markup(self, txt): + return txt.replace('-', '\\-') + + def _write_header(self): + appname = self.distribution.get_name() + ret = [] + ret.append('.TH %s 1 %s\n' % (self._markup(appname), + self._today.strftime('%Y\\-%m\\-%d'))) + description = self.distribution.get_description() + if description: + name = self._markup('%s - %s' % (self._markup(appname), + description.splitlines()[0])) + else: + name = self._markup(appname) + ret.append('.SH NAME\n%s\n' % name) + synopsis = self._parser.get_usage() + if synopsis: + synopsis = synopsis.replace('%s ' % appname, '') + ret.append('.SH SYNOPSIS\n.B %s\n%s\n' % (self._markup(appname), + synopsis)) + long_desc = self.distribution.get_long_description() + if long_desc: + ret.append('.SH DESCRIPTION\n%s\n' % self._markup(long_desc)) + return ''.join(ret) + + def _write_options(self): + ret = ['.SH OPTIONS\n'] + ret.append(self._parser.format_option_help()) + return ''.join(ret) + + def _write_footer(self): + ret = [] + appname = self.distribution.get_name() + author = '%s <%s>' % (self.distribution.get_author(), + self.distribution.get_author_email()) + ret.append(('.SH AUTHORS\n.B %s\nwas written by %s.\n' + % (self._markup(appname), self._markup(author)))) + homepage = self.distribution.get_url() + ret.append(('.SH DISTRIBUTION\nThe latest version of %s may ' + 'be downloaded from\n' + '.UR %s\n.UE\n' + % (self._markup(appname), self._markup(homepage),))) + return ''.join(ret) + + def run(self): + manpage = [] + manpage.append(self._write_header()) + manpage.append(self._write_options()) + manpage.append(self._write_footer()) + stream = open(self.output, 'w') + stream.write(''.join(manpage)) + stream.close() + + +class ManPageFormatter(optparse.HelpFormatter): + + def __init__(self, + indent_increment=2, + max_help_position=24, + width=None, + short_first=1): + optparse.HelpFormatter.__init__(self, indent_increment, + max_help_position, width, short_first) + + def _markup(self, txt): + return txt.replace('-', '\\-') + + def format_usage(self, usage): + return self._markup(usage) + + def format_heading(self, heading): + if self.level == 0: + return '' + return '.TP\n%s\n' % self._markup(heading.upper()) + + def format_option(self, option): + result = [] + opts = self.option_strings[option] + result.append('.TP\n.B %s\n' % self._markup(opts)) + if option.help: + help_text = '%s\n' % self._markup(self.expand_default(option)) + result.append(help_text) + return ''.join(result) + + +build.sub_commands.append(('build_manpage', None)) + diff --git a/portato.py b/portato.py index bdf3ddb..0f0a61b 100755 --- a/portato.py +++ b/portato.py @@ -14,93 +14,7 @@ from __future__ import with_statement, absolute_import -import sys, os -import subprocess, threading -import atexit -from optparse import OptionParser, SUPPRESS_HELP - -from portato import get_listener, log, start -from portato.su import detect_su_command -from portato.helper import debug, info, error -from portato.constants import VERSION - -def main (): - start() # the first thing to do :) - - # build the parser - desc = "Portato - A Portage GUI." - usage = "%prog [options] [frontend]" - vers = "%%prog v. %s" % VERSION - - parser = OptionParser(version = vers, prog = "Portato", description = desc, usage = usage) - - parser.add_option("--shm", action = "store", nargs = 3, type="long", dest = "shm", - help = SUPPRESS_HELP) - - parser.add_option("-F", "--no-fork", "-L", action = "store_true", dest = "nofork", default = False, - help = _("do not fork off as root") + (" (%s)" % _("-L is deprecated"))) - - # run parser - (options, args) = parser.parse_args() - - # close listener at exit - atexit.register(get_listener().close) - - if options.nofork or os.getuid() == 0: # start GUI - log.start(file = True) # start logging to file - - from portato.gui import run - info("%s v. %s", _("Starting Portato"), VERSION) - - if options.shm: - get_listener().set_send(*options.shm) - else: - get_listener().set_send() - - try: - run() - except KeyboardInterrupt: - debug("Got KeyboardInterrupt.") - - else: # start us again in root modus and launch listener - - import shm_wrapper as shm - - mem = shm.create_memory(1024, permissions=0600) - sig = shm.create_semaphore(InitialValue = 0, permissions = 0600) - rw = shm.create_semaphore(InitialValue = 1, permissions = 0600) - - # start listener - lt = threading.Thread(target=get_listener().set_recv, args = (mem, sig, rw)) - lt.setDaemon(False) - lt.start() - - try: - # set DBUS_SESSION_BUS_ADDRESS to "" to make dbus work as root ;) - env = os.environ.copy() - env.update(DBUS_SESSION_BUS_ADDRESS="") - - su = detect_su_command() - if su: - debug("Using '%s' as su command.", su.bin) - cmd = su.cmd("%s --no-fork --shm %ld %ld %ld" % (sys.argv[0], mem.key, sig.key, rw.key)) - - sp = subprocess.Popen(cmd, env = env) - - # wait for process to finish - try: - sp.wait() - debug("Subprocess finished") - except KeyboardInterrupt: - debug("Got KeyboardInterrupt.") - - else: - error(_("No valid su command detected. Aborting.")) - - finally: - if lt.isAlive(): - debug("Listener is still running. Close it.") - get_listener().close() +from portato import start if __name__ == "__main__": - main() + start() diff --git a/portato/__init__.py b/portato/__init__.py index f5d7f65..e54354a 100644 --- a/portato/__init__.py +++ b/portato/__init__.py @@ -12,17 +12,16 @@ from __future__ import absolute_import -def start(): - from . import log - import gettext, locale - from portato.constants import LOCALE_DIR, APP - - # set gettext stuff - locale.setlocale(locale.LC_ALL, '') - gettext.install(APP, LOCALE_DIR, unicode = True) +import gettext, locale +import sys, os +import subprocess, threading +import atexit +from optparse import OptionParser, SUPPRESS_HELP - # start logging - log.start(file=False) +from . import log +from .constants import LOCALE_DIR, APP, VERSION +from .su import detect_su_command +from .helper import debug, info, error # listener-handling __listener = None @@ -34,3 +33,94 @@ def get_listener(): __listener = Listener() return __listener + +def get_parser (use_ = False): + # use_ defaults to False, if it is called from outside + # where gettext is not yet initialized + + if not use_: _ = lambda s : s + + desc = "Portato - A Portage GUI." + usage = "%prog [options] [frontend]" + vers = "%%prog v. %s" % VERSION + + parser = OptionParser(version = vers, prog = "Portato", description = desc, usage = usage) + + parser.add_option("--shm", action = "store", nargs = 3, type="long", dest = "shm", + help = SUPPRESS_HELP) + + parser.add_option("-F", "--no-fork", "-L", action = "store_true", dest = "nofork", default = False, + help = _("do not fork off as root") + (" (%s)" % _("-L is deprecated"))) + + return parser + +def start(): + + # set gettext stuff + locale.setlocale(locale.LC_ALL, '') + gettext.install(APP, LOCALE_DIR, unicode = True) + + # start logging + log.start(file=False) + + # run parser + (options, args) = get_parser().parse_args() + + # close listener at exit + atexit.register(get_listener().close) + + if options.nofork or os.getuid() == 0: # start GUI + log.start(file = True) # start logging to file + + from .gui import run + info("%s v. %s", _("Starting Portato"), VERSION) + + if options.shm: + get_listener().set_send(*options.shm) + else: + get_listener().set_send() + + try: + run() + except KeyboardInterrupt: + debug("Got KeyboardInterrupt.") + + else: # start us again in root modus and launch listener + + import shm_wrapper as shm + + mem = shm.create_memory(1024, permissions=0600) + sig = shm.create_semaphore(InitialValue = 0, permissions = 0600) + rw = shm.create_semaphore(InitialValue = 1, permissions = 0600) + + # start listener + lt = threading.Thread(target=get_listener().set_recv, args = (mem, sig, rw)) + lt.setDaemon(False) + lt.start() + + try: + # set DBUS_SESSION_BUS_ADDRESS to "" to make dbus work as root ;) + env = os.environ.copy() + env.update(DBUS_SESSION_BUS_ADDRESS="") + + su = detect_su_command() + if su: + debug("Using '%s' as su command.", su.bin) + cmd = su.cmd("%s --no-fork --shm %ld %ld %ld" % (sys.argv[0], mem.key, sig.key, rw.key)) + + sp = subprocess.Popen(cmd, env = env) + + # wait for process to finish + try: + sp.wait() + debug("Subprocess finished") + except KeyboardInterrupt: + debug("Got KeyboardInterrupt.") + + else: + error(_("No valid su command detected. Aborting.")) + + finally: + if lt.isAlive(): + debug("Listener is still running. Close it.") + get_listener().close() diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..739ffe6 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,3 @@ +[build_manpage] +output=doc/portato.1 +parser=portato:get_parser diff --git a/setup.py b/setup.py index 58956b6..366298a 100644 --- a/setup.py +++ b/setup.py @@ -15,6 +15,8 @@ import sys, os, os.path from distutils.core import setup from portato.constants import VERSION, DATA_DIR, ICON_DIR, PLUGIN_DIR, TEMPLATE_DIR +from build_manpage import build_manpage + def plugin_list (*args): """Creates a list of correct plugin pathes out of the arguments.""" return [("plugins/%s.py" % x) for x in args] @@ -34,5 +36,6 @@ setup(name="Portato", author = "René 'Necoro' Neumann", author_email = "necoro@necoro.net", packages = packages, - data_files = data_files + data_files = data_files, + cmdclass={'build_manpage': build_manpage} ) -- cgit v1.2.3