# coveo-settings
Whenever you want the user to be able to configure something through an environment variable, this module has your back:
```python
from coveo_settings import StringSetting, BoolSetting
DATABASE_URL = StringSetting('project.database.url')
DATABASE_USE_SSL = BoolSetting('project.database.ssl')
```
The user can then configure the environment variables `project.database.url` and `project.database.ssl` to configure the application.
When accessed, the values are automatically converted to the desired type:
- `StringSetting` will always be a string
- `BoolSetting` is either True or False, but accepts "yes|no|true|false|1|0" as input (case-insensitive, of course)
- `IntSetting` and `FloatSetting` are self-explanatory
- `DictSetting` allows you to use JSON maps
- `PathSetting` gives a Path instance, and also implements PathLike and the `/` operator
If the input cannot be converted to the value type, an `TypeConversionConfigurationError` exception is raised.
A default (fallback) value may be specified. The fallback may be a `callable`.
A validation callback may be specified for custom logic and error messages.
Not limited to environment variables; supports redirection to custom implementations.
**A setting can be set as sensitive for logging purposes. When logging, use repr(setting) to get the correct representation.**
## Accessing the value
There are various ways to obtain the value:
```python
from coveo_settings import BoolSetting
DATABASE_USE_SSL = BoolSetting('project.database.ssl')
# this method will raise an exception if the setting has no value and no fallback
use_ssl = bool(DATABASE_USE_SSL)
use_ssl = DATABASE_USE_SSL.get_or_raise()
assert use_ssl in [True, False]
# this method will not raise an exception
use_ssl = DATABASE_USE_SSL.value
assert use_ssl in [True, False, None]
# use "is_set" to check if there is a value set for this setting; skips validation check
if DATABASE_USE_SSL.is_set:
use_ssl = bool(DATABASE_USE_SSL)
# use "is_valid" to verify if the value passes the validation callback. implies is_set.
if not DATABASE_USE_SSL.is_valid:
...
```
## Loose environment key matching
Matching the key of the environment variable `project.database.ssl` is done very loosely:
- case-insensitive
- dots and underscores are ignored completely (`foo_bar` and `f__ooba.r` are equal)
- useful for some runners that don't support dots in environment variable keys
## Use ready validation
You can quickly validate that a string is in a specific list like this:
```python
from coveo_settings.settings import StringSetting
from coveo_settings.validation import InSequence
ENV = StringSetting("environment", fallback="dev", validation=InSequence("prod", "staging", "dev"))
```
## Redirection
You can register custom redirection schemes in order to support any data source.
Much like `https://` is a clear scheme, you may register callback functions to trigger when the value of a setting
starts with the scheme(s) you define. For instance, let's support a custom API and a file storage:
```python
from coveo_settings import settings_adapter, StringSetting, ConfigValue
@settings_adapter("internal-api::")
def internal_api_adapter(key: str) -> ConfigValue:
# the scheme was automatically removed for convenience; only the resource remains
assert "internal-api::" not in key
return "internal api" # implement logic to obtain value from internal api
@settings_adapter("file::", strip_scheme=False)
def file_adapter(key: str) -> ConfigValue:
# you can keep the scheme by specifying `strip_scheme=False`
assert key.startswith("file::")
return "file adapter" # implement logic to parse the key and retrieve the setting value
assert StringSetting('...', fallback="internal-api::settings/user-name").value == "internal api"
assert StringSetting('...', fallback="file::settings.yaml/user-name").value == "file adapter"
# even though we used `fallback` above, the redirection is driven by the user:
import os
REDIRECT_ME = StringSetting('test')
os.environ['test'] = "file::user.json::name"
assert REDIRECT_ME.value == "file adapter"
os.environ['test'] = "internal-api::url"
assert REDIRECT_ME.value == "internal api"
```
Keep in mind that there's no restriction on the prefix scheme; it's your responsibility to pick something unique
that can be set as the value of an environment variable.
### Redirection is recursive
The value of a redirection may be another redirection and may juggle between adapters.
A limit of 50 redirections is supported:
```python
import os
from coveo_settings import StringSetting
os.environ["expected"] = "final value"
os.environ["redirected"] = "env->expected"
os.environ["my-setting"] = "env->redirected"
assert StringSetting("my-setting").value == "final value"
```
### Builtin environment redirection
The builtin redirection scheme `env->` can be used to redirect to a different environment variable.
The example below demonstrates the deprecation/migration of `my-setting` into `new-setting`:
```python
import os
from coveo_settings import StringSetting
os.environ["new-setting"] = "correct-value"
os.environ["my-setting"] = "env->new-setting"
assert StringSetting("my-setting").value == "correct-value"
```
## Cached
You can set a setting to cache the first valid value with `cached=True`.
This is particularly useful in redirection scenarios to avoid repeating requests too often.
## Setting the value
You can override the value using `setting.value = "some value"` and clear the override with `setting.value = None`.
Clearing the override resumes the normal behavior of the environment variables and the fallback value, if set.
This is typically used as a way to propagate CLI switches globally.
For mocking scenarios, refer to the `Mocking` section below.
## Mocking
When you need a setting value for a test, use the `mock_config_value` context manager:
```python
from coveo_settings import StringSetting
from coveo_settings.mock import mock_config_value
SETTING = StringSetting(...)
assert not SETTING.is_set
with mock_config_value(SETTING, 'new-value'):
assert SETTING.is_set
```
You can also clear the value:
```python
from coveo_settings import StringSetting
from coveo_settings.mock import mock_config_value
SETTING = StringSetting(..., fallback='test')
assert SETTING.is_set
with mock_config_value(SETTING, None):
assert not SETTING.is_set
```
Raw data
{
"_id": null,
"home_page": "https://github.com/coveooss/coveo-python-oss/tree/main/coveo-settings",
"name": "coveo-settings",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": null,
"author": "Jonathan Pich\u00e9",
"author_email": "tools@coveo.com",
"download_url": "https://files.pythonhosted.org/packages/5a/5a/8cfe842f5abc53fe87cf2035bdde795a0b6b1fbf42bf221137742a3d1a74/coveo_settings-2.1.8.tar.gz",
"platform": null,
"description": "# coveo-settings\n\nWhenever you want the user to be able to configure something through an environment variable, this module has your back:\n\n```python\nfrom coveo_settings import StringSetting, BoolSetting\n\nDATABASE_URL = StringSetting('project.database.url')\nDATABASE_USE_SSL = BoolSetting('project.database.ssl')\n```\n\nThe user can then configure the environment variables `project.database.url` and `project.database.ssl` to configure the application.\n\nWhen accessed, the values are automatically converted to the desired type:\n\n- `StringSetting` will always be a string\n- `BoolSetting` is either True or False, but accepts \"yes|no|true|false|1|0\" as input (case-insensitive, of course)\n- `IntSetting` and `FloatSetting` are self-explanatory\n- `DictSetting` allows you to use JSON maps\n- `PathSetting` gives a Path instance, and also implements PathLike and the `/` operator\n\nIf the input cannot be converted to the value type, an `TypeConversionConfigurationError` exception is raised.\n\nA default (fallback) value may be specified. The fallback may be a `callable`.\n\nA validation callback may be specified for custom logic and error messages.\n\nNot limited to environment variables; supports redirection to custom implementations.\n\n**A setting can be set as sensitive for logging purposes. When logging, use repr(setting) to get the correct representation.**\n\n\n\n## Accessing the value\n\nThere are various ways to obtain the value:\n\n```python\nfrom coveo_settings import BoolSetting\n\nDATABASE_USE_SSL = BoolSetting('project.database.ssl')\n\n# this method will raise an exception if the setting has no value and no fallback\nuse_ssl = bool(DATABASE_USE_SSL)\nuse_ssl = DATABASE_USE_SSL.get_or_raise()\nassert use_ssl in [True, False]\n\n# this method will not raise an exception\nuse_ssl = DATABASE_USE_SSL.value\nassert use_ssl in [True, False, None]\n\n# use \"is_set\" to check if there is a value set for this setting; skips validation check\nif DATABASE_USE_SSL.is_set:\n use_ssl = bool(DATABASE_USE_SSL)\n\n# use \"is_valid\" to verify if the value passes the validation callback. implies is_set.\nif not DATABASE_USE_SSL.is_valid:\n ...\n```\n\n\n## Loose environment key matching\n\nMatching the key of the environment variable `project.database.ssl` is done very loosely:\n\n- case-insensitive\n- dots and underscores are ignored completely (`foo_bar` and `f__ooba.r` are equal)\n - useful for some runners that don't support dots in environment variable keys\n\n\n## Use ready validation\n\nYou can quickly validate that a string is in a specific list like this:\n\n```python\nfrom coveo_settings.settings import StringSetting\nfrom coveo_settings.validation import InSequence\n\nENV = StringSetting(\"environment\", fallback=\"dev\", validation=InSequence(\"prod\", \"staging\", \"dev\"))\n```\n\n\n## Redirection\n\nYou can register custom redirection schemes in order to support any data source.\n\nMuch like `https://` is a clear scheme, you may register callback functions to trigger when the value of a setting\nstarts with the scheme(s) you define. For instance, let's support a custom API and a file storage:\n\n```python\nfrom coveo_settings import settings_adapter, StringSetting, ConfigValue\n\n\n@settings_adapter(\"internal-api::\")\ndef internal_api_adapter(key: str) -> ConfigValue:\n # the scheme was automatically removed for convenience; only the resource remains\n assert \"internal-api::\" not in key\n return \"internal api\" # implement logic to obtain value from internal api\n\n\n@settings_adapter(\"file::\", strip_scheme=False)\ndef file_adapter(key: str) -> ConfigValue:\n # you can keep the scheme by specifying `strip_scheme=False`\n assert key.startswith(\"file::\")\n return \"file adapter\" # implement logic to parse the key and retrieve the setting value\n\n\nassert StringSetting('...', fallback=\"internal-api::settings/user-name\").value == \"internal api\"\nassert StringSetting('...', fallback=\"file::settings.yaml/user-name\").value == \"file adapter\"\n\n\n# even though we used `fallback` above, the redirection is driven by the user:\nimport os\n\nREDIRECT_ME = StringSetting('test')\nos.environ['test'] = \"file::user.json::name\"\nassert REDIRECT_ME.value == \"file adapter\"\nos.environ['test'] = \"internal-api::url\"\nassert REDIRECT_ME.value == \"internal api\"\n```\n\nKeep in mind that there's no restriction on the prefix scheme; it's your responsibility to pick something unique\nthat can be set as the value of an environment variable.\n\n\n### Redirection is recursive\n\nThe value of a redirection may be another redirection and may juggle between adapters.\nA limit of 50 redirections is supported:\n\n```python\nimport os\n\nfrom coveo_settings import StringSetting\n\n\nos.environ[\"expected\"] = \"final value\"\nos.environ[\"redirected\"] = \"env->expected\"\nos.environ[\"my-setting\"] = \"env->redirected\"\n\nassert StringSetting(\"my-setting\").value == \"final value\"\n```\n\n### Builtin environment redirection\n\nThe builtin redirection scheme `env->` can be used to redirect to a different environment variable.\nThe example below demonstrates the deprecation/migration of `my-setting` into `new-setting`:\n\n```python\nimport os\n\nfrom coveo_settings import StringSetting\n\nos.environ[\"new-setting\"] = \"correct-value\"\nos.environ[\"my-setting\"] = \"env->new-setting\"\n\nassert StringSetting(\"my-setting\").value == \"correct-value\"\n```\n\n## Cached\n\nYou can set a setting to cache the first valid value with `cached=True`.\nThis is particularly useful in redirection scenarios to avoid repeating requests too often.\n\n\n## Setting the value\n\nYou can override the value using `setting.value = \"some value\"` and clear the override with `setting.value = None`.\nClearing the override resumes the normal behavior of the environment variables and the fallback value, if set.\n\nThis is typically used as a way to propagate CLI switches globally.\nFor mocking scenarios, refer to the `Mocking` section below.\n\n\n## Mocking\n\nWhen you need a setting value for a test, use the `mock_config_value` context manager:\n\n```python\nfrom coveo_settings import StringSetting\nfrom coveo_settings.mock import mock_config_value\n\nSETTING = StringSetting(...)\n\nassert not SETTING.is_set\nwith mock_config_value(SETTING, 'new-value'):\n assert SETTING.is_set\n```\n\nYou can also clear the value:\n\n```python\nfrom coveo_settings import StringSetting\nfrom coveo_settings.mock import mock_config_value\n\nSETTING = StringSetting(..., fallback='test')\n\nassert SETTING.is_set\nwith mock_config_value(SETTING, None):\n assert not SETTING.is_set\n```\n",
"bugtrack_url": null,
"license": "Apache-2.0",
"summary": "Settings driven by environment variables.",
"version": "2.1.8",
"project_urls": {
"Homepage": "https://github.com/coveooss/coveo-python-oss/tree/main/coveo-settings",
"Repository": "https://github.com/coveooss/coveo-python-oss/tree/main/coveo-settings"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "a9e47a089c229834823bbb228ba6aa190df9e344823ee425bda08a812d21997f",
"md5": "69aea5a3bf1c6774d7f863692f4baa3d",
"sha256": "03aa4f3fea85e6e5cc4128e6087840ab2c6465f44ec43747ceb79a228c86850a"
},
"downloads": -1,
"filename": "coveo_settings-2.1.8-py3-none-any.whl",
"has_sig": false,
"md5_digest": "69aea5a3bf1c6774d7f863692f4baa3d",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 13860,
"upload_time": "2024-04-24T12:47:25",
"upload_time_iso_8601": "2024-04-24T12:47:25.205422Z",
"url": "https://files.pythonhosted.org/packages/a9/e4/7a089c229834823bbb228ba6aa190df9e344823ee425bda08a812d21997f/coveo_settings-2.1.8-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "5a5a8cfe842f5abc53fe87cf2035bdde795a0b6b1fbf42bf221137742a3d1a74",
"md5": "1c4c8b1c23e01f28e480fad937859c34",
"sha256": "e4115db869158b7df474cd6dec82c8af238941617ce02c9584bcda1d68a35721"
},
"downloads": -1,
"filename": "coveo_settings-2.1.8.tar.gz",
"has_sig": false,
"md5_digest": "1c4c8b1c23e01f28e480fad937859c34",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 11651,
"upload_time": "2024-04-24T12:47:26",
"upload_time_iso_8601": "2024-04-24T12:47:26.871487Z",
"url": "https://files.pythonhosted.org/packages/5a/5a/8cfe842f5abc53fe87cf2035bdde795a0b6b1fbf42bf221137742a3d1a74/coveo_settings-2.1.8.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-04-24 12:47:26",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "coveooss",
"github_project": "coveo-python-oss",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "coveo-settings"
}