2014-11-20 15:20:49 +01:00
|
|
|
#!/usr/bin/python2 -O
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
'''Documentation helpers
|
|
|
|
|
2014-12-18 14:36:09 +01:00
|
|
|
This module contains classes and functions which help to maintain documentation,
|
|
|
|
particularly our custom Sphinx extension.
|
2014-11-20 15:20:49 +01:00
|
|
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
import csv
|
|
|
|
import posixpath
|
2014-12-09 18:34:00 +01:00
|
|
|
import re
|
2014-11-20 15:20:49 +01:00
|
|
|
import sys
|
|
|
|
import urllib2
|
|
|
|
|
|
|
|
import docutils
|
2014-11-21 14:47:40 +01:00
|
|
|
import docutils.nodes
|
|
|
|
import docutils.parsers.rst
|
2014-11-20 15:20:49 +01:00
|
|
|
import docutils.parsers.rst.roles
|
2014-11-21 14:47:40 +01:00
|
|
|
import docutils.statemachine
|
2014-12-09 18:34:00 +01:00
|
|
|
import sphinx
|
2014-11-21 14:47:40 +01:00
|
|
|
import sphinx.locale
|
2014-12-09 18:34:00 +01:00
|
|
|
import sphinx.util.docfields
|
2014-11-20 15:20:49 +01:00
|
|
|
|
|
|
|
def fetch_ticket_info(uri):
|
|
|
|
'''Fetch info about particular trac ticket given
|
|
|
|
|
|
|
|
:param str uri: URI at which ticket resides
|
|
|
|
:rtype: mapping
|
|
|
|
:raises: urllib2.HTTPError
|
|
|
|
'''
|
|
|
|
|
|
|
|
data = urllib2.urlopen(uri + '?format=csv').read()
|
|
|
|
reader = csv.reader((line + '\n' for line in data.split('\r\n')),
|
|
|
|
quoting=csv.QUOTE_MINIMAL, quotechar='"')
|
|
|
|
|
|
|
|
return dict(zip(*((cell.decode('utf-8') for cell in row) for row in list(reader)[:2])))
|
|
|
|
|
|
|
|
|
|
|
|
def ticket(name, rawtext, text, lineno, inliner, options={}, content=[]):
|
|
|
|
'''Link to qubes ticket
|
|
|
|
|
|
|
|
:param str name: The role name used in the document
|
|
|
|
:param str rawtext: The entire markup snippet, with role
|
|
|
|
:param str text: The text marked with the role
|
2014-12-18 14:36:09 +01:00
|
|
|
:param int lineno: The line number where rawtext appears in the input
|
2014-11-20 15:20:49 +01:00
|
|
|
:param docutils.parsers.rst.states.Inliner inliner: The inliner instance that called this function
|
|
|
|
:param options: Directive options for customisation
|
|
|
|
:param content: The directive content for customisation
|
|
|
|
'''
|
|
|
|
|
|
|
|
ticket = text.lstrip('#')
|
|
|
|
if not ticket.isdigit():
|
|
|
|
msg = inliner.reporter.error('Invalid ticket identificator: {!r}'.format(text), line=lineno)
|
|
|
|
prb = inliner.problematic(rawtext, rawtext, msg)
|
|
|
|
return [prb], [msg]
|
|
|
|
|
|
|
|
app = inliner.document.settings.env.app
|
|
|
|
uri = posixpath.join(app.config.ticket_base_uri, ticket)
|
|
|
|
try:
|
|
|
|
info = fetch_ticket_info(uri)
|
|
|
|
except urllib2.HTTPError, e:
|
|
|
|
msg = inliner.reporter.error('Error while fetching ticket info: {!s}'.format(e), line=lineno)
|
|
|
|
prb = inliner.problematic(rawtext, rawtext, msg)
|
|
|
|
return [prb], [msg]
|
|
|
|
|
|
|
|
docutils.parsers.rst.roles.set_classes(options)
|
|
|
|
|
|
|
|
node = docutils.nodes.reference(
|
|
|
|
rawtext,
|
|
|
|
'#{} ({})'.format(ticket, info['summary']),
|
|
|
|
refuri=uri,
|
|
|
|
**options)
|
|
|
|
|
|
|
|
return [node], []
|
|
|
|
|
|
|
|
|
2014-11-21 14:47:40 +01:00
|
|
|
class versioncheck(docutils.nodes.warning): pass
|
|
|
|
|
|
|
|
def visit(self, node):
|
|
|
|
self.visit_admonition(node, 'version')
|
|
|
|
|
|
|
|
def depart(self, node):
|
|
|
|
self.depart_admonition(node)
|
|
|
|
|
|
|
|
sphinx.locale.admonitionlabels['version'] = 'Version mismatch'
|
|
|
|
|
|
|
|
|
|
|
|
class VersionCheck(docutils.parsers.rst.Directive):
|
|
|
|
'''Directive versioncheck
|
|
|
|
|
|
|
|
Check if current version (from ``conf.py``) equals version specified as
|
|
|
|
argument. If not, generate warning.'''
|
|
|
|
|
|
|
|
has_content = True
|
|
|
|
required_arguments = 1
|
|
|
|
optional_arguments = 0
|
|
|
|
final_argument_whitespace = True
|
|
|
|
option_spec = {}
|
|
|
|
|
|
|
|
def run(self):
|
|
|
|
current = self.state.document.settings.env.app.config.version
|
|
|
|
version = self.arguments[0]
|
|
|
|
|
|
|
|
if current == version:
|
|
|
|
return []
|
|
|
|
|
|
|
|
text = ' '.join('''This manual page was written for version **{}**, but
|
|
|
|
current version at the time when this page was generated is **{}**.
|
|
|
|
This may or may not mean that page is outdated or has
|
|
|
|
inconsistencies.'''.format(version, current).split())
|
|
|
|
|
|
|
|
node = versioncheck(text)
|
|
|
|
node['classes'] = ['admonition', 'warning']
|
|
|
|
|
|
|
|
self.state.nested_parse(docutils.statemachine.StringList([text]),
|
|
|
|
self.content_offset, node)
|
|
|
|
return [node]
|
|
|
|
|
|
|
|
|
2014-12-09 18:34:00 +01:00
|
|
|
#
|
|
|
|
# this is lifted from sphinx' own conf.py
|
|
|
|
#
|
|
|
|
|
2015-01-12 15:48:17 +01:00
|
|
|
event_sig_re = re.compile(r'([a-zA-Z-:<>]+)\s*\((.*)\)')
|
2014-12-09 18:34:00 +01:00
|
|
|
|
|
|
|
def parse_event(env, sig, signode):
|
|
|
|
m = event_sig_re.match(sig)
|
|
|
|
if not m:
|
|
|
|
signode += sphinx.addnodes.desc_name(sig, sig)
|
|
|
|
return sig
|
|
|
|
name, args = m.groups()
|
|
|
|
signode += sphinx.addnodes.desc_name(name, name)
|
|
|
|
plist = sphinx.addnodes.desc_parameterlist()
|
|
|
|
for arg in args.split(','):
|
|
|
|
arg = arg.strip()
|
|
|
|
plist += sphinx.addnodes.desc_parameter(arg, arg)
|
|
|
|
signode += plist
|
|
|
|
return name
|
|
|
|
|
|
|
|
#
|
|
|
|
# end of codelifting
|
|
|
|
#
|
|
|
|
|
2014-11-20 15:20:49 +01:00
|
|
|
def setup(app):
|
|
|
|
app.add_role('ticket', ticket)
|
|
|
|
app.add_config_value('ticket_base_uri', 'https://wiki.qubes-os.org/ticket/', 'env')
|
2014-11-21 14:47:40 +01:00
|
|
|
app.add_node(versioncheck,
|
|
|
|
html=(visit, depart),
|
|
|
|
man=(visit, depart))
|
|
|
|
app.add_directive('versioncheck', VersionCheck)
|
|
|
|
|
2014-12-09 18:34:00 +01:00
|
|
|
fdesc = sphinx.util.docfields.GroupedField('parameter', label='Parameters',
|
|
|
|
names=['param'], can_collapse=True)
|
|
|
|
app.add_object_type('event', 'event', 'pair: %s; event', parse_event,
|
|
|
|
doc_field_types=[fdesc])
|
|
|
|
|
2014-11-20 15:20:49 +01:00
|
|
|
|
|
|
|
# vim: ts=4 sw=4 et
|