def get_file(obj): if hasattr(obj, "__file__"): return obj.__file__ if hasattr(obj, "__module__") and obj.__module__ is not None: _, module, _, _ = import_by_name(obj.__module__) return get_file(module) return None
def test_import_by_name(): import sphinx import sphinx.ext.autosummary prefixed_name, obj, parent, modname = import_by_name('sphinx') assert prefixed_name == 'sphinx' assert obj is sphinx assert parent is None assert modname == 'sphinx' prefixed_name, obj, parent, modname = import_by_name('sphinx.ext.autosummary.__name__') assert prefixed_name == 'sphinx.ext.autosummary.__name__' assert obj is sphinx.ext.autosummary.__name__ assert parent is sphinx.ext.autosummary assert modname == 'sphinx.ext.autosummary' prefixed_name, obj, parent, modname = import_by_name('sphinx.ext.autosummary.Autosummary.get_items') assert prefixed_name == 'sphinx.ext.autosummary.Autosummary.get_items' assert obj == sphinx.ext.autosummary.Autosummary.get_items assert parent is sphinx.ext.autosummary.Autosummary assert modname == 'sphinx.ext.autosummary'
def autolink_role(typ, rawtext, etext, lineno, inliner, options=None, content=None): """Smart linking role. Expands to ':obj:`text`' if `text` is an object that can be imported; otherwise expands to '*text*'. """ options = options or {} content = content or [] env = inliner.document.settings.env r = env.get_domain('py').role('obj')( 'obj', rawtext, etext, lineno, inliner, options, content) pnode = r[0][0] prefixes = get_import_prefixes_from_env(env) try: import_by_name(pnode['reftarget'], prefixes) except ImportError: content = pnode[0] r[0][0] = nodes.emphasis(rawtext, content[0].astext(), classes=content['classes']) return r
def get_items(self, names): """ Subclass get items to get support for all methods in an given object """ env = self.state.document.settings.env prefixes = get_import_prefixes_from_env(env) methodNames = [] for name in names: methodNames.append(name) _, obj, _, _ = import_by_name(name, prefixes=prefixes) methodNames.extend(["%s.%s" % (name, method) for method in dir(obj) if not method.startswith("_")]) return super(AutosummaryMethodList, self).get_items(methodNames)
def find_autosummary_in_docstring(name, module=None, filename=None): """Find out what items are documented in the given object's docstring. See `find_autosummary_in_lines`. """ try: real_name, obj, parent = import_by_name(name) lines = pydoc.getdoc(obj).splitlines() return find_autosummary_in_lines(lines, module=name, filename=filename) except AttributeError: pass except ImportError, e: print "Failed to import '%s': %s" % (name, e)
def get_items(self, names): env = self.state.document.settings.env prefixes = get_import_prefixes_from_env(env) items = [] prefix = '' shorten = '' def _get_items(name): _items = super(AutoMemberSummary, self).get_items([shorten + name]) if self.result.data and ".. deprecated::" in self.result.data[0]: # don't show deprecated classes / functions in summary return for dn, sig, summary, rn in _items: if ".. deprecated::" in summary: # don't show deprecated methods in summary continue items.append(('%s%s' % (prefix, dn), sig, summary, rn)) for name in names: if '~' in name: prefix, name = name.split('~') shorten = '~' else: prefix = '' shorten = '' try: real_name, obj, parent, _ = import_by_name(name, prefixes=prefixes) except ImportError: self.warn('failed to import %s' % name) continue if not inspect.ismodule(obj): _get_items(name) continue for member in dir(obj): if member.startswith('_'): continue mobj = getattr(obj, member) if hasattr(mobj, '__module__'): if not mobj.__module__.startswith(real_name): continue # skip imported classes & functions elif hasattr(mobj, '__name__'): continue # skip imported modules else: continue # skip instances _get_items('%s.%s' % (mobj.__module__, member)) return items
def import_name(app, name): """Import the given name and return name, obj, parent, mod_name :param name: name to import :type name: str :returns: the imported object or None :rtype: object | None :raises: None """ try: app.debug2('Importing %r', name) name, obj = autosummary.import_by_name(name)[:2] app.debug2('Imported %s', obj) return obj except ImportError as e: app.warn("Jinjapidoc failed to import %r: %s", name, e)
def find_autosummary_in_docstring(name, module=None, filename=None): """Find out what items are documented in the given object's docstring. See `find_autosummary_in_lines`. """ try: real_name, obj, parent, modname = import_by_name(name) lines = pydoc.getdoc(obj).splitlines() return find_autosummary_in_lines(lines, module=name, filename=filename) except AttributeError: pass except ImportError as e: print("Failed to import '%s': %s" % (name, e)) except SystemExit as e: print("Failed to import '%s'; the module executes module level " "statement and it might call sys.exit()." % name) return []
def find_automembers(self): env = self.state.document.settings.env prefixes = get_import_prefixes_from_env(env) objects = [import_by_name(p) for p in prefixes if p is not None] new_names = [] for name, obj, _, _ in objects: for attr, child in inspect.getmembers(obj): if getattr(child, '__module__', None) != name: continue if inspect.isfunction(child): new_names.append(attr) elif inspect.isclass(child): for childattr, grandchild in inspect.getmembers(child, inspect.isfunction): if grandchild.__module__ != name: continue new_names.append('%s.%s' % (attr, childattr)) new_names.sort(key=lambda s: s.lower()) return new_names
def get_autosummary_api(): import shutil from sphinx.ext.autosummary import import_by_name from sphinx.ext.autosummary.generate import DummyApplication, setup_documenters, generate_autosummary_docs sources = ['api.rst'] output_dir = './tmp_generated' app = DummyApplication() setup_documenters(app) generate_autosummary_docs(sources, output_dir, app=app) autosummary_api = {} for module_name in _modules: try: module = importlib.import_module(module_name) module_item = ModuleItem(module) autosummary_api[module_name] = module_item except ImportError as err: print('module {} could not be imported: {}'.format(module_name, err)) autosummary_api[module_name] = err for generated_rst_file in os.listdir(output_dir): qualname, ext = os.path.splitext(generated_rst_file) qualname, obj, parent, module_name = import_by_name(qualname) module_item = autosummary_api[module_name] name = qualname.split('.')[-1] if inspect.isclass(obj) and name in module_item.classes: continue if inspect.isclass(parent): class_name = parent.__name__ if class_name not in module_item.classes: module_item.insert_element(class_name, parent) class_item = module_item.classes[class_name] class_item.insert_element(name, obj) else: module_item.insert_element(name, obj) if os.path.exists(output_dir): shutil.rmtree(output_dir) return autosummary_api
def format_directive(module, package=None): """Create the automodule directive and add the options.""" _, obj, _, _ = import_by_name(makename(package, module)) functions = get_public_members(obj, 'function') classes = get_public_members(obj, 'class') exceptions = get_public_members(obj, 'exception') if len(functions) == 0 and len(classes) == 0 and len(exceptions) == 0: return None directive = '.. automodule:: %s\n\n' % makename(package, module) directive += '.. currentmodule:: %s\n\n' % makename(package, module) directive += get_summary(functions, "Functions", "") directive += get_summary(exceptions, "Exceptions", "", min_items=1) directive += get_summary(classes, "Classes", "", min_items=1) for function in functions: directive += '.. autofunction:: %s\n' % function for exception in exceptions: directive += '.. autoexception:: %s\n' % exception for cls in classes: directive += '.. autoclass:: %s\n' % cls directive += ' :show-inheritance:\n\n' _, cls_obj, _, _ = import_by_name(makename(package, "%s.%s" % (module, cls))) attributes = get_public_members(cls_obj, 'attribute') methods = get_public_members(cls_obj, 'method') subclasses = get_public_members(cls_obj, 'class') attributes.extend(get_private_superclass_public_members(cls_obj, 'attribute')) methods.extend(get_private_superclass_public_members(cls_obj, 'method')) abstract_attrs, real_attrs = split_into_abstract_and_non_abstract( cls_obj, attributes, "__isabstractattribute__") abstract_methods, real_methods = split_into_abstract_and_non_abstract( cls_obj, methods, "__isabstractmethod__") directive += get_summary(abstract_attrs, "Abstract Attributes", " ") directive += get_summary(real_attrs, "Attributes", " ") directive += get_summary(abstract_methods, "Abstract Methods", " ") directive += get_summary(real_methods, "Methods", " ") if len(subclasses) > 0: directive += ' .. rubric:: Detailed Types\n\n' for subcls in subclasses: directive += ' .. autoattribute:: %s\n' % subcls if len(methods) > 0: directive += ' .. rubric:: Detailed Methods\n\n' for method in methods: directive += ' .. automethod:: %s\n' % method directive += '\n' return directive
def generate_autosummary_docs(sources: List[str], output_dir: str = None, suffix: str = '.rst', base_path: str = None, builder: Builder = None, template_dir: str = None, imported_members: bool = False, app: Any = None, overwrite: bool = True) -> None: if builder: warnings.warn('builder argument for generate_autosummary_docs() is deprecated.', RemovedInSphinx50Warning) if template_dir: warnings.warn('template_dir argument for generate_autosummary_docs() is deprecated.', RemovedInSphinx50Warning) showed_sources = list(sorted(sources)) if len(showed_sources) > 20: showed_sources = showed_sources[:10] + ['...'] + showed_sources[-10:] logger.info(__('[autosummary] generating autosummary for: %s') % ', '.join(showed_sources)) if output_dir: logger.info(__('[autosummary] writing to %s') % output_dir) if base_path is not None: sources = [os.path.join(base_path, filename) for filename in sources] template = AutosummaryRenderer(app) # read items = find_autosummary_in_files(sources) # keep track of new files new_files = [] # write for entry in sorted(set(items), key=str): if entry.path is None: # The corresponding autosummary:: directive did not have # a :toctree: option continue path = output_dir or os.path.abspath(entry.path) ensuredir(path) try: name, obj, parent, mod_name = import_by_name(entry.name) except ImportError as e: logger.warning(__('[autosummary] failed to import %r: %s') % (entry.name, e)) continue context = {} if app: context.update(app.config.autosummary_context) content = generate_autosummary_content(name, obj, parent, template, entry.template, imported_members, app, entry.recursive, context) filename = os.path.join(path, name + suffix) if os.path.isfile(filename): with open(filename) as f: old_content = f.read() if content == old_content: continue elif overwrite: # content has changed with open(filename, 'w') as f: f.write(content) new_files.append(filename) else: with open(filename, 'w') as f: f.write(content) new_files.append(filename) # descend recursively to new files if new_files: generate_autosummary_docs(new_files, output_dir=output_dir, suffix=suffix, base_path=base_path, builder=builder, template_dir=template_dir, imported_members=imported_members, app=app, overwrite=overwrite)
def generate_autosummary_docs(sources, output_dir=None, suffix='.rst', warn=None, info=None, base_path=None, builder=None, template_dir=None, imported_members=False, app=None): # type: (List[str], str, str, Callable, Callable, str, Builder, str, bool, Any) -> None if info: warnings.warn( 'info argument for generate_autosummary_docs() is deprecated.', RemovedInSphinx40Warning) _info = info else: _info = logger.info if warn: warnings.warn( 'warn argument for generate_autosummary_docs() is deprecated.', RemovedInSphinx40Warning) _warn = warn else: _warn = logger.warning showed_sources = list(sorted(sources)) if len(showed_sources) > 20: showed_sources = showed_sources[:10] + ['...'] + showed_sources[-10:] _info( __('[autosummary] generating autosummary for: %s') % ', '.join(showed_sources)) if output_dir: _info(__('[autosummary] writing to %s') % output_dir) if base_path is not None: sources = [os.path.join(base_path, filename) for filename in sources] template = AutosummaryRenderer(builder, template_dir) # read items = find_autosummary_in_files(sources) # keep track of new files new_files = [] # write for name, path, template_name in sorted(set(items), key=str): if path is None: # The corresponding autosummary:: directive did not have # a :toctree: option continue path = output_dir or os.path.abspath(path) ensuredir(path) try: name, obj, parent, mod_name = import_by_name(name) except ImportError as e: _warn('[autosummary] failed to import %r: %s' % (name, e)) continue fn = os.path.join(path, name + suffix) # skip it if it exists if os.path.isfile(fn): continue new_files.append(fn) with open(fn, 'w') as f: rendered = generate_autosummary_content(name, obj, parent, template, template_name, imported_members, app) f.write(rendered) # descend recursively to new files if new_files: generate_autosummary_docs(new_files, output_dir=output_dir, suffix=suffix, warn=warn, info=info, base_path=base_path, builder=builder, template_dir=template_dir, app=app)
def get_items(self, names: List[str]) -> List[Tuple[str, str, str, str, str]]: """Try to import the given names, and return a list of ``[(name, signature, summary_string, real_name, env_summary), ...]``. """ prefixes = get_import_prefixes_from_env(self.env) items = [] # type: List[Tuple[str, str, str, str, str]] max_item_chars = 50 for name in names: display_name = name if name.startswith('~'): name = name[1:] display_name = name.split('.')[-1] try: with mock(self.config.autosummary_mock_imports): real_name, obj, parent, modname = import_by_name( name, prefixes=prefixes) except ImportError: logger.warning(__('failed to import %s'), name) items.append((name, '', '', name, '')) continue self.bridge.result = StringList() # initialize for each documenter full_name = real_name if not isinstance(obj, ModuleType): # give explicitly separated module name, so that members # of inner classes can be documented full_name = modname + '::' + full_name[len(modname) + 1:] # NB. using full_name here is important, since Documenters # handle module prefixes slightly differently doccls = get_documenter(self.env.app, obj, parent) documenter = doccls(self.bridge, full_name) if not documenter.parse_name(): logger.warning(__('failed to parse name %s'), real_name) items.append((display_name, '', '', real_name, '')) continue if not documenter.import_object(): logger.warning(__('failed to import object %s'), real_name) items.append((display_name, '', '', real_name, '')) continue if documenter.options.members and not documenter.check_module(): continue # try to also get a source code analyzer for attribute docs try: documenter.analyzer = ModuleAnalyzer.for_module( documenter.get_real_modname()) # parse right now, to get PycodeErrors on parsing (results will # be cached anyway) documenter.analyzer.find_attr_docs() except PycodeError as err: logger.debug('[autodoc] module analyzer failed: %s', err) # no source file -- e.g. for builtin and C modules documenter.analyzer = None # -- Grab the signature try: sig = documenter.format_signature(show_annotation=False) except TypeError: # the documenter does not support ``show_annotation`` option sig = documenter.format_signature() if not sig: sig = '' else: max_chars = max(10, max_item_chars - len(display_name)) sig = mangle_signature(sig, max_chars=max_chars) # -- Grab the summary documenter.add_content(None) summary = extract_summary(self.bridge.result.data[:], self.state.document) env_sum = self.extract_env_summary(self.bridge.result.data[:]) items.append((display_name, sig, summary, real_name, env_sum)) return items
def process_obj(name, typ, include_public=None): obj_name = name[name.index(':') + 1:] full_name, obj, _, _ = import_by_name(obj_name, prefixes=prefixes) members, _ = self.get_members(env.app, obj, typ, include_public) ext_names.extend(['~%s.%s' % (full_name, member) for member in members])
def get_items(self, names): """Try to import the given names, and return a list of ``[(name, signature, summary_string, real_name), ...]``. """ from sphinx.ext.autosummary import (get_import_prefixes_from_env, import_by_name, get_documenter, mangle_signature) env = self.state.document.settings.env prefixes = get_import_prefixes_from_env(env) items = [] max_item_chars = 50 for name in names: display_name = name if name.startswith('~'): name = name[1:] display_name = name.split('.')[-1] try: import_by_name_values = import_by_name(name, prefixes=prefixes) except ImportError: self.warn('[astropyautosummary] failed to import %s' % name) items.append((name, '', '', name)) continue # to accommodate Sphinx v1.2.2 and v1.2.3 if len(import_by_name_values) == 3: real_name, obj, parent = import_by_name_values elif len(import_by_name_values) == 4: real_name, obj, parent, module_name = import_by_name_values # NB. using real_name here is important, since Documenters # handle module prefixes slightly differently documenter = get_documenter(obj, parent)(self, real_name) if not documenter.parse_name(): self.warn('[astropyautosummary] failed to parse name %s' % real_name) items.append((display_name, '', '', real_name)) continue if not documenter.import_object(): self.warn('[astropyautosummary] failed to import object %s' % real_name) items.append((display_name, '', '', real_name)) continue # -- Grab the signature sig = documenter.format_signature() if not sig: sig = '' else: max_chars = max(10, max_item_chars - len(display_name)) sig = mangle_signature(sig, max_chars=max_chars) sig = sig.replace('*', r'\*') # -- Grab the summary doc = list(documenter.process_doc(documenter.get_doc())) while doc and not doc[0].strip(): doc.pop(0) m = _itemsummrex.search(" ".join(doc).strip()) if m: summary = m.group(1).strip() elif doc: summary = doc[0].strip() else: summary = '' items.append((display_name, sig, summary, real_name)) return items
def generate_autosummary_docs(sources, output_dir=None, suffix='.rst', warn=_simple_warn, info=_simple_info, base_path=None, builder=None, template_dir=None, app=None): showed_sources = list(sorted(sources)) if len(showed_sources) > 20: showed_sources = showed_sources[:10] + ['...'] + showed_sources[-10:] info('[autosummary] generating autosummary for: %s' % ', '.join(showed_sources)) if output_dir: info('[autosummary] writing to %s' % output_dir) if base_path is not None: sources = [osp.join(base_path, filename) for filename in sources] # create our own templating environment template_dirs = [osp.join(package_dir, 'ext', 'autosummary', 'templates')] if builder is not None: # allow the user to override the templates template_loader = BuiltinTemplateLoader() template_loader.init(builder, dirs=template_dirs) else: if template_dir: template_dirs.insert(0, template_dir) template_loader = FileSystemLoader(template_dirs) template_env = SandboxedEnvironment(loader=template_loader) # read items = find_autosummary_in_files(sources) # remove possible duplicates items = list(dict([(item, True) for item in items]).keys()) # keep track of new files new_files = [] # write # noinspection PyTypeChecker for name, path, template_name in sorted(items, key=str): if path is None: # The corresponding autosummary:: directive did not have # a :toctree: option continue path = output_dir or osp.abspath(path) ensuredir(path) try: name, obj, parent, mod_name = import_by_name(name) except ImportError as e: warn('[autosummary] failed to import %r: %s' % (name, e)) continue fn = osp.join(path, name + suffix) # skip it if it exists if osp.isfile(fn): continue new_files.append(fn) f = open(fn, 'w') try: try: doc = get_documenter(app, obj, parent) except TypeError: doc = get_documenter(obj, parent) if template_name is not None: template = template_env.get_template(template_name) else: try: template = template_env.get_template('autosummary/%s.rst' % doc.objtype) except TemplateNotFound: template = template_env.get_template( 'autosummary/base.rst') ns = {} if doc.objtype == 'module': ns['members'] = dir(obj) ns['functions'], ns['all_functions'] = \ get_members(app, obj, 'function') ns['classes'], ns['all_classes'] = \ get_members(app, obj, 'class') ns['exceptions'], ns['all_exceptions'] = \ get_members(app, obj, 'exception') ns['data'], ns['all_data'] = \ get_members(app, obj, 'data', imported=True) ns['data'] = ', '.join(ns['data']) ns['all_data'] = ', '.join(ns['all_data']) ns['dispatchers'], ns['all_dispatchers'] = \ get_members(app, obj, 'dispatcher', imported=True) elif doc.objtype == 'class': ns['members'] = dir(obj) ns['methods'], ns['all_methods'] = \ get_members(app, obj, 'method', ['__init__'], True) ns['attributes'], ns['all_attributes'] = \ get_members(app, obj, 'attribute') parts = name.split('.') if doc.objtype in ('method', 'attribute'): mod_name = '.'.join(parts[:-2]) cls_name = parts[-2] obj_name = '.'.join(parts[-2:]) ns['class'] = cls_name else: mod_name, obj_name = '.'.join(parts[:-1]), parts[-1] ns['fullname'] = name ns['module'] = mod_name ns['objname'] = obj_name ns['name'] = parts[-1] ns['objtype'] = doc.objtype ns['underline'] = len(name) * '=' rendered = template.render(**ns) f.write(rendered) finally: f.close() # descend recursively to new files if new_files: generate_autosummary_docs(new_files, output_dir=output_dir, suffix=suffix, warn=warn, info=info, base_path=base_path, builder=builder, template_dir=template_dir, app=app)
def get_items(self, modules): """Try to import the given names, and return a list of ``[(name, signature, summary_string, real_name), ...]``. """ env = self.state.document.settings.env prefixes = get_import_prefixes_from_env(env) items = {} max_item_chars = 80 callbacks_already_met = set() for name in modules: try: real_name, obj, parent, modename = import_by_name(name, prefixes=prefixes) except ImportError: self.warn("import name failed: %s" % name) continue try: routes = obj().routes except Exception: continue for r in routes: callback = r.callback if not hasattr(callback, "_mapped_to") or callback in callbacks_already_met: continue callbacks_already_met.add(callback) rule = r.rule method = r.method clas = callback.__self__.__class__.__name__ display_name = "%s %s" % (method, rule) documenter = get_documenter(callback, parent)(self, display_name) documenter.object = callback # -- Grab the signature sig = documenter.format_signature() if not sig: sig = '' else: max_chars = max(10, max_item_chars - len(display_name)) sig = mangle_signature(sig, max_chars=max_chars) # -- Grab the summary doc = list(documenter.process_doc(documenter.get_doc())) while doc and not doc[0].strip(): doc.pop(0) m = re.search(r"^([A-Z][^A-Z]*?\.\s)", " ".join(doc).strip()) if m: summary = m.group(1).strip() elif doc: summary = doc[0].strip() else: summary = '' items.setdefault(clas, []).append((escape_rst(display_name), escape_rst(sig), summary, display_name)) return items
def generate_automodsumm_docs(lines, srcfn, suffix='.rst', warn=None, info=None, base_path=None, builder=None, template_dir=None): """ This function is adapted from `sphinx.ext.autosummary.generate.generate_autosummmary_docs` to generate source for the automodsumm directives that should be autosummarized. Unlike generate_autosummary_docs, this function is called one file at a time. """ from sphinx.jinja2glue import BuiltinTemplateLoader from sphinx.ext.autosummary import import_by_name, get_documenter from sphinx.ext.autosummary.generate import (find_autosummary_in_lines, _simple_info, _simple_warn) from sphinx.util.osutil import ensuredir from sphinx.util.inspect import safe_getattr from jinja2 import FileSystemLoader, TemplateNotFound from jinja2.sandbox import SandboxedEnvironment if info is None: info = _simple_info if warn is None: warn = _simple_warn #info('[automodsumm] generating automodsumm for: ' + srcfn) # Create our own templating environment - here we use Astropy's # templates rather than the default autosummary templates, in order to # allow docstrings to be shown for methods. template_dirs = [os.path.join(os.path.dirname(__file__), 'templates'), os.path.join(base_path, '_templates')] if builder is not None: # allow the user to override the templates template_loader = BuiltinTemplateLoader() template_loader.init(builder, dirs=template_dirs) else: if template_dir: template_dirs.insert(0, template_dir) template_loader = FileSystemLoader(template_dirs) template_env = SandboxedEnvironment(loader=template_loader) # read #items = find_autosummary_in_files(sources) items = find_autosummary_in_lines(lines, filename=srcfn) if len(items) > 0: msg = '[automodsumm] {1}: found {0} automodsumm entries to generate' info(msg.format(len(items), srcfn)) # gennms = [item[0] for item in items] # if len(gennms) > 20: # gennms = gennms[:10] + ['...'] + gennms[-10:] # info('[automodsumm] generating autosummary for: ' + ', '.join(gennms)) # remove possible duplicates items = dict([(item, True) for item in items]).keys() # keep track of new files new_files = [] # write for name, path, template_name in sorted(items): if path is None: # The corresponding autosummary:: directive did not have # a :toctree: option continue path = os.path.abspath(path) ensuredir(path) try: import_by_name_values = import_by_name(name) except ImportError as e: warn('[automodsumm] failed to import %r: %s' % (name, e)) continue # if block to accommodate Sphinx's v1.2.2 and v1.2.3 respectively if len(import_by_name_values) == 3: name, obj, parent = import_by_name_values elif len(import_by_name_values) == 4: name, obj, parent, module_name = import_by_name_values fn = os.path.join(path, name + suffix) # skip it if it exists if os.path.isfile(fn): continue new_files.append(fn) f = open(fn, 'w') try: doc = get_documenter(obj, parent) if template_name is not None: template = template_env.get_template(template_name) else: tmplstr = 'autosummary/%s.rst' try: template = template_env.get_template(tmplstr % doc.objtype) except TemplateNotFound: template = template_env.get_template(tmplstr % 'base') def get_members_mod(obj, typ, include_public=[]): """ typ = None -> all """ items = [] for name in dir(obj): try: documenter = get_documenter(safe_getattr(obj, name), obj) except AttributeError: continue if typ is None or documenter.objtype == typ: items.append(name) public = [x for x in items if x in include_public or not x.startswith('_')] return public, items def get_members_class(obj, typ, include_public=[], include_base=False): """ typ = None -> all include_base -> include attrs that are from a base class """ items = [] # using dir gets all of the attributes, including the elements # from the base class, otherwise use __slots__ or __dict__ if include_base: names = dir(obj) else: if hasattr(obj, '__slots__'): names = tuple(getattr(obj, '__slots__')) else: names = getattr(obj, '__dict__').keys() for name in names: try: documenter = get_documenter(safe_getattr(obj, name), obj) except AttributeError: continue if typ is None or documenter.objtype == typ: items.append(name) public = [x for x in items if x in include_public or not x.startswith('_')] return public, items ns = {} if doc.objtype == 'module': ns['members'] = get_members_mod(obj, None) ns['functions'], ns['all_functions'] = \ get_members_mod(obj, 'function') ns['classes'], ns['all_classes'] = \ get_members_mod(obj, 'class') ns['exceptions'], ns['all_exceptions'] = \ get_members_mod(obj, 'exception') elif doc.objtype == 'class': api_class_methods = ['__init__', '__call__'] ns['members'] = get_members_class(obj, None) ns['methods'], ns['all_methods'] = \ get_members_class(obj, 'method', api_class_methods) ns['attributes'], ns['all_attributes'] = \ get_members_class(obj, 'attribute') ns['methods'].sort() ns['attributes'].sort() parts = name.split('.') if doc.objtype in ('method', 'attribute'): mod_name = '.'.join(parts[:-2]) cls_name = parts[-2] obj_name = '.'.join(parts[-2:]) ns['class'] = cls_name else: mod_name, obj_name = '.'.join(parts[:-1]), parts[-1] ns['fullname'] = name ns['module'] = mod_name ns['objname'] = obj_name ns['name'] = parts[-1] ns['objtype'] = doc.objtype ns['underline'] = len(name) * '=' # We now check whether a file for reference footnotes exists for # the module being documented. We first check if the # current module is a file or a directory, as this will give a # different path for the reference file. For example, if # documenting astropy.wcs then the reference file is at # ../wcs/references.txt, while if we are documenting # astropy.config.logging_helper (which is at # astropy/config/logging_helper.py) then the reference file is set # to ../config/references.txt if '.' in mod_name: mod_name_dir = mod_name.replace('.', '/').split('/', 1)[1] else: mod_name_dir = mod_name if not os.path.isdir(os.path.join(base_path, mod_name_dir)) \ and os.path.isdir(os.path.join(base_path, mod_name_dir.rsplit('/', 1)[0])): mod_name_dir = mod_name_dir.rsplit('/', 1)[0] # We then have to check whether it exists, and if so, we pass it # to the template. if os.path.exists(os.path.join(base_path, mod_name_dir, 'references.txt')): # An important subtlety here is that the path we pass in has # to be relative to the file being generated, so we have to # figure out the right number of '..'s ndirsback = path.replace(base_path, '').count('/') ref_file_rel_segments = ['..'] * ndirsback ref_file_rel_segments.append(mod_name_dir) ref_file_rel_segments.append('references.txt') ns['referencefile'] = os.path.join(*ref_file_rel_segments) rendered = template.render(**ns) f.write(rendered) finally: f.close()
def generate_autosummary_docs(sources: List[str], output_dir: str = None, suffix: str = '.rst', warn: Callable = None, info: Callable = None, base_path: str = None, builder: Builder = None, template_dir: str = None, imported_members: bool = False, app: Any = None, overwrite: bool = True, encoding: str = 'utf-8') -> None: if info: warnings.warn( 'info argument for generate_autosummary_docs() is deprecated.', RemovedInSphinx40Warning, stacklevel=2) _info = info else: _info = logger.info if warn: warnings.warn( 'warn argument for generate_autosummary_docs() is deprecated.', RemovedInSphinx40Warning, stacklevel=2) _warn = warn else: _warn = logger.warning if builder: warnings.warn( 'builder argument for generate_autosummary_docs() is deprecated.', RemovedInSphinx50Warning, stacklevel=2) if template_dir: warnings.warn( 'template_dir argument for generate_autosummary_docs() is deprecated.', RemovedInSphinx50Warning, stacklevel=2) showed_sources = list(sorted(sources)) if len(showed_sources) > 20: showed_sources = showed_sources[:10] + ['...'] + showed_sources[-10:] _info( __('[autosummary] generating autosummary for: %s') % ', '.join(showed_sources)) if output_dir: _info(__('[autosummary] writing to %s') % output_dir) if base_path is not None: sources = [os.path.join(base_path, filename) for filename in sources] template = AutosummaryRenderer(app) # read items = find_autosummary_in_files(sources) # keep track of new files new_files = [] if app: filename_map = app.config.autosummary_filename_map else: filename_map = {} # write for entry in sorted(set(items), key=str): if entry.path is None: # The corresponding autosummary:: directive did not have # a :toctree: option continue path = output_dir or os.path.abspath(entry.path) ensuredir(path) try: name, obj, parent, modname = import_by_name(entry.name) qualname = name.replace(modname + ".", "") except ImportError as e: try: # try to importl as an instance attribute name, obj, parent, modname = import_ivar_by_name(entry.name) qualname = name.replace(modname + ".", "") except ImportError: _warn( __('[autosummary] failed to import %r: %s') % (entry.name, e)) continue context = {} if app: context.update(app.config.autosummary_context) content = generate_autosummary_content(name, obj, parent, template, entry.template, imported_members, app, entry.recursive, context, modname, qualname) filename = os.path.join(path, filename_map.get(name, name) + suffix) if os.path.isfile(filename): with open(filename, encoding=encoding) as f: old_content = f.read() if content == old_content: continue elif overwrite: # content has changed with open(filename, 'w', encoding=encoding) as f: f.write(content) new_files.append(filename) else: with open(filename, 'w', encoding=encoding) as f: f.write(content) new_files.append(filename) # descend recursively to new files if new_files: generate_autosummary_docs(new_files, output_dir=output_dir, suffix=suffix, warn=warn, info=info, base_path=base_path, imported_members=imported_members, app=app, overwrite=overwrite)
def generate_automodsumm_docs(lines, srcfn, app=None, suffix='.rst', warn=None, info=None, base_path=None, builder=None, template_dir=None, inherited_members=False): """ This function is adapted from `sphinx.ext.autosummary.generate.generate_autosummmary_docs` to generate source for the automodsumm directives that should be autosummarized. Unlike generate_autosummary_docs, this function is called one file at a time. """ from sphinx.jinja2glue import BuiltinTemplateLoader from sphinx.ext.autosummary import import_by_name, get_documenter from sphinx.ext.autosummary.generate import (_simple_info, _simple_warn) from sphinx.util.osutil import ensuredir from sphinx.util.inspect import safe_getattr from jinja2 import FileSystemLoader, TemplateNotFound from jinja2.sandbox import SandboxedEnvironment from .utils import find_autosummary_in_lines_for_automodsumm as find_autosummary_in_lines if info is None: info = _simple_info if warn is None: warn = _simple_warn # info('[automodsumm] generating automodsumm for: ' + srcfn) # Create our own templating environment - here we use Astropy's # templates rather than the default autosummary templates, in order to # allow docstrings to be shown for methods. template_dirs = [ os.path.join(os.path.dirname(__file__), 'templates'), os.path.join(base_path, '_templates') ] if builder is not None: # allow the user to override the templates template_loader = BuiltinTemplateLoader() template_loader.init(builder, dirs=template_dirs) else: if template_dir: template_dirs.insert(0, template_dir) template_loader = FileSystemLoader(template_dirs) template_env = SandboxedEnvironment(loader=template_loader) # read # items = find_autosummary_in_files(sources) items = find_autosummary_in_lines(lines, filename=srcfn) if len(items) > 0: msg = '[automodsumm] {1}: found {0} automodsumm entries to generate' info(msg.format(len(items), srcfn)) # gennms = [item[0] for item in items] # if len(gennms) > 20: # gennms = gennms[:10] + ['...'] + gennms[-10:] # info('[automodsumm] generating autosummary for: ' + ', '.join(gennms)) # remove possible duplicates items = list(set(items)) # keep track of new files new_files = [] # write for name, path, template_name, inherited_mem in sorted(items): if path is None: # The corresponding autosummary:: directive did not have # a :toctree: option continue path = os.path.abspath(os.path.join(base_path, path)) ensuredir(path) try: import_by_name_values = import_by_name(name) except ImportError as e: warn('[automodsumm] failed to import %r: %s' % (name, e)) continue # if block to accommodate Sphinx's v1.2.2 and v1.2.3 respectively if len(import_by_name_values) == 3: name, obj, parent = import_by_name_values elif len(import_by_name_values) == 4: name, obj, parent, module_name = import_by_name_values fn = os.path.join(path, name + suffix) # skip it if it exists if os.path.isfile(fn): continue new_files.append(fn) f = open(fn, 'w') try: if SPHINX_LT_17: doc = get_documenter(obj, parent) else: doc = get_documenter(app, obj, parent) if template_name is not None: template = template_env.get_template(template_name) else: tmplstr = 'autosummary_core/%s.rst' try: template = template_env.get_template(tmplstr % doc.objtype) except TemplateNotFound: template = template_env.get_template(tmplstr % 'base') def get_members_mod(obj, typ, include_public=[]): """ typ = None -> all """ items = [] for name in dir(obj): try: if SPHINX_LT_17: documenter = get_documenter( safe_getattr(obj, name), obj) else: documenter = get_documenter( app, safe_getattr(obj, name), obj) except AttributeError: continue if typ is None or documenter.objtype == typ: items.append(name) public = [ x for x in items if x in include_public or not x.startswith('_') ] return public, items def get_members_class(obj, typ, include_public=[], include_base=False): """ typ = None -> all include_base -> include attrs that are from a base class """ items = [] # using dir gets all of the attributes, including the elements # from the base class, otherwise use __slots__ or __dict__ if include_base: names = dir(obj) else: if hasattr(obj, '__slots__'): names = tuple(getattr(obj, '__slots__')) else: names = getattr(obj, '__dict__').keys() for name in names: try: if SPHINX_LT_17: documenter = get_documenter( safe_getattr(obj, name), obj) else: documenter = get_documenter( app, safe_getattr(obj, name), obj) except AttributeError: continue if typ is None or documenter.objtype == typ: items.append(name) public = [ x for x in items if x in include_public or not x.startswith('_') ] return public, items ns = {} if doc.objtype == 'module': ns['members'] = get_members_mod(obj, None) ns['functions'], ns['all_functions'] = \ get_members_mod(obj, 'function') ns['classes'], ns['all_classes'] = \ get_members_mod(obj, 'class') ns['exceptions'], ns['all_exceptions'] = \ get_members_mod(obj, 'exception') elif doc.objtype == 'class': if inherited_mem is not None: # option set in this specifc directive include_base = inherited_mem else: # use default value include_base = inherited_members api_class_methods = ['__init__', '__call__'] ns['members'] = get_members_class(obj, None, include_base=include_base) ns['methods'], ns['all_methods'] = \ get_members_class(obj, 'method', api_class_methods, include_base=include_base) ns['attributes'], ns['all_attributes'] = \ get_members_class(obj, 'attribute', include_base=include_base) ns['methods'].sort() ns['attributes'].sort() parts = name.split('.') if doc.objtype in ('method', 'attribute'): mod_name = '.'.join(parts[:-2]) cls_name = parts[-2] obj_name = '.'.join(parts[-2:]) ns['class'] = cls_name else: mod_name, obj_name = '.'.join(parts[:-1]), parts[-1] ns['fullname'] = name ns['module'] = mod_name ns['objname'] = obj_name ns['name'] = parts[-1] ns['objtype'] = doc.objtype ns['underline'] = len(obj_name) * '=' # We now check whether a file for reference footnotes exists for # the module being documented. We first check if the # current module is a file or a directory, as this will give a # different path for the reference file. For example, if # documenting astropy.wcs then the reference file is at # ../wcs/references.txt, while if we are documenting # astropy.config.logging_helper (which is at # astropy/config/logging_helper.py) then the reference file is set # to ../config/references.txt if '.' in mod_name: mod_name_dir = mod_name.replace('.', '/').split('/', 1)[1] else: mod_name_dir = mod_name if not os.path.isdir(os.path.join(base_path, mod_name_dir)) \ and os.path.isdir(os.path.join(base_path, mod_name_dir.rsplit('/', 1)[0])): mod_name_dir = mod_name_dir.rsplit('/', 1)[0] # We then have to check whether it exists, and if so, we pass it # to the template. if os.path.exists( os.path.join(base_path, mod_name_dir, 'references.txt')): # An important subtlety here is that the path we pass in has # to be relative to the file being generated, so we have to # figure out the right number of '..'s ndirsback = path.replace(base_path, '').count('/') ref_file_rel_segments = ['..'] * ndirsback ref_file_rel_segments.append(mod_name_dir) ref_file_rel_segments.append('references.txt') ns['referencefile'] = os.path.join(*ref_file_rel_segments) rendered = template.render(**ns) f.write(cleanup_whitespace(rendered)) finally: f.close()
def get_items(self, names): """Try to import the given names, and return a list of ``[(name, signature, summary_string, real_name), ...]``. """ from sphinx.ext.autosummary import (get_import_prefixes_from_env, import_by_name, get_documenter, mangle_signature) env = self.state.document.settings.env prefixes = get_import_prefixes_from_env(env) items = [] max_item_chars = 50 for name in names: display_name = name if name.startswith('~'): name = name[1:] display_name = name.split('.')[-1] try: real_name, obj, parent = import_by_name(name, prefixes=prefixes) except ImportError: self.warn('[astropyautosummary] failed to import %s' % name) items.append((name, '', '', name)) continue # NB. using real_name here is important, since Documenters # handle module prefixes slightly differently documenter = get_documenter(obj, parent)(self, real_name) if not documenter.parse_name(): self.warn('[astropyautosummary] failed to parse name %s' % real_name) items.append((display_name, '', '', real_name)) continue if not documenter.import_object(): self.warn('[astropyautosummary] failed to import object %s' % real_name) items.append((display_name, '', '', real_name)) continue # -- Grab the signature sig = documenter.format_signature() if not sig: sig = '' else: max_chars = max(10, max_item_chars - len(display_name)) sig = mangle_signature(sig, max_chars=max_chars) sig = sig.replace('*', r'\*') # -- Grab the summary doc = list(documenter.process_doc(documenter.get_doc())) while doc and not doc[0].strip(): doc.pop(0) m = _itemsummrex.search(" ".join(doc).strip()) if m: summary = m.group(1).strip() elif doc: summary = doc[0].strip() else: summary = '' items.append((display_name, sig, summary, real_name)) return items
def generate_automodsumm_docs(lines, srcfn, suffix='.rst', warn=None, info=None, base_path=None, builder=None, template_dir=None): """ This function is adapted from `sphinx.ext.autosummary.generate.generate_autosummmary_docs` to generate source for the automodsumm directives that should be autosummarized. Unlike generate_autosummary_docs, this function is called one file at a time. """ import os from sphinx import package_dir from sphinx.jinja2glue import BuiltinTemplateLoader from sphinx.ext.autosummary import import_by_name, get_documenter from sphinx.ext.autosummary.generate import (find_autosummary_in_lines, _simple_info, _simple_warn) from sphinx.util.osutil import ensuredir from sphinx.util.inspect import safe_getattr from jinja2 import FileSystemLoader, TemplateNotFound from jinja2.sandbox import SandboxedEnvironment if info is None: info = _simple_info if warn is None: warn = _simple_warn #info('[automodsumm] generating automodsumm for: ' + srcfn) # create our own templating environment template_dirs = [os.path.join(package_dir, 'ext', 'autosummary', 'templates')] if builder is not None: # allow the user to override the templates template_loader = BuiltinTemplateLoader() template_loader.init(builder, dirs=template_dirs) else: if template_dir: template_dirs.insert(0, template_dir) template_loader = FileSystemLoader(template_dirs) template_env = SandboxedEnvironment(loader=template_loader) # read #items = find_autosummary_in_files(sources) items = find_autosummary_in_lines(lines, filename=srcfn) if len(items) > 0: msg = '[automodsumm] {1}: found {0} automodsumm entries to generate' info(msg.format(len(items), srcfn)) # gennms = [item[0] for item in items] # if len(gennms) > 20: # gennms = gennms[:10] + ['...'] + gennms[-10:] # info('[automodsumm] generating autosummary for: ' + ', '.join(gennms)) # remove possible duplicates items = dict([(item, True) for item in items]).keys() # keep track of new files new_files = [] # write for name, path, template_name in sorted(items): if path is None: # The corresponding autosummary:: directive did not have # a :toctree: option continue path = os.path.abspath(path) ensuredir(path) try: name, obj, parent = import_by_name(name) except ImportError, e: warn('[automodapi] failed to import %r: %s' % (name, e)) continue fn = os.path.join(path, name + suffix) # skip it if it exists if os.path.isfile(fn): continue new_files.append(fn) f = open(fn, 'w') try: doc = get_documenter(obj, parent) if template_name is not None: template = template_env.get_template(template_name) else: tmplstr = 'autosummary/%s.rst' try: template = template_env.get_template(tmplstr % doc.objtype) except TemplateNotFound: template = template_env.get_template(tmplstr % 'base') def get_members(obj, typ, include_public=[]): items = [] for name in dir(obj): try: documenter = get_documenter(safe_getattr(obj, name), obj) except AttributeError: continue if documenter.objtype == typ: items.append(name) public = [x for x in items if x in include_public or not x.startswith('_')] return public, items ns = {} if doc.objtype == 'module': ns['members'] = dir(obj) ns['functions'], ns['all_functions'] = \ get_members(obj, 'function') ns['classes'], ns['all_classes'] = \ get_members(obj, 'class') ns['exceptions'], ns['all_exceptions'] = \ get_members(obj, 'exception') elif doc.objtype == 'class': ns['members'] = dir(obj) ns['methods'], ns['all_methods'] = \ get_members(obj, 'method', ['__init__']) ns['attributes'], ns['all_attributes'] = \ get_members(obj, 'attribute') parts = name.split('.') if doc.objtype in ('method', 'attribute'): mod_name = '.'.join(parts[:-2]) cls_name = parts[-2] obj_name = '.'.join(parts[-2:]) ns['class'] = cls_name else: mod_name, obj_name = '.'.join(parts[:-1]), parts[-1] ns['fullname'] = name ns['module'] = mod_name ns['objname'] = obj_name ns['name'] = parts[-1] ns['objtype'] = doc.objtype ns['underline'] = len(name) * '=' rendered = template.render(**ns) f.write(rendered) finally: f.close()
def generate_autosummary_docs(sources, output_dir=None, suffix='.rst', warn=_simple_warn, info=_simple_info, base_path=None, builder=None, template_dir=None, imported_members=False, app=None): # type: (List[unicode], unicode, unicode, Callable, Callable, unicode, Builder, unicode, bool, Any) -> None # NOQA showed_sources = list(sorted(sources)) if len(showed_sources) > 20: showed_sources = showed_sources[:10] + ['...'] + showed_sources[-10:] info('[autosummary] generating autosummary for: %s' % ', '.join(showed_sources)) if output_dir: info('[autosummary] writing to %s' % output_dir) if base_path is not None: sources = [os.path.join(base_path, filename) for filename in sources] # create our own templating environment template_dirs = None # type: List[unicode] template_dirs = [ os.path.join(package_dir, 'ext', 'autosummary', 'templates') ] template_loader = None # type: BaseLoader if builder is not None: # allow the user to override the templates template_loader = BuiltinTemplateLoader() template_loader.init(builder, dirs=template_dirs) else: if template_dir: template_dirs.insert(0, template_dir) template_loader = FileSystemLoader(template_dirs) # type: ignore template_env = SandboxedEnvironment(loader=template_loader) template_env.filters['underline'] = _underline # replace the builtin html filters template_env.filters['escape'] = rst_escape template_env.filters['e'] = rst_escape # read items = find_autosummary_in_files(sources) # keep track of new files new_files = [] # write for name, path, template_name in sorted(set(items), key=str): if path is None: # The corresponding autosummary:: directive did not have # a :toctree: option continue path = output_dir or os.path.abspath(path) ensuredir(path) try: name, obj, parent, mod_name = import_by_name(name) except ImportError as e: warn('[autosummary] failed to import %r: %s' % (name, e)) continue fn = os.path.join(path, name + suffix) # skip it if it exists if os.path.isfile(fn): continue new_files.append(fn) with open(fn, 'w') as f: doc = get_documenter(app, obj, parent) if template_name is not None: template = template_env.get_template(template_name) else: try: template = template_env.get_template('autosummary/%s.rst' % doc.objtype) except TemplateNotFound: template = template_env.get_template( 'autosummary/base.rst') def get_members(obj, typ, include_public=[], imported=False): # type: (Any, unicode, List[unicode], bool) -> Tuple[List[unicode], List[unicode]] # NOQA items = [] # type: List[unicode] for name in dir(obj): try: value = safe_getattr(obj, name) except AttributeError: continue documenter = get_documenter(app, value, obj) if documenter.objtype == typ: if typ == 'method': items.append(name) elif imported or getattr(value, '__module__', None) == obj.__name__: # skip imported members if expected items.append(name) public = [ x for x in items if x in include_public or not x.startswith('_') ] return public, items ns = {} # type: Dict[unicode, Any] if doc.objtype == 'module': ns['members'] = dir(obj) ns['functions'], ns['all_functions'] = \ get_members(obj, 'function', imported=imported_members) ns['classes'], ns['all_classes'] = \ get_members(obj, 'class', imported=imported_members) ns['exceptions'], ns['all_exceptions'] = \ get_members(obj, 'exception', imported=imported_members) elif doc.objtype == 'class': ns['members'] = dir(obj) ns['methods'], ns['all_methods'] = \ get_members(obj, 'method', ['__init__'], imported=imported_members) ns['attributes'], ns['all_attributes'] = \ get_members(obj, 'attribute', imported=imported_members) parts = name.split('.') if doc.objtype in ('method', 'attribute'): mod_name = '.'.join(parts[:-2]) cls_name = parts[-2] obj_name = '.'.join(parts[-2:]) ns['class'] = cls_name else: mod_name, obj_name = '.'.join(parts[:-1]), parts[-1] ns['fullname'] = name ns['module'] = mod_name ns['objname'] = obj_name ns['name'] = parts[-1] ns['objtype'] = doc.objtype ns['underline'] = len(name) * '=' rendered = template.render(**ns) f.write(rendered) # type: ignore # descend recursively to new files if new_files: generate_autosummary_docs(new_files, output_dir=output_dir, suffix=suffix, warn=warn, info=info, base_path=base_path, builder=builder, template_dir=template_dir)
def generate_autosummary_docs(sources, output_dir=None, suffix=None, warn=_simple_warn, info=_simple_info): info('generating autosummary for: %s' % ', '.join(sources)) if output_dir: info('writing to %s' % output_dir) # read names = {} for name, loc in get_documented(sources).items(): for (filename, sec_title, keyword, toctree) in loc: if toctree is not None: path = os.path.join(os.path.dirname(filename), toctree) names[name] = os.path.abspath(path) # write for name, path in sorted(names.items()): path = output_dir or path ensuredir(path) try: obj, name = import_by_name(name) except ImportError, e: warn('failed to import %r: %s' % (name, e)) continue fn = os.path.join(path, name + (suffix or '.rst')) # skip it if it exists if os.path.isfile(fn): continue f = open(fn, 'w') try: if inspect.ismodule(obj): # XXX replace this with autodoc's API? tmpl = env.get_template('module') functions = [getattr(obj, item).__name__ for item in dir(obj) if inspect.isfunction(getattr(obj, item))] classes = [getattr(obj, item).__name__ for item in dir(obj) if inspect.isclass(getattr(obj, item)) and not issubclass(getattr(obj, item), Exception)] exceptions = [getattr(obj, item).__name__ for item in dir(obj) if inspect.isclass(getattr(obj, item)) and issubclass(getattr(obj, item), Exception)] rendered = tmpl.render(name=name, underline='='*len(name), functions=functions, classes=classes, exceptions=exceptions, len_functions=len(functions), len_classes=len(classes), len_exceptions=len(exceptions)) f.write(rendered) else: f.write('%s\n%s\n\n' % (name, '='*len(name))) if inspect.isclass(obj): if issubclass(obj, Exception): f.write(format_modulemember(name, 'autoexception')) else: f.write(format_modulemember(name, 'autoclass')) elif inspect.ismethod(obj) or inspect.ismethoddescriptor(obj): f.write(format_classmember(name, 'automethod')) elif callable(obj): f.write(format_modulemember(name, 'autofunction')) elif hasattr(obj, '__get__'): f.write(format_classmember(name, 'autoattribute')) else: f.write(format_modulemember(name, 'autofunction')) finally: f.close()
def generate_autosummary_docs(sources, output_dir=None, suffix='.rst', warn=_simple_warn, info=_simple_info, base_path=None, builder=None, template_dir=None): showed_sources = list(sorted(sources)) if len(showed_sources) > 20: showed_sources = showed_sources[:10] + ['...'] + showed_sources[-10:] info('[autosummary] generating autosummary for: %s' % ', '.join(showed_sources)) if output_dir: info('[autosummary] writing to %s' % output_dir) if base_path is not None: sources = [os.path.join(base_path, filename) for filename in sources] # create our own templating environment template_dirs = [os.path.join(package_dir, 'ext', 'autosummary', 'templates')] if builder is not None: # allow the user to override the templates template_loader = BuiltinTemplateLoader() template_loader.init(builder, dirs=template_dirs) else: if template_dir: template_dirs.insert(0, template_dir) template_loader = FileSystemLoader(template_dirs) template_env = SandboxedEnvironment(loader=template_loader) # read items = find_autosummary_in_files(sources) # remove possible duplicates items = list(dict([(item, True) for item in items]).keys()) # keep track of new files new_files = [] # write for name, path, template_name in sorted(items, key=str): if path is None: # The corresponding autosummary:: directive did not have # a :toctree: option continue path = output_dir or os.path.abspath(path) ensuredir(path) try: name, obj, parent = import_by_name(name) except ImportError as e: warn('[autosummary] failed to import %r: %s' % (name, e)) continue fn = os.path.join(path, name + suffix) # skip it if it exists if os.path.isfile(fn): continue new_files.append(fn) f = open(fn, 'w') try: doc = get_documenter(obj, parent) if template_name is not None: template = template_env.get_template(template_name) else: try: template = template_env.get_template('autosummary/%s.rst' % doc.objtype) except TemplateNotFound: template = template_env.get_template('autosummary/base.rst') def get_members(obj, typ, include_public=[]): items = [] for name in dir(obj): try: documenter = get_documenter(safe_getattr(obj, name), obj) except AttributeError: continue if documenter.objtype == typ: items.append(name) public = [x for x in items if x in include_public or not x.startswith('_')] return public, items ns = {} if doc.objtype == 'module': ns['members'] = dir(obj) ns['functions'], ns['all_functions'] = \ get_members(obj, 'function') ns['classes'], ns['all_classes'] = \ get_members(obj, 'class') ns['exceptions'], ns['all_exceptions'] = \ get_members(obj, 'exception') elif doc.objtype == 'class': ns['members'] = dir(obj) ns['methods'], ns['all_methods'] = \ get_members(obj, 'method', ['__init__']) ns['attributes'], ns['all_attributes'] = \ get_members(obj, 'attribute') parts = name.split('.') if doc.objtype in ('method', 'attribute'): mod_name = '.'.join(parts[:-2]) cls_name = parts[-2] obj_name = '.'.join(parts[-2:]) ns['class'] = cls_name else: mod_name, obj_name = '.'.join(parts[:-1]), parts[-1] ns['fullname'] = name ns['module'] = mod_name ns['objname'] = obj_name ns['name'] = parts[-1] ns['objtype'] = doc.objtype ns['underline'] = len(name) * '=' rendered = template.render(**ns) f.write(rendered) finally: f.close() # descend recursively to new files if new_files: generate_autosummary_docs(new_files, output_dir=output_dir, suffix=suffix, warn=warn, info=info, base_path=base_path, builder=builder, template_dir=template_dir)
def generate_autosummary_docs( sources, output_dir=None, suffix=".rst", warn=_simple_warn, info=_simple_info, base_path=None, builder=None, template_dir=None, ): showed_sources = list(sorted(sources)) if len(showed_sources) > 20: showed_sources = showed_sources[:10] + ["..."] + showed_sources[-10:] info("[autosummary] generating autosummary for: %s" % ", ".join(showed_sources)) if output_dir: info("[autosummary] writing to %s" % output_dir) if base_path is not None: sources = [os.path.join(base_path, filename) for filename in sources] # create our own templating environment template_dirs = [os.path.join(package_dir, "ext", "autosummary", "templates")] if builder is not None: # allow the user to override the templates template_loader = BuiltinTemplateLoader() template_loader.init(builder, dirs=template_dirs) else: if template_dir: template_dirs.insert(0, template_dir) template_loader = FileSystemLoader(template_dirs) template_env = SandboxedEnvironment(loader=template_loader) # read items = find_autosummary_in_files(sources) # keep track of new files new_files = [] # write for name, path, template_name in sorted(set(items), key=str): if path is None: # The corresponding autosummary:: directive did not have # a :toctree: option continue path = output_dir or os.path.abspath(path) ensuredir(path) try: name, obj, parent, mod_name = import_by_name(name) except ImportError as e: warn("[autosummary] failed to import %r: %s" % (name, e)) continue fn = os.path.join(path, name + suffix) # skip it if it exists if os.path.isfile(fn): continue new_files.append(fn) with open(fn, "w") as f: doc = get_documenter(obj, parent) if template_name is not None: template = template_env.get_template(template_name) else: try: template = template_env.get_template("autosummary/%s.rst" % doc.objtype) except TemplateNotFound: template = template_env.get_template("autosummary/base.rst") def get_members(obj, typ, include_public=[]): items = [] for name in dir(obj): try: documenter = get_documenter(safe_getattr(obj, name), obj) except AttributeError: continue if documenter.objtype == typ: items.append(name) public = [x for x in items if x in include_public or not x.startswith("_")] return public, items ns = {} if doc.objtype == "module": ns["members"] = dir(obj) ns["functions"], ns["all_functions"] = get_members(obj, "function") ns["classes"], ns["all_classes"] = get_members(obj, "class") ns["exceptions"], ns["all_exceptions"] = get_members(obj, "exception") elif doc.objtype == "class": ns["members"] = dir(obj) ns["methods"], ns["all_methods"] = get_members(obj, "method", ["__init__"]) ns["attributes"], ns["all_attributes"] = get_members(obj, "attribute") parts = name.split(".") if doc.objtype in ("method", "attribute"): mod_name = ".".join(parts[:-2]) cls_name = parts[-2] obj_name = ".".join(parts[-2:]) ns["class"] = cls_name else: mod_name, obj_name = ".".join(parts[:-1]), parts[-1] ns["fullname"] = name ns["module"] = mod_name ns["objname"] = obj_name ns["name"] = parts[-1] ns["objtype"] = doc.objtype ns["underline"] = len(name) * "=" rendered = template.render(**ns) f.write(rendered) # descend recursively to new files if new_files: generate_autosummary_docs( new_files, output_dir=output_dir, suffix=suffix, warn=warn, info=info, base_path=base_path, builder=builder, template_dir=template_dir, )
def generate_autosummary_docs(sources, output_dir=None, suffix='.rst', warn=_simple_warn, info=_simple_info, base_path=None, builder=None, template_dir=None, imported_members=False): # type: (List[unicode], unicode, unicode, Callable, Callable, unicode, Builder, unicode, bool) -> None # NOQA showed_sources = list(sorted(sources)) if len(showed_sources) > 20: showed_sources = showed_sources[:10] + ['...'] + showed_sources[-10:] info('[autosummary] generating autosummary for: %s' % ', '.join(showed_sources)) if output_dir: info('[autosummary] writing to %s' % output_dir) if base_path is not None: sources = [os.path.join(base_path, filename) for filename in sources] # create our own templating environment template_dirs = None # type: List[unicode] template_dirs = [os.path.join(package_dir, 'ext', 'autosummary', 'templates')] template_loader = None # type: BaseLoader if builder is not None: # allow the user to override the templates template_loader = BuiltinTemplateLoader() template_loader.init(builder, dirs=template_dirs) else: if template_dir: template_dirs.insert(0, template_dir) template_loader = FileSystemLoader(template_dirs) # type: ignore template_env = SandboxedEnvironment(loader=template_loader) template_env.filters['underline'] = _underline # replace the builtin html filters template_env.filters['escape'] = rst_escape template_env.filters['e'] = rst_escape # read items = find_autosummary_in_files(sources) # keep track of new files new_files = [] # write for name, path, template_name in sorted(set(items), key=str): if path is None: # The corresponding autosummary:: directive did not have # a :toctree: option continue path = output_dir or os.path.abspath(path) ensuredir(path) try: name, obj, parent, mod_name = import_by_name(name) except ImportError as e: warn('[autosummary] failed to import %r: %s' % (name, e)) continue fn = os.path.join(path, name + suffix) # skip it if it exists if os.path.isfile(fn): continue new_files.append(fn) with open(fn, 'w') as f: doc = get_documenter(obj, parent) if template_name is not None: template = template_env.get_template(template_name) else: try: template = template_env.get_template('autosummary/%s.rst' % doc.objtype) except TemplateNotFound: template = template_env.get_template('autosummary/base.rst') def get_members(obj, typ, include_public=[], imported=False): # type: (Any, unicode, List[unicode], bool) -> Tuple[List[unicode], List[unicode]] # NOQA items = [] # type: List[unicode] for name in dir(obj): try: value = safe_getattr(obj, name) except AttributeError: continue documenter = get_documenter(value, obj) if documenter.objtype == typ: if typ == 'method': items.append(name) elif imported or getattr(value, '__module__', None) == obj.__name__: # skip imported members if expected items.append(name) public = [x for x in items if x in include_public or not x.startswith('_')] return public, items ns = {} # type: Dict[unicode, Any] if doc.objtype == 'module': ns['members'] = dir(obj) ns['functions'], ns['all_functions'] = \ get_members(obj, 'function', imported=imported_members) ns['classes'], ns['all_classes'] = \ get_members(obj, 'class', imported=imported_members) ns['exceptions'], ns['all_exceptions'] = \ get_members(obj, 'exception', imported=imported_members) elif doc.objtype == 'class': ns['members'] = dir(obj) ns['methods'], ns['all_methods'] = \ get_members(obj, 'method', ['__init__'], imported=imported_members) ns['attributes'], ns['all_attributes'] = \ get_members(obj, 'attribute', imported=imported_members) parts = name.split('.') if doc.objtype in ('method', 'attribute'): mod_name = '.'.join(parts[:-2]) cls_name = parts[-2] obj_name = '.'.join(parts[-2:]) ns['class'] = cls_name else: mod_name, obj_name = '.'.join(parts[:-1]), parts[-1] ns['fullname'] = name ns['module'] = mod_name ns['objname'] = obj_name ns['name'] = parts[-1] ns['objtype'] = doc.objtype ns['underline'] = len(name) * '=' rendered = template.render(**ns) f.write(rendered) # type: ignore # descend recursively to new files if new_files: generate_autosummary_docs(new_files, output_dir=output_dir, suffix=suffix, warn=warn, info=info, base_path=base_path, builder=builder, template_dir=template_dir)
def generate_autosummary_docs(sources, output_dir=None, suffix=None, warn=_simple_warn, info=_simple_info): info('generating autosummary for: %s' % ', '.join(sources)) if output_dir: info('writing to %s' % output_dir) # read names = {} for name, loc in get_documented(sources).items(): for (filename, sec_title, keyword, toctree) in loc: if toctree is not None: path = os.path.join(os.path.dirname(filename), toctree) names[name] = os.path.abspath(path) # write for name, path in sorted(names.items()): path = output_dir or path ensuredir(path) try: obj, name = import_by_name(name) except ImportError, e: warn('failed to import %r: %s' % (name, e)) continue fn = os.path.join(path, name + (suffix or '.rst')) # skip it if it exists if os.path.isfile(fn): continue f = open(fn, 'w') try: if inspect.ismodule(obj): # XXX replace this with autodoc's API? tmpl = env.get_template('module') functions = [ getattr(obj, item).__name__ for item in dir(obj) if inspect.isfunction(getattr(obj, item)) ] classes = [ getattr(obj, item).__name__ for item in dir(obj) if inspect.isclass(getattr(obj, item)) and not issubclass(getattr(obj, item), Exception) ] exceptions = [ getattr(obj, item).__name__ for item in dir(obj) if inspect.isclass(getattr(obj, item)) and issubclass(getattr(obj, item), Exception) ] rendered = tmpl.render(name=name, underline='=' * len(name), functions=functions, classes=classes, exceptions=exceptions, len_functions=len(functions), len_classes=len(classes), len_exceptions=len(exceptions)) f.write(rendered) else: f.write('%s\n%s\n\n' % (name, '=' * len(name))) if inspect.isclass(obj): if issubclass(obj, Exception): f.write(format_modulemember(name, 'autoexception')) else: f.write(format_modulemember(name, 'autoclass')) elif inspect.ismethod(obj) or inspect.ismethoddescriptor(obj): f.write(format_classmember(name, 'automethod')) elif callable(obj): f.write(format_modulemember(name, 'autofunction')) elif hasattr(obj, '__get__'): f.write(format_classmember(name, 'autoattribute')) else: f.write(format_modulemember(name, 'autofunction')) finally: f.close()