"""Functions implementing Boolean checks."""from__future__importannotationsimportenumfromabcimportABCfromcollections.abcimportCallablefromtypingimportAnyfromattrsimportcmp_usingfromtyping_extensionsimportis_protocol,override# Used for comparing pandas dataframes in attrs classeseq_dataframe=cmp_using(lambdax,y:x.equals(y))
[docs]defis_abstract(cls:Any)->bool:"""Determine if a given class is abstract. This check is more general sense than ``inspect.abstract``, which only verifies if a class has abstract methods. The latter can be problematic when the class has no abstract methods but is nevertheless not directly usable, for example, because it has uninitialized members, which are only covered in its non-"abstract" subclasses. By contrast, this method simply checks if the class derives from ``abc.ABC`` or is a protocol class. Args: cls: The class to be inspected. Returns: ``True`` if the class is "abstract" (see definition above), ``False`` else. """returnABCincls.__bases__oris_protocol(cls)
[docs]defstrtobool(val:str)->bool:"""Convert a string representation of truth to ``True`` or ``False``. Adapted from distutils. True values are ``y``, ``yes``, ``t``, ``true``, ``on``, and ``1``. False values are ``n``, ``no``, ``f``, ``false``, ``off``, and ``0``. Raises a ``ValueError`` if ``val`` is anything else. Args: val: String to be checked. Returns: The ``bool`` value of the corresponding string representation. Raises: ValueError: If ``val`` cannot be evaluated to a suitable Boolean value. """ifval.lower()in("y","yes","t","true","on","1"):returnTrueifval.lower()in("n","no","f","false","off","0"):returnFalseraiseValueError(f"Invalid truth value: {val}")
[docs]defcheck_if_in(element:Any,allowed:list):"""Check if an element is in a given list of elements. Args: element: The element to be checked allowed: The corresponding list Raises: ValueError: If ``element`` is not in ``allowed``. """ifelementnotinallowed:raiseValueError(f"The value '{element}' is not allowed. Must be one of {allowed}.")
[docs]classUncertainBool(enum.Enum):"""Enum for representing uncertain Boolean values."""TRUE="TRUE""""Represents the Boolean value `True`."""FALSE="FALSE""""Represents the Boolean value `False`."""UNKNOWN="UNKNOWN""""Indicates that the value of the Boolean cannot be determined."""def__bool__(self):ifselfisUncertainBool.TRUE:returnTrueelifselfisUncertainBool.FALSE:returnFalseelifselfisUncertainBool.UNKNOWN:raiseTypeError(f"'{UncertainBool.UNKNOWN}' has no Boolean representation.")raiseValueError(f"Unknown value: '{self}'")
[docs]@classmethoddeffrom_erroneous_callable(cls,callable_:Callable,/)->UncertainBool:"""Create an uncertain Boolean from a potentially erroneous Boolean call."""try:returncls.TRUEifcallable_()elsecls.FALSEexceptException:returncls.UNKNOWN
[docs]classAutoBool(enum.Enum):"""Enum for representing Booleans whose values can be determined automatically."""# https://github.com/python-attrs/attrs/issues/1462__hash__=object.__hash__TRUE="TRUE""""Represents the Boolean value `True`."""FALSE="FALSE""""Represents the Boolean value `False`."""AUTO="AUTO"""" Indicates that the value of the Boolean should be determined automatically on-the-fly, using a predicate function. """def__bool__(self):ifselfisAutoBool.TRUE:returnTrueelifselfisAutoBool.FALSE:returnFalseelifselfisAutoBool.AUTO:raiseTypeError(f"'{AutoBool.AUTO}' has no Boolean representation.")raiseValueError(f"Unknown value: '{self}'")@overridedef__eq__(self,other:Any)->bool:ifnotisinstance(other,(bool,AutoBool)):raiseNotImplementedErrorifselfisAutoBool.TRUEorselfisAutoBool.FALSE:returnbool(self)==otherelifisinstance(other,AutoBool)andotherisAutoBool.AUTO:returnTruereturnFalse
[docs]defevaluate(self,predicate_function:Callable[[],bool])->bool:"""Evaluate the Boolean value under a given predicate function."""ifselfisAutoBool.TRUE:returnTrueelifselfisAutoBool.FALSE:returnFalseelifselfisAutoBool.AUTO:returnpredicate_function()raiseValueError(f"Unknown value: '{self}'")
[docs]@classmethoddeffrom_unstructured(cls,value:AutoBool|bool|str|None,/)->AutoBool:"""Create the enum member from unstructured input. For string inputs, see :func:`~baybe.utils.boolean.strtobool`. Args: value: The (possibly unstructured) input value to be converted. Returns: The corresponding enum member. Raises: ValueError: If the input cannot be converted to an enum member. Example: >>> AutoBool.from_unstructured(AutoBool.TRUE) <AutoBool.TRUE: 'TRUE'> >>> AutoBool.from_unstructured(True) <AutoBool.TRUE: 'TRUE'> >>> AutoBool.from_unstructured("t") <AutoBool.TRUE: 'TRUE'> >>> AutoBool.from_unstructured(AutoBool.FALSE) <AutoBool.FALSE: 'FALSE'> >>> AutoBool.from_unstructured(False) <AutoBool.FALSE: 'FALSE'> >>> AutoBool.from_unstructured("f") <AutoBool.FALSE: 'FALSE'> >>> AutoBool.from_unstructured(AutoBool.AUTO) <AutoBool.AUTO: 'AUTO'> >>> AutoBool.from_unstructured(None) <AutoBool.AUTO: 'AUTO'> >>> AutoBool.from_unstructured("auto") <AutoBool.AUTO: 'AUTO'> """matchvalue:caseAutoBool():returnvaluecasebool()asb:returncls.TRUEifbelsecls.FALSEcaseNone:returncls.AUTOcasestr()ass:ifs.lower()=="auto":returncls.AUTOtry:returncls.from_unstructured(strtobool(s))exceptValueError:passraiseValueError(f"Cannot convert '{value}' to '{cls.__name__}'.")