import os
from jinja2 import Template
import jinja2
from multidoc.utils import indent_line
from multidoc import logger
import re
import logging
log = logging.getLogger('root')
log.setLevel('DEBUG')
log.addHandler(logger.MyHandler())
__languages__ = [
'cpp',
'py']
# constants ------------------------------------------------------------------#
# template dir for default templates
# if source
candidate_template_dirs = [
os.path.join(os.path.dirname(__file__), 'templates'), # source
os.path.join(os.path.dirname(__file__), '../../../../share/templates'),
# unix
os.path.join(os.path.dirname(__file__), '../../../../../share/templates'),
# win
]
candidate_variable_dirs = [
('CONDA_PREFIX', 'share/templates'), # shouldn't be needed with the
# above, just in case. (wont work
# without executing with conda env
# activated
('MULTIDOC_TEMPLATE_DIR', ''), # if a dev wants to set a custom set
# of templates (e.g. for testing)
]
for var, dir in candidate_variable_dirs:
if var in os.environ:
candidate_template_dirs.insert(0, os.path.join(os.environ[var], dir))
for template_dir in candidate_template_dirs:
if os.path.isdir(template_dir):
TEMPLATE_DIR = template_dir
log.info('Using template dir: {}'.format(TEMPLATE_DIR))
break
else:
raise RuntimeError('No template directory found')
# TEMPLATE_DIR = os.path.join(os.path.dirname(__file__), "templates")
# template mapping for language docstring templates
TEMPLATE_LANGUAGE_MAP = {"cpp": os.path.join(TEMPLATE_DIR, "cpp.jinja2"),
"py": os.path.join(TEMPLATE_DIR, "numpydoc.jinja2")}
TEMPLATE_PROPERTY = os.path.join(TEMPLATE_DIR, "property.jinja2")
TEMPLATE_ENUM_MEMBER = os.path.join(TEMPLATE_DIR, "enum-member.jinja2")
# template mapping for generated compiled docstrings
TEMPLATE_DOCSTRING_HEADER = os.path.join(TEMPLATE_DIR, "docstring_h.jinja2")
# base globals required for template functions
_BASE_GLOBALS = {"indent_line": indent_line}
LINKFY_PATTERN = re.compile(r"([\w.]+)")
# functions ------------------------------------------------------------------#
def linkify_type(type_str):
# You can manually specify the number of replacements by changing the 4th argument
# result = re.sub(regex, subst, type_str, 0, re.MULTILINE)
matches = set(list(re.findall(LINKFY_PATTERN, type_str)))
print(matches)
for match in matches:
print('---', type_str)
type_str = type_str.replace(match, f':class:`{match}`')
type_str = type_str.replace('[', '\[')
type_str = type_str.replace(']', '\]')
return type_str
[docs]def get_enum_member_template(**kwargs):
"""
Parameters
----------
kwargs : dict[str, Any]
One of the supported :ref:`multidoc.template.__languages__` must be set
to true in the **kwargs in order to facilitate successful docstring
deduction.
Returns
-------
"""
_globals = kwargs.pop("globals", {})
_globals.update(_BASE_GLOBALS)
_lstrip_blocks = kwargs.pop("lstrip_blocks", True)
_trim_blocks = kwargs.pop("trim_blocks", True)
# iterate through supported languages, set as False if not in kwargs.
# highly unnecessary here?
_locals = {}
for j in __languages__:
_locals[j] = kwargs.pop(j, False)
# which language is True
aux = list(map(lambda x: x is True, [_locals[i] for i in __languages__]))
# ensure only one language detected (e.g. cpp / py)
assert aux.count(True) == 1
# finally deduce lang.
# TODO: There must be a more straightforward way.
lang = __languages__[aux.index(True)]
# open language template and return.
with open(TEMPLATE_ENUM_MEMBER) as f:
t = Template(
f.read(),
lstrip_blocks=_lstrip_blocks,
trim_blocks=_trim_blocks)
# update globals dict if globals given through kwargs
t.globals.update(_globals)
# if _globals else None (currently redundant)
return t
[docs]def get_property_template(**kwargs):
"""
Parameters
----------
kwargs : dict[str, Any]
One of the supported :ref:`multidoc.template.__languages__` must be set
to true in the **kwargs in order to facilitate successful docstring
deduction.
Returns
-------
"""
_globals = kwargs.pop("globals", {})
_globals.update(_BASE_GLOBALS)
_globals["linkify_type"] = linkify_type
_lstrip_blocks = kwargs.pop("lstrip_blocks", True)
_trim_blocks = kwargs.pop("trim_blocks", True)
# iterate through supported languages, set as False if not in kwargs.
# highly unnecessary here?
_locals = {}
for j in __languages__:
_locals[j] = kwargs.pop(j, False)
# which language is True
aux = list(map(lambda x: x is True, [_locals[i] for i in __languages__]))
# ensure only one language detected (e.g. cpp / py)
assert aux.count(True) == 1
# finally deduce lang.
# TODO: There must be a more straightforward way.
lang = __languages__[aux.index(True)]
# open language template and return.
with open(TEMPLATE_PROPERTY) as f:
t = Template(
f.read(),
lstrip_blocks=_lstrip_blocks,
trim_blocks=_trim_blocks)
# update globals dict if globals given through kwargs
t.globals.update(_globals)
# if _globals else None (currently redundant)
return t
[docs]def get_docstring_template(**kwargs):
"""
Parameters
----------
kwargs : dict[str, Any]
One of the supported :ref:`multidoc.template.__languages__` must be set
to true in the **kwargs in order to facilitate successful docstring
deduction.
Returns
-------
"""
_globals = kwargs.pop("globals", {})
_globals.update(_BASE_GLOBALS)
_lstrip_blocks = kwargs.pop("lstrip_blocks", True)
_trim_blocks = kwargs.pop("trim_blocks", True)
# iterate through supported languages, set as False if not in kwargs.
# highly unnecessary here?
_locals = {}
for j in __languages__:
_locals[j] = kwargs.pop(j, False)
# which language is True
aux = list(map(lambda x: x is True, [_locals[i] for i in __languages__]))
# ensure only one language detected (e.g. cpp / py)
assert aux.count(True) == 1
# finally deduce lang.
# TODO: There must be a more straightforward way.
lang = __languages__[aux.index(True)]
# open language template and return.
with open(TEMPLATE_LANGUAGE_MAP[lang]) as f:
t = Template(
f.read(),
lstrip_blocks=_lstrip_blocks,
trim_blocks=_trim_blocks)
# update globals dict if globals given through kwargs
t.globals.update(_globals)
# if _globals else None (currently redundant)
return t
# render the templates to source code
def render_cpp_docstring(args):
t = get_docstring_template(cpp=True, py=False)
return t.render(**args.dict())
def render_python_docstring(args):
t = get_docstring_template(py=True, cpp=False)
return t.render(**args.dict())
if __name__ == "__main__":
t1 = get_docstring_template(cpp=True)
t2 = get_docstring_template(py=True)
try:
t3 = get_docstring_template(py=True, cpp=True)
except AssertionError as e:
print("success")