import io import logging import os import pickle import urllib.parse as urllib_parse import urllib.request as urllib_request from urllib.parse import urljoin from sphinx.util.inventory import InventoryFile def info_blocref( app, doctree, fromdocname, class_name, entry_name, class_node, class_node_list ): """ Log information with :epkg:`Sphinx`. :param app: application (Sphinx) :param doctree: document tree :param fromdocname: document currently being compiled :param class_name: name of the node :param entry_name: entry name in ``TITLES`` :param class_node: class node (@see cl blocref_node) :param class_node_list: class node list (@see cl blocreflist) """ incconf = f"{class_name}_include_{class_name}s" rows2 = [] for node in doctree.traverse(class_node_list): breftag = node.get("breftag", None) rows2.append(f"tag={breftag} do={app.config[incconf]}") if len(rows2) == 0: return False attr_name = f"{class_name}_all_{class_name}s" env = app.builder.env if hasattr(env, attr_name): bloc_list_env = getattr(env, attr_name) else: bloc_list_env = [] rows = [ " [info_blocref]", f"len(bloc_list_env)={len(bloc_list_env)}", ] rows.extend(rows2) rows.extend( [ f"fromdocname='{fromdocname}'", f"entry_name='{entry_name}'", f"class_name='{class_name}'", f"class_node='{class_node}'", f"class_node_list='{class_node_list}'", f"doctree='{type(doctree)}'", f"#doctree={len(doctree)}", ] ) message = " ".join(rows) logger = logging.getLogger("info_blocref") logger.info(message) return True def sphinx_lang(env, default_value="en"): """ Returns the language defined in the configuration file. :param env: environment :param default_value: default value :return: language """ if hasattr(env, "settings"): settings = env.settings if hasattr(settings, "language_code"): lang = env.settings.language_code else: lang = "en" else: settings = None lang = "en" return lang class TinyNode: """ Returned by @see fn traverse. :param parent: node parent """ def __init__(self, parent): self.parent = parent class NodeEnter(TinyNode): """ Returned by function @see fn traverse. """ pass class NodeLeave(TinyNode): """ Returned by function @see fn traverse. """ pass class WrappedNode: """ Wraps a docutils node. """ def __init__(self, node): self.node = node def traverse(node, depth=0): """ Enumerates through all children but insert a node whenever digging or leaving the children's nodes. :param node: node (from doctree) :param depth: current depth :return: enumerate (depth, node) See :class:`NodeEnter` and :class:`NodeLeave` are returned whenever entering or leaving nodes. """ if isinstance(node, WrappedNode): node = node.node ne = NodeEnter(node) nl = NodeLeave(node) yield (depth, ne) yield (depth, node) for n in node.children: for r in traverse(n, depth + 1): # noqa: UP028 yield r yield (depth, nl) def _get_data(url): """ Loads file ``objects.inv`` generated by extension :epkg:`sphinx.ext.intersphinx`. :param url: url of documentation, example ``https://pandas.pydata.org/docs/`` :return: instance of `InventoryFile` """ url_inv = urljoin(url, "objects.inv") if urllib_parse.urlparse(url_inv).scheme in ("http", "https"): user_agent = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11" # noqa: E501 headers = {"User-Agent": user_agent} req = urllib_request.Request(url_inv, None, headers) resp = urllib_request.urlopen(req) data = resp.read() else: with open(url, "rb") as fid: data = fid.read() inv = InventoryFile.load(io.BytesIO(data), url, urljoin) return inv def get_index(index_url, cache_dir): """ Retrieves documentation data for a specific module. :param url: url of documentation, example `https://pandas.pydata.org/docs/ `_ :param cache_dir: restore a cached inventory stored with pickle :return: instance of `InventoryFile` """ if cache_dir is not None: base_file = index_url.replace("/", "_").split(":")[-1] + ".pkl" full_file = os.path.join(cache_dir, base_file) if os.path.exists(full_file): with open(full_file, "rb") as f: return pickle.load(f) index = _get_data(index_url) if cache_dir is not None: with open(full_file, "wb") as f: pickle.dump(index, f) return index def get_env_state_info(self): """ Retrieves an environment and a docname inside a directive. :param self: self inside a :epkg:`sphinx` directive :return: docname, lineno """ if hasattr(self, "env") and self.env is not None: env = self.env elif hasattr(self.state.document.settings, "env"): env = self.state.document.settings.env else: env = None reporter = self.state.document.reporter try: docname, lineno = reporter.get_source_and_line(self.lineno) except AttributeError: docname = lineno = None if docname is not None: docname = docname.replace("\\", "/").split("/")[-1] res = { "env": env, "reporter_docname": docname, "docname": env.docname, "lineno": lineno, "state_document": self.state.document, "location": self.state_machine.get_source_and_line(self.lineno), } if hasattr(self, "app"): res["srcdic"] = self.app.builder.srcdir if hasattr(self, "builder"): res["srcdic"] = self.builder.srcdir if env is not None: here = os.path.dirname(env.doc2path("HERE")) if "IMPOSSIBLE:TOFIND" not in here: res["HERE"] = here keys = list(res.keys()) for k in keys: # pylint: disable=C0206 if isinstance(res[k], str): res[k] = res[k].replace("\\", "/") elif isinstance(res[k], tuple): res[k] = (res[k][0].replace("\\", "/"), res[k][1]) return res