def parse(filename, text=None, encoding='utf-8'): from warmice.docengine.rst.directives import register_directives register_directives() if text==None: text = open(filename).read() text = unicode(text, encoding=encoding) source_path = filename input_string = text destination_path=None stream = ErrorStream() overrides = {'encoding': 'unicode', 'warning_stream': stream} output, pub = core.publish_programmatically( source_class=io.StringInput, source=input_string, source_path=source_path, destination_class=io.NullOutput, destination=None, destination_path=destination_path, reader=None, reader_name='standalone', parser=None, parser_name='restructuredtext', writer=None, writer_name='null', settings=None, settings_spec=None, settings_overrides=overrides, config_section=None, enable_exit_status=None) return pub.writer.document
def publish_file(source_path=None, destination_path=None, settings_overrides=None ): a=MyWriter() output, pub = publish_programmatically( source_class=io.FileInput, source=None,source_path=source_path, destination_class=io.FileOutput,destination=None, destination_path=destination_path,reader=None, reader_name='standalone',parser=None, parser_name='restructuredtext',writer=a,writer_name='html', settings=None, settings_spec=None, settings_overrides=settings_overrides,config_section=None, enable_exit_status=None) return pub
def _get_html_and_pub(rst): from docutils import core, io html, pub = core.publish_programmatically( source_class=io.StringInput, source=rst, source_path=None, destination_class=io.StringOutput, destination=None, destination_path=None, reader=None, reader_name='standalone', parser=None, parser_name='restructuredtext', writer=None, writer_name='HTML', settings=None, settings_spec=None, settings_overrides=None, config_section=None, enable_exit_status=None) return html, pub
def publish2doc(source): stream = WarningStream() settings_overrides = dict(warning_stream=stream, report_level=Reporter.INFO_LEVEL) output, pub = publish_programmatically(source_class=io.StringInput, source=source, source_path=None, destination_class=io.NullOutput, destination=None, destination_path=None, reader=None, reader_name='standalone', parser=None, parser_name='restructuredtext', #writer=TransformingWriter(), writer_name=None, writer=None, writer_name='null', settings=None, settings_spec=None, settings_overrides=settings_overrides, config_section=None, enable_exit_status=None) # bind the stream to be pickled as well pub.document.stream = stream return pub.document
def convert_rst2html(self, rst): """Convert a rst to html :param rst: rst source :rtype: html souce """ output, _ = publish_programmatically( source_class=io.StringInput, source=rst, source_path=None, destination_class=io.StringOutput, destination=None, destination_path=None, reader=None, reader_name='standalone', parser=None, parser_name='restructuredtext', writer=HTML5Writer(), writer_name='null', settings=None, settings_spec=None, settings_overrides={}, config_section=None, enable_exit_status=None) return html.tostring(html.fromstring(output).find('body')).decode('utf-8')
def publish_blog_parts(source, source_path=None, source_class=io.StringInput, destination_path=None, reader=None, reader_name='standalone', parser=None, parser_name='restructuredtext', writer=None, writer_name='html', settings=None, settings_spec=None, settings_overrides={'input_encoding': 'utf8', 'doctitle_xform': 1,}, config_section=None, enable_exit_status=None, initial_header_level=4,): """ This is a copy of docutils.core.publish_parts() with additional information in the returned parts. - parts['tags'] : Tags are extracted from the meta directive. - parts['body_html_no_title'] : The body_html without the title. """ settings_overrides['initial_header_level'] = initial_header_level output, pub = core.publish_programmatically( source=source, source_path=source_path, source_class=source_class, destination_class=io.StringOutput, destination=None, destination_path=destination_path, reader=reader, reader_name=reader_name, parser=parser, parser_name=parser_name, writer=writer, writer_name=writer_name, settings=settings, settings_spec=settings_spec, settings_overrides=settings_overrides, config_section=config_section, enable_exit_status=enable_exit_status) parts = pub.writer.parts # Tags parts['tags'] = None for meta in pub.document.traverse(MetaBody.meta): if meta.get('name') == 'keywords': parts['tags'] = meta.get('content') break # Body without Title body = parts['html_body'] title = parts['html_title'] parts['html_body_no_title'] = body.replace(title,'') return parts
def internals(input_string, source_path=None, destination_path=None, input_encoding='unicode', settings_overrides=None): if settings_overrides: overrides = settings_overrides.copy() else: overrides = {} overrides['input_encoding'] = input_encoding output, pub = core.publish_programmatically( source_class=io.StringInput, source=input_string, source_path=source_path, destination_class=io.NullOutput, destination=None, destination_path=destination_path, reader=None, reader_name='standalone', parser=None, parser_name='restructuredtext', writer=None, writer_name='null', settings=None, settings_spec=None, settings_overrides=overrides, config_section=None, enable_exit_status=None) return pub.writer.document, pub
def internals(input_string, source_path=None, destination_path=None, input_encoding='unicode'): """ Return the document tree and publisher, for exploring Docutils internals. Parameters: see `html_parts()`. """ overrides = {'input_encoding': input_encoding} output, pub = core.publish_programmatically( source_class=io.StringInput, source=input_string, source_path=source_path, destination_class=io.NullOutput, destination=None, destination_path=destination_path, reader=None, reader_name='standalone', parser=None, parser_name='restructuredtext', writer=None, writer_name='null', settings=None, settings_spec=None, settings_overrides=overrides, config_section=None, enable_exit_status=None) return pub.writer.document, pub
def rst2pub(source, source_path=None, source_class=None, destination_path=None, reader=None, reader_name='standalone', parser=None, parser_name='restructuredtext', writer=None, writer_name='pseudoxml', settings=None, settings_spec=None, settings_overrides=None, config_section=None, enable_exit_status=None): """ Like docutils.core.publish_parts, but returns the publisher and sets some default settings, see `default_rst_opts`. Parameters: see `docutils.core` functions for explanation. Example: pub = rst2pub(rst_string) print doctree2dict(pub.document) """ if not have_docutils: raise ImportError('docutils library is required to use reStructuredText conversion') final_settings_overrides = default_rst_opts.copy() if settings_overrides: final_settings_overrides.update(settings_overrides) source_class = source_class or io.StringInput output, pub = publish_programmatically( source=source, source_path=source_path, source_class=source_class, destination_class=io.StringOutput, destination=None, destination_path=destination_path, reader=reader, reader_name=reader_name, parser=parser, parser_name=parser_name, writer=writer, writer_name=writer_name, settings=settings, settings_spec=settings_spec, settings_overrides=final_settings_overrides, config_section=config_section, enable_exit_status=enable_exit_status) return pub
def _get_publisher(self): output, pub = publish_programmatically( source=self.source, source_path=None, source_class=docutils.io.StringInput, destination_class=docutils.io.StringOutput, destination=None, destination_path=None, reader=None, reader_name='standalone', parser=None, parser_name='restructuredtext', writer=html4css1.Writer(), writer_name='pseudoxml', settings=None, settings_spec=None, settings_overrides=RST_SETTINGS, config_section=None, enable_exit_status=False) return pub
def check_history_headings(mod_path): history_path = os.path.join(mod_path, HISTORY_NAME) source_path = None destination_path = None with open(history_path, 'r') as f: input_string = f.read() _, pub = core.publish_programmatically( source_class=io.StringInput, source=input_string, source_path=source_path, destination_class=io.NullOutput, destination=None, destination_path=destination_path, reader=None, reader_name='standalone', parser=None, parser_name='restructuredtext', writer=None, writer_name='null', settings=None, settings_spec=None, settings_overrides={}, config_section=None, enable_exit_status=None) if pub.writer.document.children[0].rawsource == RELEASE_HISTORY_TITLE: return True else: print("Expected '{}' as first heading in HISTORY.rst".format(RELEASE_HISTORY_TITLE)) return False
def check_history_headings(mod_path): history_path = os.path.join(mod_path, HISTORY_NAME) source_path = None destination_path = None with open(history_path, 'r') as f: input_string = f.read() _, pub = core.publish_programmatically( source_class=io.StringInput, source=input_string, source_path=source_path, destination_class=io.NullOutput, destination=None, destination_path=destination_path, reader=None, reader_name='standalone', parser=None, parser_name='restructuredtext', writer=None, writer_name='null', settings=None, settings_spec=None, settings_overrides={}, config_section=None, enable_exit_status=None) # Check first heading is Release History if pub.writer.document.children[0].rawsource != RELEASE_HISTORY_TITLE: print("Expected '{}' as first heading in HISTORY.rst".format(RELEASE_HISTORY_TITLE)) return False all_versions = [t['names'][0] for t in pub.writer.document.children if t['names']] # Check that no headings contain 'unreleased'. We don't require it any more if any('unreleased' in v.lower() for v in all_versions): print("We no longer require 'unreleased' in headings. Use the appropriate version number instead.") return False # Check that the current package version has a history entry if not all_versions: print("Unable to get versions from {}. Check formatting. e.g. there should be a new line after the 'Release History' heading.".format(history_path)) return False first_version_history = all_versions[0] actual_version = subprocess.check_output('python setup.py --version'.split(), cwd=mod_path, universal_newlines=True) actual_version = actual_version.strip() if first_version_history != actual_version: print("The topmost version in {} does not match version {} defined in setup.py.".format(history_path, actual_version)) return False return True
def get_existing_options_from_rst(optfiles): """Parse an existing RST table to compile a list of existing options.""" options = {} for optfile in optfiles: input_string = open(optfile).read().replace(':ref:', '') output, pub = core.publish_programmatically( source_class=io.StringInput, source=input_string, source_path=optfile, destination_class=io.NullOutput, destination=None, destination_path='/dev/null', reader=None, reader_name='standalone', parser=None, parser_name='restructuredtext', writer=None, writer_name='null', settings=None, settings_spec=None, settings_overrides=None, config_section=None, enable_exit_status=None) doc = pub.writer.document data = dict(doc.traverse(nodes.row, include_self=False)[1:]) for a, b in data.items(): option = str(a.traverse(nodes.literal)[0].children[0]) helptext = str(b.traverse(nodes.paragraph)[0].children[0]) if option not in options or 'No help text' in options[option]: # options[option.split('=',1)[0]] = helptext options[option] = helptext.strip() return options
def asDOM( text, source_path=None, template=None, flavor=None, s5_theme_url=None, navigation=None, operation=None, ): if flavor is None: flavor = 'html' settings = dict( input_encoding='utf-8', output_encoding='utf-8', embed_stylesheet=False, stylesheet_path=htmlutil.KLUDGE_KILL_CSS, generator=False, # TODO file insertion should really be disabled # but can't do that now, as that would make the # original include directive fail.. also can't # just temporarily enable it to kludge, as that # would mean the included file sees it as fully # enabled.. will have to reimplement include. # file_insertion_enabled=0, # TODO ponder disabling raw; it allows content creators to # attack the site # raw_enabled=0, _disable_config=1, roast_operation=operation, ) if flavor == 's5': writer = s5_html.Writer() assert template is None assert s5_theme_url is not None settings.update(dict( theme=None, theme_url=s5_theme_url, current_slide=True, )) elif flavor == 'html': writer = html4css1.Writer() else: raise 'Unknown RST flavor: %r' % flavor # Docutils stores default `foo` role in global state that persists # from one parser to another. Parsing directive "default-role" # sets that, usually from s5defs.txt. To avoid infecting all # latter runs (`foo` will create <span # class="incremental">foo</span> instead of <cite>foo</cite>), we # try to contain the damage, and restore the default role to # original settings before every run. try: del roles._roles[''] except KeyError: pass html, publisher = publish_programmatically( source_class=io.StringInput, source=text, source_path=source_path, destination_class=io.StringOutput, destination=None, destination_path=None, reader=None, reader_name='standalone', parser=None, parser_name='restructuredtext', writer=writer, writer_name=None, settings=None, settings_spec=None, settings_overrides=settings, config_section=None, enable_exit_status=None) tree = minidom.parseString(html) title = htmlutil.getTitle(tree) title = htmlutil.getNodeContentsAsText(title) # kill generator meta tag htmlutil.killGeneratorMetaTags(tree) # kill stylesheet htmlutil.killLinkedStylesheet(tree) if flavor == 'html': body = htmlutil.getOnlyElementByTagName(tree, 'body') docs = htmlutil.getElementsByClass(body, 'document') if len(docs) == 1: body = docs[0] # remove the headings rst promoted to top level, # the template will take care of that for h1 in body.getElementsByTagName('h1'): if htmlutil.elementHasClass(h1, 'title'): h1.parentNode.removeChild(h1) break if template is not None: template = Template(original=body, docFactory=loaders.xmlstr(template), title=title, navigation=navigation, ) html = flat.flatten(template) tree = minidom.parseString(html) htmlutil.fixXMLTags(tree) return tree
def __init__(self, filename, encoding="utf8", raise_exception=False): """ create an instance of a blog post from a file or a string @param filename filename or string @param encoding encoding @param raise_exception to raise an exception when the blog cannot be parsed The constructor creates the following members: * title * date * keywords * categories * _filename * _raw * rst_obj: the object generated by docutils (@see cl BlogPostDirective) * pub: Publisher .. versionchanged:: 1.3 Parameter *raise_exception* was added to catch the standard error. The constructor aims at getting the text of a blog post, not interpreting it. The code used to throw warnings or errors due to unreferenced nodes. """ if os.path.exists(filename): with open(filename, "r", encoding=encoding) as f: try: content = f.read() except UnicodeDecodeError as e: raise Exception( 'unable to read filename (encoding issue):\n File "{0}", line 1'.format(filename)) from e self._filename = filename else: content = filename self._filename = None self._raw = content overrides = {} overrides['input_encoding'] = encoding overrides["out_blogpostlist"] = [] overrides["blog_background"] = False overrides["sharepost"] = None overrides.update({'doctitle_xform': True, 'initial_header_level': 2, 'warning_stream': StringIO(), 'out_blogpostlist': [], 'out_runpythonlist': [], }) config = Config(None, None, overrides=overrides, tags=None) config.blog_background = False config.sharepost = None env = BuildEnvironment(None, None, config=config) env.temp_data["docname"] = "string" overrides["env"] = env errst = sys.stderr keeperr = StringIO() sys.stderr = keeperr output, pub = publish_programmatically( source_class=docio.StringInput, source=content, source_path=None, destination_class=docio.StringOutput, destination=None, destination_path=None, reader=None, reader_name='standalone', parser=None, parser_name='restructuredtext', writer=None, writer_name='null', settings=None, settings_spec=None, settings_overrides=overrides, config_section=None, enable_exit_status=None) sys.stderr = errst all_err = keeperr.getvalue() if len(all_err) > 0: if raise_exception: raise BlogPostParseError("unable to parse a blogpost:\nERR:\n{0}\nFILE\n{1}\nCONTENT\n{2}".format( all_err, self._filename, content)) else: # we assume we just need the content, raising a warnings # might make some process fail later # warnings.warn("Raw rst was caught but unable to fully parse a blogpost:\nERR:\n{0}\nFILE\n{1}\nCONTENT\n{2}".format( # all_err, self._filename, content)) pass # document = pub.writer.document objects = pub.settings.out_blogpostlist if len(objects) != 1: raise BlogPostParseError( 'no blog post (#={1}) in\n File "{0}", line 1'.format(filename, len(objects))) post = objects[0] for k in post.options: setattr(self, k, post.options[k]) self.rst_obj = post self.pub = pub self._content = post.content
def rst2html(s, fLOG=noLOG, writer="html", keep_warnings=False, directives=None, language="en", layout='docutils', document_name="<<string>>", external_docnames=None, filter_nodes=None, new_extensions=None, update_builder=None, ret_doctree=False, load_bokeh=False, **options): """ Converts a string from :epkg:`RST` into :epkg:`HTML` format or transformed :epkg:`RST`. @param s string to convert @param fLOG logging function (warnings will be logged) @param writer ``'html'`` for :epkg:`HTML` format, ``'rst'`` for :epkg:`RST` format, ``'md'`` for :epkg:`MD` format, ``'elatex'`` for :epkg:`latex` format, ``'doctree'`` to get the doctree, *writer* can also be a tuple for custom formats and must be like ``('buider_name', builder_class)``. @param keep_warnings keep_warnings in the final HTML @param directives new directives to add (see below) @param language language @param layout ``'docutils'``, ``'sphinx'``, ``'sphinx_body'``, see below. @param document_name document name, not really important since the input is a string @param external_docnames if the string to parse makes references to other documents, if one is missing, an exception is raised. @param filter_nodes transforms the doctree before writing the results (layout must be 'sphinx'), the function takes a doctree as a single parameter @param new_extensions additional extension to setup @param update_builder update the builder after it is instantiated @param ret_doctree returns the doctree @param load_bokeh load :epkg:`bokeh` extensions, disabled by default as it takes a few seconds @param options :epkg:`Sphinx` options see `Render math as images <http://www.sphinx-doc.org/en/stable/ext/math.html#module-sphinx.ext.imgmath>`_, a subset of options is used, see @see fn default_sphinx_options. By default, the theme (option *html_theme*) will ``'basic'``. @return HTML format *directives* is None or a list of 2 or 5-uple: * a directive name (mandatory) * a directive class: see `Sphinx Directive <http://sphinx-doc.org/extdev/tutorial.html>`_, see also @see cl RunPythonDirective as an example (mandatory) * a docutils node: see @see cl runpython_node as an example * two functions: see @see fn visit_runpython_node, @see fn depart_runpython_node as an example The parameter *layout* specify the kind of HTML you need. * ``'docutils'``: very simple :epkg:`HTML`, style is not included, recursive directives are not processed (recursive means they modify the doctree). The produced :epkg:`HTML` only includes the body (no :epkg:`HTML` header). * ``'sphinx'``: in memory :epkg:`sphinx`, the produced :epkg:`HTML` includes the header, it is also recursive as directives can modify the doctree. * ``'sphinx_body'``: same as ``'sphinx'`` but only the body is returned. If the writer is a tuple, it must be a 2-uple ``(builder_name, builder_class)``. However, the builder class must contain an attribute ``_writer_class`` with the associated writer. The builcer class must also implement a method ``iter_pages`` which enumerates all written pages: ``def iter_pages(self) -> Dict[str,str]`` where the key is the document name and the value is its content. .. exref:: :title: How to test a Sphinx directive? The following code defines a simple directive definedbased on an existing one. It also defined what to do if a new node is inserted in the documentation. :: from docutils import nodes from pyquickhelper.helpgen import rst2html class runpythonthis_node(nodes.Structural, nodes.Element): pass class RunPythonThisDirective (RunPythonDirective): runpython_class = runpythonthis_node def visit_node(self, node): self.body.append("<p><b>visit_node</b></p>") def depart_node(self, node): self.body.append("<p><b>depart_node</b></p>") content = ''' test a directive ================ .. runpythonthis:: print("this code shoud appear" + "___") '''.replace(" ", "") # to remove spaces at the beginning of the line tives = [ ("runpythonthis", RunPythonThisDirective, runpythonthis_node, visit_node, depart_node) ] html = rst2html(content, writer="html", keep_warnings=True, directives=tives) Unfortunately, this functionality is only tested on :epkg:`Python` 3. It might not work on :epkg:`Python` 2.7. The function produces files if the document contains latex converted into image. .. faqref:: :title: How to get more about latex errors? :index: latex :epkg:`Sphinx` is not easy to use when it comes to debug latex expressions. I did not find an easy way to read the error returned by latex about a missing bracket or an unknown command. I finally added a short piece of code in ``sphinx.ext.imgmath.py`` just after the call to the executable indicated by *imgmath_latex* :: if b'...' in stdout or b'LaTeX Error' in stdout: print(self.builder.config.imgmath_latex_preamble) print(p.returncode) print("################") print(latex) print("..........") print(stdout.decode("ascii").replace("\\r", "")) print("-----") print(stderr) It displays the output if an error happened. .. faqref:: :title: How to hide command line window while compiling latex? :lid: command line window :epkg:`Sphinx` calls :epkg:`latex` through command line. On :epkg:`Windows`, a command line window can annoyingly show up anytime a formula is compile. The following can be added to hide it: :: startupinfo = STARTUPINFO() startupinfo.dwFlags |= STARTF_USESHOWWINDOW And ``, startupinfo=startupinfo`` must be added to lines ``p = Popen(...``. By default, the function now interprets :epkg:`Sphinx` directives and not only *docutils* ones. Parameter *directives* adds a directive before parsing the :epkg:`RST`. The function is more consistent. Format ``rst`` is available as well as custom builders. .. versionchanged:: 1.8 New nodes are now optional in *directives*. Markdown format was added. Parameters *ret_doctree*, *load_bokeh* were added. """ # delayed import to speed up time def _get_MockSphinxApp(): from .sphinxm_mock_app import MockSphinxApp return MockSphinxApp MockSphinxApp = _get_MockSphinxApp() if 'html_theme' not in options: options['html_theme'] = 'basic' defopt = default_sphinx_options(**options) if "master_doc" not in defopt: defopt["master_doc"] = document_name if writer in ('latex', 'elatex') and 'latex_documents' not in defopt: latex_documents = [(document_name, ) * 5] defopt['latex_documents'] = latex_documents if writer in ["custom", "sphinx", "HTMLWriterWithCustomDirectives", "html"]: mockapp, writer, title_names = MockSphinxApp.create("sphinx", directives, confoverrides=defopt, new_extensions=new_extensions, load_bokeh=load_bokeh, fLOG=fLOG) writer_name = "HTMLWriterWithCustomDirectives" elif writer in ("rst", "md", "latex", "elatex", 'text', 'doctree'): writer_name = writer mockapp, writer, title_names = MockSphinxApp.create(writer, directives, confoverrides=defopt, new_extensions=new_extensions, load_bokeh=load_bokeh, fLOG=fLOG) elif isinstance(writer, tuple): # We extect something like ("builder_name", builder_class) writer_name = writer mockapp, writer, title_names = MockSphinxApp.create(writer, directives, confoverrides=defopt, new_extensions=new_extensions, load_bokeh=load_bokeh, fLOG=fLOG) else: raise ValueError( "Unexpected writer '{0}', should be 'rst' or 'html' or 'md' or 'elatex' or 'text'.".format(writer)) if writer is None and directives is not None and len(directives) > 0: raise NotImplementedError( "The writer must not be null if custom directives will be added, check the documentation of the fucntion.") # delayed import to speed up time from sphinx.environment import default_settings settings_overrides = default_settings.copy() settings_overrides["warning_stream"] = StringIO() settings_overrides["master_doc"] = document_name settings_overrides["source"] = document_name settings_overrides["contentsname"] = document_name settings_overrides.update({k: v[0] for k, v in mockapp.new_options.items()}) # next settings_overrides.update(defopt) config = mockapp.config config.blog_background = True config.blog_background_page = False config.sharepost = None if hasattr(writer, "add_configuration_options"): writer.add_configuration_options(mockapp.new_options) for k in {'outdir', 'imagedir', 'confdir', 'doctreedir'}: setattr(writer.builder, k, settings_overrides.get(k, '')) if update_builder: update_builder(writer.builder) env = mockapp.env if env is None: raise ValueError("No environment was built.") env.temp_data["docname"] = document_name env.temp_data["source"] = document_name mockapp.builder.env.temp_data["docname"] = document_name mockapp.builder.env.temp_data["source"] = document_name settings_overrides["env"] = env lang = languages.get_language(language) for name in title_names: if name not in lang.labels: lang.labels[name] = TITLES[language][name] for k, v in sorted(settings_overrides.items()): fLOG("[rst2html] {0}={1}{2}".format( k, v, " --- added" if hasattr(config, k) else "")) for k, v in sorted(settings_overrides.items()): if hasattr(writer.builder.config, k) and writer.builder.config[k] != v: writer.builder.config[k] = v _, pub = core.publish_programmatically(source=s, source_path=None, destination_path=None, writer=writer, writer_name=writer_name, settings_overrides=settings_overrides, source_class=StringInput, destination_class=StringOutput, destination=None, reader=None, reader_name='standalone', parser=None, parser_name='restructuredtext', settings=None, settings_spec=None, config_section=None, enable_exit_status=False) doctree = pub.document if filter_nodes is not None: if layout == "docutils" and writer != "doctree": raise ValueError( "filter_nodes is not None, layout must not be 'docutils'") filter_nodes(doctree) mockapp.finalize(doctree, external_docnames=external_docnames) parts = pub.writer.parts if not keep_warnings: if isinstance(parts["whole"], list): # Not html. exp = "".join(parts["whole"]) else: exp = re.sub( '(<div class="system-message">(.|\\n)*?</div>)', "", parts["whole"]) else: if isinstance(parts["whole"], list): exp = "".join(parts["whole"]) else: exp = parts["whole"] if ret_doctree: return doctree if layout == "docutils": return exp else: page = None pages = [] main = ("/{0}.m.html".format(document_name), "/{0}.m.{1}".format(document_name, writer_name), document_name) if not hasattr(writer.builder, "iter_pages"): raise AttributeError( "Class '{0}' must have a method 'iter_pages' which returns a dictionary.".format(writer.builder)) contents = [] for k, v in writer.builder.iter_pages(): pages.append(k) contents.append(v) if k in main: page = v break if page is None and len(contents) == 1: page = contents[0] if page is None: raise ValueError( "No page contents was produced, only '{0}'.".format(pages)) if layout == "sphinx": if isinstance(page, str): return page else: return "\n".join(page) elif layout == "sphinx_body": lines = page.replace('</head>', '</head>\n').split("\n") keep = [] begin = False for line in lines: s = line.strip(" \n\r") if s == "</body>": begin = False if begin: keep.append(line) if s == "<body>": begin = True res = "\n".join(keep) return res else: raise ValueError( "Unexpected value for layout '{0}'".format(layout))
doctest.append(' ' + line + '\n') doctest.append(' """\n') doctest.append('\n') #raw = docutils.nodes.doctest_block(content,'\n'.join(content)) #return [raw] code_block.arguments = (1,0,0) code_block.options = {'language' : docutils.parsers.rst.directives.unchanged } code_block.content = 1 docutils.parsers.rst.directives.register_directive('code-block', code_block ) publish_programmatically(source_class=docutils.io.FileInput, source=None, source_path=input, destination_class=docutils.io.FileOutput, destination=None, destination_path=None, reader=None, reader_name='standalone', parser=None, parser_name='restructuredtext', writer=NullWriter(), writer_name=None, settings=None, settings_spec=None, settings_overrides=None, config_section=None, enable_exit_status=None) # wow 8-) f = sys.stdout doctest_head = """#!/usr/bin/env python\n\n# Doctest for %s document\n""" doctest_stub = """ def _test(): import doctest doctest.testmod() if __name__ == "__main__": _test() """
def __init__(self, filename, encoding="utf8"): """ create an instance of a blog post from a file or a string @param filename filename or string @param encoding encoding The constructor creates the following members: * title * date * keywords * categories * _filename * _raw * rst_obj: the object generated by docutils (@see cl BlogPostDirective) * pub: Publisher """ if os.path.exists(filename): with open(filename, "r", encoding=encoding) as f: try: content = f.read() except UnicodeDecodeError as e: raise Exception( 'unable to read filename (encoding issue):\n File "{0}", line 1'.format(filename)) from e self._filename = filename else: content = filename self._filename = None self._raw = content overrides = {} overrides['input_encoding'] = encoding overrides["out_blogpostlist"] = [] overrides["blog_background"] = False output, pub = publish_programmatically( source_class=docio.StringInput, source=content, source_path=None, destination_class=docio.StringOutput, destination=None, destination_path=None, reader=None, reader_name='standalone', parser=None, parser_name='restructuredtext', writer=None, writer_name='null', settings=None, settings_spec=None, settings_overrides=overrides, config_section=None, enable_exit_status=None) #document = pub.writer.document objects = pub.settings.out_blogpostlist if len(objects) != 1: raise BlogPostPareError( 'no blog post (#={1}) in\n File "{0}", line 1'.format(filename, len(objects))) post = objects[0] for k in post.options: setattr(self, k, post.options[k]) self.rst_obj = post self.pub = pub self._content = post.content
def __init__(self, filename, encoding='utf-8-sig', raise_exception=False, extensions=None, **kwargs_overrides): """ Creates an instance of a blog post from a file or a string. :param filename: filename or string :param encoding: encoding :param raise_exception: to raise an exception when the blog cannot be parsed :param extensions: list of extension to use to parse the content of the blog, if None, it will consider a default list (see @see cl BlogPost and @see fn get_default_extensions) :param kwargs_overrides: additional parameters for :epkg:`sphinx` The constructor creates the following members: * title * date * keywords * categories * _filename * _raw * rst_obj: the object generated by docutils (@see cl BlogPostDirective) * pub: Publisher Parameter *raise_exception* catches the standard error. Option `:process:` of command `.. runpython::` should be used within a blog post to avoid having the same process use sphinx at the same time. """ if os.path.exists(filename): with open(filename, "r", encoding=encoding) as f: try: content = f.read() except UnicodeDecodeError as e: raise RuntimeError( 'Unable to read filename (encoding issue):\n ' 'File "{0}", line 1'.format(filename)) from e self._filename = filename else: content = filename self._filename = None self._raw = content overrides = {} overrides["out_blogpostlist"] = [] overrides["blog_background"] = True overrides["blog_background_page"] = False overrides["sharepost"] = None overrides['epkg_dictionary'] = get_epkg_dictionary() overrides.update(kwargs_overrides) overrides.update({ # 'warning_stream': StringIO(), 'out_blogpostlist': [], 'out_runpythonlist': [], 'master_doc': 'stringblog' }) if "extensions" not in overrides: if extensions is None: # To avoid circular references. from . import get_default_extensions # By default, we do not load bokeh extension (slow). extensions = get_default_extensions(load_bokeh=False) overrides["extensions"] = extensions from ..helpgen.sphinxm_mock_app import MockSphinxApp app = MockSphinxApp.create(confoverrides=overrides) env = app[0].env config = env.config if 'blog_background' not in config: raise AttributeError( "Unable to find 'blog_background' in config:\n{0}".format( "\n".join(sorted(config.values)))) if 'blog_background_page' not in config: raise AttributeError( "Unable to find 'blog_background_page' in config:\n{0}".format( "\n".join(sorted(config.values)))) if 'epkg_dictionary' in config: if len(config.epkg_dictionary) > 0: overrides['epkg_dictionary'] = config.epkg_dictionary else: overrides['epkg_dictionary'] = get_epkg_dictionary() env.temp_data["docname"] = "stringblog" overrides["env"] = env config.add('doctitle_xform', True, False, bool) config.add('initial_header_level', 2, False, int) config.add('input_encoding', encoding, False, str) keepout = StringIO() keeperr = StringIO() with redirect_stdout(keepout): with redirect_stderr(keeperr): _, pub = publish_programmatically( source_class=docio.StringInput, source=content, source_path=None, destination_class=docio.StringOutput, destination=None, destination_path=None, reader=None, reader_name='standalone', parser=None, parser_name='restructuredtext', writer=None, writer_name='null', settings=None, settings_spec=None, settings_overrides=overrides, config_section=None, enable_exit_status=None) all_err = keeperr.getvalue() if len(all_err) > 0: lines = all_err.strip(' \n\r').split('\n') lines = [ _ for _ in lines if ("in epkg_dictionary" not in _ and "to be local relative or absolute" not in _) ] std = keepout.getvalue().strip('\n\r\t ') if len(lines) > 0 and raise_exception: raise BlogPostParseError( "Unable to parse a blogpost:\n[sphinxerror]-F\n{0}" "\nFILE\n{1}\nCONTENT\n{2}\n--OUT--\n{3}".format( all_err, self._filename, content, keepout.getvalue())) if len(lines) > 0: print(all_err) if len(std) > 3: print(std) else: for _ in all_err.strip(' \n\r').split('\n'): print(" ", _) if len(std) > 3: print(std) # we assume we just need the content, raising a warnings # might make some process fail later # warnings.warn("Raw rst was caught but unable to fully parse # a blogpost:\n[sphinxerror]-H\n{0}\nFILE\n{1}\nCONTENT\n{2}".format( # all_err, self._filename, content)) # document = pub.writer.document objects = pub.settings.out_blogpostlist if len(objects) != 1: raise BlogPostParseError( 'no blog post (#={1}) in\n File "{0}", line 1'.format( filename, len(objects))) post = objects[0] for k in post.options: setattr(self, k, post.options[k]) self.rst_obj = post self.pub = pub self._content = post.content
def get_expectations(input_string): """ Converts a .rst document into a list of :py:class:`Expectation`. Expectation can be expressed in single reStructuredText documents that serve as documentation for the plugin and also as the tests. The :py:class:`PluginTestRunner` understands :py:class:`Expectation` objects and will run the plugin to assert that a plugin behaves as the expectation describes. A couple of custom directives allow the expectation to be expressed like this :: Will check a filename ===================== The filename checker plugin will look at new files being added to a repository and make sure they follow a simple set of rules. .. plugin-settings:: message_type = warn underscore_in_filenames = false capital_letters_in_filenames = false These settings will not allow underscores or capital letters in filenames. The ``message_type`` is set to ``warn`` which will notify the user that problems occur but will not prevent them from being committed. .. expectation:: :from: 01 :to: 02 ▾ File name checker ✓ New file looks OK (matches filename rules) Ran 1 plugin Info 1 Warn 0 Stop 0 """ warning_stream = StringIO() overrides = { 'input_encodings': 'unicode', 'warning_stream': warning_stream} output, pub = core.publish_programmatically( source_class=io.StringInput, source=input_string, source_path=None, destination_class=io.NullOutput, destination=None, destination_path=None, reader=None, reader_name='standalone', parser=None, parser_name='restructuredtext', writer=None, writer_name='null', settings=None, settings_spec=None, settings_overrides=overrides, config_section=None, enable_exit_status=None) if warning_stream.getvalue(): raise ExpectationParsingError(warning_stream.getvalue()) flat_nodes = pub.writer.document.traverse() for i, node in enumerate(flat_nodes): if isinstance(node, expectations_node): # We have an expectation expectation = node settings = None # Hunt for settings for hunt_i in range(i - 1, 0, -1): # pragma: no branch contender = flat_nodes[hunt_i] if isinstance(contender, plugin_settings_node): # We found a plugin setting in the same area as the # expectation, let's use this. settings = contender.settings break if isinstance(contender, DOCUTILS_DIFFERENT_SECTION_NODES): # This is a structural element, anything past this is in a # different section so we will stop. break yield Expectation( range=expectation.range, settings=settings, output=expectation.rawsource)
def rst2html(s, fLOG=noLOG, writer="sphinx", keep_warnings=False, directives=None, language="en", warnings_log=False, **options): """ converts a string into HTML format @param s string to converts @param fLOG logging function (warnings will be logged) @param writer *None* or an instance such as ``HTMLWriterWithCustomDirectives()`` or ``custom`` or ``sphinx`` @param keep_warnings keep_warnings in the final HTML @param directives new directives to add (see below) @param language language @param options Sphinx options see `Render math as images <http://www.sphinx-doc.org/en/stable/ext/math.html#module-sphinx.ext.imgmath>`_, a subset of options is used, see @see fn default_sphinx_options @param warnings_log send warnings to log (True) or to the warning stream(False) @return HTML format *directives* is None or a list of 5-uple: * a directive name * a directive class: see `Sphinx Directive <http://sphinx-doc.org/extdev/tutorial.html>`_, see also @see cl RunPythonDirective as an example * a docutils node: see @see cl runpython_node as an example * two functions: see @see fn visit_runpython_node, @see fn depart_runpython_node as an example .. exref:: :title: How to test a Sphinx directive? The following code defines a simple directive defined based on an existing one. It also defined what to do if a new node is inserted in the documentation. :: from docutils import nodes from pyquickhelper.helpgen import rst2html class runpythonthis_node(nodes.Structural, nodes.Element): pass class RunPythonThisDirective (RunPythonDirective): runpython_class = runpythonthis_node def visit_node(self, node): self.body.append("<p><b>visit_node</b></p>") def depart_node(self, node): self.body.append("<p><b>depart_node</b></p>") content = ''' test a directive ================ .. runpythonthis:: print("this code shoud appear" + "___") '''.replace(" ", "") # to remove spaces at the beginning of the line tives = [ ("runpythonthis", RunPythonThisDirective, runpythonthis_node, visit_node, depart_node) ] html = rst2html(content, fLOG=fLOG, writer="custom", keep_warnings=True, directives=tives) Unfortunately, this functionality is only tested on Python 3. It might only work on Python 2.7. The function produces files if the document contains latex converted into image. .. faqref:: :title: How to get more about latex errors? :index: latex Sphinx is not easy to use when it comes to debug latex expressions. I did not find an easy way to read the error returned by latex about a missing bracket or an unknown command. I fianlly added a short piece of code in ``sphinx.ext.imgmath.py`` just after the call to the executable indicated by *imgmath_latex* :: if b'...' in stdout or b'LaTeX Error' in stdout: print(self.builder.config.imgmath_latex_preamble) print(p.returncode) print("################") print(latex) print("..........") print(stdout.decode("ascii").replace("\r", "")) print("-----") print(stderr) It displays the output if an error happened. .. faqref:: :title: How to hide command line window while compiling latex? :lid: command line window Sphinx calls latex through command line. On Windows, a command line window can annoyingly show up anytime a formula is compile. The following line can be added to hide it: :: startupinfo = STARTUPINFO() startupinfo.dwFlags |= STARTF_USESHOWWINDOW And ``, startupinfo=startupinfo`` must be added to lines ``p = Popen(...``. .. versionadded:: 1.0 .. versionchanged:: 1.3 Parameters *writer*, *keep_warnings* were added to specifiy a custom writer and to keep the warnings. By default, the function now interprets *Sphinx* directives and not only *docutils* ones. Parameter *directives* was added to add a directive before parsing the RST. .. versionchanged:: 1.4 Add directives *todoext*, *todo*, *mathdef*, *blocref*, *faqref*,*nbref*, *exref*, parameter *language* was added. Add directives *graphviz*, *math*. Parse more extensive Sphinx syntax. """ _nbeq = [0, None] def custom_html_visit_displaymath(self, node): if not hasattr(node, "number"): node["number"] = None try: return html_visit_displaymath(self, node) except AttributeError as e: if "math_number_all" in str(e) and sys.version_info[:2] <= (2, 7): # Python 2.7 produces the following error: # AttributeError: No such config value: math_number_all # we skip return [] else: raise e class MockSphinxApp: """ Mock Sphinx application """ def __init__(self, writer, app): self.app = app self.new_options = {} self.writer = writer self.mapping = {"<class 'sphinx.ext.todo.todo_node'>": "todo", "<class 'sphinx.ext.graphviz.graphviz'>": "graphviz", "<class 'sphinx.ext.mathbase.math'>": "math", "<class 'sphinx.ext.mathbase.displaymath'>": "displaymath", "<class 'sphinx.ext.mathbase.eqref'>": "eqref", "<class 'matplotlib.sphinxext.only_directives.html_only'>": "only", "<class 'matplotlib.sphinxext.only_directives.latex_only'>": "only", } self.mapping_connect = {} self.config = Config(None, None, overrides={}, tags=None) self.confdir = "." self.doctreedir = "." self.builder = writer.builder self.domains = {} self._events = {} def add_directive(self, name, cl, *args, **options): doc_directives.register_directive(name, cl) self.mapping[str(cl)] = name self.app.add_directive(name, cl, *args, **options) def add_role(self, name, cl): doc_roles.register_canonical_role(name, cl) self.mapping[str(cl)] = name self.app.add_role(name, cl) def add_mapping(self, name, cl): self.mapping[str(cl)] = name def add_config_value(self, name, default, rebuild, types=()): if name in self.config.values: raise Exception('Config value %r already present' % name) if rebuild in (False, True): rebuild = rebuild and 'env' or '' self.new_options[name] = (default, rebuild, types) self.config.values[name] = (default, rebuild, types) def get_default_values(self): return {k: v[0] for k, v in self.new_options.items()} def add_node(self, clnode, html=None, latex=None, text=None, man=None, texinfo=None, override=True): if override and html is not None: name = str(clnode) if "displaymath" in name: self.writer.connect_directive_node( self.mapping[name], custom_html_visit_displaymath, html[1]) else: self.writer.connect_directive_node( self.mapping[name], html[0], html[1]) def connect(self, node, func): self.mapping_connect[node] = func self.app.connect(node, func) def add_domain(self, domain): if domain.name in self.domains: raise Exception( 'domain %s already registered' % domain.name) self.domains[domain.name] = domain def add_event(self, name): if name in self._events: raise Exception('Event %s already present' % name) self._events[name] = '' def emit(self, event, *args): return self.app.emit(event, *args) title_names = [] if writer in ["custom", "sphinx"]: writer = HTMLWriterWithCustomDirectives() writer_name = 'pseudoxml' mockapp = MockSphinxApp(writer, writer.app) from sphinx.ext.graphviz import setup as setup_graphviz from sphinx.ext.imgmath import setup as setup_math, html_visit_displaymath from sphinx.ext.todo import setup as setup_todo # directives from pyquickhelper setup_blog(mockapp) setup_runpython(mockapp) setup_sharenet(mockapp) setup_todoext(mockapp) setup_bigger(mockapp) setup_githublink(mockapp) setup_runpython(mockapp) setup_mathdef(mockapp) setup_blocref(mockapp) setup_faqref(mockapp) setup_exref(mockapp) setup_nbref(mockapp) # directives from sphinx setup_graphviz(mockapp) setup_math(mockapp) setup_todo(mockapp) # don't move this import to the beginning of file # it changes matplotlib backend from matplotlib.sphinxext.plot_directive import setup as setup_plot from matplotlib.sphinxext.only_directives import setup as setup_only setup_plot(mockapp) setup_only(mockapp) # titles title_names.append("todoext_node") title_names.append("todo_node") title_names.append("mathdef_node") title_names.append("blocref_node") title_names.append("faqref_node") title_names.append("nbref_node") title_names.append("exref_node") else: writer_name = 'html' mockapp = MockSphinxApp(None) if writer is None and directives is not None and len(directives) > 0: raise NotImplementedError( "the writer must not be null if custom directives will be added, check the documentation of the fucntion") if directives is not None: for tu in directives: if len(tu) != 5: raise ValueError( "directives is a list of tuple with 5 elements, check the documentation") name, cl, node, f1, f2 = tu doc_directives.register_directive(name, cl) # not necessary # nodes._add_node_class_names([node.__name__]) writer.connect_directive_node(node.__name__, f1, f2) settings_overrides = default_settings.copy() settings_overrides.update({k: v[0] for k, v in mockapp.new_options.items()}) # next defopt = default_sphinx_options(**options) settings_overrides.update(defopt) warning_stringio = defopt["warning_stream"] _nbeq[1] = warning_stringio config = mockapp.config config.init_values(fLOG) config.blog_background = False config.sharepost = None writer.add_configuration_options(mockapp.new_options) for k in {'outdir', 'imagedir', 'confdir', 'doctreedir'}: setattr(writer.builder, k, settings_overrides[k]) env = BuildEnvironment(None, None, config=config) env.temp_data["docname"] = "string" mockapp.builder.env.temp_data["docname"] = "string" settings_overrides["env"] = env lang = languages.get_language(language) for name in title_names: if name not in lang.labels: lang.labels[name] = TITLES[language][name] for k, v in sorted(settings_overrides.items()): fLOG("[rst2html] {0}={1}{2}".format( k, v, " --- added" if hasattr(config, k) else "")) for k, v in sorted(settings_overrides.items()): if hasattr(writer.builder.config, k) and writer.builder.config[k] != v: writer.builder.config[k] = v # something is screwing with sphinx or docutils, it is due to # direct call to nbconvert or sphinx # raise an exception for unknown role pending_xref output, pub = core.publish_programmatically(source=s, source_path=None, destination_path=None, writer=writer, writer_name=writer_name, settings_overrides=settings_overrides, source_class=StringInput, destination_class=StringOutput, destination=None, reader=None, reader_name='standalone', parser=None, parser_name='restructuredtext', settings=None, settings_spec=None, config_section=None, enable_exit_status=False) doctree = pub.document mockapp.emit('doctree-read', doctree) parts = pub.writer.parts warnval = settings_overrides["warning_stream"].getvalue() if warnval is not None and len(warnval) > 0: if warnings_log: fLOG(warnval) else: warnings.warn(warnval) if not keep_warnings: exp = re.sub( '(<div class="system-message">(.|\\n)*?</div>)', "", parts["whole"]) else: exp = parts["whole"] return exp
app = GBlogApplication() blog_pages = [] simple_pages = [] for root, dirs, files in os.walk(content_path): for f in files: if f.endswith('.rst'): path = os.path.join(root, f) created, year, month = path_to_timestamp(path) modified = int(os.path.getmtime(path)) output, pub = publish_programmatically( source_class=io.FileInput, source=None, source_path=path, destination_class=io.StringOutput, destination=None, destination_path=None, reader=None, reader_name='standalone', parser=None, parser_name='restructuredtext', writer=None, writer_name='html', settings=None, settings_spec=None, settings_overrides={ 'template': rel('templates/html_writer.txt'), 'initial_header_level': 1, 'doctitle_xform': False}, config_section=None, enable_exit_status=False) content = output.strip() soup = BeautifulSoup(output) header = soup.find('h1') if not header: continue if created: content = blog_page_template.generate( content=content, header=header.text,
def ProcessStructs(filenameList, noOverwrite, outputPrefix): global VERBOSE, TARGET headerFilename = outputPrefix + ".h" codeFilename = outputPrefix + ".c" publishFilename = outputPrefix + ".htm" excelFilename = outputPrefix + ".tsv" instanceList = [] codeHeader = """ /***************************************** THIS FILE IS GENERATED AUTOMATICALLY. PLEASE DO NOT MODIFY DIRECTLY. GENERATED BY: StructDeclare GENERATED ON: %s ******************************************/ """ % time.asctime() EmitCode(codeHeader) EmitHeader(codeHeader) EmitHeader('#ifndef __STRUCTS_INC__\n#define __STRUCTS_INC__\n\n') EmitHeader('typedef unsigned int uint32_t;\ntypedef int int32_t;\n\n') EmitHeader("unsigned char *GetMemberPtr(int addr);\n"); EmitHeader("#ifndef STRUCTS_TARGET_SIDE\n") EmitHeader("char *GetMemberLabel(int addr);\n"); EmitHeader("char *GetStructLabel(int addr);\n"); EmitHeader("#endif\n\n") EmitCode('#include "%s"\n\n' % os.path.basename(headerFilename)) EmitCode(""" unsigned char *GetMemberPtr(int addr) { int structAddr; structAddr = (addr >> 8) & 0x7f; if (structAddr > 0 && structAddr < N_INSTANCES) return (unsigned char *)(gInstanceTable[structAddr]) + (addr & 0xff); else return 0; } #ifndef STRUCTS_TARGET_SIDE char *GetMemberLabel(int addr) { int structAddr; structAddr = (addr >> 8) & 0x7f; if (structAddr > 0 && structAddr < N_INSTANCES) return gInstanceLabelTableList[structAddr][(addr & 0xff) >> 2]; else return 0; } char *GetStructLabel(int addr) { int structAddr; structAddr = (addr >> 8) & 0x7f; if (structAddr > 0 && structAddr < N_INSTANCES) return gInstanceLabelTable[(addr >> 8) & 0x7f]; else return 0; } #endif """ ) if VERBOSE: print "* Target: '%s'" % TARGET for filename in filenameList: if VERBOSE: print "* Reading input file: '%s'" % filename if not os.path.exists(filename): print >> sys.stderr, "**** Input file '%s' does not exists ! Exiting !" % filename sys.exit(6) structs = ReadVarFile(filename) for struct in structs: structName, instances, variables = struct if VERBOSE: print "* Adding Structure Definition: '%s'" % structName AddStructureDefinition(structName, variables) AddInstances(structName, instances, variables) instanceList.extend(instances) if VERBOSE: print "* Adding Instance Table" AddInstanceTable(instanceList) EmitHeader("#endif\n\n") EmitCode("\n") SaveString(headerString, headerFilename, noOverwrite) SaveString(codeString, codeFilename, noOverwrite) #SaveString(excelString, excelFilename) if VERBOSE: print "* Saving output file '%s'" % publishFilename core.publish_programmatically(source_class = io.StringInput, source = publishString, source_path = None, destination_class = io.FileOutput, destination = None, destination_path = publishFilename, reader = None, reader_name = 'standalone', parser = None, parser_name = 'restructuredtext', writer = None, writer_name = 'html', settings = None, settings_spec = None, settings_overrides = None, config_section = None, enable_exit_status = None )