buildzr 0.0.7__py3-none-any.whl → 0.0.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
buildzr/dsl/dsl.py CHANGED
@@ -3,9 +3,10 @@ import buildzr
3
3
  from .factory import GenerateId
4
4
  from typing_extensions import (
5
5
  Self,
6
- TypeGuard,
7
6
  TypeIs,
8
7
  )
8
+ from collections import deque
9
+ from contextvars import ContextVar
9
10
  from typing import (
10
11
  Any,
11
12
  Union,
@@ -16,33 +17,21 @@ from typing import (
16
17
  Optional,
17
18
  Generic,
18
19
  TypeVar,
19
- Protocol,
20
20
  Callable,
21
21
  Iterable,
22
22
  Literal,
23
23
  cast,
24
- overload,
25
- Sequence,
26
24
  Type,
27
25
  )
28
26
 
29
27
  from buildzr.sinks.interfaces import Sink
30
-
31
28
  from buildzr.dsl.interfaces import (
32
29
  DslWorkspaceElement,
33
30
  DslElement,
34
31
  DslViewElement,
35
32
  DslViewsElement,
36
- DslFluentSink,
37
- TSrc, TDst,
38
- TParent, TChild,
39
33
  )
40
34
  from buildzr.dsl.relations import (
41
- _is_software_fluent_relationship,
42
- _is_container_fluent_relationship,
43
- _Relationship,
44
- _RelationshipDescription,
45
- _FluentRelationship,
46
35
  DslElementRelationOverrides,
47
36
  )
48
37
 
@@ -58,6 +47,11 @@ class TypedDynamicAttribute(Generic[TypedModel]):
58
47
  def __getattr__(self, name: str) -> TypedModel:
59
48
  return cast(TypedModel, self._dynamic_attributes.get(name))
60
49
 
50
+ _current_workspace: ContextVar[Optional['Workspace']] = ContextVar('current_workspace', default=None)
51
+ _current_group_stack: ContextVar[List['Group']] = ContextVar('current_group', default=[])
52
+ _current_software_system: ContextVar[Optional['SoftwareSystem']] = ContextVar('current_software_system', default=None)
53
+ _current_container: ContextVar[Optional['Container']] = ContextVar('current_container', default=None)
54
+
61
55
  class Workspace(DslWorkspaceElement):
62
56
  """
63
57
  Represents a Structurizr workspace, which is a wrapper for a software architecture model, views, and documentation.
@@ -75,11 +69,21 @@ class Workspace(DslWorkspaceElement):
75
69
  def children(self) -> Optional[List[Union['Person', 'SoftwareSystem']]]:
76
70
  return self._children
77
71
 
78
- def __init__(self, name: str, description: str="", scope: Literal['landscape', 'software_system', None]='software_system') -> None:
72
+ def __init__(
73
+ self,
74
+ name: str,
75
+ description: str="",
76
+ scope: Literal['landscape', 'software_system', None]='software_system',
77
+ implied_relationships: bool=False,
78
+ group_separator: str='/',
79
+ ) -> None:
80
+
79
81
  self._m = buildzr.models.Workspace()
80
82
  self._parent = None
81
83
  self._children: Optional[List[Union['Person', 'SoftwareSystem']]] = []
82
84
  self._dynamic_attrs: Dict[str, Union['Person', 'SoftwareSystem']] = {}
85
+ self._use_implied_relationships = implied_relationships
86
+ self._group_separator = group_separator
83
87
  self.model.id = GenerateId.for_workspace()
84
88
  self.model.name = name
85
89
  self.model.description = description
@@ -102,58 +106,57 @@ class Workspace(DslWorkspaceElement):
102
106
  scope=scope_mapper[scope],
103
107
  )
104
108
 
105
- def _contains_group(
106
- self,
107
- name: str,
108
- *models: Union[
109
- 'Person',
110
- 'SoftwareSystem',
111
- _FluentRelationship['SoftwareSystem'],
112
- _FluentRelationship['Container'],
113
- ]
114
- ) -> None:
109
+ self.model.model.properties = {
110
+ 'structurizr.groupSeparator': group_separator,
111
+ }
115
112
 
116
- def recursive_group_name_assign(software_system: 'SoftwareSystem') -> None:
117
- software_system.model.group = name
118
- for container in software_system.children:
119
- container.model.group = name
120
- for component in container.children:
121
- component.model.group = name
122
-
123
- for model in models:
124
- if isinstance(model, Person):
125
- model.model.group = name
126
- elif isinstance(model, SoftwareSystem):
127
- recursive_group_name_assign(model)
128
- elif _is_software_fluent_relationship(model):
129
- recursive_group_name_assign(model._parent)
130
- elif _is_container_fluent_relationship(model):
131
- recursive_group_name_assign(model._parent._parent)
132
-
133
- self.contains(*models)
134
-
135
- def contains(
136
- self,
137
- *models: Union[
138
- 'Group',
139
- 'Person',
140
- 'SoftwareSystem',
141
- _FluentRelationship['SoftwareSystem'],
142
- _FluentRelationship['Container'],
143
- ]) -> _FluentRelationship['Workspace']:
144
-
145
- for model in models:
146
- if isinstance(model, Group):
147
- self._contains_group(model._name, *model._elements)
148
- elif isinstance(model, Person):
149
- self.add_element(model)
150
- elif isinstance(model, SoftwareSystem):
151
- self.add_element(model)
152
- elif _is_software_fluent_relationship(model):
153
- self.add_element(model._parent)
154
- elif _is_container_fluent_relationship(model):
155
- self.add_element(model._parent._parent)
156
- return _FluentRelationship['Workspace'](self)
113
+ def __enter__(self) -> Self:
114
+ self._token = _current_workspace.set(self)
115
+ return self
116
+
117
+ def __exit__(self, exc_type: Optional[Type[BaseException]], exc_value: Optional[BaseException], traceback: Optional[Any]) -> None:
118
+
119
+ from buildzr.dsl.explorer import Explorer
120
+
121
+ # Process implied relationships:
122
+ # If we have relationship s >> do >> a.b, then create s >> do >> a.
123
+ # If we have relationship s.ss >> do >> a.b.c, then create s.ss >> do >> a.b and s.ss >> do >> a.
124
+ # And so on...
125
+ if self._use_implied_relationships:
126
+ explorer = Explorer(self)
127
+ relationships = list(explorer.walk_relationships())
128
+ for relationship in relationships:
129
+ source = relationship.source
130
+ destination = relationship.destination
131
+ destination_parent = destination.parent
132
+
133
+ while destination_parent is not None and \
134
+ isinstance(source, DslElement) and \
135
+ not isinstance(source.model, buildzr.models.Workspace) and \
136
+ not isinstance(destination_parent, DslWorkspaceElement):
137
+
138
+ if destination_parent is source.parent:
139
+ break
140
+
141
+ rels = source.model.relationships
142
+
143
+ if rels:
144
+ already_exists = any(
145
+ r.destinationId == destination_parent.model.id and
146
+ r.description == relationship.model.description and
147
+ r.technology == relationship.model.technology
148
+ for r in rels
149
+ )
150
+ if not already_exists:
151
+ r = source.uses(
152
+ destination_parent,
153
+ description=relationship.model.description,
154
+ technology=relationship.model.technology,
155
+ )
156
+ r.model.linkedRelationshipId = relationship.model.id
157
+ destination_parent = destination_parent.parent
158
+
159
+ _current_workspace.reset(self._token)
157
160
 
158
161
  def person(self) -> TypedDynamicAttribute['Person']:
159
162
  return TypedDynamicAttribute['Person'](self._dynamic_attrs)
@@ -161,34 +164,44 @@ class Workspace(DslWorkspaceElement):
161
164
  def software_system(self) -> TypedDynamicAttribute['SoftwareSystem']:
162
165
  return TypedDynamicAttribute['SoftwareSystem'](self._dynamic_attrs)
163
166
 
164
- def add_element(self, element: Union['Person', 'SoftwareSystem']) -> None:
165
- if isinstance(element, Person):
166
- self._m.model.people.append(element._m)
167
- element._parent = self
168
- self._dynamic_attrs[_child_name_transform(element.model.name)] = element
169
- if element._label:
170
- self._dynamic_attrs[_child_name_transform(element._label)] = element
171
- self._children.append(element)
172
- elif isinstance(element, SoftwareSystem):
173
- self._m.model.softwareSystems.append(element._m)
174
- element._parent = self
175
- self._dynamic_attrs[_child_name_transform(element.model.name)] = element
176
- if element._label:
177
- self._dynamic_attrs[_child_name_transform(element._label)] = element
178
- self._children.append(element)
167
+ def add_model(self, model: Union['Person', 'SoftwareSystem']) -> None:
168
+ if isinstance(model, Person):
169
+ self._m.model.people.append(model._m)
170
+ model._parent = self
171
+ self._add_dynamic_attr(model.model.name, model)
172
+ self._children.append(model)
173
+ elif isinstance(model, SoftwareSystem):
174
+ self._m.model.softwareSystems.append(model._m)
175
+ model._parent = self
176
+ self._add_dynamic_attr(model.model.name, model)
177
+ self._children.append(model)
179
178
  else:
180
- raise ValueError('Invalid element type: Trying to add an element of type {} to a workspace.'.format(type(element)))
179
+ raise ValueError('Invalid element type: Trying to add an element of type {} to a workspace.'.format(type(model)))
181
180
 
182
- def with_views(
183
- self,
184
- *views: Union[
185
- 'SystemLandscapeView',
181
+ def apply_views( self, *views: Union[ 'SystemLandscapeView',
186
182
  'SystemContextView',
187
183
  'ContainerView',
188
184
  'ComponentView',
189
185
  ]
190
- ) -> '_FluentSink':
191
- return Views(self).contains(*views)
186
+ ) -> None:
187
+ Views(self).add_views(*views)
188
+
189
+ def to_json(self, path: str) -> None:
190
+ from buildzr.sinks.json_sink import JsonSink, JsonSinkConfig
191
+ sink = JsonSink()
192
+ sink.write(workspace=self.model, config=JsonSinkConfig(path=path))
193
+
194
+ def _add_dynamic_attr(self, name: str, model: Union['Person', 'SoftwareSystem']) -> None:
195
+ if isinstance(model, Person):
196
+ self._dynamic_attrs[_child_name_transform(name)] = model
197
+ if model._label:
198
+ self._dynamic_attrs[_child_name_transform(model._label)] = model
199
+ elif isinstance(model, SoftwareSystem):
200
+ self._dynamic_attrs[_child_name_transform(name)] = model
201
+ if model._label:
202
+ self._dynamic_attrs[_child_name_transform(model._label)] = model
203
+ else:
204
+ raise ValueError('Invalid element type: Trying to add an element of type {} to a workspace.'.format(type(model)))
192
205
 
193
206
  def __getattr__(self, name: str) -> Union['Person', 'SoftwareSystem']:
194
207
  return self._dynamic_attrs[name]
@@ -238,6 +251,7 @@ class SoftwareSystem(DslElementRelationOverrides[
238
251
 
239
252
  def __init__(self, name: str, description: str="", tags: Set[str]=set(), properties: Dict[str, Any]=dict()) -> None:
240
253
  self._m = buildzr.models.SoftwareSystem()
254
+ self.model.containers = []
241
255
  self._parent: Optional[Workspace] = None
242
256
  self._children: Optional[List['Container']] = []
243
257
  self._sources: List[DslElement] = []
@@ -251,37 +265,41 @@ class SoftwareSystem(DslElementRelationOverrides[
251
265
  self.model.tags = ','.join(self._tags)
252
266
  self.model.properties = properties
253
267
 
254
- def contains(
255
- self,
256
- *containers: Union['Container', _FluentRelationship['Container']]
257
- ) -> _FluentRelationship['SoftwareSystem']:
258
- if not self.model.containers:
259
- self.model.containers = []
260
-
261
- for child in containers:
262
- if isinstance(child, Container):
263
- self.add_element(child)
264
- elif _is_container_fluent_relationship(child):
265
- self.add_element(child._parent)
266
- return _FluentRelationship['SoftwareSystem'](self)
268
+ workspace = _current_workspace.get()
269
+ if workspace is not None:
270
+ workspace.add_model(self)
271
+ workspace._add_dynamic_attr(self.model.name, self)
267
272
 
268
- def labeled(self, label: str) -> 'SoftwareSystem':
269
- self._label = label
273
+ stack = _current_group_stack.get()
274
+ if stack:
275
+ stack[-1].add_element(self)
276
+
277
+ def __enter__(self) -> Self:
278
+ self._token = _current_software_system.set(self)
270
279
  return self
271
280
 
281
+ def __exit__(self, exc_type: Optional[Type[BaseException]], exc_value: Optional[BaseException], traceback: Optional[Any]) -> None:
282
+ _current_software_system.reset(self._token)
283
+
272
284
  def container(self) -> TypedDynamicAttribute['Container']:
273
285
  return TypedDynamicAttribute['Container'](self._dynamic_attrs)
274
286
 
275
- def add_element(self, element: 'Container') -> None:
276
- if isinstance(element, Container):
277
- self.model.containers.append(element.model)
278
- element._parent = self
279
- self._dynamic_attrs[_child_name_transform(element.model.name)] = element
280
- if element._label:
281
- self._dynamic_attrs[_child_name_transform(element._label)] = element
282
- self._children.append(element)
287
+ def add_container(self, container: 'Container') -> None:
288
+ if isinstance(container, Container):
289
+ self.model.containers.append(container.model)
290
+ container._parent = self
291
+ self._add_dynamic_attr(container.model.name, container)
292
+ self._children.append(container)
293
+ else:
294
+ raise ValueError('Invalid element type: Trying to add an element of type {} to a software system.'.format(type(container)))
295
+
296
+ def _add_dynamic_attr(self, name: str, model: 'Container') -> None:
297
+ if isinstance(model, Container):
298
+ self._dynamic_attrs[_child_name_transform(name)] = model
299
+ if model._label:
300
+ self._dynamic_attrs[_child_name_transform(model._label)] = model
283
301
  else:
284
- raise ValueError('Invalid element type: Trying to add an element of type {} to a software system.'.format(type(element)))
302
+ raise ValueError('Invalid element type: Trying to add an element of type {} to a software system.'.format(type(model)))
285
303
 
286
304
  def __getattr__(self, name: str) -> 'Container':
287
305
  return self._dynamic_attrs[name]
@@ -292,6 +310,13 @@ class SoftwareSystem(DslElementRelationOverrides[
292
310
  def __dir__(self) -> Iterable[str]:
293
311
  return list(super().__dir__()) + list(self._dynamic_attrs.keys())
294
312
 
313
+ def labeled(self, label: str) -> 'SoftwareSystem':
314
+ self._label = label
315
+ workspace = _current_workspace.get()
316
+ if workspace is not None:
317
+ workspace._add_dynamic_attr(label, self)
318
+ return self
319
+
295
320
  class Person(DslElementRelationOverrides[
296
321
  'Person',
297
322
  Union[
@@ -347,8 +372,19 @@ class Person(DslElementRelationOverrides[
347
372
  self.model.tags = ','.join(self._tags)
348
373
  self.model.properties = properties
349
374
 
375
+ workspace = _current_workspace.get()
376
+ if workspace is not None:
377
+ workspace.add_model(self)
378
+
379
+ stack = _current_group_stack.get()
380
+ if stack:
381
+ stack[-1].add_element(self)
382
+
350
383
  def labeled(self, label: str) -> 'Person':
351
384
  self._label = label
385
+ workspace = _current_workspace.get()
386
+ if workspace is not None:
387
+ workspace._add_dynamic_attr(label, self)
352
388
  return self
353
389
 
354
390
  class Container(DslElementRelationOverrides[
@@ -388,15 +424,9 @@ class Container(DslElementRelationOverrides[
388
424
  def tags(self) -> Set[str]:
389
425
  return self._tags
390
426
 
391
- def contains(self, *components: 'Component') -> _FluentRelationship['Container']:
392
- if not self.model.components:
393
- self.model.components = []
394
- for component in components:
395
- self.add_element(component)
396
- return _FluentRelationship['Container'](self)
397
-
398
427
  def __init__(self, name: str, description: str="", technology: str="", tags: Set[str]=set(), properties: Dict[str, Any]=dict()) -> None:
399
428
  self._m = buildzr.models.Container()
429
+ self.model.components = []
400
430
  self._parent: Optional[SoftwareSystem] = None
401
431
  self._children: Optional[List['Component']] = []
402
432
  self._sources: List[DslElement] = []
@@ -412,23 +442,48 @@ class Container(DslElementRelationOverrides[
412
442
  self.model.tags = ','.join(self._tags)
413
443
  self.model.properties = properties
414
444
 
445
+ software_system = _current_software_system.get()
446
+ if software_system is not None:
447
+ software_system.add_container(self)
448
+ software_system._add_dynamic_attr(self.model.name, self)
449
+
450
+ stack = _current_group_stack.get()
451
+ if stack:
452
+ stack[-1].add_element(self)
453
+
454
+ def __enter__(self) -> Self:
455
+ self._token = _current_container.set(self)
456
+ return self
457
+
458
+ def __exit__(self, exc_type: Optional[Type[BaseException]], exc_value: Optional[BaseException], traceback: Optional[Any]) -> None:
459
+ _current_container.reset(self._token)
460
+
415
461
  def labeled(self, label: str) -> 'Container':
416
462
  self._label = label
463
+ software_system = _current_software_system.get()
464
+ if software_system is not None:
465
+ software_system._add_dynamic_attr(label, self)
417
466
  return self
418
467
 
419
468
  def component(self) -> TypedDynamicAttribute['Component']:
420
469
  return TypedDynamicAttribute['Component'](self._dynamic_attrs)
421
470
 
422
- def add_element(self, element: 'Component') -> None:
423
- if isinstance(element, Component):
424
- self.model.components.append(element.model)
425
- element._parent = self
426
- self._dynamic_attrs[_child_name_transform(element.model.name)] = element
427
- if element._label:
428
- self._dynamic_attrs[_child_name_transform(element._label)] = element
429
- self._children.append(element)
471
+ def add_component(self, component: 'Component') -> None:
472
+ if isinstance(component, Component):
473
+ self.model.components.append(component.model)
474
+ component._parent = self
475
+ self._add_dynamic_attr(component.model.name, component)
476
+ self._children.append(component)
477
+ else:
478
+ raise ValueError('Invalid element type: Trying to add an element of type {} to a container.'.format(type(component)))
479
+
480
+ def _add_dynamic_attr(self, name: str, model: 'Component') -> None:
481
+ if isinstance(model, Component):
482
+ self._dynamic_attrs[_child_name_transform(name)] = model
483
+ if model._label:
484
+ self._dynamic_attrs[_child_name_transform(model._label)] = model
430
485
  else:
431
- raise ValueError('Invalid element type: Trying to add an element of type {} to a container.'.format(type(element)))
486
+ raise ValueError('Invalid element type: Trying to add an element of type {} to a container.'.format(type(model)))
432
487
 
433
488
  def __getattr__(self, name: str) -> 'Component':
434
489
  return self._dynamic_attrs[name]
@@ -491,8 +546,20 @@ class Component(DslElementRelationOverrides[
491
546
  self.model.tags = ','.join(self._tags)
492
547
  self.model.properties = properties
493
548
 
549
+ container = _current_container.get()
550
+ if container is not None:
551
+ container.add_component(self)
552
+ container._add_dynamic_attr(self.model.name, self)
553
+
554
+ stack = _current_group_stack.get()
555
+ if stack:
556
+ stack[-1].add_element(self)
557
+
494
558
  def labeled(self, label: str) -> 'Component':
495
559
  self._label = label
560
+ container = _current_container.get()
561
+ if container is not None:
562
+ container._add_dynamic_attr(label, self)
496
563
  return self
497
564
 
498
565
  class Group:
@@ -500,27 +567,53 @@ class Group:
500
567
  def __init__(
501
568
  self,
502
569
  name: str,
503
- *elements: Union[
504
- Person,
505
- SoftwareSystem,
506
- _FluentRelationship[SoftwareSystem],
507
- _FluentRelationship[Container],
508
- ]) -> None:
570
+ ) -> None:
509
571
  self._name = name
510
- self._elements = elements
511
572
 
512
- class _FluentSink(DslFluentSink):
573
+ def add_element(
574
+ self,
575
+ model: Union[
576
+ 'Person',
577
+ 'SoftwareSystem',
578
+ 'Container',
579
+ 'Component',
580
+ ],
581
+ group_separator: str="/",
582
+ ) -> None:
513
583
 
514
- def __init__(self, workspace: Workspace) -> None:
515
- self._workspace = workspace
584
+ separator = group_separator
516
585
 
517
- def to_json(self, path: str) -> None:
518
- from buildzr.sinks.json_sink import JsonSink, JsonSinkConfig
519
- sink = JsonSink()
520
- sink.write(workspace=self._workspace.model, config=JsonSinkConfig(path=path))
586
+ workspace = _current_workspace.get()
587
+ if workspace is not None:
588
+ separator = workspace._group_separator
521
589
 
522
- def get_workspace(self) -> Workspace:
523
- return self._workspace
590
+ if len(separator) > 1:
591
+ raise ValueError('Group separator must be a single character.')
592
+
593
+ if separator in self._name:
594
+ raise ValueError('Group name cannot contain the group separator.')
595
+
596
+ stack = _current_group_stack.get()
597
+
598
+ index = next((i for i, group in enumerate(stack) if group._name == self._name), -1)
599
+ if index >= 0:
600
+ model.model.group = separator.join([group._name for group in stack[:index + 1]])
601
+
602
+ def __enter__(self) -> Self:
603
+ stack = _current_group_stack.get() # stack: a/b
604
+ stack.extend([self]) # stack: a/b -> a/b/self
605
+ self._token = _current_group_stack.set(stack)
606
+ return self
607
+
608
+ def __exit__(
609
+ self,
610
+ exc_type: Optional[Type[BaseException]],
611
+ exc_value: Optional[BaseException],
612
+ traceback: Optional[Any]
613
+ ) -> None:
614
+ stack = _current_group_stack.get()
615
+ stack.pop() # stack: a/b/self -> a/b
616
+ _current_group_stack.reset(self._token)
524
617
 
525
618
  _RankDirection = Literal['tb', 'bt', 'lr', 'rl']
526
619
 
@@ -607,10 +700,10 @@ class SystemLandscapeView(DslViewElement):
607
700
  description: str,
608
701
  auto_layout: _AutoLayout='tb',
609
702
  title: Optional[str]=None,
610
- include_elements: List[Callable[[Workspace, Element], bool]]=[],
611
- exclude_elements: List[Callable[[Workspace, Element], bool]]=[],
612
- include_relationships: List[Callable[[Workspace, Relationship], bool]]=[],
613
- exclude_relationships: List[Callable[[Workspace, Relationship], bool]]=[],
703
+ include_elements: List[Union[DslElement, Callable[[Workspace, Element], bool]]]=[],
704
+ exclude_elements: List[Union[DslElement, Callable[[Workspace, Element], bool]]]=[],
705
+ include_relationships: List[Union[DslElement, Callable[[Workspace, Relationship], bool]]]=[],
706
+ exclude_relationships: List[Union[DslElement, Callable[[Workspace, Relationship], bool]]]=[],
614
707
  properties: Optional[Dict[str, str]]=None,
615
708
  ) -> None:
616
709
  self._m = buildzr.models.SystemLandscapeView()
@@ -628,6 +721,10 @@ class SystemLandscapeView(DslViewElement):
628
721
  self._include_relationships = include_relationships
629
722
  self._exclude_relationships = exclude_relationships
630
723
 
724
+ workspace = _current_workspace.get()
725
+ if workspace is not None:
726
+ workspace.apply_views(self)
727
+
631
728
  def _on_added(self) -> None:
632
729
 
633
730
  from buildzr.dsl.expression import Expression, Element, Relationship
@@ -642,17 +739,17 @@ class SystemLandscapeView(DslViewElement):
642
739
 
643
740
  workspace = self._parent._parent
644
741
 
645
- include_view_elements_filter: List[Callable[[Workspace, Element], bool]] = [
742
+ include_view_elements_filter: List[Union[DslElement, Callable[[Workspace, Element], bool]]] = [
646
743
  lambda w, e: e.type == Person,
647
744
  lambda w, e: e.type == SoftwareSystem
648
745
  ]
649
746
 
650
- exclude_view_elements_filter: List[Callable[[Workspace, Element], bool]] = [
747
+ exclude_view_elements_filter: List[Union[DslElement, Callable[[Workspace, Element], bool]]] = [
651
748
  lambda w, e: e.type == Container,
652
749
  lambda w, e: e.type == Component,
653
750
  ]
654
751
 
655
- include_view_relationships_filter: List[Callable[[Workspace, Relationship], bool]] = [
752
+ include_view_relationships_filter: List[Union[DslElement, Callable[[Workspace, Relationship], bool]]] = [
656
753
  lambda w, r: r.source.type == Person,
657
754
  lambda w, r: r.source.type == SoftwareSystem,
658
755
  lambda w, r: r.destination.type == Person,
@@ -703,15 +800,15 @@ class SystemContextView(DslViewElement):
703
800
 
704
801
  def __init__(
705
802
  self,
706
- software_system_selector: Callable[[Workspace], SoftwareSystem],
803
+ software_system_selector: Union[SoftwareSystem, Callable[[Workspace], SoftwareSystem]],
707
804
  key: str,
708
805
  description: str,
709
806
  auto_layout: _AutoLayout='tb',
710
807
  title: Optional[str]=None,
711
- include_elements: List[Callable[[Workspace, Element], bool]]=[],
712
- exclude_elements: List[Callable[[Workspace, Element], bool]]=[],
713
- include_relationships: List[Callable[[Workspace, Relationship], bool]]=[],
714
- exclude_relationships: List[Callable[[Workspace, Relationship], bool]]=[],
808
+ include_elements: List[Union[DslElement, Callable[[Workspace, Element], bool]]]=[],
809
+ exclude_elements: List[Union[DslElement, Callable[[Workspace, Element], bool]]]=[],
810
+ include_relationships: List[Union[DslElement, Callable[[Workspace, Relationship], bool]]]=[],
811
+ exclude_relationships: List[Union[DslElement, Callable[[Workspace, Relationship], bool]]]=[],
715
812
  properties: Optional[Dict[str, str]]=None,
716
813
  ) -> None:
717
814
  self._m = buildzr.models.SystemContextView()
@@ -730,20 +827,27 @@ class SystemContextView(DslViewElement):
730
827
  self._include_relationships = include_relationships
731
828
  self._exclude_relationships = exclude_relationships
732
829
 
830
+ workspace = _current_workspace.get()
831
+ if workspace is not None:
832
+ workspace.apply_views(self)
833
+
733
834
  def _on_added(self) -> None:
734
835
 
735
836
  from buildzr.dsl.expression import Expression, Element, Relationship
736
837
  from buildzr.models import ElementView, RelationshipView
737
838
 
738
- software_system = self._selector(self._parent._parent)
839
+ if isinstance(self._selector, SoftwareSystem):
840
+ software_system = self._selector
841
+ else:
842
+ software_system = self._selector(self._parent._parent)
739
843
  self._m.softwareSystemId = software_system.model.id
740
- view_elements_filter: List[Callable[[Workspace, Element], bool]] = [
844
+ view_elements_filter: List[Union[DslElement, Callable[[Workspace, Element], bool]]] = [
741
845
  lambda w, e: e == software_system,
742
846
  lambda w, e: software_system.model.id in e.sources.ids,
743
847
  lambda w, e: software_system.model.id in e.destinations.ids,
744
848
  ]
745
849
 
746
- view_relationships_filter: List[Callable[[Workspace, Relationship], bool]] = [
850
+ view_relationships_filter: List[Union[DslElement, Callable[[Workspace, Relationship], bool]]] = [
747
851
  lambda w, r: software_system == r.source,
748
852
  lambda w, r: software_system == r.destination,
749
853
  ]
@@ -789,15 +893,15 @@ class ContainerView(DslViewElement):
789
893
 
790
894
  def __init__(
791
895
  self,
792
- software_system_selector: Callable[[Workspace], SoftwareSystem],
896
+ software_system_selector: Union[SoftwareSystem, Callable[[Workspace], SoftwareSystem]],
793
897
  key: str,
794
898
  description: str,
795
899
  auto_layout: _AutoLayout='tb',
796
900
  title: Optional[str]=None,
797
- include_elements: List[Callable[[Workspace, Element], bool]]=[],
798
- exclude_elements: List[Callable[[Workspace, Element], bool]]=[],
799
- include_relationships: List[Callable[[Workspace, Relationship], bool]]=[],
800
- exclude_relationships: List[Callable[[Workspace, Relationship], bool]]=[],
901
+ include_elements: List[Union[DslElement, Callable[[Workspace, Element], bool]]]=[],
902
+ exclude_elements: List[Union[DslElement, Callable[[Workspace, Element], bool]]]=[],
903
+ include_relationships: List[Union[DslElement, Callable[[Workspace, Relationship], bool]]]=[],
904
+ exclude_relationships: List[Union[DslElement, Callable[[Workspace, Relationship], bool]]]=[],
801
905
  properties: Optional[Dict[str, str]]=None,
802
906
  ) -> None:
803
907
  self._m = buildzr.models.ContainerView()
@@ -816,23 +920,30 @@ class ContainerView(DslViewElement):
816
920
  self._include_relationships = include_relationships
817
921
  self._exclude_relationships = exclude_relationships
818
922
 
923
+ workspace = _current_workspace.get()
924
+ if workspace is not None:
925
+ workspace.apply_views(self)
926
+
819
927
  def _on_added(self) -> None:
820
928
 
821
929
  from buildzr.dsl.expression import Expression, Element, Relationship
822
930
  from buildzr.models import ElementView, RelationshipView
823
931
 
824
- software_system = self._selector(self._parent._parent)
932
+ if isinstance(self._selector, SoftwareSystem):
933
+ software_system = self._selector
934
+ else:
935
+ software_system = self._selector(self._parent._parent)
825
936
  self._m.softwareSystemId = software_system.model.id
826
937
 
827
938
  container_ids = { container.model.id for container in software_system.children}
828
939
 
829
- view_elements_filter: List[Callable[[Workspace, Element], bool]] = [
940
+ view_elements_filter: List[Union[DslElement, Callable[[Workspace, Element], bool]]] = [
830
941
  lambda w, e: e.parent == software_system,
831
942
  lambda w, e: any(container_ids.intersection({ id for id in e.sources.ids })),
832
943
  lambda w, e: any(container_ids.intersection({ id for id in e.destinations.ids })),
833
944
  ]
834
945
 
835
- view_relationships_filter: List[Callable[[Workspace, Relationship], bool]] = [
946
+ view_relationships_filter: List[Union[DslElement, Callable[[Workspace, Relationship], bool]]] = [
836
947
  lambda w, r: software_system == r.source.parent,
837
948
  lambda w, r: software_system == r.destination.parent,
838
949
  ]
@@ -878,15 +989,15 @@ class ComponentView(DslViewElement):
878
989
 
879
990
  def __init__(
880
991
  self,
881
- container_selector: Callable[[Workspace], Container],
992
+ container_selector: Union[Container, Callable[[Workspace], Container]],
882
993
  key: str,
883
994
  description: str,
884
995
  auto_layout: _AutoLayout='tb',
885
996
  title: Optional[str]=None,
886
- include_elements: List[Callable[[Workspace, Element], bool]]=[],
887
- exclude_elements: List[Callable[[Workspace, Element], bool]]=[],
888
- include_relationships: List[Callable[[Workspace, Relationship], bool]]=[],
889
- exclude_relationships: List[Callable[[Workspace, Relationship], bool]]=[],
997
+ include_elements: List[Union[DslElement, Callable[[Workspace, Element], bool]]]=[],
998
+ exclude_elements: List[Union[DslElement, Callable[[Workspace, Element], bool]]]=[],
999
+ include_relationships: List[Union[DslElement, Callable[[Workspace, Relationship], bool]]]=[],
1000
+ exclude_relationships: List[Union[DslElement, Callable[[Workspace, Relationship], bool]]]=[],
890
1001
  properties: Optional[Dict[str, str]]=None,
891
1002
  ) -> None:
892
1003
  self._m = buildzr.models.ComponentView()
@@ -905,23 +1016,30 @@ class ComponentView(DslViewElement):
905
1016
  self._include_relationships = include_relationships
906
1017
  self._exclude_relationships = exclude_relationships
907
1018
 
1019
+ workspace = _current_workspace.get()
1020
+ if workspace is not None:
1021
+ workspace.apply_views(self)
1022
+
908
1023
  def _on_added(self) -> None:
909
1024
 
910
1025
  from buildzr.dsl.expression import Expression, Element, Relationship
911
1026
  from buildzr.models import ElementView, RelationshipView
912
1027
 
913
- container = self._selector(self._parent._parent)
1028
+ if isinstance(self._selector, Container):
1029
+ container = self._selector
1030
+ else:
1031
+ container = self._selector(self._parent._parent)
914
1032
  self._m.containerId = container.model.id
915
1033
 
916
1034
  component_ids = { component.model.id for component in container.children }
917
1035
 
918
- view_elements_filter: List[Callable[[Workspace, Element], bool]] = [
1036
+ view_elements_filter: List[Union[DslElement, Callable[[Workspace, Element], bool]]] = [
919
1037
  lambda w, e: e.parent == container,
920
1038
  lambda w, e: any(component_ids.intersection({ id for id in e.sources.ids })),
921
1039
  lambda w, e: any(component_ids.intersection({ id for id in e.destinations.ids })),
922
1040
  ]
923
1041
 
924
- view_relationships_filter: List[Callable[[Workspace, Relationship], bool]] = [
1042
+ view_relationships_filter: List[Union[DslElement, Callable[[Workspace, Relationship], bool]]] = [
925
1043
  lambda w, r: container == r.source.parent,
926
1044
  lambda w, r: container == r.destination.parent,
927
1045
  ]
@@ -971,10 +1089,10 @@ class Views(DslViewsElement):
971
1089
  self._parent = workspace
972
1090
  self._parent._m.views = self._m
973
1091
 
974
- def contains(
1092
+ def add_views(
975
1093
  self,
976
1094
  *views: DslViewElement
977
- ) -> _FluentSink:
1095
+ ) -> None:
978
1096
 
979
1097
  for view in views:
980
1098
  if isinstance(view, SystemLandscapeView):
@@ -1008,8 +1126,6 @@ class Views(DslViewsElement):
1008
1126
  else:
1009
1127
  raise NotImplementedError("The view {0} is currently not supported", type(view))
1010
1128
 
1011
- return _FluentSink(self._parent)
1012
-
1013
1129
  def get_workspace(self) -> Workspace:
1014
1130
  """
1015
1131
  Get the `Workspace` which contain this views definition.