fmeval.transforms.util
1from typing import Any, Callable, Dict, List, Tuple 2from fmeval.util import assert_condition 3 4 5def validate_key_uniqueness(keys: List[str]) -> None: 6 """Validate that a list of keys contains unique values. 7 8 This function exists to capture the full list of duplicate keys 9 in the error message that is raised, for a better debugging experience. 10 11 :param keys: The keys to be validated. 12 :raises: EvalAlgorithmInternalError if the values in `keys` are not unique. 13 """ 14 seen = set() 15 duplicates = [] 16 for key in keys: 17 if key in seen: 18 duplicates.append(key) 19 else: 20 seen.add(key) 21 assert_condition(len(duplicates) == 0, f"Duplicate keys found: {duplicates}.") 22 23 24def validate_existing_keys(record: Dict[str, Any], keys: List[str]) -> None: 25 """Validate that all expected keys are present in a record. 26 27 :param record: The record to be validated. 28 :param keys: The keys that are expected to be present in the record. 29 :raises: EvalAlgorithmInternalError if any validation fails. 30 """ 31 missing_keys = [] 32 for key in keys: 33 if key not in record: 34 missing_keys.append(key) 35 assert_condition( 36 len(missing_keys) == 0, 37 f"Record {record} is expected to contain the following keys, " f"but they are missing: {missing_keys}.", 38 ) 39 40 41def validate_call(call_method: Callable) -> Callable: 42 """Decorator for the __call__ method of Transforms used for validating input and output. 43 44 This decorator validates that all keys in a Transform's `input_keys` attribute are 45 present in the input record that is passed to `__call__` and that the keys that 46 are added to the record by the Transform's internal `__call__` logic are limited 47 to the keys specified by the Transform's `output_keys` attribute. 48 49 Note that this decorator should only be used by Transforms that mutate their input record, 50 as the output key validation may not make sense in the case where a new record object 51 (which may not keep all the same keys as the original record) is returned as the output. 52 53 Additionally, this decorator should be used in conjunction with the 54 `register_input_output_keys` method, as the `input_keys` and `output_keys` are initialized 55 to None in `Transform.__init__`. 56 57 :param call_method: The `__call__` method of a Transform. 58 :returns: A wrapper function that performs pre- and post-validation on top of `__call__`. 59 """ 60 61 def wrapper(self, record: Dict[str, Any]) -> Dict[str, Any]: 62 assert_condition( 63 self.input_keys is not None, 64 "self.input_keys has not been set. You should set this attribute using " 65 "the register_input_output_keys method.", 66 ) 67 assert_condition( 68 self.output_keys is not None, 69 "self.output_keys has not been set. You should set this attribute using " 70 "the register_input_output_keys method.", 71 ) 72 validate_existing_keys(record, self.input_keys) 73 call_output = call_method(self, record) 74 validate_existing_keys(call_output, self.output_keys) 75 return call_output 76 77 return wrapper 78 79 80def create_output_key(transform_name: str, *args, **kwargs) -> str: 81 """Create an output key to be used by a Transform instance. 82 83 This method is used to create unique, easily-identifiable output keys 84 for Transform instances. *args and **kwargs are used purely for 85 ensuring key uniqueness, and need not be arguments to the Transform's 86 initializer, though they generally will be, for ease of interpretability. 87 88 :param transform_name: The name of the Transform class. 89 This argument is generally passed via the __name__ attribute of 90 a class. Note that we do not simply pass the class itself (which 91 would be the more intuitive approach), as Ray wraps actor classes 92 in its own wrapper class, which will cause the __name__ attribute 93 to return an unexpected value. 94 :param *args: Variable length argument list. 95 :param **kwargs: Arbitrary keyword arguments. 96 """ 97 98 def args_to_str(positional_args: Tuple[str]) -> str: 99 return ", ".join(str(arg) for arg in positional_args) 100 101 def kwargs_to_str(keyword_args: Dict[str, Any]) -> str: 102 return ", ".join(f"{k}={str(v)}" for k, v in keyword_args.items()) 103 104 args_string = args_to_str(args) 105 kwargs_string = kwargs_to_str(kwargs) 106 output_key = ( 107 f"{transform_name}" 108 f"({args_string if args_string else ''}" 109 f"{', ' if args_string and kwargs_string else ''}" 110 f"{kwargs_string if kwargs_string else ''})" 111 ) 112 return output_key
6def validate_key_uniqueness(keys: List[str]) -> None: 7 """Validate that a list of keys contains unique values. 8 9 This function exists to capture the full list of duplicate keys 10 in the error message that is raised, for a better debugging experience. 11 12 :param keys: The keys to be validated. 13 :raises: EvalAlgorithmInternalError if the values in `keys` are not unique. 14 """ 15 seen = set() 16 duplicates = [] 17 for key in keys: 18 if key in seen: 19 duplicates.append(key) 20 else: 21 seen.add(key) 22 assert_condition(len(duplicates) == 0, f"Duplicate keys found: {duplicates}.")
Validate that a list of keys contains unique values.
This function exists to capture the full list of duplicate keys in the error message that is raised, for a better debugging experience.
Parameters
- keys: The keys to be validated.
Raises
- EvalAlgorithmInternalError if the values in
keys
are not unique.
25def validate_existing_keys(record: Dict[str, Any], keys: List[str]) -> None: 26 """Validate that all expected keys are present in a record. 27 28 :param record: The record to be validated. 29 :param keys: The keys that are expected to be present in the record. 30 :raises: EvalAlgorithmInternalError if any validation fails. 31 """ 32 missing_keys = [] 33 for key in keys: 34 if key not in record: 35 missing_keys.append(key) 36 assert_condition( 37 len(missing_keys) == 0, 38 f"Record {record} is expected to contain the following keys, " f"but they are missing: {missing_keys}.", 39 )
Validate that all expected keys are present in a record.
Parameters
- record: The record to be validated.
- keys: The keys that are expected to be present in the record.
Raises
- EvalAlgorithmInternalError if any validation fails.
42def validate_call(call_method: Callable) -> Callable: 43 """Decorator for the __call__ method of Transforms used for validating input and output. 44 45 This decorator validates that all keys in a Transform's `input_keys` attribute are 46 present in the input record that is passed to `__call__` and that the keys that 47 are added to the record by the Transform's internal `__call__` logic are limited 48 to the keys specified by the Transform's `output_keys` attribute. 49 50 Note that this decorator should only be used by Transforms that mutate their input record, 51 as the output key validation may not make sense in the case where a new record object 52 (which may not keep all the same keys as the original record) is returned as the output. 53 54 Additionally, this decorator should be used in conjunction with the 55 `register_input_output_keys` method, as the `input_keys` and `output_keys` are initialized 56 to None in `Transform.__init__`. 57 58 :param call_method: The `__call__` method of a Transform. 59 :returns: A wrapper function that performs pre- and post-validation on top of `__call__`. 60 """ 61 62 def wrapper(self, record: Dict[str, Any]) -> Dict[str, Any]: 63 assert_condition( 64 self.input_keys is not None, 65 "self.input_keys has not been set. You should set this attribute using " 66 "the register_input_output_keys method.", 67 ) 68 assert_condition( 69 self.output_keys is not None, 70 "self.output_keys has not been set. You should set this attribute using " 71 "the register_input_output_keys method.", 72 ) 73 validate_existing_keys(record, self.input_keys) 74 call_output = call_method(self, record) 75 validate_existing_keys(call_output, self.output_keys) 76 return call_output 77 78 return wrapper
Decorator for the __call__ method of Transforms used for validating input and output.
This decorator validates that all keys in a Transform's input_keys
attribute are
present in the input record that is passed to __call__
and that the keys that
are added to the record by the Transform's internal __call__
logic are limited
to the keys specified by the Transform's output_keys
attribute.
Note that this decorator should only be used by Transforms that mutate their input record, as the output key validation may not make sense in the case where a new record object (which may not keep all the same keys as the original record) is returned as the output.
Additionally, this decorator should be used in conjunction with the
register_input_output_keys
method, as the input_keys
and output_keys
are initialized
to None in Transform.__init__
.
Parameters
- call_method: The
__call__
method of a Transform. :returns: A wrapper function that performs pre- and post-validation on top of__call__
.
81def create_output_key(transform_name: str, *args, **kwargs) -> str: 82 """Create an output key to be used by a Transform instance. 83 84 This method is used to create unique, easily-identifiable output keys 85 for Transform instances. *args and **kwargs are used purely for 86 ensuring key uniqueness, and need not be arguments to the Transform's 87 initializer, though they generally will be, for ease of interpretability. 88 89 :param transform_name: The name of the Transform class. 90 This argument is generally passed via the __name__ attribute of 91 a class. Note that we do not simply pass the class itself (which 92 would be the more intuitive approach), as Ray wraps actor classes 93 in its own wrapper class, which will cause the __name__ attribute 94 to return an unexpected value. 95 :param *args: Variable length argument list. 96 :param **kwargs: Arbitrary keyword arguments. 97 """ 98 99 def args_to_str(positional_args: Tuple[str]) -> str: 100 return ", ".join(str(arg) for arg in positional_args) 101 102 def kwargs_to_str(keyword_args: Dict[str, Any]) -> str: 103 return ", ".join(f"{k}={str(v)}" for k, v in keyword_args.items()) 104 105 args_string = args_to_str(args) 106 kwargs_string = kwargs_to_str(kwargs) 107 output_key = ( 108 f"{transform_name}" 109 f"({args_string if args_string else ''}" 110 f"{', ' if args_string and kwargs_string else ''}" 111 f"{kwargs_string if kwargs_string else ''})" 112 ) 113 return output_key
Create an output key to be used by a Transform instance.
This method is used to create unique, easily-identifiable output keys for Transform instances. args and *kwargs are used purely for ensuring key uniqueness, and need not be arguments to the Transform's initializer, though they generally will be, for ease of interpretability.
Parameters
- transform_name: The name of the Transform class. This argument is generally passed via the __name__ attribute of a class. Note that we do not simply pass the class itself (which would be the more intuitive approach), as Ray wraps actor classes in its own wrapper class, which will cause the __name__ attribute to return an unexpected value.
- *args: Variable length argument list.
- **kwargs: Arbitrary keyword arguments.