File: C:/Users/fred/anaconda3/Lib/site-packages/spyder/plugins/help/utils/sphinxify.py
# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# Copyright (C) 2009 Tim Dumol <[email protected]>
# Copyright (C) 2010- Spyder Project Contributors
#
# Distributed under the terms of the Modified BSD License
# (BSD 3-clause; see NOTICE.txt in the Spyder root directory for details).
# -----------------------------------------------------------------------------
"""
Process docstrings with Sphinx.
**AUTHORS**:
* Tim Joseph Dumol (2009-09-29): Initial version.
* The Spyder Project Contributors: Several changes to make it work with Spyder.
Originally based on sagenb/misc/sphinxify.py from the
`Sage Notebook project <https://github.com/sagemath/sagenb>`_,
part of the `SageMath <https://www.sagemath.org/>`_ system.
"""
# Standard library imports
import codecs
import os
import os.path as osp
import pathlib
import shutil
import sys
from tempfile import mkdtemp
from xml.sax.saxutils import escape
# Third party imports
from docutils.utils import SystemMessage as SystemMessage
from jinja2 import Environment, FileSystemLoader
import sphinx
from sphinx.application import Sphinx
# Local imports
from spyder.config.base import (_, get_module_data_path,
get_module_source_path)
from spyder.utils import encoding
#-----------------------------------------------------------------------------
# Globals and constants
#-----------------------------------------------------------------------------
# Note: we do not use __file__ because it won't be working in the stand-alone
# version of Spyder (i.e. the py2exe or cx_Freeze build)
CONFDIR_PATH = get_module_source_path('spyder.plugins.help.utils')
CSS_PATH = osp.join(CONFDIR_PATH, 'static', 'css')
DARK_CSS_PATH = osp.join(CONFDIR_PATH, 'static', 'dark_css')
BASE_CSS_PATH = osp.join(CONFDIR_PATH, 'static', 'base_css')
JS_PATH = osp.join(CONFDIR_PATH, 'js')
# To let Debian packagers redefine the MathJax and JQuery locations so they can
# use their own packages for them. See spyder-ide/spyder#1230, comment #7.
MATHJAX_PATH = get_module_data_path('spyder',
relpath=osp.join('utils', 'help',
JS_PATH, 'mathjax'),
attr_name='MATHJAXPATH')
JQUERY_PATH = get_module_data_path('spyder',
relpath=osp.join('utils', 'help',
JS_PATH),
attr_name='JQUERYPATH')
#-----------------------------------------------------------------------------
# Utility functions
#-----------------------------------------------------------------------------
def is_sphinx_markup(docstring):
"""Returns whether a string contains Sphinx-style ReST markup."""
# this could be made much more clever
return ("`" in docstring or "::" in docstring)
def warning(message, css_path=CSS_PATH):
"""Print a warning message on the rich text view"""
env = Environment()
env.loader = FileSystemLoader(osp.join(CONFDIR_PATH, 'templates'))
warning = env.get_template("warning.html")
return warning.render(css_path=css_path, text=message)
def usage(title, message, tutorial_message, tutorial, css_path=CSS_PATH):
"""Print a usage message on the rich text view"""
env = Environment()
env.loader = FileSystemLoader(osp.join(CONFDIR_PATH, 'templates'))
usage = env.get_template("usage.html")
return usage.render(css_path=css_path, title=title, intro_message=message,
tutorial_message=tutorial_message, tutorial=tutorial)
def loading(message, loading_img, css_path=CSS_PATH):
"""Print loading message on the rich text view."""
env = Environment()
env.loader = FileSystemLoader(osp.join(CONFDIR_PATH, 'templates'))
loading = env.get_template("loading.html")
return loading.render(
css_path=css_path, loading_img=loading_img, message=message)
def generate_context(name='', argspec='', note='', math=False, collapse=False,
img_path='', css_path=CSS_PATH):
"""
Generate the html_context dictionary for our Sphinx conf file.
This is a set of variables to be passed to the Jinja template engine and
that are used to control how the webpage is rendered in connection with
Sphinx
Parameters
----------
name : str
Object's name.
note : str
A note describing what type has the function or method being
introspected
argspec : str
Argspec of the the function or method being introspected
math : bool
Turn on/off Latex rendering on the OI. If False, Latex will be shown in
plain text.
collapse : bool
Collapse sections
img_path : str
Path for images relative to the file containing the docstring
Returns
-------
A dict of strings to be used by Jinja to generate the webpage
"""
if img_path and os.name == 'nt':
img_path = img_path.replace('\\', '/')
context = \
{
# Arg dependent variables
'math_on': 'true' if math else '',
'name': name,
'argspec': argspec,
'note': note,
'collapse': collapse,
'img_path': img_path,
# Static variables
'base_css_path': BASE_CSS_PATH,
'css_path': css_path,
'js_path': JS_PATH,
'jquery_path': JQUERY_PATH,
'mathjax_path': MATHJAX_PATH,
'right_sphinx_version': '' if sphinx.__version__ < "1.1" else 'true',
'sphinx_version_2': '' if sphinx.__version__ < "2.0" else 'true',
'platform': sys.platform
}
return context
def sphinxify(docstring, context, buildername='html'):
"""
Runs Sphinx on a docstring and outputs the processed documentation.
Parameters
----------
docstring : str
a ReST-formatted docstring
context : dict
Variables to be passed to the layout template to control how its
rendered (through the Sphinx variable *html_context*).
buildername: str
It can be either `html` or `text`.
Returns
-------
An Sphinx-processed string, in either HTML or plain text format, depending
on the value of `buildername`
"""
confdir = osp.join(get_module_source_path('spyder.plugins.help.utils'))
srcdir = mkdtemp()
srcdir = encoding.to_unicode_from_fs(srcdir)
destdir = osp.join(srcdir, '_build')
temp_confdir_needed = False
if os.name == 'nt':
# Check if confdir and srcdir are in the same drive
# See spyder-ide/spyder#11762
drive_confdir = pathlib.Path(confdir).parts[0]
drive_srcdir = pathlib.Path(srcdir).parts[0]
temp_confdir_needed = drive_confdir != drive_srcdir
if temp_confdir_needed:
# TODO: This may be inefficient. Find a faster way to do it.
confdir = mkdtemp()
confdir = encoding.to_unicode_from_fs(confdir)
generate_configuration(confdir)
rst_name = osp.join(srcdir, 'docstring.rst')
if buildername == 'html':
suffix = '.html'
else:
suffix = '.txt'
output_name = osp.join(destdir, 'docstring' + suffix)
# This is needed so users can type \\ on latex eqnarray envs inside raw
# docstrings
if context['right_sphinx_version'] and context['math_on']:
docstring = docstring.replace('\\\\', '\\\\\\\\')
# Needed to prevent MathJax render the '\*' red.
# Also the '\*' seems to actually by a simple '*'
# See spyder-ide/spyder#9785
docstring = docstring.replace("\\*", "*")
# Add a class to several characters on the argspec. This way we can
# highlight them using css, in a similar way to what IPython does.
# NOTE: Before doing this, we escape common html chars so that they
# don't interfere with the rest of html present in the page
argspec = escape(context['argspec'])
for char in ['=', ',', '(', ')', '*', '**']:
argspec = argspec.replace(char,
'<span class="argspec-highlight">' + char + '</span>')
context['argspec'] = argspec
doc_file = codecs.open(rst_name, 'w', encoding='utf-8')
doc_file.write(docstring)
doc_file.close()
confoverrides = {'html_context': context}
doctreedir = osp.join(srcdir, 'doctrees')
sphinx_app = Sphinx(srcdir, confdir, destdir, doctreedir, buildername,
confoverrides, status=None, warning=None,
freshenv=True, warningiserror=False, tags=None)
try:
sphinx_app.build(None, [rst_name])
except SystemMessage:
output = _("It was not possible to generate rich text help for this "
"object.</br>"
"Please see it in plain text.")
return warning(output)
# TODO: Investigate if this is necessary/important for us
if osp.exists(output_name):
output = codecs.open(output_name, 'r', encoding='utf-8').read()
output = output.replace('<pre>', '<pre class="literal-block">')
else:
output = _("It was not possible to generate rich text help for this "
"object.</br>"
"Please see it in plain text.")
return warning(output)
if temp_confdir_needed:
shutil.rmtree(confdir, ignore_errors=True)
shutil.rmtree(srcdir, ignore_errors=True)
return output
def generate_configuration(directory):
"""
Generates a Sphinx configuration in `directory`.
Parameters
----------
directory : str
Base directory to use
"""
# conf.py file for Sphinx
conf = osp.join(get_module_source_path('spyder.plugins.help.utils'),
'conf.py')
# Docstring layout page (in Jinja):
layout = osp.join(osp.join(CONFDIR_PATH, 'templates'), 'layout.html')
os.makedirs(osp.join(directory, 'templates'))
os.makedirs(osp.join(directory, 'static'))
shutil.copy(conf, directory)
shutil.copy(layout, osp.join(directory, 'templates'))
open(osp.join(directory, '__init__.py'), 'w').write('')
open(osp.join(directory, 'static', 'empty'), 'w').write('')