diff --git a/.travis.yml b/.travis.yml index 9b8c72a..ad9a2f4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,8 @@ python: - '3.7' - '3.8' install: +- pip install -U importlib-metadata +- pip list - pip install -r test-requirements.txt -U script: - echo $(git log -1 --pretty=%B) > latest_commit_msg; pre-commit run --hook-stage commit-msg commitlint --commit-msg-filename latest_commit_msg; rm -rf latest_commit_msg diff --git a/CHANGELOG.md b/CHANGELOG.md index b869fcd..6ac9ed3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ## [Unreleased] ### Added - Added `post_ready` signal that will be sended after sea app is ready +- Added reading default config from environment variables, will overwrite the config file ## [2.3.2] - 2022-01-12 ### Changed diff --git a/docs/configuration.md b/docs/configuration.md index 0d3aacd..fd83af3 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -7,6 +7,8 @@ 程序运行的时候,会根据 `SEA_ENV` 环境变量的值加载同名的 `configs` 子模块。需要注意: **配置名称不得包含小写字母,否则会被忽略。** +**和配置文件中配置项同名的环境变量优先级更高,会覆盖配置文件中的值,例如环境变量`GRPC_WORKERS`将覆盖配置文件中的GRPC_WORKERS。** + ## 基本配置 `DEBUG` diff --git a/sea/__init__.py b/sea/__init__.py index e57cdc6..4cad558 100644 --- a/sea/__init__.py +++ b/sea/__init__.py @@ -28,6 +28,8 @@ def create_app(root_path=None): app_class = import_string("app:App") _app = app_class(root_path, env=env) _app.config.from_object(config) + # only filter default configurations + _app.config.load_config_from_env() _app.load_middlewares() _app.load_extensions_in_module(import_string("app.extensions")) diff --git a/sea/config.py b/sea/config.py index de4dfb2..6f6d458 100644 --- a/sea/config.py +++ b/sea/config.py @@ -1,4 +1,8 @@ +import numbers +import os + from sea.datatypes import ConstantsObject +from sea.utils import strtobool class ConfigAttribute: @@ -45,5 +49,23 @@ def get_namespace(self, namespace, lowercase=True, trim_namespace=True): rv[key] = v return ConstantsObject(rv) + def load_config_from_env(self): + """ + read environment variables and overwrite same name key's values. + `True` will be converted to python's bool `True`. + `1` will be converted to python's int `1`. + """ + keys = self.keys() + + for k in keys: + env_value = os.getenv(k) + if not env_value: + continue + value_type = type(self[k]) + if value_type is bool: + self[k] = strtobool(env_value) + elif isinstance(env_value, (str, numbers.Number)): + self[k] = value_type(env_value) + def __repr__(self): return '<%s %s>' % (self.__class__.__name__, dict.__repr__(self)) diff --git a/sea/utils.py b/sea/utils.py index 28c96df..940e04b 100644 --- a/sea/utils.py +++ b/sea/utils.py @@ -67,3 +67,19 @@ def logger_has_level_handler(logger): current = current.parent return False + + +def strtobool(val): + """Convert a string representation of truth to true (1) or false (0). + + True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values + are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if + 'val' is anything else. + """ + val = val.lower() + if val in ('y', 'yes', 't', 'true', 'on', '1'): + return True + elif val in ('n', 'no', 'f', 'false', 'off', '0'): + return False + else: + raise ValueError("invalid truth value %r" % (val,)) diff --git a/tests/test_app.py b/tests/test_app.py index 3ecefc6..6c47ded 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -20,6 +20,10 @@ def test_baseapp(caplog): from configs import testing _app.config.from_object(testing) + assert _app.config["PORT"] == 4000 + os.environ["PORT"] = "4001" + _app.config.load_config_from_env() + assert _app.config["PORT"] == 4001 assert _app.debug assert _app.testing _app.load_middlewares() diff --git a/tests/test_config.py b/tests/test_config.py index a3d229f..d5e6acd 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,4 +1,5 @@ import json +import os from sea.config import ConfigAttribute, Config @@ -29,6 +30,26 @@ class Test(Base): assert '