dochelpers.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. #!/usr/bin/python2 -O
  2. # -*- coding: utf-8 -*-
  3. '''Documentation helpers
  4. This module contains classes and functions which help to mainain documentation,
  5. particulary our custom Sphinx extension.
  6. '''
  7. import csv
  8. import posixpath
  9. import sys
  10. import urllib2
  11. import docutils
  12. import docutils.nodes
  13. import docutils.parsers.rst
  14. import docutils.parsers.rst.roles
  15. import docutils.statemachine
  16. import sphinx.locale
  17. def fetch_ticket_info(uri):
  18. '''Fetch info about particular trac ticket given
  19. :param str uri: URI at which ticket resides
  20. :rtype: mapping
  21. :raises: urllib2.HTTPError
  22. '''
  23. data = urllib2.urlopen(uri + '?format=csv').read()
  24. reader = csv.reader((line + '\n' for line in data.split('\r\n')),
  25. quoting=csv.QUOTE_MINIMAL, quotechar='"')
  26. return dict(zip(*((cell.decode('utf-8') for cell in row) for row in list(reader)[:2])))
  27. def ticket(name, rawtext, text, lineno, inliner, options={}, content=[]):
  28. '''Link to qubes ticket
  29. :param str name: The role name used in the document
  30. :param str rawtext: The entire markup snippet, with role
  31. :param str text: The text marked with the role
  32. :param int lineno: The line noumber where rawtext appearn in the input
  33. :param docutils.parsers.rst.states.Inliner inliner: The inliner instance that called this function
  34. :param options: Directive options for customisation
  35. :param content: The directive content for customisation
  36. '''
  37. ticket = text.lstrip('#')
  38. if not ticket.isdigit():
  39. msg = inliner.reporter.error('Invalid ticket identificator: {!r}'.format(text), line=lineno)
  40. prb = inliner.problematic(rawtext, rawtext, msg)
  41. return [prb], [msg]
  42. app = inliner.document.settings.env.app
  43. uri = posixpath.join(app.config.ticket_base_uri, ticket)
  44. try:
  45. info = fetch_ticket_info(uri)
  46. except urllib2.HTTPError, e:
  47. msg = inliner.reporter.error('Error while fetching ticket info: {!s}'.format(e), line=lineno)
  48. prb = inliner.problematic(rawtext, rawtext, msg)
  49. return [prb], [msg]
  50. docutils.parsers.rst.roles.set_classes(options)
  51. node = docutils.nodes.reference(
  52. rawtext,
  53. '#{} ({})'.format(ticket, info['summary']),
  54. refuri=uri,
  55. **options)
  56. return [node], []
  57. class versioncheck(docutils.nodes.warning): pass
  58. def visit(self, node):
  59. self.visit_admonition(node, 'version')
  60. def depart(self, node):
  61. self.depart_admonition(node)
  62. sphinx.locale.admonitionlabels['version'] = 'Version mismatch'
  63. class VersionCheck(docutils.parsers.rst.Directive):
  64. '''Directive versioncheck
  65. Check if current version (from ``conf.py``) equals version specified as
  66. argument. If not, generate warning.'''
  67. has_content = True
  68. required_arguments = 1
  69. optional_arguments = 0
  70. final_argument_whitespace = True
  71. option_spec = {}
  72. def run(self):
  73. current = self.state.document.settings.env.app.config.version
  74. version = self.arguments[0]
  75. if current == version:
  76. return []
  77. text = ' '.join('''This manual page was written for version **{}**, but
  78. current version at the time when this page was generated is **{}**.
  79. This may or may not mean that page is outdated or has
  80. inconsistencies.'''.format(version, current).split())
  81. node = versioncheck(text)
  82. node['classes'] = ['admonition', 'warning']
  83. self.state.nested_parse(docutils.statemachine.StringList([text]),
  84. self.content_offset, node)
  85. return [node]
  86. def setup(app):
  87. app.add_role('ticket', ticket)
  88. app.add_config_value('ticket_base_uri', 'https://wiki.qubes-os.org/ticket/', 'env')
  89. app.add_node(versioncheck,
  90. html=(visit, depart),
  91. man=(visit, depart))
  92. app.add_directive('versioncheck', VersionCheck)
  93. # vim: ts=4 sw=4 et