Source code for earthkit.utils.decorators._experimental

# (C) Copyright 2026 ECMWF.
#
# This software is licensed under the terms of the Apache Licence Version 2.0
# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
# In applying this licence, ECMWF does not waive the privileges and immunities
# granted to it by virtue of its status as an intergovernmental organisation
# nor does it submit to any jurisdiction.

import warnings
from functools import wraps

__all__ = ["ExperimentalWarning", "experimental"]

# This prefix enables silencing via PYTHONWARNINGS message filters, e.g.
#   PYTHONWARNINGS='ignore:earthkit experimental:'
# Category‑based filters are brittle for non‑stdlib warnings and can fail
# easily (see https://github.com/python/cpython/issues/66733).
# A short, stable prefix ensures reliable re.match‑based filtering.
# In‑code filtering remains preferred:
#   warnings.filterwarnings("ignore", category=ExperimentalWarning)
_WARNING_PREFIX = "earthkit experimental:"

_DEFAULT_DOCS_MESSAGE = "**Experimental API**: may change or be removed without notice."


[docs] class ExperimentalWarning(UserWarning): """Warning category for experimental API usage."""
[docs] def experimental( obj=None, *, msg=_DEFAULT_DOCS_MESSAGE, warn_runtime=True, ): """Mark a function as experimental. This signals to users that the decorated function is considered experimental and may change or be removed without notice. Prepends a Sphinx ``.. warning::`` directive to the function's docstring and, when *warn_runtime* is ``True``, emits an :class:`ExperimentalWarning` at runtime each time the function is called. Parameters ---------- obj : callable or None When used as a bare decorator (``@experimental``), *obj* is the decorated function. When used with arguments (``@experimental(...)``), *obj* is ``None`` and a decorator is returned. msg : str, optional Text inserted into the Sphinx ``.. warning::`` directive. If multi-line, each line should be plain unindented text. warn_runtime : bool, optional If ``True`` (default), emit an :class:`ExperimentalWarning` on each call. If ``False``, only the docstring is modified and the original function is returned. Notes ----- The Sphinx ``.. warning::`` directive is only rendered by documentation tools that import and execute the decorator during the build process. This works with ``sphinx.ext.autodoc`` but not with ``Sphinx AutoAPI``, which generates documentation from static code analysis without importing modules. To silence runtime warnings, use any of Python's standard mechanisms:: import warnings from earthkit.utils.decorators.experimental import ExperimentalWarning warnings.filterwarnings("ignore", category=ExperimentalWarning) Or via the environment variable ``PYTHONWARNINGS``:: export PYTHONWARNINGS="ignore:earthkit experimental:" Examples -------- >>> from earthkit.utils.decorators.experimental import experimental >>> @experimental ... def compute(): ... return 42 ... """ # Every line of msg must be indented under the directive or reST silently drops it. indented = "\n".join(" " + line for line in msg.splitlines()) warning_block = f".. warning::\n{indented}\n" def decorate(target): doc = (target.__doc__ or "").strip() target.__doc__ = warning_block + ("\n" + doc if doc else "") if not warn_runtime: return target @wraps(target) def wrapper(*args, **kwargs): warnings.warn( f"{_WARNING_PREFIX} {target.__qualname__} may change or be removed without notice.", category=ExperimentalWarning, stacklevel=2, ) return target(*args, **kwargs) return wrapper if obj is None: return decorate return decorate(obj)