{ "info": { "author": "Thibaut Le Page", "author_email": "thilp@thilp.net", "bugtrack_url": null, "classifiers": [ "License :: OSI Approved :: MIT License", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7" ], "description": "# Umwelt\n\n[dataclasses]: https://docs.python.org/3/library/dataclasses.html\n[pydantic]: https://pydantic-docs.helpmanual.io/\n\nDescribe a configuration schema with [dataclasses][] or [pydantic][] and\nload values from the environment, in a static-typing-friendly way.\n\n## Examples\n\n### Flat\n\n```python\n>>> os.environ[\"APP_HOSTS\"] = '[\"b.org\",\"sky.net\"]'\n>>> os.environ[\"APP_TOKEN\"] = \"very secret\"\n```\n\n```python\nfrom typing import Sequence\nfrom pydantic import SecretStr\nimport umwelt\n\nclass MyConfig:\n hosts: Sequence[str]\n token: SecretStr\n replicas: int = 2\n\nconfig = umwelt.new(MyConfig, prefix=\"app\")\n```\n\n```python\n>>> dataclasses.is_dataclass(config)\nTrue\n>>> config.hosts\n[\"b.org\", \"sky.net\"]\n>>> config.token\nSecretStr('**********')\n>>> config.replicas\n2\n```\n\n### Nested\n\n```python\n>>> os.environ[\"APP_DB_PORT\"] = \"32\"\n```\n\n```python\nfrom __future__ import annotations # for forward-references\nfrom pydantic import UrlStr\nimport umwelt\n\nclass MyConfig:\n db: DbConfig\n host: UrlStr = \"http://b.org\"\n\n@umwelt.subconfig\nclass DbConfig:\n port: int\n debug: bool = False\n\nconfig = umwelt.new(MyConfig, prefix=\"app\")\n```\n\n```python\n>>> config.host\n\"http://b.org\"\n>>> config.db.port\n32\n```\n\n## Install\n\n```shell script\n$ pip install umwelt\n```\n\n## Features\n\n### umwelt.new\n\n`umwelt.new` expects one positional argument: the config class to fill.\nUmwelt will convert it into a [dataclass][dataclasses] if it's not one already.\n\n`umwelt.new` also accepts named arguments:\n- **`source`** (by default `os.environ`) is a `Mapping[str, str]` from which\nvalues are extracted.\n- **`prefix`** can be a string or a callable. As a string, it is prepended to\nthe config field's name. As a callable, it receives the config field's name and\nits result is the source key name.\n- **`decoder`** is a callable expecting a type and a string, and returns a\nconversion of that string in that type, or in a type that pydantic can convert\nin that type.\nFor example, when umwelt's default `decoder` is called with (`List[Set[int]]`,\n`\"[[1]]\"`), it simply decodes the string from JSON and hence returns a list of\n_lists_, which pydantic properly converts into a list of _sets_. \n\n### @umwelt.subconfig\n\n`@umwelt.subconfig` tags classes so that, when they appear as field annotations\nin another config class, `umwelt.new` doesn't instantiate them from a single\n`source` value, but rather from one `source` value _per class field_.\n\nExample:\n\n```python\nclass Point: # no @subconfig\n def __init__(self, s: str): # string input\n self.x, self.y = s.split(\",\", 1) # arbitrary implementation\n\nclass MyConf:\n point: Point\n\nconf = umwelt.new(MyConf, source={\"POINT\": \"1,2\"}) # one source entry\nconf.point # \n```\n\n`conf.point` is an instance of _Point_, built by passing the input value `\"1,2\"`\ndirectly to `Point.__new__`.\nThere is only one `source` key: `POINT`.\n\nNow compare with `@umwelt.subconfig`:\n\n```python\n@umwelt.subconfig\nclass Point:\n x: int\n y: int\n\nclass MyConf:\n point: Point\n\nconf = umwelt.new(MyConf, source={\"POINT_X\": \"1\", \"POINT_Y\": \"2\"})\nconf.point # Point(x=1, y=2)\n```\n\n`conf.point` is still an instance of _Point_ (_Point_ has been made a\ndataclass by Umwelt, hence the automatic `__str__` implementation).\nThere are **two** `source` keys: `POINT_X` and `POINT_Y`, each corresponding to\na field of the _Point_ class.\n\n## Comparison with Ecological\n\nI've used [Ecological][] for a long time.\nToday, a large part of Ecological's codebase implements features already found\nin [dataclasses][] and [pydantic][], which are more mature.\nI believe Ecological's design can be dramatically simplified _and_ improved by\nenforcing a strict separation of concerns:\n\n- class scaffolding is the responsibility of [dataclasses][] (which, compared\n to metaclasses, is simpler, more introspectable, and comes with helpers like\n `asdict`);\n- type coercion and validation is the responsibility of [pydantic][] (which has\n more features, e.g. nested data types, JSON Schema, serialization, etc.);\n- mapping a [pydantic][] schema (the configuration class) to a string-to-string\n dict (like `os.environ`) is the responsibility of Umwelt.\n\nSome compatibility-breaking decisions prevent from doing this in Ecological:\n\n- Don't autoload configuration values, especially not at class definition time.\n Instead, offer just one function (`umwelt.new`) that loads the configuration\n when it is called.\n- Don't tie variable prefixes to configuration classes, as that doesn't play\n well with nested configurations.\n\n[ecological]: https://github.com/jmcs/ecological\n[autoloading]: https://github.com/jmcs/ecological/issues/20\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/thilp/umwelt", "keywords": "", "license": "MIT", "maintainer": "Thibaut Le Page", "maintainer_email": "thilp@thilp.net", "name": "umwelt", "package_url": "https://pypi.org/project/umwelt/", "platform": "", "project_url": "https://pypi.org/project/umwelt/", "project_urls": { "Homepage": "https://github.com/thilp/umwelt", "Repository": "https://github.com/thilp/umwelt" }, "release_url": "https://pypi.org/project/umwelt/2019.8.2/", "requires_dist": [ "pydantic (>=0.17,<1)" ], "requires_python": ">=3.6,<4.0", "summary": "Configure your program via environment variables, validated by pydantic.", "version": "2019.8.2" }, "last_serial": 5722835, "releases": { "0.1.0": [ { "comment_text": "", "digests": { "md5": "ad8d8cd36a92490a2e1d2fde35fe8d84", "sha256": "f47db5e090f988a88f5e4a75d6519b61adb90d3d10bd4a528f55c2eaeb359e80" }, "downloads": -1, "filename": "umwelt-0.1.0-py3-none-any.whl", "has_sig": false, "md5_digest": "ad8d8cd36a92490a2e1d2fde35fe8d84", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6,<4.0", "size": 3457, "upload_time": "2019-08-18T14:36:04", "url": "https://files.pythonhosted.org/packages/6a/8d/21e95668f5d44e1930b00717b7359ae276c4da27ab43de98064e79523dd8/umwelt-0.1.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "06a59715959e91ff4d002c1d03835be8", "sha256": "dc1a080d7f239f767582f78ade6054179fa992c0efeac54459eafc7e6a59ce84" }, "downloads": -1, "filename": "umwelt-0.1.0.tar.gz", "has_sig": false, "md5_digest": "06a59715959e91ff4d002c1d03835be8", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6,<4.0", "size": 2544, "upload_time": "2019-08-18T14:36:06", "url": "https://files.pythonhosted.org/packages/2c/aa/e66f2fe7db1dc5e4f79e06eff1cbe8672128b8ad316a4e87e9cf6a8c05b7/umwelt-0.1.0.tar.gz" } ], "2019.8.1": [ { "comment_text": "", "digests": { "md5": "ec15fb7bdcc00b61644741a0005c092b", "sha256": "8d93527aee7bab5c30f2601647404d245953b0bd5b6f2003398f3fdf423cb09f" }, "downloads": -1, "filename": "umwelt-2019.8.1-py3-none-any.whl", "has_sig": false, "md5_digest": "ec15fb7bdcc00b61644741a0005c092b", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6,<4.0", "size": 5823, "upload_time": "2019-08-20T22:19:14", "url": "https://files.pythonhosted.org/packages/8f/c0/802797bc29f5a0f6b7a1b0387584064c5199658dd72db71fd2cc1afb89e5/umwelt-2019.8.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "338ccf9b76c6cb1f0162af5c2edb6525", "sha256": "fada1c46375e6c88533f44de178bae1fbe716d22beb7e8883d1b0b5a90ca4b90" }, "downloads": -1, "filename": "umwelt-2019.8.1.tar.gz", "has_sig": false, "md5_digest": "338ccf9b76c6cb1f0162af5c2edb6525", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6,<4.0", "size": 5469, "upload_time": "2019-08-20T22:19:16", "url": "https://files.pythonhosted.org/packages/be/d4/4d0739beb822cf4d6fd89d232684f7abbbfdedbf09412177b5982a26b263/umwelt-2019.8.1.tar.gz" } ], "2019.8.2": [ { "comment_text": "", "digests": { "md5": "92dc3d5760a614b5fcb46ab7fb75b32a", "sha256": "4e6b8c9aa33324fab9807975051548953f7cc524af5301059028e2f1fc91cf0e" }, "downloads": -1, "filename": "umwelt-2019.8.2-py3-none-any.whl", "has_sig": false, "md5_digest": "92dc3d5760a614b5fcb46ab7fb75b32a", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6,<4.0", "size": 6159, "upload_time": "2019-08-23T21:13:39", "url": "https://files.pythonhosted.org/packages/d3/dc/2fcaa9d72457c2bbcbe0aa6897d3443848044b7d7bcc0ef089b328ed30b2/umwelt-2019.8.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "eae743b4d26b12b2447f38ca92d9c548", "sha256": "72c2c095e3b4aa059fb0082d0e70f117641782b4a35459f6a6fdeef85c873dbf" }, "downloads": -1, "filename": "umwelt-2019.8.2.tar.gz", "has_sig": false, "md5_digest": "eae743b4d26b12b2447f38ca92d9c548", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6,<4.0", "size": 5863, "upload_time": "2019-08-23T21:13:40", "url": "https://files.pythonhosted.org/packages/e7/1a/e85037f106b8f6035262b8bd50046c6a0a0a5e56dd6e23b6c32bb378578a/umwelt-2019.8.2.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "92dc3d5760a614b5fcb46ab7fb75b32a", "sha256": "4e6b8c9aa33324fab9807975051548953f7cc524af5301059028e2f1fc91cf0e" }, "downloads": -1, "filename": "umwelt-2019.8.2-py3-none-any.whl", "has_sig": false, "md5_digest": "92dc3d5760a614b5fcb46ab7fb75b32a", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": ">=3.6,<4.0", "size": 6159, "upload_time": "2019-08-23T21:13:39", "url": "https://files.pythonhosted.org/packages/d3/dc/2fcaa9d72457c2bbcbe0aa6897d3443848044b7d7bcc0ef089b328ed30b2/umwelt-2019.8.2-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "eae743b4d26b12b2447f38ca92d9c548", "sha256": "72c2c095e3b4aa059fb0082d0e70f117641782b4a35459f6a6fdeef85c873dbf" }, "downloads": -1, "filename": "umwelt-2019.8.2.tar.gz", "has_sig": false, "md5_digest": "eae743b4d26b12b2447f38ca92d9c548", "packagetype": "sdist", "python_version": "source", "requires_python": ">=3.6,<4.0", "size": 5863, "upload_time": "2019-08-23T21:13:40", "url": "https://files.pythonhosted.org/packages/e7/1a/e85037f106b8f6035262b8bd50046c6a0a0a5e56dd6e23b6c32bb378578a/umwelt-2019.8.2.tar.gz" } ] }