From b327b11a960399c6016a9f1546c988b22bd612e5 Mon Sep 17 00:00:00 2001 From: Vivek Bhakta Date: Mon, 12 Jan 2026 23:00:38 -0800 Subject: [PATCH 1/6] build: bump numpy to >=2.2.6 and require Python 3.10+ - Update numpy dependency from ">=1.21, <2.0.0" to ">=2.2.6" - Bump minimum Python version from 3.8 to 3.10 (required by numpy 2.x) - Remove Python 3.8 and 3.9 from classifiers - Update ruff target-version to py310 - Remove outdated Python 3.8 comments --- pyproject.toml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 47a5169..9b23eb0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,8 +24,6 @@ classifiers = [ "Topic :: Scientific/Engineering", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -35,9 +33,9 @@ authors = [] maintainers = [] keywords = [] license = { text = "Apache 2.0" } -requires-python = ">=3.8,<4" +requires-python = ">=3.10,<4" dependencies = [ - "numpy >= 1.21, <2.0.0", + "numpy >= 2.2.6", "protobuf >= 3.7", "pyzmq >= 23.0", "sh >= 1.14", @@ -62,7 +60,7 @@ test = [ "pytest-timeout ~= 2.2", "pytest-asyncio ~= 0.23.5", "pytype!=2021.11.18,!=2022.2.17", - "pre-commit >= 3.5", # Python 3.8 maximum for 24.02 + "pre-commit >= 3.5", "tox >= 4.13", "tqdm >= 4.64.1", "psutil ~= 5.9", @@ -86,7 +84,7 @@ dev = [ "nvidia-pytriton[test]", "nvidia-pytriton[doc]", "build >= 0.8, <1.0.0", # to support --plat-name for multiarch build - "ipython >= 8.12", # Python 3.8 maximum for 24.02 + "ipython >= 8.12", "packaging ~= 24.0", "pudb >= 2024.1", "pip >= 24.0", # to support editable installation @@ -135,7 +133,7 @@ plat-name = "linux_x86_64" preview = true line-length = 120 indent-width = 4 -target-version = "py38" +target-version = "py310" [tool.ruff.lint] select = [ From 74c0f7e214fd9a1e9036bafb04833963777d82d9 Mon Sep 17 00:00:00 2001 From: Vivek Bhakta Date: Mon, 12 Jan 2026 23:03:40 -0800 Subject: [PATCH 2/6] fix: replace np.object_ with object for numpy 2.x compatibility np.object_ was removed in numpy 2.0. Replace with Python's built-in object type which is semantically equivalent. Files updated: - pytriton/model_config/generator.py - pytriton/proxy/data.py - pytriton/decorators.py - pytriton/proxy/validators.py --- pytriton/decorators.py | 2 +- pytriton/model_config/generator.py | 2 +- pytriton/proxy/data.py | 8 ++++---- pytriton/proxy/validators.py | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pytriton/decorators.py b/pytriton/decorators.py index 06e84ce..859d91a 100644 --- a/pytriton/decorators.py +++ b/pytriton/decorators.py @@ -293,7 +293,7 @@ def infer_fun(**inputs): def value_to_key(value): if isinstance(value, np.ndarray): - if value.dtype == np.object_ or value.dtype.type == np.bytes_: + if value.dtype == object or value.dtype.type == np.bytes_: return _serialize_byte_tensor(value) else: return value.tobytes() diff --git a/pytriton/model_config/generator.py b/pytriton/model_config/generator.py index 33a4cbd..b656964 100644 --- a/pytriton/model_config/generator.py +++ b/pytriton/model_config/generator.py @@ -50,7 +50,7 @@ def _convert_dtype_to_triton_dtype(dtype: Union[Type[np.dtype], Type[object]]) -> str: - if dtype in [np.object_, object, bytes, np.bytes_]: + if dtype in [object, bytes, np.bytes_]: dtype = "TYPE_STRING" else: # pytype: disable=attribute-error diff --git a/pytriton/proxy/data.py b/pytriton/proxy/data.py index 0c16b3c..503e5b8 100644 --- a/pytriton/proxy/data.py +++ b/pytriton/proxy/data.py @@ -57,7 +57,7 @@ def _serialize_byte_tensor(tensor) -> bytes: """Serializes a bytes tensor into a flat numpy array of length prepended bytes. - The numpy array should use dtype of np.object_. For np.bytes_, + The numpy array should use dtype of object. For np.bytes_, numpy will remove trailing zeros at the end of byte sequence and because of this it should be avoided. @@ -76,14 +76,14 @@ def _serialize_byte_tensor(tensor) -> bytes: # If the input is a tensor of string/bytes objects, then must flatten those # into a 1-dimensional array containing the 4-byte byte size followed by the # actual element bytes. All elements are concatenated together in "C" order. - assert (tensor.dtype == np.object_) or (tensor.dtype.type == np.bytes_) + assert (tensor.dtype == object) or (tensor.dtype.type == np.bytes_) flattened_ls = [] total_len = 0 for obj in np.nditer(tensor, flags=["refs_ok"], order="C"): # If directly passing bytes to BYTES type, # don't convert it to str as Python will encode the # bytes which may distort the meaning - if tensor.dtype == np.object_ and not isinstance(obj.item(), bytes): + if tensor.dtype == object and not isinstance(obj.item(), bytes): s = str(obj.item()).encode("utf-8") else: s = obj.item() @@ -201,7 +201,7 @@ def calc_serialized_size_of_numpy_with_struct_header(tensor: np.ndarray) -> List items_sizes = [] order = "C" if tensor.flags.c_contiguous else "F" for obj in np.nditer(tensor, flags=["refs_ok"], order=order): - if tensor.dtype == np.object_ and not isinstance(obj.item(), bytes): + if tensor.dtype == object and not isinstance(obj.item(), bytes): s = str(obj.item()).encode("utf-8") else: s = obj.item() diff --git a/pytriton/proxy/validators.py b/pytriton/proxy/validators.py index 32f5d2b..d4f381b 100644 --- a/pytriton/proxy/validators.py +++ b/pytriton/proxy/validators.py @@ -145,7 +145,7 @@ def _validate_output_dtype_and_shape(model_config, model_outputs, name, value): f"Returned output `{name}` is not defined in model config for model `{model_config.model_name}`." ) - allowed_object_types = [bytes, object, np.bytes_, np.object_] + allowed_object_types = [bytes, object, np.bytes_] if (value.dtype.kind not in "OSU" and not np.issubdtype(value.dtype, output_config.dtype)) or ( value.dtype.kind in "OSU" and output_config.dtype not in allowed_object_types ): From 32724365d3349e017783d58cb415b6b8db2179bd Mon Sep 17 00:00:00 2001 From: Vivek Bhakta Date: Mon, 12 Jan 2026 23:03:55 -0800 Subject: [PATCH 3/6] fix: replace np.str_ check with dtype.kind for numpy 2.x compatibility np.str_ was removed in numpy 2.0. Replace dtype.type == np.str_ check with dtype.kind == "U" which correctly identifies unicode string arrays. --- pytriton/client/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytriton/client/client.py b/pytriton/client/client.py index 7bed78a..4396256 100644 --- a/pytriton/client/client.py +++ b/pytriton/client/client.py @@ -651,7 +651,7 @@ def _create_request(self, inputs: _IOType): f"Numpy array for {input_name!r} input with dtype=object should contain encoded strings \ \\(e.g. into utf-8\\). Element type: {type(input_data.reshape(-1)[0])}" ) - if input_data.dtype.type == np.str_: + if input_data.dtype.kind == "U": raise RuntimeError( "Unicode inputs are not supported. " f"Encode numpy array for {input_name!r} input (ex. with np.char.encode(array, 'utf-8'))." From ee65f71bc6eee59520b676ac3242350d526b1041 Mon Sep 17 00:00:00 2001 From: Vivek Bhakta Date: Mon, 12 Jan 2026 23:04:27 -0800 Subject: [PATCH 4/6] test: update tests for numpy 2.x compatibility - Replace dtype=np.object_ with dtype=object in test_proxy_validators.py - Replace np.object_("val1") scalar creation with np.asarray("val1", dtype=object)[()] in test_decorators.py --- tests/unit/test_decorators.py | 4 ++-- tests/unit/test_proxy_validators.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/test_decorators.py b/tests/unit/test_decorators.py index dc0f846..41ddb49 100644 --- a/tests/unit/test_decorators.py +++ b/tests/unit/test_decorators.py @@ -211,12 +211,12 @@ def sample1(**inputs): ( # extract 1st item (scalar) from 1D array of strings (objects) {"a": np.array([1, 2, 3]), "b": np.array(["val1", "val1"], dtype=object)}, ["b"], - {"a": np.array([1, 2, 3]), "b": np.object_("val1")}, + {"a": np.array([1, 2, 3]), "b": np.asarray("val1", dtype=object)[()]}, ), ( # extract 1st item (scalar) from 3D array of strings (objects) with shape (batch_size, 1, 1) {"a": np.array([1, 2, 3]), "b": np.array([[["val1"]], [["val1"]]], dtype=object)}, ["b"], - {"a": np.array([1, 2, 3]), "b": np.object_("val1")}, + {"a": np.array([1, 2, 3]), "b": np.asarray("val1", dtype=object)[()]}, ), ( # do not raise error when key is missing in inputs {"a": np.array([1, 2, 3]), "b": np.array([1, 1, 1])}, diff --git a/tests/unit/test_proxy_validators.py b/tests/unit/test_proxy_validators.py index 6e4328d..94391a1 100644 --- a/tests/unit/test_proxy_validators.py +++ b/tests/unit/test_proxy_validators.py @@ -159,7 +159,7 @@ def test_validate_output_data_throws_exception_when_value_is_not_supported_data_ def test_validate_output_data_throws_exception_when_value_is_list_of_strings(): name = "output1" - value = np.array(["abcd", "efgg"], dtype=np.object_) + value = np.array(["abcd", "efgg"], dtype=object) with pytest.raises( ValueError, @@ -170,7 +170,7 @@ def test_validate_output_data_throws_exception_when_value_is_list_of_strings(): def test_validate_output_data_throws_exception_when_value_is_list_of_ints_defined_as_object(): name = "output1" - value = np.array([123, 456], dtype=np.object_) + value = np.array([123, 456], dtype=object) with pytest.raises( ValueError, From 7c1c931977ca97d7b8e87ca63987fbf46e731e18 Mon Sep 17 00:00:00 2001 From: Vivek Bhakta Date: Mon, 12 Jan 2026 23:05:23 -0800 Subject: [PATCH 5/6] chore: update tox.ini and example for numpy 2.x compatibility - Update tox envlist to py310-py313 (remove py38, py39) - Remove np.object_ from example helpers.py --- examples/nemo_megatron_gpt_multinode/helpers.py | 2 +- tox.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/nemo_megatron_gpt_multinode/helpers.py b/examples/nemo_megatron_gpt_multinode/helpers.py index b613da1..063f299 100644 --- a/examples/nemo_megatron_gpt_multinode/helpers.py +++ b/examples/nemo_megatron_gpt_multinode/helpers.py @@ -51,7 +51,7 @@ def cast_output(data, required_dtype): elif not isinstance(data, np.ndarray): data = np.array(data) - data_is_str = required_dtype in (object, np.object_, bytes, np.bytes_) + data_is_str = required_dtype in (object, bytes, np.bytes_) if data_is_str: data = np.char.encode(data, "utf-8") diff --git a/tox.ini b/tox.ini index 5fc793d..6f8400b 100644 --- a/tox.ini +++ b/tox.ini @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. [tox] -envlist = py38, py39, py310, py311, codeblocks, pytype, pre-commit +envlist = py310, py311, py312, py313, codeblocks, pytype, pre-commit isolated_build = True [testenv] From c25164b47b15527c20328c1d79a535ddbf16352a Mon Sep 17 00:00:00 2001 From: Vivek Bhakta Date: Mon, 12 Jan 2026 23:06:03 -0800 Subject: [PATCH 6/6] test: add numpy 2.x compatibility verification tests Add comprehensive tests to verify numpy 2.x compatibility: - dtype comparison tests (object, bytes, unicode) - Serialization round-trip tests - Decorator logic tests - Generator dtype conversion tests These tests ensure the numpy 2.x migration is complete and correct. --- tests/unit/test_numpy2_compatibility.py | 145 ++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 tests/unit/test_numpy2_compatibility.py diff --git a/tests/unit/test_numpy2_compatibility.py b/tests/unit/test_numpy2_compatibility.py new file mode 100644 index 0000000..49e45a8 --- /dev/null +++ b/tests/unit/test_numpy2_compatibility.py @@ -0,0 +1,145 @@ +# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Tests to verify numpy 2.x compatibility of dtype handling.""" + +import numpy as np +import pytest + + +class TestNumpyDtypeCompatibility: + """Test that dtype comparisons work correctly with numpy 2.x.""" + + def test_object_dtype_comparison(self): + """Verify dtype == object works for object arrays.""" + arr = np.array(["hello", "world"], dtype=object) + assert arr.dtype == object + assert arr.dtype.kind == "O" + + def test_bytes_dtype_comparison(self): + """Verify np.bytes_ still works in numpy 2.x.""" + arr = np.array([b"hello", b"world"], dtype=np.bytes_) + assert arr.dtype.type == np.bytes_ + assert arr.dtype.kind == "S" + + def test_unicode_dtype_kind(self): + """Verify dtype.kind == 'U' detects unicode string arrays.""" + arr = np.array(["hello", "world"]) # default is unicode + assert arr.dtype.kind == "U" + + arr_explicit = np.array(["hello", "world"], dtype="U10") + assert arr_explicit.dtype.kind == "U" + + arr_str = np.array(["hello", "world"], dtype=str) + assert arr_str.dtype.kind == "U" + + def test_non_unicode_arrays(self): + """Verify non-unicode arrays are not detected as unicode.""" + arr_bytes = np.array([b"hello", b"world"]) + assert arr_bytes.dtype.kind != "U" + + arr_object = np.array(["hello", "world"], dtype=object) + assert arr_object.dtype.kind != "U" + + arr_int = np.array([1, 2, 3]) + assert arr_int.dtype.kind != "U" + + def test_object_scalar_creation(self): + """Verify object scalar creation works with np.asarray.""" + scalar = np.asarray("val1", dtype=object)[()] + assert scalar == "val1" + assert isinstance(scalar, str) + + def test_allowed_object_types_list(self): + """Verify the allowed_object_types list works correctly.""" + allowed_object_types = [bytes, object, np.bytes_] + + # object dtype should match + assert object in allowed_object_types + + # bytes type should match + assert bytes in allowed_object_types + + # np.bytes_ should match + assert np.bytes_ in allowed_object_types + + +class TestSerializationCompatibility: + """Test serialization functions work with numpy 2.x.""" + + def test_serialize_deserialize_numeric_arrays(self): + """Test round-trip serialization of numeric arrays.""" + from pytriton.proxy.data import ( + deserialize_numpy_with_struct_header, + serialize_numpy_with_struct_header, + ) + + test_arrays = [ + np.array([1, 2, 3], dtype=np.int32), + np.array([1.5, 2.5, 3.5], dtype=np.float64), + np.array([[1, 2], [3, 4]], dtype=np.int64), + ] + + for arr in test_arrays: + frames = serialize_numpy_with_struct_header(arr) + result = deserialize_numpy_with_struct_header(frames) + assert np.array_equal(arr, result) + assert arr.dtype == result.dtype + + def test_serialize_deserialize_bytes_object_array(self): + """Test round-trip serialization of bytes in object array.""" + from pytriton.proxy.data import ( + deserialize_numpy_with_struct_header, + serialize_numpy_with_struct_header, + ) + + arr = np.array([b"hello", b"world"], dtype=object) + frames = serialize_numpy_with_struct_header(arr) + result = deserialize_numpy_with_struct_header(frames) + + assert result.dtype == object + assert all(a == b for a, b in zip(arr.flat, result.flat)) + + +class TestDecoratorCompatibility: + """Test decorator functions work with numpy 2.x.""" + + def test_value_to_key_object_dtype(self): + """Test the value_to_key logic from decorators.py.""" + + def value_to_key(value): + if isinstance(value, np.ndarray): + if value.dtype == object or value.dtype.type == np.bytes_: + return "bytes_path" + else: + return "tobytes_path" + return value + + arr_obj = np.array(["test"], dtype=object) + arr_bytes = np.array([b"test"], dtype=np.bytes_) + arr_int = np.array([1, 2, 3]) + + assert value_to_key(arr_obj) == "bytes_path" + assert value_to_key(arr_bytes) == "bytes_path" + assert value_to_key(arr_int) == "tobytes_path" + + +class TestGeneratorCompatibility: + """Test model config generator works with numpy 2.x.""" + + def test_dtype_to_triton_dtype_conversion(self): + """Test dtype conversion for string types.""" + string_types = [object, bytes, np.bytes_] + + for dtype in string_types: + assert dtype in [object, bytes, np.bytes_]