-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
48be5f8
commit 66a1e53
Showing
16 changed files
with
200 additions
and
79 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
__version__ = '0.2.2' | ||
__version__ = '0.2.3' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
from inspect import isfunction | ||
from typing import Any, Callable, Dict, FrozenSet, Iterable, Optional, TypeVar, Union | ||
|
||
from pydantic import BaseModel | ||
|
||
from easyconfig.__const__ import ARG_NAME_IN_FILE, MISSING, MISSING_TYPE | ||
from easyconfig.config_objs.app_config import AppConfig, yaml_rt | ||
from easyconfig.errors import ExtraKwArgsNotAllowed | ||
|
||
TYPE_WRAPPED = TypeVar('TYPE_WRAPPED', bound=BaseModel) | ||
TYPE_DEFAULTS = Union[BaseModel, Dict[str, Any]] | ||
|
||
|
||
def check_field_args(model: AppConfig, allowed: FrozenSet[str]): | ||
"""Check extra args of pydantic fields""" | ||
|
||
# Model fields | ||
for name, field in model._obj_model_fields.items(): | ||
if not set(field.field_info.extra).issubset(allowed): | ||
forbidden = sorted(set(field.field_info.extra) - allowed) | ||
raise ExtraKwArgsNotAllowed(f'Extra kwargs for field "{name}" of {model._last_model.__class__.__name__} ' | ||
f'are not allowed: {", ".join(forbidden)}') | ||
|
||
# Submodels | ||
for name, sub_model in model._obj_children.items(): | ||
if isinstance(sub_model, tuple): | ||
for _sub_model in sub_model: | ||
check_field_args(model, allowed) | ||
else: | ||
check_field_args(sub_model, allowed) | ||
|
||
|
||
def get_file_values(model: TYPE_WRAPPED, file_values: Union[ | ||
MISSING_TYPE, None, TYPE_DEFAULTS, Callable[[], TYPE_DEFAULTS]] = MISSING) -> Optional[BaseModel]: | ||
|
||
# Implicit default | ||
if file_values is MISSING: | ||
file_values = model | ||
|
||
# if it's a callback we get the values | ||
if isfunction(file_values): | ||
file_values = file_values() | ||
|
||
# dict -> build models | ||
if isinstance(file_values, dict): | ||
file_values = model.__class__.parse_obj(file_values) | ||
|
||
if file_values is not None and not isinstance(file_values, BaseModel): | ||
raise ValueError( | ||
f'Default must be None or an instance of {BaseModel.__class__.__name__}! Got {type(file_values)}') | ||
|
||
return file_values | ||
|
||
|
||
def create_app_config( | ||
model: TYPE_WRAPPED, | ||
file_values: Union[MISSING_TYPE, None, TYPE_DEFAULTS, Callable[[], TYPE_DEFAULTS]] = MISSING, | ||
validate_file_values=True, | ||
check_field_extra_args: Optional[Iterable[str]] = (ARG_NAME_IN_FILE, )) -> TYPE_WRAPPED: | ||
|
||
app_cfg = AppConfig.from_model(model) | ||
app_cfg._file_defaults = get_file_values(model, file_values) | ||
|
||
# ensure that the extra args have no typos | ||
if check_field_extra_args is not None: | ||
check_field_args(app_cfg, frozenset(check_field_extra_args)) | ||
|
||
# validate the default file | ||
if file_values is not None and validate_file_values: | ||
_yaml = app_cfg.generate_default_yaml() | ||
_dict = yaml_rt.load(_yaml) | ||
model.__class__.parse_obj(_dict) | ||
|
||
return app_cfg |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
from .errors import DuplicateSubscriptionError, FunctionCallNotAllowedError, \ | ||
ReferenceFolderMissingError, SubscriptionAlreadyCanceledError | ||
from .errors import DefaultNotSet, DuplicateSubscriptionError, ExtraKwArgsNotAllowed, \ | ||
FunctionCallNotAllowedError, SubscriptionAlreadyCanceledError | ||
from .handler import set_exception_handler |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import pytest | ||
from pydantic import BaseModel, Field, ValidationError | ||
|
||
from easyconfig import create_app_config | ||
from easyconfig.errors import DefaultNotSet, ExtraKwArgsNotAllowed | ||
|
||
|
||
def test_simple(): | ||
class SimpleModel(BaseModel): | ||
a: int = Field(5, alias='aaa') | ||
|
||
create_app_config(SimpleModel(aaa=99)) | ||
create_app_config(SimpleModel(), {'aaa': 999}) | ||
|
||
with pytest.raises(ValidationError): | ||
create_app_config(SimpleModel(), {'aaa': 'asdf'}) | ||
|
||
|
||
def test_default_yaml(): | ||
class SimpleModel(BaseModel): | ||
a: int = Field(5, alias='aaa') | ||
|
||
a = create_app_config(SimpleModel(aaa=99)) | ||
assert a.generate_default_yaml() == 'aaa: 99\n' | ||
|
||
a = create_app_config(SimpleModel(), file_values=SimpleModel(aaa=12)) | ||
assert a.generate_default_yaml() == 'aaa: 12\n' | ||
|
||
a = create_app_config(SimpleModel(), file_values=None) | ||
with pytest.raises(DefaultNotSet): | ||
a.generate_default_yaml() | ||
|
||
|
||
def test_callback_for_default(): | ||
class SimpleModel(BaseModel): | ||
a: int = Field(5, alias='aaa') | ||
|
||
def get_default(): | ||
return SimpleModel(aaa=999) | ||
|
||
a = create_app_config(SimpleModel(), get_default) | ||
assert a._file_defaults.a == 999 | ||
|
||
a = create_app_config(SimpleModel(), lambda: {'aaa': 999}) | ||
assert a._file_defaults.a == 999 | ||
|
||
|
||
def test_extra_kwargs(): | ||
class SimpleModelOk(BaseModel): | ||
a: int = Field(5, alias='aaa', in_file=False) | ||
|
||
create_app_config(SimpleModelOk(aaa=99)) | ||
|
||
class SimpleModelErr(BaseModel): | ||
a: int = Field(5, alias='aaa', in__file=False) | ||
|
||
with pytest.raises(ExtraKwArgsNotAllowed) as e: | ||
create_app_config(SimpleModelErr(aaa=99)) | ||
|
||
assert str(e.value) == 'Extra kwargs for field "a" of SimpleModelErr are not allowed: in__file' |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.