Skip to content

Commit

Permalink
Morphology parsers & pipeline blocks (#799)
Browse files Browse the repository at this point in the history
* skip inferring a point at certain morphology transitions

* add shortform to `types`

* add scaffold type hint

* separated parsing code

* deleted old divio folder

* type casting for `property`. closes #681

* lint

* correct docs: params are kwargs

* use `parse_morphology_file`

* remove more dead code replaced by new parse func

* allow pipeline node in morphologies block

* added morphio flags to parser

* reusable morpho pipelines, with customizable parsers

* docstr tweak

* bump dep

* fix docs

---------

Co-authored-by: danilobenozzo <danilo.benozzo@gmail.com>
  • Loading branch information
Helveg and danilobenozzo committed Jan 16, 2024
1 parent 00e43c6 commit e4f9e90
Show file tree
Hide file tree
Showing 31 changed files with 758 additions and 6,187 deletions.
19 changes: 12 additions & 7 deletions bsb/config/_attrs.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,15 +252,17 @@ def slot(**kwargs):
return ConfigurationAttributeSlot(**kwargs)


def property(val=None, /, **kwargs):
def property(val=None, /, type=None, **kwargs):
"""
Create a configuration property attribute. You may provide a value or a callable. Call
`setter` on the return value as you would with a regular property.
"""
if type is None:
type = lambda v: v

def decorator(val):
prop = val if callable(val) else lambda s: val
return ConfigurationProperty(prop, **kwargs)
return ConfigurationProperty(prop, type=type, **kwargs)

if val is None:
return decorator
Expand Down Expand Up @@ -440,16 +442,18 @@ def __get__(self, instance, owner):
return self
return _getattr(instance, self.attr_name)

def fset(self, instance, value):
return _setattr(instance, self.attr_name, value)

def __set__(self, instance, value):
if _hasattr(instance, self.attr_name):
ex_value = _getattr(instance, self.attr_name)
_unset_nodes(ex_value)
if value is None:
# Don't cast None to a value of the attribute type.
return _setattr(instance, self.attr_name, None)
# Don't try to cast None to a value of the attribute type.
return self.fset(instance, None)
try:
value = self.type(value, _parent=instance, _key=self.attr_name)
self.flag_dirty(instance)
except ValueError:
# This value error should only arise when users are manually setting
# attributes in an already bootstrapped config tree.
Expand All @@ -467,8 +471,9 @@ def __set__(self, instance, value):
instance,
self.attr_name,
) from e
self.flag_dirty(instance)
# The value was cast to its intented type and the new value can be set.
_setattr(instance, self.attr_name, value)
self.fset(instance, value)
root = _strict_root(instance)
if _is_booted(root):
_boot_nodes(value, root.scaffold)
Expand Down Expand Up @@ -1058,7 +1063,7 @@ def __set__(self, instance, value):
e.node = self
raise e
else:
return self.fset(instance, value)
return super().__set__(instance, value)


def _collect_kv(n, d, k, v):
Expand Down
8 changes: 6 additions & 2 deletions bsb/config/_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@
from ..placement import PlacementStrategy
from ..postprocessing import PostProcessingHook
from ..simulation.simulation import Simulation
from ..storage._files import CodeDependencyNode, MorphologyDependencyNode
from ..storage._files import (
CodeDependencyNode,
MorphologyDependencyNode,
MorphologyPipelineNode,
)
from ..storage.interfaces import StorageNode
from ..topology import Partition, Region, RegionGroup, create_topology, get_partitions
from . import types
Expand Down Expand Up @@ -60,7 +64,7 @@ class Configuration:
type=CodeDependencyNode,
)
morphologies: cfglist[MorphologyDependencyNode] = config.list(
type=MorphologyDependencyNode,
type=types.or_(MorphologyDependencyNode, MorphologyPipelineNode),
)
storage: StorageNode = config.attr(
type=StorageNode,
Expand Down
14 changes: 7 additions & 7 deletions bsb/config/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -705,16 +705,14 @@ def type_handler(value, _parent, _key=None):
return type_handler


def mut_excl(*mutuals, required=True, max=1):
def mut_excl(*mutuals, required=True, max=1, shortform=False):
"""
Requirement handler for mutually exclusive attributes.
:param mutuals: The keys of the mutually exclusive attributes.
:type mutuals: str
:param required: Whether at least one of the keys is required
:type required: bool
:param max: The maximum amount of keys that may occur together.
:type max: int
:param str mutuals: The keys of the mutually exclusive attributes.
:param bool required: Whether at least one of the keys is required
:param int max: The maximum amount of keys that may occur together.
:param bool shortform: Allow the short form alternative.
:returns: Requirement function
:rtype: Callable
"""
Expand All @@ -723,6 +721,8 @@ def mut_excl(*mutuals, required=True, max=1):
listed += f" {{}} `{mutuals[-1]}`"

def requirement(section):
if shortform and section.is_shortform:
return False
bools = [m in section for m in mutuals]
given = sum(bools)
if given > max:
Expand Down

0 comments on commit e4f9e90

Please sign in to comment.