-
Notifications
You must be signed in to change notification settings - Fork 17
switch to pydantic v2 #485
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,16 +1,22 @@ | ||
| # pylint: disable=no-self-argument, inconsistent-return-statements, invalid-name, import-outside-toplevel | ||
| import pathlib | ||
| from pathlib import Path | ||
| from typing import Any, Dict, List, Optional | ||
|
|
||
| from pydantic import BaseModel, Field, field_validator | ||
| from pydantic_settings import ( | ||
| BaseSettings, | ||
| PydanticBaseSettingsSource, | ||
| SettingsConfigDict, | ||
| ) | ||
| from pydantic_settings import ( | ||
| YamlConfigSettingsSource as _YamlConfigSettingsSource, | ||
| ) | ||
| from ruamel.yaml import YAML | ||
|
|
||
| from gto.constants import assert_name_is_valid | ||
| from gto.exceptions import UnknownStage, UnknownType, WrongConfig | ||
| from gto.ext import EnrichmentReader, find_enrichment_types, find_enrichments | ||
|
|
||
| from ._pydantic import BaseModel, BaseSettings, InitSettingsSource, validator | ||
|
|
||
| yaml = YAML(typ="safe", pure=True) | ||
| yaml.default_flow_style = False | ||
|
|
||
|
|
@@ -27,45 +33,47 @@ def load(self) -> EnrichmentReader: | |
|
|
||
| class NoFileConfig(BaseSettings): # type: ignore[valid-type] | ||
| INDEX: str = "artifacts.yaml" | ||
| TYPES: Optional[List[str]] = None | ||
| STAGES: Optional[List[str]] = None | ||
| CONFIG_FILE_NAME: Optional[str] = CONFIG_FILE_NAME | ||
| LOG_LEVEL: str = "INFO" | ||
| DEBUG: bool = False | ||
| ENRICHMENTS: List[EnrichmentConfig] = [] | ||
| AUTOLOAD_ENRICHMENTS: bool = True | ||
| CONFIG_FILE_NAME: Optional[str] = CONFIG_FILE_NAME | ||
| EMOJIS: bool = True | ||
|
|
||
| class Config: | ||
| env_prefix = "gto_" | ||
| types: Optional[List[str]] = None | ||
| stages: Optional[List[str]] = None | ||
| enrichments: List[EnrichmentConfig] = Field(default_factory=list) | ||
| autoload_enrichments: bool = True | ||
|
|
||
| model_config = SettingsConfigDict(env_prefix="gto_") | ||
|
|
||
| def assert_type(self, name): | ||
| assert_name_is_valid(name) | ||
| # pylint: disable-next=unsupported-membership-test | ||
| if self.TYPES is not None and name not in self.TYPES: | ||
| raise UnknownType(name, self.TYPES) | ||
| if self.types is not None and name not in self.types: | ||
| raise UnknownType(name, self.types) | ||
|
|
||
| def assert_stage(self, name): | ||
| assert_name_is_valid(name) | ||
| # pylint: disable-next=unsupported-membership-test | ||
| if self.STAGES is not None and name not in self.STAGES: | ||
| raise UnknownStage(name, self.STAGES) | ||
| if self.stages is not None and name not in self.stages: | ||
| raise UnknownStage(name, self.stages) | ||
|
|
||
| @property | ||
| def enrichments(self) -> Dict[str, EnrichmentReader]: | ||
| res = {e.source: e for e in (e.load() for e in self.ENRICHMENTS)} | ||
| if self.AUTOLOAD_ENRICHMENTS: | ||
| def enrichments_(self) -> Dict[str, EnrichmentReader]: | ||
| res = {e.source: e for e in (e.load() for e in self.enrichments)} | ||
| if self.autoload_enrichments: | ||
| return {**find_enrichments(), **res} | ||
| return res | ||
|
|
||
| @validator("TYPES") | ||
| @field_validator("types") | ||
| @classmethod | ||
| def types_are_valid(cls, v): # pylint: disable=no-self-use | ||
| if v: | ||
| for name in v: | ||
| assert_name_is_valid(name) | ||
| return v | ||
|
|
||
| @validator("STAGES") | ||
| @field_validator("stages") | ||
| @classmethod | ||
| def stages_are_valid(cls, v): # pylint: disable=no-self-use | ||
| if v: | ||
| for name in v: | ||
|
|
@@ -77,61 +85,48 @@ def check_index_exist(self, repo: str): | |
| return index.exists() and index.is_file() | ||
|
|
||
|
|
||
| def _set_location_init_source(init_source: InitSettingsSource): | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All of the changes here and below is to support passing custom config file, which is no longer possible in Before, we were accessing |
||
| def inner(settings: "RegistryConfig"): | ||
| if "CONFIG_FILE_NAME" in init_source.init_kwargs: | ||
| settings.__dict__["CONFIG_FILE_NAME"] = init_source.init_kwargs[ | ||
| "CONFIG_FILE_NAME" | ||
| ] | ||
| return {} | ||
|
|
||
| return inner | ||
| class YamlConfigSettingsSource(_YamlConfigSettingsSource): | ||
| def _read_file(self, file_path: pathlib.Path) -> dict[str, Any]: | ||
| with open(file_path, encoding=self.yaml_file_encoding) as yaml_file: | ||
| return yaml.load(yaml_file) or {} | ||
|
|
||
|
|
||
| def config_settings_source(settings: "RegistryConfig") -> Dict[str, Any]: | ||
| """ | ||
| A simple settings source that loads variables from a yaml file in GTO DIR | ||
| """ | ||
|
|
||
| encoding = settings.__config__.env_file_encoding | ||
| config_file = getattr(settings, "CONFIG_FILE_NAME", CONFIG_FILE_NAME) | ||
| if not isinstance(config_file, Path): | ||
| config_file = Path(config_file) | ||
| if not config_file.exists(): | ||
| return {} | ||
| conf = yaml.load(config_file.read_text(encoding=encoding)) | ||
|
|
||
| return {k.upper(): v for k, v in conf.items()} if conf else {} | ||
| class RegistryConfig(NoFileConfig): | ||
| model_config = SettingsConfigDict(env_prefix="gto_", env_file_encoding="utf-8") | ||
|
|
||
| def config_file_exists(self): | ||
| config = pathlib.Path(self.CONFIG_FILE_NAME) | ||
| return config.exists() and config.is_file() | ||
|
|
||
| class RegistryConfig(NoFileConfig): | ||
| class Config: | ||
| env_prefix = "gto_" | ||
| env_file_encoding = "utf-8" | ||
|
|
||
| def read_registry_config(config_file_name) -> "RegistryConfig": | ||
| class _RegistryConfig(RegistryConfig): | ||
| @classmethod | ||
| def customise_sources( | ||
| def settings_customise_sources( | ||
| cls, | ||
| init_settings, | ||
| env_settings, | ||
| file_secret_settings, | ||
| settings_cls: type[BaseSettings], | ||
| init_settings: PydanticBaseSettingsSource, | ||
| env_settings: PydanticBaseSettingsSource, | ||
| dotenv_settings: PydanticBaseSettingsSource, | ||
| file_secret_settings: PydanticBaseSettingsSource, | ||
| ): | ||
| encoding = getattr(settings_cls.model_config, "env_file_encoding", "utf-8") | ||
| return ( | ||
| _set_location_init_source(init_settings), | ||
| init_settings, | ||
| env_settings, | ||
| config_settings_source, | ||
| ( | ||
| YamlConfigSettingsSource( | ||
| settings_cls, | ||
| yaml_file=config_file_name, | ||
| yaml_file_encoding=encoding, | ||
| ) | ||
| ), | ||
| dotenv_settings, | ||
| file_secret_settings, | ||
| ) | ||
|
|
||
| def config_file_exists(self): | ||
| config = pathlib.Path(self.CONFIG_FILE_NAME) | ||
| return config.exists() and config.is_file() | ||
|
|
||
|
|
||
| def read_registry_config(config_file_name): | ||
| try: | ||
| return RegistryConfig(CONFIG_FILE_NAME=config_file_name) | ||
| return _RegistryConfig(CONFIG_FILE_NAME=config_file_name) | ||
| except Exception as e: # pylint: disable=bare-except | ||
| raise WrongConfig(config_file_name) from e | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In pydantic v1, these fields were case-insensitive and converted to lowercase fields.
That is no longer possible (except for environment variables).