예제 #1
0
    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
예제 #2
0
    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
예제 #3
0
    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
예제 #4
0
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
예제 #5
0
    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
예제 #6
0
    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