# 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()
```
Raw data
{
"_id": null,
"home_page": "https://github.com/erezinman/loguru-config",
"name": "loguru-config",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.7",
"maintainer_email": "",
"keywords": "loguru,configuration,config,logging,log",
"author": "Erez Zinman",
"author_email": "",
"download_url": "https://files.pythonhosted.org/packages/2e/c7/e9528e9439214e885ce04fb68725ee1d10d64f333a9d68c9ea1545089cab/loguru-config-0.1.0.tar.gz",
"platform": null,
"description": "# Loguru-config\n\nLoguru-config is a simple configurator for the [Loguru](https://github.com/Delgan/loguru) logging library. It extends\nthe functionality of Loguru by allowing the user to configure the logger from a configuration file. This package\nprovides a much-needed feature to Loguru, which is the ability to configure the logger from a configuration file (for\nexample, using loguru alone, one can't automatically configure the logger to write to `sys.stdout` or `sys.stderr`\nfrom within a configuration file).\n\nThe configuration can have syntax similar to the one used by the native `logging` library in Python (i.e. support\n`cfg://`, `ext://`, etc.), but extends it to support even more features. It can also be easily extended to support even\nmore features quite easily (see [Extending the configurator](#extending-the-configurator) for more details).\n\nThe configurator supports parsing of JSON, JSON5, YAML, and TOML files (out of the box) and can be extended to support\nother formats (again, see [Extending the configurator](#extending-the-configurator) below).\n\n## Installation\n\n```bash\npip install loguru-config\n```\n\n## Features\n\n- Supports parsing of JSON, YAML, and TOML files (out of the box) with a simple `Configurator.load` call.\n- Supports loading a member of a module from a string (e.g. `ext://sys.stdout`).\n- Support referencing another member of the configuration file (e.g. `cfg://loggers.default.handlers.0`).\n- Support calling a user-defined function from within the configuration file (e.g. `{ '()': 'datetime.datetime.now' }`).\n- Support referencing an environment variable (e.g. `env://HOME`).\n- Support referencing (and parsing) referencing another file (e.g. `file://./path/to/file.json`).\n- Support parsing literal python (strings, integers, lists, etc.) from within string values in a configuration (e.g.\n `literal://[1, 2, 3]`).\n- Support string formatting (e.g. `fmt://{cfg://loggers.default.handlers.0} - {{ESCAPED}}`).\n- Also, almost all of these parsings are recursively parsed (except user-defined functions).\n- Both the special-case parsing and configuration loading can be easily extended to support more features (see\n [Extending the configurator](#extending-the-configurator) below).\n\n## Examples\n\nThe following YAML configuration file\n\n```yaml\nhandlers:\n - sink: ext://sys.stderr\n format: '[{time}] {message}'\n - sink: file.log\n enqueue: true\n serialize: true\nlevels:\n - name: NEW\n 'no': 13\n icon: \u00c2\u00a4\n color: \"\"\nextra:\n common_to_all: default\nactivation:\n - [ \"my_module.secret\", false ]\n - [ \"another_library.module\", true ]\n```\n\nwill be parsed to\n\n```python\nfrom loguru import logger\nimport sys\n\nlogger.configure(\n handlers=[\n dict(sink=sys.stderr, format=\"[{time}] {message}\"),\n dict(sink=\"file.log\", enqueue=True, serialize=True),\n ],\n levels=[dict(name=\"NEW\", no=13, icon=\"\u00c2\u00a4\", color=\"\")],\n extra={\"common_to_all\": \"default\"},\n activation=[(\"my_module.secret\", False), (\"another_library.module\", True)],\n)\n```\n\n## Special-case parsing\n\nThere are multiple special cases that are applicable. Some are recursive (i.e. after parsing, their contents will be\nreparsed), and some aren't. The recursive cases will be marked as such in their header.\n\n### String fields\n\n1. `ext://` (recursive) - Load the member according to the reference after the prefix. This can be used, for example to\n refer to the application's out-streams (`ext://sys.stdout` or `ext://sys.stderr`), or to any loaded/loadable member\n in your own code (e.g. set the level of verbosity according to a predefined\n member `ext://my_package.utils.log_level`).\n2. `cfg://` (recursive) - load a member from elsewhere in the configuration. This is similar to `ext://`, only within\n the configuration. For example, `cfg://handlers.0.level` will refer to the log-level in the first handler. The\n referencing supports both item-getting in dictionaries (`dict.key`), tuples and lists (`list.index`), and\n attribute-getting in other classes (uses the class' `__dict__` attribute).\n3. `env://` (recursive) - load the field's value from an environmental variable. Since environmental-variables are only\n strings, `env://` fields can be combined (i.e. contain) `literal://` tags (more on these below). As an example use\n case, one can set an `extra` field to be the Windows username:\n ```yaml\n ...\n extra: \n username: env://USERNAME\n ...\n ```\n4. `literal://` - python-evaluate the contents of string following the prefix as literal python. For security reasons,\n the evaluation supports only simple built-in types (i.e. `int`, `float`, `str`, `bool`, `None` and lists,\n dictionaries, sets and tuples of these) without conditionals, assignments, lambda-expressions, etc. These are\n especially useful from loading string-only configurations (like `.ini` files), or mixed with `env://` for loading\n non-string values. Example: `\"literal://True\"` will evaluate to `True`\n5. `fmt://` (recursive) - formats a string in an f-string-like way. This is useful to chain multiple variables. For\n example: `fmt://{env://APPDATA/}/{cfg://extra.name}/logs` evaluates to sub-folder of the application-data directory\n with the name given as the key `name` in the `extra` part.\n Some notes on this tag:\n - To escape curly braces, use double-curly braces (`{{` evaluates to `\"{\"`).\n - For now, specifying the individual formats of the formatted placeholders is not supported (e.g. one can not\n specify `\"{number:.3f}\")` because `:` is used in the tag prefixes. This might be resolved in the future.\n6. `file://` (recursive) - for cases when you wish parts of the configuration to be shared among different\n configurations, one can do it using this tag. This tag loads the contents of the file (the same way the original file\n is loaded), and parses them to be inplace of the given tag. As an example, consider the case where multiple\n configurations have different `extra` section but similar handlers, the configuration might look like:\n ```yaml\n handlers: 'file://handlers.yaml'\n extra:\n ...\n ...\n ```\n\n### Dictionary fields\n\n7. The user-defined field, or `()` (NON-RECURSIVE) - when declaring a user-defined field, one should have the contents\n of the field parse as a dictionary with the following keys:\n - `()`: parses as an `ext://` field above, but must refer to a callable.\n - `*` _(optional)_: this key's value, if such key is given, must be a list/tuple of positional arguments to pass to\n the function.\n - `<key>` _(optional)_: keyword-arguments to give the function.\n\n For example, one might wish to configure a `logging` handler as a sink:\n ```yaml\n handlers:\n - sink: \n \"()\": logging.handlers.DatagramHandler\n \"host\": '127.0.0.1'\n \"port\": 3871\n level: ...\n ...\n ...\n ```\n\n## Extending the configurator\n\nAside from inheriting the `LoguruConfig` class, there are two ways to extend the existing configurator class:\n\n1. **Add a custom loading function** - by modifying the `LoguruConfig.supported_loaders` field. This field contains a\n list\n of callables that take a string (file name) as an argument and return a parsable object (i.e. a dictionary that can\n be passed as keyword-arguments to the `LoguruConfig` class' constructor). Note that the order of this list matters\n because the loaders will be attempted according to their order in the list.\n2. **Add a custom string-field parser** - similarly, one can extend the class' parsing capabilities by\n extending `LoguruConfig.supported_protocol_parsers`. This field contains a list of tuples, where each tuple contains\n two elements:\n - Condition: can be either a callable taking a string and returning a boolean, or a regular expression. If the\n latter is given, then if the expression contains any groups, the first group will be passed to the parsing\n function.\n - Parsing function: a function that takes a string field and parses it.\n\nAs an example to the latter, consider the case where a special `eval` field can be given. In this case, one should\nextend the configurator as follows:\n\n```python\nimport re\nfrom loguru_config import LoguruConfig\n\neval_protocol = re.compile(r'^eval://(.+)$')\nLoguruConfig.supported_protocol_parsers = list(LoguruConfig.supported_protocol_parsers) + [\n (eval_protocol, eval)\n]\n\nLoguruConfig.load(...)\n```\n\nIn contrast, one might not want affect all configurators - just one of them. In this case:\n\n```python\nimport re\nfrom loguru_config import LoguruConfig\n\neval_protocol = re.compile(r'^eval://(.+)$')\nconfig = LoguruConfig.load(..., configure=False)\n\nconfig.supported_protocol_parsers = list(LoguruConfig.supported_protocol_parsers) + [\n (eval_protocol, eval)\n]\n\nconfig.parse().configure()\n```",
"bugtrack_url": null,
"license": "MIT",
"summary": "Loguru configuration from configuration files.",
"version": "0.1.0",
"project_urls": {
"Download": "https://github.com/erezinman/loguru-config/archive/refs/tags/0.1.0.tar.gz",
"Homepage": "https://github.com/erezinman/loguru-config"
},
"split_keywords": [
"loguru",
"configuration",
"config",
"logging",
"log"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "2ec7e9528e9439214e885ce04fb68725ee1d10d64f333a9d68c9ea1545089cab",
"md5": "fffe46d0bbab49ec29b9d2bbfd9fdeb7",
"sha256": "432fbc134b118cdfbe0de0226eaab3f5247fc142cd005bf1ebe471f7c572aa34"
},
"downloads": -1,
"filename": "loguru-config-0.1.0.tar.gz",
"has_sig": false,
"md5_digest": "fffe46d0bbab49ec29b9d2bbfd9fdeb7",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.7",
"size": 15129,
"upload_time": "2023-07-15T11:55:48",
"upload_time_iso_8601": "2023-07-15T11:55:48.381794Z",
"url": "https://files.pythonhosted.org/packages/2e/c7/e9528e9439214e885ce04fb68725ee1d10d64f333a9d68c9ea1545089cab/loguru-config-0.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-07-15 11:55:48",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "erezinman",
"github_project": "loguru-config",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"tox": true,
"lcname": "loguru-config"
}