| 1 | ## rst_xhtml_generator.py |
|---|
| 2 | ## |
|---|
| 3 | ## Copyright (C) 2006 Yann Le Boulanger <asterix@lagaule.org> |
|---|
| 4 | ## Copyright (C) 2006 Nikos Kouremenos <kourem@gmail.com> |
|---|
| 5 | ## Copyright (C) 2006 Santiago Gala |
|---|
| 6 | ## |
|---|
| 7 | ## This program is free software; you can redistribute it and/or modify |
|---|
| 8 | ## it under the terms of the GNU General Public License as published |
|---|
| 9 | ## by the Free Software Foundation; version 2 only. |
|---|
| 10 | ## |
|---|
| 11 | ## This program is distributed in the hope that it will be useful, |
|---|
| 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 14 | ## GNU General Public License for more details. |
|---|
| 15 | ## |
|---|
| 16 | |
|---|
| 17 | try: |
|---|
| 18 | from docutils import io |
|---|
| 19 | from docutils.core import Publisher |
|---|
| 20 | from docutils.parsers.rst import roles |
|---|
| 21 | from docutils import nodes,utils |
|---|
| 22 | from docutils.parsers.rst.roles import set_classes |
|---|
| 23 | except: |
|---|
| 24 | print "Requires docutils 0.4 for set_classes to be available" |
|---|
| 25 | def create_xhtml(text): |
|---|
| 26 | return None |
|---|
| 27 | else: |
|---|
| 28 | def pos_int_validator(text): |
|---|
| 29 | """Validates that text can be evaluated as a positive integer.""" |
|---|
| 30 | result = int(text) |
|---|
| 31 | if result < 0: |
|---|
| 32 | raise ValueError("Error: value '%(text)s' " |
|---|
| 33 | "must be a positive integer") |
|---|
| 34 | return result |
|---|
| 35 | |
|---|
| 36 | def generate_uri_role( role_name, aliases, |
|---|
| 37 | anchor_text, base_url, |
|---|
| 38 | interpret_url, validator): |
|---|
| 39 | '''Creates and register a uri based "interpreted role". |
|---|
| 40 | |
|---|
| 41 | Those are similar to the RFC, and PEP ones, and take |
|---|
| 42 | role_name: |
|---|
| 43 | name that will be registered |
|---|
| 44 | aliases: |
|---|
| 45 | list of alternate names |
|---|
| 46 | anchor_text: |
|---|
| 47 | text that will be used, together with the role |
|---|
| 48 | base_url: |
|---|
| 49 | base url for the link |
|---|
| 50 | interpret_url: |
|---|
| 51 | this, modulo the validated text, will be added to it |
|---|
| 52 | validator: |
|---|
| 53 | should return the validated text, or raise ValueError |
|---|
| 54 | ''' |
|---|
| 55 | def uri_reference_role(role, rawtext, text, lineno, inliner, |
|---|
| 56 | options={}, content=[]): |
|---|
| 57 | try: |
|---|
| 58 | valid_text = validator(text) |
|---|
| 59 | except ValueError, e: |
|---|
| 60 | msg = inliner.reporter.error( e.message % dict(text=text), line=lineno) |
|---|
| 61 | prb = inliner.problematic(rawtext, rawtext, msg) |
|---|
| 62 | return [prb], [msg] |
|---|
| 63 | ref = base_url + interpret_url % valid_text |
|---|
| 64 | set_classes(options) |
|---|
| 65 | node = nodes.reference(rawtext, anchor_text + utils.unescape(text), refuri=ref, |
|---|
| 66 | **options) |
|---|
| 67 | return [node], [] |
|---|
| 68 | |
|---|
| 69 | uri_reference_role.__doc__ = """Role to make handy references to URIs. |
|---|
| 70 | |
|---|
| 71 | Use as :%(role_name)s:`71` (or any of %(aliases)s). |
|---|
| 72 | It will use %(base_url)s+%(interpret_url)s |
|---|
| 73 | validator should throw a ValueError, containing optionally |
|---|
| 74 | a %%(text)s format, if the interpreted text is not valid. |
|---|
| 75 | """ % locals() |
|---|
| 76 | roles.register_canonical_role(role_name, uri_reference_role) |
|---|
| 77 | from docutils.parsers.rst.languages import en |
|---|
| 78 | en.roles[role_name] = role_name |
|---|
| 79 | for alias in aliases: |
|---|
| 80 | en.roles[alias] = role_name |
|---|
| 81 | |
|---|
| 82 | generate_uri_role('xep-reference', ('jep', 'xep'), |
|---|
| 83 | 'XEP #', 'http://www.xmpp.org/extensions/', 'xep-%04d.html', |
|---|
| 84 | pos_int_validator) |
|---|
| 85 | generate_uri_role('gajim-ticket-reference', ('ticket','gtrack'), |
|---|
| 86 | 'Gajim Ticket #', 'http://trac.gajim.org/ticket/', '%d', |
|---|
| 87 | pos_int_validator) |
|---|
| 88 | |
|---|
| 89 | class HTMLGenerator: |
|---|
| 90 | '''Really simple HTMLGenerator starting from publish_parts. |
|---|
| 91 | |
|---|
| 92 | It reuses the docutils.core.Publisher class, which means it is *not* |
|---|
| 93 | threadsafe. |
|---|
| 94 | ''' |
|---|
| 95 | def __init__(self, |
|---|
| 96 | settings_spec=None, |
|---|
| 97 | settings_overrides=dict(report_level=5, halt_level=5), |
|---|
| 98 | config_section='general'): |
|---|
| 99 | self.pub = Publisher(reader=None, parser=None, writer=None, |
|---|
| 100 | settings=None, |
|---|
| 101 | source_class=io.StringInput, |
|---|
| 102 | destination_class=io.StringOutput) |
|---|
| 103 | self.pub.set_components(reader_name='standalone', |
|---|
| 104 | parser_name='restructuredtext', |
|---|
| 105 | writer_name='html') |
|---|
| 106 | # hack: JEP-0071 does not allow HTML char entities, so we hack our way |
|---|
| 107 | # out of it. |
|---|
| 108 | # — == u"\u2014" |
|---|
| 109 | # a setting to only emit charater entities in the writer would be nice |
|---|
| 110 | # FIXME: several are emitted, and they are explicitly forbidden |
|---|
| 111 | # in the JEP |
|---|
| 112 | # == u"\u00a0" |
|---|
| 113 | self.pub.writer.translator_class.attribution_formats['dash'] = ( |
|---|
| 114 | u'\u2014', '') |
|---|
| 115 | self.pub.process_programmatic_settings(settings_spec, |
|---|
| 116 | settings_overrides, |
|---|
| 117 | config_section) |
|---|
| 118 | |
|---|
| 119 | |
|---|
| 120 | def create_xhtml(self, text, |
|---|
| 121 | destination=None, |
|---|
| 122 | destination_path=None, |
|---|
| 123 | enable_exit_status=None): |
|---|
| 124 | ''' Create xhtml for a fragment of IM dialog. |
|---|
| 125 | We can use the source_name to store info about |
|---|
| 126 | the message.''' |
|---|
| 127 | self.pub.set_source(text, None) |
|---|
| 128 | self.pub.set_destination(destination, destination_path) |
|---|
| 129 | output = self.pub.publish(enable_exit_status=enable_exit_status) |
|---|
| 130 | # kludge until we can get docutils to stop generating (rare) |
|---|
| 131 | # entities |
|---|
| 132 | return u'\u00a0'.join(self.pub.writer.parts['fragment'].strip().split( |
|---|
| 133 | ' ')) |
|---|
| 134 | |
|---|
| 135 | Generator = HTMLGenerator() |
|---|
| 136 | |
|---|
| 137 | def create_xhtml(text): |
|---|
| 138 | return Generator.create_xhtml(text) |
|---|
| 139 | |
|---|
| 140 | |
|---|
| 141 | if __name__ == '__main__': |
|---|
| 142 | print "test 1\n", Generator.create_xhtml(""" |
|---|
| 143 | test:: |
|---|
| 144 | |
|---|
| 145 | >>> print 1 |
|---|
| 146 | 1 |
|---|
| 147 | |
|---|
| 148 | *I* like it. It is for :JEP:`71` |
|---|
| 149 | |
|---|
| 150 | this `` should trigger`` should trigger the problem. |
|---|
| 151 | |
|---|
| 152 | """) |
|---|
| 153 | print "test 2\n", Generator.create_xhtml(""" |
|---|
| 154 | *test1 |
|---|
| 155 | |
|---|
| 156 | test2_ |
|---|
| 157 | """) |
|---|
| 158 | print "test 3\n", Generator.create_xhtml(""":ticket:`316` implements :xep:`71`""") |
|---|