From e736e3cb3f7bcc1fd611e1b5777b1e2545b90126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugues=20de=20Saxc=C3=A9?= Date: Sun, 2 Nov 2025 15:35:43 -0500 Subject: [PATCH 1/2] fix: replace deprecated pkg_resources --- tensorboard/default.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/tensorboard/default.py b/tensorboard/default.py index 7e6711337a..c28b98e669 100644 --- a/tensorboard/default.py +++ b/tensorboard/default.py @@ -24,10 +24,9 @@ for less repetition. """ - import logging -import pkg_resources +from importlib import metadata from tensorboard.plugins.audio import audio_plugin from tensorboard.plugins.core import core_plugin @@ -119,8 +118,6 @@ def get_dynamic_plugins(): [1]: https://packaging.python.org/specifications/entry-points/ """ return [ - entry_point.resolve() - for entry_point in pkg_resources.iter_entry_points( - "tensorboard_plugins" - ) + entry_point.load() + for entry_point in metadata.entry_points().select(group="tensorboard_plugins") ] From 1d7a674c7fd04ae9792973dce96a7e1c887059d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugues=20de=20Saxc=C3=A9?= Date: Mon, 3 Nov 2025 04:18:31 -0500 Subject: [PATCH 2/2] fix: replace all pkg_resources refs --- tensorboard/BUILD | 8 -------- tensorboard/data/BUILD | 1 - tensorboard/data/server_ingester.py | 19 +++++-------------- tensorboard/default_test.py | 23 +++++++++++++---------- tensorboard/pip_package/requirements.txt | 2 +- tensorboard/version_test.py | 15 ++++----------- 6 files changed, 23 insertions(+), 45 deletions(-) diff --git a/tensorboard/BUILD b/tensorboard/BUILD index 405d6b8d99..57ec500188 100644 --- a/tensorboard/BUILD +++ b/tensorboard/BUILD @@ -303,7 +303,6 @@ py_library( srcs = ["default.py"], srcs_version = "PY3", deps = [ - "//tensorboard:expect_pkg_resources_installed", "//tensorboard/backend:experimental_plugin", "//tensorboard/plugins/audio:audio_plugin", "//tensorboard/plugins/core:core_plugin", @@ -341,7 +340,6 @@ py_test( deps = [ ":default", ":test", - "//tensorboard:expect_pkg_resources_installed", "//tensorboard/plugins:base_plugin", ], ) @@ -361,7 +359,6 @@ py_test( deps = [ ":test", ":version", - "//tensorboard:expect_pkg_resources_installed", ], ) @@ -455,11 +452,6 @@ py_library(name = "expect_absl_logging_installed") # `pip install absl-py` py_library(name = "expect_absl_testing_absltest_installed") -# This is a dummy rule used as a pkg-resources dependency in open-source. -# We expect pkg-resources to already be installed on the system, e.g., via -# `pip install setuptools`. -py_library(name = "expect_pkg_resources_installed") - # This is a dummy rule used as a pandas dependency in open-source. # We expect pandas to already be installed on the system, e.g. via # `pip install pandas`. diff --git a/tensorboard/data/BUILD b/tensorboard/data/BUILD index 0a3337af1a..94963e9e4e 100644 --- a/tensorboard/data/BUILD +++ b/tensorboard/data/BUILD @@ -62,7 +62,6 @@ py_library( ":grpc_provider", ":ingester", "//tensorboard:expect_grpc_installed", - "//tensorboard:expect_pkg_resources_installed", "//tensorboard/data/proto:protos_all_py_pb2", "//tensorboard/util:tb_logging", ], diff --git a/tensorboard/data/server_ingester.py b/tensorboard/data/server_ingester.py index fb0d2fc59c..4884ab3050 100644 --- a/tensorboard/data/server_ingester.py +++ b/tensorboard/data/server_ingester.py @@ -22,7 +22,7 @@ import time import grpc -import pkg_resources +from packaging.version import parse as parse_version from tensorboard.data import grpc_provider from tensorboard.data import ingester @@ -152,10 +152,7 @@ def start(self): if popen.poll() is not None: msg = (_maybe_read_file(error_file_path) or "").strip() if not msg: - msg = ( - "exited with %d; check stderr for details" - % popen.poll() - ) + msg = "exited with %d; check stderr for details" % popen.poll() raise DataServerStartupError(msg) logger.info("Polling for data server port (attempt %d)", i) port_file_contents = _maybe_read_file(port_file_path) @@ -230,11 +227,7 @@ def __init__(self, path, version): it's on you to make sure that it's up to date. """ self._path = path - self._version = ( - pkg_resources.parse_version(version) - if version is not None - else version - ) + self._version = parse_version(version) if version is not None else version @property def path(self): @@ -260,7 +253,7 @@ def at_least_version(self, required_version): """ if self._version is None: return True - return self._version >= pkg_resources.parse_version(required_version) + return self._version >= parse_version(required_version) def get_server_binary(): @@ -287,9 +280,7 @@ def get_server_binary(): else: pkg_result = tensorboard_data_server.server_binary() version = tensorboard_data_server.__version__ - logging.info( - "Server binary (from Python package v%s): %s", version, pkg_result - ) + logging.info("Server binary (from Python package v%s): %s", version, pkg_result) if pkg_result is None: raise NoDataServerError( "TensorBoard data server not supported on this platform." diff --git a/tensorboard/default_test.py b/tensorboard/default_test.py index e131be9ea6..246c92bd50 100644 --- a/tensorboard/default_test.py +++ b/tensorboard/default_test.py @@ -14,10 +14,9 @@ # ============================================================================== """Unit tests for `tensorboard.default`.""" - from unittest import mock -import pkg_resources +from importlib import metadata from tensorboard import default from tensorboard.plugins import base_plugin @@ -30,7 +29,7 @@ class FakePlugin(base_plugin.TBPlugin): plugin_name = "fake" -class FakeEntryPoint(pkg_resources.EntryPoint): +class FakeEntryPoint(metadata.EntryPoint): """EntryPoint class that fake loads FakePlugin.""" @classmethod @@ -40,10 +39,10 @@ def create(cls): Returns: instance of FakeEntryPoint """ - return cls("foo", "bar") + return cls("foo", "bar", "tensorboard_plugins") - def resolve(self): - """Returns FakePlugin instead of resolving module. + def load(self): + """Returns FakePlugin instead of loading module. Returns: FakePlugin @@ -52,13 +51,17 @@ def resolve(self): class DefaultTest(test.TestCase): - @mock.patch.object(pkg_resources, "iter_entry_points") - def test_get_dynamic_plugin(self, mock_iter_entry_points): - mock_iter_entry_points.return_value = [FakeEntryPoint.create()] + @mock.patch.object(metadata, "entry_points") + def test_get_dynamic_plugin(self, mock_entry_points): + fake_eps = [FakeEntryPoint.create()] + mock_entry_points.return_value.select.return_value = fake_eps actual_plugins = default.get_dynamic_plugins() - mock_iter_entry_points.assert_called_with("tensorboard_plugins") + mock_entry_points.assert_called_once() + mock_entry_points.return_value.select.assert_called_with( + group="tensorboard_plugins" + ) self.assertEqual(actual_plugins, [FakePlugin]) diff --git a/tensorboard/pip_package/requirements.txt b/tensorboard/pip_package/requirements.txt index ac31021fbb..24e8805624 100644 --- a/tensorboard/pip_package/requirements.txt +++ b/tensorboard/pip_package/requirements.txt @@ -33,6 +33,6 @@ pillow # 4.24.0 had an issue that broke our tests, so we should avoid that release: # https://github.com/protocolbuffers/protobuf/issues/13485 protobuf >= 3.19.6, != 4.24.0 -setuptools >= 41.0.0 # Note: provides pkg_resources as well as setuptools +setuptools >= 41.0.0 tensorboard-data-server >= 0.7.0, < 0.8.0 werkzeug >= 1.0.1 diff --git a/tensorboard/version_test.py b/tensorboard/version_test.py index dbfd59b0f0..dd1a5cddba 100644 --- a/tensorboard/version_test.py +++ b/tensorboard/version_test.py @@ -13,7 +13,7 @@ # limitations under the License. -import pkg_resources +from packaging.version import parse as parse_version from tensorboard import test as tb_test from tensorboard import version @@ -22,21 +22,14 @@ class VersionTest(tb_test.TestCase): def test_valid_pep440_version(self): """Ensure that our version is PEP 440-compliant.""" - # pkg_resources.parse_version() doesn't have a public return type, - # so we get a handle to it by parsing known good and bad versions. - # - # Note: depending on the version of the module (which is bundled - # with setuptools), when called with a non-compliant version, it - # either returns a `LegacyVersion` (setuptools < 66) or raises an - # `InvalidVersion` exception (setuptools >= 66). Handle both cases. - compliant_version = pkg_resources.parse_version("1.0.0") + compliant_version = parse_version("1.0.0") try: - legacy_version = pkg_resources.parse_version("arbitrary string") + legacy_version = parse_version("arbitrary string") except Exception: legacy_version = None self.assertNotEqual(type(compliant_version), type(legacy_version)) - tensorboard_version = pkg_resources.parse_version(version.VERSION) + tensorboard_version = parse_version(version.VERSION) self.assertIsInstance(tensorboard_version, type(compliant_version))