[docs]defxor_args(*arg_groups:Tuple[str,...])->Callable:"""Validate specified keyword args are mutually exclusive." Args: *arg_groups (Tuple[str, ...]): Groups of mutually exclusive keyword args. Returns: Callable: Decorator that validates the specified keyword args are mutually exclusive Raises: ValueError: If more than one arg in a group is defined. """defdecorator(func:Callable)->Callable:@functools.wraps(func)defwrapper(*args:Any,**kwargs:Any)->Any:"""Validate exactly one arg in each group is not None."""counts=[sum(1forarginarg_groupifkwargs.get(arg)isnotNone)forarg_groupinarg_groups]invalid_groups=[ifori,countinenumerate(counts)ifcount!=1]ifinvalid_groups:invalid_group_names=[", ".join(arg_groups[i])foriininvalid_groups]raiseValueError("Exactly one argument in each of the following"" groups must be defined:"f" {', '.join(invalid_group_names)}")returnfunc(*args,**kwargs)returnwrapperreturndecorator
[docs]defraise_for_status_with_text(response:Response)->None:"""Raise an error with the response text. Args: response (Response): The response to check for errors. Raises: ValueError: If the response has an error status code. """try:response.raise_for_status()exceptHTTPErrorase:raiseValueError(response.text)frome
[docs]@contextlib.contextmanagerdefmock_now(dt_value):# type: ignore"""Context manager for mocking out datetime.now() in unit tests. Args: dt_value: The datetime value to use for datetime.now(). Yields: datetime.datetime: The mocked datetime class. Example: with mock_now(datetime.datetime(2011, 2, 3, 10, 11)): assert datetime.datetime.now() == datetime.datetime(2011, 2, 3, 10, 11) """classMockDateTime(datetime.datetime):"""Mock datetime.datetime.now() with a fixed datetime."""@classmethoddefnow(cls):# type: ignore# Create a copy of dt_value.returndatetime.datetime(dt_value.year,dt_value.month,dt_value.day,dt_value.hour,dt_value.minute,dt_value.second,dt_value.microsecond,dt_value.tzinfo,)real_datetime=datetime.datetimedatetime.datetime=MockDateTimetry:yielddatetime.datetimefinally:datetime.datetime=real_datetime
[docs]defguard_import(module_name:str,*,pip_name:Optional[str]=None,package:Optional[str]=None)->Any:"""Dynamically import a module and raise an exception if the module is not installed. Args: module_name (str): The name of the module to import. pip_name (str, optional): The name of the module to install with pip. Defaults to None. package (str, optional): The package to import the module from. Defaults to None. Returns: Any: The imported module. Raises: ImportError: If the module is not installed. """try:module=importlib.import_module(module_name,package)except(ImportError,ModuleNotFoundError):pip_name=pip_nameormodule_name.split(".")[0].replace("_","-")raiseImportError(f"Could not import {module_name} python package. "f"Please install it with `pip install {pip_name}`.")returnmodule
[docs]defcheck_package_version(package:str,lt_version:Optional[str]=None,lte_version:Optional[str]=None,gt_version:Optional[str]=None,gte_version:Optional[str]=None,)->None:"""Check the version of a package. Args: package (str): The name of the package. lt_version (str, optional): The version must be less than this. Defaults to None. lte_version (str, optional): The version must be less than or equal to this. Defaults to None. gt_version (str, optional): The version must be greater than this. Defaults to None. gte_version (str, optional): The version must be greater than or equal to this. Defaults to None. Raises: ValueError: If the package version does not meet the requirements. """imported_version=parse(version(package))iflt_versionisnotNoneandimported_version>=parse(lt_version):raiseValueError(f"Expected {package} version to be < {lt_version}. Received "f"{imported_version}.")iflte_versionisnotNoneandimported_version>parse(lte_version):raiseValueError(f"Expected {package} version to be <= {lte_version}. Received "f"{imported_version}.")ifgt_versionisnotNoneandimported_version<=parse(gt_version):raiseValueError(f"Expected {package} version to be > {gt_version}. Received "f"{imported_version}.")ifgte_versionisnotNoneandimported_version<parse(gte_version):raiseValueError(f"Expected {package} version to be >= {gte_version}. Received "f"{imported_version}.")
[docs]defget_pydantic_field_names(pydantic_cls:Any)->Set[str]:"""Get field names, including aliases, for a pydantic class. Args: pydantic_cls: Pydantic class. Returns: Set[str]: Field names. """all_required_field_names=set()ifis_pydantic_v1_subclass(pydantic_cls):forfieldinpydantic_cls.__fields__.values():all_required_field_names.add(field.name)iffield.has_alias:all_required_field_names.add(field.alias)else:# Assuming pydantic 2 for nowforname,fieldinpydantic_cls.model_fields.items():all_required_field_names.add(name)iffield.alias:all_required_field_names.add(field.alias)returnall_required_field_names
[docs]defbuild_extra_kwargs(extra_kwargs:Dict[str,Any],values:Dict[str,Any],all_required_field_names:Set[str],)->Dict[str,Any]:"""Build extra kwargs from values and extra_kwargs. Args: extra_kwargs: Extra kwargs passed in by user. values: Values passed in by user. all_required_field_names: All required field names for the pydantic class. Returns: Dict[str, Any]: Extra kwargs. Raises: ValueError: If a field is specified in both values and extra_kwargs. ValueError: If a field is specified in model_kwargs. """forfield_nameinlist(values):iffield_nameinextra_kwargs:raiseValueError(f"Found {field_name} supplied twice.")iffield_namenotinall_required_field_names:warnings.warn(f"""WARNING! {field_name} is not default parameter.{field_name} was transferred to model_kwargs. Please confirm that {field_name} is what you intended.""")extra_kwargs[field_name]=values.pop(field_name)invalid_model_kwargs=all_required_field_names.intersection(extra_kwargs.keys())ifinvalid_model_kwargs:raiseValueError(f"Parameters {invalid_model_kwargs} should be specified explicitly. "f"Instead they were passed in as part of `model_kwargs` parameter.")returnextra_kwargs
[docs]defconvert_to_secret_str(value:Union[SecretStr,str])->SecretStr:"""Convert a string to a SecretStr if needed. Args: value (Union[SecretStr, str]): The value to convert. Returns: SecretStr: The SecretStr value. """ifisinstance(value,SecretStr):returnvaluereturnSecretStr(value)
class_NoDefaultType:"""Type to indicate no default value is provided."""pass_NoDefault=_NoDefaultType()@overloaddeffrom_env(key:str,/)->Callable[[],str]:...@overloaddeffrom_env(key:str,/,*,default:str)->Callable[[],str]:...@overloaddeffrom_env(key:str,/,*,error_message:str)->Callable[[],str]:...@overloaddeffrom_env(key:str,/,*,default:str,error_message:Optional[str])->Callable[[],str]:...@overloaddeffrom_env(key:str,/,*,default:None,error_message:Optional[str])->Callable[[],Optional[str]]:...
[docs]deffrom_env(key:str,/,*,default:Union[str,_NoDefaultType,None]=_NoDefault,error_message:Optional[str]=None,)->Union[Callable[[],str],Callable[[],Optional[str]]]:"""Create a factory method that gets a value from an environment variable. Args: key: The environment variable to look up. default: The default value to return if the environment variable is not set. error_message: the error message which will be raised if the key is not found and no default value is provided. This will be raised as a ValueError. """defget_from_env_fn()->Optional[str]:"""Get a value from an environment variable."""ifkeyinos.environ:returnos.environ[key]elifisinstance(default,(str,type(None))):returndefaultelse:iferror_message:raiseValueError(error_message)else:raiseValueError(f"Did not find {key}, please add an environment variable"f" `{key}` which contains it, or pass"f" `{key}` as a named parameter.")returnget_from_env_fn
[docs]defsecret_from_env(key:str,/,*,default:Union[str,_NoDefaultType,None]=_NoDefault,error_message:Optional[str]=None,)->Union[Callable[[],Optional[SecretStr]],Callable[[],SecretStr]]:"""Secret from env. Args: key: The environment variable to look up. default: The default value to return if the environment variable is not set. error_message: the error message which will be raised if the key is not found and no default value is provided. This will be raised as a ValueError. Returns: factory method that will look up the secret from the environment. """defget_secret_from_env()->Optional[SecretStr]:"""Get a value from an environment variable."""ifkeyinos.environ:returnSecretStr(os.environ[key])elifisinstance(default,str):returnSecretStr(default)elifisinstance(default,type(None)):returnNoneelse:iferror_message:raiseValueError(error_message)else:raiseValueError(f"Did not find {key}, please add an environment variable"f" `{key}` which contains it, or pass"f" `{key}` as a named parameter.")returnget_secret_from_env