Source code for bioarch.left_right
#!/usr/bin/env python
import inspect
from statistics import mean
from typing import Any, cast, Generic, List, Optional, TypeVar
T = TypeVar('T')
[docs]def best_effort_avg(left: Optional[T], right: Optional[T]) -> Optional[T]:
if left is None:
return right
if right is None:
return left
if isinstance(left, int):
if not isinstance(right, int):
raise TypeError
return cast(T, int(mean((left, right))))
if isinstance(left, float):
if not isinstance(right, float):
raise TypeError
return cast(T, mean((left, right)))
if right.__class__ != left.__class__:
raise TypeError
type_class = left.__class__
if hasattr(type_class, 'avg') and inspect.isfunction(getattr(type_class, 'avg')):
avg_func = getattr(type_class, 'avg')
arg_spec = inspect.getfullargspec(avg_func)
if len(arg_spec.args) != 2:
raise ValueError
if arg_spec.args[0] == 'self':
raise ValueError
if arg_spec.varargs is not None:
raise ValueError
if arg_spec.varkw is not None:
raise ValueError
return cast(T, avg_func(left, right))
arg_spec = inspect.getfullargspec(type_class)
if arg_spec.varargs is not None:
raise ValueError
if arg_spec.varkw is not None:
raise ValueError
avg_args: List[Any] = []
for key in arg_spec.args[1:]:
if key.startswith('_'):
key = key[1:]
left_val = getattr(left, key)
right_val = getattr(right, key)
avg = best_effort_avg(left_val, right_val)
avg_args.append(avg)
return type_class(*avg_args) # type: ignore
[docs]class LeftRight(Generic[T]):
"""Wrapper around measurements taken on both the left and right sides.
This augments the two measurements by adding a meta-measurement with the "avg"/"best" combination of both."""
__slots__ = ['left', 'right']
def __init__(self, left: Optional[T], right: Optional[T]):
if type(left) != type(right) and left is not None and right is not None: # pylint: disable=C0123
raise ValueError(f'Left and right types not the same: left="{type(left)}", left="{type(right)}"')
self.left: Optional[T] = left
self.right: Optional[T] = right
[docs] def avg(self) -> Optional[T]:
"""meta-measurement with the "avg"/"best" combination of both."""
return best_effort_avg(self.left, self.right)
def __eq__(self, other):
if other is None:
return False
if type(other) != type(self): # pylint: disable=C0123
raise NotImplementedError
return ((self.left, self.right) == (other.left, other.right)) # pylint: disable=C0325
def __repr__(self):
return f'{self.__class__.__name__}({self.left}, {self.right})'
def __str__(self):
return f'left: "{self.left}", right: "{self.right}")'
if __name__ == "__main__":
raise RuntimeError('No main available')