tmp
/
pip-install-ghxuqwgs
/numpy_78e94bf2b6094bf9a1f3d92042f9bf46
/doc
/sphinxext
/numpydoc
/phantom_import.py
| """ | |
| ============== | |
| 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'])) | |