Skip to content

Commit 11c49c4

Browse files
committed
Merge branch 'various_fixes' into develop
2 parents 611a519 + a9712bd commit 11c49c4

19 files changed

Lines changed: 175 additions & 181 deletions

ymmsl/__init__.py

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,11 @@
77
from ymmsl.document import Document
88
from ymmsl.io import dump, load, load_as, save
99

10-
# Below imports are for backwards compatibility only, and deprecated
11-
from ymmsl.v0_1.checkpoint import (
12-
CheckpointRule, CheckpointRangeRule, CheckpointAtRule, Checkpoints)
13-
from ymmsl.v0_1.component import Component, Operator, Port, Ports
14-
from ymmsl.v0_1.configuration import Configuration, PartialConfiguration
15-
from ymmsl.v0_1.execution import (
16-
BaseEnv, ExecutionModel, Implementation, MPICoresResReq,
17-
MPINodesResReq, ResourceRequirements, ThreadedResReq,
18-
KeepsStateForNextUse)
19-
from ymmsl.v0_1.settings import Settings, SettingValue
20-
from ymmsl.v0_1.identity import Identifier, Reference
21-
from ymmsl.v0_1.model import Conduit, Model, ModelReference
2210

2311
__version__ = '0.14.1-dev'
2412
__author__ = 'Lourens Veen'
2513
__email__ = '[email protected]'
2614

2715

2816
__all__ = [
29-
'BaseEnv', 'CheckpointRule', 'CheckpointRangeRule', 'CheckpointAtRule',
30-
'Checkpoints', 'Component', 'Conduit', 'Configuration', 'convert_to',
31-
'Document', 'dump', 'DowngradeError', 'ExecutionModel', 'Identifier',
32-
'Implementation', 'KeepsStateForNextUse', 'load', 'load_as', 'Model',
33-
'ModelReference', 'MPICoresResReq', 'MPINodesResReq', 'Operator',
34-
'PartialConfiguration', 'Port', 'Ports', 'Reference', 'ResourceRequirements',
35-
'save', 'Settings', 'SettingValue', 'ThreadedResReq']
17+
'convert_to', 'Document', 'dump', 'DowngradeError', 'load', 'load_as', 'save']

ymmsl/conversion/convert_v0_1_to_v0_2.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ def convert_v0_1_to_v0_2(config: v0_1.PartialConfiguration) -> v0_2.Configuratio
3434
checkpoints = deepcopy(config.checkpoints)
3535
resume = deepcopy(config.resume)
3636

37+
warnings.warn(
38+
'Comments can unfortunately not be read by this converter, and so have been'
39+
' ignored. Please copy them into an appropriate description field.')
40+
3741
return v0_2.Configuration(
3842
description, None, models, None, settings, programs, resources, checkpoints,
3943
resume)

ymmsl/conversion/tests/test_convert_v0_1_to_v0_2.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414
Ref2 = v0_2.Reference
1515

1616

17+
@pytest.mark.filterwarnings('ignore:Comments.*')
1718
def test_convert_simple_config(empty_config: v0_1.PartialConfiguration) -> None:
1819
v2 = convert_v0_1_to_v0_2(empty_config)
20+
1921
assert v2.description == 'Please add a description'
2022
assert isinstance(v2.settings, v0_2.Settings)
2123
assert len(v2.settings) == 0
@@ -27,9 +29,10 @@ def test_convert_simple_config(empty_config: v0_1.PartialConfiguration) -> None:
2729
assert v2.resume == {}
2830

2931

32+
@pytest.mark.filterwarnings('ignore:Comments.*')
33+
@pytest.mark.filterwarnings('ignore:.*implementations have become programs.*')
3034
def test_convert_full_config(full_config: v0_1.Configuration) -> None:
31-
with pytest.warns(UserWarning):
32-
v2 = convert_v0_1_to_v0_2(full_config)
35+
v2 = convert_v0_1_to_v0_2(full_config)
3336
assert v2.description == 'Testing a full configuration'
3437

3538
assert v2.settings is not full_config.settings

ymmsl/conversion/tests/test_converter.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ def test_convert_to_no_change(full_config: v0_1.PartialConfiguration) -> None:
1414

1515

1616
@pytest.mark.filterwarnings('ignore:.*specify the ports.*')
17+
@pytest.mark.filterwarnings('ignore:Comments.*')
1718
def test_convert_to(full_config: v0_1.PartialConfiguration) -> None:
1819
new_config = convert_to(v0_2.Configuration, full_config)
1920
assert isinstance(new_config, v0_2.Configuration)

ymmsl/tests/test_load_as.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@
44
import ymmsl.v0_1 as v0_1
55
import ymmsl.v0_2 as v0_2
66

7+
import pytest
8+
79

810
def test_load_as_v0_1(test_yaml1: str) -> None:
911
config = load_as(v0_1.PartialConfiguration, test_yaml1)
1012
assert isinstance(config, v0_1.PartialConfiguration)
1113
assert cast(List[int], config.settings['test_list'])[0] == 12.3
1214

1315

16+
@pytest.mark.filterwarnings('ignore:Comments.*')
1417
def test_load_as_v0_2(test_yaml1: str) -> None:
1518
config = load_as(v0_2.Configuration, test_yaml1)
1619
assert isinstance(config, v0_2.Configuration)

ymmsl/v0_2/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from ymmsl.v0_2.program import Program
1313
from ymmsl.v0_2.resources import (
1414
MPICoresResReq, MPINodesResReq, ResourceRequirements, ThreadedResReq)
15+
from ymmsl.v0_2.resolver import resolve
1516
from ymmsl.v0_2.settings import Settings, SettingValue
1617
from ymmsl.v0_2.supported_settings import (
1718
SettingType, SupportedSetting, SupportedSettings)
@@ -23,6 +24,6 @@
2324
'ExecutionModel', 'Identifier', 'Implementation', 'ImportKind',
2425
'ImportStatement', 'KeepsStateForNextUse', 'Model', 'MPICoresResReq',
2526
'MPINodesResReq', 'Operator', 'Port', 'Ports', 'Program', 'Reference',
26-
'ReferencePart', 'ResourceRequirements', 'Settings', 'SettingType',
27+
'ReferencePart', 'resolve', 'ResourceRequirements', 'Settings', 'SettingType',
2728
'SettingValue', 'SupportedSetting', 'SupportedSettings', 'ThreadedResReq',
2829
'Timeline']

ymmsl/v0_2/component.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import yatiml
55

66
from ymmsl.v0_2.ports import Ports
7-
from ymmsl.v0_2.identity import Identifier, Reference
7+
from ymmsl.v0_2.identity import Reference
88

99

1010
class Component:
@@ -46,7 +46,7 @@ def __init__(
4646
multiplicity: The shape of the set of instances, or a number describing the
4747
size of a 1D set of them, or None to have a single instance.
4848
"""
49-
self.name = Identifier(name)
49+
self.name = Reference(name)
5050
self.ports = ports
5151
self.description = description
5252
self.optional = optional
@@ -113,10 +113,10 @@ def generate_indices(multiplicity: List[int]) -> List[List[int]]:
113113
result = list() # type: List[Reference]
114114

115115
if len(self.multiplicity) == 0:
116-
result.append(Reference([self.name]))
116+
result.append(self.name)
117117
else:
118118
for index in generate_indices(self.multiplicity):
119-
result.append(Reference([self.name]) + index)
119+
result.append(self.name + index)
120120

121121
return result
122122

ymmsl/v0_2/configuration.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from ymmsl.v0_2.checkpoint import Checkpoints
1313
from ymmsl.v0_2.execution import ExecutionModel
1414
from ymmsl.v0_2.resources import MPICoresResReq, MPINodesResReq, ResourceRequirements
15-
from ymmsl.v0_2.identity import Reference
15+
from ymmsl.v0_2.identity import Identifier, Reference
1616
from ymmsl.v0_2.implementation import Implementation # noqa: F401
1717
from ymmsl.v0_2.imports import ImportStatement
1818
from ymmsl.v0_2.settings import Settings, SettingValue
@@ -224,7 +224,7 @@ def top_models(self) -> List[Model]:
224224
top_models = copy(self.models)
225225

226226
for model in self.models.values():
227-
for component in model.components:
227+
for component in model.components.values():
228228
if component.implementation:
229229
if component.implementation in top_models:
230230
del top_models[component.implementation]
@@ -251,7 +251,7 @@ def _component_paths(self) -> Dict[Reference, Component]:
251251

252252
while queue:
253253
model, prefix = queue.pop(0)
254-
for component in model.components:
254+
for component in model.components.values():
255255
path = prefix + component.name
256256
impl = self.custom_implementations.get(path, component.implementation)
257257
if impl is not None:
@@ -403,7 +403,7 @@ def _check_consistent_settings(
403403
return errors
404404

405405
def _check_supported_setting(
406-
self, component: Component, component_path: Reference, name: Reference,
406+
self, component: Component, component_path: Reference, name: Identifier,
407407
typ: SettingType, impl: Implementation) -> List[str]:
408408
"""Check that the value of the given setting matches the given type.
409409

ymmsl/v0_2/model.py

Lines changed: 36 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from collections import OrderedDict
2+
from copy import copy
23
from enum import Enum
34
from typing import Any, cast, List, Optional, Sequence, Union
45

@@ -263,7 +264,7 @@ def __init__(
263264
self, name: str, ports: Optional[Ports] = None,
264265
description: str = '',
265266
supported_settings: Optional[SupportedSettings] = None,
266-
components: Optional[List[Component]] = None,
267+
components: Optional[Sequence[Component]] = None,
267268
conduits: Optional[Sequence[AnyConduit]] = None) -> None:
268269
"""Create a Model.
269270
@@ -278,9 +279,19 @@ def __init__(
278279
super().__init__(name, ports, description, supported_settings)
279280

280281
if components is None:
281-
self.components = []
282+
self.components = {}
282283
else:
283-
self.components = components
284+
for i1, c1 in enumerate(components):
285+
num_conflicts = 0
286+
for i2 in range(i1-1):
287+
if c1.name == components[i2].name:
288+
num_conflicts += 1
289+
if num_conflicts > 0:
290+
raise ValueError(
291+
f'There are {num_conflicts + 1} components named'
292+
f' {c1.name}.')
293+
294+
self.components = {copy(c.name): c for c in components}
284295

285296
self.conduits = list() # type: List[Conduit]
286297
if conduits:
@@ -294,16 +305,13 @@ def check_consistent(self) -> List[str]:
294305
"""Check that the model is internally consistent.
295306
296307
This checks:
297-
- that no two components have the same name
298308
- that every conduit is connected to two existing ports on existing
299309
components, or on the model itself.
300310
301311
Returns a list of errors, or an empty list if none were found.
302312
"""
303313
errors: List[str] = list()
304314

305-
errors.extend(self._check_component_name_conflicts())
306-
307315
model_receiving_ports = self.ports.receiving_port_names()
308316
model_sending_ports = self.ports.sending_port_names()
309317

@@ -313,24 +321,6 @@ def check_consistent(self) -> List[str]:
313321

314322
return errors
315323

316-
def _check_component_name_conflicts(self) -> List[str]:
317-
"""Check that no two components have the same name.
318-
319-
Returns a list of errors.
320-
"""
321-
errors = list()
322-
323-
for i1, c1 in enumerate(self.components):
324-
num_conflicts = 0
325-
for i2 in range(i1-1):
326-
if c1.name == self.components[i2].name:
327-
num_conflicts += 1
328-
if num_conflicts > 0:
329-
errors.append(
330-
f'There are {num_conflicts + 1} components named {c1.name}.')
331-
332-
return errors
333-
334324
def _check_sending_side(
335325
self, conduit: Conduit, model_receiving_ports: List[Identifier]
336326
) -> List[str]:
@@ -344,23 +334,20 @@ def _check_sending_side(
344334
f' {conduit.sending_port()} that does not exist.')
345335
else:
346336
# from component
347-
snd_cmp = [
348-
c for c in self.components
349-
if c.name == conduit.sending_component()]
350-
if not snd_cmp:
337+
if conduit.sending_component() not in self.components:
351338
errors.append(
352339
f'Conduit {conduit} refers to a component named'
353340
f' {conduit.sending_component()}, which is not present in'
354341
' the model.')
355342
else:
356-
if len(snd_cmp) == 1:
357-
cmp_sending_ports = snd_cmp[0].ports.sending_port_names()
358-
if conduit.sending_port() not in cmp_sending_ports:
359-
errors.append(
360-
f'Conduit {conduit} refers to a sending port named'
361-
f' {conduit.sending_port()}, which is not present'
362-
f' on sending component {snd_cmp[0].name}, or is not'
363-
' an O_I or O_F port.')
343+
snd_cmp = self.components[conduit.sending_component()]
344+
cmp_sending_ports = snd_cmp.ports.sending_port_names()
345+
if conduit.sending_port() not in cmp_sending_ports:
346+
errors.append(
347+
f'Conduit {conduit} refers to a sending port named'
348+
f' {conduit.sending_port()}, which is not present on'
349+
f' sending component {snd_cmp.name}, or is not an O_I or'
350+
' O_F port.')
364351
return errors
365352

366353
def _check_receiving_side(
@@ -376,26 +363,23 @@ def _check_receiving_side(
376363
f' {conduit.receiving_port()} that does not exist.')
377364
else:
378365
# to component
379-
rcvng_cmp = [
380-
c for c in self.components
381-
if c.name == conduit.receiving_component()]
382-
if not rcvng_cmp:
366+
if conduit.receiving_component() not in self.components:
383367
errors.append(
384368
f'Conduit {conduit} refers to a component named'
385369
f' {conduit.receiving_component()}, which is not present in'
386370
' the model.')
387371
else:
388-
if len(rcvng_cmp) == 1:
389-
rcvr_recv_ports = (
390-
rcvng_cmp[0].ports.receiving_port_names() +
391-
['muscle_settings_in'])
392-
393-
if conduit.receiving_port() not in rcvr_recv_ports:
394-
errors.append(
395-
f'Conduit {conduit} refers to a receiving port named'
396-
f' {conduit.receiving_port()}, which is not present'
397-
f' on receiving component {rcvng_cmp[0].name}, or is'
398-
' not an F_INIT or S port.')
372+
rcvng_cmp = self.components[conduit.receiving_component()]
373+
rcvr_recv_ports = (
374+
rcvng_cmp.ports.receiving_port_names() +
375+
['muscle_settings_in'])
376+
377+
if conduit.receiving_port() not in rcvr_recv_ports:
378+
errors.append(
379+
f'Conduit {conduit} refers to a receiving port named'
380+
f' {conduit.receiving_port()}, which is not present on'
381+
f' receiving component {rcvng_cmp.name}, or is not an'
382+
' F_INIT or S port.')
399383
return errors
400384

401385
def _conduits_for_export(self) -> List[AnyConduit]:
@@ -439,7 +423,7 @@ def _yatiml_savorize(cls, node: yatiml.Node) -> None:
439423

440424
@classmethod
441425
def _yatiml_sweeten(cls, node: yatiml.Node) -> None:
442-
node.seq_attribute_to_map('components', 'name',)
426+
node.index_attribute_to_map('components', 'name')
443427

444428
if len(node.get_attribute('conduits').seq_items()) == 0:
445429
node.remove_attribute('conduits')

ymmsl/v0_2/ports.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from collections import OrderedDict
2-
from typing import Any, cast, Iterator, List, overload, Sequence, Union
2+
from typing import Any, cast, Iterator, List, Optional, overload, Sequence, Union
33

44
from ymmsl.v0_1.component import Operator # also the v0.2 version, import from here
55
from ymmsl.v0_2.identity import Identifier, Reference
@@ -124,19 +124,23 @@ class Port:
124124
Attributes:
125125
name: The name of the port
126126
operator: The MMSL operator in which this port is used
127-
timeline: The timeline this port is on, relative to its component
127+
timeline: The timeline this port is on, relative to its component.
128128
"""
129129
def __init__(
130-
self, name: Identifier, operator: Operator, timeline: Timeline) -> None:
130+
self, name: Identifier, operator: Operator,
131+
timeline: Optional[Timeline] = None) -> None:
131132
"""Create a Port.
132133
133134
Args:
134135
name: The name of the port
135136
operator: The MMSL Operator in which this port is used
136-
timeline: The Timeline this port is on
137+
timeline: The Timeline this port is on. If None or omitted, an empty
138+
timeline will be set.
137139
"""
138140
self.name = name
139141
self.operator = operator
142+
if timeline is None:
143+
timeline = Timeline('')
140144
self.timeline = timeline
141145

142146
def __eq__(self, other: Any) -> bool:

0 commit comments

Comments
 (0)