Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion glotaran/builtin/io/folder/folder_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ def save_result(
"glotaran.io.save_result(result, Path(result_path) / 'result.yml')"
),
to_be_removed_in_version="0.8.0",
stacklevel=5,
)

return save_result(
Expand Down Expand Up @@ -143,7 +144,8 @@ def save_result(
"The folder plugin is only intended for internal use by other plugins "
"as quick way to save most of the files. The saved result will be incomplete, "
"thus it is not recommended to be used directly."
)
),
stacklevel=4,
)

result_folder = Path(result_path)
Expand Down
1 change: 1 addition & 0 deletions glotaran/builtin/io/folder/test/test_folder_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def test_save_result_folder(
)

assert len(record) == 1
assert Path(record[0].filename) == Path(__file__)
if format_name == "legacy":
assert record[0].category == GlotaranApiDeprecationWarning
else:
Expand Down
49 changes: 49 additions & 0 deletions glotaran/builtin/io/yml/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""Utility functionality module for ``glotaran.builtin.io.yml.yml``"""
from __future__ import annotations

from typing import TYPE_CHECKING

from ruamel.yaml import YAML
from ruamel.yaml.compat import StringIO

if TYPE_CHECKING:
from typing import Any
from typing import Mapping

from ruamel.yaml.nodes import ScalarNode
from ruamel.yaml.representer import BaseRepresenter


def write_dict(data: Mapping[str, Any], file_name: str | None = None) -> str | None:
yaml = YAML()
yaml.representer.add_representer(type(None), _yaml_none_representer)
yaml.indent(mapping=2, sequence=2, offset=2)
if file_name is not None:
with open(file_name, "w") as f:
yaml.dump(data, f)
else:
stream = StringIO()
yaml.dump(data, stream)
return stream.getvalue()


def _yaml_none_representer(representer: BaseRepresenter, data: Mapping[str, Any]) -> ScalarNode:
"""Yaml repr for ``None`` python values.

Parameters
----------
representer : BaseRepresenter
Representer of the :class:`YAML` instance.
data : Mapping[str, Any]
Data to write to yaml.

Returns
-------
ScalarNode
Node representing the value.

References
----------
https://stackoverflow.com/a/44314840
"""
return representer.represent_scalar("tag:yaml.org,2002:null", "null")
45 changes: 3 additions & 42 deletions glotaran/builtin/io/yml/yml.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
from typing import TYPE_CHECKING

from ruamel.yaml import YAML
from ruamel.yaml.compat import StringIO

from glotaran.builtin.io.yml.utils import write_dict
from glotaran.deprecation.modules.builtin_io_yml import model_spec_deprecations
from glotaran.deprecation.modules.builtin_io_yml import scheme_spec_deprecations
from glotaran.io import SAVING_OPTIONS_DEFAULT
Expand All @@ -17,18 +17,14 @@
from glotaran.io import save_scheme
from glotaran.model import Model
from glotaran.parameter import ParameterGroup
from glotaran.project import Result
from glotaran.project import Scheme
from glotaran.project.dataclass_helpers import asdict
from glotaran.project.dataclass_helpers import fromdict
from glotaran.project.project import Result
from glotaran.project.scheme import Scheme
from glotaran.utils.sanitize import sanitize_yaml

if TYPE_CHECKING:
from typing import Any
from typing import Mapping

from ruamel.yaml.nodes import ScalarNode
from ruamel.yaml.representer import BaseRepresenter


@register_project_io(["yml", "yaml", "yml_str"])
Expand Down Expand Up @@ -199,38 +195,3 @@ def _load_yml(self, file_name: str) -> dict[str, Any]:
with open(file_name) as f:
spec = yaml.load(f)
return spec


def write_dict(data: Mapping[str, Any], file_name: str | None = None) -> str | None:
yaml = YAML()
yaml.representer.add_representer(type(None), _yaml_none_representer)
yaml.indent(mapping=2, sequence=2, offset=2)
if file_name is not None:
with open(file_name, "w") as f:
yaml.dump(data, f)
else:
stream = StringIO()
yaml.dump(data, stream)
return stream.getvalue()


def _yaml_none_representer(representer: BaseRepresenter, data: Mapping[str, Any]) -> ScalarNode:
"""Yaml repr for ``None`` python values.

Parameters
----------
representer : BaseRepresenter
Representer of the :class:`YAML` instance.
data : Mapping[str, Any]
Data to write to yaml.

Returns
-------
ScalarNode
Node representing the value.

References
----------
https://stackoverflow.com/a/44314840
"""
return representer.represent_scalar("tag:yaml.org,2002:null", "null")
19 changes: 19 additions & 0 deletions glotaran/model/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ def decorator(cls):
markdown = _markdown_factory(cls)
setattr(cls, "markdown", markdown)

get_parameter_labels = _get_parameter_labels_factory(cls)
setattr(cls, "get_parameter_labels", get_parameter_labels)

return cls

return decorator
Expand Down Expand Up @@ -372,3 +375,19 @@ def mprint_item(
return MarkdownStr(md)

return mprint_item


def _get_parameter_labels_factory(cls):
@wrap_func_as_method(cls, name="get_parameter_labels")
def get_parameter_labels(self) -> list[str]:

parameter_labels = []

for name in self._glotaran_properties:
prop = getattr(self.__class__, name)
value = getattr(self, name)
parameter_labels += prop.glotaran_get_parameter_labels(value)

return parameter_labels

return get_parameter_labels
21 changes: 8 additions & 13 deletions glotaran/model/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,7 @@ def from_dict(
warn(f"Unknown model item type '{item_name}'.")
continue

is_list = isinstance(getattr(model, item_name), list)

if is_list:
if is_list := isinstance(getattr(model, item_name), list):
model._add_list_items(item_name, items)
else:
model._add_dict_items(item_name, items)
Expand All @@ -138,8 +136,7 @@ def _add_dict_items(self, item_name: str, items: dict):

for label, item in items.items():
item_cls = self.model_items[item_name]
is_typed = hasattr(item_cls, "_glotaran_model_item_typed")
if is_typed:
if is_typed := hasattr(item_cls, "_glotaran_model_item_typed"):
if "type" not in item and item_cls.get_default_type() is None:
raise ValueError(f"Missing type for attribute '{item_name}'")
item_type = item.get("type", item_cls.get_default_type())
Expand All @@ -156,8 +153,7 @@ def _add_list_items(self, item_name: str, items: list):

for item in items:
item_cls = self.model_items[item_name]
is_typed = hasattr(item_cls, "_glotaran_model_item_typed")
if is_typed:
if is_typed := hasattr(item_cls, "_glotaran_model_item_typed"):
if "type" not in item:
raise ValueError(f"Missing type for attribute '{item_name}'")
item_type = item["type"]
Expand Down Expand Up @@ -312,14 +308,14 @@ def as_dict(self) -> dict:

return model_dict

def get_parameters(self) -> list[str]:
parameters = []
def get_parameter_labels(self) -> list[str]:
parameter_labels = []
for item_name in self.model_items:
items = getattr(self, item_name)
item_iterator = items if isinstance(items, list) else items.values()
for item in item_iterator:
parameters += item.get_parameters()
return parameters
parameter_labels += item.get_parameter_labels()
return parameter_labels

def need_index_dependent(self) -> bool:
"""Returns true if e.g. clp_relations with intervals are present."""
Expand Down Expand Up @@ -374,8 +370,7 @@ def validate(self, parameters: ParameterGroup = None, raise_exception: bool = Fa
"""
result = ""

problems = self.problem_list(parameters)
if problems:
if problems := self.problem_list(parameters):
result = f"Your model has {len(problems)} problems:\n"
for p in problems:
result += f"\n * {p}"
Expand Down
21 changes: 21 additions & 0 deletions glotaran/model/property.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,27 @@ def glotaran_format_value(
else str(value)
)

def glotaran_get_parameter_labels(self, value: Any) -> list[str]:
"""Get a list of all parameter labels if the property is parameter.

Parameters
----------
value : Any
The value of the property.

Returns
-------
list[str]
The list of full parameter labels.
"""
if value is None or not self.glotaran_is_parameter_property:
return []
elif self.glotaran_is_sequence_property:
return [v.full_label for v in value]
elif self.glotaran_is_mapping_property:
return [v.full_label for v in value.values()]
return [value.full_label]


def _model_property_getter_factory(cls: type, model_property: ModelProperty) -> Callable:
"""Create a getter function for model property.
Expand Down
1 change: 1 addition & 0 deletions glotaran/project/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""The glotaran project package."""

from glotaran.project.project import Project
from glotaran.project.result import Result
from glotaran.project.scheme import Scheme
2 changes: 1 addition & 1 deletion glotaran/project/generators/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from typing import TypedDict
from typing import cast

from glotaran.builtin.io.yml.yml import write_dict
from glotaran.builtin.io.yml.utils import write_dict
from glotaran.model import Model


Expand Down
Loading