diff --git a/kernels/src/kernels/backends.py b/kernels/src/kernels/backends.py index c5b82920..cd447342 100644 --- a/kernels/src/kernels/backends.py +++ b/kernels/src/kernels/backends.py @@ -1,7 +1,7 @@ import ctypes import ctypes.util +import logging import re -import warnings from dataclasses import dataclass from typing import ClassVar, Optional, Protocol, runtime_checkable @@ -10,6 +10,8 @@ from kernels.compat import has_torch +logger = logging.getLogger(__name__) + @runtime_checkable class Backend(Protocol): @@ -292,7 +294,7 @@ def _get_cuda() -> Optional[CUDA]: runtime_version = ctypes.c_int(0) result = libcudart.cudaRuntimeGetVersion(ctypes.byref(runtime_version)) if result != 0: - warnings.warn("System has CUDA runtime library, but cannot get runtime version.") + logger.warning("System has CUDA runtime library, but cannot get runtime version.") return None # cudaRuntimeGetVersion encodes the version as (major * 1000 + minor * 10). diff --git a/kernels/src/kernels/cli/benchmark.py b/kernels/src/kernels/cli/benchmark.py index 5e601d58..55cb5040 100644 --- a/kernels/src/kernels/cli/benchmark.py +++ b/kernels/src/kernels/cli/benchmark.py @@ -1,13 +1,13 @@ import hashlib import importlib.util import json +import logging import os import platform import random import subprocess import sys import time -import warnings from dataclasses import dataclass from pathlib import Path from typing import Any @@ -24,6 +24,8 @@ from kernels.benchmark import Benchmark from kernels.hf_hub import _get_hf_api +logger = logging.getLogger(__name__) + MISSING_DEPS: list[str] = [] try: @@ -709,7 +711,7 @@ def run_benchmark( if is_local: if repo_id.count("/") == 1 and not repo_id.startswith(("./", "../")): - warnings.warn( + logger.warning( f"'{repo_id}' exists locally but looks like a repo_id. Use './{repo_id}' to be explicit.", stacklevel=2, ) diff --git a/kernels/src/kernels/hf_hub.py b/kernels/src/kernels/hf_hub.py index c61a74e6..ddb30951 100644 --- a/kernels/src/kernels/hf_hub.py +++ b/kernels/src/kernels/hf_hub.py @@ -1,6 +1,6 @@ +import logging import os import platform -import warnings from dataclasses import dataclass from huggingface_hub import HfApi, constants @@ -9,6 +9,8 @@ from kernels.backends import _select_backend from kernels.compat import has_torch, has_tvm_ffi +logger = logging.getLogger(__name__) + def _platform() -> str: cpu = platform.machine() @@ -116,7 +118,7 @@ def _check_trust_remote_code(repo_id: str, local_files_only: bool, trust_remote_ # Publisher trust cannot be verified offline. The user opted into # offline mode and the kernel must already be in the local cache, # so trust was established when it was originally downloaded. - warnings.warn( + logger.warning( f"Skipping publisher trust check for '{repo_id}' because Hugging Face Hub is in offline mode.", stacklevel=3, ) diff --git a/kernels/src/kernels/layer/func.py b/kernels/src/kernels/layer/func.py index 8518ef42..77040ee2 100644 --- a/kernels/src/kernels/layer/func.py +++ b/kernels/src/kernels/layer/func.py @@ -1,5 +1,5 @@ import functools -import warnings +import logging from pathlib import Path from types import ModuleType from typing import TYPE_CHECKING, Protocol, Type @@ -27,6 +27,8 @@ if TYPE_CHECKING: from torch import nn +logger = logging.getLogger(__name__) + class FuncRepositoryProtocol(RepositoryProtocol, Protocol): @property @@ -81,9 +83,8 @@ def __init__( version: int | None = None, trust_remote_code: bool | list[str] = False, ): - warnings.warn( + logger.warning( "FuncRepository is deprecated and will be removed in kernels 0.17. Use LayerRepository instead.", - DeprecationWarning, stacklevel=2, ) @@ -181,9 +182,8 @@ def __init__( *, func_name: str, ): - warnings.warn( + logger.warning( "LocalFuncRepository is deprecated and will be removed in kernels 0.17. Use LocalLayerRepository instead.", - DeprecationWarning, stacklevel=2, ) @@ -258,9 +258,8 @@ def forward(self, x): # model = kernelize(model, mode=Mode.TRAINING | Mode.TORCH_COMPILE, device="cuda") ``` """ - warnings.warn( + logger.warning( "use_kernel_func_from_hub is deprecated and will be removed in kernels 0.17. Use [`use_kernel_forward_from_hub`] instead.", - DeprecationWarning, stacklevel=2, ) @@ -299,9 +298,8 @@ def __init__( Construct a function repository. """ - warnings.warn( + logger.warning( "LockedFuncRepository is deprecated and will be removed in kernels 0.17. Use LockedLayerRepository instead.", - DeprecationWarning, stacklevel=2, ) diff --git a/kernels/src/kernels/layer/layer.py b/kernels/src/kernels/layer/layer.py index 8b8cac86..10a4409c 100644 --- a/kernels/src/kernels/layer/layer.py +++ b/kernels/src/kernels/layer/layer.py @@ -3,7 +3,6 @@ import functools import inspect import logging -import warnings from inspect import Parameter, Signature from pathlib import Path from types import MethodType, ModuleType @@ -34,6 +33,8 @@ if TYPE_CHECKING: from torch import nn +logger = logging.getLogger(__name__) + class LayerRepositoryProtocol(RepositoryProtocol, Protocol): @property @@ -478,7 +479,7 @@ def kernelize_layer(module: "nn.Module", *, mode: Mode, device_type: Device, use kernel = _KERNEL_MAPPING.get().get(str(layer_name)) if kernel is None: - warnings.warn( + logger.warning( "\n" f"No kernel mapping found for layer `{layer_name}`. " f"Check if the layer name matches one of the kernels in the mapping or add the kernel " diff --git a/kernels/src/kernels/status.py b/kernels/src/kernels/status.py index ce2b9f2b..418ae7f5 100644 --- a/kernels/src/kernels/status.py +++ b/kernels/src/kernels/status.py @@ -1,4 +1,4 @@ -import warnings +import logging from dataclasses import dataclass from typing import Union @@ -9,6 +9,8 @@ from kernels.compat import tomllib +logger = logging.getLogger(__name__) + @strict @dataclass @@ -71,11 +73,7 @@ def resolve_status(api: HfApi, repo_id: str, revision: str) -> tuple[str, str]: # In the case of a redirect, return the destination repo and revision if isinstance(status, Redirect): - warnings.warn( - f"'{repo_id}' redirected to '{status.destination}'", - UserWarning, - stacklevel=2, - ) + logger.warning(f"'{repo_id}' redirected to '{status.destination}'", stacklevel=2) return status.destination, status.revision return repo_id, revision diff --git a/kernels/src/kernels/validate.py b/kernels/src/kernels/validate.py index 9df7eba9..02306e08 100644 --- a/kernels/src/kernels/validate.py +++ b/kernels/src/kernels/validate.py @@ -1,4 +1,4 @@ -import warnings +import logging from dataclasses import dataclass from typing import Protocol @@ -9,6 +9,8 @@ from kernels.backends import _backend from kernels.python_deps import validate_dependencies +logger = logging.getLogger(__name__) + class MetadataValidator(Protocol): """Metadata validator for a kernel build variant.""" @@ -49,7 +51,7 @@ def validate_metadata(self, *, metadata: Metadata, variant: str) -> None: if builder_git is not None and builder_git.dirty: dirty_sources.append("kernel-builder") - warnings.warn( + logger.warning( f"Kernel '{metadata.name}' variant '{variant}' was built from a dirty " f"git tree ({', '.join(dirty_sources)} had uncommitted changes). Its " "recorded git revision does not fully identify the sources it was built " diff --git a/kernels/src/kernels/variants.py b/kernels/src/kernels/variants.py index 443cd921..f5ec9168 100644 --- a/kernels/src/kernels/variants.py +++ b/kernels/src/kernels/variants.py @@ -3,7 +3,6 @@ import re import sys import sysconfig -import warnings from dataclasses import dataclass from pathlib import Path from typing import ClassVar @@ -26,6 +25,8 @@ ) from kernels.compat import has_torch, has_tvm_ffi +logger = logging.getLogger(__name__) + # Metal kernels are currently built with `-std=metal4.0`, which requires # macOS 26 or later. Until the macOS/Metal version is encoded in the build # variant, reject Metal arch variants on older systems, since they fail to @@ -384,11 +385,10 @@ def _check_variants( # Prefilter all arch kernels on free-threaded Python pre-3.15, since # they do not support the stable ABI. if is_unsupported_free_threaded: - warnings.warn( + logger.warning( "Arch kernels use the stable ABI, which is not supported on free-threaded " "Python before version 3.15. Arch kernels will not be used. Consider using " "a non-free-threaded interpreter, or upgrade to Python 3.15+.", - UserWarning, stacklevel=2, ) variants = [v for v in variants if not isinstance(v, ArchVariant)] diff --git a/kernels/tests/test_func.py b/kernels/tests/test_func.py index c72d442f..0fff7e5c 100644 --- a/kernels/tests/test_func.py +++ b/kernels/tests/test_func.py @@ -1,3 +1,4 @@ +import logging from pathlib import Path import pytest @@ -41,7 +42,6 @@ def identity(x): assert isinstance(identity, nn.Module) -@pytest.mark.filterwarnings("ignore:.*will be removed in kernels 0.17:DeprecationWarning") def test_deprecated_decorator(): @use_kernel_func_from_hub("identity_func") def identity(x): @@ -51,13 +51,11 @@ def identity(x): assert isinstance(identity, nn.Module) -@pytest.mark.filterwarnings("ignore:.*will be removed in kernels 0.17:DeprecationWarning") def test_deprecated_func_repository_requires_version_or_revision(): with pytest.raises(ValueError, match="Either a revision or a version must be specified"): FuncRepository("kernels-test/flattened-build", func_name="silu_and_mul") -@pytest.mark.filterwarnings("ignore:.*will be removed in kernels 0.17:DeprecationWarning") def test_deprecated_func_repository(device): model = SurpriseMe() @@ -116,7 +114,6 @@ def test_kernel_func_with_layer(): assert model(x) is x -@pytest.mark.filterwarnings("ignore:.*will be removed in kernels 0.17:DeprecationWarning") def test_deprecated_local_kernel_func(device): model = SurpriseMe() @@ -145,31 +142,29 @@ def test_deprecated_local_kernel_func(device): assert model(x) is x -def test_deprecated_kernel_func(): - with pytest.deprecated_call(match="kernels 0.17"): +def test_deprecated_kernel_func(caplog): + with caplog.at_level(logging.WARNING, logger="kernels.layer.func"): FuncRepository("kernels-test/flattened-build", func_name="silu_and_mul", version=1) - project_dir = Path(__file__).parent / "layer_locking" - with pytest.deprecated_call(match="kernels 0.17"): + project_dir = Path(__file__).parent / "layer_locking" LockedFuncRepository( "kernels-test/versions", func_name="version", lockfile=project_dir / "kernels.lock", ) - with pytest.deprecated_call(match="kernels 0.17"): LocalFuncRepository( # We are never loading the kernel, so we can just use an invalid path. repo_path=Path("."), func_name="silu_and_mul", ) - with pytest.deprecated_call(match="kernels 0.17"): - @use_kernel_func_from_hub("deprecated") def deprecated_func(x): return x + assert caplog.text.count("kernels 0.17") == 4 + def test_use_kernelized_func_used_on_non_kernelized_func(): def not_kernelized(x): diff --git a/kernels/tests/test_validate.py b/kernels/tests/test_validate.py index 238ecd85..5d9c7418 100644 --- a/kernels/tests/test_validate.py +++ b/kernels/tests/test_validate.py @@ -1,4 +1,5 @@ import json +import logging import re from pathlib import Path @@ -47,19 +48,22 @@ def _metadata_with_provenance(provenance): @pytest.mark.parametrize("provenance", [None, CLEAN_PROVENANCE]) -def test_dirty_validator_does_not_warn_when_clean(recwarn, provenance): - DirtyValidator().validate_metadata(metadata=_metadata_with_provenance(provenance), variant="test-variant") - assert len(recwarn) == 0 +def test_dirty_validator_does_not_warn_when_clean(caplog, provenance): + with caplog.at_level(logging.WARNING, logger="kernels.validate"): + DirtyValidator().validate_metadata(metadata=_metadata_with_provenance(provenance), variant="test-variant") + assert not caplog.records -def test_dirty_validator_warns_on_dirty_kernel_source(): - with pytest.warns(UserWarning, match="dirty git tree"): +def test_dirty_validator_warns_on_dirty_kernel_source(caplog): + with caplog.at_level(logging.WARNING, logger="kernels.validate"): DirtyValidator().validate_metadata(metadata=_metadata_with_provenance(DIRTY_KERNEL), variant="test-variant") + assert "dirty git tree" in caplog.text -def test_dirty_validator_names_dirty_sources(): - with pytest.warns(UserWarning, match="kernel-builder"): +def test_dirty_validator_names_dirty_sources(caplog): + with caplog.at_level(logging.WARNING, logger="kernels.validate"): DirtyValidator().validate_metadata(metadata=_metadata_with_provenance(DIRTY_BUILDER), variant="test-variant") + assert "kernel-builder" in caplog.text def test_dirty_validator_is_enabled_by_default():