Developer Tools#

System-level utilities used across spey.

The spey.system subpackage groups infrastructure that is not part of the statistical-model API itself but is needed to operate it:

  • spey.system.logger — coloured logger setup and a capture_logs() context manager that deduplicates noisy messages emitted from hot loops.

  • spey.system.exceptions — package-specific exception classes (e.g. PluginError, NegativeExpectedYields).

  • spey.system.cachecache_results(), a thread-safe, mutable-argument-aware decorator with per-instance support.

  • spey.system.webutils — small HTTP helpers for retrieving BibTeX entries and checking for new spey releases on PyPI.

Exceptions#

AnalysisQueryError

Analysis query exception

MethodNotAvailable

If the method is not available for a given backend

NegativeExpectedYields

Negative expected yields exception

PluginError

Invalid plugin exception

UnknownCrossSection

Unknown cross-section exception

UnknownTestStatistics

Unknown test statistics exception

CalculatorNotAvailable

Unavailable calculator Exception

CanNotFindRoots

Unable to find roots of the function

UnknownComputer

Unknown computation base

CombinerNotAvailable

Unavailable combination routine exception

Logging#

disable_logging

Temporary disable logging implementation

capture_logs

Context manager that captures log records emitted while inside the context and suppresses duplicate messages.

Caching#

Thread-safe, mutable-argument-aware function result cache.

Provides cache_results(), a decorator that caches function return values keyed on both positional and keyword arguments. Unlike functools.lru_cache(), it handles non-hashable inputs (NumPy arrays, lists, dicts, etc.) by building a deterministic cache key from a deep, content-based representation of every argument.

Cache key construction#

Each argument is converted to a hashable token via _make_token():

  • Hashable scalars (int, float, str, …) -> used directly.

  • numpy.ndarray -> (shape, dtype, sha256_digest) tuple built from a SHA-256 hash of the raw buffer so the key captures the full content without retaining a copy of the array bytes.

  • list / tuple -> recursively tokenised element-by-element.

  • dict -> sorted-items tuple of (key_token, value_token) pairs.

  • Anything else -> falls back to repr().

The final key is a (args_token, sorted_kwargs_token) tuple that is hashed by a standard Python dict.

Thread safety#

A threading.Lock serialises reads and writes to the internal cache dict. To prevent thundering-herd duplicate computation, a sentinel is inserted under the key before the decorated function runs. A per-key threading.Event is used so that concurrent callers block efficiently (no busy-wait) until the real result is stored. Each unique input is computed exactly once even under contention.

Per-instance caching#

When cache_results is applied to an instance method (first parameter named self), it automatically returns a _PerInstanceCacheDescriptor. This can also be requested explicitly via per_instance=True. The descriptor installs a dedicated cached callable on each instance the first time the method is accessed, ensuring that two distinct instances of the same class (or of different subclasses) can never share a cache entry. self is never included in the cache key and no reference to the instance is held in the cache dictionary.

For classes that use __slots__ without __dict__, the descriptor falls back to a descriptor-level mapping keyed by id(obj). If the instance supports weak references, cleanup is automatic; otherwise the mapping entry persists for the lifetime of the descriptor (bounded and equivalent to the prior behaviour).

cache_results([func, maxsize, ...])

Decorator that caches function results for identical arguments.

_PerInstanceCacheDescriptor(fn, maxsize, ...)

Non-data descriptor that provides per-instance caching for instance methods.