From 11c600953a09f22b289e608ef6381d01e1e7a9ed Mon Sep 17 00:00:00 2001 From: Martin Lehmann Date: Mon, 12 Aug 2024 12:15:18 +0200 Subject: [PATCH 1/2] feat: Upgrade to capellambse v0.6.x --- .pre-commit-config.yaml | 2 +- capella_model_explorer/backend/explorer.py | 24 +++++-------- capella_model_explorer/backend/model_diff.py | 37 +++++++++----------- capella_model_explorer/backend/templates.py | 3 +- pyproject.toml | 4 +-- 5 files changed, 30 insertions(+), 40 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5030375..e892cd5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -51,7 +51,7 @@ repos: hooks: - id: mypy additional_dependencies: - - capellambse==0.5.72 + - capellambse==0.6.6 - types-pyyaml==6.0.11 - repo: https://github.com/pylint-dev/pylint rev: v3.2.7 diff --git a/capella_model_explorer/backend/explorer.py b/capella_model_explorer/backend/explorer.py index a2769e0..c56c541 100644 --- a/capella_model_explorer/backend/explorer.py +++ b/capella_model_explorer/backend/explorer.py @@ -12,6 +12,7 @@ from pathlib import Path import capellambse +import capellambse.model as m import fastapi import markupsafe import prometheus_client @@ -113,15 +114,9 @@ def __make_href_filter(self, obj: object) -> str | None: if is_undefined(obj) or obj is None: return "#" - if isinstance(obj, capellambse.model.ElementList): + if isinstance(obj, m.ElementList): raise TypeError("Cannot make an href to a list of elements") - if not isinstance( - obj, - ( - capellambse.model.GenericElement, - capellambse.model.diagram.AbstractDiagram, - ), - ): + if not isinstance(obj, (m.ModelElement, m.AbstractDiagram)): raise TypeError(f"Expected a model object, got {obj!r}") try: @@ -132,11 +127,7 @@ def __make_href_filter(self, obj: object) -> str | None: return self.__make_href(obj) def __make_href( - self, - obj: ( - capellambse.model.GenericElement - | capellambse.model.diagram.AbstractDiagram - ), + self, obj: m.ModelElement | m.AbstractDiagram ) -> str | None: if self.templates_index is None: return None @@ -265,12 +256,13 @@ def render_template(template_name: str, object_id: str): @self.router.get("/api/model-info") def model_info(): info = self.model.info + resinfo = info.resources["\x00"] return { "title": info.title, - "revision": info.revision, - "hash": info.rev_hash, + "revision": resinfo.revision, + "hash": resinfo.rev_hash, "capella_version": info.capella_version, - "branch": info.branch, + "branch": resinfo.branch, "badge": self.model.description_badge, } diff --git a/capella_model_explorer/backend/model_diff.py b/capella_model_explorer/backend/model_diff.py index 03d2b79..c4f7cce 100644 --- a/capella_model_explorer/backend/model_diff.py +++ b/capella_model_explorer/backend/model_diff.py @@ -10,7 +10,6 @@ import capellambse import capellambse.model as m -import capellambse.model.common as c import diff_match_patch import typing_extensions as te from capellambse.filehandler import git, local @@ -125,9 +124,9 @@ def populate_commits(model: capellambse.MelodyModel): def _serialize_obj(obj: t.Any) -> t.Any: - if isinstance(obj, c.GenericElement): + if isinstance(obj, m.ModelElement): return {"uuid": obj.uuid, "display_name": _get_name(obj)} - elif isinstance(obj, c.ElementList): + elif isinstance(obj, m.ElementList): return [{"uuid": i.uuid, "display_name": _get_name(i)} for i in obj] elif isinstance(obj, (enum.Enum, enum.Flag)): return obj.name @@ -202,7 +201,7 @@ def get_commit_hashes(path: str) -> list[RevisionInfo]: return commits -def _get_name(obj: m.diagram.Diagram | c.ModelObject) -> str: +def _get_name(obj: m.ModelObject) -> str: """Return the object's name. If the object doesn't own a name, its type is returned instead. @@ -242,18 +241,16 @@ def compare_objects( children: dict[str, t.Any] = {} for attr in dir(type(new_object)): acc = getattr(type(new_object), attr, None) - if isinstance(acc, c.AttributeProperty): - _handle_attribute_property( - attr, old_object, new_object, attributes - ) + if isinstance(acc, m.BasePOD): + _handle_pod(attr, old_object, new_object, attributes) elif isinstance( - acc, c.AttrProxyAccessor | c.LinkAccessor | c.ParentAccessor + acc, m.AttrProxyAccessor | m.LinkAccessor | m.ParentAccessor ): _handle_accessors(attr, old_object, new_object, attributes) elif ( # pylint: disable=unidiomatic-typecheck - type(acc) is c.RoleTagAccessor - or (type(acc) is c.DirectProxyAccessor and not acc.rootelem) + type(acc) is m.RoleTagAccessor + or (type(acc) is m.DirectProxyAccessor and not acc.rootelem) ): _handle_direct_accessors( attr, old_object, new_object, children, old_model @@ -270,7 +267,7 @@ def compare_objects( return {} -def _handle_attribute_property(attr, old_object, new_object, attributes): +def _handle_pod(attr, old_object, new_object, attributes): if attr != "uuid": try: old_value = getattr(old_object, attr, None) @@ -289,8 +286,8 @@ def _handle_attribute_property(attr, old_object, new_object, attributes): def _handle_accessors(attr, old_object, new_object, attributes): old_value = getattr(old_object, attr, None) new_value = getattr(new_object, attr, None) - if isinstance(old_value, c.GenericElement | type(None)) and isinstance( - new_value, c.GenericElement | type(None) + if isinstance(old_value, m.ModelElement | type(None)) and isinstance( + new_value, m.ModelElement | type(None) ): if old_value is new_value is None: pass @@ -309,8 +306,8 @@ def _handle_accessors(attr, old_object, new_object, attributes): "previous": _serialize_obj(old_value), "current": _serialize_obj(new_value), } - elif isinstance(old_value, c.ElementList | type(None)) and isinstance( - new_value, c.ElementList + elif isinstance(old_value, m.ElementList | type(None)) and isinstance( + new_value, m.ElementList ): old_value = old_value or [] if [i.uuid for i in old_value] != [i.uuid for i in new_value]: @@ -330,8 +327,8 @@ def _handle_direct_accessors( ): old_value = getattr(old_object, attr, None) new_value = getattr(new_object, attr, None) - if isinstance(old_value, c.GenericElement | type(None)) and isinstance( - new_value, c.GenericElement | type(None) + if isinstance(old_value, m.ModelElement | type(None)) and isinstance( + new_value, m.ModelElement | type(None) ): if old_value is new_value is None: pass @@ -357,8 +354,8 @@ def _handle_direct_accessors( children[new_value.uuid] = compare_objects( None, new_value, old_model ) - elif isinstance(old_value, c.ElementList | type(None)) and isinstance( - new_value, c.ElementList + elif isinstance(old_value, m.ElementList | type(None)) and isinstance( + new_value, m.ElementList ): old_value = old_value or [] for item in new_value: diff --git a/capella_model_explorer/backend/templates.py b/capella_model_explorer/backend/templates.py index c9767dc..9e5bade 100644 --- a/capella_model_explorer/backend/templates.py +++ b/capella_model_explorer/backend/templates.py @@ -7,6 +7,7 @@ from typing import Any, Dict, List, Optional import capellambse +import capellambse.model as m import yaml from pydantic import BaseModel, Field @@ -28,7 +29,7 @@ def find_objects(model, obj_type=None, below=None, attr=None, filters=None): objects = getter(model) if hasattr(objects, "_element"): objects = [objects] - elif not isinstance(objects, capellambse.model.ElementList): + elif not isinstance(objects, m.ElementList): raise ValueError( f"Expected a list of model objects or a single model object" f" for {attr!r} of the model, got {objects!r}" diff --git a/pyproject.toml b/pyproject.toml index a823cf4..f699acf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,8 +25,8 @@ classifiers = [ "Programming Language :: Python :: 3.12", ] dependencies = [ - "capellambse>=0.5.72,<0.6", - "capellambse-context-diagrams", + "capellambse>=0.6.6,<0.7", + "capellambse-context-diagrams>=0.4.0", "capella-diff-tools", "diff-match-patch", "jinja2", From eb2078b309a65e8c2286ccc3b48421bda1b433d0 Mon Sep 17 00:00:00 2001 From: Martin Lehmann Date: Fri, 16 Aug 2024 11:49:03 +0200 Subject: [PATCH 2/2] fix: Use the public function to preinstall elk.js --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 46ae664..b10c95a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -52,7 +52,7 @@ COPY frontend/fetch-version.py ./frontend/ RUN python frontend/fetch-version.py # Pre-install npm dependencies for context diagrams -RUN python -c "from capellambse_context_diagrams import _elkjs; _elkjs._install_required_npm_pkg_versions()" +RUN python -c "from capellambse_context_diagrams import install_elk; install_elk()" # Run as non-root user per default RUN chmod -R 777 /home