From 5aae35a7d3b6a3959263b1df656aeb794bd688c5 Mon Sep 17 00:00:00 2001 From: Padraic Shafer <76011594+padraic-shafer@users.noreply.github.com> Date: Fri, 15 Mar 2024 10:30:17 -0700 Subject: [PATCH] Ignore DeprecationWarning from Pandas via cachey (#682) * Ignore Pandas DeprecationWarning from cachey.nbytes() * Ignore Pandas DeprecationWarning in tests? * Subclass warnings.catch_warnings for backward compatibility * Make catch_warning_msg a utility; use specialized instance forPandas block warning --- tiled/_tests/test_writing.py | 5 +++++ tiled/server/object_cache.py | 21 ++++++++++++++++++-- tiled/utils.py | 38 ++++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 2 deletions(-) diff --git a/tiled/_tests/test_writing.py b/tiled/_tests/test_writing.py index 6c7c37cce..7b335fab4 100644 --- a/tiled/_tests/test_writing.py +++ b/tiled/_tests/test_writing.py @@ -18,6 +18,8 @@ from ..client import Context, from_context, record_history from ..queries import Key from ..server.app import build_app + +# from ..server.object_cache import WARNING_PANDAS_BLOCKS from ..structures.core import Spec from ..structures.data_source import DataSource from ..structures.sparse import COOStructure @@ -111,6 +113,7 @@ def test_write_array_chunked(tree): assert result.specs == specs +# @pytest.mark.filterwarnings(f"ignore:{WARNING_PANDAS_BLOCKS}:DeprecationWarning") def test_write_dataframe_full(tree): with Context.from_app( build_app(tree, validation_registry=validation_registry) @@ -136,6 +139,7 @@ def test_write_dataframe_full(tree): assert result.specs == specs +# @pytest.mark.filterwarnings(f"ignore:{WARNING_PANDAS_BLOCKS}:DeprecationWarning") def test_write_dataframe_partitioned(tree): with Context.from_app( build_app(tree, validation_registry=validation_registry) @@ -389,6 +393,7 @@ async def test_delete_non_empty_node(tree): client.delete("a") +# @pytest.mark.filterwarnings(f"ignore:{WARNING_PANDAS_BLOCKS}:DeprecationWarning") @pytest.mark.asyncio async def test_write_in_container(tree): "Create a container and write a structure into it." diff --git a/tiled/server/object_cache.py b/tiled/server/object_cache.py index c6813aadb..8ad55a972 100644 --- a/tiled/server/object_cache.py +++ b/tiled/server/object_cache.py @@ -13,6 +13,8 @@ import cachey from dask.callbacks import Callback +from ..utils import catch_warning_msg + if __debug__: import logging @@ -23,6 +25,19 @@ logger.addHandler(handler) +# See https://github.com/bluesky/tiled/issues/675#issuecomment-1983581882 +WARNING_PANDAS_BLOCKS = ( + "DataFrame._data is deprecated and will be removed in a future version. " + "Use public APIs instead." +) + + +def catch_pandas_blocks_warning(): + return catch_warning_msg( + action="ignore", message=WARNING_PANDAS_BLOCKS, category=DeprecationWarning + ) + + class _NO_CACHE_SENTINEL: def __init__(self): self.dask_context = contextlib.nullcontext() @@ -129,7 +144,8 @@ def put(self, key, value, cost, nbytes=None): Computed (with best effort) if not provided. """ if nbytes is None: - nbytes = self._cache.get_nbytes(value) + with catch_pandas_blocks_warning(): + nbytes = self._cache.get_nbytes(value) logger.debug("Store %r (cost=%.3f, nbytes=%d)", key, cost, nbytes) self._cache.put(key, value, cost, nbytes=nbytes) @@ -196,7 +212,8 @@ def _posttask(self, key, value, dsk, state, id): if deps: duration += max(self.durations.get(k, 0) for k in deps) self.durations[key] = duration - nb = self._nbytes(value) + with catch_pandas_blocks_warning(): + nb = self._nbytes(value) self.cache.put(("dask", *key), value, cost=duration, nbytes=nb) def _finish(self, dsk, state, errored): diff --git a/tiled/utils.py b/tiled/utils.py index 17f7c38e7..558017c3f 100644 --- a/tiled/utils.py +++ b/tiled/utils.py @@ -3,6 +3,7 @@ import collections.abc import contextlib import enum +import functools import importlib import importlib.util import inspect @@ -12,6 +13,7 @@ import re import sys import threading +import warnings from pathlib import Path from typing import Any, Callable from urllib.parse import urlparse, urlunparse @@ -709,3 +711,39 @@ def ensure_uri(uri_or_path): mutable[1] = "localhost" uri_str = urlunparse(mutable) return str(uri_str) + + +class catch_warning_msg(warnings.catch_warnings): + """Backward compatible version of catch_warnings for python <3.11. + + Allows regex matching of warning message, as in filterwarnings(). + Note that this context manager is not thread-safe. + https://docs.python.org/3.12/library/warnings.html#warnings.catch_warnings + """ + + def __init__( + self, + *, + action="", + message="", + category=Warning, + module="", + lineno=0, + append=False, + record=False, + ): + super().__init__(record=record) + self.apply_filter = functools.partial( + warnings.filterwarnings, + action, + message=message, + category=category, + module=module, + lineno=lineno, + append=append, + ) + + def __enter__(self): + super().__enter__() + self.apply_filter() + return self