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

packages = \
['ilexconf', 'ilexconf.tests']

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

extras_require = \
{'console': ['cleo>=0.8.1,<0.9.0'], 'yaml': ['ruamel.yaml>=0.16.12,<0.17.0']}

setup_kwargs = {
    'name': 'ilexconf',
    'version': '0.6.11',
    'description': 'Configuration library for Python 🔧 Load from multiple sources',
    'long_description': '![ilexconf](https://raw.githubusercontent.com/vduseev/ilexconf/master/docs/img/logo.png)\n\n<h2 align="center">Configuration Library 🔧 for Python</h2>\n\n<p align="center">\n<a href="https://travis-ci.org/vduseev/ilexconf"><img alt="Build Status" src="https://travis-ci.org/vduseev/ilexconf.svg?branch=master"></a>\n<a href="https://coveralls.io/github/psf/black?branch=master"><img alt="Coverage Status" src="https://coveralls.io/repos/github/psf/black/badge.svg?branch=master"></a>\n<a href="https://github.com/psf/black/blob/master/LICENSE"><img alt="License: MIT" src="https://black.readthedocs.io/en/stable/_static/license.svg"></a>\n<a href="https://pypi.org/project/ilexconf/"><img alt="PyPI" src="https://img.shields.io/pypi/v/ilexconf"></a>\n<a href="https://github.com/psf/black"><img alt="Code style: black" src="https://img.shields.io/badge/code%20style-black-000000.svg"></a>\n</p>\n\n`ilexconf` is a Python library to load and merge configs from multiple sources, access & change the values, and write them back, if needed. It has no dependencies by default but provides additional functions, relying on popular libraries to parse `yaml`, `toml`, provide `CLI` app, etc.\n\n## Table of contents \n\n* <a href="#quick_start">🚀 Quick Start</a>\n  * <a href="#quick_start_install">Installation</a>\n  * <a href="#quick_start_create">Create `Config` object</a>\n  * <a href="#quick_start_read">Read using `from_` functions</a>\n  * <a href="#quick_start_access">Access values</a>\n  * <a href="#quick_start_change_create">Change & create values</a>\n  * <a href="#quick_start_merge">`merge` another `Mapping` into config</a>\n  * <a href="#quick_start_as_dict">Convert to simple `dict` using `as_dict`</a>\n  * <a href="#quick_start_write">Save to file using `to_` functions</a>\n  * <a href="#quick_start_subclass">Subclass `Config` to customize</a>\n* <a href="#internals">⚙️ Internals – How it Works</a>\n\n<a id="quick_start"></a>\n## Quick Start\n\n<a id="quick_start_install"></a>\n### Install\n\n```shell\n$ pip install ilexconf\n```\n\n<a id="quick_start_create"></a>\n### Populate Config with values\n\nConfig object is initialized using arbitrary number of Mapping objects and keyword arguments. It can even be empty. \n\nWhen we initialize config all the values are merged. Last Mapping is merged on top of the previous mapping values. And keyword arguments override even that.\n\nFor a settings file `settings.json` with the following content ...\n\n```json\n{\n    "database": {\n        "connection": {\n            "host": "localhost",\n            "port": 5432\n        }\n    }\n}\n```\n\nThe code below will produce a merged Config with merged values:\n\n```python\nfrom ilexconf import Config, from_json\n\n# Create configuration using JSON file, dictionary and key-value pair\nconfig = Config(\n    from_json("settings.json"),\n    { "database": { "connection": { "host": "test.local" } } },\n    database__connection__port=4000\n)\n\n# Check it was created and values are merged properly\nassert config.as_dict() == {\n    "database": {\n        "connection": {\n            "host": "test.local",\n            "port": 4000\n        }\n    }\n}\n```\n\n<a id="quick_start_read"></a>\n### Read from files & environment variables\n\nFiles like `.json`, `.yaml`, `.toml`, `.ini`, `.env`, `.py` as well as environment variables can all be read & loaded using a set of `from_` functions.\n\n```python\nfrom ilexconf import (\n    from_json,\n    from_yaml,\n    from_toml,\n    from_ini,\n    from_python,\n    from_dotenv,\n    from_env\n)\n\nconfig = Config(\n    from_json("settings.json"),\n    from_yaml("settings.yaml"),\n    from_toml("settings.toml"),\n    from_ini("settings.ini"),\n    from_python("settings.py"),\n    from_dotenv(".env"),\n    from_env()\n)\n```\n\n<a id="quick_start_access"></a>\n### Access values however you like\n\nYou can access any key in the hierarchical structure using classical Python dict notation, dotted keys, attributes, or any combination of this methods.\n\n```python\n# Classic way\nassert config["database"]["conection"]["host"] == "test.local"\n\n# Dotted key\nassert config["database.connection.host"] == "test.local"\n\n# Attributes\nassert config.database.connection.host == "test.local"\n\n# Any combination of the above\nassert config["database"].connection.host == "test.local"\nassert config.database["connection.host"] == "test.local"\nassert config.database["connection"].host == "test.local"\nassert config.database.connection["host"] == "test.local"\n```\n\n<a id="quick_start_change_create"></a>\n### Change existing values and create new ones\n\nSimilarly, you can set values of any key (_even if it doesn\'t exist in the Config_) using all of the ways above.\n\n**Notice**, _contrary to what you would expect from the Python dictionaries, setting nested keys that do not exist is **allowed**_.\n\n```python\n# Classic way\nconfig["database"]["connection"]["port"] = 8080\nassert config["database"]["connection"]["port"] == 8080\n\n# Dotted key (that does not exist yet)\nconfig["database.connection.user"] = "root"\nassert config["database.connection.user"] == "root"\n\n# Attributes (also does not exist yet)\nconfig.database.connection.password = "secret stuff"\nassert config.database.connection.password == "secret stuff"\n```\n\n<a id="quick_start_merge"></a>\n### Update with another Mapping object\n\nIf you just assign a value to any key, you override any previous value of that key.\n\nIn order to merge assigned value with an existing one, use `merge` method.\n\n```python\nconfig.database.connection.merge({ "password": "different secret" })\nassert config.database.connection.password == "different secret"\n```\n\n<a id="quick_start_as_dict"></a>\n### Represent as dictionary\n\nFor any purposes you might find fit you can convert entire structure of the Config object into dictionary, which will be essentially returned to you as a deep copy of the object.\n\n```python\nassert config.as_dict() == {\n    "database": {\n        "connection": {\n            "host": "test.local",\n            "port": 8080,\n            "user": "root",\n            "password": "different secret"\n        }\n    }\n}\n```\n\n<a id="quick_start_write"></a>\n### Write to file\n\nYou can serialize the file as JSON or other types any time using the `to_` functions.\n\n```python\n# Write updated config back as JSON file\nfrom ilexconf import to_json\n\nto_json(config, "settings.json")\n```\n\n**WARNING**: _This might throw a serialization error if any of the values contained in the Config are custom objects that cannot be converted to `str`. Also, obviously, you might not be able to correctly parse an object back, if it\'s saved to JSON as `MyObject(<function MyObject.__init__.<locals>.<lambda> at 0x108927af0>, {})` or something._\n\n<a id="quick_start_subclass"></a>\n### Subclass\n\nSubclassing `Config` class is very convenient for implementation of your own config classes with custom logic.\n\nConsider this example:\n\n```python\nimport ilexconf\n\nclass Config(ilexconf.Config):\n    """\n    Your custom Configuration class\n    """\n\n    def __init__(do_stuff=False):\n        # Initialize your custom config with JSON by default\n        super().__init__(self, ilexconf.from_json("setting.json"))\n\n        # Add some custom value depending on some logic\n        if do_stuff:\n            self.my.custom.key = "Yes, do stuff"\n\n        self.merge({\n            "Horizon": "Up"\n        })\n\n# Now you can use your custom Configuration everywhere\nconfig = Config(do_stuff=True)\nassert config.my.custom.key == "Yes, do stuff"\nassert config.Horizon == "Up"\n```\n\n<a id="internals"></a>\n## Internals\n\nUnder the hood `ilexconf` is implemented as a `defaultdict` where every key with Mapping value is represented as another `Config` object. This creates a hierarchy of `Config` objects.\n\n`__getitem__`, `__setitem__`, `__getattr__`, and `__setattr__` methods are overloaded with custom logic to support convenient get/set approach presented by the library.\n\n<a id="alternatives"></a>\n## Alternative Libraries\n\nBelow is a primitive analysis of features of alternative libraries doing similar job.\n\n| Library                           | **ilexconf** | dynaconf | python-configuration |\n| --------------------------------- | ----- | -------- | -- |\n| **Read from `.json`**             | x     | x        | x  |\n| **Read from `.toml`**             | x     | x        | x  |\n| **Read from `.ini`**              | x     | x        | x  |\n| **Read from env vars**            | x     | x        | x  |\n| **Read from `.py`**               |       | x        | x  |\n| **Read from `.env`**              |       | x        |    |\n| **Read from dict object**         | x     |          | x  |\n| **Read from Redis**               |       | x        |    |\n| **Read from Hashicorp Vault**     |       | x        |    |\n| **Default values**                | x     | x        |    |    \n| **Multienvironment**              |       | x        |    |\n| **Attribute access**              | x     | x        | x  |\n| **Dotted key access**             | x     | x        | x  |\n| **Merging**                       | x     | x        | x  |\n| **Interpolation**                 |       | x        | x  |\n| **Saving**                        | x     | x        |    |\n| **CLI**                           | x     | x        |    |\n| **Printing**                      | x     | x        |    |\n| **Validators**                    |       | x        |    |\n| **Masking sensitive info**        |       | x        | x  |\n| **Django integration**            |       | x        |    |\n| **Flask integration**             |       | x        |    |\n| **Hot reload**                    |       |          |    |\n| *Python 3.6*                      |       |          | x  |\n| *Python 3.7*                      |       |          | x  |\n| *Python 3.8*                      | x     |          | x  |\n\n## Kudos\n\n`ilexconf` heavily borrows from `python-configuration` library and is inspired by it.\n\n## License\n\nMIT',
    'author': 'vduseev',
    'author_email': 'vagiz@duseev.com',
    'maintainer': 'vduseev',
    'maintainer_email': 'vagiz@duseev.com',
    'url': 'https://github.com/vduseev/ilexconf',
    'packages': packages,
    'package_data': package_data,
    'extras_require': extras_require,
    'python_requires': '>=3.8,<4.0',
}


setup(**setup_kwargs)
