Skip to content

Commit 641d738

Browse files
Allow to init CellMorphologyProtocol directly (#127)
1 parent 0b64d27 commit 641d738

File tree

5 files changed

+79
-25
lines changed

5 files changed

+79
-25
lines changed

examples/02_morphology.ipynb

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
" CellMorphology,\n",
2323
" CellMorphologyProtocol,\n",
2424
" Contribution,\n",
25-
" DigitalReconstructionCellMorphologyProtocol,\n",
2625
" MTypeClass,\n",
2726
" MTypeClassification,\n",
2827
" Organization,\n",
@@ -32,6 +31,7 @@
3231
" Subject,\n",
3332
")\n",
3433
"from entitysdk.types import (\n",
34+
" CellMorphologyGenerationType,\n",
3535
" CellMorphologyProtocolDesign,\n",
3636
" SlicingDirectionType,\n",
3737
")"
@@ -195,16 +195,37 @@
195195
"metadata": {},
196196
"outputs": [],
197197
"source": [
198-
"morphology_protocol = DigitalReconstructionCellMorphologyProtocol(\n",
198+
"morphology_protocol = CellMorphologyProtocol(\n",
199+
" generation_type=CellMorphologyGenerationType.digital_reconstruction,\n",
199200
" protocol_document=\"https://example.com/\",\n",
200201
" protocol_design=CellMorphologyProtocolDesign.cell_patch,\n",
201202
" slicing_thickness=20.0,\n",
202203
" slicing_direction=SlicingDirectionType.horizontal,\n",
203204
")\n",
205+
"rprint(morphology_protocol.__class__.__name__)\n",
204206
"morphology_protocol = client.register_entity(morphology_protocol)\n",
205207
"rprint(morphology_protocol)"
206208
]
207209
},
210+
{
211+
"cell_type": "markdown",
212+
"id": "ff0d1d01",
213+
"metadata": {},
214+
"source": [
215+
"### Optional, list morphology protocols"
216+
]
217+
},
218+
{
219+
"cell_type": "code",
220+
"execution_count": null,
221+
"id": "71818153",
222+
"metadata": {},
223+
"outputs": [],
224+
"source": [
225+
"protocols = client.search_entity(entity_type=CellMorphologyProtocol).all()\n",
226+
"rprint(protocols)"
227+
]
228+
},
208229
{
209230
"cell_type": "markdown",
210231
"id": "a0193055-3448-4d1c-99cc-4d953727ef4e",

src/entitysdk/models/__init__.py

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,7 @@
66
from entitysdk.models.brain_region import BrainRegion
77
from entitysdk.models.brain_region_hierarchy import BrainRegionHierarchy
88
from entitysdk.models.cell_morphology import CellMorphology
9-
from entitysdk.models.cell_morphology_protocol import (
10-
CellMorphologyProtocol,
11-
ComputationallySynthesizedCellMorphologyProtocol,
12-
DigitalReconstructionCellMorphologyProtocol,
13-
ModifiedReconstructionCellMorphologyProtocol,
14-
PlaceholderCellMorphologyProtocol,
15-
)
9+
from entitysdk.models.cell_morphology_protocol import CellMorphologyProtocol
1610
from entitysdk.models.circuit import Circuit
1711
from entitysdk.models.classification import ETypeClassification, MTypeClassification
1812
from entitysdk.models.contribution import Contribution, Role
@@ -54,11 +48,9 @@
5448
"CellMorphology",
5549
"CellMorphologyProtocol",
5650
"Circuit",
57-
"ComputationallySynthesizedCellMorphologyProtocol",
5851
"Consortium",
5952
"Contribution",
6053
"Derivation",
61-
"DigitalReconstructionCellMorphologyProtocol",
6254
"ElectricalCellRecording",
6355
"ElectricalRecordingStimulus",
6456
"EMCellMesh",
@@ -73,13 +65,11 @@
7365
"License",
7466
"MEModel",
7567
"MEModelCalibrationResult",
76-
"ModifiedReconstructionCellMorphologyProtocol",
7768
"MTypeClass",
7869
"MTypeClassification",
7970
"NeuronBlock",
8071
"Organization",
8172
"Person",
82-
"PlaceholderCellMorphologyProtocol",
8373
"Publication",
8474
"Role",
8575
"ScientificArtifactPublicationLink",

src/entitysdk/models/cell_morphology_protocol.py

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
from typing import Annotated, Any, ClassVar, Literal
44

5-
from pydantic import BaseModel, Field, HttpUrl, TypeAdapter
5+
from pydantic import ConfigDict, Field, HttpUrl, TypeAdapter
66

7+
from entitysdk.models.core import Identifiable
78
from entitysdk.models.entity import Entity
89
from entitysdk.types import (
910
CellMorphologyGenerationType,
@@ -16,9 +17,17 @@
1617

1718

1819
class CellMorphologyProtocolBase(Entity):
19-
"""Cell Morphology Protocol Base, used by all the protocols except placeholder."""
20+
"""Cell Morphology Protocol Base, used by all the protocols."""
21+
22+
# forbid extra parameters to prevent providing attributes of other classes by mistake
23+
model_config = ConfigDict(extra="forbid")
2024

2125
type: EntityType | None = EntityType.cell_morphology_protocol
26+
27+
28+
class CellMorphologyProtocolExtendedBase(CellMorphologyProtocolBase):
29+
"""Cell Morphology Protocol Extended Base, used by all the protocols except placeholder."""
30+
2231
protocol_document: Annotated[
2332
HttpUrl | None,
2433
Field(description="URL link to protocol document or publication."),
@@ -30,7 +39,7 @@ class CellMorphologyProtocolBase(Entity):
3039

3140

3241
class DigitalReconstructionCellMorphologyProtocol(
33-
CellMorphologyProtocolBase,
42+
CellMorphologyProtocolExtendedBase,
3443
):
3544
"""Experimental morphology method for capturing cell morphology data."""
3645

@@ -58,7 +67,7 @@ class DigitalReconstructionCellMorphologyProtocol(
5867

5968

6069
class ModifiedReconstructionCellMorphologyProtocol(
61-
CellMorphologyProtocolBase,
70+
CellMorphologyProtocolExtendedBase,
6271
):
6372
"""Modified Reconstruction Cell Morphology Protocol."""
6473

@@ -69,7 +78,7 @@ class ModifiedReconstructionCellMorphologyProtocol(
6978

7079

7180
class ComputationallySynthesizedCellMorphologyProtocol(
72-
CellMorphologyProtocolBase,
81+
CellMorphologyProtocolExtendedBase,
7382
):
7483
"""Computationally Synthesized Cell Morphology Protocol."""
7584

@@ -80,7 +89,7 @@ class ComputationallySynthesizedCellMorphologyProtocol(
8089

8190

8291
class PlaceholderCellMorphologyProtocol(
83-
Entity,
92+
CellMorphologyProtocolBase,
8493
):
8594
"""Placeholder Cell Morphology Protocol."""
8695

@@ -98,24 +107,31 @@ class PlaceholderCellMorphologyProtocol(
98107
]
99108

100109

101-
class CellMorphologyProtocol(BaseModel):
110+
class CellMorphologyProtocol(Identifiable):
102111
"""Polymorphic wrapper for consistent API, to be used for searching and retrieving.
103112
104113
The correct specific protocols are automatically instantiated.
105114
106-
For the registration it's possible to use any of the specific classes:
115+
For the registration it's possible to use this same class, or any of the specific classes:
107116
108117
- `DigitalReconstructionCellMorphologyProtocol`
109118
- `ModifiedReconstructionCellMorphologyProtocol`
110119
- `ComputationallySynthesizedCellMorphologyProtocol`
111120
- `PlaceholderCellMorphologyProtocol`
112-
113-
or this polymorphic class `CellMorphologyProtocol`, but in that case the instance should be
114-
created with `CellMorphologyProtocol.model_validate()` to instantiate the correct object.
115121
"""
116122

117123
_adapter: ClassVar[TypeAdapter] = TypeAdapter(CellMorphologyProtocolUnion)
118124

125+
def __new__(cls, *args, **kwargs) -> CellMorphologyProtocolUnion: # type: ignore[misc]
126+
"""Construct a CellMorphologyProtocol from keyword arguments."""
127+
if args:
128+
msg = "Positional args not supported, use keyword args instead."
129+
raise TypeError(msg)
130+
return cls._adapter.validate_python(kwargs)
131+
132+
def __init__(self, **kwargs: Any) -> None:
133+
"""Catch-all to satisfy type checkers."""
134+
119135
@classmethod
120136
def model_validate(cls, obj: Any, *args, **kwargs) -> CellMorphologyProtocolUnion: # type: ignore[override]
121137
"""Return the correct instance of CellMorphologyProtocolUnion."""

src/entitysdk/route.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"BrainLocation": "brain-location",
1212
"BrainRegion": "brain-region",
1313
"BrainRegionHierarchy": "brain-region-hierarchy",
14+
"CellMorphology": "cell-morphology",
1415
"Circuit": "circuit",
1516
"Consortium": "consortium",
1617
"Contribution": "contribution",
@@ -35,7 +36,6 @@
3536
"Organization": "organization",
3637
"Person": "person",
3738
"Publication": "publication",
38-
"CellMorphology": "cell-morphology",
3939
"ScientificArtifactPublicationLink": "scientific-artifact-publication-link",
4040
"Simulation": "simulation",
4141
"SimulationCampaign": "simulation-campaign",

tests/unit/models/test_cell_morphology_protocol.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from pathlib import Path
33

44
import pytest
5+
from pydantic import ValidationError
56

67
from entitysdk.models.cell_morphology_protocol import (
78
CellMorphologyProtocol,
@@ -112,6 +113,32 @@ def test_register(request, client, httpx_mock, auth_token, json_data_fixture, mo
112113
assert registered.model_dump(mode="json", exclude_unset=True) == expected_json
113114

114115

116+
@pytest.mark.parametrize(
117+
("json_data_fixture", "model_fixture"),
118+
[
119+
("json_digital_reconstruction", "model_digital_reconstruction"),
120+
("json_modified_reconstruction", "model_modified_reconstruction"),
121+
("json_computationally_synthesized", "model_computationally_synthesized"),
122+
("json_placeholder", "model_placeholder"),
123+
],
124+
)
125+
def test_adapter(request, json_data_fixture, model_fixture):
126+
json_data = request.getfixturevalue(json_data_fixture)
127+
model = request.getfixturevalue(model_fixture)
128+
129+
new_model = Model(**json_data)
130+
assert new_model == model
131+
132+
new_model = Model.model_validate(json_data)
133+
assert new_model == model
134+
135+
with pytest.raises(TypeError, match="Positional args not supported"):
136+
Model("name")
137+
138+
with pytest.raises(ValidationError, match="Extra inputs are not permitted"):
139+
Model(generation_type=json_data["generation_type"], invalid_input="invalid")
140+
141+
115142
@pytest.mark.parametrize(
116143
("json_data_fixture", "model_fixture"),
117144
[

0 commit comments

Comments
 (0)