From 610320a293c4d16587b865b0a1ce0f3221212a86 Mon Sep 17 00:00:00 2001 From: "Jason C. Klima" Date: Sat, 16 May 2026 14:22:33 -0700 Subject: [PATCH 01/12] Initial commit --- .../python/PyRosetta/src/test/T021_Pose_Methods.py | 13 +++++++++++++ .../PyRosetta/src/test/T022_Pose_Alignment.py | 13 +++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 source/src/python/PyRosetta/src/test/T021_Pose_Methods.py create mode 100644 source/src/python/PyRosetta/src/test/T022_Pose_Alignment.py diff --git a/source/src/python/PyRosetta/src/test/T021_Pose_Methods.py b/source/src/python/PyRosetta/src/test/T021_Pose_Methods.py new file mode 100644 index 00000000000..c0f5156e6a9 --- /dev/null +++ b/source/src/python/PyRosetta/src/test/T021_Pose_Methods.py @@ -0,0 +1,13 @@ +# :noTabs=true: +# (c) Copyright Rosetta Commons Member Institutions. +# (c) This file is part of the Rosetta software suite and is made available under license. +# (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +# (c) For more information, see http://www.rosettacommons.org. +# (c) Questions about this can be addressed to University of Washington CoMotion, email: license@uw.edu. + +__author__ = "Jason C. Klima" + +from utils.distributed import run_unittest + +if __name__ == "__main__": + run_unittest("pyrosetta.tests.bindings.core.test_pose", timeout=60) diff --git a/source/src/python/PyRosetta/src/test/T022_Pose_Alignment.py b/source/src/python/PyRosetta/src/test/T022_Pose_Alignment.py new file mode 100644 index 00000000000..0fac4583560 --- /dev/null +++ b/source/src/python/PyRosetta/src/test/T022_Pose_Alignment.py @@ -0,0 +1,13 @@ +# :noTabs=true: +# (c) Copyright Rosetta Commons Member Institutions. +# (c) This file is part of the Rosetta software suite and is made available under license. +# (c) The Rosetta software is developed by the contributing members of the Rosetta Commons. +# (c) For more information, see http://www.rosettacommons.org. +# (c) Questions about this can be addressed to University of Washington CoMotion, email: license@uw.edu. + +__author__ = "Jason C. Klima" + +from utils.distributed import run_unittest + +if __name__ == "__main__": + run_unittest("pyrosetta.tests.numeric.test_alignment", timeout=30) From 7fce8d3648aeff2d1006587267531077751d04ac Mon Sep 17 00:00:00 2001 From: "Jason C. Klima" Date: Wed, 20 May 2026 00:10:38 -0700 Subject: [PATCH 02/12] Remove duplicate tests; exit if numpy is not installed --- .../PyRosetta/src/test/T021_Pose_Methods.py | 3 ++- .../PyRosetta/src/test/T022_Pose_Alignment.py | 3 ++- .../PyRosetta/src/test/T900_distributed.py | 2 -- .../PyRosetta/src/test/utils/distributed.py | 22 ++++++++++++++++--- 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/source/src/python/PyRosetta/src/test/T021_Pose_Methods.py b/source/src/python/PyRosetta/src/test/T021_Pose_Methods.py index c0f5156e6a9..f5882cb446b 100644 --- a/source/src/python/PyRosetta/src/test/T021_Pose_Methods.py +++ b/source/src/python/PyRosetta/src/test/T021_Pose_Methods.py @@ -7,7 +7,8 @@ __author__ = "Jason C. Klima" -from utils.distributed import run_unittest +from utils.distributed import exit_if_missing_numpy_requirement, run_unittest if __name__ == "__main__": + exit_if_missing_numpy_requirement() run_unittest("pyrosetta.tests.bindings.core.test_pose", timeout=60) diff --git a/source/src/python/PyRosetta/src/test/T022_Pose_Alignment.py b/source/src/python/PyRosetta/src/test/T022_Pose_Alignment.py index 0fac4583560..3005602cfe5 100644 --- a/source/src/python/PyRosetta/src/test/T022_Pose_Alignment.py +++ b/source/src/python/PyRosetta/src/test/T022_Pose_Alignment.py @@ -7,7 +7,8 @@ __author__ = "Jason C. Klima" -from utils.distributed import run_unittest +from utils.distributed import exit_if_missing_numpy_requirement, run_unittest if __name__ == "__main__": + exit_if_missing_numpy_requirement() run_unittest("pyrosetta.tests.numeric.test_alignment", timeout=30) diff --git a/source/src/python/PyRosetta/src/test/T900_distributed.py b/source/src/python/PyRosetta/src/test/T900_distributed.py index 4dd74cd17d6..c80a39eff94 100644 --- a/source/src/python/PyRosetta/src/test/T900_distributed.py +++ b/source/src/python/PyRosetta/src/test/T900_distributed.py @@ -13,13 +13,11 @@ def main(wait: bool, streaming: bool, timeout: int) -> None: run_test_cases( "pyrosetta.tests.bindings.init.test_init_files", - "pyrosetta.tests.bindings.core.test_pose", "pyrosetta.tests.distributed.test_concurrency", "pyrosetta.tests.distributed.test_dask", "pyrosetta.tests.distributed.test_gil", "pyrosetta.tests.distributed.test_smoke", "pyrosetta.tests.distributed.test_viewer", - "pyrosetta.tests.numeric.test_alignment", wait=wait, streaming=streaming, timeout=timeout, diff --git a/source/src/python/PyRosetta/src/test/utils/distributed.py b/source/src/python/PyRosetta/src/test/utils/distributed.py index 304275a4fee..f83fcafa832 100644 --- a/source/src/python/PyRosetta/src/test/utils/distributed.py +++ b/source/src/python/PyRosetta/src/test/utils/distributed.py @@ -24,8 +24,6 @@ Any, Callable, List, - NoReturn, - Optional, TypeVar, cast, ) @@ -62,12 +60,22 @@ def has_pyrosetta_distributed_package_requirements() -> bool: return False +def has_numpy_installed() -> bool: + """Test if `numpy` is installed in the virtual environment.""" + try: + import numpy + return True + except ImportError as ex: + print(f"{type(ex).__name__}: {ex}") + return False + + def has_python_version(major: int, minor: int) -> bool: """Test if the Python version is greater than or equal to the provided `major` and `minor` values.""" return tuple(sys.version_info) >= (major, minor) -def exit_if_missing_pyrosetta_distributed_requirements(returncode: int = 0) -> Optional[NoReturn]: +def exit_if_missing_pyrosetta_distributed_requirements(returncode: int = 0) -> None: """Exit the Python process if the `pyrosetta.distributed` framework requirements are missing from the virtual environment.""" if not has_python_version(3, 6): @@ -81,6 +89,14 @@ def exit_if_missing_pyrosetta_distributed_requirements(returncode: int = 0) -> O sys.exit(returncode) +def exit_if_missing_numpy_requirement(returncode: int = 0) -> None: + """Exit the Python process if `numpy` is missing from the virtual environment.""" + + if not has_numpy_installed(): + print(f"The `numpy` package required for the tests is missing. Skipping the tests...") + sys.exit(returncode) + + def print_environment_export() -> None: """Print the active virtual environment export.""" From 90135a744c30a7bff10dab09a03eafd3afb345e2 Mon Sep 17 00:00:00 2001 From: "Jason C. Klima" Date: Fri, 26 Jun 2026 12:43:56 -0700 Subject: [PATCH 03/12] Also install numpy in non-serialization builds --- tests/benchmark/tests/__init__.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/benchmark/tests/__init__.py b/tests/benchmark/tests/__init__.py index 959efce8ef6..be36f83dd32 100644 --- a/tests/benchmark/tests/__init__.py +++ b/tests/benchmark/tests/__init__.py @@ -525,7 +525,7 @@ def get_required_pyrosetta_python_packages_for_release_package(platform, conda=T IMPORTANT: there should be no spaces between package name and version number - IMPORTANT: if PyRosetta build without serialization support or on Python-2 platform then we DO NOT REQUIRE any packages + IMPORTANT: if PyRosetta build without serialization support or on Python-2 platform then we require only `numpy` ''' python_version = tuple( map(int, platform.get('python', DEFAULT_PYTHON_VERSION).split('.') ) ) @@ -542,7 +542,11 @@ def get_required_pyrosetta_python_packages_for_release_package(platform, conda=T if static_versions: packages = set_static_versions_for_python_packages(packages) - return get_packages_list_for_python_packages(packages) if 'serialization' in platform['extras'] and platform.get('python', DEFAULT_PYTHON_VERSION)[:2] != '2.' else [] + return ( + get_packages_list_for_python_packages(packages) + if 'serialization' in platform['extras'] and platform.get('python', DEFAULT_PYTHON_VERSION)[:2] != '2.' + else get_packages_list_for_python_packages({'numpy': packages.get('numpy', DEFAULT_PACKAGE_VERSIONS_FOR_PYROSETTA_DISTRIBUTED['numpy'])}) + ) def build_pyrosetta(rosetta_dir, platform, jobs, config, mode='MinSizeRel', options='', conda=None, verbose=False, skip_compile=False, version=None): From 242a3b04fb5d314584d21ce58870674c8e085638 Mon Sep 17 00:00:00 2001 From: "Jason C. Klima" Date: Fri, 26 Jun 2026 12:46:48 -0700 Subject: [PATCH 04/12] Remove exit on missing numpy requirement --- source/src/python/PyRosetta/src/test/T021_Pose_Methods.py | 3 +-- source/src/python/PyRosetta/src/test/T022_Pose_Alignment.py | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/source/src/python/PyRosetta/src/test/T021_Pose_Methods.py b/source/src/python/PyRosetta/src/test/T021_Pose_Methods.py index f5882cb446b..c0f5156e6a9 100644 --- a/source/src/python/PyRosetta/src/test/T021_Pose_Methods.py +++ b/source/src/python/PyRosetta/src/test/T021_Pose_Methods.py @@ -7,8 +7,7 @@ __author__ = "Jason C. Klima" -from utils.distributed import exit_if_missing_numpy_requirement, run_unittest +from utils.distributed import run_unittest if __name__ == "__main__": - exit_if_missing_numpy_requirement() run_unittest("pyrosetta.tests.bindings.core.test_pose", timeout=60) diff --git a/source/src/python/PyRosetta/src/test/T022_Pose_Alignment.py b/source/src/python/PyRosetta/src/test/T022_Pose_Alignment.py index 3005602cfe5..0fac4583560 100644 --- a/source/src/python/PyRosetta/src/test/T022_Pose_Alignment.py +++ b/source/src/python/PyRosetta/src/test/T022_Pose_Alignment.py @@ -7,8 +7,7 @@ __author__ = "Jason C. Klima" -from utils.distributed import exit_if_missing_numpy_requirement, run_unittest +from utils.distributed import run_unittest if __name__ == "__main__": - exit_if_missing_numpy_requirement() run_unittest("pyrosetta.tests.numeric.test_alignment", timeout=30) From 0dc2dbb66d24ef65da2d3302a6ddcc129b3042da Mon Sep 17 00:00:00 2001 From: "Jason C. Klima" Date: Fri, 26 Jun 2026 16:34:50 -0700 Subject: [PATCH 05/12] Install numpy in non-serialization builds in get_required_pyrosetta_python_packages_for_testing --- tests/benchmark/tests/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/benchmark/tests/__init__.py b/tests/benchmark/tests/__init__.py index be36f83dd32..ab49c177653 100644 --- a/tests/benchmark/tests/__init__.py +++ b/tests/benchmark/tests/__init__.py @@ -514,7 +514,11 @@ def get_required_pyrosetta_python_packages_for_testing(platform, conda=False, st if static_versions: packages = set_static_versions_for_python_packages(packages) - return get_packages_str_for_python_packages(packages) if 'serialization' in platform['extras'] and platform.get('python', DEFAULT_PYTHON_VERSION)[:2] != '2.' else '' + return ( + get_packages_str_for_python_packages(packages) + if 'serialization' in platform['extras'] and platform.get('python', DEFAULT_PYTHON_VERSION)[:2] != '2.' + else get_packages_str_for_python_packages({'numpy': packages.get('numpy', DEFAULT_PACKAGE_VERSIONS_FOR_PYROSETTA_DISTRIBUTED['numpy'])}) + ) def get_required_pyrosetta_python_packages_for_release_package(platform, conda=True, static_versions=True, distributed_packages=False): From aa328195fc474f0ba1a68ec31a3ed57c30b9a2b8 Mon Sep 17 00:00:00 2001 From: "Jason C. Klima" Date: Sat, 27 Jun 2026 16:27:22 -0700 Subject: [PATCH 06/12] git diff main...fix_diagnostics_port | git apply --- .../pyrosetta/tests/distributed/test_dask.py | 25 ++++++++++--------- .../tests/distributed/test_dask_worker.py | 16 +++++++++--- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/source/src/python/PyRosetta/src/pyrosetta/tests/distributed/test_dask.py b/source/src/python/PyRosetta/src/pyrosetta/tests/distributed/test_dask.py index 29c169845e4..720cf85e5d8 100644 --- a/source/src/python/PyRosetta/src/pyrosetta/tests/distributed/test_dask.py +++ b/source/src/python/PyRosetta/src/pyrosetta/tests/distributed/test_dask.py @@ -32,21 +32,22 @@ def setUp(self, local_dir=workdir): if not self._dask_scheduler: n_workers = 2 threads_per_worker = 2 - diagnostics_port = None + dashboard_address = None + local_cluster_kwargs = dict( + n_workers=n_workers, + threads_per_worker=threads_per_worker, + dashboard_address=dashboard_address, + local_directory=local_dir, + ) if __dask_version__ <= (2, 1, 0): - self.local_cluster = dask.distributed.LocalCluster( - n_workers=n_workers, - threads_per_worker=threads_per_worker, - diagnostics_port=diagnostics_port, - local_dir=local_dir, + local_cluster_kwargs["local_dir"] = local_cluster_kwargs.pop( + "local_directory", local_dir ) - else: - self.local_cluster = dask.distributed.LocalCluster( - n_workers=n_workers, - threads_per_worker=threads_per_worker, - diagnostics_port=diagnostics_port, - local_directory=local_dir, + if __dask_version__ < (2026, 6, 0): + local_cluster_kwargs["diagnostics_port"] = local_cluster_kwargs.pop( + "dashboard_address", dashboard_address ) + self.local_cluster = dask.distributed.LocalCluster(**local_cluster_kwargs) cluster = self.local_cluster else: self.local_cluster = None diff --git a/source/src/python/PyRosetta/src/pyrosetta/tests/distributed/test_dask_worker.py b/source/src/python/PyRosetta/src/pyrosetta/tests/distributed/test_dask_worker.py index 6b209aca686..4f7f6ab6848 100644 --- a/source/src/python/PyRosetta/src/pyrosetta/tests/distributed/test_dask_worker.py +++ b/source/src/python/PyRosetta/src/pyrosetta/tests/distributed/test_dask_worker.py @@ -14,6 +14,10 @@ import time import unittest +from pyrosetta.utility import get_package_version + + +__dask_version__ = get_package_version("dask") class TestDaskArgs(unittest.TestCase): @@ -36,8 +40,14 @@ def test_worker_extra(self): import pyrosetta.distributed.tasks.score as score # Setup cluster scheduler and working directory + dashboard_address = None + local_cluster_kwargs = dict(n_workers=0, dashboard_address=dashboard_address) + if __dask_version__ < (2026, 6, 0): + local_cluster_kwargs["diagnostics_port"] = local_cluster_kwargs.pop( + "dashboard_address", dashboard_address + ) with tempfile.TemporaryDirectory() as workdir, LocalCluster( - n_workers=0, diagnostics_port=None + **local_cluster_kwargs ) as cluster: # Context manager controls launch & teardown of test worker @@ -102,8 +112,8 @@ def one_worker(init_flags): cluster_result = delayed(protocol)(pose).compute() self.assertEqual( - cluster_result.scores["total_score"], - local_result.scores["total_score"] + cluster_result.pose.cache["total_score"], + local_result.pose.cache["total_score"] ) From c8e5d90ba6295a20fa110f5a0c06b0bc1c17ab74 Mon Sep 17 00:00:00 2001 From: "Jason C. Klima" Date: Sat, 27 Jun 2026 16:51:11 -0700 Subject: [PATCH 07/12] Skipping Pose object serialization for non-serialization builds --- .../pyrosetta/tests/bindings/core/test_pose.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/source/src/python/PyRosetta/src/pyrosetta/tests/bindings/core/test_pose.py b/source/src/python/PyRosetta/src/pyrosetta/tests/bindings/core/test_pose.py index 61da03a0f21..33a8c022bcd 100644 --- a/source/src/python/PyRosetta/src/pyrosetta/tests/bindings/core/test_pose.py +++ b/source/src/python/PyRosetta/src/pyrosetta/tests/bindings/core/test_pose.py @@ -53,6 +53,7 @@ get_unpickle_hmac_key, set_unpickle_hmac_key, ) +from pyrosetta.utility import has_cereal pyrosetta.init(extra_options="-constant_seed", set_logging_handler="logging") @@ -220,9 +221,11 @@ def test_score_serialization(self): with self.assertRaises(Exception): test_pose.cache[str(obj_type)] = value_input continue - else: - test_pose.cache[str(obj_type)] = value_input - value_output = test_pose.cache[str(obj_type)] + if obj_type == pyrosetta.Pose and not has_cereal(): + # `Pose` instances require cereal support for round-trip serialization + continue + test_pose.cache[str(obj_type)] = value_input + value_output = test_pose.cache[str(obj_type)] # Test instance types self.assertIsInstance(value_input, obj_type) @@ -780,9 +783,10 @@ def test_pose_cache(self): with self.assertWarns(UserWarning): self.pose.cache.metrics.string["float"] = 1e5 self.assertIn("float", self.pose.cache.metrics.real.keys()) - with self.assertWarns(UserWarning): - self.pose.cache.metrics.real["pose"] = pyrosetta.pose_from_sequence("DATA") - self.assertIn("pose", self.pose.cache.metrics.string.keys()) + if has_cereal(): + with self.assertWarns(UserWarning): + self.pose.cache.metrics.real["pose"] = pyrosetta.pose_from_sequence("DATA") + self.assertIn("pose", self.pose.cache.metrics.string.keys()) with self.assertWarns(UserWarning): self.pose.cache.extra.real["str"] = "String" From bc91b523580212874e045fdd4ceae4ce1506f5f6 Mon Sep 17 00:00:00 2001 From: "Jason C. Klima" Date: Sat, 27 Jun 2026 17:28:03 -0700 Subject: [PATCH 08/12] Remove scipy dependency via a numpy-only equivalent implementation --- .../pyrosetta/tests/numeric/test_alignment.py | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/source/src/python/PyRosetta/src/pyrosetta/tests/numeric/test_alignment.py b/source/src/python/PyRosetta/src/pyrosetta/tests/numeric/test_alignment.py index 3ac985cc671..af4ad0789db 100644 --- a/source/src/python/PyRosetta/src/pyrosetta/tests/numeric/test_alignment.py +++ b/source/src/python/PyRosetta/src/pyrosetta/tests/numeric/test_alignment.py @@ -8,9 +8,26 @@ class TestAlignment(unittest.TestCase): @staticmethod def _rotation_matrix(axis, theta): - from numpy import cross, eye - from scipy.linalg import expm, norm - return expm(cross(eye(3), axis/norm(axis)*theta)) + """ + NumPy-only equivalent of the SciPy implementation: + from numpy import cross, eye + from scipy.linalg import expm, norm + return expm(cross(eye(3), axis/norm(axis)*theta)) + """ + from numpy import array, cos, eye, sin + from numpy.linalg import norm + + axis = array(axis, dtype=float) + axis /= norm(axis) + x, y, z = axis + + K = array([ + [0, -z, y], + [z, 0, -x], + [-y, x, 0], + ]) + + return eye(3) + sin(theta) * K + (1 - cos(theta)) * (K @ K) def test_superimpose(self): numpy.random.seed(1663) From 4c1f2e4ab925f6b8c938c0fe0f8cb440470f2d08 Mon Sep 17 00:00:00 2001 From: "Jason C. Klima" Date: Sat, 27 Jun 2026 18:26:38 -0700 Subject: [PATCH 09/12] Skipping Pose object serialization for non-serialization builds --- .../tests/bindings/core/test_pose.py | 55 ++++++++++--------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/source/src/python/PyRosetta/src/pyrosetta/tests/bindings/core/test_pose.py b/source/src/python/PyRosetta/src/pyrosetta/tests/bindings/core/test_pose.py index 33a8c022bcd..e8606620be7 100644 --- a/source/src/python/PyRosetta/src/pyrosetta/tests/bindings/core/test_pose.py +++ b/source/src/python/PyRosetta/src/pyrosetta/tests/bindings/core/test_pose.py @@ -874,27 +874,28 @@ def test_pose_cache(self): with self.assertWarns(ClobberWarning): dict(self.pose.cache) - # Test that `CacheableDataType.SIMPLE_METRIC_DATA` pose data is not automatically set by accessing `Pose.cache` - pose = pyrosetta.io.pose_from_sequence("TEST/SIMPLE/METRIC/DATA") - pickler = partial(pickle.dumps, protocol=pickle.DEFAULT_PROTOCOL) - bytes_start_1 = pickler(pose) - self.assertFalse(pose.data().has(CacheableDataType.SIMPLE_METRIC_DATA)) - self.assertFalse(pose.cache.metrics._has_sm_data()) - _ = dict(pose.cache) # Access `Pose.cache` - self.assertFalse(pose.data().has(CacheableDataType.SIMPLE_METRIC_DATA)) - self.assertFalse(pose.cache.metrics._has_sm_data()) - bytes_final_1 = pickler(pose) - self.assertEqual(bytes_start_1, bytes_final_1, msg="Pose is not bitwise identical after accessing `Pose.cache`.") - pose.cache.metrics["pi"] = math.pi # Add SimpleMetrics data - bytes_start_2 = pickler(pose) - self.assertTrue(pose.data().has(CacheableDataType.SIMPLE_METRIC_DATA)) - self.assertTrue(pose.cache.metrics._has_sm_data()) - _ = dict(pose.cache) # Access `Pose.cache` - self.assertTrue(pose.data().has(CacheableDataType.SIMPLE_METRIC_DATA)) - self.assertTrue(pose.cache.metrics._has_sm_data()) - bytes_final_2 = pickler(pose) - self.assertEqual(bytes_start_2, bytes_final_2, msg="Pose is not bitwise identical after accessing `Pose.cache`.") - self.assertNotEqual(bytes_final_1, bytes_final_2, msg="Pose is bitwise identical after adding SimpleMetrics data to `Pose.cache`.") + if has_cereal(): + # Test that `CacheableDataType.SIMPLE_METRIC_DATA` pose data is not automatically set by accessing `Pose.cache` + pose = pyrosetta.io.pose_from_sequence("TEST/SIMPLE/METRIC/DATA") + pickler = partial(pickle.dumps, protocol=pickle.DEFAULT_PROTOCOL) + bytes_start_1 = pickler(pose) + self.assertFalse(pose.data().has(CacheableDataType.SIMPLE_METRIC_DATA)) + self.assertFalse(pose.cache.metrics._has_sm_data()) + _ = dict(pose.cache) # Access `Pose.cache` + self.assertFalse(pose.data().has(CacheableDataType.SIMPLE_METRIC_DATA)) + self.assertFalse(pose.cache.metrics._has_sm_data()) + bytes_final_1 = pickler(pose) + self.assertEqual(bytes_start_1, bytes_final_1, msg="Pose is not bitwise identical after accessing `Pose.cache`.") + pose.cache.metrics["pi"] = math.pi # Add SimpleMetrics data + bytes_start_2 = pickler(pose) + self.assertTrue(pose.data().has(CacheableDataType.SIMPLE_METRIC_DATA)) + self.assertTrue(pose.cache.metrics._has_sm_data()) + _ = dict(pose.cache) # Access `Pose.cache` + self.assertTrue(pose.data().has(CacheableDataType.SIMPLE_METRIC_DATA)) + self.assertTrue(pose.cache.metrics._has_sm_data()) + bytes_final_2 = pickler(pose) + self.assertEqual(bytes_start_2, bytes_final_2, msg="Pose is not bitwise identical after accessing `Pose.cache`.") + self.assertNotEqual(bytes_final_1, bytes_final_2, msg="Pose is bitwise identical after adding SimpleMetrics data to `Pose.cache`.") # Test `Pose.cache` access to SimpleMetrics data when `CacheableDataType.SIMPLE_METRIC_DATA` is not yet set up pose = pyrosetta.io.pose_from_sequence("EMPTY/DATA/CACHE") @@ -1328,11 +1329,13 @@ def __reduce__(self): else: instance = module() test_pose.cache[builtin] = instance - _test_pose = SecureSerializerBase.secure_loads( - pickle.dumps(test_pose, protocol=pickle.HIGHEST_PROTOCOL) - ) - _instance = _test_pose.cache[builtin] - self.assertIsInstance(_instance, type(instance)) + self.assertIsInstance(test_pose.cache[builtin], type(instance)) + if has_cereal(): + _test_pose = SecureSerializerBase.secure_loads( + pickle.dumps(test_pose, protocol=pickle.HIGHEST_PROTOCOL) + ) + _instance = _test_pose.cache[builtin] + self.assertIsInstance(_instance, type(instance)) # Test disallowed packages test_pose = pyrosetta.pose_from_sequence("TEST/CACHE") From a2e7defca066df604306886ee7f3d5b61f7f0e24 Mon Sep 17 00:00:00 2001 From: "Jason C. Klima" Date: Sat, 27 Jun 2026 18:29:43 -0700 Subject: [PATCH 10/12] Skipping Pose object serialization for non-serialization builds --- .../src/pyrosetta/tests/bindings/core/test_pose.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/source/src/python/PyRosetta/src/pyrosetta/tests/bindings/core/test_pose.py b/source/src/python/PyRosetta/src/pyrosetta/tests/bindings/core/test_pose.py index e8606620be7..4685c3a5486 100644 --- a/source/src/python/PyRosetta/src/pyrosetta/tests/bindings/core/test_pose.py +++ b/source/src/python/PyRosetta/src/pyrosetta/tests/bindings/core/test_pose.py @@ -1268,8 +1268,9 @@ def __reduce__(self): data = { "foo": list(range(10)), "bar": dict(enumerate([complex(1, i) for i in range(10)])), - "baz": pyrosetta.pose_from_sequence("TEST"), } + if has_cereal(): + data["baz"] = pyrosetta.pose_from_sequence("TEST") _hmac_size = 32 set_unpickle_hmac_key(os.urandom(_hmac_size)) self.assertEqual(len(get_unpickle_hmac_key()), _hmac_size) @@ -1277,8 +1278,9 @@ def __reduce__(self): out = SecureSerializerBase.secure_from_base64_pickle(string) self.assertEqual(out["foo"], data["foo"]) self.assertEqual(out["bar"], data["bar"]) - self.assertIsInstance(out["baz"], pyrosetta.Pose) - self.assertEqual(out["baz"].sequence(), data["baz"].sequence()) + if has_cereal(): + self.assertIsInstance(out["baz"], pyrosetta.Pose) + self.assertEqual(out["baz"].sequence(), data["baz"].sequence()) # Test tampering with HMAC tag arr = bytearray(base64.b64decode(string.encode(SecureSerializerBase._encoder))) _hmac_tag_idx = 3 From f91b16d06514ac09d8dec001e0279f3566a2a8f4 Mon Sep 17 00:00:00 2001 From: "Jason C. Klima" Date: Sat, 27 Jun 2026 21:13:38 -0700 Subject: [PATCH 11/12] Setting default pickle protocol for Python 3.7 --- .../PyRosetta/src/pyrosetta/tests/bindings/core/test_pose.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/src/python/PyRosetta/src/pyrosetta/tests/bindings/core/test_pose.py b/source/src/python/PyRosetta/src/pyrosetta/tests/bindings/core/test_pose.py index 4685c3a5486..68aaeb0d8af 100644 --- a/source/src/python/PyRosetta/src/pyrosetta/tests/bindings/core/test_pose.py +++ b/source/src/python/PyRosetta/src/pyrosetta/tests/bindings/core/test_pose.py @@ -1241,7 +1241,7 @@ def test_secure_unpickler(self): # Test secure serialization round-trip data = {"foo": [1, 2, 3], "bar": ("String", b"Bytes"), "baz": complex(1, -2)} - obj = pickle.dumps(data, protocol=5) + obj = pickle.dumps(data, protocol=pickle.DEFAULT_PROTOCOL) key = get_unpickle_hmac_key() if key is not None: obj = SecureSerializerBase._prepend_hmac_tag(obj, key) From 04669b474f1c71b9b3e00dd13ed083457b713e71 Mon Sep 17 00:00:00 2001 From: "Jason C. Klima" Date: Sun, 28 Jun 2026 10:38:01 -0700 Subject: [PATCH 12/12] Setting default pickle protocol for Python 3.7 --- .../src/pyrosetta/tests/bindings/core/test_pose.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/src/python/PyRosetta/src/pyrosetta/tests/bindings/core/test_pose.py b/source/src/python/PyRosetta/src/pyrosetta/tests/bindings/core/test_pose.py index 68aaeb0d8af..f5ef2b93e67 100644 --- a/source/src/python/PyRosetta/src/pyrosetta/tests/bindings/core/test_pose.py +++ b/source/src/python/PyRosetta/src/pyrosetta/tests/bindings/core/test_pose.py @@ -520,7 +520,7 @@ def test_pose_cache(self): self.assertEqual(self.pose.cache["bytes_to_str"], "ASCII binary") with self.assertWarns(UserWarning): - setPoseExtraScore(self.pose, "invalid_bytes", pickle.dumps("raw binary")) # Manually set; warns since it's raw binary + setPoseExtraScore(self.pose, "invalid_bytes", pickle.dumps("raw binary", protocol=pickle.DEFAULT_PROTOCOL)) # Manually set; warns since it's raw binary with self.assertRaises(UnicodeDecodeError): self.pose.cache["invalid_bytes"] with self.assertRaises(UnicodeDecodeError): @@ -531,7 +531,7 @@ def test_pose_cache(self): with self.assertRaises(KeyError): self.pose.cache["invalid_bytes"] - for bytestring in (b'ASCII binary', pickle.dumps("raw binary")): + for bytestring in (b'ASCII binary', pickle.dumps("raw binary", protocol=pickle.DEFAULT_PROTOCOL)): self.pose.cache.extra["bytes"] = bytestring # Automatically gets serialized self.assertIn("bytes", self.pose.cache.extra.string) with self.assertRaises(KeyError): @@ -1334,7 +1334,7 @@ def __reduce__(self): self.assertIsInstance(test_pose.cache[builtin], type(instance)) if has_cereal(): _test_pose = SecureSerializerBase.secure_loads( - pickle.dumps(test_pose, protocol=pickle.HIGHEST_PROTOCOL) + pickle.dumps(test_pose, protocol=pickle.DEFAULT_PROTOCOL) ) _instance = _test_pose.cache[builtin] self.assertIsInstance(_instance, type(instance))