Welcome to ConfigAlchemy’s documentation!¶
ConfigAlchemy¶
The Settings and Configuration on ideal practices for cloud-native app development and package building.
- Free software: MIT license
- Documentation: https://configalchemy.readthedocs.io.
Example¶
from configalchemy import BaseConfig
class DefaultConfig(BaseConfig):
NAME = "test"
config = DefaultConfig()
config.NAME
>>> 'test'
Features¶
Base on The Twelve-Factor App Configuration.
Configurable dynamic configurator
Configuration-Oriented Development
- Define default config value and its type which is used in your project
- Use class to support inheritance to explicitly define configurable config
Override config value from multiple source with priority supported
- Callable function return value
- File (default: json)
- Environment Variables
Proper Typecast before overriding
Generic Config Type Support by custom typecast
Lazy and Proxy Object Support.
Extension
- Full Apollo - A reliable configuration management system Features Support
TODO¶
- IOC - Injector, Singleton
Installation¶
Stable release¶
To install ConfigAlchemy, run this command in your terminal:
$ pipenv install configalchemy
✨🍰✨
This is the preferred method to install configalchemy, as it will always install the most recent stable release.
If you don’t have pip installed, this Python installation guide can guide you through the process.
From sources¶
The sources for configalchemy can be downloaded from the Github repo.
You can either clone the public repository:
$ git clone git://github.com/GuangTianLi/configalchemy
Or download the tarball:
$ curl -OL https://github.com/GuangTianLi/configalchemy/tarball/master
Once you have a copy of the source, you can install it with:
$ python setup.py install
Or using pipenv install straightly:
$ pipenv install -e git+https://github.com/GuangTianLi/configalchemy#egg=configalchemy
Usage¶
To use ConfigAlchemy in a project.
Note
the configuration key should be uppercase.
from configalchemy import BaseConfig
class DefaultConfig(BaseConfig):
TEST = "test"
config = DefaultConfig()
How to Use the Config¶
Inherit from BaseConfig
to dynamic configure your configuration.
Note
Default Priority Environment Variables > File > Function Return Value > Default Value
Define the Default Config Class¶
Inherit from BaseConfig
to define your config value explicitly:
Note
the config key should be uppercase.
from configalchemy import BaseConfig
class DefaultConfig(BaseConfig):
DEBUG = False
TESTING = False
DATABASE_URI = 'sqlite:///:memory:'
class ProductionConfig(DefaultConfig):
DATABASE_URI = 'mysql://user@localhost/foo'
class DevelopmentConfig(DefaultConfig):
DEBUG = True
class TestingConfig(DefaultConfig):
TESTING = True
config = DevelopmentConfig()
>>> config.DEBUG
True
Enable Environment Variables¶
Define the CONFIGALCHEMY_ENV_PREFIX to enable access config from environment variables:
Note
The BaseConfig
will try to load config value with f’{CONFIGALCHEMY_ENV_PREFIX}{key}’.
from configalchemy import BaseConfig
import os
os.environ['TEST_NAME'] = 'env'
class DefaultConfig(BaseConfig):
CONFIGALCHEMY_ENV_PREFIX = 'TEST_'
NAME = 'base'
config = DefaultConfig()
>>> config['NAME']
env
Enable Configure from File¶
Define CONFIGALCHEMY_CONFIG_FILE to enable access config from config file:
Note
Support JSON file
from configalchemy import BaseConfig
class DefaultConfig(BaseConfig):
CONFIGALCHEMY_CONFIG_FILE = 'test.json' #: etc: {'NAME': 'json'}
NAME = 'base'
config = DefaultConfig()
>>> config['NAME']
json
Enable Configure with function return value¶
Define CONFIGALCHEMY_ENABLE_FUNCTION to configure from function return value (support coroutine):
from configalchemy import BaseConfig, ConfigType
class SyncDefaultConfig(BaseConfig):
CONFIGALCHEMY_ENABLE_FUNCTION = True
TYPE = "base"
def configuration_function(self) -> ConfigType:
return {"TYPE": "sync"}
class AsyncDefaultConfig(BaseConfig):
CONFIGALCHEMY_ENABLE_FUNCTION = True
TYPE = "base"
async def async_function(self) -> ConfigType:
return {"TYPE": "async"}
sync_config = SyncDefaultConfig()
async_config = AsyncDefaultConfig()
>>> sync_config['TYPE']
sync
>>> async_config['NAME']
async
Auto Validation and Dynamic typecast¶
When new value is assigned to config, the value will be validated and typecast if possible and the process bases on default value or type annotations.
from typing import Optional
from configalchemy import BaseConfig
class DefaultConfig(BaseConfig):
id = 1
name = "Tony"
limit: Optional[int] = None
config = DefaultConfig()
config.id = '10'
print(config.id) # 10
config.limit = 10
print(config.limit) # 10
Json Type¶
You can use Json data type - configalchemy will use json.loads to typecast.
import json
from configalchemy import BaseConfig
from configalchemy.types import Json
class DefaultConfig(BaseConfig):
TEST_LIST: Json[list] = ["str"]
TEST_DICT: Json[dict] = {"name": "default"}
config = DefaultConfig()
config.TEST_LIST = json.dumps(["test"])
config.TEST_LIST
>>> ["test"]
config.TEST_DICT = json.dumps({"name": "test"})
config.TEST_DICT
>>> {"name": "test"}
Advanced Usage¶
Singleton¶
Nested Config for Modular Purpose¶
class NestedConfig(BaseConfig):
NAME = "nested"
ADDRESS = "default"
class DefaultConfig(BaseConfig):
NESTED_CONFIG = NestedConfig()
config = DefaultConfig()
config.update(NESTED_CONFIG={"NAME": "updated"})
config["NESTED_CONFIG.ADDRESS"] = "address"
>>> config.NESTED_CONFIG.NAME
updated
>>> config.NESTED_CONFIG.ADDRESS
address
Lazy¶
Use lazy to turn any callable into a lazy evaluated callable. Results are memoized; the function is evaluated on first access.
from configalchemy.lazy import lazy, reset_lazy
def get_name():
print("evaluating")
return "World"
lazy_name = lazy(get_name)
>>> print(f"Hello {lazy_name}")
evaluating
Hello World
>>> print(f"Hello {lazy_name}")
Hello World
>>> reset_lazy(lazy_name)
>>> print(f"Hello {lazy_name}")
evaluating
Hello World
Proxy¶
Use proxy to turn any callable into a lazy evaluated callable. Results are not memoized; the function is evaluated on every access.
from configalchemy.lazy import proxy
def get_name():
print("evaluating")
return "World"
lazy_name = proxy(get_name)
>>> print(f"Hello {lazy_name}")
evaluating
Hello World
>>> print(f"Hello {lazy_name}")
evaluating
Hello World
Pool¶
Use Pool to turn any callable into a pool. Result will be return by a context manager. the function is evaluated on result first access.
from configalchemy.lazy import Pool
def connect():
print("connecting")
return socket
connect_pool = Pool(connect)
>>> with connect_pool as connect:
... connect.send("")
connecting
0
>>> with connect_pool as connect:
... connect.send("")
0
Access config from Apollo¶
Apollo - A reliable configuration management system
You can inherit from ApolloBaseConfig
to access config from Apollo.
from configalchemy.contrib.apollo import ApolloBaseConfig
class DefaultConfig(ApolloBaseConfig):
#: apollo
ENABLE_LONG_POLL = True
APOLLO_SERVER_URL = ""
APOLLO_APP_ID = ""
APOLLO_CLUSTER = "default"
APOLLO_NAMESPACE = "application"
configalchemy¶
configalchemy package¶
BaseConfig module¶
-
class
configalchemy.
BaseConfig
[source]¶ Initialize the
BaseConfig
with the Priority:configure from env > configure from local file > configure from function > default configuration
Example of class-based configuration:
class DefaultConfig(BaseConfig): TEST = "test" config = DefaultConfig()
-
CONFIGALCHEMY_ENABLE_FUNCTION
= False¶ set to
True
if you want to override config from function return value.
-
CONFIGALCHEMY_ENV_PREFIX
= ''¶ The prefix to construct the full environment variable key to access overrode config.
-
CONFIGALCHEMY_LOAD_FILE_SILENT
= False¶ set to
True
if you want silent failure for missing files.
-
CONFIGALCHEMY_ROOT_PATH
= ''¶ The the filename of the JSON file. This can either be an absolute filename or a filename relative to the CONFIGALCHEMY_ROOT_PATH.
-
CONFIGALCHEMY_SETITEM_PRIORITY
= 99¶ The priority of config[‘TEST’] = value, config.TEST = value and config.update(TEST=value)
-
access_config_from_coroutine
(priority: int) → bool[source]¶ Async updates the values in the config from the configuration_function.
-
Contributing¶
Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given.
You can contribute in many ways:
Types of Contributions¶
Report Bugs¶
Report bugs at https://github.com/GuangTianLi/configalchemy/issues.
If you are reporting a bug, please include:
- Your operating system name and version.
- Any details about your local setup that might be helpful in troubleshooting.
- Detailed steps to reproduce the bug.
Fix Bugs¶
Look through the GitHub issues for bugs. Anything tagged with “bug” and “help wanted” is open to whoever wants to implement it.
Implement Features¶
Look through the GitHub issues for features. Anything tagged with “enhancement” and “help wanted” is open to whoever wants to implement it.
Write Documentation¶
configalchemy could always use more documentation, whether as part of the official configalchemy docs, in docstrings, or even on the web in blog posts, articles, and such.
Submit Feedback¶
The best way to send feedback is to file an issue at https://github.com/GuangTianLi/configalchemy/issues.
If you are proposing a feature:
- Explain in detail how it would work.
- Keep the scope as narrow as possible, to make it easier to implement.
- Remember that this is a volunteer-driven project, and that contributions are welcome :)
Get Started!¶
Ready to contribute? Here’s how to set up configalchemy for local development.
Fork the configalchemy repo on GitHub.
Clone your fork locally:
$ git clone git@github.com:your_name_here/configalchemy.git
Install your local copy into a virtualenv. Assuming you have Pipenv installed, this is how you set up your fork for local development:
$ cd configalchemy/ $ make init $ pipenv shell
Create a branch for local development:
$ git checkout -b name-of-your-bugfix-or-feature
Now you can make your changes locally.
When you’re done making changes, check that your changes pass the tests.:
$ make lint $ make test
Commit your changes and push your branch to GitHub:
$ git add . $ git commit -m ":tag: [#id] Your detailed description of your changes." $ git push origin name-of-your-bugfix-or-feature
Submit a pull request through the GitHub website.
Pull Request Guidelines¶
Before you submit a pull request, check that it meets these guidelines:
- The pull request should include tests.
- If the pull request adds functionality, the docs should be updated. Put your new functionality into a function with a docstring, and add the feature to the list in README.rst.
- The pull request should work for Python 3.6+. Check https://github.com/GuangTianLi/configalchemy/actions and make sure that the tests pass for all supported Python versions.
Credits¶
Development Lead¶
- GuangTian Li <guangtian_li@qq.com>
Contributors¶
None yet. Why not be the first?
History¶
0.5.* (2020-12)¶
- Support SingletonMetaClass
- Remove unpack feature in BaseConfig by removing Mapping Class
- Support nested config for large modular purpose
- Support local object
- Support generic pool object
- Support Dot Notation to update BaseConfig object
0.4.* (2020-06)¶
- Refatory configure function
- Support property configuration
0.3.* (2020-03)¶
- Add proxy and lazy module.
- Add find_caller to trace source of config value
- Refactory ConfigMeta data structure
0.2.* (2019-08)¶
- Change global variable to weak reference
- Remove Lock (Prepare to implement optimistic raw lock if necessary)
- Improve Priority Data Structure
- Improve Field Validation
- Use OOP to define call function
- Properer validation and typecast
- Improve Type Annotations
- Import JSON type
0.1.0 (2019-08-01)¶
- Init Project.