Source code for ipypublish.convert.config_manager

import os
import inspect
import glob
import json
import importlib
import logging

from six import string_types
from jinja2 import DictLoader
import jsonschema

from ipypublish.utils import pathlib
from ipypublish import export_plugins
from ipypublish.scripts.create_template import create_template

_TEMPLATE_KEY = 'new_template'
_EXPORT_SCHEMA_FILE = "export_config.schema.json"
_EXPORT_SCHEMA = None


[docs]def handle_error(msg, err_type, raise_msg=None, log_msg=None): """handle an error, by logging it, then raising""" if raise_msg is None: raise_msg = msg if log_msg is None: log_msg = msg logging.error(log_msg) raise err_type(raise_msg)
[docs]def read_json_from_directory(dir_path, file_name, jtype): """load a json file situated in a directory""" if isinstance(dir_path, string_types): dir_path = pathlib.Path(dir_path) file_path = dir_path.joinpath(file_name) if not file_path.exists(): handle_error( "the {} does not exist: {}".format(jtype, file_path), IOError) with file_path.open() as fobj: try: data = json.load(fobj) except Exception as err: handle_error("failed to read {} ({}): {}".format( jtype, file_path, err), IOError) return data
[docs]def get_module_path(module): """return a directory path to a module""" return pathlib.Path(os.path.dirname( os.path.abspath(inspect.getfile(module))))
[docs]def read_json_from_module(module_path, file_name, jtype): """load a json file situated in a python module""" try: outline_module = importlib.import_module(module_path) except ModuleNotFoundError: handle_error( "module {} containing {} {} not found".format( module_path, jtype, file_name), ModuleNotFoundError) return read_json_from_directory(get_module_path(outline_module), file_name, jtype)
[docs]def get_export_config_path(export_key, config_folder_paths=()): # type (string, Tuple[str]) -> Union[string, None] """we search for a plugin name, which matches the supplied plugin name """ for name, jsonpath in iter_all_export_paths(config_folder_paths): if name == export_key: return pathlib.Path(jsonpath) return None
[docs]def iter_all_export_paths(config_folder_paths=(), regex="*.json"): """we iterate through all json files in the supplied plugin_folder_paths, and then in the `export_plugins` folder """ for plugin_folder_path in config_folder_paths: for jsonpath in glob.glob(os.path.join(plugin_folder_path, regex)): name = os.path.splitext(os.path.basename(jsonpath))[0] yield name, pathlib.Path(jsonpath) module_path = get_module_path(export_plugins) for jsonpath in glob.glob(os.path.join(str(module_path), regex)): name = os.path.splitext(os.path.basename(jsonpath))[0] yield name, pathlib.Path(jsonpath)
[docs]def load_export_config(export_config_path): """load the export configuration""" if isinstance(export_config_path, string_types): export_config_path = pathlib.Path(export_config_path) data = read_json_from_directory( export_config_path.parent, export_config_path.name, "export configuration") # validate against schema global _EXPORT_SCHEMA if _EXPORT_SCHEMA is None: # lazy load schema once _EXPORT_SCHEMA = read_json_from_directory( os.path.dirname(os.path.realpath(__file__)), _EXPORT_SCHEMA_FILE, "export configuration schema") try: jsonschema.validate(data, _EXPORT_SCHEMA) except jsonschema.ValidationError as err: handle_error( "validation of export config {} failed against {}: {}".format( export_config_path, _EXPORT_SCHEMA_FILE, err.message), jsonschema.ValidationError) return data
[docs]def iter_all_export_infos(config_folder_paths=(), regex="*.json"): """iterate through all export configuration and yield a dict of info""" for name, path in iter_all_export_paths(config_folder_paths, regex): data = load_export_config(path) yield dict([ ("key", str(name)), ("class", data["exporter"]["class"]), ("path", str(path)), ("description", data["description"]) ])
[docs]def create_exporter_cls(class_str): """dynamically load export class""" export_class_path = class_str.split(".") module_path = ".".join(export_class_path[0:-1]) class_name = export_class_path[-1] try: export_module = importlib.import_module(module_path) except ModuleNotFoundError: handle_error( "module {} containing exporter class {} not found".format( module_path, class_name), ModuleNotFoundError) if hasattr(export_module, class_name): export_class = getattr(export_module, class_name) else: handle_error( "module {} does not contain class {}".format( module_path, class_name), ImportError) # if a template is used we need to override the default template class BespokeTemplateExporter(export_class): """override the default template""" template_file = _TEMPLATE_KEY return BespokeTemplateExporter # type: nbconvert.
[docs]def get_export_extension(export_config_path): """return the file extension of the exporter class""" data = load_export_config(export_config_path) exporter_cls = create_exporter_cls(data["exporter"]["class"]) return exporter_cls.file_extension
[docs]def str_to_jinja(template_str): return DictLoader({_TEMPLATE_KEY: template_str})
[docs]def load_template(template_dict): if template_dict is None: return None if "directory" in template_dict["outline"]: outline_schema = read_json_from_directory( template_dict["outline"]["directory"], template_dict["outline"]["file"], "template outline") else: outline_schema = read_json_from_module( template_dict["outline"]["module"], template_dict["outline"]["file"], "template outline") segments = [] for segment in template_dict["segments"]: if "directory" in segment: seg_data = read_json_from_directory( segment["directory"], segment["file"], "template segment") else: seg_data = read_json_from_module( segment["module"], segment["file"], "template segment") segments.append(seg_data) template_str = create_template(outline_schema, segments) return str_to_jinja(template_str)