def single_load(path_or_stream, ac_parser=None, ac_template=False, ac_context=None, **options): """ Load single config file. :param path_or_stream: Configuration file path or file / file-like object :param ac_parser: Forced parser type or parser object :param ac_template: Assume configuration file may be a template file and try to compile it AAR if True :param ac_context: A dict presents context to instantiate template :param options: Optional keyword arguments such as: - Common options: - ac_namedtuple: Convert result to nested namedtuple object if True - ac_schema: JSON schema file path to validate given config file - Common backend options: - ignore_missing: Ignore and just return empty result if given file (``path_or_stream``) does not exist. - Backend specific options such as {"indent": 2} for JSON backend :return: dict or dict-like object supports merge operations """ is_path_ = is_path(path_or_stream) if is_path_: path_or_stream = anyconfig.utils.ensure_expandusr(path_or_stream) filepath = path_or_stream else: filepath = anyconfig.utils.get_path_from_stream(path_or_stream) psr = find_loader(path_or_stream, ac_parser, is_path_) if psr is None: return None schema = _load_schema(ac_template=ac_template, ac_context=ac_context, **options) options["ac_schema"] = None # It's not needed now. LOGGER.info("Loading: %s", filepath) if ac_template and filepath is not None: try: LOGGER.debug("Compiling: %s", filepath) content = anyconfig.template.render(filepath, ac_context) cnf = psr.loads(content, **options) return _maybe_validated(cnf, schema, **options) except Exception as exc: LOGGER.warning( "Failed to compile %s, fallback to no template " "mode, exc=%r", path_or_stream, exc) cnf = psr.load(path_or_stream, **options) return _maybe_validated(cnf, schema, **options)
def query(data, **options): """ Filter data with given JMESPath expression. See also: https://github.com/jmespath/jmespath.py and http://jmespath.org. :param data: Target object (a dict or a dict-like object) to query :param options: Keyword option may include 'ac_query' which is a string represents JMESPath expression. :return: Maybe queried result data, primitive (int, str, ...) or dict """ expression = options.get("ac_query", None) if expression is None or not expression: return data try: pexp = jmespath.compile(expression) return pexp.search(data) except ValueError as exc: # jmespath.exceptions.*Error inherit from it. LOGGER.warning("Failed to compile or search: exp=%s, exc=%r", expression, exc) except (NameError, AttributeError): LOGGER.warning("Filter module (jmespath) is not available. " "Do nothing.") return data
def _maybe_validated(cnf, schema, format_checker=None, **options): """ :param cnf: Configuration object :param schema: JSON schema object :param format_checker: A format property checker object of which class is inherited from jsonschema.FormatChecker, it's default if None given. :param options: Keyword options such as: - ac_namedtuple: Convert result to nested namedtuple object if True :return: Given `cnf` as it is if validation succeeds else None """ valid = True if schema: (valid, msg) = validate(cnf, schema, format_checker=format_checker, safe=True) if msg: LOGGER.warning(msg) if valid: if options.get("ac_namedtuple", False): return anyconfig.mdicts.convert_to(cnf, ac_namedtuple=True) else: return cnf return None
def query(data, **options): """ Filter data with given JMESPath expression. See also: https://github.com/jmespath/jmespath.py and http://jmespath.org. :parae data: Target object (a dict or a dict-like object) to query :param options: Keyword option may include 'ac_query' which is a string represents JMESPath expression. :return: Maybe queried result data, primitive (int, str, ...) or dict """ expression = options.get("ac_query", None) if expression is None or not expression: return data try: pexp = jmespath.compile(expression) return pexp.search(data) except ValueError as exc: # jmespath.exceptions.*Error inherit from it. LOGGER.warning("Failed to compile or search: exp=%s, exc=%r", expression, exc) except (NameError, AttributeError): LOGGER.warning("Filter module (jmespath) is not available. " "Do nothing.") return data
def find_loader(path_or_stream, parser_or_type=None, is_path_=False): """ Find out config parser object appropriate to load from a file of given path or file/file-like object. :param path_or_stream: Configuration file path or file / file-like object :param parser_or_type: Forced configuration parser type or parser object :param is_path_: True if given `path_or_stream` is a file path :return: Config parser instance or None """ if anyconfig.backends.is_parser(parser_or_type): return parser_or_type (psr, err) = anyconfig.backends.find_parser(path_or_stream, parser_or_type, is_path_=is_path_) if err: LOGGER.error(err) if psr is None: if parser_or_type is None: LOGGER.warning("No parser (type) was given!") else: LOGGER.warning("Parser %s was not found!", str(parser_or_type)) return None LOGGER.debug("Using config parser: %r [%s]", psr, psr.type()) return psr() # TBD: Passing initialization arguments.
def single_load(path_or_stream, ac_parser=None, ac_template=False, ac_context=None, **options): """ Load single config file. :param path_or_stream: Configuration file path or file / file-like object :param ac_parser: Forced parser type or parser object :param ac_template: Assume configuration file may be a template file and try to compile it AAR if True :param ac_context: A dict presents context to instantiate template :param options: Optional keyword arguments such as: - Common options: - ac_namedtuple: Convert result to nested namedtuple object if True - ac_schema: JSON schema file path to validate given config file - Common backend options: - ignore_missing: Ignore and just return empty result if given file (``path_or_stream``) does not exist. - Backend specific options such as {"indent": 2} for JSON backend :return: dict or dict-like object supports merge operations """ is_path_ = is_path(path_or_stream) if is_path_: path_or_stream = anyconfig.utils.ensure_expandusr(path_or_stream) filepath = path_or_stream else: filepath = anyconfig.utils.get_path_from_stream(path_or_stream) psr = find_loader(path_or_stream, ac_parser, is_path_) if psr is None: return None schema = _load_schema(ac_template=ac_template, ac_context=ac_context, **options) options["ac_schema"] = None # It's not needed now. LOGGER.info("Loading: %s", filepath) if ac_template and filepath is not None: try: LOGGER.debug("Compiling: %s", filepath) content = anyconfig.template.render(filepath, ac_context) cnf = psr.loads(content, **options) return _maybe_validated(cnf, schema, **options) except Exception as exc: LOGGER.warning("Failed to compile %s, fallback to no template " "mode, exc=%r", path_or_stream, exc) cnf = psr.load(path_or_stream, **options) return _maybe_validated(cnf, schema, **options)
def loads(content, ac_parser=None, ac_dict=None, ac_template=False, ac_context=None, **options): """ :param content: Configuration file's content (a string) :param ac_parser: Forced parser type or ID or parser object :param ac_dict: callable (function or class) to make mapping object will be returned as a result or None. If not given or ac_dict is None, default mapping object used to store resutls is dict or :class:`collections.OrderedDict` if ac_ordered is True and selected backend can keep the order of items in mapping objects. :param ac_template: Assume configuration file may be a template file and try to compile it AAR if True :param ac_context: Context dict to instantiate template :param options: Optional keyword arguments. See also the description of 'options' in :func:`single_load` function. :return: Mapping object or any query result might be primitive objects :raises: ValueError, UnknownProcessorTypeError """ if ac_parser is None: LOGGER.warning("ac_parser was not given but it's must to find correct " "parser to load configurations from string.") return None psr = find(None, forced_type=ac_parser) schema = None ac_schema = options.get("ac_schema", None) if ac_schema is not None: options["ac_schema"] = None schema = loads(ac_schema, ac_parser=psr, ac_dict=ac_dict, ac_template=ac_template, ac_context=ac_context, **options) if ac_template: compiled = anyconfig.template.try_render(content=content, ctx=ac_context, **options) if compiled is not None: content = compiled cnf = psr.loads(content, ac_dict=ac_dict, **options) cnf = _try_validate(cnf, schema, **options) return _try_query(cnf, options.get("ac_query", False), **options)
def _find_dumper(path_or_stream, ac_parser=None): """ Find configuration parser to dump data. :param path_or_stream: Output file path or file / file-like object :param ac_parser: Forced parser type or parser object :return: Parser-inherited class object """ psr = find_loader(path_or_stream, ac_parser) if psr is None or not getattr(psr, "dump", False): LOGGER.warning("Dump method not implemented. Fallback to json.Parser") psr = anyconfig.backend.json.Parser() return psr
def _try_validate(cnf, schema, **options): """ :param cnf: Mapping object represents configuration data :param schema: JSON schema object :param options: Keyword options passed to :func:`~jsonschema.validate` :return: Given `cnf` as it is if validation succeeds else None """ valid = True if schema: (valid, msg) = validate(cnf, schema, **options) if msg: LOGGER.warning(msg) if valid: return cnf return None
def _try_validate(cnf, schema, **options): """ :param cnf: Mapping object represents configuration data :param schema: JSON schema object :param options: Keyword options passed to :func:`jsonschema.validate` :return: Given 'cnf' as it is if validation succeeds else None """ valid = True if schema: (valid, msg) = validate(cnf, schema, **options) if msg: LOGGER.warning(msg) if valid: return cnf return None
def loads(content, ac_parser=None, ac_dict=None, ac_template=False, ac_context=None, **options): """ :param content: Configuration file's content (a string) :param ac_parser: Forced parser type or ID or parser object :param ac_dict: callable (function or class) to make mapping object will be returned as a result or None. If not given or ac_dict is None, default mapping object used to store resutls is dict or :class:`collections.OrderedDict` if ac_ordered is True and selected backend can keep the order of items in mapping objects. :param ac_template: Assume configuration file may be a template file and try to compile it AAR if True :param ac_context: Context dict to instantiate template :param options: Optional keyword arguments. See also the description of 'options' in :func:`single_load` function. :return: Mapping object or any query result might be primitive objects :raises: ValueError, UnknownProcessorTypeError """ if ac_parser is None: LOGGER.warning("ac_parser was not given but it's must to find correct " "parser to load configurations from string.") return None psr = find(None, forced_type=ac_parser) schema = None ac_schema = options.get("ac_schema", None) if ac_schema is not None: options["ac_schema"] = None schema = loads(ac_schema, ac_parser=psr, ac_dict=ac_dict, ac_template=ac_template, ac_context=ac_context, **options) if ac_template: compiled = anyconfig.template.try_render(content=content, ctx=ac_context, **options) if compiled is not None: content = compiled cnf = psr.loads(content, ac_dict=ac_dict, **options) cnf = _try_validate(cnf, schema, **options) return anyconfig.query.query(cnf, **options)
def loads(content, ac_parser=None, ac_template=False, ac_context=None, **options): """ :param content: Configuration file's content :param ac_parser: Forced parser type or parser object :param ac_template: Assume configuration file may be a template file and try to compile it AAR if True :param ac_context: Context dict to instantiate template :param options: Optional keyword arguments. See also the description of `options` in `single_load` function. :return: dict or dict-like object supports merge operations """ msg = "Try parsing with a built-in parser because %s" if ac_parser is None: LOGGER.warning(msg, "ac_parser was not given.") return None psr = find_loader(None, ac_parser) if psr is None: LOGGER.warning(msg, "parser '%s' was not found" % ac_parser) return None schema = None ac_schema = options.get("ac_schema", None) if ac_schema is not None: options["ac_schema"] = None schema = loads(ac_schema, ac_parser=psr, ac_template=ac_template, ac_context=ac_context, **options) if ac_template: try: LOGGER.debug("Compiling") content = anyconfig.template.render_s(content, ac_context) except Exception as exc: LOGGER.warning( "Failed to compile and fallback to no template " "mode: '%s', exc=%r", content[:50] + '...', exc) cnf = psr.loads(content, **options) return _maybe_validated(cnf, schema, **options)
def loads(content, ac_parser=None, ac_template=False, ac_context=None, **options): """ :param content: Configuration file's content :param ac_parser: Forced parser type or parser object :param ac_template: Assume configuration file may be a template file and try to compile it AAR if True :param ac_context: Context dict to instantiate template :param options: Optional keyword arguments. See also the description of `options` in `single_load` function. :return: dict or dict-like object supports merge operations """ msg = "Try parsing with a built-in parser because %s" if ac_parser is None: LOGGER.warning(msg, "ac_parser was not given.") return None psr = find_loader(None, ac_parser) if psr is None: LOGGER.warning(msg, "parser '%s' was not found" % ac_parser) return None schema = None ac_schema = options.get("ac_schema", None) if ac_schema is not None: options["ac_schema"] = None schema = loads(ac_schema, ac_parser=psr, ac_template=ac_template, ac_context=ac_context, **options) if ac_template: try: LOGGER.debug("Compiling") content = anyconfig.template.render_s(content, ac_context) except Exception as exc: LOGGER.warning("Failed to compile and fallback to no template " "mode: '%s', exc=%r", content[:50] + '...', exc) cnf = psr.loads(content, **options) return _maybe_validated(cnf, schema, **options)