def recurse(cls): cache.add(cls) nodename = self.class_name(cls, parts) fullname = self.class_name(cls, 0) # Use first line of docstring as tooltip, if available tooltip = None try: if cls.__doc__: enc = ModuleAnalyzer.for_module(cls.__module__).encoding doc = cls.__doc__.strip().split("\n")[0] if not isinstance(doc, text_type): doc = force_decode(doc, enc) if doc: tooltip = '"%s"' % doc.replace('"', '\\"') except Exception: # might raise AttributeError for strange classes pass baselist = [self.class_name(base, parts) for base in cls.__bases__] graph.node_add\ ( ClassInfo(cls, fullname, baselist, tooltip) , nodename, baselist ) for base in cls.__bases__: if base not in cache: recurse(base)
def recurse(cls): # type: (Any) -> None if not show_builtins and cls in py_builtins: return if not private_bases and cls.__name__.startswith('_'): return nodename = self.class_name(cls, parts) fullname = self.class_name(cls, 0) # Use first line of docstring as tooltip, if available tooltip = None try: if cls.__doc__: enc = ModuleAnalyzer.for_module(cls.__module__).encoding doc = cls.__doc__.strip().split("\n")[0] if not isinstance(doc, text_type): doc = force_decode(doc, enc) if doc: tooltip = '"%s"' % doc.replace('"', '\\"') except Exception: # might raise AttributeError for strange classes pass baselist = [] # type: List[unicode] all_classes[cls] = (nodename, fullname, baselist, tooltip) for base in cls.__bases__: if not show_builtins and base in py_builtins: continue if not private_bases and base.__name__.startswith('_'): continue baselist.append(self.class_name(base, parts)) if base not in all_classes: recurse(base)
def has_tag(modname, fullname, docname, refname): entry = env._viewcode_modules.get(modname, None) # type: ignore if entry is False: return code_tags = app.emit_firstresult('viewcode-find-source', modname) if code_tags is None: try: analyzer = ModuleAnalyzer.for_module(modname) except Exception: env._viewcode_modules[modname] = False # type: ignore return analyzer.find_tags() code = analyzer.code tags = analyzer.tags else: code, tags = code_tags if entry is None or entry[0] != code: entry = code, tags, {}, refname env._viewcode_modules[modname] = entry # type: ignore _, tags, used, _ = entry if fullname in tags: used[fullname] = docname return True
def make_rst(self, section_title_set): # print('importing falcon app %s...' % self.arguments[0]) app = autohttp_import_object(self.arguments[0]) for method, path, handler in get_routes(app): docstring = handler.__doc__ if not isinstance(docstring, unicode): analyzer = ModuleAnalyzer.for_module(handler.__module__) docstring = force_decode(docstring, analyzer.encoding) if not docstring and 'include-empty-docstring' not in self.options: continue if not docstring: continue docstring = prepare_docstring(docstring) # generate section title if needed if path.startswith('/v'): section_title = '/'.join(path.split('/')[0:3]) else: section_title = path if section_title not in section_title_set: section_title_set.add(section_title) yield section_title yield '_' * len(section_title) for line in autohttp_http_directive(method, path, docstring): yield line
def make_rst(self): app = import_object(self.arguments[0]) if self.endpoints: routes = itertools.chain( *[get_routes(app, endpoint) for endpoint in self.endpoints]) else: routes = get_routes(app) # sort by path then method for method, paths, endpoint in sorted(routes, key=operator.itemgetter(1, 0)): if endpoint in self.undoc_endpoints: continue view = app._endpoints[endpoint] docstring = view.__doc__ or '' if hasattr(view, 'view_class'): meth_func = getattr(view.view_class, method.lower(), None) if meth_func and meth_func.__doc__: docstring = meth_func.__doc__ if not isinstance(docstring, six.text_type): analyzer = ModuleAnalyzer.for_module(view.__module__) docstring = force_decode(docstring, analyzer.encoding) if not docstring and 'include-empty-docstring' not in self.options: continue docstring = self.fix_docstring(docstring) docstring = prepare_docstring(docstring) for line in http_directive(method, paths, docstring): yield line
def recurse(cls): if not show_builtins and cls in py_builtins: return if not private_bases and cls.__name__.startswith('_'): return nodename = self.class_name(cls, parts) fullname = self.class_name(cls, 0) # Use first line of docstring as tooltip, if available tooltip = None try: if cls.__doc__: enc = ModuleAnalyzer.for_module(cls.__module__).encoding doc = cls.__doc__.strip().split("\n")[0] if not isinstance(doc, text_type): doc = force_decode(doc, enc) if doc: tooltip = '"%s"' % doc.replace('"', '\\"') except Exception: # might raise AttributeError for strange classes pass baselist = [] all_classes[cls] = (nodename, fullname, baselist, tooltip) for base in cls.__bases__: if not show_builtins and base in py_builtins: continue if not private_bases and base.__name__.startswith('_'): continue baselist.append(self.class_name(base, parts)) if base not in all_classes: recurse(base)
def import_ivar_by_name( name: str, prefixes: List[str] = [None], grouped_exception: bool = True) -> Tuple[str, Any, Any, str]: """Import an instance variable that has the given *name*, under one of the *prefixes*. The first name that succeeds is used. """ try: name, attr = name.rsplit(".", 1) real_name, obj, parent, modname = import_by_name( name, prefixes, grouped_exception) qualname = real_name.replace(modname + ".", "") analyzer = ModuleAnalyzer.for_module( getattr(obj, '__module__', modname)) analyzer.analyze() # check for presence in `annotations` to include dataclass attributes if (qualname, attr) in analyzer.attr_docs or (qualname, attr) in analyzer.annotations: return real_name + "." + attr, INSTANCEATTR, obj, modname except (ImportError, ValueError, PycodeError) as exc: raise ImportError from exc except ImportExceptionGroup: raise # pass through it as is raise ImportError
def make_rst(self): app = autohttp.import_object(self.arguments[0]) for method, path, endpoint in get_routes(app): try: blueprint, endpoint_internal = endpoint.split('.') if blueprint in self.undoc_blueprints: continue except ValueError: pass # endpoint is not within a blueprint if self.endpoints and endpoint not in self.endpoints: continue if endpoint in self.undoc_endpoints: continue try: static_url_path = app.static_url_path # Flask 0.7 or higher except AttributeError: static_url_path = app.static_path # Flask 0.6 or under if ('undoc-static' in self.options and endpoint == 'static' and path == static_url_path + '/(path:filename)'): continue view = app.view_functions[endpoint] docstring = view.__doc__ or '' if not isinstance(docstring, unicode): analyzer = ModuleAnalyzer.for_module(view.__module__) docstring = force_decode(docstring, analyzer.encoding) if not docstring and 'include-empty-docstring' not in self.options: continue docstring = prepare_docstring(docstring) for line in autohttp.http_directive(method, path, docstring): yield line
def make_rst(self): app = import_object(self.arguments[0]) for method, path, endpoint in get_routes(app): try: blueprint, endpoint_internal = endpoint.split('.') if blueprint in self.undoc_blueprints: continue except ValueError: pass # endpoint is not within a blueprint if self.endpoints and endpoint not in self.endpoints: continue if endpoint in self.undoc_endpoints: continue try: static_url_path = app.static_url_path # Flask 0.7 or higher except AttributeError: static_url_path = app.static_path # Flask 0.6 or under if ('undoc-static' in self.options and endpoint == 'static' and path == static_url_path + '/(path:filename)'): continue view = app.view_functions[endpoint] docstring = view.__doc__ or '' if hasattr(view, 'view_class'): meth_func = getattr(view.view_class, method.lower(), None) if meth_func and meth_func.__doc__: docstring = meth_func.__doc__ if not isinstance(docstring, unicode): analyzer = ModuleAnalyzer.for_module(view.__module__) docstring = force_decode(docstring, analyzer.encoding) if not docstring and 'include-empty-docstring' not in self.options: continue docstring = prepare_docstring(docstring) for line in http_directive(method, path, docstring): yield line
def has_tag(modname, fullname, docname, refname): entry = env._viewcode_modules.get(modname, None) # type: ignore if entry is False: return code_tags = app.emit_firstresult('viewcode-find-source', modname) if code_tags is None: try: analyzer = ModuleAnalyzer.for_module(modname) except Exception: env._viewcode_modules[modname] = False # type: ignore return if not isinstance(analyzer.code, text_type): code = analyzer.code.decode(analyzer.encoding) else: code = analyzer.code analyzer.find_tags() tags = analyzer.tags else: code, tags = code_tags if entry is None or entry[0] != code: entry = code, tags, {}, refname env._viewcode_modules[modname] = entry # type: ignore _, tags, used, _ = entry if fullname in tags: used[fullname] = docname return True
def make_rst(self, qref=False): app = import_object(self.arguments[0]) if self.endpoints: routes = itertools.chain(*[ get_routes(app, endpoint, self.order) for endpoint in self.endpoints ]) else: routes = get_routes(app, order=self.order) for method, paths, endpoint in routes: try: blueprint, _, endpoint_internal = endpoint.rpartition('.') if self.blueprints and blueprint not in self.blueprints: continue if blueprint in self.undoc_blueprints: continue except ValueError: pass # endpoint is not within a blueprint if endpoint in self.undoc_endpoints: continue try: static_url_path = app.static_url_path # Flask 0.7 or higher except AttributeError: static_url_path = app.static_path # Flask 0.6 or under if ('undoc-static' in self.options and endpoint == 'static' and static_url_path + '/(path:filename)' in paths): continue view = app.view_functions[endpoint] if self.modules and view.__module__ not in self.modules: continue if self.undoc_modules and view.__module__ in self.modules: continue docstring = view.__doc__ or '' if hasattr(view, 'view_class'): meth_func = getattr(view.view_class, method.lower(), None) if meth_func and meth_func.__doc__: docstring = meth_func.__doc__ if not isinstance(docstring, six.text_type): analyzer = ModuleAnalyzer.for_module(view.__module__) docstring = force_decode(docstring, analyzer.encoding) if not docstring and 'include-empty-docstring' not in self.options: continue docstring = prepare_docstring(docstring) if qref == True: for path in paths: row = quickref_directive(method, path, docstring) yield row else: lines = list(http_directive(method, paths, docstring)) self.state.document.settings.env.app.emit( 'autodoc-process-docstring', 'function', None, view, {}, lines) for line in lines: yield line
def inspect_routes(self, app): """Inspects the views of Flask. :param app: The Flask application. :returns: 4-tuple like ``(method, paths, view_func, view_doc)`` """ if self.endpoints: routes = itertools.chain(*[get_routes(app, endpoint, self.order) for endpoint in self.endpoints]) else: routes = get_routes(app, order=self.order) for method, paths, endpoint in routes: try: blueprint, _, endpoint_internal = endpoint.rpartition('.') if self.blueprints and blueprint not in self.blueprints: continue if blueprint in self.undoc_blueprints: continue except ValueError: pass # endpoint is not within a blueprint if endpoint in self.undoc_endpoints: continue try: static_url_path = app.static_url_path # Flask 0.7 or higher except AttributeError: static_url_path = app.static_path # Flask 0.6 or under if ('undoc-static' in self.options and endpoint == 'static' and static_url_path + '/(path:filename)' in paths): continue view = app.view_functions[endpoint] if self.modules and view.__module__ not in self.modules: continue if self.undoc_modules and view.__module__ in self.modules: continue view_class = getattr(view, 'view_class', None) if view_class is None: view_func = view else: view_func = getattr(view_class, method.lower(), None) view_doc = view.__doc__ or '' if view_func and view_func.__doc__: view_doc = view_func.__doc__ if not isinstance(view_doc, six.text_type): analyzer = ModuleAnalyzer.for_module(view.__module__) view_doc = force_decode(view_doc, analyzer.encoding) if not view_doc and 'include-empty-docstring' not in self.options: continue yield (method, paths, view_func, view_doc)
def viewcode_find_source(app, modname): if modname == 'xml_state.XMLNode': modname = 'xml_state' analyzer = ModuleAnalyzer.for_module(modname) code = analyzer.code analyzer.find_tags() tags = analyzer.tags tags = {k.replace('XMLNode.', ''): v for k, v in tags.items()} return code, tags
def test_ModuleAnalyzer_for_module_in_egg(rootdir): try: path = rootdir / 'test-pycode-egg' / 'sample-0.0.0-py3.7.egg' sys.path.insert(0, path) analyzer = ModuleAnalyzer.for_module('sample') docs = analyzer.find_attr_docs() assert docs == {('', 'CONSTANT'): ['constant on sample.py', '']} finally: sys.path.pop(0)
def get_attr_docs(self, ty): # this reaches into some undocumented stuff in sphinx to # extract the attribute documentation. analyzer = ModuleAnalyzer.for_module(ty.__module__) module_attrs = analyzer.find_attr_docs() # (scope is broken!) # Make sure we can split lines for type docs on long lines attrs_docs = {} for k, v in module_attrs.iteritems(): attrs_docs[k[1]] = "\n".join(v).strip() return attrs_docs
def _str_member_list(self): """ Generate a member listing, autosummary:: table . """ out = [] for name in ['Attributes', 'Methods']: if not self[name]: continue out += ['.. rubric:: %s' % name, ''] prefix = getattr(self, '_name', '') if prefix: prefix = '%s.' % prefix elif hasattr(self, 'name') and self.name: # This is a class: Use its name to make sure Sphinx can find # the methods and attributes prefix = '%s.' % (self.name) else: prefix = '' autosum = [] for param, _, desc in self[name]: param = param.strip() if self._obj: # Fake the attribute as a class property, but do not touch # methods if (hasattr(self._obj, '__module__') and not (hasattr(self._obj, param) and callable(getattr(self._obj, param)))): # Do not override directly provided docstrings if not len(''.join(desc).strip()): analyzer = ModuleAnalyzer.for_module(self._obj.__module__) desc = analyzer.find_attr_docs().get((self._obj.__name__, param), '') # Only fake a property if we got a docstring if len(''.join(desc).strip()): setattr(self._obj, param, property(lambda self: None, doc='\n'.join(desc))) if len(prefix): autosum += [" ~%s%s" % (prefix, param)] else: autosum += [" %s" % param] if autosum: out += ['.. autosummary::', ''] out += autosum out += [''] return out
def _str_member_list(self): """ Generate a member listing, autosummary:: table . """ out = [] for name in ['Attributes', 'Methods']: if not self[name]: continue out += [f'.. rubric:: {name}', ''] prefix = getattr(self, '_name', '') if prefix: prefix = f'{prefix}.' autosum = [] for param, _, desc in self[name]: param = param.strip() if self._obj: # Fake the attribute as a class property, but do not touch # methods if hasattr(self._obj, '__module__') and not ( hasattr(self._obj, param) and callable(getattr(self._obj, param))): # Do not override directly provided docstrings if not len(''.join(desc).strip()): analyzer = ModuleAnalyzer.for_module( self._obj.__module__) desc = analyzer.find_attr_docs().get( (self._obj.__name__, param), '') # Only fake a property if we got a docstring if len(''.join(desc).strip()): setattr( self._obj, param, property(lambda self: None, doc='\n'.join(desc)), ) if len(prefix): autosum += [f" ~{prefix}{param}"] else: autosum += [f" {param}"] if autosum: out += ['.. autosummary::', ''] out += autosum out += [''] return out
def parse_module(self, modname: str): if modname in self.cache: return self.cache[modname] logger.debug(f'Parsing "{modname}"') result = self.cache[modname] = {} analyzer = ModuleAnalyzer.for_module(modname) analyzer.analyze() tree = ast.parse(analyzer.code) for fullname, decorator_name in self.iter_find_slot_defs(tree): result[fullname] = decorator_name return result
def parse_override_docs(namespace, version): import_namespace(namespace, version) try: ma = ModuleAnalyzer.for_module("pgi.overrides.%s" % namespace) except PycodeError: return {} docs = {} for key, value in ma.find_attr_docs().iteritems(): docs[namespace + "." + ".".join(filter(None, key))] = "\n".join(value) return docs
def make_rst(self, qref=False): app = import_object(self.arguments[0]) if self.endpoints: routes = itertools.chain(*[get_routes(app, endpoint, self.order) for endpoint in self.endpoints]) else: routes = get_routes(app, order=self.order) for method, paths, endpoint in routes: try: blueprint, _, endpoint_internal = endpoint.rpartition('.') if self.blueprints and blueprint not in self.blueprints: continue if blueprint in self.undoc_blueprints: continue except ValueError: pass # endpoint is not within a blueprint if endpoint in self.undoc_endpoints: continue try: static_url_path = app.static_url_path # Flask 0.7 or higher except AttributeError: static_url_path = app.static_path # Flask 0.6 or under if ('undoc-static' in self.options and endpoint == 'static' and static_url_path + '/(path:filename)' in paths): continue view = app.view_functions[endpoint] if self.modules and view.__module__ not in self.modules: continue if self.undoc_modules and view.__module__ in self.modules: continue docstring = view.__doc__ or '' if hasattr(view, 'view_class'): meth_func = getattr(view.view_class, method.lower(), None) if meth_func and meth_func.__doc__: docstring = meth_func.__doc__ if not isinstance(docstring, six.text_type): analyzer = ModuleAnalyzer.for_module(view.__module__) docstring = force_decode(docstring, analyzer.encoding) if not docstring and 'include-empty-docstring' not in self.options: continue docstring = prepare_docstring(docstring) if qref == True: for path in paths: row = quickref_directive(method, path, docstring) yield row else: for line in http_directive(method, paths, docstring): yield line
def properties(self): if self._cls is None: return [] analyzer = ModuleAnalyzer.for_module(self._cls.__module__) instance_members = set([attr_name for (class_name, attr_name) in analyzer.find_attr_docs().keys() if class_name == self._cls.__name__]) class_members = set([name for name, func in getattr(self._cls, '__dict__').iteritems() if not name.startswith('_') and (func is None or inspect.isdatadescriptor(func))]) return instance_members | class_members
def properties(self): if self._cls is None: return [] analyzer = ModuleAnalyzer.for_module(self._cls.__module__) instance_members = {attr_name for (class_name, attr_name) in analyzer.find_attr_docs().keys() if class_name == self._cls.__name__} class_members = {name for name, func in getattr(self._cls, '__dict__').items() if not name.startswith('_') and (func is None or inspect.isdatadescriptor(func))} return instance_members | class_members
def config_attrs(config): annotations, defaults = config.config.annotations_and_defaults() analyzer = ModuleAnalyzer.for_module(config.config.__module__) attr_docs = { attr: list(lines) for (classname, attr), lines in analyzer.find_attr_docs().items() if classname == config.name } return [ Attribute(type, name, defaults.get(name, NO_DEFAULT), attr_docs.get(name, "")) for name, type in annotations.items() ]
def get_module_attrs(members: Any) -> Tuple[List[str], List[str]]: """Find module attributes with docstrings.""" attrs, public = [], [] try: analyzer = ModuleAnalyzer.for_module(name) attr_docs = analyzer.find_attr_docs() for namespace, attr_name in attr_docs: if namespace == '' and attr_name in members: attrs.append(attr_name) if not attr_name.startswith('_'): public.append(attr_name) except PycodeError: pass # give up if ModuleAnalyzer fails to parse code return public, attrs
def get_iattributes(obj): items = list() name = obj.__name__ obj_attr = dir(obj) analyzer = ModuleAnalyzer.for_module(obj.__module__) attr_docs = analyzer.find_attr_docs() for pair, doc in attr_docs.items(): if name != pair[0]: continue if not pair[1] in obj_attr: items.append({"name": pair[1], "doc": '\n '.join(doc)}) items.sort(key=lambda d: d["name"]) return items
def get_iattributes(obj): items = [] name = obj.__name__ obj_attr = dir(obj) analyzer = ModuleAnalyzer.for_module(obj.__module__) attr_docs = analyzer.find_attr_docs() for pair, doc in attr_docs.iteritems(): if name!=pair[0]: continue if not pair[1] in obj_attr: items.append({"name":pair[1], "doc":'\n '.join(doc)}) items.sort(key=lambda d: d["name"]) return items
def run(self): module_name = str(self.arguments[0]) module = importlib.import_module(module_name) analyzer = ModuleAnalyzer.for_module(module_name) attr_docs = analyzer.find_attr_docs() with open(module.__file__) as module_file: symbols = symtable.symtable(module_file.read(), module.__file__, "exec") members = [] for name in dir(module): member = getattr(module, name) # Ignore private members if name.startswith("_"): continue # Ignore imported modules if isinstance(member, types.ModuleType): continue # Ignore members imported from other modules member_module_name = getattr(member, "__module__", None) if member_module_name is None: try: if symbols.lookup(name).is_imported(): continue except KeyError: continue else: if member_module_name != module_name: continue documenter = get_documenter(self.env.app, member, module) # Ignore data items that do not have docstrings if documenter.objtype == "data" and ("", name) not in attr_docs: continue members.append(name) # Sort members in the order they appear in source code tagorder = analyzer.tagorder members.sort(key=lambda name: tagorder.get(name, len(tagorder))) self.content = [f"{module_name}.{member}" for member in members] return super().run()
def sort_members( self, documenters: List[Tuple[Documenter, bool]], order: str, ) -> List[Tuple[Documenter, bool]]: """ Sort the TypedDict's members. :param documenters: :param order: """ # The documenters for the keys, in the desired order documenters = super().sort_members(documenters, order) # Mapping of key names to docstrings (as list of strings) docstrings = { k[1]: v for k, v in ModuleAnalyzer.for_module( self.object.__module__).find_attr_docs().items() } required_keys = [] optional_keys = [] types = get_type_hints(self.object) for d in documenters: name = d[0].name.split('.')[-1] if name in self.object.__required_keys__: required_keys.append(name) elif name in self.object.__optional_keys__: optional_keys.append(name) # else: warn user. This shouldn't ever happen, though. sourcename = self.get_sourcename() if required_keys: self.add_line('', sourcename) self.add_line(":Required Keys:", sourcename) self.document_keys(required_keys, types, docstrings) self.add_line('', sourcename) if optional_keys: self.add_line('', sourcename) self.add_line(":Optional Keys:", sourcename) self.document_keys(optional_keys, types, docstrings) self.add_line('', sourcename) return []
def resolve_arg_doc_and_index( self, name, dataclass: type) -> Optional[Tuple[Tuple[int, int], str]]: if self.settings.inherited_options: bases = dataclass.__mro__ else: bases = [dataclass] for i, base in enumerate(bases): analyzer = ModuleAnalyzer.for_module(base.__module__) docs = analyzer.find_attr_docs() if (base.__qualname__, name) in docs: tag = analyzer.tagorder[f'{base.__qualname__}.{name}'] return (-i, tag), self.canonize_docstring('\n'.join( docs[base.__qualname__, name])) return None
def import_ivar_by_name(name: str, prefixes: List[str] = [None]) -> Tuple[str, Any, Any, str]: """Import an instance variable that has the given *name*, under one of the *prefixes*. The first name that succeeds is used. """ try: name, attr = name.rsplit(".", 1) real_name, obj, parent, modname = import_by_name(name, prefixes) qualname = real_name.replace(modname + ".", "") analyzer = ModuleAnalyzer.for_module(modname) if (qualname, attr) in analyzer.find_attr_docs(): return real_name + "." + attr, INSTANCEATTR, obj, modname except (ImportError, ValueError, PycodeError): pass raise ImportError
def isinstanceattribute(self) -> bool: """ Check the subject is an instance attribute. """ try: analyzer = ModuleAnalyzer.for_module(self.modname) attr_docs = analyzer.find_attr_docs() if self.objpath: key = ('.'.join(self.objpath[:-1]), self.objpath[-1]) if key in attr_docs: return True return False except PycodeError: return False
def generate( self, more_content=None, real_modname=None, check_module=False, all_members=False, ): """ Generate reST for the object given by *self.name*, and possibly members. If *more_content* is given, include that content. If *real_modname* is given, use that module name to find attribute docs. If *check_module* is True, only generate if the object is defined in the module name it is imported from. If *all_members* is True, document all members. """ if not self.parse_name(): # need a module to import self.directive.warn( "don't know which module to import for autodocumenting " '%r (try placing a "module" or "currentmodule" directive ' "in the document, or giving an explicit module name)" % self.name ) return # now, import the module and get object to document if not self.import_object(): return # If there is no real module defined, figure out which to use. # The real module is used in the module analyzer to look up the module # where the attribute documentation would actually be found in. # This is used for situations where you have a module that collects the # functions and classes of internal submodules. self.real_modname = real_modname or self.get_real_modname() # try to also get a source code analyzer for attribute docs try: self.analyzer = ModuleAnalyzer.for_module(self.real_modname) # parse right now, to get PycodeErrors on parsing (results will # be cached anyway) self.analyzer.find_attr_docs() except PycodeError, err: # no source file -- e.g. for builtin and C modules self.analyzer = None # at least add the module.__file__ as a dependency if hasattr(self.module, "__file__") and self.module.__file__: self.directive.filename_set.add(self.module.__file__)
def register_source(app, env, modname): """ Registers source code. :param app: application :param env: environment of the plugin :param modname: name of the module to load :return: True if the code is registered successfully, False otherwise """ entry = env._viewcode_modules.get(modname, None) if entry is False: print(f"[{modname}] Entry is false for ") return False code_tags = app.emit_firstresult("viewcode-find-source", modname) if code_tags is None: try: analyzer = ModuleAnalyzer.for_module(modname) except Exception as ex: logger.info( "Module \"%s\" could not be loaded. Full source will not be available. \"%s\"", modname, ex) # We cannot use regular warnings or exception methods because those warnings are interpreted # by running python process and converted into "real" warnings, so we need to print the # traceback here at info level tb = traceback.format_exc() logger.info("%s", tb) env._viewcode_modules[modname] = False return False if not isinstance(analyzer.code, str): code = analyzer.code.decode(analyzer.encoding) else: code = analyzer.code analyzer.find_tags() tags = analyzer.tags else: code, tags = code_tags if entry is None or entry[0] != code: entry = code, tags, {}, "" env._viewcode_modules[modname] = entry return True
def has_tag(modname, fullname, docname): entry = env._viewcode_modules.get(modname, None) if entry is None: try: analyzer = ModuleAnalyzer.for_module(modname) except Exception: env._viewcode_modules[modname] = False return analyzer.find_tags() entry = analyzer.code.decode(analyzer.encoding), analyzer.tags, {} env._viewcode_modules[modname] = entry elif entry is False: return code, tags, used = entry if fullname in tags: used[fullname] = docname return True
def handle_find_source(app, modname): try: module = importlib.import_module(modname) except Exception: return None if not getattr(module, '_isScenicModule', False): return None # no special handling for Python modules # Run usual analysis on the translated source to get tag dictionary try: analyzer = ModuleAnalyzer.for_module(modname) analyzer.find_tags() except Exception: return None # bail out; viewcode will try analyzing again but oh well # Return original Scenic source, plus tags (line numbers will correspond) return module._source, analyzer.tags
def make_rst(self): app = import_object(self.arguments[0]) if self.endpoints: routes = itertools.chain(*[get_routes(app, endpoint) for endpoint in self.endpoints]) else: routes = get_routes(app) for method, paths, endpoint in routes: if not self.check_regex_validate_path(paths): continue if self.check_regex_cancel_path(paths): continue try: blueprint, _, endpoint_internal = endpoint.rpartition('.') if self.blueprints and blueprint not in self.blueprints: continue if blueprint in self.undoc_blueprints: continue except ValueError: pass # endpoint is not within a blueprint if endpoint in self.undoc_endpoints: continue try: static_url_path = app.static_url_path # Flask 0.7 or higher except AttributeError: static_url_path = app.static_path # Flask 0.6 or under if ('undoc-static' in self.options and endpoint == 'static' and static_url_path + '/(path:filename)' in paths): continue view = app.view_functions[endpoint] docstring = view.__doc__ or '' if hasattr(view, 'view_class'): meth_func = getattr(view.view_class, method.lower(), None) if meth_func and meth_func.__doc__: docstring = meth_func.__doc__ if not isinstance(docstring, six.text_type): analyzer = ModuleAnalyzer.for_module(view.__module__) docstring = force_decode(docstring, analyzer.encoding) if not docstring and 'include-empty-docstring' not in self.options: continue docstring = prepare_docstring(docstring) for line in http_directive(method, paths, docstring): yield line
def make_rst(self): app = import_object(self.arguments[0]) for method, path, target in get_routes(app): endpoint = target.name or target.callback.__name__ if self.endpoints and endpoint not in self.endpoints: continue if endpoint in self.undoc_endpoints: continue view = target.callback docstring = view.__doc__ or '' if not isinstance(docstring, six.text_type): analyzer = ModuleAnalyzer.for_module(view.__module__) docstring = force_decode(docstring, analyzer.encoding) if not docstring and 'include-empty-docstring' not in self.options: continue docstring = prepare_docstring(docstring) for line in http_directive(method, path, docstring): yield line
def generate(self, more_content=None, real_modname=None, check_module=False, all_members=False): """Generate reST for the object given by *self.name*, and possibly for its members. If *more_content* is given, include that content. If *real_modname* is given, use that module name to find attribute docs. If *check_module* is True, only generate if the object is defined in the module name it is imported from. If *all_members* is True, document all members. """ if not self.parse_name(): # need a module to import self.directive.warn( 'don\'t know which module to import for autodocumenting ' '%r (try placing a "module" or "currentmodule" directive ' 'in the document, or giving an explicit module name)' % self.name) return # now, import the module and get object to document if not self.import_object(): return # If there is no real module defined, figure out which to use. # The real module is used in the module analyzer to look up the module # where the attribute documentation would actually be found in. # This is used for situations where you have a module that collects the # functions and classes of internal submodules. self.real_modname = real_modname or self.get_real_modname() # try to also get a source code analyzer for attribute docs try: self.analyzer = ModuleAnalyzer.for_module(self.real_modname) # parse right now, to get PycodeErrors on parsing (results will # be cached anyway) self.analyzer.find_attr_docs() except PycodeError, err: self.env.app.debug('[autodoc] module analyzer failed: %s', err) # no source file -- e.g. for builtin and C modules self.analyzer = None # at least add the module.__file__ as a dependency if hasattr(self.module, '__file__') and self.module.__file__: self.directive.filename_set.add(self.module.__file__)
def register_source(app, env, modname): """ Registers source code. :param app: application :param env: environment of the plugin :param modname: name of the module to load :return: True if the code is registered successfully, False otherwise """ entry = env._viewcode_modules.get(modname, None) # type: ignore if entry is False: print("[%s] Entry is false for " % modname) return False code_tags = app.emit_firstresult("viewcode-find-source", modname) if code_tags is None: # noinspection PyBroadException try: analyzer = ModuleAnalyzer.for_module(modname) except Exception as ex: # pylint: disable=broad-except logger.info( "Module \"%s\" could not be loaded. Full source will not be available. \"%s\"", modname, ex) env._viewcode_modules[modname] = False # type: ignore return False if not isinstance(analyzer.code, text_type): code = analyzer.code.decode(analyzer.encoding) else: code = analyzer.code analyzer.find_tags() tags = analyzer.tags else: code, tags = code_tags if entry is None or entry[0] != code: entry = code, tags, {}, "" env._viewcode_modules[modname] = entry # type: ignore return True
def has_tag(modname, fullname, docname): entry = env._viewcode_modules.get(modname, None) try: analyzer = ModuleAnalyzer.for_module(modname) except Exception: env._viewcode_modules[modname] = False return if not isinstance(analyzer.code, str): code = analyzer.code.decode(analyzer.encoding) else: code = analyzer.code if entry is None or entry[0] != code: analyzer.find_tags() entry = code, analyzer.tags, {} env._viewcode_modules[modname] = entry elif entry is False: return code, tags, used = entry if fullname in tags: used[fullname] = docname return True
def has_tag(modname, fullname, docname, refname): entry = env._viewcode_modules.get(modname, None) if entry is None: try: analyzer = ModuleAnalyzer.for_module(modname) except Exception: env._viewcode_modules[modname] = False return analyzer.find_tags() if not isinstance(analyzer.code, text_type): code = analyzer.code.decode(analyzer.encoding) else: code = analyzer.code entry = code, analyzer.tags, {}, refname env._viewcode_modules[modname] = entry elif entry is False: return _, tags, used, _ = entry if fullname in tags: used[fullname] = docname return True
def _update_tags(env, modname, fullname=None): # Analyze modname code and return True if fullname is present. Also caches # the analysis results in _viewcode_modules. entry = env._viewcode_modules.get(modname, None) if entry is None: try: analyzer = ModuleAnalyzer.for_module(modname) except Exception: env._viewcode_modules[modname] = False return analyzer.find_tags() if not isinstance(analyzer.code, str): code = analyzer.code.decode(analyzer.encoding) else: code = analyzer.code entry = code, analyzer.tags, {} env._viewcode_modules[modname] = entry elif entry is False: return code, tags, used = entry if fullname is not None and fullname in tags: used[fullname] = env.docname return True
def doctree_read(app, doctree): env = app.builder.env if not hasattr(env, '_viewcode_modules'): env._viewcode_modules = {} def has_tag(modname, fullname, docname, refname): entry = env._viewcode_modules.get(modname, None) try: analyzer = ModuleAnalyzer.for_module(modname) except Exception: env._viewcode_modules[modname] = False return if not isinstance(analyzer.code, text_type): code = analyzer.code.decode(analyzer.encoding) else: code = analyzer.code if entry is None or entry[0] != code: analyzer.find_tags() entry = code, analyzer.tags, {}, refname env._viewcode_modules[modname] = entry elif entry is False: return _, tags, used, _ = entry if fullname in tags: used[fullname] = docname return True for objnode in doctree.traverse(addnodes.desc): if objnode.get('domain') != 'py': continue names = set() for signode in objnode: if not isinstance(signode, addnodes.desc_signature): continue modname = signode.get('module') fullname = signode.get('fullname') refname = modname if env.config.viewcode_import: modname = _get_full_modname(app, modname, fullname) if not modname: continue fullname = signode.get('fullname') if not has_tag(modname, fullname, env.docname, refname): continue if fullname in names: # only one link per name, please continue names.add(fullname) pagename = '_modules/' + modname.replace('.', '/') onlynode = addnodes.only(expr='html') onlynode += addnodes.pending_xref( '', reftype='viewcode', refdomain='std', refexplicit=False, reftarget=pagename, refid=fullname, refdoc=env.docname) onlynode[0] += nodes.inline('', _('[source]'), classes=['viewcode-link']) signode += onlynode for objnode in doctree.traverse(nodes.title): rawsource = objnode.rawsource if rawsource.endswith(' module'): # HACK: use rawsource string as source of module name. modname = rawsource[:-len(' module')] entry = env._viewcode_modules.get(modname, None) try: analyzer = ModuleAnalyzer.for_module(modname) except Exception: env._viewcode_modules[modname] = False return if not isinstance(analyzer.code, text_type): code = analyzer.code.decode(analyzer.encoding) else: code = analyzer.code if entry is None or entry[0] != code: analyzer.find_tags() entry = code, analyzer.tags, {}, modname env._viewcode_modules[modname] = entry pagename = '_modules/' + modname.replace('.', '/') onlynode = addnodes.only(expr='html') onlynode += addnodes.pending_xref( '', reftype='viewcode', refdomain='std', refexplicit=False, reftarget=pagename, refid='', refdoc=env.docname) onlynode[0] += nodes.inline('', _('[source]'), classes=['viewcode-link']) objnode += onlynode
def make_rst(self): app = import_object(self.arguments[0]) yield "Services:\n" yield "" for x in self._make_toc(get_routes(app)): yield '- {}'.format(x) yield "" for method, path, endpoint in get_routes(app): try: blueprint, _, endpoint_internal = endpoint.rpartition('.') if self.blueprints and blueprint not in self.blueprints: continue if blueprint in self.undoc_blueprints: continue except ValueError: pass # endpoint is not within a blueprint if self.endpoints and endpoint not in self.endpoints: continue if endpoint in self.undoc_endpoints: continue try: static_url_path = app.static_url_path # Flask 0.7 or higher except AttributeError: static_url_path = app.static_path # Flask 0.6 or under if ('undoc-static' in self.options and endpoint == 'static' and path == static_url_path + '/(path:filename)'): continue view = app.view_functions[endpoint] docstring = view.__doc__ or '' if hasattr(view, 'view_class'): meth_func = getattr(view.view_class, method.lower(), None) if meth_func and meth_func.__doc__: docstring = meth_func.__doc__ if not isinstance(docstring, six.text_type): analyzer = ModuleAnalyzer.for_module(view.__module__) docstring = force_decode(docstring, analyzer.encoding) if not docstring and 'include-empty-docstring' not in self.options: continue #Thanks flask-classy for this :D if len(endpoint.split(":")) == 2: view_cls, view_func = endpoint.split(":") if hasattr(app, 'view_classes') and view_cls in app.view_classes: cls = app.view_classes[view_cls] members = inspect.getmembers(cls,predicate=inspect.ismethod) if hasattr(cls,'args_rules'): rules = cls.args_rules if view_func in rules: for m in members: if m[0] == view_func: docstring += m[1].__doc__.strip() docstring += '\n\n' for a in rules[view_func]: t = str(a.type).replace('type','').replace("'","").replace('<','').replace('>','') params = dict( type=t, name=str(a.name), description=str(a.description), default=str(a.default) ) if a.required: docstring += ' :<json {type} {name}: {description}.\n'.format(**params) else: docstring += ' :<json {type} {name}: *(optional)* {description}. *Default*={default}\n'.format(**params) docstring = prepare_docstring(docstring) for line in http_directive(method, path, docstring): yield line
def test_ModuleAnalyzer_for_module(): analyzer = ModuleAnalyzer.for_module('sphinx') assert analyzer.modname == 'sphinx' assert analyzer.srcname == SPHINX_MODULE_PATH assert analyzer.encoding == 'utf-8'
def get_items(self, names): """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 = 50 for name in names: display_name = name if name.startswith('~'): name = name[1:] display_name = name.split('.')[-1] try: real_name, obj, parent, modname = import_by_name(name, prefixes=prefixes) except ImportError: self.warn('failed to import %s' % name) items.append((name, '', '', name)) continue self.result = ViewList() # 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 documenter = get_documenter(obj, parent)(self, full_name) if not documenter.parse_name(): self.warn('failed to parse name %s' % real_name) items.append((display_name, '', '', real_name)) continue if not documenter.import_object(): self.warn('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: documenter.env.app.debug( '[autodoc] module analyzer failed: %s', err) # no source file -- e.g. for builtin and C modules documenter.analyzer = None # -- 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 documenter.add_content(None) doc = list(documenter.process_doc([self.result.data])) while doc and not doc[0].strip(): doc.pop(0) # If there's a blank line, then we can assume the first sentence / # paragraph has ended, so anything after shouldn't be part of the # summary for i, piece in enumerate(doc): if not piece.strip(): doc = doc[:i] break # Try to find the "first sentence", which may span multiple lines m = re.search(r"^([A-Z].*?\.)(?:\s|$)", " ".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 get_items(self, names): # type: (List[unicode]) -> List[Tuple[unicode, unicode, unicode, unicode]] """Try to import the given names, and return a list of ``[(name, signature, summary_string, real_name), ...]``. """ prefixes = get_import_prefixes_from_env(self.env) items = [] # type: List[Tuple[unicode, unicode, unicode, unicode]] 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, modname = import_by_name(name, prefixes=prefixes) except ImportError: self.warn('failed to import %s' % name) items.append((name, '', '', name)) continue self.result = ViewList() # 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 documenter = get_documenter(self.env.app, obj, parent)(self, full_name) if not documenter.parse_name(): self.warn('failed to parse name %s' % real_name) items.append((display_name, '', '', real_name)) continue if not documenter.import_object(): self.warn('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 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.result.data[:], self.state.document) items.append((display_name, sig, summary, real_name)) return items
def get_attr_docs(self, ty): # this reaches into some undocumented stuff in sphinx to # extract the attribute documentation. analyzer = ModuleAnalyzer.for_module(ty.__module__) module_attrs = analyzer.find_attr_docs() # (scope is broken!) return {k[1]: v[0] for k, v in module_attrs.iteritems()}
def run(self): try: modfn = ModuleAnalyzer.for_module(self.arguments[0]).srcname except PycodeError, e: warnstr = "can't find module %s for todomodule: %s" % (self.arguments[0], e) return [self.state.document.reporter.warning(warnstr, lineno=self.lineno)]
def test_ModuleAnalyzer_for_module(): analyzer = ModuleAnalyzer.for_module('sphinx') assert analyzer.modname == 'sphinx' assert analyzer.srcname in (SPHINX_MODULE_PATH, os.path.abspath(SPHINX_MODULE_PATH)) assert analyzer.encoding == 'utf-8'
def generate(self, more_content=None, real_modname=None, check_module=False, all_members=False): # type: (Any, str, bool, bool) -> None """Generate reST for the object given by *self.name*, and possibly for its members. If *more_content* is given, include that content. If *real_modname* is given, use that module name to find attribute docs. If *check_module* is True, only generate if the object is defined in the module name it is imported from. If *all_members* is True, document all members. """ # print('generate', more_content, real_modname, check_module, all_members) # print(self.name) # print('---------------------') # > generate None fontbakery.profiles.post True True # > fontbakery.profiles.post::com_google_fonts_check_post_table_version # > --------------------- # # > generate None fontbakery.profiles.shared_conditions True True # > fontbakery.profiles.shared_conditions::glyph_metrics_stats # > --------------------- if not self.parse_name(): # need a module to import logger.warning( __('don\'t know which module to import for autodocumenting ' '%r (try placing a "module" or "currentmodule" directive ' 'in the document, or giving an explicit module name)') % self.name, type='autodoc') return # now, import the module and get object to document if not self.import_object(): return # doesn't do anything! # if self.objtype == 'fontbakerycheck': # self.name = self.object.id # If there is no real module defined, figure out which to use. # The real module is used in the module analyzer to look up the module # where the attribute documentation would actually be found in. # This is used for situations where you have a module that collects the # functions and classes of internal submodules. self.real_modname = real_modname or self.get_real_modname() # type: str # try to also get a source code analyzer for attribute docs try: self.analyzer = ModuleAnalyzer.for_module(self.real_modname) # parse right now, to get PycodeErrors on parsing (results will # be cached anyway) self.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 self.analyzer = None # at least add the module.__file__ as a dependency if hasattr(self.module, '__file__') and self.module.__file__: self.directive.filename_set.add(self.module.__file__) else: self.directive.filename_set.add(self.analyzer.srcname) # check __module__ of object (for members not given explicitly) if check_module: if not self.check_module(): return sourcename = self.get_sourcename() # make sure that the result starts with an empty line. This is # necessary for some situations where another directive preprocesses # reST and no starting newline is present self.add_line('', sourcename) # format the object's signature, if any sig = self.format_signature() # generate the directive header and options, if applicable self.add_directive_header(sig) self.add_line('', sourcename) # e.g. the module directive doesn't have content self.indent += self.content_indent # add all content (from docstrings, attribute docs etc.) self.add_content(more_content) # document members, if possible self.document_members(all_members)
def generate(self, more_content=None, real_modname=None, check_module=False, all_members=False): """ Generate reST for the object given by *self.name*, and possibly members. If *more_content* is given, include that content. If *real_modname* is given, use that module name to find attribute docs. If *check_module* is True, only generate if the object is defined in the module name it is imported from. If *all_members* is True, document all members. """ if not self.parse_name(): # need a module to import self.directive.warn( 'don\'t know which module to import for autodocumenting ' '%r (try placing a "module" or "currentmodule" directive ' 'in the document, or giving an explicit module name)' % self.name) return # now, import the module and get object to document if not self.import_object(): return # If there is no real module defined, figure out which to use. # The real module is used in the module analyzer to look up the module # where the attribute documentation would actually be found in. # This is used for situations where you have a module that collects the # functions and classes of internal submodules. self.real_modname = real_modname or self.get_real_modname() # try to also get a source code analyzer for attribute docs try: self.analyzer = ModuleAnalyzer.for_module(self.real_modname) # parse right now, to get PycodeErrors on parsing (results will # be cached anyway) self.analyzer.find_attr_docs() except PycodeError: # no source file -- e.g. for builtin and C modules self.analyzer = None # at least add the module.__file__ as a dependency if hasattr(self.module, '__file__') and self.module.__file__: self.directive.filename_set.add(self.module.__file__) else: self.directive.filename_set.add(self.analyzer.srcname) # check __module__ of object (for members not given explicitly) if check_module: if not self.check_module(): return # make sure that the result starts with an empty line. This is # necessary for some situations where another directive preprocesses # reST and no starting newline is present self.add_line(u'', '<autodoc>') # format the object's signature, if any sig = self.format_signature() # generate the directive header and options, if applicable self.add_directive_header(sig) self.add_line(u'', '<autodoc>') # e.g. the module directive doesn't have content self.indent += self.content_indent # add all content (from docstrings, attribute docs etc.) self.add_content(more_content) # document members, if possible self.document_members(all_members)