Source code for pypillometry.eyedata.history
"""
history.py
==========
The history of an eyedata object: :func:`keephistory` records the calls of
the decorated methods (of :class:`~pypillometry.EyeData` and its
namespaces ``d.pupil`` and ``d.gaze``).
"""
import functools
import numpy as np
from .namespace import Namespace
def _history_arg(v):
"""Short text for an argument in the history (no reference to the object)."""
if isinstance(v, str):
return repr(v)
if v is None or isinstance(v, (bool, int, float, np.number)):
return str(v)
if isinstance(v, np.ndarray):
return f"array{v.shape}"
if callable(v):
return getattr(v, "__name__", type(v).__name__)
r = repr(v) if isinstance(v, (list, tuple, dict)) else str(v)
if len(r) <= 40 and "\n" not in r:
return r # short enough, e.g. [1, 2], (1280, 1024), 30 cm
try:
return f"{type(v).__name__}({len(v)})" # e.g. Intervals(13)
except TypeError:
return type(v).__name__
# number of decorated methods currently running; only the outermost call
# (the one made by the user) is recorded
_history_depth = 0
[docs]
def keephistory(func):
"""
Decorator that records a method call in the history of the returned object.
After the decorated method has run, a short text version of the call is
added (via ``add_to_history()``) to the object it returned (or to `self`
if it returned nothing, e.g. for setters), provided that object is an
eyedata object (i.e., has an ``add_to_history`` method).
Calls made from inside another decorated method are not recorded, and
large arguments are only summarized (e.g., ``Intervals(13)``); the
`inplace` argument is left out (it does not change the result). The history
is a log of the operations; show it with
:meth:`~pypillometry.EyeData.print_history`.
Parameters
----------
func : callable
method to wrap; its first argument must be the object (`self`) or a
:class:`~pypillometry.eyedata.namespace.Namespace` of it
Returns
-------
callable
wrapped method that returns the same value as `func`
"""
@functools.wraps(func)
def wrapper(*args,**kwargs):
global _history_depth
_history_depth += 1
try:
obj=func(*args,**kwargs)
finally:
_history_depth -= 1
# record in the returned object, or in `self` for in-place setters
# that return nothing; methods of a namespace (e.g., ``d.pupil``)
# are recorded with its name (``pupil.blinks_detect``)
self, name = args[0], func.__name__
if isinstance(self, Namespace):
self, name = self._d, f"{self._name}.{name}"
target = obj if obj is not None else self
if _history_depth == 0 and hasattr(target, 'add_to_history'):
allargs = [_history_arg(v) for v in args[1:]]
allargs += [f"{k}={_history_arg(v)}" for k, v in kwargs.items()
if k != "inplace"] # does not change the result
fstr = f"{name}({','.join(allargs)})"
target.add_to_history({"funcstring": fstr, "funcname": name})
return obj
return wrapper