# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['confz', 'confz.loaders']

package_data = \
{'': ['*']}

install_requires = \
['PyYAML>=5.4.1,<7.0.0',
 'pydantic>=1.9.0,<2.0.0',
 'python-dotenv>=0.19.2,<0.21.0']

setup_kwargs = {
    'name': 'confz',
    'version': '1.4.0',
    'description': 'ConfZ is a configuration management library for Python based on pydantic.',
    'long_description': '# ConfZ – Pydantic Config Management\n\n[![test](https://github.com/Zuehlke/ConfZ/actions/workflows/test.yml/badge.svg)](https://github.com/Zuehlke/ConfZ/actions/workflows/test.yml)\n[![documentation](https://readthedocs.org/projects/confz/badge/?version=latest)](https://confz.readthedocs.io/en/latest/)\n[![coverage](https://img.shields.io/badge/coverage-100%25-brightgreen)](https://github.com/Zuehlke/ConfZ/actions/workflows/coverage.yml)  <!-- hard-code because can not merge if below 100 -->\n[![python](https://img.shields.io/pypi/pyversions/confz)](https://pypi.org/project/confz/)\n[![pypi](https://img.shields.io/pypi/v/confz)](https://pypi.org/project/confz/)\n\n`ConfZ` is a configuration management library for Python based on [pydantic](https://pydantic-docs.helpmanual.io/).\nIt easily allows you to\n\n* load your configuration from config files, environment variables, command line arguments and more\n* transform the loaded data into a desired format and validate it\n* access the results as Python dataclass-like objects with full IDE support\n\nIt furthermore supports you in common use cases like:\n\n* Multiple environments\n* Singleton with lazy loading\n* Config changes for unit tests\n* Custom config sources\n\n\n## :package: Installation\n\n`ConfZ` is on [PyPI](https://pypi.org/project/confz/) and can be installed with pip:\n\n```shell\npip install confz\n```\n\n\n## :rocket: Quick Start\n\nThe first step of using `ConfZ` is to declare your config classes and sources, for example in `config.py`:\n\n```python\nfrom pathlib import Path\n\nfrom confz import ConfZ, ConfZFileSource\nfrom pydantic import SecretStr, AnyUrl\n\nclass DBConfig(ConfZ):\n    user: str\n    password: SecretStr\n\nclass APIConfig(ConfZ):\n    host: AnyUrl\n    port: int\n    db: DBConfig\n\n    CONFIG_SOURCES = ConfZFileSource(file=Path(\'/path/to/config.yml\'))\n```\n\nThanks to [pydantic](https://pydantic-docs.helpmanual.io/), you can use a wide variety of\n[field types](https://pydantic-docs.helpmanual.io/usage/types/) and\n[validators](https://pydantic-docs.helpmanual.io/usage/validators/).\n\nFrom now on, in any other file, you can access your config directly:\n\n```python\nfrom config import APIConfig\n\nprint(f"Serving API at {APIConfig().host}, port {APIConfig().port}.")\n```\n\nAs can be seen, the config does neither have to be loaded explicitly, nor instantiated globally. `ConfZ` automatically\nloads your config as defined in `CONFIG_SOURCES` the first time you access it. Thanks to its singleton mechanism, this\nhappens the first time only, afterwards you get back a cached,\n[immutable](https://pydantic-docs.helpmanual.io/usage/models/#faux-immutability) instance, behaving like any other\n_pydantic_ instance.\n\n```python\nassert APIConfig() is APIConfig()   # true because of singleton mechanism\nAPIConfig().port = 1234             # raises an error because of immutability\nAPIConfig().json()                  # call pydantic\'s method to get a json representation\n```\n\n**Note:** While the implicit and hidden loading of your config might be surprising and feel a bit like Python magic at\nfirst, it allows you to reduce a lot of boilerplate. Instead of having to load your config explicitly and then passing\nit down to all code layers that need it, you can directly access it from anywhere by just importing your config class\nand accessing for example `APIConfig().db.user` directly.\n\n### More Config Sources\n\n`ConfZ` is highly flexible in defining the source of your config. Do you have multiple environments? No Problem:\n\n```python\nfrom pathlib import Path\n\nfrom confz import ConfZ, ConfZFileSource\n\nclass MyConfig(ConfZ):\n    ...\n    CONFIG_SOURCES = ConfZFileSource(\n        folder=Path(\'/path/to/config/folder\'),\n        file_from_env=\'ENVIRONMENT\'\n    )\n```\n\nYour config file can now be defined in the environment variable `ENVIRONMENT` and is relative to `folder`.\n\nYou can also provide a list as config source and read for example from environment variables including a .env file and\nfrom command line arguments:\n\n```python\nfrom pathlib import Path\nfrom confz import ConfZ, ConfZEnvSource, ConfZCLArgSource\n\nclass MyConfig(ConfZ):\n    ...\n    CONFIG_SOURCES = [\n        ConfZEnvSource(allow_all=True, file=Path(".env.local")),\n        ConfZCLArgSource(prefix=\'conf_\')\n    ]\n```\n\n`ConfZ` now tries to populate your config either from environment variables having the same name as your attributes or\nby reading command line arguments that start with `conf_`. Recursive models are supported too, for example if you want\nto control the user-name in the API above, you can either set the environment variable `DB.USER` or pass the command\nline argument `--conf_db.user`.\n\n### Explicit Loading\n\nIn some scenarios, the config should not be a global singleton, but loaded explicitly and passed around locally.\nInstead of defining `CONFIG_SOURCES` as class variable, the sources can also be defined in the constructor directly:\n\n```python\nfrom pathlib import Path\n\nfrom confz import ConfZ, ConfZFileSource, ConfZEnvSource\n\nclass MyConfig(ConfZ):\n    number: int\n    text: str\n\nconfig1 = MyConfig(config_sources=ConfZFileSource(file=Path(\'/path/to/config.yml\')))\nconfig2 = MyConfig(config_sources=ConfZEnvSource(prefix=\'CONF_\', allow=[\'text\']), number=1)\nconfig3 = MyConfig(number=1, text=\'hello world\')\n```\n\nAs can be seen, additional keyword-arguments can be provided as well.\n\n**Note:** If neither class variable `CONFIG_SOURCES` nor constructor argument `config_sources` is provided, `ConfZ`\nbehaves like a regular _pydantic_ class.\n\n### Change Config Values\n\nIn some scenarios, you might want to change your config values, for example within a unit test. However, if you set the\n`CONFIG_SOURCES` class variable, this is not directly possible. To overcome this, every config class provides a context\nmanager to temporarily change your config:\n\n```python\nfrom pathlib import Path\n\nfrom confz import ConfZ, ConfZFileSource, ConfZDataSource\n\nclass MyConfig(ConfZ):\n    number: int\n    CONFIG_SOURCES = ConfZFileSource(file=Path(\'/path/to/config.yml\'))\n\nprint(MyConfig().number)                            # will print the value from the config-file\n\nnew_source = ConfZDataSource(data={\'number\': 42})\nwith MyConfig.change_config_sources(new_source):\n    print(MyConfig().number)                        # will print \'42\'\n\nprint(MyConfig().number)                            # will print the value from the config-file again\n```\n\n### Early Validation\n\nBy default, your config gets loaded the first time you instantiate the class, e.g. with `MyConfig().attribute`. This\nprevents side effects like loading a file while you import your config classes. If the config class cannot populate all\nmandatory fields in the correct format, _pydantic_ will raise an error at this point. To make sure this does not happen\nin an inconvenient moment, you can also instruct `ConfZ` to load all configs beforehand:\n\n```python\nfrom confz import validate_all_configs\n\nif __name__ == \'__main__\':\n    validate_all_configs()\n    # your application code\n```\n\nThe function `validate_all_configs` will instantiate all config classes defined in your code at any (reachable)\nlocation that have `CONFIG_SOURCES` set.\n\n\n## :book: Documentation\n\nNow you\'ve seen the two ways how `ConfZ` can be used: With class variable config sources, unlocking a singleton with\nlazy loading, or with keyword argument config sources, allowing to directly load your config values. In both cases,\ndefining your config sources from files, command line arguments and environment variables is highly flexible\n(and also extendable, by the way), while _pydantic_ still makes sure that everything matches your expectations in the\nend. You\'ve also seen how to temporarily change your config for example in unit tests and how to validate\nyour singleton config classes early in the code already.\n\nThe full documentation of `ConfZ`\'s features can be found at [readthedocs](https://confz.readthedocs.io/).\n\n\n## :information_source: About\n\n`ConfZ` was programmed and will be maintained by [Zühlke](https://www.zuehlke.com).\nThe first version was realized by [Silvan](https://github.com/silvanmelchior).\nSpecial thanks to Iwan with his [ConfMe](https://github.com/iwanbolzern/ConfMe), which inspired this project.\n\nWant to contribute to `ConfZ`? Check out the contribution [instruction & guidelines](CONTRIBUTING.md).\n',
    'author': 'Zühlke',
    'author_email': None,
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/Zuehlke/ConfZ',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'python_requires': '>=3.7,<4.0',
}


setup(**setup_kwargs)
