{ "info": { "author": "Anton Ostapenko", "author_email": "olsnod@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Utilities" ], "description": "# Magic-settings\n\n## Installation\n\n```bash\npip install magic-settings\n```\n\nUsing settings from `yaml` file\n\n```bash\npip install magic-settings[yaml]\n```\n\n## Initialization\n\n### Project settings class declaration\n\n```python\nfrom magic_settings import BaseSettings, Property\nfrom functools import partial\n\nclass MySettings(BaseSettings):\n VERSION = Property(types=str)\n PROJECT_DIR = Property(types=str)\n LOGGING_LEVEL = Property(default='INFO', choices=['NOTSET', 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'])\n RETRIES_NUMBER = Property(types=int, converts=[int])\n COEFFICIENT = Property(types=float, converts=[float])\n DEBUG = Property(types=bool, converts=[int, bool], default=False)\n DISTRIBUTED_SERVICE_HOST_NAMES = Property(types=list, converts=[partial(str.split, sep=',')])\n```\n\nClass ```Property``` is a descriptor with following parameters:\n\n- ***types*** - Type of ```value``` or a tuple of possible ```types```. It is a ```ValueError``` if ```value``` is not one of the ```types```.\n- ***validators*** - List of ```callable``` objects each of which is successively applied to ```value```. Raises ```ValueError``` if ```value``` does not pass at least one of the validations (if any validation function returns ```False```).\n- ***choices*** - List of any objects. If ```value``` is not in ```choices``` - raises ```ValueError```. When using this parameter, parameters ```types``` and ```validators``` are ignored.\n- ***default*** - Sets the default value of ```Property```.\n- ***converts*** - List of ```callable``` objects. It is a chain of transformations that are successively applied to the ```value``` and overwrite it each time. It applies to ```value``` only if ```value``` is a string. Raises ```ValueError``` if ```value``` at least one of the transformations failed to apply.\n\n### Property classes\n\nBesides ```Property``` following classes may be used for standard types:\n\n- ```BoolProperty```: accepts boolean values, converts case-insensitive ```true``` or ```false``` to appropriate python boolean value. Also this property accepts numbers (```0``` is False, ```1``` is True).\n- ```FloatProperty```: accepts float number values.\n- ```IntProperty```: accepts integer number values.\n- ```StringProperty```: accepts string values.\n- ```StringListProperty```: accepts list of strings. You can specify delimiter in constructor of this class (```,``` is default value).\n- ```HostListProperty```: accepts list of hosts. Each host is a tuple containing a ```string``` hostname and an ```int``` port. Pairs should be divided by comma, hostname and port should be divided by colon. For example, ```192.168.20.1:80,www.yandex.ru:1234,localhost:8888``` will be converted into ```[('192.168.20.1', 80), ('www.yandex.ru', 1234), ('localhost', 8888)]```.\n\nAbove example may be simplified using these properties:\n\n```python\nfrom magic_settings import (BaseSettings, Property,\n BoolProperty, FloatProperty, IntProperty, StringListProperty, StringProperty)\n\nclass MySettings(BaseSettings):\n VERSION = StringProperty()\n PROJECT_DIR = StringProperty()\n LOGGING_LEVEL = Property(default='INFO', choices=['NOTSET', 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'])\n RETRIES_NUMBER = IntProperty()\n COEFFICIENT = FloatProperty()\n DEBUG = BoolProperty(default=False)\n DISTRIBUTED_SERVICE_HOST_NAMES = StringListProperty()\n``` \n\n### Settings configuration\n\nSettings configuration occurs at the stage of creating a Settings object.\n\n```python\nfrom my_project import my_module, my_awesome_module\nfrom my_config import MySettings\n\nsettings = MySettings(\n modules=[my_module, my_awesome_module],\n prefix='MY_PROJECT_ENV_SETTINGS',\n dotenv_path='/path/to/my/env',\n override_env=True,\n yaml_settings_path='/path/to/my/yaml/settings.yaml',\n use_env=True\n)\n```\n\n### Parameters\n\n- ***modules***: List of Python modules with variables to import. Default ```None```.\n- ***prefix***: The prefix with which the environment variables are taken. Default - ```None```.\n\n _settings.py_\n\n ```python\n class MySettings(BaseSettings):\n PSYDUCK = Property(types=str)\n ```\n\n _.env_\n\n ```dotenv\n MYPROJECT_PSYDUCK=Owowowow\n ```\n\n _some_other_place.py_\n\n ```python\n settings = MySettings(prefix='MYPROJECT')\n ```\n\n or\n\n ```python\n settings = MySettings(prefix='MYPROJECT_')\n ```\n\n- ***dotenv_path***: Path to env-file. Default - ```None```. Using for exporting variables from env-file to environment. If ```dotenv_path``` is ```None``` - walking up the directory tree looking for the specified file - called ```.env``` by default.\n- ***override_env***: ```True``` - override existing system environment variables with variables from `.env` - file, ```False``` - do not override. Default - ```False```.\n- ***yaml_settings_path***: Path to yaml config file. Default - ```None```.\n- ***use_env***: ```True``` - use environment variables. Default - ```True```.\n\n### Exceptions\n\n***ValueError***: If ***modules*** type is not ```list``` or ```NoneType``` and if type of element in ***modules*** is not ```ModuleType```.\n\n## Settings loading\n\nLoading settings can be initiated anywhere in the project.\n\n```python\nfrom where_your_settings import settings\n\nsettings.init()\n```\n\nIf called again, it goes through the configuration files and update properties.\n\n## Settings priority\n\nIn case of intersection of settings the following priority will be applied:\n_my_module_ -> _my_awesome_module_ -> _.env_ -> _settings.yaml_\n\n```python\nclass MySettings(BaseSettings):\n PSYDUCK = Property(types=str)\n```\n\n_my_module.py_\n\n```python\nPSYDUCK = 'one'\n```\n\n_my_awesome_module.py_\n\n```python\nPSYDUCK = 'two'\n```\n\n_.env_\n\n```dotenv\nMYPROJECTPREFIX_PSYDUCK=env\n```\n\n_setting.yaml_\n\n```yaml\nPSYDUCK: yaml\n```\n\n## Examples\n\n```python\n>>> settings = MySettings(modules=[my_module])\n>>> settings.PSYDUCK\n'one'\n```\n\n```python\n>>> settings = MySettings(modules=[my_module, my_awesome_module])\n>>> settings.PSYDUCK\n'two'\n```\n\n```python\n>>> settings = MySettings(modules=[my_awesome_module, my_module])\n>>> settings.PSYDUCK\n'one'\n```\n\n```python\n>>> settings = MySettings(modules=[my_module, my_awesome_module], dotenv_path='/path/to/dotenv')\n>>> settings.PSYDUCK\n'env'\n```\n\n```python\n>>> settings = MySettings(modules=[my_module, my_awesome_module], dotenv_path='/path/to/dotenv', use_env=False)\n>>> settings.PSYDUCK\n'two'\n```\n\n```python\n>>> settings = MySettings(modules=[my_module, my_awesome_module], dotenv_path='/path/to/dotenv', yaml_settings_path='/path/to/yaml/settings.yaml')\n>>> settings.PSYDUCK\n'yaml'\n```\n\n## Temporary Property override\n\n_my_module.py_\n\n```python\nPIKACHU = 'Psyduck_is_not_fine'\nPSYDUCK = 'Owowowow'\n\n```\n\n```python\nfrom my_project import my_module\nfrom my_config import MySettings\n\nclass MySettings(BaseSettings):\n PSYDUCK = Property(types=str)\n PIKACHU = Property(types=str)\n\nsettings = MySettings(modules=[my_module])\nsettings.init()\n\nwith settings.temp_set_attributes(PSYDUCK='I_am_ok', PIKACHU='Psyduck_is_ok'):\n print(settings.PSYDUCK) # 'I_am_ok'\n print(settings.PIKACHU) # 'Psyduck_is_ok'\nprint(settings.PSYDUCK) # 'Owowowow'\nprint(settings.PIKACHU) # 'Psyduck_is_not_fine'\n```\n\nMethod ```temp_set_attributes``` is not thread-safe.\n\n## Settings list\n\nYou can use methods `to_dict()`, `to_json()` to get current settings:\n\n```python\nfrom magic_settings import BaseSettings, Property\n\nclass MySettings(BaseSettings):\n PSYDUCK = Property(types=str)\n PIKACHU = Property(types=str)\n\nsettings = MySettings(dotenv_path='12345.env')\nsettings.PIKACHU = '3'\nsettings.PSYDUCK = '12345'\n\npprint(settings.to_dict())\n\n{\n 'properties': {\n 'PIKACHU': '3',\n 'PSYDUCK': '12345'\n },\n 'sources': [{\n 'source_type': 'dotenv',\n 'address': {\n 'dotenv_path': '12345.env',\n 'override': False\n }\n }]\n}\n```\n\n## Validation\n\nIt is recommended to use following `BaseSettings` class methods during redefinition `update_settings_from_source` method:\n\n1. `pre_validate` - check that types are configured correctly; check that the values from `choices` and the default pass the type check.\n2. `post_validate` - check if each `Property` is assigned a value.\n\n## Dynamic settings\n\n### Implementing a custom dynamic settings source\n\nExample with storing settings in dict `source`:\n\n```python\nfrom magic_settings import BaseDynamicSettings, Property\n\nsource = {\n 'JIGGLYPUFF': 'pink'\n}\n\nclass BaseDynamicSettingsDict(BaseDynamicSettings):\n async def update_settings_from_source(self):\n super().update_config(**source)\n\n async def update_config(self, **kwargs):\n source.update(kwargs)\n return super().update_config(**kwargs)\n```\n\n### Definition of project`s dynamic settings class\n\n```python\nclass MyDynamicSettings(BaseDynamicSettingsDict):\n JIGGLYPUFF = Property(types=str)\n```\n\n### Dynamic Settings Initialization\n\n```python\nloop = asyncio.get_event_loop()\ndynamic_settings = MyDynamicSettings(loop=loop, update_period=5, task_retries_number=5)\n```\n\n- ***update_period***: time between updating settings from source, in seconds.\n- ***task_retries_number***: the number of attempts to update the settings when an exception occurred before stopping the task.\n\n### Dynamic settings update\n\n#### Updating settings only once\n\n```python\nawait dynamic_settings.update_settings_from_source()\n```\n\n#### Starting the update loop\n\n```python\nawait dynamic_settings.start_update()\n```\n\n#### Stopping the update loop\n\n```python\nawait dynamic_settings.stop_update()\n```\n\n### Writing settings into the source\n\n```python\nawait dynamic_settings.update_config(JIGGLYPUFF='magenta')\n```\n\n### Exceptions\n\n- ***magic_settings.DynamicSettingsSourceError*** - this exception should be selected if the settings source in the class inherited from `BaseDynamicSettings` is unavailable.\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "https://pypi.org/project/magic-settings/", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/shocking-rodents/magic-settings/", "keywords": "parser config environment settings configuration", "license": "MIT", "maintainer": "", "maintainer_email": "", "name": "magic-settings", "package_url": "https://pypi.org/project/magic-settings/", "platform": "", "project_url": "https://pypi.org/project/magic-settings/", "project_urls": { "Download": "https://pypi.org/project/magic-settings/", "Homepage": "https://github.com/shocking-rodents/magic-settings/" }, "release_url": "https://pypi.org/project/magic-settings/1.2.0/", "requires_dist": [ "python-dotenv (~=0.10.2)", "PyYAML (~=5.1) ; extra == 'yaml'" ], "requires_python": "~=3.6", "summary": "Configuration manager for Python applications.", "version": "1.2.0" }, "last_serial": 5667285, "releases": { "1.2.0": [ { "comment_text": "", "digests": { "md5": "a8f3c6a85759ad1dc3dc4a45a72d1293", "sha256": "1c5928b6a51f41b62d8287898b271904ac15115314a0afbdd6a16ca6b7b5e3b7" }, "downloads": -1, "filename": "magic_settings-1.2.0-py3-none-any.whl", "has_sig": false, "md5_digest": "a8f3c6a85759ad1dc3dc4a45a72d1293", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": "~=3.6", "size": 11068, "upload_time": "2019-08-12T16:02:04", "url": "https://files.pythonhosted.org/packages/44/d0/1c2364baf665a737f5d68ae9e7e05df6021caaed4337d0c9ece6b7e5f2b6/magic_settings-1.2.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "5172e046ad5192060de4b80608e5e653", "sha256": "be24793617d9faef25a8b540c3bf1920e2a2587327d1ae84c42ca368405a9363" }, "downloads": -1, "filename": "magic-settings-1.2.0.tar.gz", "has_sig": false, "md5_digest": "5172e046ad5192060de4b80608e5e653", "packagetype": "sdist", "python_version": "source", "requires_python": "~=3.6", "size": 12502, "upload_time": "2019-08-12T16:02:06", "url": "https://files.pythonhosted.org/packages/51/b9/5f570c03cee61c12ae097c1df2a7e3de22070af24674f633a88008c79d2a/magic-settings-1.2.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "a8f3c6a85759ad1dc3dc4a45a72d1293", "sha256": "1c5928b6a51f41b62d8287898b271904ac15115314a0afbdd6a16ca6b7b5e3b7" }, "downloads": -1, "filename": "magic_settings-1.2.0-py3-none-any.whl", "has_sig": false, "md5_digest": "a8f3c6a85759ad1dc3dc4a45a72d1293", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": "~=3.6", "size": 11068, "upload_time": "2019-08-12T16:02:04", "url": "https://files.pythonhosted.org/packages/44/d0/1c2364baf665a737f5d68ae9e7e05df6021caaed4337d0c9ece6b7e5f2b6/magic_settings-1.2.0-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "5172e046ad5192060de4b80608e5e653", "sha256": "be24793617d9faef25a8b540c3bf1920e2a2587327d1ae84c42ca368405a9363" }, "downloads": -1, "filename": "magic-settings-1.2.0.tar.gz", "has_sig": false, "md5_digest": "5172e046ad5192060de4b80608e5e653", "packagetype": "sdist", "python_version": "source", "requires_python": "~=3.6", "size": 12502, "upload_time": "2019-08-12T16:02:06", "url": "https://files.pythonhosted.org/packages/51/b9/5f570c03cee61c12ae097c1df2a7e3de22070af24674f633a88008c79d2a/magic-settings-1.2.0.tar.gz" } ] }