From f80bbf15fbeef30335530cebf68aa79fd79aebd1 Mon Sep 17 00:00:00 2001 From: Bartok9 Date: Wed, 22 Jul 2026 05:22:36 -0400 Subject: [PATCH] fix(python): validate dimensions in list_to_2d_float_array Reject negative sizes and length mismatches with an explicit ValueError instead of an opaque numpy reshape failure. Soft-empty 0x0 buffers stay supported. Offline unit tests included (no simulator). Signed-off-by: Bartok9 --- PythonClient/airsim/tests/__init__.py | 1 + .../airsim/tests/test_utils_reshape.py | 52 +++++++++++++++++++ PythonClient/airsim/utils.py | 24 ++++++++- 3 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 PythonClient/airsim/tests/__init__.py create mode 100644 PythonClient/airsim/tests/test_utils_reshape.py diff --git a/PythonClient/airsim/tests/__init__.py b/PythonClient/airsim/tests/__init__.py new file mode 100644 index 0000000000..35411ac0d2 --- /dev/null +++ b/PythonClient/airsim/tests/__init__.py @@ -0,0 +1 @@ +# unit tests diff --git a/PythonClient/airsim/tests/test_utils_reshape.py b/PythonClient/airsim/tests/test_utils_reshape.py new file mode 100644 index 0000000000..d3274b68db --- /dev/null +++ b/PythonClient/airsim/tests/test_utils_reshape.py @@ -0,0 +1,52 @@ +"""Offline tests for list_to_2d_float_array guards.""" +import importlib.util +import os +import sys +import types as pytypes +import unittest + +import numpy as np + +_HERE = os.path.dirname(os.path.abspath(__file__)) +_UTILS = os.path.join(os.path.dirname(_HERE), "utils.py") + + +def _load_utils(): + pkg = pytypes.ModuleType("airsim") + pkg.__path__ = [os.path.dirname(_HERE)] + sys.modules["airsim"] = pkg + stub = pytypes.ModuleType("airsim.types") + sys.modules["airsim.types"] = stub + uspec = importlib.util.spec_from_file_location("airsim.utils", _UTILS) + umod = importlib.util.module_from_spec(uspec) + sys.modules["airsim.utils"] = umod + uspec.loader.exec_module(umod) + return umod + + +_u = _load_utils() + + +class TestListTo2d(unittest.TestCase): + def test_happy_path(self): + out = _u.list_to_2d_float_array([1, 2, 3, 4], 2, 2) + self.assertEqual(out.shape, (2, 2)) + self.assertEqual(out.dtype, np.float32) + self.assertAlmostEqual(float(out[0, 0]), 1.0) + + def test_empty_zero_by_zero(self): + out = _u.list_to_2d_float_array([], 0, 0) + self.assertEqual(out.shape, (0, 0)) + + def test_length_mismatch(self): + with self.assertRaises(ValueError) as ctx: + _u.list_to_2d_float_array([1, 2, 3], 2, 2) + self.assertIn("flat length", str(ctx.exception)) + + def test_negative_dim(self): + with self.assertRaises(ValueError): + _u.list_to_2d_float_array([], -1, 1) + + +if __name__ == "__main__": + unittest.main() diff --git a/PythonClient/airsim/utils.py b/PythonClient/airsim/utils.py index 7f866d7d0a..cbb9f18064 100644 --- a/PythonClient/airsim/utils.py +++ b/PythonClient/airsim/utils.py @@ -18,7 +18,29 @@ def string_to_float_array(bstr): return np.fromstring(bstr, np.float32) def list_to_2d_float_array(flst, width, height): - return np.reshape(np.asarray(flst, np.float32), (height, width)) + """Reshape a flat float list into a height x width float32 array. + + Raises ValueError with a clear message when dimensions are invalid so + empty or truncated depth buffers fail closed instead of cascading + opaque numpy reshape errors. + """ + try: + w = int(width) + h = int(height) + except (TypeError, ValueError) as exc: + raise ValueError("width and height must be integers") from exc + if w < 0 or h < 0: + raise ValueError("width and height must be non-negative, got width=%r height=%r" % (w, h)) + arr = np.asarray(flst, np.float32).reshape(-1) + expected = w * h + if arr.size != expected: + raise ValueError( + "flat length %d does not match width*height (%d*%d=%d)" + % (arr.size, w, h, expected) + ) + if expected == 0: + return np.zeros((h, w), dtype=np.float32) + return np.reshape(arr, (h, w)) def get_pfm_array(response): return list_to_2d_float_array(response.image_data_float, response.width, response.height)