From 4ca72a8d531a7a794b1973a01f792836bb42ed18 Mon Sep 17 00:00:00 2001 From: Brian Dolbec Date: Sat, 3 Jan 2015 10:13:18 -0800 Subject: Initial py2man pkg for auto-generating our man pages --- py2man/__init__.py | 1 + py2man/manpages.py | 172 +++++++++++++++++++++++++++++++++++++++++++++++++++++ py2man/options.py | 93 +++++++++++++++++++++++++++++ 3 files changed, 266 insertions(+) create mode 100644 py2man/__init__.py create mode 100644 py2man/manpages.py create mode 100644 py2man/options.py diff --git a/py2man/__init__.py b/py2man/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/py2man/__init__.py @@ -0,0 +1 @@ + diff --git a/py2man/manpages.py b/py2man/manpages.py new file mode 100644 index 0000000..2490fcc --- /dev/null +++ b/py2man/manpages.py @@ -0,0 +1,172 @@ +# +#-*- coding:utf-8 -*- + + +import os +from datetime import datetime + +from .options import LONG_OPTIONS, SHORT_OPTS + + +ActionStr = '.BR gkeys-%s (1),' + +ExampleHeader = '''.SH Example''' + +BreakStr = '''.br +%s''' + +SubCmdStr = '''.IP %(cmd)s +%(cmd-desc)s''' + +SubCmdHdr = '.SH \\ %s' + +class ManPage(object): + + def __init__(self, prog, version, template, path): + self.prog = prog + self.version = version + self.template = template + self.path = path + + + @staticmethod + def gen_opts(options): + _opts = list() + for opt in options: + _opts.append(SHORT_OPTS.get(opt)) + return _opts + + + @staticmethod + def gen_optsStr(firstline, data, opts): + indent = ' ' + escapes = 15 + wrapl = 72 + escapes + output = [] + line = firstline.rstrip('%(opts)s') % data + ll = len(line) + l1 = True + for opt in opts: + if (ll + len(SHORT_OPTS[opt])) < wrapl: + line = line + '%s ' % SHORT_OPTS[opt] + ll = len(line) + else: + if l1: + output.append(line) + l1 = False + else: + output.append(BreakStr % line) + line = indent + '%s ' % SHORT_OPTS[opt] + ll = len(line) + return '\n'.join(output) + + + @staticmethod + def gen_actions(actions): + acts = [] + for act in actions: + if not act.startswith("--"): + acts.append(ActionStr % act) + return '\n'.join(acts) + + + @staticmethod + def gen_options(options): + _opts = [] + for opt in options: + _opts.append(LONG_OPTIONS[opt]) + return '\n'.join(_opts) + + + @staticmethod + def gen_example(text): + example = [] + if text: + for line in text.split('\n'): + if line and line[0] in [' ']: + example.append(line) + else: + example.append(BreakStr % line) + return '\n'.join(example) + + + @staticmethod + def gen_subcmd(cmds): + #print(cmds.values()) + output = [] + for cmd in list(cmds): + print(cmd) + if cmd.startswith('--'): + output.append(SubCmdHdr % cmd.strip('-').upper()) + else: + output.append(SubCmdStr % {'cmd': cmd, 'cmd-desc': cmds[cmd]}) + return '\n'.join(output) + + + def make_subpage(self, action, Action_Map, actions): + '''Create and saves one sub-command man page using the + classes template definition setting''' + actions.remove(action) + # remove the help group separators + actions = [x for x in actions if not x.startswith("---")] + data = {} + data['prog'] = self.prog + data['version'] = self.version + data['date'] = datetime.strftime(datetime.today(),'%B %d, %Y') + data['action'] = action + data['actions'] = self.gen_actions(actions) + data['options'] = self.gen_options(Action_Map[action]['options']) + data['desc'] = Action_Map[action]['desc'] + data['long_desc'] = Action_Map[action]['long_desc'] + if Action_Map[action]['example']: + data['example'] = self.gen_example(Action_Map[action]['example']) + data['exampleheader'] = ExampleHeader + else: + data['example'] = '' + data['exampleheader'] = '' + doc = [] + for line in self.template.split('\n'): + if '%(opts)s' in line: + doc.append(self.gen_optsStr( + line, data, Action_Map[action]['options'])) + else: + doc.append(line % data) + filepath = os.path.join(self.path, "%s-%s.1" % (self.prog, action)) + with open(filepath, 'w', encoding='utf-8') as man: + man.write('\n'.join(doc)) + + + def make_subpages(self, Action_Map, actions): + '''Create man pages for all sub-commands listed + + @param prog: string of the base application command + @param version: string to embed in the man pages + @param Action_Map: Dictionary of sub-command actions and other data + @param actions: list of keys in Action_Map to generate pages for + @param location: string, path to save the newly created man pages + ''' + for action in actions: + self.make_subpage(action, Action_Map, actions) + + + def make_prog(self, prog_map): + data = {} + data['prog'] = self.prog + data['version'] = self.version + data['date'] = datetime.strftime(datetime.today(),'%B %d, %Y') + data['actions'] = self.gen_actions(list(prog_map['sub-cmds'])) + data['options'] = self.gen_options(prog_map['options']) + data['desc'] = prog_map['desc'] + data['long_desc'] = prog_map['long_desc'] + data['sub-cmds'] = self.gen_subcmd(prog_map['sub-cmds']) + doc = [] + for line in self.template.split('\n'): + doc.append(line % data) + filepath = os.path.join(self.path, "%s.1" % (self.prog)) + with open(filepath, 'w', encoding='utf-8') as man: + man.write('\n'.join(doc)) + + def read_template(self, path, filename): + filepath = os.path.join(path, filename) + with open(filepath, 'r', encoding='utf-8') as template: + self.template = template.read() diff --git a/py2man/options.py b/py2man/options.py new file mode 100644 index 0000000..1beb132 --- /dev/null +++ b/py2man/options.py @@ -0,0 +1,93 @@ +# +#-*- coding:utf-8 -*- + +from collections import OrderedDict + + +LONG_OPTIONS = OrderedDict({ + 'help': '''.IP "-h, --help" +show this help message and exit''', + 'status': '''.IP "-A, --status" +Toggles the active status of a member for LDAP searches''', + 'all': '''.IP "-a, --all" +Toggles matching all input arguments for searches''', + 'category': '''.IP "-C \\fICATEGORY\\fR, --category \\fICATEGORY" +The category name of the seed file being added to. +.br +This name must be listed in the gkeys.conf file's +[seeds], [seedurls] and [verify-seeds] sections''', + 'cleankey': '''.IP " --clean-key" +Clean the key from the keyring due to failures.''', + 'cleanseed': '''.IP " --clean-seed" +Clean the seed from the seedfile due to failures. +.br +Used during binary keyring release creation.''', + 'config': '''.IP "-c \\fICONFIG\\fR, --config \\fICONFIG\\fR" +The path to an alternate config file''', + 'debug': '''.IP "-D, --debug \\fI{WARNING,INFO,FATAL,NOTSET,WARN,DEBUG,ERROR,CRITICAL}\\fR" +The logging level to set for the logfile''', + 'dest': '''.IP "-d \\fIDESTINATION\\fR, --dest \\fIDESTINATION" +The category name of the seed file being added to.''', + 'exact': '''.IP "-e, --exact" +Use CASE matching in searches''', + 'file': '''.IP "-F \\fIFILENAME\\fR, --file \\fIFILENAME" +The path/URL to use for the (signed) file''', + '1file': '''.IP "-F \\fIFILENAME\\fR, --file \\fIFILENAME" +The path/URL to use for the (signed) file''', + 'fingerprint': '''.IP "-f \\fIFINGERPRINT\\fR, --fingerprint \\fIFINGERPRINT" +The fingerprint(s) of the the key(s) or subkey(s)''', + 'gpgsearch': '''.IP "-g, --gpgsearch" +Do a gpg search operation, rather than a gkey search''', + 'homedir': '''.IP "-H \\fIHOMEDIR\\fR, --file \\fIHOMEDIR" +The destination for the generated key''', + 'keyid': '''.IP "-i \\fIKEYID\\fR, --keyid \\fIKEYID" +The long keyid of the gpg key to search for''', + 'keyring': '''.IP "-k \\fIKEYRING\\fR, --keyring \\fIKEYRING" +The name of the keyring to use for verification, etc.''', + 'keys': '''.IP "-K \\fIKEYS\\fR, --keys \\fIKEYS" +The fingerprint(s) of the primary keys in the keyring.''', + 'mail': '''.IP "-m \\fIEMAIL\\fR, --mail \\fIEMAIL" +The email address to search for or use.''', + 'nick': '''.IP "-n \\fINICK\\fR, --nick \\fINICK" +The nick of the user whose gkey seed is being added''', + 'name': '''.IP "-N \\fINAME\\fR, --name \\fINAME" +The name of the user whose gkey seed is being added''', + 'keydir': '''.IP "-r \\fIKEYDIR\\fR, --keydir \\fIKEYDIR" +The key directory the key is to be installed to''', + 'signature': '''.IP "-s \\fISIGNATURE\\fR, --signature \\fISIGNATURE" +The path/URL to use for the signature.''', + 'spec': '''.IP "-S \\fISPEC\\fR, --psec \\fISPEC" +The spec file to use from the gkeys-gen.conf file.''', + 'timestamp': '''.IP "-t, --timestamp" +Turn on timestamp use.''', + 'uid': '''.IP "-u \\fIUID\\fR, --uid \\fIUID" +The user id(s) (and email) of the key(s) being added (optional)''', +}) + +SHORT_OPTS = OrderedDict({ + 'help': '[\\fB\\-h\\fR]', + 'status': '[\\fB\\-A\\fR]', + 'all': '[\\fB\\-a\\fR]', + 'category': '[\\fB\\-C\\fR \\fICATEGORY\\fR]', + 'cleankey': '[\\fB\\-\\-cleankey\\fR]', + 'cleanseed': '[\\fB\\-\\-cleanseed\\fR]', + 'dest': '[\\fB\\-d\\fR \\fIDESTINATION\\fR]', + 'exact': '[\\fB\\-e\\fR]', + 'file': '[\\fB\\-F\\fR \\fIFILENAME\\fR]', + '1file': '[\\fB\\-F\\fR \\fIFILENAME\\fR]', + 'fingerprint': '[\\fB\\-f\\fR \\fIFINGERPRINT\\fR [\\fIFINGERPRINT\\fR ...]]', + 'gpgsearch': '[\\fB\\-g\\fR]', + 'homedir': '[\\fB\\-H\\fR \\fIHOMEDIR\\fR]', + 'keyid': '[\\fB\\-i\\fR \\fIKEYID\\fR [\\fIKEYID\\fR ...]]', + 'keyring': '[\\fB\\-k\\fR \\fIKEYRING\\fR]', + 'keys': '[\\fB\\-K\\fR [\\fIKEYS\\fR [\\fIKEYS\\fR ...]]]', + 'mail': '[\\fB\\-m\\fR \\fIEMAIL\\fR]', + 'nick': '[\\fB\\-n\\fR \\fINICK\\fR]', + 'name': '[\\fB\\-N\\fR [\\fINAME\\fR [\\fINAME\\fR ...]]]', + '1name': '[\\fB\\-N\\fR \\fINAME\\fR]', + 'keydir': '[\\fB\\-r\\fR \\fIKEYDIR\\fR]', + 'signature': '[\\fB\\-s\\fR \\fISIGNATURE\\fR]', + 'spec': '[\\fB\\-S\\fR \\fISPEC\\fR]', + 'timestamp': '[\\fB\\-t\\fR]', + 'uid': '[\\fB\\-u\\fR [\\fIUID\\fR [\\fIUID\\fR ...]]]', +}) -- cgit v1.2.3-65-gdbad