Skip to content

Commit e12fba6

Browse files
committed
unfreeze nodes
1 parent 2e1584b commit e12fba6

File tree

17 files changed

+125
-188
lines changed

17 files changed

+125
-188
lines changed

bioimageio/spec/_internal/base_nodes.py

Lines changed: 9 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,16 @@
44
import collections.abc
55
import inspect
66
import os
7-
import sys
87
from abc import ABC
98
from typing import (
109
Any,
1110
ClassVar,
1211
Dict,
1312
FrozenSet,
14-
Generic,
15-
Iterator,
1613
List,
1714
Optional,
18-
Set,
1915
Tuple,
2016
Type,
21-
TypeVar,
2217
Union,
2318
cast,
2419
get_type_hints,
@@ -30,6 +25,7 @@
3025
DirectoryPath,
3126
Field,
3227
GetCoreSchemaHandler,
28+
PrivateAttr,
3329
StringConstraints,
3430
TypeAdapter,
3531
ValidationInfo,
@@ -40,27 +36,15 @@
4036

4137
from bioimageio.spec._internal.constants import IN_PACKAGE_MESSAGE
4238
from bioimageio.spec._internal.types import NotEmpty, RdfContent, Version, YamlValue
43-
from bioimageio.spec._internal.types.field_validation import is_valid_yaml_mapping
4439
from bioimageio.spec._internal.utils import unindent
4540
from bioimageio.spec._internal.validation_context import InternalValidationContext, get_internal_validation_context
4641
from bioimageio.spec.summary import ValidationSummary
4742

48-
K = TypeVar("K", bound=str)
49-
V = TypeVar("V")
50-
51-
if sys.version_info < (3, 9):
52-
53-
class FrozenDictBase(collections.abc.Mapping, Generic[K, V]): # pyright: ignore[reportMissingTypeArgument]
54-
pass
55-
56-
else:
57-
FrozenDictBase = collections.abc.Mapping[K, V]
58-
5943

6044
class Node(
6145
pydantic.BaseModel,
6246
extra="forbid",
63-
frozen=True,
47+
frozen=False,
6448
populate_by_name=True,
6549
revalidate_instances="always",
6650
validate_assignment=True,
@@ -134,7 +118,7 @@ def _set_undefined_field_descriptions_from_field_name(cls):
134118
info.description = name
135119

136120

137-
class NodeWithExplicitlySetFields(Node, frozen=True):
121+
class NodeWithExplicitlySetFields(Node):
138122
fields_to_set_explicitly: ClassVar[FrozenSet[LiteralString]] = frozenset()
139123
"""set set these fields explicitly with their default value if they are not set,
140124
such that they are always included even when dumping with 'exlude_unset'"""
@@ -150,13 +134,13 @@ def set_fields_explicitly(cls, data: Union[Any, Dict[Any, Any]]) -> Union[Any, D
150134
return data
151135

152136

153-
class ResourceDescriptionBase(NodeWithExplicitlySetFields, frozen=True):
137+
class ResourceDescriptionBase(NodeWithExplicitlySetFields):
154138
"""base class for all resource descriptions"""
155139

156-
__slots__ = ("_internal_validation_context", "_validation_summaries")
157-
158140
type: str
159141
format_version: str
142+
_internal_validation_context: InternalValidationContext
143+
_validation_summaries: List[ValidationSummary] = PrivateAttr(default_factory=list)
160144

161145
fields_to_set_explicitly: ClassVar[FrozenSet[LiteralString]] = frozenset({"type", "format_version"})
162146
implemented_format_version: ClassVar[str]
@@ -184,11 +168,7 @@ def update_context_and_data(cls, data: RdfContent, info: ValidationInfo):
184168

185169
@model_validator(mode="after")
186170
def remember_internal_validation_context(self, info: ValidationInfo) -> Self:
187-
self.model_config["frozen"] = False
188-
self._validation_summaries: List[ValidationSummary] = [] # type: ignore[frozen]
189-
self._internal_validation_context: InternalValidationContext
190-
self._internal_validation_context = get_internal_validation_context(info.context) # type: ignore[frozen]
191-
self.model_config["frozen"] = True
171+
self._internal_validation_context = get_internal_validation_context(info.context)
192172
return self
193173

194174
@classmethod
@@ -314,42 +294,5 @@ def _serialize(self) -> str:
314294
return self.data
315295

316296

317-
D = TypeVar("D")
318-
319-
320-
class FrozenDictNode(Node, FrozenDictBase[K, V], frozen=True):
321-
def __getitem__(self, item: K) -> V:
322-
try:
323-
return getattr(self, item)
324-
except AttributeError:
325-
raise KeyError(item) from None
326-
327-
def __iter__(self) -> Iterator[K]: # type: ignore iterate over keys like a dict, not (key, value) tuples
328-
yield from self.model_fields_set # type: ignore
329-
330-
def __len__(self) -> int:
331-
return len(self.model_fields_set)
332-
333-
def keys(self) -> Set[K]: # type: ignore
334-
return set(self.model_fields_set) # type: ignore
335-
336-
def __contains__(self, key: Any):
337-
return key in self.model_fields_set
338-
339-
def get(self, item: Any, default: D = None) -> Union[V, D]:
340-
return getattr(self, item, default)
341-
342-
@model_validator(mode="after")
343-
def validate_raw_mapping(self) -> Self:
344-
if not is_valid_yaml_mapping(self):
345-
raise AssertionError(f"{self} contains values unrepresentable in YAML")
346-
347-
return self
348-
349-
350-
class ConfigNode(FrozenDictNode[NotEmpty[str], YamlValue], frozen=True):
351-
model_config = {**Node.model_config, "extra": "allow"}
352-
353-
354-
class Kwargs(FrozenDictNode[NotEmpty[str], YamlValue], frozen=True):
355-
model_config = {**Node.model_config, "extra": "allow"}
297+
ConfigNode = Dict[NotEmpty[str], YamlValue]
298+
Kwargs = Dict[NotEmpty[str], YamlValue]

bioimageio/spec/application/v0_2.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from bioimageio.spec.generic.v0_2 import Maintainer as Maintainer
1313

1414

15-
class Application(GenericBase, frozen=True, title="bioimage.io application specification"):
15+
class Application(GenericBase, title="bioimage.io application specification"):
1616
"""Bioimage.io description of an application."""
1717

1818
type: Literal["application"] = "application"

bioimageio/spec/application/v0_3.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from bioimageio.spec.generic.v0_3 import Maintainer as Maintainer
1313

1414

15-
class Application(GenericBase, frozen=True, title="bioimage.io application specification"):
15+
class Application(GenericBase, title="bioimage.io application specification"):
1616
"""Bioimage.io description of an application."""
1717

1818
type: Literal["application"] = "application"

bioimageio/spec/collection/v0_2.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
EntryNode = Union[Annotated[Union[Model, Dataset, Application, Notebook], Field(discriminator="type")], Generic]
3737

3838

39-
class CollectionEntryBase(Node, extra="allow", frozen=True):
39+
class CollectionEntryBase(Node, extra="allow"):
4040
entry_adapter: ClassVar[TypeAdapter[Any]]
4141

4242
rdf_source: Annotated[
@@ -89,13 +89,11 @@ def set_entry(self, info: ValidationInfo) -> Self:
8989
if type_ == "collection":
9090
raise ValueError("Collections may not be nested!")
9191

92-
self.model_config["frozen"] = False
93-
self._entry = self.entry_adapter.validate_python(entry_data) # type: ignore[frozen]
94-
self.model_config["frozen"] = True
92+
self._entry = self.entry_adapter.validate_python(entry_data)
9593
return self
9694

9795

98-
class CollectionEntry(CollectionEntryBase, frozen=True):
96+
class CollectionEntry(CollectionEntryBase):
9997
"""A valid resource description (RD).
10098
The entry RD is based on the collection description itself.
10199
Fields are added/overwritten by the content of `rdf_source` if `rdf_source` is specified,
@@ -113,7 +111,7 @@ def entry(self) -> EntryNode:
113111
return self._entry
114112

115113

116-
class Collection(GenericBase, extra="allow", frozen=True, title="bioimage.io collection specification"):
114+
class Collection(GenericBase, extra="allow", title="bioimage.io collection specification"):
117115
"""A bioimage.io collection describes several other bioimage.io resources.
118116
Note that collections cannot be nested; resources listed under `collection` may not be collections themselves.
119117
"""

bioimageio/spec/collection/v0_3.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
]
3838

3939

40-
class CollectionEntry(v0_2.CollectionEntryBase, frozen=True):
40+
class CollectionEntry(v0_2.CollectionEntryBase):
4141
"""A valid resource description (RD).
4242
The entry RD is based on the collection description itself.
4343
Fields are added/overwritten by the content of `rdf_source` if `rdf_source` is specified,
@@ -55,7 +55,7 @@ def entry(self) -> EntryNode:
5555
return self._entry
5656

5757

58-
class Collection(GenericBase, extra="allow", frozen=True, title="bioimage.io collection specification"):
58+
class Collection(GenericBase, extra="allow", title="bioimage.io collection specification"):
5959
"""A bioimage.io collection resource description file (collection RDF) describes a collection of bioimage.io
6060
resources.
6161
The resources listed in a collection RDF have types other than 'collection'; collections cannot be nested.

bioimageio/spec/dataset/v0_2.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@
1010
from bioimageio.spec.generic.v0_2 import Maintainer as Maintainer
1111

1212

13-
class Dataset(GenericBase, frozen=True, title="bioimage.io dataset specification"):
13+
class Dataset(GenericBase, title="bioimage.io dataset specification"):
1414
"""A bioimage.io dataset resource description file (dataset RDF) describes a dataset relevant to bioimage
1515
processing.
1616
"""
1717

1818
type: Literal["dataset"] = "dataset"
1919

2020

21-
class LinkedDataset(LinkedResource, frozen=True):
21+
class LinkedDataset(LinkedResource):
2222
"""Reference to a bioimage.io dataset."""
2323

2424
id: DatasetId

bioimageio/spec/dataset/v0_3.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@
1212
from bioimageio.spec.generic.v0_3 import RelativeFilePath as RelativeFilePath
1313

1414

15-
class Dataset(GenericBase, frozen=True, title="bioimage.io dataset specification"):
15+
class Dataset(GenericBase, title="bioimage.io dataset specification"):
1616
"""A bioimage.io dataset resource description file (dataset RDF) describes a dataset relevant to bioimage
1717
processing.
1818
"""
1919

2020
type: Literal["dataset"] = "dataset"
2121

2222

23-
class LinkedDataset(LinkedResource, frozen=True):
23+
class LinkedDataset(LinkedResource):
2424
"""Reference to a bioimage.io dataset."""
2525

2626
id: DatasetId

bioimageio/spec/description.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
"""Any of the implemented resource descriptions"""
8989

9090

91-
class InvalidDescription(ResourceDescriptionBase, frozen=True, extra="allow", title="An invalid resource description"):
91+
class InvalidDescription(ResourceDescriptionBase, extra="allow", title="An invalid resource description"):
9292
type: Any = "unknown"
9393
format_version: Any = "unknown"
9494
fields_to_set_explicitly: ClassVar[FrozenSet[LiteralString]] = frozenset()

bioimageio/spec/generic/v0_2.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,14 @@
3434
)
3535

3636

37-
class Attachments(Node, frozen=True):
37+
class Attachments(Node):
3838
model_config = {**Node.model_config, "extra": "allow"}
3939
"""update pydantic model config to allow additional unknown keys"""
4040
files: Tuple[FileSource, ...] = ()
4141
"""∈📦 File attachments"""
4242

4343

44-
class _Person(Node, frozen=True):
44+
class _Person(Node):
4545
name: Optional[Annotated[str, Predicate(lambda s: "/" not in s and "\\" not in s)]]
4646
"""Full name"""
4747

@@ -68,17 +68,17 @@ def convert_name(cls, name: Any, info: FieldValidationInfo):
6868
"""
6969

7070

71-
class Author(_Person, frozen=True):
71+
class Author(_Person):
7272
name: Annotated[str, Predicate(lambda s: "/" not in s and "\\" not in s)]
7373
github_user: Optional[str] = None
7474

7575

76-
class Maintainer(_Person, frozen=True):
76+
class Maintainer(_Person):
7777
name: Optional[Annotated[str, Predicate(lambda s: "/" not in s and "\\" not in s)]] = None
7878
github_user: str
7979

8080

81-
class Badge(Node, title="Custom badge", frozen=True):
81+
class Badge(Node, title="Custom badge"):
8282
"""A custom badge"""
8383

8484
label: Annotated[str, Field(examples=["Open in Colab"])]
@@ -100,7 +100,7 @@ class Badge(Node, title="Custom badge", frozen=True):
100100
"""target URL"""
101101

102102

103-
class CiteEntry(Node, frozen=True):
103+
class CiteEntry(Node):
104104
text: str
105105
"""free text description"""
106106

@@ -132,14 +132,14 @@ def check_doi_or_url(cls, value: Optional[str], info: FieldValidationInfo) -> Op
132132
return value
133133

134134

135-
class LinkedResource(Node, frozen=True):
135+
class LinkedResource(Node):
136136
"""Reference to a bioimage.io resource"""
137137

138138
id: ResourceId
139139
"""A valid resource `id` from the bioimage.io collection."""
140140

141141

142-
class GenericBaseNoSource(ResourceDescriptionBase, frozen=True):
142+
class GenericBaseNoSource(ResourceDescriptionBase):
143143
"""GenericBaseNoFormatVersion without a source field
144144
145145
(needed because `model.v0_4.Model` and `model.v0_5.Model` have no `source` field)
@@ -326,7 +326,7 @@ def warn_about_tag_categories(cls, value: Tuple[str, ...], info: FieldValidation
326326
ResourceDescriptionType = TypeVar("ResourceDescriptionType", bound=GenericBaseNoSource)
327327

328328

329-
class GenericBaseNoFormatVersion(GenericBaseNoSource, frozen=True):
329+
class GenericBaseNoFormatVersion(GenericBaseNoSource):
330330
"""A generic node base without format version
331331
to allow a derived resource description to define its format_version independently."""
332332

@@ -336,7 +336,7 @@ class GenericBaseNoFormatVersion(GenericBaseNoSource, frozen=True):
336336
"""The primary source of the resource"""
337337

338338

339-
class GenericBase(GenericBaseNoFormatVersion, frozen=True):
339+
class GenericBase(GenericBaseNoFormatVersion):
340340
format_version: Literal["0.2.3"] = "0.2.3"
341341

342342
@classmethod
@@ -346,7 +346,7 @@ def convert_from_older_format(cls, data: RdfContent, context: InternalValidation
346346
convert_from_older_format(data, context)
347347

348348

349-
class Generic(GenericBase, extra="ignore", frozen=True, title="bioimage.io generic specification"):
349+
class Generic(GenericBase, extra="ignore", title="bioimage.io generic specification"):
350350
"""Specification of the fields used in a generic bioimage.io-compliant resource description file (RDF).
351351
352352
An RDF is a YAML file that describes a resource such as a model, a dataset, or a notebook.

bioimageio/spec/generic/v0_3.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,20 @@
3232
KNOWN_SPECIFIC_RESOURCE_TYPES = ("application", "collection", "dataset", "model", "notebook")
3333

3434

35-
class Attachment(Node, frozen=True):
35+
class Attachment(Node):
3636
source: FileSource
3737
"""∈📦 """
3838
sha256: Annotated[Optional[Sha256], warn(Sha256, "Missing SHA-256 hash value.")] = None
3939

4040

41-
class LinkedResource(Node, frozen=True):
41+
class LinkedResource(Node
4242
"""Reference to a bioimage.io resource"""
4343

4444
id: ResourceId
4545
"""A valid resource `id` from the official bioimage.io collection."""
4646

4747

48-
class GenericBaseNoSource(ResourceDescriptionBase, frozen=True):
48+
class GenericBaseNoSource(ResourceDescriptionBase ):
4949
"""GenericBaseNoFormatVersion without a source field
5050
5151
(because `bioimageio.spec.model.v0_5.ModelDescription has no source field)
@@ -230,14 +230,14 @@ def warn_about_tag_categories(cls, value: Tuple[str, ...], info: FieldValidation
230230
ResourceDescriptionType = TypeVar("ResourceDescriptionType", bound=GenericBaseNoSource)
231231

232232

233-
class GenericBaseNoFormatVersion(GenericBaseNoSource, frozen=True):
233+
class GenericBaseNoFormatVersion(GenericBaseNoSource ):
234234
"""GenericBase without a format version"""
235235

236236
source: Optional[FileSource] = None
237237
"""The primary source of the resource"""
238238

239239

240-
class GenericBase(GenericBaseNoFormatVersion, frozen=True, extra="ignore"):
240+
class GenericBase(GenericBaseNoFormatVersion , extra="ignore"):
241241
format_version: Literal["0.3.0"] = "0.3.0"
242242

243243
@classmethod
@@ -247,7 +247,7 @@ def convert_from_older_format(cls, data: RdfContent, context: InternalValidation
247247
convert_from_older_format(data, context)
248248

249249

250-
class Generic(GenericBase, frozen=True, title="bioimage.io generic specification"):
250+
class Generic(GenericBase , title="bioimage.io generic specification"):
251251
"""Specification of the fields used in a generic bioimage.io-compliant resource description file (RDF).
252252
253253
An RDF is a YAML file that describes a resource such as a model, a dataset, or a notebook.

0 commit comments

Comments
 (0)