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

packages = \
['chonf',
 'chonf.parsers',
 'chonf.parsers.env',
 'chonf.parsers.env.test',
 'chonf.parsers.json',
 'chonf.parsers.json.test',
 'chonf.parsers.toml',
 'chonf.parsers.toml.test',
 'chonf.test']

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

install_requires = \
['toml>=0.10.2,<0.11.0']

setup_kwargs = {
    'name': 'chonf',
    'version': '0.1.1',
    'description': 'User config management made simple and powerful',
    'long_description': '```\n      _                  __ \n  ___| |__   ___  _ __  / _|\n / __| \'_ \\ / _ \\| \'_ \\| |_ \n| (__| | | | (_) | | | |  _|\n \\___|_| |_|\\___/|_| |_|_|\n```\n\nAn early concept for a opinionated configuration framework to give\npower users the freedom to use whatever config file syntax they like.\nIt\'s written in python.\n\n**Chonf** enforces a structure of configuration that can always be\ntranslated into a tree, such as nested dictionaries in python. \nMost of the widely used config file syntaxes work like this: Json, \nToml, Yaml, and so on.\n\n# How To\n\nThe configuration loading in your program should look like this:\n\n```python\nfrom chonf import load, Option\n\n# Create a configuration model as a dictionary\nmymodel = {\n    "ui_theme": Option("dark"), # default value here is "dark"\n    "user_info": {\n        "name": Option(), # empty Option, default here is None\n        "email": Option(),\n    }\n}\n\nmyconfigs = load(\n    mymodel, \n    author="mycompany",\n    name="myprogram",\n)\n\n# myconfigs is now a dict of your configurations\n```\n\nA Toml config file, for example, could look like this:\n```toml\nui_theme = "light"\n\n[user_info]\nname = "Tom Preston-Werner"\nemail = "tomate@hotmail.com"\n```\n\n## Overwriting stuff with environment variables\n\nFor **Chonf**, environment variables have higher *precedence*\nthan config files such as Toml or Json files, so you can quickly\noverwrite a configuration set in a config file with a environment\nvariable such as:\n\n```console\nfoo@bar:~$ export myprogram__ui_theme="adwaita"\n```\n\nThis allows for quick tests in config changes without opening, editing\nand saving config files multiple times.\n\nThe syntax for the env variable names is the following: first the \nspecified `env_prefix` argument on the load function, than the keys\nfrom the root of the dictionary tree down to the name of the option itself,\nseparated by ***double__underscores***.\n\nOn the previous example, this is how you would name the environment\nvariables:\n\n- `myconfigs["ui_theme"]` is named `myprogram__ui_theme`\n- `myconfigs["user_info"]["name"]` is named `myprogram__user_info__name`\n- `myconfigs["user_info"]["email"]` is named `myprogram__user_info__email`\n\nThis unusual double underscore syntax allows usage of single underscores\nas word separators on the option names without creating ambiguity. \n\nNote that the default for environment variables is to use only letters,\ndigits and underscores, so it\'s advisable to use only these characters\nfor naming the model dictionary keys. Otherwise, users might not be able\nto easily access those options through the shell.\n\n## Required options\n\nFrom **Chonf** you can also import a `Required` dataclass that will\nnot take a default value, and will cause the `load()` function to\nraise a `ConfigLoadingIncomplete` exception if the option is not found.\nThis exception is also a\ndataclass that will contain the data that was read, with `Required`\nobjects left where stuff was not found, a list of all the keys for\nthe unlocated options and also invalid options \n(see *Functional Pre-Processing and Validation*). \nAs an example, if your code looks like this:\n\n```python\nfrom chonf import load, Option, Required\n\nmodel = {\n    "a": Required(),\n    "b": Option(),\n    "c": {\n        "c_a": Required(),\n        "c_b": Option(),\n    }\n}\n\nconf = load(model, ... # your program arguments would be here\n\n```\n\n, if the option `conf["a"]` or `conf["c"]["a"]` are missing,\nthe `load()` function will raise a `ConfigLoadingIncomplete` \nexception. In case all options are missing (following comments \nrepresent output):\n\n```python\ntry:\n    conf = load(model, ... # your program arguments would be here\nexcept ConfigLoadingIncomplete as err:\n    print(err.unlocated_keys) \n    # [["a"], ["c","c_a"]]\n\n    print(err.loaded_configs) \n    # {\n    #   "a": InvalidOption(value=None, expected=Required()),\n    #   "b": None,\n    #   "c": {\n    #       "c_a": InvalidOption(value=None, expected=Required()),\n    #       "c_b": None\n    #   }\n    # }\n\n```\n\n# Multiple Config Paths\n\nIf you decide to offer more than one option of config file location,\npass a list of paths instead:\n\n```python\nconfigs = load(\n    model = mymodel,\n    author = "mycompany",\n    name = "myprogram",\n    path = ["/home/me/.config/myprogram", "/etc/myprogram"],\n)\n```\n\nYou can have several config directory options. What comes first in the\nlist will have higher priority. In this example, the user level\nconfigurations will be able to shadow the ones defined system-wide.\n\n# Change Environment Variables Prefix\n\nIf you would like to use something other than the name of your\nprogram as the prefix for the env vars, pass the `env_prefix`\nargument, as the name is also used in the path to the default\nconfig directories.\n\n```python\nconfigs = load(\n    model = mymodel,\n    author = "mycompany",\n    name = "myprogram",\n    env_prefix = "mypro",\n    ...\n```\n\n# Functional Pre-Processing and Validation\n\nIf some of your options require some specific type of data or\nany other kind of validation, **Chonf** provides a functional\napproach to verifying and pre-processing your options during\nloading.\n\nPass to your option a callable object (like a function) as the\n`pre_process` argument. The function should be able to receive\na single positional argument (the value read from some env var\nor file) and returning the pre-processed value. If the value\nis invalid, the function should raise a `chonf.InvalidOption`\nexception containing the value received and some info about\nwhat was expected.\n\nIn the following snippet, we can check if a option is a number\nand immediately convert it into it\'s numeric type:\n\n```python\nfrom chonf import load, Option, InvalidOption\n\ndef into_number(value):\n    try:\n        return float(value)\n    except ValueError as err:\n        raise InvalidOption(value, "something compatible with float") from err\n\nmodel = {"username": Option(), "a_number": Option(pre_process=into_number)}\n```\n\nFuture versions of **Chonf** will implement common predefined pre-process\nfunctions.\n\n# Repeating Structures\n\nSometimes you might want to have a group of many similar configuration\nstructures somewhere in your model. For example, in a text editor,\nyou might have a group of language server definitions, with each one\'s\nspecific info. A simple example:\n\n```toml\n[language_servers]\n\n    [language_servers.python]\n    name = "Python Language Server"\n    command = "pyls"\n\n    [language_servers.fortran]\n    name = "Fortran Language Server"\n    command = "flang"\n```\n\n**Chonf** provides a simple way to define such repetitions. Define\nthe repeating sub-model in a `Repeat` object:\n\n```python\nfrom chonf import load, Required, Repeat\n\nmodel = {\n    "language_servers": Repeat({\n        "name": Required(),\n        "command": Required()\n    })\n}\n\nconfigs = load(model, "mycompany", "myapp")\n```\n\nNotice how you can have required options inside the repeating structures.\nThe blocks for python and fortran in the previous example are not required,\nbut if they are found, their name and command will be required. Also, \n`Repeat` must find a subtree on your configurations, if it finds a leaf node\n(a value such as a string) it will deem it invalid.\n\nAlso, if you know the keys for each block, nothing is stopping you from\nusing dictionary comprehensions.\n\n# Default Config Paths\n\n**Chonf** should be able to identify the user\'s OS and locate\ndefault locations for user-wide and system-wide configurations, \nbut this feature is not well tested yet. Feedback is very welcome. \n\nTo try it, just skip the `path` argument on the `load()`\nfunction, and it will try to find the config files in your system\'s\ndefault. If you wish to see where this location might be\nby **Chonf**\'s algorithm, call `chonf.default_path()` or\ntake a look at the `paths.py` module on the source code.\n\n# Next Steps\n\nThis project is still in its early stages, this is what\nwe plan to allow next:\n\n- Procedurally generated options from python scripts \n- Yaml and XML (.ini is way too limiting so we probably won\'t implement it)\n- User and System level priority order customization for the end user\n- Support for custom relative paths (location of config files inside config folder)\n- Function for dumping data back into whatever format the end user defined as preferred\n- Make all file formats optional other than Json that already comes with python. \n- Figure out versioning and publish on pip\n',
    'author': 'João C. Rodrigues Jr.',
    'author_email': 'jc.rodrigues1997@usp.br',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://gitlab.com/Ovenbird-j/chonf',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'python_requires': '>=3.9,<4.0',
}


setup(**setup_kwargs)
