Metadata-Version: 2.1
Name: loguru-config
Version: 0.1.0
Summary: Loguru configuration from configuration files.
Home-page: https://github.com/erezinman/loguru-config
Author: Erez Zinman
License: MIT
Download-URL: https://github.com/erezinman/loguru-config/archive/refs/tags/0.1.0.tar.gz
Description: # Loguru-config
        
        Loguru-config is a simple configurator for the [Loguru](https://github.com/Delgan/loguru) logging library. It extends
        the functionality of Loguru by allowing the user to configure the logger from a configuration file. This package
        provides a much-needed feature to Loguru, which is the ability to configure the logger from a configuration file (for
        example, using loguru alone, one can't automatically configure the logger to write to `sys.stdout` or `sys.stderr`
        from within a configuration file).
        
        The configuration can have syntax similar to the one used by the native `logging` library in Python (i.e. support
        `cfg://`, `ext://`, etc.), but extends it to support even more features. It can also be easily extended to support even
        more features quite easily (see [Extending the configurator](#extending-the-configurator) for more details).
        
        The configurator supports parsing of JSON, JSON5, YAML, and TOML files (out of the box) and can be extended to support
        other formats (again, see [Extending the configurator](#extending-the-configurator) below).
        
        ## Installation
        
        ```bash
        pip install loguru-config
        ```
        
        ## Features
        
        - Supports parsing of JSON, YAML, and TOML files (out of the box) with a simple `Configurator.load` call.
        - Supports loading a member of a module from a string (e.g. `ext://sys.stdout`).
        - Support referencing another member of the configuration file (e.g. `cfg://loggers.default.handlers.0`).
        - Support calling a user-defined function from within the configuration file (e.g. `{ '()': 'datetime.datetime.now' }`).
        - Support referencing an environment variable (e.g. `env://HOME`).
        - Support referencing (and parsing) referencing another file (e.g. `file://./path/to/file.json`).
        - Support parsing literal python (strings, integers, lists, etc.) from within string values in a configuration (e.g.
          `literal://[1, 2, 3]`).
        - Support string formatting (e.g. `fmt://{cfg://loggers.default.handlers.0} - {{ESCAPED}}`).
        - Also, almost all of these parsings are recursively parsed (except user-defined functions).
        - Both the special-case parsing and configuration loading can be easily extended to support more features (see
          [Extending the configurator](#extending-the-configurator) below).
        
        ## Examples
        
        The following YAML configuration file
        
        ```yaml
        handlers:
          - sink: ext://sys.stderr
            format: '[{time}] {message}'
          - sink: file.log
            enqueue: true
            serialize: true
        levels:
          - name: NEW
            'no': 13
            icon: Â¤
            color: ""
        extra:
          common_to_all: default
        activation:
          - [ "my_module.secret", false ]
          - [ "another_library.module", true ]
        ```
        
        will be parsed to
        
        ```python
        from loguru import logger
        import sys
        
        logger.configure(
            handlers=[
                dict(sink=sys.stderr, format="[{time}] {message}"),
                dict(sink="file.log", enqueue=True, serialize=True),
            ],
            levels=[dict(name="NEW", no=13, icon="Â¤", color="")],
            extra={"common_to_all": "default"},
            activation=[("my_module.secret", False), ("another_library.module", True)],
        )
        ```
        
        ## Special-case parsing
        
        There are multiple special cases that are applicable. Some are recursive (i.e. after parsing, their contents will be
        reparsed), and some aren't. The recursive cases will be marked as such in their header.
        
        ### String fields
        
        1. `ext://` (recursive) - Load the member according to the reference after the prefix. This can be used, for example to
           refer to the application's out-streams (`ext://sys.stdout` or `ext://sys.stderr`), or to any loaded/loadable member
           in your own code (e.g. set the level of verbosity according to a predefined
           member `ext://my_package.utils.log_level`).
        2. `cfg://` (recursive) - load a member from elsewhere in the configuration. This is similar to `ext://`, only within
           the configuration. For example, `cfg://handlers.0.level` will refer to the log-level in the first handler. The
           referencing supports both item-getting in dictionaries (`dict.key`), tuples and lists (`list.index`), and
           attribute-getting in other classes (uses the class' `__dict__` attribute).
        3. `env://` (recursive) - load the field's value from an environmental variable. Since environmental-variables are only
           strings, `env://` fields can be combined (i.e. contain) `literal://` tags (more on these below). As an example use
           case, one can set an `extra` field to be the Windows username:
           ```yaml
           ...
           extra: 
              username: env://USERNAME
           ...
           ```
        4. `literal://` - python-evaluate the contents of string following the prefix as literal python. For security reasons,
           the evaluation supports only simple built-in types (i.e. `int`, `float`, `str`, `bool`, `None` and lists,
           dictionaries, sets and tuples of these) without conditionals, assignments, lambda-expressions, etc. These are
           especially useful from loading string-only configurations (like `.ini` files), or mixed with `env://` for loading
           non-string values. Example: `"literal://True"` will evaluate to `True`
        5. `fmt://` (recursive) - formats a string in an f-string-like way. This is useful to chain multiple variables. For
           example: `fmt://{env://APPDATA/}/{cfg://extra.name}/logs` evaluates to sub-folder of the application-data directory
           with the name given as the key `name` in the `extra` part.
           Some notes on this tag:
            - To escape curly braces, use double-curly braces (`{{` evaluates to `"{"`).
            - For now, specifying the individual formats of the formatted placeholders is not supported (e.g. one can not
              specify `"{number:.3f}")` because `:` is used in the tag prefixes. This might be resolved in the future.
        6. `file://` (recursive) - for cases when you wish parts of the configuration to be shared among different
           configurations, one can do it using this tag. This tag loads the contents of the file (the same way the original file
           is loaded), and parses them to be inplace of the given tag. As an example, consider the case where multiple
           configurations have different `extra` section but similar handlers, the configuration might look like:
           ```yaml
           handlers: 'file://handlers.yaml'
           extra:
              ...
           ...
           ```
        
        ### Dictionary fields
        
        7. The user-defined field, or `()`  (NON-RECURSIVE) - when declaring a user-defined field, one should have the contents
           of the field parse as a dictionary with the following keys:
            - `()`: parses as an `ext://` field above, but must refer to a callable.
            - `*` _(optional)_: this key's value, if such key is given, must be a list/tuple of positional arguments to pass to
              the function.
            - `<key>` _(optional)_: keyword-arguments to give the function.
        
           For example, one might wish to configure a `logging` handler as a sink:
           ```yaml
           handlers:
              - sink: 
                  "()": logging.handlers.DatagramHandler
                  "host": '127.0.0.1'
                  "port": 3871
                level: ...
                ...
           ...
           ```
        
        ## Extending the configurator
        
        Aside from inheriting the `LoguruConfig` class, there are two ways to extend the existing configurator class:
        
        1. **Add a custom loading function** - by modifying the `LoguruConfig.supported_loaders` field. This field contains a
           list
           of callables that take a string (file name) as an argument and return a parsable object (i.e. a dictionary that can
           be passed as keyword-arguments to the `LoguruConfig` class' constructor). Note that the order of this list matters
           because the loaders will be attempted according to their order in the list.
        2. **Add a custom string-field parser** - similarly, one can extend the class' parsing capabilities by
           extending `LoguruConfig.supported_protocol_parsers`. This field contains a list of tuples, where each tuple contains
           two elements:
            - Condition: can be either a callable taking a string and returning a boolean, or a regular expression. If the
              latter is given, then if the expression contains any groups, the first group will be passed to the parsing
              function.
            - Parsing function: a function that takes a string field and parses it.
        
        As an example to the latter, consider the case where a special `eval` field can be given. In this case, one should
        extend the configurator as follows:
        
        ```python
        import re
        from loguru_config import LoguruConfig
        
        eval_protocol = re.compile(r'^eval://(.+)$')
        LoguruConfig.supported_protocol_parsers = list(LoguruConfig.supported_protocol_parsers) + [
            (eval_protocol, eval)
        ]
        
        LoguruConfig.load(...)
        ```
        
        In contrast, one might not want affect all configurators - just one of them. In this case:
        
        ```python
        import re
        from loguru_config import LoguruConfig
        
        eval_protocol = re.compile(r'^eval://(.+)$')
        config = LoguruConfig.load(..., configure=False)
        
        config.supported_protocol_parsers = list(LoguruConfig.supported_protocol_parsers) + [
            (eval_protocol, eval)
        ]
        
        config.parse().configure()
        ```
        
Keywords: loguru,configuration,config,logging,log
Platform: UNKNOWN
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Operating System :: OS Independent
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Logging
Requires-Python: >=3.7
Description-Content-Type: text/markdown
