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 # pragma: no cover
else:
lang = "en"
else:
settings = None # pragma: no cover
lang = "en" # pragma: no cover
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 childrens 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):
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 # pragma: no cover
reporter = self.state.document.reporter
try:
docname, lineno = reporter.get_source_and_line(self.lineno)
except AttributeError: # pragma: no cover
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