Skip to content

Commit 43f0b73

Browse files
Add docker dependency
1 parent de52dac commit 43f0b73

File tree

3 files changed

+42
-8
lines changed

3 files changed

+42
-8
lines changed

pixi.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,14 @@ default = { solve-group = "default" }
178178
dev = { features = ["dev"], solve-group = "default" }
179179
test = { features = ["test"], solve-group = "default" }
180180

181+
[tool.pixi.feature.test.pypi-dependencies]
182+
pytest = "*"
183+
pytest-cov = "*"
184+
pytest-env = "*"
185+
docker = "*"
186+
requests = "*"
187+
certifi = "*"
188+
181189
[tool.pixi.tasks]
182190

183191
[tool.pixi.dependencies]

src/datajoint/settings.py

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
from pathlib import Path
3636
from typing import Any, Iterator, Literal
3737

38-
from pydantic import Field, SecretStr, field_validator
38+
from pydantic import Field, SecretStr, field_validator, model_validator
3939
from pydantic_settings import BaseSettings, SettingsConfigDict
4040

4141
from .errors import DataJointError
@@ -288,6 +288,13 @@ class Config(BaseSettings):
288288
_config_path: Path | None = None
289289
_secrets_dir: Path | None = None
290290

291+
@model_validator(mode="after")
292+
def _init_dynamic(self):
293+
"""Initialize dynamic settings storage after model validation."""
294+
if not hasattr(self, "_dynamic"):
295+
object.__setattr__(self, "_dynamic", {})
296+
return self
297+
291298
@field_validator("loglevel", mode="after")
292299
@classmethod
293300
def set_logger_level(cls, v: str) -> str:
@@ -655,6 +662,10 @@ def override(self, **kwargs: Any) -> Iterator["Config"]:
655662
# Dict-like access for convenience
656663
def __getitem__(self, key: str) -> Any:
657664
"""Get setting by dot-notation key (e.g., 'database.host')."""
665+
# Check dynamic settings first for backwards compatibility
666+
if key in self._dynamic:
667+
return self._dynamic[key]
668+
658669
parts = key.split(".")
659670
obj: Any = self
660671
for part in parts:
@@ -673,15 +684,30 @@ def __setitem__(self, key: str, value: Any) -> None:
673684
"""Set setting by dot-notation key (e.g., 'database.host')."""
674685
parts = key.split(".")
675686
if len(parts) == 1:
676-
if hasattr(self, key):
687+
if hasattr(self, key) and not key.startswith("_"):
677688
setattr(self, key, value)
678689
else:
679-
raise KeyError(f"Setting '{key}' not found")
690+
# Store unknown top-level keys in _dynamic
691+
self._dynamic[key] = value
680692
else:
681-
obj: Any = self
682-
for part in parts[:-1]:
683-
obj = getattr(obj, part)
684-
setattr(obj, parts[-1], value)
693+
# Try to set via typed schema first
694+
try:
695+
obj: Any = self
696+
for part in parts[:-1]:
697+
if hasattr(obj, part):
698+
obj = getattr(obj, part)
699+
else:
700+
# Fall back to dynamic storage
701+
self._dynamic[key] = value
702+
return
703+
if hasattr(obj, parts[-1]):
704+
setattr(obj, parts[-1], value)
705+
else:
706+
# Attribute doesn't exist, store in dynamic
707+
self._dynamic[key] = value
708+
except AttributeError:
709+
# Path doesn't exist in typed schema, store in dynamic
710+
self._dynamic[key] = value
685711

686712
def get(self, key: str, default: Any = None) -> Any:
687713
"""Get setting with optional default value."""

0 commit comments

Comments
 (0)