{"id":19286740,"url":"https://github.com/daviskirk/climatecontrol","last_synced_at":"2025-08-30T23:36:53.748Z","repository":{"id":8913697,"uuid":"58873222","full_name":"daviskirk/climatecontrol","owner":"daviskirk","description":"Python library for loading settings and config data from files and environment variables","archived":false,"fork":false,"pushed_at":"2024-04-25T06:14:52.000Z","size":376,"stargazers_count":19,"open_issues_count":1,"forks_count":1,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-04-25T07:29:01.139Z","etag":null,"topics":["12factorapp","app-config","application-settings","cerberus","config","configuration","configuration-files","configuration-management","configuration-parser","dataclasses","environment-configuration","environment-variables","json","pydantic","python","python3","settings","toml","yaml"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/daviskirk.png","metadata":{"files":{"readme":"README.rst","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-05-15T16:53:00.000Z","updated_at":"2024-04-25T07:29:01.140Z","dependencies_parsed_at":"2024-11-09T22:04:05.280Z","dependency_job_id":"bca532a3-38fd-4a88-81a0-986fd3ce66c4","html_url":"https://github.com/daviskirk/climatecontrol","commit_stats":{"total_commits":119,"total_committers":4,"mean_commits":29.75,"dds":0.4201680672268907,"last_synced_commit":"7f0f2cd30b94e991d0ec0f41a2170a7811747605"},"previous_names":[],"tags_count":34,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daviskirk%2Fclimatecontrol","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daviskirk%2Fclimatecontrol/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daviskirk%2Fclimatecontrol/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daviskirk%2Fclimatecontrol/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/daviskirk","download_url":"https://codeload.github.com/daviskirk/climatecontrol/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250167614,"owners_count":21386004,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["12factorapp","app-config","application-settings","cerberus","config","configuration","configuration-files","configuration-management","configuration-parser","dataclasses","environment-configuration","environment-variables","json","pydantic","python","python3","settings","toml","yaml"],"created_at":"2024-11-09T22:03:54.648Z","updated_at":"2025-04-22T03:33:39.403Z","avatar_url":"https://github.com/daviskirk.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"|Build Status| |Coverage Status| |PyPi version| |PyPI license| |PyPI pyversions| |Conda version|\n|Code Style Black|\n\n\n.. image:: https://raw.githubusercontent.com/daviskirk/climatecontrol/logo/climatecontrol-text.svg?sanitize=true\n\n\nCLIMATECONTROL controls your applications settings and configuration\nenvironment. It is a Python library for loading app configurations from files\nand/or namespaced environment variables.\n\nFeatures\n========\n\n* Separation of settings and code\n* Loading from files (`.yaml`, `.json`, `.toml`)\n* Loading multiple files using glob syntax\n* Loading from environment variables, including loading of nested values\n* Freely reference nested configurations via files or environment variables\n* CLI integration\n* Validation using the Validation library of your choice\n* Logging configuration integration\n* Testing integration\n\n\nInstall\n=======\n\n::\n\n    pip install climatecontrol\n\n\n\nUsage\n=====\n\nSet some environment variables in your shell\n\n.. code:: sh\n\n   export CLIMATECONTROL_VALUE1=test1\n   export CLIMATECONTROL_VALUE2=test2\n\nThen use them in your python modules:\n\n.. code:: python\n\n   from climatecontrol import climate\n   print(climate.settings)\n\n   {\n       'value1': 'test1',\n       'value2': 'test2'\n   }\n\nIn case you want to update your settings or your environment variables have\nchanged and you want to reload them, the `update` method will reload your\nsettings:\n\n.. code:: python\n\n   import os\n   os.environ['CLIMATECONTROL_VALUE3'] = 'new_env_data'\n   climate.reload()\n   print(climate.settings)\n\n   {\n       'value1': 'test1',\n       'value2': 'test2',\n       'value3': 'new_env_data'\n   }\n\n\nNow you've noticed that you want more complex configurations and need nested\nsettings. For this situation we can delimit sections using a double underscore:\n\n.. code:: sh\n\n   export CLIMATECONTROL_SECTION1__VALUE1=test1\n   export CLIMATECONTROL_SECTION2__VALUE2=test2\n   export CLIMATECONTROL_SECTION2__VALUE3=test3\n   export CLIMATECONTROL_SECTION2__SUB_SECTION__VALUE4=test4\n\n.. code:: python\n\n   from climatecontrol import climate\n   print(climate.settings)\n\n   {\n       'section1': {\n           'value1': 'test1'\n       },\n       'section2': {\n           'value2': 'test2',\n           'value3': 'test3',\n           'sub_section': {\n               'value4': 'test4'\n           }\n       }\n   }\n\n\nSettings file support\n---------------------\n\nIf you don't want to use an environment variable for every single setting and\nwant to put your settings in a single file instead you can to this as well.\nSettings files can be yaml files (`.yml`/ `.yaml`), json files (`.json`) or toml_ files (`.toml`).\n\n.. code-block:: sh\n\n   export CLIMATECONTROL_SETTINGS_FILE=./my_settings_file.yml\n\n\nThe file could look like this:\n\n.. code-block:: yaml\n\n   # ./climatecontrol_settings.yaml\n   section1:\n     subsection1 = test1\n\n   section2:\n     subsection2: test2\n     subsection3: test3\n\n\nor in toml form:\n\n.. code-block:: sh\n\n   # ./climatecontrol_settings.toml\n   [section1]\n   subsection1 = \"test1\"\n\n   [section2]\n   subsection2 = \"test2\"\n   subsection3 = \"test3\"\n\n\nIn the following documentation examples, yaml files will be used, but any\nexamples will work using the other file syntaxes as well.\n\nSee the `climatecontrol.core.Climate.inferred_settings_files` docstring\nfor further examples of how settings files are loaded and how they can be named.\nAlso note that you can set your own settings files explicitely either by\nsettings an environment variable:\n\n.. code-block:: sh\n\n   export CLIMATECONTROL_SETTINGS_FILE=\"mysettings.yaml, mysettings.toml, override.yml\"\n\nor by adding them in code:\n\n.. code-block:: python\n\n   climate.settings_files.extend([\"mysettings.yaml\", \"mysettings.toml\", \"override.yml\"]\n\n\nAdvanced Features\n-----------------\n\nSetting variables from values saved in files\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nSometimes we don't want to save values in plain text in environment files or in\nthe settings file itself. Instead we have a file that contains the value of the\nsetting we want. A good example for this behaviour are docker secrets_ that\nstore secrets in temporary files.\n\nTo read a variable from a file, simply add a `\"_from_file\"` to the variable\nname and give it the path to the file that contains the variable as a value.\n\nUsing a settings file with the contents (in this case yaml):\n\n.. code-block:: yaml\n\n   section1:\n     subsection1_from_file: /home/myuser/supersecret.txt\n\nor using an environment variable:\n\n.. code-block:: sh\n\n   export CLIMATECONTROL_SECTION1_SUBSECTION1_FROM_FILE=\"/home/myuser/supersecret.txt\"\n\nwill both write the content of the file at `\"/home/myuser/supersecret.txt\"`\ninto the variable `section1 -\u003e subsection1`.\n\n\nSetting variables from values saved in specific environment variables\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nSimilarly, to read a value from an environment variable, add a `\"_from_env\"` to\nthe variable name. For example if we wanted to obtain a value from the variable\n`SPECIFIC_ENV_VAR`:\n\n.. code-block:: sh\n\n   export SPECIFIC_ENV_VAR=\"some value\"\n\nUsing a settings file with the contents (in this case yaml):\n\n.. code-block:: yaml\n\n   section1:\n     subsection1_from_env: SPECIFIC_ENV_VAR\n\nor using an environment variable:\n\n.. code-block:: sh\n\n   export CLIMATECONTROL_SECTION1_SUBSECTION1_FROM_FILE=\"/home/myuser/supersecret.txt\"\n\nwill both write \"some value\" into the variable `section1 -\u003e subsection1`.\n\nSettings variables from serialized content\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n.. code-block:: yaml\n\n   section1_from_json_content: '{\"subsection1\": \"test\", \"subsection2\": 2}'\n   section2_from_toml_content: 'subsection1 = \"test\"\\nsubsection2 = 2\\n'\n   section3_from_yaml_content: 'subsection1: test\\nsubsection2: 2\\n'\n\n\nThe equivilant environment variables are also handled correctly:\n\n.. code-block:: sh\n\n   CLIMATECONTROL_SECTION1_FROM_JSON_CONTENT='{\"subsection1\": \"test\", \"subsection2\": 2}'\n   CLIMATECONTROL_SECTION2_FROM_TOML_CONTENT='subsection1 = \"test\"\\nsubsection2 = 2\\n'\n   CLIMATECONTROL_SECTION3_FROM_YAML_CONTENT='subsection1: test\\nsubsection2: 2\\n'\n\n\nNested settings files\n^^^^^^^^^^^^^^^^^^^^^\n\nIn addition, file variables can also target other settings files directly. To\ndo this, just make sure the target file is has an extension supported by\nclimate control. A simple example is illustrated here. Given a settings file:\n\n.. code-block:: yaml\n\n   value1: \"spam\"\n   section1_from_file: /home/myuser/nestedfile.yaml\n\n\nwhere the content of `/home/myuser/nestedfile.yaml` is:\n\n.. code-block:: yaml\n\n   value2: \"cheese\"\n   subsection:\n     value3: \"parrot\"\n\nwhich would result in a settings structure:\n\n.. code-block:: python\n\n   {\n       \"value1\": \"spam\",\n       \"section1\": {\n           \"value2\": \"cheese\",\n           \"subsection\": {\n               \"value3\": \"parrot\"\n           }\n       }\n   }\n\nYou can also expand the settings at the root of the document by using only\n\"_from_file\" as the key:\n\n.. code-block:: yaml\n\n   value1: \"spam\"\n   _from_file: /home/myuser/nestedfile.yaml\n\n.. code-block:: python\n\n   {\n       \"value1\": \"spam\",\n       \"value2\": \"cheese\",\n       \"subsection\": {\n           \"value3\": \"parrot\"\n       }\n   }\n\n\nExtensions\n----------\n\nWhile the default `climate` object is great for most uses, perhaps you already\nhave a settings object style that you like or use a specific library for\nvalidation.  In these cases, CLIMATECONTROL can be extended to use these\nlibraries.\n\nDataclasses\n^^^^^^^^^^^\n\n\u003e\u003e\u003e from climatecontrol.ext.dataclasses import Climate\n\u003e\u003e\u003e from dataclasses import dataclass, field\n\u003e\u003e\u003e\n\u003e\u003e\u003e @dataclass\n... class SettingsSubSchema:\n...     d: int = 4\n...\n\u003e\u003e\u003e @dataclass\n... class SettingsSchema:\n...     a: str = 'test'\n...     b: bool = False\n...     c: SettingsSubSchema = field(default_factory=SettingsSubSchema)\n...\n\u003e\u003e\u003e climate = Climate(dataclass_cls=SettingsSchema)\n\u003e\u003e\u003e # defaults are initialized automatically:\n\u003e\u003e\u003e climate.settings.a\n'test'\n\u003e\u003e\u003e climate.settings.c.d\n4\n\u003e\u003e\u003e # Types are checked if given\n\u003e\u003e\u003e climate.update({'c': {'d': 'boom!'}})\nTraceback (most recent call last):\n    ...\ndacite.exceptions.WrongTypeError: wrong type for field \"c.d\" - should be \"int\" instead of \"str\"\n\n\nPydantic\n^^^^^^^^\n\nPydantic is a great data validation library:\nhttps://github.com/samuelcolvin/pydantic and climatecontrol also provides a\nsimple extension to use pydantic models directly (typing functionality mentioned\nabove works here as well).\n\n\u003e\u003e\u003e from climatecontrol.ext.pydantic import Climate\n\u003e\u003e\u003e\n\u003e\u003e\u003e class SettingsSubSchema(BaseModel):\n...     d: int = 4\n...\n\u003e\u003e\u003e class SettingsSchema(BaseModel):\n...     a: str = 'test'\n...     b: bool = False\n...     c: SettingsSubSchema = SettingsSubSchema()\n...\n\u003e\u003e\u003e climate = Climate(model=SettingsSchema)\n\u003e\u003e\u003e # defaults are initialized automatically:\n\u003e\u003e\u003e climate.settings.a\n'test'\n\u003e\u003e\u003e climate.settings.c.d\n4\n\u003e\u003e\u003e # Types are checked if given\n\u003e\u003e\u003e climate.update({'c': {'d': 'boom!'}})\nTraceback (most recent call last):\n    ...\npydantic.error_wrappers.ValidationError: 1 validation error for SettingsSchema\nc -\u003e d\n    value is not a valid integer (type=type_error.integer)\n\n\nIntegrations\n------------\n\nCommand line support using click\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nThe click_ library is a great tool for creating command line applications. If\nyou don't want to have to use an environment to set your configuration file.\nWrite your command line application like this:\n\n.. code-block:: python\n\n   import click\n\n   @click.command()\n   @climate.click_settings_file_option()\n   def cli():\n      print(climate.settings)\n\nsave it to a file like \"cli.py\" and then call it after installing click:\n\n.. code-block:: sh\n\n   pip install click\n   python cli.py --settings ./my_settings_file.toml\n\nwhithout needing to set any env vars.\n\nMultiple files are supported. They will be automatically recursively merged\nwith the last file overriting any overlapping keys of the first file.\n\n.. code-block:: sh\n\n   pip install click\n   python cli.py --settings ./my_settings_file.toml  --settings ./my_settings_file.yaml\n\n\nLogging\n^^^^^^^\n\nIf you have a \"logging\" section in your settings files, you can configure\npython standard library logging using that section directly:\n\n.. code:: yaml\n\n   logging:\n     formatters:\n       default:\n         format': \"%(levelname)s \u003e %(message)s\"\n     root:\n       level: DEBUG\n\n\n.. code:: python\n\n   import logging\n   from climatecontrol import climate\n\n   climate.setup_logging()\n   logging.debug('test')\n   # outputs: DEBUG \u003e test\n\n\nTesting\n-------\n\nWhen testing your application, different behaviours often depend on settings\ntaking on different values. Assuming that you are using a single `Settings`\nobject accross multiple functions or modules, handling these settings changes\nin tests can be tricky.\n\nThe settings object provides a simple method for modifying your settings object\ntemporarily:\n\n.. code-block:: python\n\n   climate.update({'a': 1})\n   # Enter a temporary changes context block:\n   with climate.temporary_changes():\n       climate.update({'a': 1})\n       # Inside the context, the settings can be modified and used as you choose\n       print(climate['a'])  # outputs: 2\n   # After the context exits the settings map\n   print(climate['a'])  # outputs: 1\n\n\nContributing\n============\n\nSee: `CONTRIBUTING.md \u003c./CONTRIBUTING.md\u003e`__\n\n\n.. |Build Status| image:: https://img.shields.io/github/workflow/status/daviskirk/climatecontrol/ci?style=flat-square\n   :target: https://github.com/daviskirk/climatecontrol\n.. |Coverage Status| image:: https://img.shields.io/codecov/c/github/daviskirk/climatecontrol/master?style=flat-square\n   :target: https://codecov.io/gh/daviskirk/climatecontrol\n.. |PyPI version| image:: https://img.shields.io/pypi/v/climatecontrol?style=flat-square\n   :target: https://pypi.python.org/pypi/climatecontrol/\n.. |PyPI license| image:: https://img.shields.io/pypi/l/climatecontrol?style=flat-square\n   :target: https://pypi.python.org/pypi/climatecontrol/\n.. |PyPI pyversions| image:: https://img.shields.io/pypi/pyversions/climatecontrol?style=flat-square\n   :target: https://pypi.python.org/pypi/climatecontrol/\n.. |Conda version| image:: https://img.shields.io/conda/vn/conda-forge/climatecontrol?style=flat-square\n   :target: https://anaconda.org/conda-forge/climatecontrol\n.. |Code Style Black| image:: https://img.shields.io/badge/code%20style-black-000000.svg?style=flat-square\n   :target: https://github.com/psf/black\n.. _click: http://click.pocoo.org/\n.. _toml: https://github.com/toml-lang/toml\n.. _secrets: https://docs.docker.com/engine/swarm/secrets\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaviskirk%2Fclimatecontrol","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdaviskirk%2Fclimatecontrol","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaviskirk%2Fclimatecontrol/lists"}