def METHOD_NAME(*event_names) -> Callable:
    if any(event_name not in _ALL_CACHE_EVENT_NAMES for event_name in event_names):
        raise ValueError(
            f"Unknown event name[s] passed to the `cache_until` decorator: {event_names}.\n"
            f"Expected: {_ALL_CACHE_EVENT_NAMES}"
        )
    def inner(func):
        if hasattr(func, "cache"):
            raise ValueError(
                f"Cannot cache the {func.__name__} function twice! "
                "Pass multiple event names to the `cache_until` decorator."
            )
        cache: Cache = {} if not debug else DebugCacheDict(func)
        @wraps(func)
        def decorator(*a, **kw):
            if IS_CACHING:
                key: tuple[Any] = (a, _KWARGS_SEPARATOR, *kw.items())
                # Check if the value is in the cache; if we have a cache miss,
                # we return a special singleton object `_NOT_FOUND_IN_CACHE`. This way
                # we can also cache a result of 'None' from a function
                value = cache.get(key, _NOT_FOUND_IN_CACHE)
                if value is not _NOT_FOUND_IN_CACHE:
                    return value
                value = func(*a, **kw)
                # Sanity check: its not perfect and won't cover all cases like ([],)
                # but it should be good enough
                if isinstance(value, list):
                    print(f"Should not cache mutable types! {func.__name__}")
                cache[key] = value
                return value
            return func(*a, **kw)
        # Set the cache on the function so it can be cleared on demand
        # this may be useful for tests
        decorator.cache = cache
        # Register the cache for the given event so it can be cleared
        for event_name in event_names:
            _ALL_CACHE_UNTIL_EVENTS[event_name].add_cache(cache)
        return decorator
    return inner