Skip to content

Commit 79633dd

Browse files
feat(config): add explicit validation for required configuration fields (#2078)
* feat(config): add explicit validation for required configuration fields * feat(config): add explicit validation for required configuration fields * fix(config): always validate configuration regardless of registration * config: clarify validation semantics and document future migration plan * fix: pylint error --------- Co-authored-by: Linlang <Lv.Linlang@hotmail.com>
1 parent d5379c5 commit 79633dd

2 files changed

Lines changed: 39 additions & 2 deletions

File tree

qlib/config.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,33 @@ class QSettings(BaseSettings):
6363

6464
class Config:
6565
def __init__(self, default_conf):
66-
self.__dict__["_default_config"] = copy.deepcopy(default_conf) # avoiding conflicts with __getattr__
66+
self.__dict__["_default_config"] = copy.deepcopy(default_conf)
6767
self.reset()
6868

69+
# TODO: This validation logic is a temporary solution.
70+
# The long-term goal is to migrate Qlib Config to a typed configuration
71+
# system based on pydantic.BaseModel, with explicit schema and field validation.
72+
def validate(self):
73+
errors = []
74+
75+
if not self.get("provider_uri"):
76+
errors.append("provider_uri must be set (e.g. ~/.qlib/qlib_data or a valid path)")
77+
78+
if not self.get("region"):
79+
errors.append("region must be specified (e.g. 'cn', 'us')")
80+
81+
if errors:
82+
raise ValueError(
83+
"Invalid Qlib configuration (note: the global config has already been updated):\n"
84+
"Invalid Qlib configuration:\n- " + "\n- ".join(errors)
85+
)
86+
6987
def __getitem__(self, key):
7088
return self.__dict__["_config"][key]
7189

7290
def __getattr__(self, attr):
7391
if attr in self.__dict__["_config"]:
7492
return self.__dict__["_config"][attr]
75-
7693
raise AttributeError(f"No such `{attr}` in self._config")
7794

7895
def get(self, key, default=None):
@@ -116,8 +133,11 @@ def register_from_C(config, skip_register=True):
116133
return
117134

118135
C.set_conf_from_C(config)
136+
C.validate()
137+
119138
if C.logging_config:
120139
set_log_with_config(C.logging_config)
140+
121141
C.register()
122142

123143

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import pytest
2+
3+
from qlib.config import Config
4+
5+
6+
def test_missing_provider_uri_raises():
7+
default_conf = {
8+
"provider_uri": None,
9+
"region": "us",
10+
}
11+
12+
cfg = Config(default_conf)
13+
14+
with pytest.raises(ValueError) as exc:
15+
cfg.validate()
16+
17+
assert "provider_uri must be set" in str(exc.value)

0 commit comments

Comments
 (0)