ext/rest.py
brancholdstable
changeset 5422 0865e1e90674
parent 5421 8167de96c523
child 5424 8ecbcbff9777
equal deleted inserted replaced
4985:02b52bf9f5f8 5422:0865e1e90674
       
     1 # copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
       
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     3 #
       
     4 # This file is part of CubicWeb.
       
     5 #
       
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
       
     7 # terms of the GNU Lesser General Public License as published by the Free
       
     8 # Software Foundation, either version 2.1 of the License, or (at your option)
       
     9 # any later version.
       
    10 #
       
    11 # logilab-common is distributed in the hope that it will be useful, but WITHOUT
       
    12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
       
    13 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
       
    14 # details.
       
    15 #
       
    16 # You should have received a copy of the GNU Lesser General Public License along
       
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
     1 """rest publishing functions
    18 """rest publishing functions
     2 
    19 
     3 contains some functions and setup of docutils for cubicweb. Provides the
    20 contains some functions and setup of docutils for cubicweb. Provides the
     4 following ReST directives:
    21 following ReST directives:
     5 
    22 
    10 
    27 
    11 * `winclude`, reference to a web documentation file (in wdoc/ directories)
    28 * `winclude`, reference to a web documentation file (in wdoc/ directories)
    12 
    29 
    13 * `sourcecode` (if pygments is installed), source code colorization
    30 * `sourcecode` (if pygments is installed), source code colorization
    14 
    31 
    15 :organization: Logilab
       
    16 :copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
       
    17 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
    18 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
       
    19 """
    32 """
    20 __docformat__ = "restructuredtext en"
    33 __docformat__ = "restructuredtext en"
    21 
    34 
    22 from cStringIO import StringIO
    35 from cStringIO import StringIO
    23 from itertools import chain
    36 from itertools import chain
    24 from logging import getLogger
    37 from logging import getLogger
    25 from os.path import join
    38 from os.path import join
    26 
    39 
    27 from docutils import statemachine, nodes, utils, io
    40 from docutils import statemachine, nodes, utils, io
    28 from docutils.core import publish_string
    41 from docutils.core import Publisher
    29 from docutils.parsers.rst import Parser, states, directives
    42 from docutils.parsers.rst import Parser, states, directives
    30 from docutils.parsers.rst.roles import register_canonical_role, set_classes
    43 from docutils.parsers.rst.roles import register_canonical_role, set_classes
    31 
    44 
    32 from logilab.mtconverter import ESC_UCAR_TABLE, ESC_CAR_TABLE, xml_escape
    45 from logilab.mtconverter import ESC_UCAR_TABLE, ESC_CAR_TABLE, xml_escape
    33 
    46 
    90 
   103 
    91     Most part of this implementation is copied from `include` directive defined
   104     Most part of this implementation is copied from `include` directive defined
    92     in `docutils.parsers.rst.directives.misc`
   105     in `docutils.parsers.rst.directives.misc`
    93     """
   106     """
    94     context = state.document.settings.context
   107     context = state.document.settings.context
       
   108     cw = context._cw
    95     source = state_machine.input_lines.source(
   109     source = state_machine.input_lines.source(
    96         lineno - state_machine.input_offset - 1)
   110         lineno - state_machine.input_offset - 1)
    97     #source_dir = os.path.dirname(os.path.abspath(source))
   111     #source_dir = os.path.dirname(os.path.abspath(source))
    98     fid = arguments[0]
   112     fid = arguments[0]
    99     for lang in chain((context._cw.lang, context.vreg.property_value('ui.language')),
   113     for lang in chain((cw.lang, cw.vreg.property_value('ui.language')),
   100                       context.config.available_languages()):
   114                       cw.vreg.config.available_languages()):
   101         rid = '%s_%s.rst' % (fid, lang)
   115         rid = '%s_%s.rst' % (fid, lang)
   102         resourcedir = context.config.locate_doc_file(rid)
   116         resourcedir = cw.vreg.config.locate_doc_file(rid)
   103         if resourcedir:
   117         if resourcedir:
   104             break
   118             break
   105     else:
   119     else:
   106         severe = state_machine.reporter.severe(
   120         severe = state_machine.reporter.severe(
   107               'Problems with "%s" directive path:\nno resource matching %s.'
   121               'Problems with "%s" directive path:\nno resource matching %s.'
   194                                                convert_whitespace=1)
   208                                                convert_whitespace=1)
   195         self.statemachine.run(inputlines, document, inliner=self.inliner)
   209         self.statemachine.run(inputlines, document, inliner=self.inliner)
   196         self.finish_parse()
   210         self.finish_parse()
   197 
   211 
   198 
   212 
       
   213 # XXX docutils keep a ref on context, can't find a correct way to remove it
       
   214 class CWReSTPublisher(Publisher):
       
   215     def __init__(self, context, settings, **kwargs):
       
   216         Publisher.__init__(self, **kwargs)
       
   217         self.set_components('standalone', 'restructuredtext', 'pseudoxml')
       
   218         self.process_programmatic_settings(None, settings, None)
       
   219         self.settings.context = context
       
   220 
       
   221 
   199 def rest_publish(context, data):
   222 def rest_publish(context, data):
   200     """publish a string formatted as ReStructured Text to HTML
   223     """publish a string formatted as ReStructured Text to HTML
   201 
   224 
   202     :type context: a cubicweb application object
   225     :type context: a cubicweb application object
   203 
   226 
   216     else:
   239     else:
   217         encoding = req.encoding
   240         encoding = req.encoding
   218         # remove unprintable characters unauthorized in xml
   241         # remove unprintable characters unauthorized in xml
   219         data = data.translate(ESC_CAR_TABLE)
   242         data = data.translate(ESC_CAR_TABLE)
   220     settings = {'input_encoding': encoding, 'output_encoding': 'unicode',
   243     settings = {'input_encoding': encoding, 'output_encoding': 'unicode',
   221                 'warning_stream': StringIO(), 'context': context,
   244                 'warning_stream': StringIO(),
   222                 # dunno what's the max, severe is 4, and we never want a crash
   245                 # dunno what's the max, severe is 4, and we never want a crash
   223                 # (though try/except may be a better option...)
   246                 # (though try/except may be a better option...)
   224                 'halt_level': 10,
   247                 'halt_level': 10,
   225                 }
   248                 }
   226     if context:
   249     if context:
   231         else:
   254         else:
   232             base_url = req.base_url()
   255             base_url = req.base_url()
   233     else:
   256     else:
   234         base_url = None
   257         base_url = None
   235     try:
   258     try:
   236         return publish_string(writer=Writer(base_url=base_url),
   259         pub = CWReSTPublisher(context, settings,
   237                               parser=CubicWebReSTParser(), source=data,
   260                               parser=CubicWebReSTParser(),
   238                               settings_overrides=settings)
   261                               writer=Writer(base_url=base_url),
       
   262                               source_class=io.StringInput,
       
   263                               destination_class=io.StringOutput)
       
   264         pub.set_source(data)
       
   265         pub.set_destination()
       
   266         res = pub.publish(enable_exit_status=None)
       
   267         # necessary for proper garbage collection, else a ref is kept somewhere in docutils...
       
   268         del pub.settings.context
       
   269         return res
   239     except Exception:
   270     except Exception:
   240         LOGGER.exception('error while publishing ReST text')
   271         LOGGER.exception('error while publishing ReST text')
   241         if not isinstance(data, unicode):
   272         if not isinstance(data, unicode):
   242             data = unicode(data, encoding, 'replace')
   273             data = unicode(data, encoding, 'replace')
   243         return xml_escape(req._('error while publishing ReST text')
   274         return xml_escape(req._('error while publishing ReST text')