umlmeta 0.2.0__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.
- umlmeta/__init__.py +37 -0
- umlmeta/boxes_view.py +311 -0
- umlmeta/bpmn.py +2917 -0
- umlmeta/canon.py +157 -0
- umlmeta/derived.py +480 -0
- umlmeta/mdzip.py +364 -0
- umlmeta/mdzip_diff.py +435 -0
- umlmeta/mdzip_import.py +742 -0
- umlmeta/mm_write.py +343 -0
- umlmeta/ocl_translator.py +1325 -0
- umlmeta/query.py +151 -0
- umlmeta/standard_profile.py +233 -0
- umlmeta/sysml.py +1466 -0
- umlmeta/sysml2.py +6436 -0
- umlmeta/uaf.py +3752 -0
- umlmeta/uaf13.py +3946 -0
- umlmeta/uml25.py +7840 -0
- umlmeta/v1_to_v2.py +1322 -0
- umlmeta/v1_to_v2_as.py +1328 -0
- umlmeta/xmi21.py +381 -0
- umlmeta/xmi_write.py +378 -0
- umlmeta-0.2.0.dist-info/METADATA +167 -0
- umlmeta-0.2.0.dist-info/RECORD +26 -0
- umlmeta-0.2.0.dist-info/WHEEL +5 -0
- umlmeta-0.2.0.dist-info/licenses/LICENSE +373 -0
- umlmeta-0.2.0.dist-info/top_level.txt +1 -0
umlmeta/__init__.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""umlmeta — clean-room UML 2.5.1 / SysML v1 metamodel and the SysML v1 -> v2
|
|
2
|
+
transformation, generated from OMG-published documents.
|
|
3
|
+
|
|
4
|
+
The generated metamodel modules (:mod:`umlmeta.uml25`, :mod:`umlmeta.sysml`,
|
|
5
|
+
:mod:`umlmeta.sysml2`, :mod:`umlmeta.uaf`, :mod:`umlmeta.uaf13`,
|
|
6
|
+
:mod:`umlmeta.standard_profile`, :mod:`umlmeta.bpmn`) are pure stdlib and
|
|
7
|
+
self-contained. The library modules around them (XMI/CMOF/EMOF readers and
|
|
8
|
+
writers, the textual and abstract-syntax v1->v2 transformers, normative
|
|
9
|
+
derivations, query helpers) are importable as ``umlmeta.<module>``.
|
|
10
|
+
|
|
11
|
+
Clean-room discipline: every semantic is traceable to an OMG document.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
__version__ = "0.2.0"
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"bpmn",
|
|
18
|
+
"boxes_view",
|
|
19
|
+
"canon",
|
|
20
|
+
"derived",
|
|
21
|
+
"mdzip",
|
|
22
|
+
"mdzip_diff",
|
|
23
|
+
"mdzip_import",
|
|
24
|
+
"mm_write",
|
|
25
|
+
"ocl_translator",
|
|
26
|
+
"query",
|
|
27
|
+
"standard_profile",
|
|
28
|
+
"sysml",
|
|
29
|
+
"sysml2",
|
|
30
|
+
"uaf",
|
|
31
|
+
"uaf13",
|
|
32
|
+
"uml25",
|
|
33
|
+
"v1_to_v2",
|
|
34
|
+
"v1_to_v2_as",
|
|
35
|
+
"xmi21",
|
|
36
|
+
"xmi_write",
|
|
37
|
+
]
|
umlmeta/boxes_view.py
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""umlmeta boxes_view — render v1 data (UML 2.5.1 / SysML v1 model
|
|
3
|
+
objects) as SysML v1 diagrams through the sibling diagramboxes package,
|
|
4
|
+
the same pattern as sysmlpy's boxes_view adapters.
|
|
5
|
+
|
|
6
|
+
Supported views (this commit): Block Definition Diagram (bdd).
|
|
7
|
+
- A block = a Class (or DataType) carrying the Block stereotype (or any
|
|
8
|
+
Class under the walked scope when the model is plain UML).
|
|
9
|
+
- Part properties (aggregation == 'composite') / value properties
|
|
10
|
+
(ValueProperty stereotype) / plain properties render as rows in the
|
|
11
|
+
block's parts / values compartments — Delligatti §3.4/Fig 3.5:
|
|
12
|
+
properties are COMPARTMENT STRINGS, not nested boxes.
|
|
13
|
+
- Ports render on the block's boundary (§4.12); flow specs on a port
|
|
14
|
+
surface the port's items as its label.
|
|
15
|
+
- Generalizations render as open-triangle edges to the general class.
|
|
16
|
+
- Reference properties (ReferenceProperty stereotype) render as rows in
|
|
17
|
+
a 'references' compartment (§3.4).
|
|
18
|
+
- Packages render as folder-tab views (§10.4) holding their owned
|
|
19
|
+
packages and blocks.
|
|
20
|
+
|
|
21
|
+
Rendering = diagramboxes (diagramboxes on the path); the caller renders
|
|
22
|
+
braille (`Diagram.render()`) or SVG (`render_svg()`).
|
|
23
|
+
"""
|
|
24
|
+
import os
|
|
25
|
+
import sys
|
|
26
|
+
|
|
27
|
+
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__))))
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
from diagramboxes import Diagram
|
|
31
|
+
except ImportError:
|
|
32
|
+
if os.path.isdir(os.path.expanduser('~/proj/diagramboxes')):
|
|
33
|
+
sys.path.insert(0, os.path.expanduser('~/proj/diagramboxes'))
|
|
34
|
+
from diagramboxes import Diagram
|
|
35
|
+
else:
|
|
36
|
+
raise
|
|
37
|
+
|
|
38
|
+
def _stereo_name(obj):
|
|
39
|
+
"""The stereotype name for an element's `_applied_stereotypes`
|
|
40
|
+
list of (profile, stereo, app_id) tuples."""
|
|
41
|
+
apps = getattr(obj, '_applied_stereotypes', None) or []
|
|
42
|
+
if not apps:
|
|
43
|
+
return None
|
|
44
|
+
return apps[0][1]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _block_sides(n):
|
|
48
|
+
"""Alternate left/right for boundary ports (the BDD port spread)."""
|
|
49
|
+
return 'left' if n % 2 == 0 else 'right'
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def as_bdd_boxes(scope, frame_kind='bdd', element_kind=None,
|
|
53
|
+
element_name=None, diagram_name=None):
|
|
54
|
+
"""Build a Block Definition Diagram from umlmeta data.
|
|
55
|
+
|
|
56
|
+
Parameters
|
|
57
|
+
----------
|
|
58
|
+
scope : a Package / Model / Namespace object (umlmeta generated
|
|
59
|
+
metaclasses), or a list of elements.
|
|
60
|
+
frame_* : the diagram-kind frame strings (Delligatti §2.4).
|
|
61
|
+
|
|
62
|
+
Returns
|
|
63
|
+
-------
|
|
64
|
+
diagramboxes.Diagram
|
|
65
|
+
"""
|
|
66
|
+
d = Diagram()
|
|
67
|
+
if frame_kind:
|
|
68
|
+
d.add_frame(frame_kind, element_kind, element_name,
|
|
69
|
+
diagram_name)
|
|
70
|
+
|
|
71
|
+
pkg_views = {} # package id -> View (folder tab)
|
|
72
|
+
block_nodes = {} # class id -> Node
|
|
73
|
+
ports_seen = {} # class id -> count (for side spreading)
|
|
74
|
+
|
|
75
|
+
def _walk_pkg(pkg, parent_view=None):
|
|
76
|
+
el = pkg
|
|
77
|
+
|
|
78
|
+
view = None
|
|
79
|
+
pname = getattr(el, 'name', None)
|
|
80
|
+
if pname is not None and type(el).__name__ in (
|
|
81
|
+
'Package', 'Model', 'Profile'):
|
|
82
|
+
view = parent_view or d.add_view(
|
|
83
|
+
pname, stereotypes=['package'])
|
|
84
|
+
pkg_views[id(el)] = view
|
|
85
|
+
|
|
86
|
+
blocks = []
|
|
87
|
+
for child in (getattr(el, 'packagedElement', None) or []):
|
|
88
|
+
if type(child).__name__ == 'Package':
|
|
89
|
+
_walk_pkg(child, view)
|
|
90
|
+
continue
|
|
91
|
+
if type(child).__name__ in ('Class', 'DataType', 'Signal',
|
|
92
|
+
'Enumeration', 'Interface'):
|
|
93
|
+
blocks.append(child)
|
|
94
|
+
|
|
95
|
+
# two passes: every block draws BEFORE any generalization edge
|
|
96
|
+
# resolves its target (a generalization to a later sibling must
|
|
97
|
+
# not mint a duplicate node)
|
|
98
|
+
for b in blocks:
|
|
99
|
+
_draw_block(b, view, draw_edges=False)
|
|
100
|
+
for b in blocks:
|
|
101
|
+
_block_edges(b)
|
|
102
|
+
|
|
103
|
+
def _draw_block(b, view, draw_edges=True):
|
|
104
|
+
shape = _block_shape(b, view)
|
|
105
|
+
if draw_edges:
|
|
106
|
+
_block_edges(b)
|
|
107
|
+
return shape
|
|
108
|
+
|
|
109
|
+
def _block_shape(b, view):
|
|
110
|
+
kind = type(b).__name__
|
|
111
|
+
stereo = _stereo_name(b)
|
|
112
|
+
# the frame's block context suppresses a bare «block»; anything
|
|
113
|
+
# else (Signal, Interface, «value type», «enumeration»…) prints
|
|
114
|
+
st_lines = None if stereo in (None, 'Block') else [stereo]
|
|
115
|
+
node = d.add_node(getattr(b, 'name', None) or 'unnamed',
|
|
116
|
+
stereotypes=st_lines)
|
|
117
|
+
if view is not None:
|
|
118
|
+
view.add_child(node)
|
|
119
|
+
block_nodes[id(b)] = node
|
|
120
|
+
|
|
121
|
+
# ports on the boundary (flow-qualified labels, §4.12)
|
|
122
|
+
n = 0
|
|
123
|
+
for p in list(getattr(b, 'ownedAttribute', None) or []):
|
|
124
|
+
if type(p).__name__ != 'Port':
|
|
125
|
+
continue
|
|
126
|
+
lab = getattr(p, 'name', None) or ''
|
|
127
|
+
t = getattr(p, 'type', None)
|
|
128
|
+
if t is not None and getattr(t, 'name', None):
|
|
129
|
+
lab = '%s : %s' % (lab, t.name)
|
|
130
|
+
node.add_port(lab, side=_block_sides(n), direction=None)
|
|
131
|
+
n += 1
|
|
132
|
+
ports_seen[id(b)] = n
|
|
133
|
+
|
|
134
|
+
# compartment rows: parts / values / references / plain
|
|
135
|
+
parts, values, refs, plain = [], [], [], []
|
|
136
|
+
for p in (getattr(b, 'ownedAttribute', None) or []):
|
|
137
|
+
if type(p).__name__ == 'Port':
|
|
138
|
+
continue
|
|
139
|
+
tname = getattr(getattr(p, 'type', None), 'name', None)
|
|
140
|
+
mult = _end_multiplicity(b, p)
|
|
141
|
+
nm = getattr(p, 'name', None)
|
|
142
|
+
if not nm:
|
|
143
|
+
continue
|
|
144
|
+
stereo = _stereo_name(p)
|
|
145
|
+
s = (p.aggregation if isinstance(p.aggregation, str) else None) \
|
|
146
|
+
if hasattr(p, 'aggregation') else None
|
|
147
|
+
is_part = (s == 'composite')
|
|
148
|
+
is_ref = stereo in ('ReferenceProperty',) or s == 'shared'
|
|
149
|
+
if stereo == 'ValueProperty' or stereo == 'Measurement':
|
|
150
|
+
values.append((nm, tname, mult))
|
|
151
|
+
elif is_ref:
|
|
152
|
+
refs.append((nm, tname, mult))
|
|
153
|
+
elif is_part:
|
|
154
|
+
parts.append((nm, tname, mult))
|
|
155
|
+
else:
|
|
156
|
+
plain.append((nm, tname, mult))
|
|
157
|
+
|
|
158
|
+
def _row(nm, tname, mult):
|
|
159
|
+
s = '%s : %s' % (nm, tname) if tname else nm
|
|
160
|
+
if mult:
|
|
161
|
+
s += ' [%s]' % mult
|
|
162
|
+
return s
|
|
163
|
+
|
|
164
|
+
if values:
|
|
165
|
+
node.compartments = list(getattr(node, 'compartments', None) or [])
|
|
166
|
+
node.compartments.append(
|
|
167
|
+
('values', [_row(*r) for r in values]))
|
|
168
|
+
if parts:
|
|
169
|
+
node.compartments = list(getattr(node, 'compartments', None) or [])
|
|
170
|
+
node.compartments.append(
|
|
171
|
+
('parts', [_row(*r) for r in parts]))
|
|
172
|
+
if refs:
|
|
173
|
+
node.compartments = list(getattr(node, 'compartments', None) or [])
|
|
174
|
+
node.compartments.append(
|
|
175
|
+
('references', [_row(*r) for r in refs]))
|
|
176
|
+
if plain:
|
|
177
|
+
node.attributes = [_row(*r) for r in plain]
|
|
178
|
+
|
|
179
|
+
def _block_edges(b):
|
|
180
|
+
# generalizations: open triangle at the general class
|
|
181
|
+
for g in (getattr(b, 'generalization', None) or []):
|
|
182
|
+
gcls = getattr(g, 'general', None)
|
|
183
|
+
if gcls is None:
|
|
184
|
+
continue
|
|
185
|
+
node = block_nodes.get(id(b))
|
|
186
|
+
target_node = block_nodes.get(id(gcls))
|
|
187
|
+
if node is None or target_node is None:
|
|
188
|
+
continue # target outside the walked scope
|
|
189
|
+
d.add_edge(node, target_node, target_style='triangle')
|
|
190
|
+
node._calc_size(); target_node._calc_size()
|
|
191
|
+
|
|
192
|
+
def _end_multiplicity(owner, prop):
|
|
193
|
+
# the multiplicity of the end that OWNS this property is the
|
|
194
|
+
# property's own lower/upper
|
|
195
|
+
lo = getattr(prop, 'lower', None)
|
|
196
|
+
up = getattr(prop, 'upper', None)
|
|
197
|
+
if lo is None and up is None:
|
|
198
|
+
return None
|
|
199
|
+
if (lo or '0') == '1' and (up or '1') == '1':
|
|
200
|
+
return None # 1..1 = default, omitted (Delligatti §3.4)
|
|
201
|
+
if (up or lo) == (lo or up):
|
|
202
|
+
return lo or up
|
|
203
|
+
return '%s..%s' % (lo, up)
|
|
204
|
+
|
|
205
|
+
if isinstance(scope, (list, tuple)):
|
|
206
|
+
for el in scope:
|
|
207
|
+
if type(el).__name__ in ('Package', 'Model'):
|
|
208
|
+
_walk_pkg(el)
|
|
209
|
+
else:
|
|
210
|
+
_draw_block(el)
|
|
211
|
+
else:
|
|
212
|
+
_walk_pkg(scope)
|
|
213
|
+
|
|
214
|
+
return d
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def as_deployment_boxes(scope, frame_kind='deployment',
|
|
218
|
+
element_kind=None, element_name=None):
|
|
219
|
+
"""Build a UML 2.5.1 deployment diagram (ch19) from umlmeta data.
|
|
220
|
+
|
|
221
|
+
- Node, Device, ExecutionEnvironment, or an InstanceSpecification
|
|
222
|
+
of one = a perspective cube (instance labels are the ':name'
|
|
223
|
+
string); the deployment's elements connect in.
|
|
224
|
+
- Artifact = a Class rectangle + the keyword artifact.
|
|
225
|
+
- Deployment.deployedArtifact x location/deployment's owner = the
|
|
226
|
+
dashed 'deploy' dependency arrow (19.2.4).
|
|
227
|
+
- Artifact.manifestation[] = dashed 'manifest' lines (19.3.x).
|
|
228
|
+
- CommunicationPath memberEnds = plain association links (19.4.4).
|
|
229
|
+
"""
|
|
230
|
+
d = Diagram()
|
|
231
|
+
if frame_kind:
|
|
232
|
+
d.add_frame(frame_kind, element_kind, element_name, None)
|
|
233
|
+
|
|
234
|
+
def _name(el, default='unnamed'):
|
|
235
|
+
return getattr(el, 'name', None) or default
|
|
236
|
+
|
|
237
|
+
def _node_of(el, inst=False):
|
|
238
|
+
kinds = []
|
|
239
|
+
st = getattr(el, '_applied_stereotypes', None)
|
|
240
|
+
if st:
|
|
241
|
+
kinds = [s[1] for s in st]
|
|
242
|
+
return d.node(_name(el), kinds=kinds)
|
|
243
|
+
|
|
244
|
+
node_map = {}
|
|
245
|
+
|
|
246
|
+
def _add(el, cube):
|
|
247
|
+
cube_ = d.node(_name(el), kinds=[]) if cube else d.add_node(
|
|
248
|
+
_name(el), stereotypes=['artifact'])
|
|
249
|
+
node_map[id(el)] = cube_
|
|
250
|
+
d.nodes.append(cube_)
|
|
251
|
+
return cube_
|
|
252
|
+
|
|
253
|
+
def _walk(pkg):
|
|
254
|
+
for child in (getattr(pkg, 'packagedElement', None) or []):
|
|
255
|
+
tn = type(child).__name__
|
|
256
|
+
if tn == 'Package':
|
|
257
|
+
_walk(child)
|
|
258
|
+
elif tn == 'Node':
|
|
259
|
+
node_map[id(child)] = _node_of(child)
|
|
260
|
+
elif tn == 'Artifact':
|
|
261
|
+
node_map[id(child)] = d.artifact(_name(child))
|
|
262
|
+
elif tn == 'InstanceSpecification':
|
|
263
|
+
node_map[id(child)] = d.node(_name(child))
|
|
264
|
+
elif tn == 'Component':
|
|
265
|
+
node_map[id(child)] = d.add_node(_name(child))
|
|
266
|
+
# CommunicationPath (19.4.4): a plain association link between
|
|
267
|
+
# its endpoint types
|
|
268
|
+
for child in (getattr(pkg, 'packagedElement', None) or []):
|
|
269
|
+
if type(child).__name__ != 'CommunicationPath':
|
|
270
|
+
continue
|
|
271
|
+
ends = [p for p in (getattr(child, 'memberEnd', None) or [])
|
|
272
|
+
if getattr(p, 'type', None) is not None]
|
|
273
|
+
if len(ends) == 2:
|
|
274
|
+
d.add_edge(node_map.get(id(ends[0].type)),
|
|
275
|
+
node_map.get(id(ends[1].type)))
|
|
276
|
+
# deployments owned by each element
|
|
277
|
+
for child in (getattr(pkg, 'packagedElement', None) or []):
|
|
278
|
+
for dep in (getattr(child, 'deployment', None) or []):
|
|
279
|
+
for art in (getattr(dep, 'deployedArtifact', None) or []):
|
|
280
|
+
if isinstance(art, (str, int)):
|
|
281
|
+
continue
|
|
282
|
+
if id(art) not in node_map:
|
|
283
|
+
node_map[id(art)] = d.artifact(_name(art))
|
|
284
|
+
d.deploy(node_map[id(art)], node_map[id(child)])
|
|
285
|
+
for man in (getattr(child, 'manifestation', None) or []):
|
|
286
|
+
util = getattr(man, 'utilizedElement', None)
|
|
287
|
+
if util is None:
|
|
288
|
+
continue
|
|
289
|
+
if id(util) not in node_map:
|
|
290
|
+
node_map[id(util)] = d.add_node(_name(util))
|
|
291
|
+
d.manifest(node_map[id(child)], node_map[id(util)])
|
|
292
|
+
|
|
293
|
+
if isinstance(scope, (list, tuple)):
|
|
294
|
+
for el in scope:
|
|
295
|
+
_walk(el)
|
|
296
|
+
else:
|
|
297
|
+
_walk(scope)
|
|
298
|
+
|
|
299
|
+
d.render()
|
|
300
|
+
return d
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def render_bdd_boxes(scope, routing='orthogonal', **layout_kw) -> str:
|
|
304
|
+
d = as_bdd_boxes(scope)
|
|
305
|
+
d.render()
|
|
306
|
+
return d.render()
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def render_bdd_boxes_svg(scope, routing='orthogonal', **layout_kw) -> str:
|
|
310
|
+
d = as_bdd_boxes(scope)
|
|
311
|
+
return d.render_svg(routing=routing)
|