|
@@ -29,8 +29,10 @@ particularly our custom Sphinx extension.
|
|
|
'''
|
|
|
|
|
|
import csv
|
|
|
+import os
|
|
|
import posixpath
|
|
|
import re
|
|
|
+import StringIO
|
|
|
import urllib2
|
|
|
|
|
|
import docutils
|
|
@@ -39,9 +41,13 @@ import docutils.parsers.rst
|
|
|
import docutils.parsers.rst.roles
|
|
|
import docutils.statemachine
|
|
|
import sphinx
|
|
|
+import sphinx.errors
|
|
|
import sphinx.locale
|
|
|
import sphinx.util.docfields
|
|
|
|
|
|
+import qubes.tools
|
|
|
+
|
|
|
+
|
|
|
def fetch_ticket_info(uri):
|
|
|
'''Fetch info about particular trac ticket given
|
|
|
|
|
@@ -75,7 +81,7 @@ def ticket(name, rawtext, text, lineno, inliner, options=None, content=None):
|
|
|
options = {}
|
|
|
|
|
|
ticketno = text.lstrip('#')
|
|
|
- if not ticket.isdigit():
|
|
|
+ if not ticketno.isdigit():
|
|
|
msg = inliner.reporter.error(
|
|
|
'Invalid ticket identificator: {!r}'.format(text), line=lineno)
|
|
|
prb = inliner.problematic(rawtext, rawtext, msg)
|
|
@@ -147,6 +153,119 @@ class VersionCheck(docutils.parsers.rst.Directive):
|
|
|
return [node]
|
|
|
|
|
|
|
|
|
+def make_rst_section(heading, char):
|
|
|
+ return '{}\n{}\n\n'.format(heading, char[0] * len(heading))
|
|
|
+
|
|
|
+
|
|
|
+def prepare_manpage(command):
|
|
|
+ parser = qubes.tools.get_parser_for_command(command)
|
|
|
+ stream = StringIO.StringIO()
|
|
|
+ stream.write('.. program:: {}\n\n'.format(command))
|
|
|
+ stream.write(make_rst_section(
|
|
|
+ ':program:`{}` -- {}'.format(command, parser.description), '='))
|
|
|
+ stream.write('''.. warning::
|
|
|
+
|
|
|
+ This page was autogenerated from command-line parser. It shouldn't be 1:1
|
|
|
+ conversion, because it would add little value. Please revise it and add
|
|
|
+ more descriptive help, which normally won't fit in standard ``--help``
|
|
|
+ option.
|
|
|
+
|
|
|
+ After rewrite, please remove this admonition.\n\n''')
|
|
|
+
|
|
|
+ stream.write(make_rst_section('Synopsis', '-'))
|
|
|
+ usage = ' '.join(parser.format_usage().strip().split())
|
|
|
+ if usage.startswith('usage: '):
|
|
|
+ usage = usage[len('usage: '):]
|
|
|
+
|
|
|
+ # replace METAVARS with *METAVARS*
|
|
|
+ usage = re.sub(r'\b([A-Z]{2,})\b', r'*\1*', usage)
|
|
|
+
|
|
|
+ stream.write(':command:`{}` {}\n\n'.format(command, usage))
|
|
|
+
|
|
|
+ stream.write(make_rst_section('Options', '-'))
|
|
|
+
|
|
|
+ for action in parser._actions: # pylint: disable=protected-access
|
|
|
+ stream.write('.. option:: ')
|
|
|
+ if action.metavar:
|
|
|
+ stream.write(', '.join('{}{}{}'.format(
|
|
|
+ option,
|
|
|
+ '=' if option.startswith('--') else ' ',
|
|
|
+ action.metavar)
|
|
|
+ for option in sorted(action.option_strings)))
|
|
|
+ else:
|
|
|
+ stream.write(', '.join(sorted(action.option_strings)))
|
|
|
+ stream.write('\n\n {}\n\n'.format(action.help))
|
|
|
+
|
|
|
+ stream.write(make_rst_section('Authors', '-'))
|
|
|
+ stream.write('''\
|
|
|
+| Joanna Rutkowska <joanna at invisiblethingslab dot com>
|
|
|
+| Rafal Wojtczuk <rafal at invisiblethingslab dot com>
|
|
|
+| Marek Marczykowski <marmarek at invisiblethingslab dot com>
|
|
|
+| Wojtek Porczyk <woju at invisiblethingslab dot com>
|
|
|
+
|
|
|
+.. vim: ts=3 sw=3 et tw=80
|
|
|
+''')
|
|
|
+
|
|
|
+ return stream.getvalue()
|
|
|
+
|
|
|
+
|
|
|
+class ArgumentCheckVisitor(docutils.nodes.SparseNodeVisitor):
|
|
|
+ def __init__(self, app, command, document):
|
|
|
+ docutils.nodes.SparseNodeVisitor.__init__(self, document)
|
|
|
+
|
|
|
+ self.app = app
|
|
|
+ self.command = command
|
|
|
+ self.args = set()
|
|
|
+
|
|
|
+ try:
|
|
|
+ parser = qubes.tools.get_parser_for_command(command)
|
|
|
+ except ImportError:
|
|
|
+ self.app.warn('cannot import module for command')
|
|
|
+ self.command = None
|
|
|
+ return
|
|
|
+ except AttributeError:
|
|
|
+ raise sphinx.errors.SphinxError('cannot find parser in module')
|
|
|
+
|
|
|
+ # pylint: disable=protected-access
|
|
|
+ for action in parser._actions:
|
|
|
+ self.args.update(action.option_strings)
|
|
|
+
|
|
|
+
|
|
|
+ # pylint: disable=no-self-use,unused-argument
|
|
|
+
|
|
|
+ def visit_desc(self, node):
|
|
|
+ if not node.get('desctype', None) == 'option':
|
|
|
+ raise docutils.nodes.SkipChildren
|
|
|
+
|
|
|
+
|
|
|
+ def visit_desc_name(self, node):
|
|
|
+ if self.command is None:
|
|
|
+ return
|
|
|
+
|
|
|
+ if not isinstance(node[0], docutils.nodes.Text):
|
|
|
+ raise sphinx.errors.SphinxError('first child should be Text')
|
|
|
+
|
|
|
+ arg = str(node[0])
|
|
|
+ try:
|
|
|
+ self.args.remove(arg)
|
|
|
+ except KeyError:
|
|
|
+ raise sphinx.errors.SphinxError(
|
|
|
+ 'No such argument for {!r}: {!r}'.format(self.command, arg))
|
|
|
+
|
|
|
+
|
|
|
+ def depart_document(self, node):
|
|
|
+ if self.args:
|
|
|
+ raise sphinx.errors.SphinxError(
|
|
|
+ 'Undocumented arguments: {!r}'.format(
|
|
|
+ ', '.join(sorted(self.args))))
|
|
|
+
|
|
|
+
|
|
|
+def check_man_args(app, doctree, docname):
|
|
|
+ command = os.path.split(docname)[1]
|
|
|
+ app.info('Checking arguments for {!r}'.format(command))
|
|
|
+ doctree.walk(ArgumentCheckVisitor(app, command, doctree))
|
|
|
+
|
|
|
+
|
|
|
#
|
|
|
# this is lifted from sphinx' own conf.py
|
|
|
#
|
|
@@ -172,10 +291,19 @@ def parse_event(env, sig, signode):
|
|
|
# end of codelifting
|
|
|
#
|
|
|
|
|
|
+
|
|
|
+def break_to_pdb(app, *dummy):
|
|
|
+ if not app.config.break_to_pdb:
|
|
|
+ return
|
|
|
+ import pdb
|
|
|
+ pdb.set_trace()
|
|
|
+
|
|
|
+
|
|
|
def setup(app):
|
|
|
app.add_role('ticket', ticket)
|
|
|
app.add_config_value('ticket_base_uri',
|
|
|
'https://wiki.qubes-os.org/ticket/', 'env')
|
|
|
+ app.add_config_value('break_to_pdb', False, 'env')
|
|
|
app.add_node(versioncheck,
|
|
|
html=(visit, depart),
|
|
|
man=(visit, depart))
|
|
@@ -186,5 +314,8 @@ def setup(app):
|
|
|
app.add_object_type('event', 'event', 'pair: %s; event', parse_event,
|
|
|
doc_field_types=[fdesc])
|
|
|
|
|
|
+ app.connect('doctree-resolved', break_to_pdb)
|
|
|
+ app.connect('doctree-resolved', check_man_args)
|
|
|
+
|
|
|
|
|
|
# vim: ts=4 sw=4 et
|