diff --git a/bluecellulab/circuit/circuit_access.py b/bluecellulab/circuit/circuit_access.py index 2684a3b3..59ecb268 100644 --- a/bluecellulab/circuit/circuit_access.py +++ b/bluecellulab/circuit/circuit_access.py @@ -36,7 +36,6 @@ from bluepysnap.exceptions import BluepySnapError from bluepysnap import Circuit as SnapCircuit import pandas as pd -from pydantic import Extra from pydantic.dataclasses import dataclass from bluecellulab import circuit, neuron @@ -54,7 +53,7 @@ logger = logging.getLogger(__name__) -@dataclass(config=dict(extra=Extra.forbid)) +@dataclass(config=dict(extra="forbid")) class EmodelProperties: threshold_current: float holding_current: float diff --git a/bluecellulab/circuit/config/sections.py b/bluecellulab/circuit/config/sections.py index e236cc6f..4ceb8dd9 100644 --- a/bluecellulab/circuit/config/sections.py +++ b/bluecellulab/circuit/config/sections.py @@ -14,10 +14,9 @@ """Classes to represent config sections.""" from __future__ import annotations -from typing import Optional +from typing import Literal, Optional -from pydantic import Extra, Field, validator -from pydantic.typing import Literal +from pydantic import field_validator, Field from pydantic.dataclasses import dataclass from libsonata._libsonata import Conditions as LibSonataConditions @@ -34,14 +33,14 @@ def string_to_bool(value: str) -> bool: raise ValueError(f"Invalid boolean value {value}") -@dataclass(frozen=True, config=dict(extra=Extra.forbid)) +@dataclass(frozen=True, config=dict(extra="forbid")) class ConditionEntry: """For mechanism specific conditions.""" minis_single_vesicle: Optional[int] = Field(None, ge=0, le=1) init_depleted: Optional[int] = Field(None, ge=0, le=1) -@dataclass(frozen=True, config=dict(extra=Extra.forbid)) +@dataclass(frozen=True, config=dict(extra="forbid")) class MechanismConditions: """For mechanism specific conditions.""" ampanmda: Optional[ConditionEntry] = None @@ -49,7 +48,7 @@ class MechanismConditions: glusynapse: Optional[ConditionEntry] = None -@dataclass(frozen=True, config=dict(extra=Extra.forbid)) +@dataclass(frozen=True, config=dict(extra="forbid")) class Conditions: mech_conditions: Optional[MechanismConditions] = None celsius: Optional[float] = None @@ -125,7 +124,7 @@ def init_empty(cls) -> Conditions: ) -@dataclass(frozen=True, config=dict(extra=Extra.forbid)) +@dataclass(frozen=True, config=dict(extra="forbid")) class ConnectionOverrides: source: str target: str @@ -135,7 +134,8 @@ class ConnectionOverrides: synapse_configure: Optional[str] = None mod_override: Optional[Literal["GluSynapse"]] = None - @validator("mod_override") + @field_validator("mod_override") + @classmethod def validate_mod_override(cls, value): """Make sure the mod file to override is present.""" if isinstance(value, str) and not hasattr(bluecellulab.neuron.h, value): diff --git a/bluecellulab/stimuli.py b/bluecellulab/stimuli.py index e603ca8b..9b8c7a01 100644 --- a/bluecellulab/stimuli.py +++ b/bluecellulab/stimuli.py @@ -19,7 +19,7 @@ from typing import Optional import warnings -from pydantic import Extra, NonNegativeFloat, PositiveFloat, validator +from pydantic import field_validator, NonNegativeFloat, PositiveFloat from pydantic.dataclasses import dataclass @@ -92,7 +92,7 @@ def from_sonata(cls, pattern: str) -> Pattern: raise ValueError(f"Unknown pattern {pattern}") -@dataclass(frozen=True, config=dict(extra=Extra.forbid)) +@dataclass(frozen=True, config=dict(extra="forbid")) class Stimulus: target: str delay: NonNegativeFloat @@ -301,42 +301,43 @@ def from_sonata(cls, stimulus_entry: dict) -> Optional[Stimulus]: raise ValueError(f"Unknown pattern {pattern}") -@dataclass(frozen=True, config=dict(extra=Extra.forbid)) +@dataclass(frozen=True, config=dict(extra="forbid")) class Noise(Stimulus): mean_percent: float variance: float -@dataclass(frozen=True, config=dict(extra=Extra.forbid)) +@dataclass(frozen=True, config=dict(extra="forbid")) class Hyperpolarizing(Stimulus): ... -@dataclass(frozen=True, config=dict(extra=Extra.forbid)) +@dataclass(frozen=True, config=dict(extra="forbid")) class Pulse(Stimulus): amp_start: float width: float frequency: float -@dataclass(frozen=True, config=dict(extra=Extra.forbid)) +@dataclass(frozen=True, config=dict(extra="forbid")) class RelativeLinear(Stimulus): percent_start: float -@dataclass(frozen=True, config=dict(extra=Extra.forbid)) +@dataclass(frozen=True, config=dict(extra="forbid")) class SynapseReplay(Stimulus): spike_file: str source: str - @validator("spike_file") + @field_validator("spike_file") + @classmethod def spike_file_exists(cls, v): if not Path(v).exists(): raise ValueError(f"spike_file {v} does not exist") return v -@dataclass(frozen=True, config=dict(extra=Extra.forbid)) +@dataclass(frozen=True, config=dict(extra="forbid")) class ShotNoise(Stimulus): rise_time: float decay_time: float @@ -348,14 +349,15 @@ class ShotNoise(Stimulus): mode: ClampMode = ClampMode.CURRENT reversal: float = 0.0 - @validator("decay_time") + @field_validator("decay_time") + @classmethod def decay_time_gt_rise_time(cls, v, values): - if v <= values["rise_time"]: + if v <= values.data["rise_time"]: raise ValueError("decay_time must be greater than rise_time") return v -@dataclass(frozen=True, config=dict(extra=Extra.forbid)) +@dataclass(frozen=True, config=dict(extra="forbid")) class RelativeShotNoise(Stimulus): rise_time: float decay_time: float @@ -367,14 +369,15 @@ class RelativeShotNoise(Stimulus): mode: ClampMode = ClampMode.CURRENT reversal: float = 0.0 - @validator("decay_time") + @field_validator("decay_time") + @classmethod def decay_time_gt_rise_time(cls, v, values): - if v <= values["rise_time"]: + if v <= values.data["rise_time"]: raise ValueError("decay_time must be greater than rise_time") return v -@dataclass(frozen=True, config=dict(extra=Extra.forbid)) +@dataclass(frozen=True, config=dict(extra="forbid")) class OrnsteinUhlenbeck(Stimulus): tau: float sigma: PositiveFloat @@ -384,9 +387,10 @@ class OrnsteinUhlenbeck(Stimulus): mode: ClampMode = ClampMode.CURRENT reversal: float = 0.0 - @validator("mean") + @field_validator("mean") + @classmethod def mean_in_range(cls, v, values): - if v < 0 and abs(v) > 2 * values["sigma"]: + if v < 0 and abs(v) > 2 * values.data["sigma"]: warnings.warn( "mean is outside of range [0, 2*sigma],", " ornstein uhlenbeck signal is mostly zero.", @@ -394,7 +398,7 @@ def mean_in_range(cls, v, values): return v -@dataclass(frozen=True, config=dict(extra=Extra.forbid)) +@dataclass(frozen=True, config=dict(extra="forbid")) class RelativeOrnsteinUhlenbeck(Stimulus): tau: float mean_percent: float diff --git a/examples/2-sonata-network/sonata-network.ipynb b/examples/2-sonata-network/sonata-network.ipynb index f26f4d2a..65452eff 100644 --- a/examples/2-sonata-network/sonata-network.ipynb +++ b/examples/2-sonata-network/sonata-network.ipynb @@ -24,7 +24,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 37, "metadata": { "vscode": { "languageId": "shellscript" @@ -35,7 +35,9 @@ "name": "stdout", "output_type": "stream", "text": [ - "/home/anil/git-repos/BlueCelluLab/examples/2-sonata-network\n", + "/home/anilbey/.virtualenvs/bluecellulab311/bin/nrnivmodl:10: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html\n", + " from pkg_resources import working_set\n", + "/home/anilbey/git-repos/BlueCelluLab/examples/2-sonata-network\n", "Mod files: \"../mechanisms/../mechanisms/CaDynamics_DC0.mod\" \"../mechanisms/../mechanisms/CaDynamics_E2.mod\" \"../mechanisms/../mechanisms/Ca_HVA2.mod\" \"../mechanisms/../mechanisms/Ca_HVA.mod\" \"../mechanisms/../mechanisms/Ca_LVAst.mod\" \"../mechanisms/../mechanisms/Ca.mod\" \"../mechanisms/../mechanisms/DetAMPANMDA.mod\" \"../mechanisms/../mechanisms/DetGABAAB.mod\" \"../mechanisms/../mechanisms/gap.mod\" \"../mechanisms/../mechanisms/GluSynapse.mod\" \"../mechanisms/../mechanisms/Ih.mod\" \"../mechanisms/../mechanisms/Im.mod\" \"../mechanisms/../mechanisms/KdShu2007.mod\" \"../mechanisms/../mechanisms/K_Pst.mod\" \"../mechanisms/../mechanisms/K_Tst.mod\" \"../mechanisms/../mechanisms/Nap_Et2.mod\" \"../mechanisms/../mechanisms/NaTa_t.mod\" \"../mechanisms/../mechanisms/NaTg.mod\" \"../mechanisms/../mechanisms/NaTs2_t.mod\" \"../mechanisms/../mechanisms/netstim_inhpoisson.mod\" \"../mechanisms/../mechanisms/ProbAMPANMDA_EMS.mod\" \"../mechanisms/../mechanisms/ProbGABAAB_EMS.mod\" \"../mechanisms/../mechanisms/SK_E2.mod\" \"../mechanisms/../mechanisms/SKv3_1.mod\" \"../mechanisms/../mechanisms/StochKv3.mod\" \"../mechanisms/../mechanisms/StochKv.mod\" \"../mechanisms/../mechanisms/TTXDynamicsSwitch.mod\" \"../mechanisms/../mechanisms/VecStim.mod\"\n", "\n", " -> \u001b[32mCompiling\u001b[0m mod_func.cpp\n", @@ -58,7 +60,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 38, "metadata": {}, "outputs": [], "source": [ @@ -84,7 +86,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 39, "metadata": {}, "outputs": [], "source": [ @@ -101,7 +103,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 40, "metadata": {}, "outputs": [], "source": [ @@ -119,7 +121,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 41, "metadata": {}, "outputs": [ { @@ -195,7 +197,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 42, "metadata": {}, "outputs": [ { @@ -228,17 +230,9 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 43, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\u001b[32mNo Error: Success.\u001b[0m\n" - ] - } - ], + "outputs": [], "source": [ "sim = SSim(simulation_config)" ] @@ -253,16 +247,16 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 44, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "[Hyperpolarizing(pattern=, target='Mosaic_A', delay=0.0, duration=4000.0)]" + "[Hyperpolarizing(target='Mosaic_A', delay=0.0, duration=4000.0)]" ] }, - "execution_count": 42, + "execution_count": 44, "metadata": {}, "output_type": "execute_result" } @@ -274,16 +268,16 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 45, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "Hyperpolarizing(pattern=, target='Mosaic_A', delay=0.0, duration=4000.0)" + "Hyperpolarizing(target='Mosaic_A', delay=0.0, duration=4000.0)" ] }, - "execution_count": 43, + "execution_count": 45, "metadata": {}, "output_type": "execute_result" } @@ -307,7 +301,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 46, "metadata": {}, "outputs": [], "source": [ @@ -324,7 +318,7 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 47, "metadata": {}, "outputs": [], "source": [ @@ -343,7 +337,16 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 51, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd" + ] + }, + { + "cell_type": "code", + "execution_count": 55, "metadata": {}, "outputs": [ { @@ -535,13 +538,13 @@ "[3 rows x 21 columns]" ] }, - "execution_count": 46, + "execution_count": 55, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "snap_access.circuit.nodes.get(\"Mosaic_A\")" + "pd.concat([x[1] for x in snap_access.circuit.nodes.get(\"Mosaic_A\")])" ] }, { @@ -560,7 +563,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 57, "metadata": {}, "outputs": [ { @@ -813,13 +816,13 @@ "[5 rows x 21 columns]" ] }, - "execution_count": 47, + "execution_count": 57, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "all_nodes = snap_access.circuit.nodes.get()\n", + "all_nodes = pd.concat([x[1] for x in snap_access.circuit.nodes.get()])\n", "all_nodes" ] }, @@ -833,7 +836,7 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 58, "metadata": {}, "outputs": [ { @@ -842,7 +845,7 @@ "[('NodeA', 0), ('NodeA', 1), ('NodeB', 0), ('NodeB', 1)]" ] }, - "execution_count": 48, + "execution_count": 58, "metadata": {}, "output_type": "execute_result" } @@ -854,7 +857,7 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 59, "metadata": {}, "outputs": [], "source": [ diff --git a/setup.py b/setup.py index 9ce7e8ed..7d1ac55e 100644 --- a/setup.py +++ b/setup.py @@ -12,13 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -import sys import setuptools -if sys.version_info[:2] < (3, 7): - raise RuntimeError("Python version >= 3.7 required.") - # Read the README.rst file with open("README.rst", "r", encoding="utf-8") as fh: long_description = fh.read() @@ -29,6 +25,7 @@ 'version_scheme': 'python-simplified-semver', 'local_scheme': 'no-local-version' }, + python_requires='>=3.8', setup_requires=['setuptools_scm'], packages=setuptools.find_packages(include=['bluecellulab', 'bluecellulab.*']), author="Blue Brain Project, EPFL", @@ -41,8 +38,8 @@ "numpy>=1.8.0,<2.0.0", "matplotlib>=3.0.0,<4.0.0", "pandas>=1.0.0,<3.0.0", - "bluepysnap>=1.0.5,<2.0.0", - "pydantic>=1.10.2,<2.0.0", + "bluepysnap>=2.0.0,<3.0.0", + "pydantic>=2.5.2,<3.0.0", "typing-extensions>=4.8.0" ], keywords=[ diff --git a/tests/test_cell/test_injector.py b/tests/test_cell/test_injector.py index 596a52dd..63da23aa 100644 --- a/tests/test_cell/test_injector.py +++ b/tests/test_cell/test_injector.py @@ -56,7 +56,7 @@ def test_inject_pulse(self): assert tstim.stim.to_python() == [0.0, 4.0, 4.0, 0.0, 0.0] assert tstim.tvec.to_python() == [2.0, 2.0, 4.0, 4.0, 22.0] - with raises(TypeError): + with raises(ValidationError): unsupported_stimulus = Pulse( target="single-cell", delay=2,