# -*- 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': '4.0.2',
    '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. [Using with Pydantic](#using-with-pydantic)\n8. [How Stela find the configuration files?](#how-stela-find-the-configuration-files)\n9. [How Stela find the Environment Variables?](#how-stela-find-the-environment-variables)\n10. [How Stela handle more complex cases?](#how-stela-handle-more-complex-cases)\n11. [Full Lifecycle example](#full-lifecycle-example)\n12. [When Stela read the data?](#when-stela-read-the-data)\n13. [Refreshing Stela settings](#refreshing-stela-settings)\n14. [How Stela read the dictionary values?](#how-stela-read-the-dictionary-values)\n15. [Logging Data](#logging-data)\n16. [All Stela Configuration Options](#all-stela-configuration-options)\n17. [Migrating from version 3.x](#migrating-from-version-3x)\n18. [Migrating from version 2.x](#migrating-from-version-2x)\n19. [Migrating from version 1.x](#migrating-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 need to create a `pyproject.toml` file, and\nunder the table `environment` add the key/values you need:\n\n```toml\n# pyproject.toml\n[environment]\nmy_file_path = "/foo/bar"\nmy_api_url = "https://foo.bar"\ndb.url = "user@password:db"\n```\n\nIn Python you will access these settings importing stela like this:\n\n```python\nfrom stela import settings\n\nAPI_URL = settings["my_api_url"]\n# API_URL = "https://foo.bar"\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 value from `pyproject.toml` tables.\n\nSome examples:\n\n| Variable in toml   | Environment Variable Name |\n|:-------------------|:--------------------------|\n| debug              | DEBUG                     |\n| 3rd_party_api_url  | 3_RD_PARTY_API_URL        |\n| db.password        | DB_PASSWORD               |\n\nFor example, for `db.url` Stela will look for\n`DB_URL` in the following order:\n\n1. Import `.env` data, if exists, in python environment,\nusing [python-dotenv](https://github.com/theskumar/python-dotenv) library.\n2. Check for `DB_URL` in `os.environ`\n3. Fallback to `db.url` in `pyproject.toml`\n\nIf data does not exist, Stela will raise a `KeyError`, like a dictionary.\n\n```toml\n# pyproject.toml\ndb.url = "fake@credentials"\n```\n\n```dotenv\n# .env file\nDB_URL=real@credentials  # will override info from pyproject.toml\n```\n\n```python\nfrom stela import settings\n\nDATABASE_URL = settings["db.url"]\n# DATABASE_URL = "real@credentials"\n```\n\n### Using layered environments\n\nFor now, Stela are looking only for the `environment` table in your\n`pyproject.toml`. But you may need to define\nyour settings *per environment layer* (i.e. development, tests, staging,\nproduction, etc.)\n\nFirst add `use_environment_layers` in Stela options:\n\n```toml\n# pyproject.toml\n[tool.stela]\nuse_environment_layers = true\n```\n\nThen define `environment` sub-tables in\n`pyproject.toml`:\n\n```toml\n# pyproject.toml\n[tool.stela]\nuse_environment_layers = true\n\n[environment]  # now is a shared data between environments\nmy_api_timeout = 30\n\n[environment.local]\nmy_api_url = "http://localhost:8000"\n\n[environment.production]\nmy_api_url = "https://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`.\n\nTo add a default environment, use the `default_environment` option:\n\n```toml\n# pyproject.toml\n[tool.stela]\nuse_environment_layers = true\ndefault_environment = "local"\n\n[environment]  # now is a shared data between environments\nmy_api_timeout = 30\n\n[environment.local]\nmy_api_url = "http://localhost:8000"\n\n[environment.production]\nmy_api_url = "https://foo.bar"\n```\n\nTo use different environment variable, use the `environment_variable_name` option:\n\n```toml\n# pyproject.toml\n[tool.stela]\nuse_environment_layers = true\ndefault_environment = "local"\nenvironment_variable_name = "ENV"\n\n[environment]  # now is a shared data between environments\nmy_api_timeout = 30\n\n[environment.local]\nmy_api_url = "http://localhost:8000"\n\n[environment.production]\nmy_api_url = "https://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 can be override from 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\nshow_logs = false                           # Show Stela Logs\nlog_filtered_value = true                  # Show Value from Stela settings in logs filtered. Use with caution.\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 environment layer per config files\n(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### Using with Pydantic\n[Pydantic](https://pydantic-docs.helpmanual.io/) is a very popular library for data validation and settings management.\nStela can enhance pydantic experience, by being a additional settings for it. You can import stela settings as a dictionary\nand import in pydantic settings, as per [Customize settings sources](https://pydantic-docs.helpmanual.io/usage/settings/#customise-settings-sources) documentation:\n\n```toml\n# pyproject.toml\n[tool.stela]\nuse_environment_layers = true\ndefault_environment = "local"\nenvironment_variable_name = "ENV"\n\n[environment]  # Shared data between environments\nmy_api_timeout = 30\ndb.host = "localhost"\n\n[environment.local]\nmy_api_url = "http://localhost:8000"\n\n[environment.production]\nmy_api_url = "https://foo.bar"\n```\n\n```python\nfrom pydantic import BaseSettings, Extra\nfrom stela.pydantic import stela_settings\n\nclass DbSettings(BaseSettings):\n    host: str\n    port: int = 5432\n\nclass Settings(BaseSettings):\n    my_api_url: str\n    my_api_timeout: int\n    db: DbSettings\n\n    class Config:\n        # If you need to debug stela dictionary,\n        # you can use this option to log stela settings dictionary\n        log_stela_settings = True\n\n        # If Stela dictionary has extra fields,\n        # you can use this option to ignore them\n        extra = Extra.ignore\n\n        @classmethod\n        def customise_sources(\n            cls,\n            init_settings,\n            env_settings,\n            file_secret_settings,\n        ):\n            return (\n                init_settings,\n                stela_settings,  # Add stela settings before env_settings\n                env_settings,\n                file_secret_settings,\n            )\n\nprint(Settings())  # for ENV=local\n#> my_api_url=\'http://localhost:8000\', my_api_timeout=30, db=DbSettings(host=\'localhost\', port=5432)\n```\n\nBoth config options (`log_stela_settings` and `extra`) are optional.\n\nPlease check file `test_pydantic.py` in tests folder for additional info.\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 `os.environ`\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\n```python\nfrom stela import settings\n\nmy_conf = settings["foo.bar"]\n# my_conf = "hello world" from MYPROJECT_FOO_BAR\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, using the `pre_load`, `custom_load` and `post_load` decorators\nto modified data received from files and pyproject.toml, before importing settings.\n\nTo use this:\n\n1. The decorated function **must** return a valid python dictionary.\n2. Just one decorated function per phase.\n3. Import the decorated functions before import stela settings for the first time. If you create the file\n`conf_stela.py` at project root, decorated functions in there will be imported automatically.\n\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.\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 env\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    response = ssm.get_parameters_by_path(\n        Path=f\'/foo/bar/{environment}\',\n        WithDecryption=True\n    )\n    return {parameter["Name"]: parameter["Value"] for parameter in response["Parameters"]}\n```\n\n### Full Lifecycle example\n\n```toml\n# pyproject.toml\n[environment]\ntest = true\n```\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\n```python\nfrom stela import settings\n\nprint(settings)\n# {"foo": "bar", "has_dogs": True, "number_of_dogs": 1, "test": True}\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, after run all lifecycle\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.\nCheck the [unit tests](/tests/test_environments.py) for additional input.\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### Logging data\nStela use the [loguru](https://github.com/Delgan/loguru) package for logging, using `INFO` for general messages\nand `DEBUG` for key/values retrieved in toml, environment keys, decorators, etc...\nYou can use the logs to debug data during Stela lifecycle.\n\nBy default, log are disabled. You can modify this behavior globally with the following configurations:\n\n```toml\n[tool.stela]\nshow_logs = true\nlog_filtered_value = true\n```\n\nAlso, you can use decorators for fine-tuning logging:\n\n```python\nfrom stela import settings\nfrom stela.decorators import stela_enable_logs, stela_disable_logs\n\n@stela_enable_logs\ndef my_bugged_code():\n    return settings["my_api_url"]\n\n@stela_disable_logs\ndef my_sensible_code():\n    return settings["my_credentials"]\n```\n\nThe log level can be defined using the `LOGURU_LOG_LEVEL` as per loguru documentation.\n\n### All Stela Configuration Options\n\nAll configuration files can be overridden using an 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 = false                                   # As per loguru settings.\nlog_filtered_value = true                           # When logging data, filter values from dict/env.\nuse_environment_layers = false                      # Use environment layers\ndotenv_overwrites_memory = true                     # If True, values from os.environ will be override with dotenv values. If False, order is os.environ -> dotenv values -> files\n```\n\n### Migrating from version 3.x\n* Support for Python 3.7 was dropped\n\n### Migrating from version 2.x\n\n* Logs are now disabled, by default. Too re-enable Stela logs:\n\n```toml\n[tool.stela]\nshow_logs = true\n```\n\nOr set `STELA_SHOW_LOGS=true` environment variable.\n\n### Migrating 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 (Pre -> Load or Custom -> Post),\n  please configure the old lifecycle in `pyproject.toml`:\n\n```toml\n[tool.stela]\nuse_environment_layers = true\ndo_not_read_dotenv = true\nload_order = ["file"] # or ["custom"] with @custom_load decorator\n```\n\n### Not working?\n\nDon\'t panic. Get a towel and, please, open an\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.8,<4',
}


setup(**setup_kwargs)
