# Path: django_docker_helpers/config/backends/base.py
# class BaseParser:
#     """
#     Base class to inherit from in custom parsers.
#     """
#     def __init__(self,
#                  scope: t.Optional[str] = None,
#                  config: t.Optional[str] = None,
#                  nested_delimiter: str = '__',
#                  path_separator: str = '.',
#                  env: t.Optional[t.Dict[str, str]] = None):
#         """
#         All ``__init__`` arguments **MUST** be optional if you need
#         :meth:`~django_docker_helpers.config.ConfigLoader.from_env`
#         automatic parser initializer (it initializes parsers like ``parser_class(**parser_options)``).
# 
#         Since :class:`~django_docker_helpers.config.ConfigLoader` can initialize parsers from environment variables
#         it's **recommended** to annotate argument types to provide a correct auto typecast.
# 
#         ``BaseParser`` creates a ``logger`` with name ``__class__.__name__``.
# 
#         ``BaseParser`` implements generic copying of following arguments without any backend-specific logic inside.
# 
#         :param scope: a global prefix to all underlying values
#         :param config: optional config
#         :param nested_delimiter: optional delimiter for environment backend
#         :param path_separator: specifies which character separates nested variables, default is ``'.'``
#         :param env: a dict with environment variables, default is ``os.environ``
#         """
# 
#         self.config = config
#         self.scope = scope
#         self.nested_delimiter = nested_delimiter
#         self.path_separator = path_separator
#         self.env = env or os.environ
#         self.sentinel = object()
#         self._client = None
# 
#         self.logger = logging.getLogger(self.__class__.__name__)
# 
#     def get(self,
#             variable_path: str,
#             default: t.Optional[t.Any] = None,
#             coerce_type: t.Optional[t.Type] = None,
#             coercer: t.Optional[t.Callable] = None,
#             **kwargs):
#         """
#         Inherited method should take all specified arguments.
# 
#         :param variable_path: a delimiter-separated path to a nested value
#         :param default: default value if there's no object by specified path
#         :param coerce_type: cast a type of a value to a specified one
#         :param coercer: perform a type casting with specified callback
#         :param kwargs: additional arguments inherited parser may need
#         :return: value or default
#         """
#         raise NotImplementedError
# 
#     @staticmethod
#     def coerce(val: t.Any,
#                coerce_type: t.Optional[t.Type] = None,
#                coercer: t.Optional[t.Callable] = None) -> t.Any:
#         """
#         Casts a type of ``val`` to ``coerce_type`` with ``coercer``.
# 
#         If ``coerce_type`` is bool and no ``coercer`` specified it uses
#         :func:`~django_docker_helpers.utils.coerce_str_to_bool` by default.
# 
#         :param val: a value of any type
#         :param coerce_type: any type
#         :param coercer: provide a callback that takes ``val`` and returns a value with desired type
#         :return: type casted value
#         """
#         if not coerce_type and not coercer:
#             return val
# 
#         if coerce_type and type(val) is coerce_type:
#             return val
# 
#         if coerce_type and coerce_type is bool and not coercer:
#             coercer = coerce_str_to_bool
# 
#         if coercer is None:
#             coercer = coerce_type
# 
#         return coercer(val)
# 
#     @property
#     def client(self):
#         """
#         Helper property to lazy initialize and cache client. Runs
#         :meth:`~django_docker_helpers.config.backends.base.BaseParser.get_client`.
# 
#         :return: an instance of backend-specific client
#         """
#         if self._client is not None:
#             return self._client
# 
#         self._client = self.get_client()
#         return self._client
# 
#     def get_client(self):
#         """
#         If your backend needs a client, inherit this method and use
#         :meth:`~django_docker_helpers.config.backends.base.BaseParser.client` shortcut.
# 
#         :return: an instance of backend-specific client
#         """
#         raise NotImplementedError
# 
# Path: django_docker_helpers/utils.py
# def default_yaml_object_deserialize(stream, Loader=SafeLoader):
#     return yaml_load(stream, Loader=Loader)


import typing as t
        import redis
from django_docker_helpers.config.backends.base import BaseParser
from django_docker_helpers.utils import default_yaml_object_deserialize



class MPTRedisParser(BaseParser):
    """
    Materialized Path Tree Redis Parser.

    Compared to, e.g. :class:`~django_docker_helpers.config.backends.redis_parser.RedisParser`
    it does not load a whole config file from a single key, but reads every config option
    from a corresponding variable path.

    Example:
    ::

        parser = MPTRedisParser(host=REDIS_HOST, port=REDIS_PORT)
        parser.get('nested.a.b')
        parser.get('debug')

    If you want to store your config with separated key paths take
    :func:`~django_docker_helpers.utils.mp_serialize_dict` helper to materialize your dict.
    """

    def __init__(self,
                 scope: t.Optional[str] = None,
                 host: str = '127.0.0.1',
                 port: int = 6379,
                 db: int = 0,
                 path_separator: str = '.',
                 key_prefix: str = '',
                 object_deserialize_prefix: str = '::YAML::\n',

# Path: django_docker_helpers/config/backends/base.py
# class BaseParser:
#     """
#     Base class to inherit from in custom parsers.
#     """
#     def __init__(self,
#                  scope: t.Optional[str] = None,
#                  config: t.Optional[str] = None,
#                  nested_delimiter: str = '__',
#                  path_separator: str = '.',
#                  env: t.Optional[t.Dict[str, str]] = None):
#         """
#         All ``__init__`` arguments **MUST** be optional if you need
#         :meth:`~django_docker_helpers.config.ConfigLoader.from_env`
#         automatic parser initializer (it initializes parsers like ``parser_class(**parser_options)``).
# 
#         Since :class:`~django_docker_helpers.config.ConfigLoader` can initialize parsers from environment variables
#         it's **recommended** to annotate argument types to provide a correct auto typecast.
# 
#         ``BaseParser`` creates a ``logger`` with name ``__class__.__name__``.
# 
#         ``BaseParser`` implements generic copying of following arguments without any backend-specific logic inside.
# 
#         :param scope: a global prefix to all underlying values
#         :param config: optional config
#         :param nested_delimiter: optional delimiter for environment backend
#         :param path_separator: specifies which character separates nested variables, default is ``'.'``
#         :param env: a dict with environment variables, default is ``os.environ``
#         """
# 
#         self.config = config
#         self.scope = scope
#         self.nested_delimiter = nested_delimiter
#         self.path_separator = path_separator
#         self.env = env or os.environ
#         self.sentinel = object()
#         self._client = None
# 
#         self.logger = logging.getLogger(self.__class__.__name__)
# 
#     def get(self,
#             variable_path: str,
#             default: t.Optional[t.Any] = None,
#             coerce_type: t.Optional[t.Type] = None,
#             coercer: t.Optional[t.Callable] = None,
#             **kwargs):
#         """
#         Inherited method should take all specified arguments.
# 
#         :param variable_path: a delimiter-separated path to a nested value
#         :param default: default value if there's no object by specified path
#         :param coerce_type: cast a type of a value to a specified one
#         :param coercer: perform a type casting with specified callback
#         :param kwargs: additional arguments inherited parser may need
#         :return: value or default
#         """
#         raise NotImplementedError
# 
#     @staticmethod
#     def coerce(val: t.Any,
#                coerce_type: t.Optional[t.Type] = None,
#                coercer: t.Optional[t.Callable] = None) -> t.Any:
#         """
#         Casts a type of ``val`` to ``coerce_type`` with ``coercer``.
# 
#         If ``coerce_type`` is bool and no ``coercer`` specified it uses
#         :func:`~django_docker_helpers.utils.coerce_str_to_bool` by default.
# 
#         :param val: a value of any type
#         :param coerce_type: any type
#         :param coercer: provide a callback that takes ``val`` and returns a value with desired type
#         :return: type casted value
#         """
#         if not coerce_type and not coercer:
#             return val
# 
#         if coerce_type and type(val) is coerce_type:
#             return val
# 
#         if coerce_type and coerce_type is bool and not coercer:
#             coercer = coerce_str_to_bool
# 
#         if coercer is None:
#             coercer = coerce_type
# 
#         return coercer(val)
# 
#     @property
#     def client(self):
#         """
#         Helper property to lazy initialize and cache client. Runs
#         :meth:`~django_docker_helpers.config.backends.base.BaseParser.get_client`.
# 
#         :return: an instance of backend-specific client
#         """
#         if self._client is not None:
#             return self._client
# 
#         self._client = self.get_client()
#         return self._client
# 
#     def get_client(self):
#         """
#         If your backend needs a client, inherit this method and use
#         :meth:`~django_docker_helpers.config.backends.base.BaseParser.client` shortcut.
# 
#         :return: an instance of backend-specific client
#         """
#         raise NotImplementedError
# 
# Path: django_docker_helpers/utils.py
# def default_yaml_object_deserialize(stream, Loader=SafeLoader):
#     return yaml_load(stream, Loader=Loader)


# Path: django_docker_helpers/config/backends/mpt_redis_parser.py
import typing as t
        import redis
from django_docker_helpers.config.backends.base import BaseParser
from django_docker_helpers.utils import default_yaml_object_deserialize




class MPTRedisParser(BaseParser):
    """
    Materialized Path Tree Redis Parser.

    Compared to, e.g. :class:`~django_docker_helpers.config.backends.redis_parser.RedisParser`
    it does not load a whole config file from a single key, but reads every config option
    from a corresponding variable path.

    Example:
    ::

        parser = MPTRedisParser(host=REDIS_HOST, port=REDIS_PORT)
        parser.get('nested.a.b')
        parser.get('debug')

    If you want to store your config with separated key paths take
    :func:`~django_docker_helpers.utils.mp_serialize_dict` helper to materialize your dict.
    """

    def __init__(self,
                 scope: t.Optional[str] = None,
                 host: str = '127.0.0.1',
                 port: int = 6379,
                 db: int = 0,
                 path_separator: str = '.',
                 key_prefix: str = '',
                 object_deserialize_prefix: str = '::YAML::\n',

                 object_deserialize: t.Optional[t.Callable] = default_yaml_object_deserialize,