model of DCN pyramidal neuron
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

182 lines
5.8 KiB

"""
==============
phantom_import
==============
Sphinx extension to make directives from ``sphinx.ext.autodoc`` and similar
extensions to use docstrings loaded from an XML file.
This extension loads an XML file in the Pydocweb format [1] and
creates a dummy module that contains the specified docstrings. This
can be used to get the current docstrings from a Pydocweb instance
without needing to rebuild the documented module.
.. [1] http://code.google.com/p/pydocweb
"""
from __future__ import division, absolute_import, print_function
import imp, sys, compiler, types, os, inspect, re
def setup(app):
app.connect("builder-inited", initialize)
app.add_config_value("phantom_import_file", None, True)
def initialize(app):
fn = app.config.phantom_import_file
if fn and os.path.isfile(fn):
print("[numpydoc] Phantom importing modules from", fn, "...")
import_phantom_module(fn)
# ------------------------------------------------------------------------------
# Creating 'phantom' modules from an XML description
# ------------------------------------------------------------------------------
def import_phantom_module(xml_file):
"""
Insert a fake Python module to sys.modules, based on a XML file.
The XML file is expected to conform to Pydocweb DTD. The fake
module will contain dummy objects, which guarantee the following:
- Docstrings are correct.
- Class inheritance relationships are correct (if present in XML).
- Function argspec is *NOT* correct (even if present in XML).
Instead, the function signature is prepended to the function docstring.
- Class attributes are *NOT* correct; instead, they are dummy objects.
Parameters
----------
xml_file : str
Name of an XML file to read
"""
import lxml.etree as etree
object_cache = {}
tree = etree.parse(xml_file)
root = tree.getroot()
# Sort items so that
# - Base classes come before classes inherited from them
# - Modules come before their contents
all_nodes = dict([(n.attrib["id"], n) for n in root])
def _get_bases(node, recurse=False):
bases = [x.attrib["ref"] for x in node.findall("base")]
if recurse:
j = 0
while True:
try:
b = bases[j]
except IndexError:
break
if b in all_nodes:
bases.extend(_get_bases(all_nodes[b]))
j += 1
return bases
type_index = ["module", "class", "callable", "object"]
def base_cmp(a, b):
x = cmp(type_index.index(a.tag), type_index.index(b.tag))
if x != 0:
return x
if a.tag == "class" and b.tag == "class":
a_bases = _get_bases(a, recurse=True)
b_bases = _get_bases(b, recurse=True)
x = cmp(len(a_bases), len(b_bases))
if x != 0:
return x
if a.attrib["id"] in b_bases:
return -1
if b.attrib["id"] in a_bases:
return 1
return cmp(a.attrib["id"].count("."), b.attrib["id"].count("."))
nodes = root.getchildren()
nodes.sort(base_cmp)
# Create phantom items
for node in nodes:
name = node.attrib["id"]
doc = (node.text or "").decode("string-escape") + "\n"
if doc == "\n":
doc = ""
# create parent, if missing
parent = name
while True:
parent = ".".join(parent.split(".")[:-1])
if not parent:
break
if parent in object_cache:
break
obj = imp.new_module(parent)
object_cache[parent] = obj
sys.modules[parent] = obj
# create object
if node.tag == "module":
obj = imp.new_module(name)
obj.__doc__ = doc
sys.modules[name] = obj
elif node.tag == "class":
bases = [object_cache[b] for b in _get_bases(node) if b in object_cache]
bases.append(object)
init = lambda self: None
init.__doc__ = doc
obj = type(name, tuple(bases), {"__doc__": doc, "__init__": init})
obj.__name__ = name.split(".")[-1]
elif node.tag == "callable":
funcname = node.attrib["id"].split(".")[-1]
argspec = node.attrib.get("argspec")
if argspec:
argspec = re.sub("^[^(]*", "", argspec)
doc = "%s%s\n\n%s" % (funcname, argspec, doc)
obj = lambda: 0
obj.__argspec_is_invalid_ = True
if sys.version_info[0] >= 3:
obj.__name__ = funcname
else:
obj.func_name = funcname
obj.__name__ = name
obj.__doc__ = doc
if inspect.isclass(object_cache[parent]):
obj.__objclass__ = object_cache[parent]
else:
class Dummy(object):
pass
obj = Dummy()
obj.__name__ = name
obj.__doc__ = doc
if inspect.isclass(object_cache[parent]):
obj.__get__ = lambda: None
object_cache[name] = obj
if parent:
if inspect.ismodule(object_cache[parent]):
obj.__module__ = parent
setattr(object_cache[parent], name.split(".")[-1], obj)
# Populate items
for node in root:
obj = object_cache.get(node.attrib["id"])
if obj is None:
continue
for ref in node.findall("ref"):
if node.tag == "class":
if ref.attrib["ref"].startswith(node.attrib["id"] + "."):
setattr(
obj, ref.attrib["name"], object_cache.get(ref.attrib["ref"])
)
else:
setattr(obj, ref.attrib["name"], object_cache.get(ref.attrib["ref"]))