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

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

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

install_requires = \
['loguru', 'python-dotenv', 'pyyaml', 'scalpl', 'toml']

setup_kwargs = {
    'name': 'stela',
    'version': '2.0.7',
    'description': 'Find and read your project configuration files easily',
    'long_description': '# Welcome to Stela\n\n[![PyPI](https://img.shields.io/pypi/v/stela)](https://pypi.org/project/stela/)\n[![Build](https://github.com/chrismaille/stela/workflows/tests/badge.svg)](https://github.com/chrismaille/stela/actions)\n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/stela)](https://www.python.org)\n[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://conventionalcommits.org)\n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)\n[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit)\n\n[Stela](https://en.wikipedia.org/wiki/Stele) were the "configuration\nfiles" of ancient times. This library aims to simplify your project\nconfigurations, from *json*, *yaml*, *ini* or *toml* files in a single\nPython dictionary, which values can be easily be overridden by\nenvironment variables.\n\n### Index\n\n1. [Install](#install)\n2. [Basic Use](#basic-use)\n3. [Environment Variables from Shell](#environment-variables-from-shell)\n4. [Using layered environments](#using-layered-environments)\n5. [Customize Stela](#customize-stela)\n6. [Advanced Use](#advanced-use)\n7. [How Stela find the configuration files?](#how-stela-find-the-configuration-files)\n8. [How Stela find the Environment Variables?](#how-stela-find-the-environment-variables)\n9. [How Stela handle more complex cases?](#how-stela-handle-more-complex-cases)\n10. [Full Lifecycle example](#full-lifecycle-example)\n11. [When Stela read the data?](#when-stela-read-the-data)\n12. [Refreshing Stela settings](#refreshing-stela-settings)\n13. [How Stela read the dictionary values?](#how-stela-read-the-dictionary-values)\n14. [All Stela Configuration Options](#all-stela-configuration-options)\n15. [Migrate from version 1.x](#migrate-from-version-1x)\n\n\n## Install\n\n```shell\n$ pip install stela\n```\n\n## Basic Use\n\nFor your *project settings*, you can create a `pyproject.toml` file, and\nunder the table `environment` add the key/values:\n\n```toml\n# pyproject.toml\n[environment]\nmy_file_path = "/foo/bar"\nmy_database_credentials = "user@password:db" # faked data\n```\n\nOn Python you can access this settings like this:\n\n```python\nfrom stela import settings\n\ndb_conf = settings["my_database_credentials"]\n# db_conf = "user@password:db"\n```\n\n### Environment Variables from Shell\n\nStela will check first, for the requested key, his SCREAMING_SNAKE_CASE\nformat in environment memory or `.env` file. If no data is found, Stela\nwill return the `pyproject.toml` value.\n\nFor example, for `my_database_credentials` Stela will look for\n`MY_DATABASE_CREDENTIALS` in the following order:\n\n1. Add `.env` data in python environment, overwriting original data\n   if exists.\n2. Check for `MY_DATABASE_CREDENTIALS` in `os.environ`\n3. Check for `my_database_credentials` in `pyproject.toml`\n\n### Using layered environments\n\nFor now, Stela are looking only for the `environment` table in your\n`pyproject.toml`. But for layered environments you maybe need to define\nyour settings *per environment layer* (i.e. development, tests, staging,\nproduction, etc.)\n\nTo achieve this, first define `environment` sub-tables in\n`pyproject.toml`:\n\n```toml\n# pyproject.toml\n\n[environment]  # now is a shared data between environments\nmy_database_credentials = "user@password:db"  # faked data\n\n[environment.local]\nmy_file_path = "/local/foo/bar"\n\n[environment.production]\nmy_file_path = "/server/foo/bar"\n```\n\nThen add `use_environment_layers` in Stela options:\n\n```toml\n# pyproject.toml\n\n[tool.stela]\nuse_environment_layers = true\n\n[environment]  # now is a shared data between environments\nmy_database_credentials = "user@password:db"  # faked data\n\n[environment.local]\nmy_file_path = "/local/foo/bar"\n\n[environment.production]\nmy_file_path = "/server/foo/bar"\n```\n\nWhen you add `use_environment_layers = true` in config, Stela will now\nalways try to find the current environment looking for the `ENVIRONMENT`\nvariable. If this variable is not defined, Stela will use the default\nenvironment, if available. If not, will raise a\n`StelaEnvironmentNotFoundError`. To add a default environment:\n\n```toml\n# pyproject.toml\n\n[tool.stela]\nuse_environment_layers = true\ndefault_environment = "local"\n\n[environment]  # now is a shared data between environments\nmy_database_credentials = "user@password:db"  # faked data\n\n[environment.local]\nmy_file_path = "/local/foo/bar"\n\n[environment.production]\nmy_file_path = "/server/foo/bar"\n```\n\n### Customize Stela\n\nUse the following variables to customize Stela behavior:\n\n```toml\n# You can also set these as environment variables too using STELA_ prefix.\n# For example, environment_variable_name turns STELA_ENVIRONMENT_VARIABLE_NAME\n\n[tool.stela]\nenvironment_variable_name = "ENVIRONMENT"   # The Environment variable\ndefault_environment = ""                    # The default value for Environment variable\nenv_table = "environment"                   # The main environment table in pyproject.toml\nuse_environment_layers = false              # Use environment layers\nenv_file = ".env"                           # dotenv file name\nconfig_file_path = "."                      # relative path for configuration files\ndo_not_read_dotenv = False                  # If True, will load dotenv file in os.environ\ndotenv_overwrites_memory = True             # If True, will not overwrite keys from dotenv file if they exists on environ\n```\n\nExample:\n\n```bash\n# shell\n$ export DJANGO_ENV=production\n```\n\n```toml\n# pyproject.toml\n[tool.stela]\nuse_environment_layers = true\nenvironment_variable_name = "DJANGO_ENV"\nenv_table = "my_project.config"\n\n[my_project.config]\nproject.debug = true\n\n[my_project.config.production]\nproject.debug = false\n```\n\n```python\n# settings.py\nfrom stela import settings\n\nDEBUG = settings["project.debug"]  # False from pyproject.toml or from PROJECT_DEBUG in environment\n```\n\n## Advanced Use\n\nFor very large projects, you can use separate config files per\nenvironment (like `development.ini`, `staging.ini`, etc.)\n\nAlso, you can use `.yaml`, `.ini`, `.json` and `.toml` files.\n\nIn all files, Stela will always respect nested data. Suppose a file\ncalled `development.ini` which contains:\n\n```ini\n[foo]\nbar = value\n```\n\nAs we know, Stela will convert data into a python dictionary:\n\n```python\n{\n    "foo": {\n        "bar": "value"\n    }\n}\n```\n\nYou can use the settings like this:\n\n```python\nfrom stela import settings\n\nmy_conf = settings["foo.bar"]  # my_conf = "value"\n```\n\nThis is possible because Stela uses under the hood the\n[Scalpl](https://github.com/ducdetronquito/scalpl) library.\n\n### How Stela find the configuration files?\n\nBy default, Stela will use the value from `ENVIRONMENT` environment\nvariable to find correspondent INI file. For example, if you set\n`ENVIRONMENT=development` Stela will look for `development.ini` file in\nproject root.\n\nYou can change this behaviour inside `pyproject.toml` file:\n\n```toml\n[tools.stela]\nenvironment_variable_name = "ENVIRONMENT"   # Default Enviroment variable name\nconfig_file_extension = "INI"               # YAML, TOML, JSON\nconfig_file_prefix = ""                     # You can add a prefix before name - ex.: env_development.ini\nconfig_file_suffix = ""                     # You can add a suffix after name - ex.: development_v1.ini\ndefault_environment = "development"         # use this if you do not want to define the ENVIRONMENT key\nconfig_file_path = "."                      # relative path from project root for configuration files\n```\n\n### How Stela find the Environment Variables?\n\nWe know Stela will try to find an environment variable using his\nSCREAMING_SNAKE_CASE version. In this case, when the key is `foo.bar`,\nStela will search for an env called `FOO_BAR` in memory and dotenv\nfiles, before returning his dict value. Also, you can add a\nprefix/suffix in this name (ex.: `MYPROJECT_FOO_BAR`). To do this,\ndefine them in `pyproject.toml`:\n\n```toml\n[tools.stela]\nenvironment_prefix = "MYPROJECT_"\nenvironment_suffix = ""\n```\n\nIn above case, Stela will look for the `MYPROJECT_FOO_BAR` env:\n\n```python\n# FOO_BAR = "hello_world" or\n# MYPROJECT_FOO_BAR = "hello world" if you define environment_prefix\nfrom stela import settings\n\nmy_conf = settings["foo.bar"]\n# my_conf = "hello world"\n```\n\nAlso, you can define Stela to never get values from shell and/or dotenv,\nonly from dictionary:\n\n```toml\n[tools.stela]\ndo_not_read_environment = true\ndo_not_read_dotenv = true\n```\n\n### How Stela handle more complex cases?\n\nStela uses this lifecycle to handle the settings load:\n\n#### The Pre-Load Phase (optional)\n\nIf defined, will always be the first step. To setup, create a\n`conf_stela.py` file on project root and use the `pre_load` decorator\nfor your code. This function must return a valid python dictionary.\n\nPre-Load Example:\n\n```python\n# conf_stela.py at project root\nimport plaster\nfrom stela.decorators import pre_load\nfrom stela.stela_options import StelaOptions\nfrom typing import Dict, Any\n\n@pre_load\ndef get_from_plaster(options: StelaOptions) -> Dict[Any, Any]:\n    """Get data from plaster.\n\n    Must return a valid Python dictionary.\n\n    :param options: StelaOptions instance\n    :return dict\n    """\n    env = options.current_environment  # get current environment during lifecycle\n    config_uri = f"{env}.ini#myapp"\n    settings = plaster.get_settings(config_uri, \'my-settings\')\n    return settings\n```\n\n#### The Load Phase\n\nIn this phase Stela will run 2 default loaders and a third optional\ncustom loader (if defined) on this default order:\n\n1. Runs `embed` loader (retrieve data from `pyproject.toml`, if exists)\n2. Runs `file` loader (retrieve data from config files, if exists)\n3. Runs `custom` loader if defined (from `custom_load` decorator, if\n   exists)\n\nEach step updates data received from the previous step. You can change\nthis order, modifying the `load_order` in config:\n\n```toml\n# Or STELA_LOAD_ORDER\n[tool.stela]\n# Default value is ["embed", "file", "custom"]\nload_order = ["custom"]\n```\n\nCustom Load Example:\n\n```python\n# conf_stela.py at project root\nfrom stela.decorators import custom_load\nfrom stela.stela_options import StelaOptions\nfrom typing import Dict, Any\n\n@custom_load\ndef remove_bad_data(data: Dict[Any, Any], options: StelaOptions) -> Dict[Any, Any]:\n    """Remove bad data.\n\n    Must return a valid Python dictionary.\n\n    :param options: StelaOptions instance\n    :return dict\n    """\n    can_remove_bad_data = options.dotenv_data.get("REMOVE_BAD_DATA")  # reading dotenv during lifecycle\n    if can_remove_bad_data:\n        data.pop("bad_key", None)\n    return data\n```\n\n#### The Post-Load Phase\n\nThis is, always, the last phase.\n\n```python\n# conf_stela.py at project root\nimport boto3\nfrom stela.decorators import post_load\nfrom stela import StelaOptions\nfrom typing import Dict, Any\n\n@post_load\ndef add_ssm_parameters(data: dict, options: StelaOptions) -> Dict[Any, Any]:\n    """Load settings from SSM to current Stela data.\n\n    Data returned must be a Python Dictionary.\n\n    :param data (dict): Data parsed from previous phases\n    :param options (obj): Stela Options from pyproject.toml\n    :return Dict[Any, Any]\n    """\n    ssm = boto3.client(\'ssm\')\n    environment = options.current_environment\n    parameters = ssm.get_parameters_by_path(\n        Name=f\'/foo/bar/{environment}\',\n        WithDecryption=True\n    )\n    return parameters\n```\n\n### Full Lifecycle example\n\n```python\n# conf_stela.py at project root\nfrom stela.decorators import pre_load, custom_load, post_load\nfrom typing import Dict, Any\nfrom stela import StelaOptions\n\n@pre_load\ndef pre_load(options: StelaOptions) -> Dict[Any, Any]:\n    # data value is: {}\n    # Stela Options are available in options object.\n    return {"foo": "bar"}\n\n@custom_load\ndef load(data: dict, options: StelaOptions) -> Dict[Any, Any]:\n    # data value is: {"foo": "bar", "test": True} from from pre_load and pyproject.toml\n    return {"has_dogs": True}\n\n@post_load\ndef post_load(data: dict, options: StelaOptions) -> Dict[Any, Any]:\n    # data value is: {"foo": "bar", "has_dogs": True, "test": True}\n    return {"number_of_dogs": 1}\n\n# Final data is {"foo": "bar", "has_dogs": True, "number_of_dogs": 1}\n```\n\n### When Stela read the data?\n\nStela are imported once, at module level - project settings are load and\nimmediately available:\n\n```python\nfrom stela import settings\nfrom flask import Flask\n\napp = Flask(__name__)\napp.config.update(\n    SECRET_KEY=settings["my_app_secret"] # will read from dict or MY_APP_SECRET value\n)\n\n@app.route("/")\ndef hello():\n    return f"Hello, Environment is {settings.stela_options.current_environment}"\n```\n\n### Refreshing Stela settings\n\nIf you need to reload settings, use the `stela.stela_reload` function:\n\n```python\nfrom stela.utils import stela_reload\n\ndef test_different_environments(monkeypatch):\n    from stela import settings\n    assert settings.stela_options.current_environment == "test"\n\n    monkeypatch.setenv("ENVIRONMENT", "production")\n    settings = stela_reload()\n    assert settings.stela_options.current_environment == "production"\n    monkeypatch.delenv("ENVIRONMENT")\n```\n\n### How Stela read the dictionary values?\n\nStela will respect the file format limitations. For example, INI files\nalways return values as string, TOML files returning datetime objects,\netc...\n\nFor environment variables, Stela will return value as string, by\ndefault. For example: `NUMBER_OF_CATS=3` will return a string.\n\nYou can set Stela to evaluate these values, as per\n[ast.literal_eval](https://docs.python.org/3.7/library/ast.html?highlight=literal_eval#ast.literal_eval)\nrules. To do this, add in `pyproject.toml`:\n\n```toml\n[tool.stela]\nevaluate_data = true\n```\n\n### All Stela Configuration Options\n\nAll configuration files can be override using a environment variable,\nusing the `STELA_` prefix. For example, `default_environment` turns\n`STELA_DEFAULT_ENVIRONMENT`.\n\n```toml\n# All keys and default values available in pyproject.toml\n[tool.stela]\nconfig_file_extension = "INI"                       # or YAML, TOML, JSON\nconfig_file_path = "."                              # relative path from project root, for config files (i.e. .env, development.ini, etc.)\nconfig_file_prefix = ""                             # You can add a prefix before name - ex.: env_development.ini\nconfig_file_suffix = ""                             # You can add a suffix after name - ex.: development_v1.ini\ndefault_environment = ""                            # The default value for Environment variable\ndo_not_read_environment = false                     # Do not read environment variables from shell\ndo_not_read_dotenv = false                          # Do not read dotenv file\nenv_file = ".env"                                   # dotenv file name\nenv_table = "environment"                           # The main environment table in pyproject.toml\nenvironment_prefix = ""                             # ex.: settings["foo.bar"] looks for MY_PREFIX_FOO_BAR\nenvironment_suffix = ""                             # ex.: settings["foo.bar"] looks for FOO_BAR_MY_SUFFIX\nenvironment_variable_name = "ENVIRONMENT"           # The Environment variable\nevaluate_data = false                               # Evaluate data received from config files\nload_order = ["embed", "file", "custom"]            # Default order for Loaders in Load Phase\nshow_logs = true                                    # As per loguru settings.\nuse_environment_layers = false                      # Use environment layers\ndotenv_overwrites_memory = True                     # If True, will not overwrite keys from dotenv file if they exists on environ\n```\n\n### Migrate from version 1.x\n\n* Support for Python 3.6 was dropped\n* The `stela_reload` function now is imported now from `stela.utils`\n* The `@load` decorator now was renamed to `@custom_load`\n* To mimic Stela load behavior from 1.x, please configure the old\n  lifecycle in `pyproject.toml`:\n\n```toml\n[tool.stela]\nuse_environment_layers = true\ndo_not_read_dotenv = true\nload_order = ["file"] # or ["custom"] together with @custom_load decorator\n```\n\n### Not working?\n\nDont panic. Get a towel and, please, open a\n[issue](https://github.com/chrismaille/stela/issues).\n',
    'author': 'Chris Maillefaud',
    'author_email': 'chrismaillefaud@gmail.com',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/chrismaille/stela',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'python_requires': '>=3.7,<4.0',
}


setup(**setup_kwargs)
