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

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

package_data = \
{'': ['*'], 'ilexconf.tests': ['files/*']}

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.12',
    '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\n```python\nfrom ilexconf import Config, from_json\n\n# All of these are valid methods to initialize a config\nconfig = Config()\nconfig = Config({ "database": { "connection": { "host": "test.local" } } })\nconfig = Config(database__connection__port=4000)\nconfig = from_json("settings.json")\nconfig = from_env()\n\n# Or, you can combine them\nconfig = Config(\n    # Take the basic settings from JSON file\n    from_json("settings.json"),\n    # Merge them with a dictionary\n    { "database": { "connection": { "host": "test.local" } } },\n    # Merge the keyword arguments on top\n    database__connection__port=4000\n)\n```\n\nWhen we initialize config all the values are merged. Every next argument 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 above will produce a merged `config` with merged values:\n\n```json\n{\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,      # from JSON file or string\n    from_yaml,      # from YAML file or string\n    from_toml,      # from TOML file or string\n    from_ini,       # from INI file or string\n    from_python,    # from .py module\n    from_dotenv,    # from .env file\n    from_env        # from environment variables\n)\n\ncfg1 = from_json("settings.json")\n\ncfg2 = Config(\n    from_yaml("settings.yaml"),\n    from_toml("settings.toml")\n)\n\ncfg3 = Config(\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__(self, 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        | 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## Contributing\n\nContributions are welcome!\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)
