import sys, os, sphinx
SAGE_ROOT = os.environ['SAGE_ROOT']
SAGE_DOC = os.path.join(SAGE_ROOT, 'devel/sage/doc')
def get_doc_abspath(path):
"""
return the absolute path from a SAGE_DOC relative path
"""
return os.path.abspath(os.path.join(SAGE_DOC, path))
sys.path.append(get_doc_abspath('common'))
extensions = ['sage_autodoc', 'sphinx.ext.graphviz',
'sphinx.ext.inheritance_diagram', 'sphinx.ext.todo',
'sphinx.ext.extlinks']
if 'SAGE_DOC_JSMATH' in os.environ:
extensions.append('sphinx.ext.jsmath')
else:
extensions.append('sphinx.ext.pngmath')
jsmath_path = 'jsmath_sage.js'
templates_path = [os.path.join(SAGE_DOC, 'common/templates'), 'templates']
source_suffix = '.rst'
master_doc = 'index'
project = u""
copyright = u'2005--2011, The Sage Development Team'
from sage.version import version
release = version
exclude_trees = ['.build']
default_role = 'math'
pygments_style = 'sphinx'
graphviz_dot = 'dot'
inheritance_graph_attrs = { 'rankdir' : 'BT' }
inheritance_node_attrs = { 'height' : 0.5, 'fontsize' : 12, 'shape' : 'oval' }
inheritance_edge_attrs = {}
todo_include_todos = True
intersphinx_mapping = {
'http://docs.python.org/': get_doc_abspath('common/python.inv')}
def set_intersphinx_mappings(app):
"""
Add reference's objects.inv to intersphinx if not compiling reference
"""
app.config.intersphinx_mapping = intersphinx_mapping
refpath = 'output/html/en/reference/'
if not app.srcdir.endswith('reference'):
app.config.intersphinx_mapping[get_doc_abspath(refpath)] = get_doc_abspath(refpath+'objects.inv')
pythonversion = sys.version.split(' ')[0]
extlinks = {
'python': ('http://docs.python.org/release/'+pythonversion+'/%s', ''),
'trac': ('http://trac.sagemath.org/%s', 'trac ticket #'),
'wikipedia': ('http://en.wikipedia.org/wiki/%s', 'Wikipedia article ')}
html_theme = 'sage'
html_theme_options = {}
if 'SAGE_DOC_JSMATH' in os.environ:
from sage.misc.latex_macros import sage_jsmath_macros_easy
html_theme_options['jsmath_macros'] = sage_jsmath_macros_easy
from sage.misc.package import is_package_installed
html_theme_options['jsmath_image_fonts'] = is_package_installed('jsmath-image-fonts')
html_theme_path = [os.path.join(SAGE_DOC, 'common/themes')]
html_favicon = 'favicon.ico'
html_static_path = [os.path.join(SAGE_DOC, 'common/static'), 'static']
if 'SAGE_DOC_JSMATH' in os.environ:
from pkg_resources import Requirement, working_set
sagenb_path = working_set.find(Requirement.parse('sagenb')).location
jsmath_static = os.path.join(sagenb_path, 'sagenb', 'data', 'jsmath')
html_static_path.insert(0, jsmath_static)
html_split_index = True
latex_elements = {}
latex_elements['inputenc'] = '\\usepackage[utf8x]{inputenc}'
latex_elements['utf8extra'] = ''
latex_documents = []
latex_elements['preamble'] = '\usepackage{amsmath}\n\usepackage{amssymb}\n'
from sage.misc.latex_macros import sage_latex_macros
try:
pngmath_latex_preamble
except NameError:
pngmath_latex_preamble = ""
for macro in sage_latex_macros:
latex_elements['preamble'] += macro + '\n'
pngmath_latex_preamble += macro + '\n'
def process_docstring_aliases(app, what, name, obj, options, docstringlines):
"""
Change the docstrings for aliases to point to the original object.
"""
basename = name.rpartition('.')[2]
if hasattr(obj, '__name__') and obj.__name__ != basename:
docstringlines[:] = ['See :obj:`%s`.' % name]
def process_directives(app, what, name, obj, options, docstringlines):
"""
Remove 'nodetex' and other directives from the first line of any
docstring where they appear.
"""
if len(docstringlines) == 0:
return
first_line = docstringlines[0]
directives = [ d.lower() for d in first_line.split(',') ]
if 'nodetex' in directives:
docstringlines.pop(0)
def process_docstring_cython(app, what, name, obj, options, docstringlines):
"""
Remove Cython's filename and location embedding.
"""
if len(docstringlines) <= 1:
return
first_line = docstringlines[0]
if first_line.startswith('File:') and '(starting at' in first_line:
docstringlines.pop(0)
docstringlines.pop(0)
def process_docstring_module_title(app, what, name, obj, options, docstringlines):
"""
Removes the first line from the beginning of the module's docstring. This
corresponds to the title of the module's documentation page.
"""
if what != "module":
return
title_removed = False
while len(docstringlines) > 1 and not title_removed:
if docstringlines[0].strip() != "":
title_removed = True
docstringlines.pop(0)
while len(docstringlines) > 1:
if docstringlines[0].strip() == "":
docstringlines.pop(0)
else:
break
skip_picklability_check_modules = [
'sage.misc.latex',
'sage.misc.explain_pickle',
'__builtin__',
]
def check_nested_class_picklability(app, what, name, obj, skip, options):
"""
Print a warning if pickling is broken for nested classes.
"""
import types
if hasattr(obj, '__dict__') and hasattr(obj, '__module__'):
module = sys.modules[obj.__module__]
for (nm, v) in obj.__dict__.iteritems():
if (isinstance(v, (type, types.ClassType)) and
v.__name__ == nm and
v.__module__ == module.__name__ and
getattr(module, nm, None) is not v and
v.__module__ not in skip_picklability_check_modules):
app.warn('Pickling of nested class %r is probably broken. '
'Please set __metaclass__ of the parent class to '
'sage.misc.nested_class.NestedClassMetaclass.' % (
v.__module__ + '.' + name + '.' + nm))
def skip_member(app, what, name, obj, skip, options):
"""
To suppress Sphinx warnings / errors, we
- Don't include [aliases of] builtins.
- Don't include the docstring for any nested class which has been
inserted into its module by
:class:`sage.misc.NestedClassMetaclass` only for pickling. The
class will be properly documented inside its surrounding class.
- Don't include
sagenb.notebook.twist.userchild_download_worksheets.zip.
- Optionally, check whether pickling is broken for nested classes.
- Optionally, include objects whose name begins with an underscore
('_'), i.e., "private" or "hidden" attributes, methods, etc.
Otherwise, we abide by Sphinx's decision. Note: The object
``obj`` is excluded (included) if this handler returns True
(False).
"""
if 'SAGE_CHECK_NESTED' in os.environ:
check_nested_class_picklability(app, what, name, obj, skip, options)
if getattr(obj, '__module__', None) == '__builtin__':
return True
if (hasattr(obj, '__name__') and obj.__name__.find('.') != -1 and
obj.__name__.split('.')[-1] != name):
return True
if name.find("userchild_download_worksheets.zip") != -1:
return True
if 'SAGE_DOC_UNDERSCORE' in os.environ:
if name.split('.')[-1].startswith('_'):
return False
return skip
def process_dollars(app, what, name, obj, options, docstringlines):
r"""
Replace dollar signs with backticks.
See sage.misc.sagedoc.process_dollars for more information
"""
if len(docstringlines) > 0 and name.find("process_dollars") == -1:
from sage.misc.sagedoc import process_dollars as sagedoc_dollars
s = sagedoc_dollars("\n".join(docstringlines))
lines = s.split("\n")
for i in range(len(lines)):
docstringlines[i] = lines[i]
def process_mathtt(app, what, name, obj, options, docstringlines):
r"""
Replace \mathtt{BLAH} with \verb|BLAH| if using jsMath.
See sage.misc.sagedoc.process_mathtt for more information
"""
if (len(docstringlines) > 0 and 'SAGE_DOC_JSMATH' in os.environ
and name.find("process_mathtt") == -1):
from sage.misc.sagedoc import process_mathtt as sagedoc_mathtt
s = sagedoc_mathtt("\n".join(docstringlines), True)
lines = s.split("\n")
for i in range(len(lines)):
docstringlines[i] = lines[i]
def process_inherited(app, what, name, obj, options, docstringlines):
"""
If we're including inherited members, omit their docstrings.
"""
if not options.get('inherited-members'):
return
if what in ['class', 'data', 'exception', 'function', 'module']:
return
name = name.split('.')[-1]
if what == 'method' and hasattr(obj, 'im_class'):
if name in obj.im_class.__dict__.keys():
return
if what == 'attribute' and hasattr(obj, '__objclass__'):
if name in obj.__objclass__.__dict__.keys():
return
for i in xrange(len(docstringlines)):
docstringlines.pop()
dangling_debug = False
def debug_inf(app, message):
if dangling_debug: app.info(message)
def call_intersphinx(app, env, node, contnode):
"""
Call intersphinx and work around its misshandling of relative links
"""
debug_inf(app, "???? Trying intersphinx for %s"%node['reftarget'])
builder = app.builder
res = sphinx.ext.intersphinx.missing_reference(
app, env, node, contnode)
if res:
if res['refuri'].startswith(SAGE_DOC):
here = os.path.dirname(os.path.join(builder.outdir,
node['refdoc']))
res['refuri'] = os.path.relpath(res['refuri'], here)
debug_inf(app, "++++ Found at %s"%res['refuri'])
else:
debug_inf(app, "---- Intersphinx: %s not Found"%node['reftarget'])
return res
def find_sage_dangling_links(app, env, node, contnode):
"""
Try to find dangling link in local module imports or all.py.
"""
debug_inf(app, "==================== find_sage_dangling_links ")
reftype = node['reftype']
reftarget = node['reftarget']
try:
doc = node['refdoc']
except KeyError:
debug_inf(app, "-- no refdoc in node %s"%node)
return None
debug_inf(app, "Searching %s from %s"%(reftarget, doc))
if reftarget in base_class_as_func and reftype == 'class':
node['reftype'] = 'func'
res = call_intersphinx(app, env, node, contnode)
if res:
debug_inf(app, "++ DONE %s"%(res['refuri']))
return res
if node.get('refdomain') != 'py':
return None
try:
module = node['py:module']
cls = node['py:class']
except KeyError:
debug_inf(app, "-- no module or class for :%s:%s"%(reftype, reftarget))
return None
basename = reftarget.split(".")[0]
try:
target_module = getattr(sys.modules['sage.all'], basename).__module__
except AttributeError:
debug_inf(app, "-- %s not found in sage.all"%(basename))
return None
if target_module is None:
target_module = ""
debug_inf(app, "?? found in None !!!")
debug_inf(app, "++ found %s using sage.all in %s"%(basename, target_module))
newtarget = target_module+'.'+reftarget
node['reftarget'] = newtarget
builder = app.builder
searchmode = node.hasattr('refspecific') and 1 or 0
matches = builder.env.domains['py'].find_obj(
builder.env, module, cls, newtarget, reftype, searchmode)
if not matches:
debug_inf(app, "?? no matching doc for %s"%newtarget)
return call_intersphinx(app, env, node, contnode)
elif len(matches) > 1:
env.warn(target_module,
'more than one target found for cross-reference '
'%r: %s' % (newtarget,
', '.join(match[0] for match in matches)),
node.line)
name, obj = matches[0]
debug_inf(app, "++ match = %s %s"%(name, obj))
from docutils import nodes
newnode = nodes.reference('', '', internal=True)
if name == target_module:
newnode['refid'] = name
else:
newnode['refuri'] = builder.get_relative_uri(node['refdoc'], obj[0])
newnode['refuri'] += '#' + name
debug_inf(app, "++ DONE at URI %s"%(newnode['refuri']))
newnode['reftitle'] = name
newnode.append(contnode)
return newnode
base_class_as_func = [
'bool', 'complex', 'dict', 'file', 'float',
'frozenset', 'int', 'list', 'long', 'object',
'set', 'slice', 'str', 'tuple', 'type', 'unicode', 'xrange']
nitpick_ignore = (
('py:class', 'twisted.web2.resource.Resource'),
('py:class', 'twisted.web2.resource.PostableResource'))
def nitpick_patch_config(app):
"""
Patch the default config for nitpicky
Calling path_config ensure that nitpicky is not considered as a Sphinx
environment variable but rather as a Sage environment variable. As a
consequence, changing it doesn't force the recompilation of the entire
documentation.
"""
app.config.values['nitpicky'] = (False, 'sage')
app.config.values['nitpick_ignore'] = ([], 'sage')
from sage.misc.sageinspect import sage_getargspec
autodoc_builtin_argspec = sage_getargspec
def setup(app):
app.connect('autodoc-process-docstring', process_docstring_cython)
app.connect('autodoc-process-docstring', process_directives)
app.connect('autodoc-process-docstring', process_docstring_module_title)
app.connect('autodoc-process-docstring', process_dollars)
app.connect('autodoc-process-docstring', process_mathtt)
app.connect('autodoc-process-docstring', process_inherited)
app.connect('autodoc-skip-member', skip_member)
if app.srcdir.startswith(SAGE_DOC):
app.add_config_value('intersphinx_mapping', {}, True)
app.add_config_value('intersphinx_cache_limit', 5, False)
app.connect('missing-reference', find_sage_dangling_links)
import sphinx.ext.intersphinx
app.connect('builder-inited', set_intersphinx_mappings)
app.connect('builder-inited', sphinx.ext.intersphinx.load_mappings)
app.connect('builder-inited', nitpick_patch_config)