From 811c9135b570bc4d9230f474a17447a8ea58a13c Mon Sep 17 00:00:00 2001 From: Wes John-Alder Date: Thu, 12 Nov 2026 09:17:31 -0500 Subject: [PATCH 1/2] PNE-9649: Deprecate allowed_names on ProcessTemplate The platform no longer stores or returns process-template allowed_names. Ingredient names for tables now come from ingredient specs. Keep the argument and the field so constructors and loaders stay compatible, and warn when a caller passes a non-empty list. Co-Authored-By: Claude Opus 5.5 --- src/citrine/__version__.py | 2 +- src/citrine/resources/process_template.py | 16 +++++++++++ tests/serialization/test_object_template.py | 31 ++++++++++++++++++++- tests/serialization/test_process_spec.py | 4 +-- 4 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/citrine/__version__.py b/src/citrine/__version__.py index 6c235c596..f5752882d 100644 --- a/src/citrine/__version__.py +++ b/src/citrine/__version__.py @@ -1 +1 @@ -__version__ = "5.2.0" +__version__ = "5.3.0" diff --git a/src/citrine/resources/process_template.py b/src/citrine/resources/process_template.py index e5fc838fe..dac0095ff 100644 --- a/src/citrine/resources/process_template.py +++ b/src/citrine/resources/process_template.py @@ -1,5 +1,6 @@ """Resources that represent process templates.""" +import warnings from collections.abc import Sequence from gemd.entity.bounds.base_bounds import BaseBounds @@ -56,6 +57,12 @@ class ProcessTemplate( Templates for associated parameters. Each template can be provided by itself, or as a list with the second entry being a separate, *more restrictive* Bounds object that defines the limits of the value for this parameter. + allowed_labels: list[str], optional + Labels that ingredients of a process with this template may carry. + allowed_names: list[str], optional + [DEPRECATED] The platform ignores this value, and responses no longer include it. + Ingredient names are now discovered from ingredient specs. Passing a non-empty list emits a + DeprecationWarning. This argument will be removed in a future release. """ @@ -89,6 +96,15 @@ def __init__( description: str | None = None, tags: list[str] | None = None, ): + # gemd serializes an unset allowed_names as an empty list, so only a non-empty list warns + if allowed_names: + warnings.warn( + "As of 5.3.0, 'allowed_names' on ProcessTemplate is deprecated. The platform " + "ignores it and discovers ingredient names from ingredient specs. It will be " + "removed in a future release.", + DeprecationWarning, + stacklevel=2, + ) if uids is None: uids = dict() super(ObjectTemplate, self).__init__() diff --git a/tests/serialization/test_object_template.py b/tests/serialization/test_object_template.py index 6172e3215..e980a9f3d 100644 --- a/tests/serialization/test_object_template.py +++ b/tests/serialization/test_object_template.py @@ -1,7 +1,9 @@ """Tests of the object template schema.""" +import warnings from uuid import uuid4 +import pytest from gemd.entity.bounds.categorical_bounds import CategoricalBounds from gemd.entity.bounds.integer_bounds import IntegerBounds from gemd.entity.bounds.real_bounds import RealBounds @@ -45,7 +47,6 @@ def test_object_template_serde(): parameters=[index_template], conditions=[pressure_template], allowed_labels=["Label"], - allowed_names=["first sample", "second sample"], ) assert ProcessTemplate.build(proc_template.dump()) == proc_template @@ -99,3 +100,31 @@ def link(): for _, bounds in dumped[name][1:]: assert bounds is None assert template_type.build(dumped) == template + + +def test_process_template_allowed_names_deprecated(): + """Passing allowed_names warns, and data that still carries the field loads.""" + with pytest.warns(DeprecationWarning, match="allowed_names"): + template = ProcessTemplate("Make an object", allowed_names=["first sample"]) + assert template.allowed_names == ["first sample"] + + data = template.dump() + assert data["allowed_names"] == ["first sample"] + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + copy_template = ProcessTemplate.build(data) + assert copy_template.allowed_names == ["first sample"] + + +def test_process_template_without_allowed_names(): + """Omitting allowed_names emits no deprecation warning, in construction or deserialization.""" + with warnings.catch_warnings(): + warnings.simplefilter("error", DeprecationWarning) + template = ProcessTemplate("Make an object", allowed_labels=["Label"]) + # gemd round-trips an unset value as an empty list + assert ProcessTemplate("Make an object", allowed_names=[]).allowed_names == [] + data = template.dump() + data.pop("allowed_names", None) + copy_template = ProcessTemplate.build(data) + assert copy_template == template + assert copy_template.allowed_labels == ["Label"] diff --git a/tests/serialization/test_process_spec.py b/tests/serialization/test_process_spec.py index 82c107d43..d45e9f18d 100644 --- a/tests/serialization/test_process_spec.py +++ b/tests/serialization/test_process_spec.py @@ -68,7 +68,6 @@ def valid_data(): ] ], "allowed_labels": ["a", "b"], - "allowed_names": ["a name"], "description": "a long description", }, file_links=[{"type": "file_link", "filename": "cake_recipe.txt", "url": "www.baking.com"}], @@ -103,7 +102,6 @@ def test_simple_deserialization(valid_data): ], description="a long description", allowed_labels=["a", "b"], - allowed_names=["a name"], ) assert process_spec.name == "Process 1" assert process_spec.notes == "make sure to use oven mitts" @@ -118,4 +116,6 @@ def test_serialization(valid_data): serialized = process_spec.dump() # Audit info & dataset are not included in the dump serialized["audit_info"] = valid_data["audit_info"] + # The response lacks the deprecated allowed_names, which gemd dumps as an empty list + assert serialized["template"].pop("allowed_names") == [] assert serialized == valid_data From 8477ce63d90952549389d57ebd7c71243b4f2a5a Mon Sep 17 00:00:00 2001 From: Wes John-Alder Date: Thu, 12 Nov 2026 09:20:39 -0500 Subject: [PATCH 2/2] PNE-9649: Remove allowed_names from ProcessTemplate The platform no longer stores or returns process-template allowed_names, and gemd 4.0.0 drops the field. Remove the field and the constructor argument from ProcessTemplate, require gemd 4, and bump to 6.0.0 for the breaking change. Co-Authored-By: Claude Opus 5.5 --- pyproject.toml | 2 +- requirements.txt | 2 +- src/citrine/__version__.py | 2 +- src/citrine/resources/process_template.py | 19 ------------- tests/serialization/test_object_template.py | 30 --------------------- tests/serialization/test_process_spec.py | 2 -- 6 files changed, 3 insertions(+), 54 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b1d8d3264..814d00c18 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ dependencies = [ "requests>=2.32.2,<3", "pyjwt>=2,<3", "arrow>=1.0.0,<2", - "gemd>=3,<4", + "gemd>=4,<5", "boto3>=1.34.35,<2", "deprecation>=2.1.0,<3", "urllib3>=2.5.0,<3", diff --git a/requirements.txt b/requirements.txt index ff79c80ce..87314d36b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ arrow==1.4.0 boto3==1.43.36 deprecation==2.1.0 -gemd==3.0.0 +gemd==4.0.0 pyjwt==2.15.0 requests==2.34.2 tqdm==4.68.3 diff --git a/src/citrine/__version__.py b/src/citrine/__version__.py index f5752882d..0f607a5d2 100644 --- a/src/citrine/__version__.py +++ b/src/citrine/__version__.py @@ -1 +1 @@ -__version__ = "5.3.0" +__version__ = "6.0.0" diff --git a/src/citrine/resources/process_template.py b/src/citrine/resources/process_template.py index dac0095ff..e3eb1fff0 100644 --- a/src/citrine/resources/process_template.py +++ b/src/citrine/resources/process_template.py @@ -1,6 +1,5 @@ """Resources that represent process templates.""" -import warnings from collections.abc import Sequence from gemd.entity.bounds.base_bounds import BaseBounds @@ -57,12 +56,6 @@ class ProcessTemplate( Templates for associated parameters. Each template can be provided by itself, or as a list with the second entry being a separate, *more restrictive* Bounds object that defines the limits of the value for this parameter. - allowed_labels: list[str], optional - Labels that ingredients of a process with this template may carry. - allowed_names: list[str], optional - [DEPRECATED] The platform ignores this value, and responses no longer include it. - Ingredient names are now discovered from ingredient specs. Passing a non-empty list emits a - DeprecationWarning. This argument will be removed in a future release. """ @@ -72,7 +65,6 @@ class ProcessTemplate( parameters = Optional(List(_attr_tuple(GEMDParameterTemplate)), "parameters", override=True) allowed_labels = Optional(List(String()), "allowed_labels", override=True) - allowed_names = Optional(List(String()), "allowed_names", override=True) def __init__( self, @@ -92,19 +84,9 @@ def __init__( ] | None = None, allowed_labels: list[str] | None = None, - allowed_names: list[str] | None = None, description: str | None = None, tags: list[str] | None = None, ): - # gemd serializes an unset allowed_names as an empty list, so only a non-empty list warns - if allowed_names: - warnings.warn( - "As of 5.3.0, 'allowed_names' on ProcessTemplate is deprecated. The platform " - "ignores it and discovers ingredient names from ingredient specs. It will be " - "removed in a future release.", - DeprecationWarning, - stacklevel=2, - ) if uids is None: uids = dict() super(ObjectTemplate, self).__init__() @@ -117,7 +99,6 @@ def __init__( tags=tags, description=description, allowed_labels=allowed_labels, - allowed_names=allowed_names, ) def __str__(self): diff --git a/tests/serialization/test_object_template.py b/tests/serialization/test_object_template.py index e980a9f3d..5ea7a8df2 100644 --- a/tests/serialization/test_object_template.py +++ b/tests/serialization/test_object_template.py @@ -1,9 +1,7 @@ """Tests of the object template schema.""" -import warnings from uuid import uuid4 -import pytest from gemd.entity.bounds.categorical_bounds import CategoricalBounds from gemd.entity.bounds.integer_bounds import IntegerBounds from gemd.entity.bounds.real_bounds import RealBounds @@ -100,31 +98,3 @@ def link(): for _, bounds in dumped[name][1:]: assert bounds is None assert template_type.build(dumped) == template - - -def test_process_template_allowed_names_deprecated(): - """Passing allowed_names warns, and data that still carries the field loads.""" - with pytest.warns(DeprecationWarning, match="allowed_names"): - template = ProcessTemplate("Make an object", allowed_names=["first sample"]) - assert template.allowed_names == ["first sample"] - - data = template.dump() - assert data["allowed_names"] == ["first sample"] - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - copy_template = ProcessTemplate.build(data) - assert copy_template.allowed_names == ["first sample"] - - -def test_process_template_without_allowed_names(): - """Omitting allowed_names emits no deprecation warning, in construction or deserialization.""" - with warnings.catch_warnings(): - warnings.simplefilter("error", DeprecationWarning) - template = ProcessTemplate("Make an object", allowed_labels=["Label"]) - # gemd round-trips an unset value as an empty list - assert ProcessTemplate("Make an object", allowed_names=[]).allowed_names == [] - data = template.dump() - data.pop("allowed_names", None) - copy_template = ProcessTemplate.build(data) - assert copy_template == template - assert copy_template.allowed_labels == ["Label"] diff --git a/tests/serialization/test_process_spec.py b/tests/serialization/test_process_spec.py index d45e9f18d..481caad04 100644 --- a/tests/serialization/test_process_spec.py +++ b/tests/serialization/test_process_spec.py @@ -116,6 +116,4 @@ def test_serialization(valid_data): serialized = process_spec.dump() # Audit info & dataset are not included in the dump serialized["audit_info"] = valid_data["audit_info"] - # The response lacks the deprecated allowed_names, which gemd dumps as an empty list - assert serialized["template"].pop("allowed_names") == [] assert serialized == valid_data