Skip to content

Commit

Permalink
feat: Add support for python lambda filter
Browse files Browse the repository at this point in the history
  • Loading branch information
huyenngn committed Nov 27, 2024
1 parent bed6a30 commit 23b2ec0
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 2 deletions.
92 changes: 91 additions & 1 deletion capellambse_context_diagrams/collectors/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
"""This module defines the collector for the CustomDiagram."""
from __future__ import annotations

import builtins
import collections.abc as cabc
import copy
import typing as t

import capellambse
import capellambse.model as m

from .. import _elkjs, context
Expand Down Expand Up @@ -116,9 +118,97 @@ def _fix_box_heights(self) -> None:
box = self.boxes[uuid]
box.height = max([box.height] + list(min_heights.values()))

def _safely_eval_filter(self, obj: m.ModelElement, filter: str) -> bool:
if not filter.startswith("lambda"):
raise ValueError(f"Filter '{filter}' is not a lambda expression.")

safe_builtins = {
"abs",
"all",
"any",
"ascii",
"bin",
"bool",
"bytearray",
"bytes",
"callable",
"chr",
"classmethod",
"complex",
"dict",
"divmod",
"enumerate",
"filter",
"float",
"format",
"frozenset",
"getattr",
"hasattr",
"hash",
"hex",
"id",
"int",
"isinstance",
"issubclass",
"iter",
"len",
"list",
"map",
"max",
"memoryview",
"min",
"next",
"object",
"oct",
"ord",
"pow",
"print",
"property",
"range",
"repr",
"reversed",
"round",
"set",
"slice",
"sorted",
"staticmethod",
"str",
"sum",
"tuple",
"type",
"vars",
"zip",
}
allowed_builtins = {
name: getattr(builtins, name) for name in safe_builtins
}
allowed_builtins.update(
{
"True": True,
"False": False,
"capellambse": capellambse,
}
)

try:
result = eval(filter, {"__builtins__": allowed_builtins})(obj)
except Exception as e:
raise ValueError(
f"Filter '{filter}' raised an exception: {e}"
) from e

if not isinstance(result, bool):
raise ValueError(
f"Filter '{filter}' did not return a boolean value."
)

return result

def _matches_filters(
self, obj: m.ModelElement, filters: dict[str, t.Any]
self, obj: m.ModelElement, filters: dict[str, t.Any] | str
) -> bool:
if isinstance(filters, str):
return self._safely_eval_filter(obj, filters)
for key, value in filters.items():
if getattr(obj, key) != value:
return False
Expand Down
14 changes: 13 additions & 1 deletion docs/custom_diagram.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ In the example above, we first `get` all the inputs of our target element and it

### `filter`

Whenever you have a list of elements and you want to filter them, you can use the `filter` keyword. The `filter` keyword takes a dictionary as an argument. The dictionary should have the key as the attribute name and the value as the value you want to filter on.
Whenever you have a list of elements and you want to filter them, you can use the `filter` keyword. The `filter` keyword takes a dictionary or a string as an argument. The dictionary should have the key as the attribute name and the value as the value you want to filter on.

```yaml
get:
Expand All @@ -47,6 +47,18 @@ get:

In the example above, we get all the inputs of our target element and include all the exchanges that are of kind `FunctionalExchange` in the resulting diagram.

For a string, the filter should be a lambda expression that takes the element as an argument and returns a boolean.

```yaml
get:
- name: inputs
filter: "lambda x: isinstance(x, capellambse.metamodel.fa.FunctionPort)"
include:
- name: exchanges
```

In the example above, we get all the inputs that are of type `FunctionPort` and include all it's exchanges in the resulting diagram.

### `repeat`

With the `repeat` keyword, you can repeat the collection. The value of `repeat` should be an integer. If the value is -1, the collection will repeat until no new elements are found. If the value is 0, the collection will not repeat. If the value is 1, the collection will repeat once and so on.
Expand Down

0 comments on commit 23b2ec0

Please sign in to comment.