buildzr 0.0.6__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]
@@ -199,7 +212,15 @@ class Workspace(DslWorkspaceElement):
199
212
  def __dir__(self) -> Iterable[str]:
200
213
  return list(super().__dir__()) + list(self._dynamic_attrs.keys())
201
214
 
202
- class SoftwareSystem(DslElementRelationOverrides):
215
+ class SoftwareSystem(DslElementRelationOverrides[
216
+ 'SoftwareSystem',
217
+ Union[
218
+ 'Person',
219
+ 'SoftwareSystem',
220
+ 'Container',
221
+ 'Component'
222
+ ]
223
+ ]):
203
224
  """
204
225
  A software system.
205
226
  """
@@ -230,6 +251,7 @@ class SoftwareSystem(DslElementRelationOverrides):
230
251
 
231
252
  def __init__(self, name: str, description: str="", tags: Set[str]=set(), properties: Dict[str, Any]=dict()) -> None:
232
253
  self._m = buildzr.models.SoftwareSystem()
254
+ self.model.containers = []
233
255
  self._parent: Optional[Workspace] = None
234
256
  self._children: Optional[List['Container']] = []
235
257
  self._sources: List[DslElement] = []
@@ -243,37 +265,41 @@ class SoftwareSystem(DslElementRelationOverrides):
243
265
  self.model.tags = ','.join(self._tags)
244
266
  self.model.properties = properties
245
267
 
246
- def contains(
247
- self,
248
- *containers: Union['Container', _FluentRelationship['Container']]
249
- ) -> _FluentRelationship['SoftwareSystem']:
250
- if not self.model.containers:
251
- self.model.containers = []
252
-
253
- for child in containers:
254
- if isinstance(child, Container):
255
- self.add_element(child)
256
- elif _is_container_fluent_relationship(child):
257
- self.add_element(child._parent)
258
- 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)
259
272
 
260
- def labeled(self, label: str) -> 'SoftwareSystem':
261
- 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)
262
279
  return self
263
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
+
264
284
  def container(self) -> TypedDynamicAttribute['Container']:
265
285
  return TypedDynamicAttribute['Container'](self._dynamic_attrs)
266
286
 
267
- def add_element(self, element: 'Container') -> None:
268
- if isinstance(element, Container):
269
- self.model.containers.append(element.model)
270
- element._parent = self
271
- self._dynamic_attrs[_child_name_transform(element.model.name)] = element
272
- if element._label:
273
- self._dynamic_attrs[_child_name_transform(element._label)] = element
274
- 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)
275
293
  else:
276
- raise ValueError('Invalid element type: Trying to add an element of type {} to a software system.'.format(type(element)))
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
301
+ else:
302
+ raise ValueError('Invalid element type: Trying to add an element of type {} to a software system.'.format(type(model)))
277
303
 
278
304
  def __getattr__(self, name: str) -> 'Container':
279
305
  return self._dynamic_attrs[name]
@@ -284,7 +310,22 @@ class SoftwareSystem(DslElementRelationOverrides):
284
310
  def __dir__(self) -> Iterable[str]:
285
311
  return list(super().__dir__()) + list(self._dynamic_attrs.keys())
286
312
 
287
- class Person(DslElementRelationOverrides):
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
+
320
+ class Person(DslElementRelationOverrides[
321
+ 'Person',
322
+ Union[
323
+ 'Person',
324
+ 'SoftwareSystem',
325
+ 'Container',
326
+ 'Component'
327
+ ]
328
+ ]):
288
329
  """
289
330
  A person who uses a software system.
290
331
  """
@@ -331,11 +372,30 @@ class Person(DslElementRelationOverrides):
331
372
  self.model.tags = ','.join(self._tags)
332
373
  self.model.properties = properties
333
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
+
334
383
  def labeled(self, label: str) -> 'Person':
335
384
  self._label = label
385
+ workspace = _current_workspace.get()
386
+ if workspace is not None:
387
+ workspace._add_dynamic_attr(label, self)
336
388
  return self
337
389
 
338
- class Container(DslElementRelationOverrides):
390
+ class Container(DslElementRelationOverrides[
391
+ 'Container',
392
+ Union[
393
+ 'Person',
394
+ 'SoftwareSystem',
395
+ 'Container',
396
+ 'Component'
397
+ ]
398
+ ]):
339
399
  """
340
400
  A container (something that can execute code or host data).
341
401
  """
@@ -364,15 +424,9 @@ class Container(DslElementRelationOverrides):
364
424
  def tags(self) -> Set[str]:
365
425
  return self._tags
366
426
 
367
- def contains(self, *components: 'Component') -> _FluentRelationship['Container']:
368
- if not self.model.components:
369
- self.model.components = []
370
- for component in components:
371
- self.add_element(component)
372
- return _FluentRelationship['Container'](self)
373
-
374
427
  def __init__(self, name: str, description: str="", technology: str="", tags: Set[str]=set(), properties: Dict[str, Any]=dict()) -> None:
375
428
  self._m = buildzr.models.Container()
429
+ self.model.components = []
376
430
  self._parent: Optional[SoftwareSystem] = None
377
431
  self._children: Optional[List['Component']] = []
378
432
  self._sources: List[DslElement] = []
@@ -388,23 +442,48 @@ class Container(DslElementRelationOverrides):
388
442
  self.model.tags = ','.join(self._tags)
389
443
  self.model.properties = properties
390
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
+
391
461
  def labeled(self, label: str) -> 'Container':
392
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)
393
466
  return self
394
467
 
395
468
  def component(self) -> TypedDynamicAttribute['Component']:
396
469
  return TypedDynamicAttribute['Component'](self._dynamic_attrs)
397
470
 
398
- def add_element(self, element: 'Component') -> None:
399
- if isinstance(element, Component):
400
- self.model.components.append(element.model)
401
- element._parent = self
402
- self._dynamic_attrs[_child_name_transform(element.model.name)] = element
403
- if element._label:
404
- self._dynamic_attrs[_child_name_transform(element._label)] = element
405
- 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
406
485
  else:
407
- 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)))
408
487
 
409
488
  def __getattr__(self, name: str) -> 'Component':
410
489
  return self._dynamic_attrs[name]
@@ -415,7 +494,15 @@ class Container(DslElementRelationOverrides):
415
494
  def __dir__(self) -> Iterable[str]:
416
495
  return list(super().__dir__()) + list(self._dynamic_attrs.keys())
417
496
 
418
- class Component(DslElementRelationOverrides):
497
+ class Component(DslElementRelationOverrides[
498
+ 'Component',
499
+ Union[
500
+ 'Person',
501
+ 'SoftwareSystem',
502
+ 'Container',
503
+ 'Component'
504
+ ]
505
+ ]):
419
506
  """
420
507
  A component (a grouping of related functionality behind an interface that runs inside a container).
421
508
  """
@@ -459,8 +546,20 @@ class Component(DslElementRelationOverrides):
459
546
  self.model.tags = ','.join(self._tags)
460
547
  self.model.properties = properties
461
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
+
462
558
  def labeled(self, label: str) -> 'Component':
463
559
  self._label = label
560
+ container = _current_container.get()
561
+ if container is not None:
562
+ container._add_dynamic_attr(label, self)
464
563
  return self
465
564
 
466
565
  class Group:
@@ -468,24 +567,53 @@ class Group:
468
567
  def __init__(
469
568
  self,
470
569
  name: str,
471
- *elements: Union[
472
- Person,
473
- SoftwareSystem,
474
- _FluentRelationship[SoftwareSystem],
475
- _FluentRelationship[Container],
476
- ]) -> None:
570
+ ) -> None:
477
571
  self._name = name
478
- self._elements = elements
479
572
 
480
- 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:
481
583
 
482
- def __init__(self, workspace: Workspace) -> None:
483
- self._workspace = workspace
584
+ separator = group_separator
484
585
 
485
- def to_json(self, path: str) -> None:
486
- from buildzr.sinks.json_sink import JsonSink, JsonSinkConfig
487
- sink = JsonSink()
488
- 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
589
+
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)
489
617
 
490
618
  _RankDirection = Literal['tb', 'bt', 'lr', 'rl']
491
619
 
@@ -572,10 +700,10 @@ class SystemLandscapeView(DslViewElement):
572
700
  description: str,
573
701
  auto_layout: _AutoLayout='tb',
574
702
  title: Optional[str]=None,
575
- include_elements: List[Callable[[Workspace, Element], bool]]=[],
576
- exclude_elements: List[Callable[[Workspace, Element], bool]]=[],
577
- include_relationships: List[Callable[[Workspace, Relationship], bool]]=[],
578
- 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]]]=[],
579
707
  properties: Optional[Dict[str, str]]=None,
580
708
  ) -> None:
581
709
  self._m = buildzr.models.SystemLandscapeView()
@@ -593,6 +721,10 @@ class SystemLandscapeView(DslViewElement):
593
721
  self._include_relationships = include_relationships
594
722
  self._exclude_relationships = exclude_relationships
595
723
 
724
+ workspace = _current_workspace.get()
725
+ if workspace is not None:
726
+ workspace.apply_views(self)
727
+
596
728
  def _on_added(self) -> None:
597
729
 
598
730
  from buildzr.dsl.expression import Expression, Element, Relationship
@@ -607,17 +739,17 @@ class SystemLandscapeView(DslViewElement):
607
739
 
608
740
  workspace = self._parent._parent
609
741
 
610
- include_view_elements_filter: List[Callable[[Workspace, Element], bool]] = [
742
+ include_view_elements_filter: List[Union[DslElement, Callable[[Workspace, Element], bool]]] = [
611
743
  lambda w, e: e.type == Person,
612
744
  lambda w, e: e.type == SoftwareSystem
613
745
  ]
614
746
 
615
- exclude_view_elements_filter: List[Callable[[Workspace, Element], bool]] = [
747
+ exclude_view_elements_filter: List[Union[DslElement, Callable[[Workspace, Element], bool]]] = [
616
748
  lambda w, e: e.type == Container,
617
749
  lambda w, e: e.type == Component,
618
750
  ]
619
751
 
620
- include_view_relationships_filter: List[Callable[[Workspace, Relationship], bool]] = [
752
+ include_view_relationships_filter: List[Union[DslElement, Callable[[Workspace, Relationship], bool]]] = [
621
753
  lambda w, r: r.source.type == Person,
622
754
  lambda w, r: r.source.type == SoftwareSystem,
623
755
  lambda w, r: r.destination.type == Person,
@@ -668,15 +800,15 @@ class SystemContextView(DslViewElement):
668
800
 
669
801
  def __init__(
670
802
  self,
671
- software_system_selector: Callable[[Workspace], SoftwareSystem],
803
+ software_system_selector: Union[SoftwareSystem, Callable[[Workspace], SoftwareSystem]],
672
804
  key: str,
673
805
  description: str,
674
806
  auto_layout: _AutoLayout='tb',
675
807
  title: Optional[str]=None,
676
- include_elements: List[Callable[[Workspace, Element], bool]]=[],
677
- exclude_elements: List[Callable[[Workspace, Element], bool]]=[],
678
- include_relationships: List[Callable[[Workspace, Relationship], bool]]=[],
679
- 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]]]=[],
680
812
  properties: Optional[Dict[str, str]]=None,
681
813
  ) -> None:
682
814
  self._m = buildzr.models.SystemContextView()
@@ -695,20 +827,27 @@ class SystemContextView(DslViewElement):
695
827
  self._include_relationships = include_relationships
696
828
  self._exclude_relationships = exclude_relationships
697
829
 
830
+ workspace = _current_workspace.get()
831
+ if workspace is not None:
832
+ workspace.apply_views(self)
833
+
698
834
  def _on_added(self) -> None:
699
835
 
700
836
  from buildzr.dsl.expression import Expression, Element, Relationship
701
837
  from buildzr.models import ElementView, RelationshipView
702
838
 
703
- 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)
704
843
  self._m.softwareSystemId = software_system.model.id
705
- view_elements_filter: List[Callable[[Workspace, Element], bool]] = [
844
+ view_elements_filter: List[Union[DslElement, Callable[[Workspace, Element], bool]]] = [
706
845
  lambda w, e: e == software_system,
707
846
  lambda w, e: software_system.model.id in e.sources.ids,
708
847
  lambda w, e: software_system.model.id in e.destinations.ids,
709
848
  ]
710
849
 
711
- view_relationships_filter: List[Callable[[Workspace, Relationship], bool]] = [
850
+ view_relationships_filter: List[Union[DslElement, Callable[[Workspace, Relationship], bool]]] = [
712
851
  lambda w, r: software_system == r.source,
713
852
  lambda w, r: software_system == r.destination,
714
853
  ]
@@ -754,15 +893,15 @@ class ContainerView(DslViewElement):
754
893
 
755
894
  def __init__(
756
895
  self,
757
- software_system_selector: Callable[[Workspace], SoftwareSystem],
896
+ software_system_selector: Union[SoftwareSystem, Callable[[Workspace], SoftwareSystem]],
758
897
  key: str,
759
898
  description: str,
760
899
  auto_layout: _AutoLayout='tb',
761
900
  title: Optional[str]=None,
762
- include_elements: List[Callable[[Workspace, Element], bool]]=[],
763
- exclude_elements: List[Callable[[Workspace, Element], bool]]=[],
764
- include_relationships: List[Callable[[Workspace, Relationship], bool]]=[],
765
- 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]]]=[],
766
905
  properties: Optional[Dict[str, str]]=None,
767
906
  ) -> None:
768
907
  self._m = buildzr.models.ContainerView()
@@ -781,23 +920,30 @@ class ContainerView(DslViewElement):
781
920
  self._include_relationships = include_relationships
782
921
  self._exclude_relationships = exclude_relationships
783
922
 
923
+ workspace = _current_workspace.get()
924
+ if workspace is not None:
925
+ workspace.apply_views(self)
926
+
784
927
  def _on_added(self) -> None:
785
928
 
786
929
  from buildzr.dsl.expression import Expression, Element, Relationship
787
930
  from buildzr.models import ElementView, RelationshipView
788
931
 
789
- 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)
790
936
  self._m.softwareSystemId = software_system.model.id
791
937
 
792
938
  container_ids = { container.model.id for container in software_system.children}
793
939
 
794
- view_elements_filter: List[Callable[[Workspace, Element], bool]] = [
940
+ view_elements_filter: List[Union[DslElement, Callable[[Workspace, Element], bool]]] = [
795
941
  lambda w, e: e.parent == software_system,
796
942
  lambda w, e: any(container_ids.intersection({ id for id in e.sources.ids })),
797
943
  lambda w, e: any(container_ids.intersection({ id for id in e.destinations.ids })),
798
944
  ]
799
945
 
800
- view_relationships_filter: List[Callable[[Workspace, Relationship], bool]] = [
946
+ view_relationships_filter: List[Union[DslElement, Callable[[Workspace, Relationship], bool]]] = [
801
947
  lambda w, r: software_system == r.source.parent,
802
948
  lambda w, r: software_system == r.destination.parent,
803
949
  ]
@@ -843,15 +989,15 @@ class ComponentView(DslViewElement):
843
989
 
844
990
  def __init__(
845
991
  self,
846
- container_selector: Callable[[Workspace], Container],
992
+ container_selector: Union[Container, Callable[[Workspace], Container]],
847
993
  key: str,
848
994
  description: str,
849
995
  auto_layout: _AutoLayout='tb',
850
996
  title: Optional[str]=None,
851
- include_elements: List[Callable[[Workspace, Element], bool]]=[],
852
- exclude_elements: List[Callable[[Workspace, Element], bool]]=[],
853
- include_relationships: List[Callable[[Workspace, Relationship], bool]]=[],
854
- 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]]]=[],
855
1001
  properties: Optional[Dict[str, str]]=None,
856
1002
  ) -> None:
857
1003
  self._m = buildzr.models.ComponentView()
@@ -870,23 +1016,30 @@ class ComponentView(DslViewElement):
870
1016
  self._include_relationships = include_relationships
871
1017
  self._exclude_relationships = exclude_relationships
872
1018
 
1019
+ workspace = _current_workspace.get()
1020
+ if workspace is not None:
1021
+ workspace.apply_views(self)
1022
+
873
1023
  def _on_added(self) -> None:
874
1024
 
875
1025
  from buildzr.dsl.expression import Expression, Element, Relationship
876
1026
  from buildzr.models import ElementView, RelationshipView
877
1027
 
878
- 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)
879
1032
  self._m.containerId = container.model.id
880
1033
 
881
1034
  component_ids = { component.model.id for component in container.children }
882
1035
 
883
- view_elements_filter: List[Callable[[Workspace, Element], bool]] = [
1036
+ view_elements_filter: List[Union[DslElement, Callable[[Workspace, Element], bool]]] = [
884
1037
  lambda w, e: e.parent == container,
885
1038
  lambda w, e: any(component_ids.intersection({ id for id in e.sources.ids })),
886
1039
  lambda w, e: any(component_ids.intersection({ id for id in e.destinations.ids })),
887
1040
  ]
888
1041
 
889
- view_relationships_filter: List[Callable[[Workspace, Relationship], bool]] = [
1042
+ view_relationships_filter: List[Union[DslElement, Callable[[Workspace, Relationship], bool]]] = [
890
1043
  lambda w, r: container == r.source.parent,
891
1044
  lambda w, r: container == r.destination.parent,
892
1045
  ]
@@ -936,10 +1089,10 @@ class Views(DslViewsElement):
936
1089
  self._parent = workspace
937
1090
  self._parent._m.views = self._m
938
1091
 
939
- def contains(
1092
+ def add_views(
940
1093
  self,
941
1094
  *views: DslViewElement
942
- ) -> _FluentSink:
1095
+ ) -> None:
943
1096
 
944
1097
  for view in views:
945
1098
  if isinstance(view, SystemLandscapeView):
@@ -973,8 +1126,6 @@ class Views(DslViewsElement):
973
1126
  else:
974
1127
  raise NotImplementedError("The view {0} is currently not supported", type(view))
975
1128
 
976
- return _FluentSink(self._parent)
977
-
978
1129
  def get_workspace(self) -> Workspace:
979
1130
  """
980
1131
  Get the `Workspace` which contain this views definition.