Source code for crl.doc.crldocbuilder

import logging
import os
import shutil
import subprocess
import errno
from collections import namedtuple
import pkg_resources
from .robotws_util import add_toc_tree  # pylint: disable=import-error
from .util import create_dir
from .crldocutils import (
    get_robot_builtin_path,
    generate_builtin_doc,
    generate_rflibrary_doc_from_robotdocsconf,
    error_handling)
from .robotdocsconfs import EntrypointRobotdocsconf
from .template import (
    TEMPLATE_DIR,
    TOC_TEMPLATE)
from .resource import Resource


LOGGER = logging.getLogger(__name__)


__copyright__ = 'Copyright (C) 2019, Nokia'


[docs]class InvalidRobotdocsconfType(Exception): pass
[docs]class NamespacePath(namedtuple('NamespacePath', ['namespace', 'path'])): @property def namespacepath(self): return os.path.join(self.path, self.namespace)
[docs]class CRLDocBuilder(): # pylint: disable=too-many-instance-attributes """ CRLDocBuilder generates Sphinx documentation source from Robot Framework test libraries as default. Document type can be filtered with argument if not all the 3 types (builtin, api, crl) needed **Example usage**: - CRLDockBuilder().create_doc() - CRLDockBuilder().create_doc(['crl', 'builtin']) .. note:: This tool does not generated the final documentation. The Spinx configuration files (e.g. conf.py and index.rst) must be strored either before or after the generation into **sphinxdocs** directory prior Sphinx build. """ invalid_builtinlib_starts = ['__', 'dialogs', 'dialogs_jy', 'dialogs_py', 'Remote', 'Dialogs'] def __init__(self, args): self.static = pkg_resources.resource_filename('crl.doc', 'resource') self.template_lib_rst = os.path.join(TEMPLATE_DIR, 'lib.rst') self.template_index_rst = os.path.join(TEMPLATE_DIR, 'index.rst') self.xml_root = os.path.join('xmldocs') self.rst_root = os.path.join('sphinxdocs') self.crl_index_rst_file = os.path.join(self.rst_root, 'crl_index.rst') self.rst_crl_api = os.path.join(self.rst_root, 'crl_api') self.rst_crl = os.path.join(self.rst_root, 'crl') self.relative_path_to_crl = 'crl' self.rst_builtin = os.path.join(self.rst_root, 'builtin') self._args = args @property def tmpsrc(self): return 'tmpsrc' def create_doc(self): self._clean_temp_files() self._generate_auto_index_docs() self._generate_special_index_docs() def _clean_temp_files(self): for directory in self._generated_dirs: shutil.rmtree(directory, True) def _generate_auto_index_docs(self): self._generate_docspec_docs(self._methods_with_auto_index) self._generate_resource_docs() self._add_toc_tree() def _generate_special_index_docs(self): self._generate_docspec_docs(self._methods_with_special_index) self._generate_robotdocsconfs_docs() def _generate_docspec_docs(self, methods): for docspec, method in methods: if self._docspec_matches(docspec): method() @property def _methods_with_auto_index(self): return [('builtin', self._generate_builtin_documentation), ('api', self._generate_api_documentation)] @property def _methods_with_special_index(self): return [('crl', self._generate_crl_documentation)] def _docspec_matches(self, docspec): return set(['all', docspec]) & set(self._args.docspec) def _generate_robotdocsconfs_docs(self): for robot_conf in self._args.robotdocsconfs: self._generate_docs_for_robotdocs(robot_conf) def _generate_docs_for_robotdocs(self, robotdocsconf): section = robotdocsconf.metadata.get('Name') robotconftoxml = generate_rflibrary_doc_from_robotdocsconf( robotdocsconfs=[robotdocsconf], template_file=self.template_lib_rst, xml_dir=self.xml_root, rst_dir=self._get_rst_dir_for_section(section), relative_path_to_dir=self._get_relative_path_to_section(section)) robotconftoxml.set_section(section) robotconftoxml.generate_index( index_template=self._simple_index_template, index_rst_file=self._get_index_rst_for_section(section)) @property def _simple_index_template(self): return os.path.join(TEMPLATE_DIR, 'simple_index.rst') def _get_rst_dir_for_section(self, section): return os.path.join(self.rst_root, self._get_relative_path_to_section(section)) def _get_index_rst_for_section(self, section): return os.path.join(self.rst_root, '{}_index.rst'.format( self._get_relative_path_to_section(section))) @staticmethod def _get_relative_path_to_section(section): return section.replace(' ', '_').lower()[:50] @property def _generated_dirs(self): return [self.xml_root, self.rst_builtin, self.rst_crl_api, self.rst_crl, self.tmpsrc] @property def crl_namespaces(self): return ['cloudtaf', 'crl'] def _generate_builtin_documentation(self): for builtin_lib in os.listdir(get_robot_builtin_path()): if (builtin_lib.endswith('.py') and not self._lib_starts_with_any_invalid(builtin_lib)): generate_builtin_doc(builtin_lib, TEMPLATE_DIR, self.xml_root, self.rst_root) def _generate_api_documentation(self): self._generate_api_documentation_for_crl_namespaces() def _generate_api_documentation_with_filter( self, dist_filter, out, dist_translate=lambda dist: str(dist)): # pylint: disable=unnecessary-lambda for libpath in self._dist_paths(dist_filter, dist_translate): self._sphinx_apidoc(out=out, source=libpath) @staticmethod def _sphinx_apidoc(out, source): subprocess.call( 'sphinx-apidoc --implicit-namespaces -o {out} {source}'.format( out=out, source=source), shell=True) @staticmethod def _dist_paths(dist_filter, dist_translate): for _, dists in pkg_resources.working_set.entry_keys.items(): for dist in [d for d in dists if dist_filter(dist_translate(d))]: yield os.path.dirname(pkg_resources.resource_filename( dist_translate(dist), '__init__.py')) def _generate_api_documentation_for_crl_namespaces(self): for nspath in self._namespacepaths(self.crl_namespaces): self._copy_directories(nspath.namespacepath, os.path.join(self.tmpsrc, nspath.namespace)) self._sphinx_apidoc(out=self.rst_crl_api, source=self.tmpsrc) @staticmethod def _namespacepaths(namespaces): for ns in namespaces: for path, dists in pkg_resources.working_set.entry_keys.items(): for d in dists: if str(d).startswith('{}.'.format(ns)): yield NamespacePath(namespace=ns, path=path) break @staticmethod def _copy_directories(src_dir, dest_dir): try: shutil.copytree(src_dir, dest_dir) except OSError as e: if e.errno == errno.ENOTDIR: shutil.copy(src_dir, dest_dir) else: LOGGER.warning('Directory not copied. Error: %s', e) def _generate_crl_documentation(self): robotconftoxml = generate_rflibrary_doc_from_robotdocsconf( robotdocsconfs=self._robotdocsconfs, template_file=self.template_lib_rst, xml_dir=self.xml_root, rst_dir=self.rst_crl, relative_path_to_dir=self.relative_path_to_crl) robotconftoxml.set_section('Common Robot Libraries') robotconftoxml.generate_index( index_template=self.template_index_rst, index_rst_file=self.crl_index_rst_file) @property def _robotdocsconfs(self): return [r for r in self._robotdocsconf_generator()] @staticmethod def _robotdocsconf_generator(): for ep in pkg_resources.iter_entry_points(group='robotdocsconf'): with error_handling( 'Not generating Robot framework documentation for ' 'entry point {}'.format( ep)): yield EntrypointRobotdocsconf(ep) def _generate_resource_docs(self): for r in self._args.resource: dst_path = os.path.join(self.rst_root, self._flat(r)) create_dir(dst_path) r = Resource(src_path=r, dst_path=dst_path) r.generate_rst() @staticmethod def _flat(path): return path.replace(os.sep, '_')[-50:] def _lib_starts_with_any_invalid(self, lib): return ( any([lib.startswith(i) for i in self.invalid_builtinlib_starts]) or lib.lower().startswith('deprecated')) def _add_toc_tree(self): add_toc_tree(self.rst_root, TOC_TEMPLATE)