def _build_complex_value(self, value: Union[str, bytes, bytearray], path: str): if isinstance(value, str) or isinstance(value, bytes) or isinstance(value, bytearray): try: value = self.__config__.json_loads(value) # type: ignore except ValueError as e: raise SettingsError(f"Error parsing for variable {path}") from e return value
def _build_environ( self, _env_file: Union[Path, str, None] = None, _env_file_encoding: Optional[str] = None ) -> Dict[str, Optional[str]]: """ Build environment variables suitable for passing to the Model. """ d: Dict[str, Optional[str]] = {} if self.__config__.case_sensitive: env_vars: Mapping[str, Optional[str]] = os.environ else: env_vars = {k.lower(): v for k, v in os.environ.items()} env_file_vars: Dict[str, Optional[str]] = {} env_file = _env_file if _env_file != env_file_sentinel else self.__config__.env_file env_file_encoding = _env_file_encoding if _env_file_encoding is not None else self.__config__.env_file_encoding if env_file is not None: env_path = Path(env_file) if env_path.is_file(): env_file_vars = read_env_file( env_path, encoding=env_file_encoding, case_sensitive=self.__config__.case_sensitive) env_vars = {**env_file_vars, **env_vars} for field in self.__fields__.values(): env_val: Optional[str] = None for env_name in field.field_info.extra['env_names']: env_val = env_vars.get(env_name) if env_name in env_file_vars: del env_file_vars[env_name] if env_val is not None: break if env_val is None: continue if field.is_complex(): try: env_val = self.__config__.json_loads(env_val) except ValueError as e: raise SettingsError( f'error parsing JSON for "{env_name}"' # type: ignore ) from e d[field.alias] = env_val if env_file_vars: for env_name, env_val in env_file_vars.items(): try: env_val = self.__config__.json_loads(env_val) except ValueError as e: pass d[env_name] = env_val return d
def _inner(settings: BaseSettings) -> Dict[str, Any]: # first call the original implementation d = super_eset(settings) if settings.__config__.case_sensitive: env_vars: Mapping[str, Optional[str]] = os.environ else: env_vars = {k.lower(): v for k, v in os.environ.items()} # now iterate through all subfields looking for nested env vars # For example: # NapariSettings has a Config.env_prefix of 'napari_' # so every field in the NapariSettings.Application subfield will be # available at 'napari_application_fieldname' for field in settings.__fields__.values(): if not isinstance(field.type_, type(BaseModel)): continue # pragma: no cover field_type = cast(BaseModel, field.type_) for env_name in field.field_info.extra['env_names']: for subf in field_type.__fields__.values(): # first check if subfield directly declares an "env" # (for example: ExperimentalSettings.async_) for e in subf.field_info.extra.get('env_names', []): env_val = env_vars.get(e.lower()) if env_val is not None: break # otherwise, look for the standard nested env var else: env_val = env_vars.get(f'{env_name}_{subf.name}') if env_val is not None: break is_complex, all_json_fail = super_eset.field_is_complex(subf) if env_val is not None and is_complex: try: env_val = settings.__config__.json_loads(env_val) except ValueError as e: if not all_json_fail: msg = f'error parsing JSON for "{env_name}"' raise SettingsError(msg) from e if isinstance(env_val, dict): explode = super_eset.explode_env_vars(field, env_vars) env_val = deep_update(env_val, explode) # if we found an env var, store it and return it if env_val is not None: if field.alias not in d: d[field.alias] = {} d[field.alias][subf.name] = env_val return d
def vault_config_settings_source(settings: BaseSettings) -> Dict[str, Any]: d: Dict[str, Optional[str]] = {} vault_client = _get_authenticated_vault_client(settings) # Get secrets for field in settings.__fields__.values(): vault_val: Optional[str] = None vault_secret_path = field.field_info.extra.get("vault_secret_path") vault_secret_key = field.field_info.extra.get("vault_secret_key") if vault_secret_path is None or vault_secret_key is None: logging.debug(f"Skipping field {field.name}") continue vault_secret_mount_point = getattr(settings.__config__, "vault_secret_mount_point", None) read_secret_parameters: HvacReadSecretParameters = { "path": vault_secret_path } if vault_secret_mount_point is not None: read_secret_parameters["mount_point"] = vault_secret_mount_point try: vault_val = vault_client.secrets.kv.v2.read_secret_version( **read_secret_parameters)["data"]["data"][vault_secret_key] except VaultError: logging.info( f'could not get secret "{vault_secret_path}:{vault_secret_key}"' ) if field.is_complex(): try: vault_val = settings.__config__.json_loads( vault_val) # type: ignore except ValueError as e: raise SettingsError( f'error parsing JSON for "{vault_secret_path}:{vault_secret_key}' ) from e d[field.alias] = vault_val return d
def _build_cloud_environ(self) -> Dict[str, Optional[str]]: if self.__config__.case_sensitive: env_vars: Mapping[str, Optional[str]] = os.environ else: env_vars = {k.lower(): v for k, v in os.environ.items()} cloud_env_file, cloud_env_file_encoding = ( self.__config__.cloud_env_file, self.__config__.cloud_env_file_encoding) if cloud_env_file is not None: try: env_vars = { **read_cloud_env_file(env_vars[cloud_env_file.lower()], encoding=cloud_env_file_encoding), **env_vars, } except KeyError: logger.warning("%s not found in env", cloud_env_file) d: Dict[str, Optional[str]] = {} for field in self.__fields__.values(): env_val: Optional[str] = None for env_name in field.field_info.extra['env_names']: env_val = env_vars.get(env_name) if env_val is not None: break if env_val is None: continue if field.is_complex(): try: env_val = self.__config__.json_loads( env_val) # type: ignore except ValueError as e: raise SettingsError( f'error parsing JSON for "{env_name}"') from e d[field.alias] = env_val return d
def __call__(self, settings: BaseSettings) -> Dict[str, Any]: """ Build environment variables suitable for passing to the Model. """ d: Dict[str, Optional[str]] = {} if settings.__config__.case_sensitive: env_vars: Mapping[str, Optional[str]] = os.environ # pragma: no cover else: env_vars = {k.lower(): v for k, v in os.environ.items()} env_file_vars: Dict[str, Optional[str]] = {} env_file = (self.env_file if self.env_file != env_file_sentinel else settings.__config__.env_file) env_file_encoding = (self.env_file_encoding if self.env_file_encoding is not None else settings.__config__.env_file_encoding) if env_file is not None: env_path = Path(env_file) if env_path.is_file(): env_file_vars = read_env_file( env_path, encoding=env_file_encoding, case_sensitive=settings.__config__.case_sensitive, ) env_vars = {**env_file_vars, **env_vars} for field in settings.__fields__.values(): env_val: Optional[str] = None for env_name in field.field_info.extra["env_names"]: env_val = env_vars.get(env_name) if env_name in env_file_vars: del env_file_vars[env_name] if env_val is not None: break if env_val is None: continue if field.is_complex(): try: env_val = settings.__config__.json_loads(env_val) except ValueError as e: # pragma: no cover raise SettingsError( f'error parsing JSON for "{env_name}"' # type: ignore ) from e d[field.alias] = env_val if env_file_vars: for env_name, env_val in env_file_vars.items(): if (env_val is None or len(env_val) == 0) and env_name in env_vars: env_val = env_vars[env_name] try: if env_val: env_val = settings.__config__.json_loads( env_val.strip()) except ValueError as e: logger.opt(colors=True, exception=e).trace( f"Error while parsing JSON for {escape_tag(env_name)}. Assumed as string." ) d[env_name] = env_val return d