Skip to content
This repository was archived by the owner on Sep 30, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1 +1 @@
ONION_CONFIG_EXTRA_DIR="./extra_dir"
ONION_CONFIG_EXTRA_DIR="./extra_configs"
3 changes: 1 addition & 2 deletions .github/workflows/2.build-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@ jobs:
python -m pip install -U pip
python -m pip install -r ./requirements.test.txt
- name: Test with pytest
run: python -m pytest -sv
# run: python -m pytest -sv -o log_cli=true
run: ./scripts/test.sh

build_publish:
needs: test
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ Load order:
You can use the following environment variables inside [**`.env.example`**](https://github.com/bybatkhuu/mod.python-config/blob/main/.env.example) file:

```sh
ONION_CONFIG_EXTRA_DIR="./extra_dir"
ONION_CONFIG_EXTRA_DIR="./extra_configs"
```

## Documentation
Expand Down
4 changes: 2 additions & 2 deletions docs/scripts/4.test.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ To execute the test script, simply run the following command in the terminal:

---

### References
## References

- <https://docs.pytest.org>
- <https://docs.pytest.org>
4 changes: 2 additions & 2 deletions docs/scripts/6.build.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@ This script is particularly beneficial for developers, streamlining the build, t

---

### References
## References

- <https://https://packaging.python.org/en/latest/tutorials/packaging-projects>
- <https://https://packaging.python.org/en/latest/tutorials/packaging-projects>
32 changes: 17 additions & 15 deletions onion_config/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,15 @@ def __init__(
Args:
config_schema (Union[Type[BaseConfig],
Type[BaseSettings],
Type[BaseModel]] , optional): Main config schema class to load and validate configs. Defaults to `BaseConfig`.
configs_dirs (Union[List[str], str] , optional): Main configs directories as <list> or <str> to load all config files. Defaults to `ConfigLoader._CONFIGS_DIR`.
env_file_paths (Union[List[str], str] , optional): Dotenv file paths as <list> or <str> to load. Defaults to `ConfigLoader._ENV_FILE_PATH`.
required_envs (List[str] , optional): Required environment variables to check. Defaults to [].
pre_load_hook (function , optional): Custom pre-load method, this method will executed before validating `config`. Defaults to `ConfigLoader._PRE_LOAD_HOOK`.
extra_dir (Union[str, None] , optional): Extra configs directory to load extra config files. Defaults to None.
config_data (dict , optional): Base config data as <dict> before everything. Defaults to {}.
quiet (bool , optional): If False, will show warning messages. Defaults to True.
auto_load (bool , optional): Auto load configs on init or not. Defaults to False.
Type[BaseModel] ], optional): Main config schema class to load and validate configs. Defaults to `BaseConfig`.
configs_dirs (Union[List[str], str] , optional): Main configs directories as <list> or <str> to load all config files. Defaults to `ConfigLoader._CONFIGS_DIR`.
env_file_paths (Union[List[str], str] , optional): Dotenv file paths as <list> or <str> to load. Defaults to `ConfigLoader._ENV_FILE_PATH`.
required_envs (List[str] , optional): Required environment variables to check. Defaults to [].
pre_load_hook (function , optional): Custom pre-load method, this method will executed before validating `config`. Defaults to `ConfigLoader._PRE_LOAD_HOOK`.
extra_dir (Union[str, None] , optional): Extra configs directory to load extra config files. Defaults to None.
config_data (dict , optional): Base config data as <dict> before everything. Defaults to {}.
quiet (bool , optional): If False, will show warning messages. Defaults to True.
auto_load (bool , optional): Auto load configs on init or not. Defaults to False.
"""

self.config_schema = config_schema
Expand All @@ -105,7 +105,6 @@ def __init__(
if auto_load:
self.load()

@validate_call
def load(self) -> Union[BaseConfig, BaseSettings, BaseModel]:
"""Load and validate every configs into `config`.
Load order:
Expand Down Expand Up @@ -169,7 +168,7 @@ def _load_dotenv_file(self, env_file_path: str):
env_file_path = os.path.join(os.getcwd(), env_file_path)

if os.path.isfile(env_file_path):
load_dotenv(dotenv_path=env_file_path, override=True, encoding="utf8")
load_dotenv(dotenv_path=env_file_path, override=True, encoding="utf-8")
else:
if self.quiet:
logger.debug(f"'{env_file_path}' file is not exist!")
Expand Down Expand Up @@ -242,7 +241,7 @@ def _load_yaml_file(self, file_path: str):

if os.path.isfile(file_path):
try:
with open(file_path, "r", encoding="utf8") as _file:
with open(file_path, "r", encoding="utf-8") as _file:
_new_config_dict = yaml.safe_load(_file) or {}
self.config_data = deep_merge(self.config_data, _new_config_dict)
except Exception:
Expand All @@ -265,7 +264,7 @@ def _load_json_file(self, file_path: str):

if os.path.isfile(file_path):
try:
with open(file_path, "r", encoding="utf8") as _file:
with open(file_path, "r", encoding="utf-8") as _file:
_new_config_dict = json.load(_file) or {}
self.config_data = deep_merge(self.config_data, _new_config_dict)
except Exception:
Expand All @@ -290,7 +289,7 @@ def _load_json_file(self, file_path: str):
# try:
# import toml

# with open(file_path, "r", encoding="utf8") as _file:
# with open(file_path, "r", encoding="utf-8") as _file:
# _new_config_dict = toml.load(_file) or {}
# self.config_data = deep_merge(self.config_data, _new_config_dict)
# except Exception:
Expand Down Expand Up @@ -331,7 +330,7 @@ def config(self, config: Union[BaseConfig, BaseSettings, BaseModel]):
f"`config` attribute type {type(config)} is invalid, must be a <class 'BaseConfig'> or `pydantic` <class 'BaseSettings'> or <class 'BaseModel'>."
)

self.__config = config
self.__config = copy.deepcopy(config)

## config ##

Expand Down Expand Up @@ -451,6 +450,9 @@ def extra_dir(self, extra_dir: str):
if extra_dir == "":
raise ValueError("The `extra_dir` attribute value is empty!")

if not os.path.isabs(extra_dir):
extra_dir = os.path.join(os.getcwd(), extra_dir)

self.__extra_dir = extra_dir

## extra_dir ##
Expand Down
28 changes: 14 additions & 14 deletions tests/test_onion_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def test_init(config_loader):
assert config_loader.config_data == {}
assert config_loader.config == None

logger.info("Done: Initialization of 'ConfigLoader'.")
logger.info("Done: Initialization of 'ConfigLoader'.\n")


def test_load(config_loader):
Expand Down Expand Up @@ -125,7 +125,7 @@ def test_config_load(
assert isinstance(_config, config_schema)
assert _config.model_dump() == expected

logger.info("Done: Main config cases.")
logger.info("Done: Main config cases.\n")


@pytest.mark.parametrize(
Expand All @@ -152,7 +152,7 @@ def test_load_dotenv_files(tmp_path, config_loader, content, expected):
assert config_loader.env_file_paths == [_tmp_env_path]
assert os.getenv(_env_var) == expected

logger.info("Done: '_load_dotenv_files' method.")
logger.info("Done: '_load_dotenv_files' method.\n")


def test_check_required_envs(config_loader):
Expand All @@ -172,7 +172,7 @@ def test_check_required_envs(config_loader):
config_loader._check_required_envs()
assert os.getenv(_none_existent_env_var) == None

logger.info("Done: '_check_required_envs' method.")
logger.info("Done: '_check_required_envs' method.\n")


def test_load_configs_dirs(config_loader, configs_dir):
Expand All @@ -188,7 +188,7 @@ def test_load_configs_dirs(config_loader, configs_dir):
assert config_loader.config_data["yaml_test"] == True
assert config_loader.config_data == _expected

logger.info("Done: '_load_configs_dirs' method.")
logger.info("Done: '_load_configs_dirs' method.\n")


def test_load_extra_dir(tmp_path, config_loader, configs_dir):
Expand Down Expand Up @@ -218,7 +218,7 @@ def test_load_extra_dir(tmp_path, config_loader, configs_dir):
assert config_loader.config_data["extra_test"] == "extra_value"
assert config_loader.config_data == _expected

logger.info("Done: '_load_extra_dir' method.")
logger.info("Done: '_load_extra_dir' method.\n")


def test_config(config_loader):
Expand All @@ -245,7 +245,7 @@ class _ConfigSchema(BaseConfig):
config_loader.config = False
config_loader.config = None

logger.info("Done: 'config' property.")
logger.info("Done: 'config' property.\n")


def test_config_schema(config_loader):
Expand All @@ -263,7 +263,7 @@ class _ConfigSchema(BaseConfig):
config_loader.config_schema = False
config_loader.config_schema = None

logger.info("Done: 'config_schema' property.")
logger.info("Done: 'config_schema' property.\n")


def test_config_data(config_loader):
Expand All @@ -284,7 +284,7 @@ def test_config_data(config_loader):
config_loader.config_data = False
config_loader.config_data = None

logger.info("Done: 'config_data' property.")
logger.info("Done: 'config_data' property.\n")


def test_configs_dirs(config_loader):
Expand All @@ -301,7 +301,7 @@ def test_configs_dirs(config_loader):
with pytest.raises(ValueError):
config_loader.configs_dirs = ""

logger.info("Done: 'configs_dirs' property.")
logger.info("Done: 'configs_dirs' property.\n")


def test_extra_dir(config_loader):
Expand All @@ -318,7 +318,7 @@ def test_extra_dir(config_loader):
with pytest.raises(ValueError):
config_loader.extra_dir = ""

logger.info("Done: 'extra_dir' property.")
logger.info("Done: 'extra_dir' property.\n")


def test_env_file_paths(config_loader):
Expand All @@ -335,7 +335,7 @@ def test_env_file_paths(config_loader):
with pytest.raises(ValueError):
config_loader.env_file_paths = ""

logger.info("Done: 'env_file_paths' property.")
logger.info("Done: 'env_file_paths' property.\n")


def test_required_envs(config_loader):
Expand All @@ -356,7 +356,7 @@ def test_required_envs(config_loader):
with pytest.raises(ValueError):
config_loader.required_envs = ["TEST_ENV_VAR", 1, None]

logger.info("Done: 'required_envs' property.")
logger.info("Done: 'required_envs' property.\n")


def test_pre_load_hook(config_loader):
Expand All @@ -377,4 +377,4 @@ def _pre_load_hook(config_data):
config_loader.pre_load_hook = False
config_loader.pre_load_hook = None

logger.info("Done: 'pre_load_hook' property.")
logger.info("Done: 'pre_load_hook' property.\n")