wavesimpro 1.10.0__tar.gz → 1.10.2__tar.gz
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.
- {wavesimpro-1.10.0 → wavesimpro-1.10.2}/PKG-INFO +3 -2
- {wavesimpro-1.10.0 → wavesimpro-1.10.2}/README.md +2 -1
- {wavesimpro-1.10.0 → wavesimpro-1.10.2}/pyproject.toml +1 -1
- {wavesimpro-1.10.0 → wavesimpro-1.10.2}/src/wavesimpro/__init__.py +4 -2
- {wavesimpro-1.10.0 → wavesimpro-1.10.2}/src/wavesimpro/client.py +121 -172
- wavesimpro-1.10.2/src/wavesimpro/model.py +588 -0
- wavesimpro-1.10.2/src/wavesimpro/runs.py +955 -0
- {wavesimpro-1.10.0 → wavesimpro-1.10.2}/src/wavesimpro.egg-info/PKG-INFO +3 -2
- {wavesimpro-1.10.0 → wavesimpro-1.10.2}/src/wavesimpro.egg-info/SOURCES.txt +4 -1
- wavesimpro-1.10.2/tests/test_command_wait.py +64 -0
- {wavesimpro-1.10.0 → wavesimpro-1.10.2}/setup.cfg +0 -0
- {wavesimpro-1.10.0 → wavesimpro-1.10.2}/src/wavesimpro/__main__.py +0 -0
- {wavesimpro-1.10.0 → wavesimpro-1.10.2}/src/wavesimpro/binary_utils.py +0 -0
- {wavesimpro-1.10.0 → wavesimpro-1.10.2}/src/wavesimpro/config.py +0 -0
- {wavesimpro-1.10.0 → wavesimpro-1.10.2}/src/wavesimpro/exceptions.py +0 -0
- {wavesimpro-1.10.0 → wavesimpro-1.10.2}/src/wavesimpro/execute.py +0 -0
- {wavesimpro-1.10.0 → wavesimpro-1.10.2}/src/wavesimpro/json_helper.py +0 -0
- {wavesimpro-1.10.0 → wavesimpro-1.10.2}/src/wavesimpro/py.typed +0 -0
- {wavesimpro-1.10.0 → wavesimpro-1.10.2}/src/wavesimpro/setup.py +0 -0
- {wavesimpro-1.10.0 → wavesimpro-1.10.2}/src/wavesimpro/simulate.py +0 -0
- {wavesimpro-1.10.0 → wavesimpro-1.10.2}/src/wavesimpro/validate.py +0 -0
- {wavesimpro-1.10.0 → wavesimpro-1.10.2}/src/wavesimpro.egg-info/dependency_links.txt +0 -0
- {wavesimpro-1.10.0 → wavesimpro-1.10.2}/src/wavesimpro.egg-info/requires.txt +0 -0
- {wavesimpro-1.10.0 → wavesimpro-1.10.2}/src/wavesimpro.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: wavesimpro
|
|
3
|
-
Version: 1.10.
|
|
3
|
+
Version: 1.10.2
|
|
4
4
|
Summary: WavesimPro Python API — cloud execution client for the Rayfos simulation platform
|
|
5
5
|
Author: Rayfos
|
|
6
6
|
Project-URL: Repository, https://github.com/rayfos/wavesimpro
|
|
@@ -67,7 +67,8 @@ field = wp.read_binary_field(run.download("output.bin"))
|
|
|
67
67
|
`create_stl_primitive`, `create_gds_primitive` (with `create_gds_layer_config` /
|
|
68
68
|
`create_gds_structure_override`), `create_voxels_model_primitive`.
|
|
69
69
|
- Per-primitive sub-pixel flag (`subpixel=True/False` on every shape, STL and GDSII helper)
|
|
70
|
-
to choose which shapes get smoothed interfaces.
|
|
70
|
+
to choose which shapes get smoothed interfaces. Off by default: a setup with no averaged
|
|
71
|
+
shape is voxelised exactly as before 1.10 and runs on every compute node.
|
|
71
72
|
- Per-axis pixel sizes (`pixel_size=[dx, dy, dz]`) and sub-pixel permittivity averaging
|
|
72
73
|
(`subpixel_factor=`), when the compute node supports them.
|
|
73
74
|
- Preflight memory check and cost estimate before a run.
|
|
@@ -44,7 +44,8 @@ field = wp.read_binary_field(run.download("output.bin"))
|
|
|
44
44
|
`create_stl_primitive`, `create_gds_primitive` (with `create_gds_layer_config` /
|
|
45
45
|
`create_gds_structure_override`), `create_voxels_model_primitive`.
|
|
46
46
|
- Per-primitive sub-pixel flag (`subpixel=True/False` on every shape, STL and GDSII helper)
|
|
47
|
-
to choose which shapes get smoothed interfaces.
|
|
47
|
+
to choose which shapes get smoothed interfaces. Off by default: a setup with no averaged
|
|
48
|
+
shape is voxelised exactly as before 1.10 and runs on every compute node.
|
|
48
49
|
- Per-axis pixel sizes (`pixel_size=[dx, dy, dz]`) and sub-pixel permittivity averaging
|
|
49
50
|
(`subpixel_factor=`), when the compute node supports them.
|
|
50
51
|
- Preflight memory check and cost estimate before a run.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "wavesimpro"
|
|
7
|
-
version = "1.10.
|
|
7
|
+
version = "1.10.2"
|
|
8
8
|
description = "WavesimPro Python API — cloud execution client for the Rayfos simulation platform"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
@@ -6,7 +6,7 @@ supporting both cloud and local deployment modes, as well as direct interaction
|
|
|
6
6
|
with RayfosMachineService via its REST API.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
from .client import RayfosClient
|
|
9
|
+
from .client import RayfosClient, command_status_name, is_terminal_command_status
|
|
10
10
|
from .config import ClientConfig
|
|
11
11
|
from .exceptions import (
|
|
12
12
|
RayfosAPIError,
|
|
@@ -54,10 +54,12 @@ from .json_helper import (
|
|
|
54
54
|
parse_progress_data,
|
|
55
55
|
)
|
|
56
56
|
|
|
57
|
-
__version__ = "1.10.
|
|
57
|
+
__version__ = "1.10.2"
|
|
58
58
|
__all__ = [
|
|
59
59
|
# Client
|
|
60
60
|
"RayfosClient",
|
|
61
|
+
"command_status_name",
|
|
62
|
+
"is_terminal_command_status",
|
|
61
63
|
"ClientConfig",
|
|
62
64
|
# Exceptions
|
|
63
65
|
"RayfosAPIError",
|
|
@@ -77,6 +77,49 @@ def _make_progress_callback(total_bytes: int, desc: str):
|
|
|
77
77
|
logger = logging.getLogger(__name__)
|
|
78
78
|
|
|
79
79
|
|
|
80
|
+
|
|
81
|
+
# CommandStatus enum (C# RayfosCommandModels) by value; the server sends the NAME in "status"
|
|
82
|
+
# (JsonStringEnumConverter). Older servers sent the integer in "commandStatus".
|
|
83
|
+
_COMMAND_STATUS_BY_INT = {
|
|
84
|
+
0: 'New', 1: 'Assigned', 2: 'SentToMachine', 3: 'SendToMachineForceCancellation',
|
|
85
|
+
4: 'SendToMachineForceDeleteTask', 5: 'RequestedForceCancellationInMachine',
|
|
86
|
+
6: 'RequestedForceStopTaskInMachine', 7: 'Cancelled', 8: 'Deleted', 9: 'Running',
|
|
87
|
+
10: 'Completed', 11: 'Failed', 12: 'FailedRemovedFromMachine',
|
|
88
|
+
}
|
|
89
|
+
_TERMINAL_STATUSES = {'Cancelled', 'Deleted', 'Completed', 'Failed', 'FailedRemovedFromMachine'}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def command_status_name(status: Dict[str, Any]) -> str:
|
|
93
|
+
"""The command state name from a command-status dict, whatever the server version.
|
|
94
|
+
|
|
95
|
+
Reads ``status`` (current servers: the enum name, e.g. ``"Completed"``), falling back to the
|
|
96
|
+
legacy ``commandStatus`` / ``CommandStatus`` (integer or name). Returns ``"Unknown"`` when absent.
|
|
97
|
+
"""
|
|
98
|
+
if not isinstance(status, dict):
|
|
99
|
+
return 'Unknown'
|
|
100
|
+
for key in ('status', 'Status', 'commandStatus', 'CommandStatus'):
|
|
101
|
+
raw = status.get(key)
|
|
102
|
+
if raw is None or raw == '':
|
|
103
|
+
continue
|
|
104
|
+
if isinstance(raw, bool):
|
|
105
|
+
continue
|
|
106
|
+
if isinstance(raw, int):
|
|
107
|
+
return _COMMAND_STATUS_BY_INT.get(raw, f'Unknown({raw})')
|
|
108
|
+
text = str(raw).strip()
|
|
109
|
+
if text.isdigit():
|
|
110
|
+
return _COMMAND_STATUS_BY_INT.get(int(text), f'Unknown({text})')
|
|
111
|
+
for name in _COMMAND_STATUS_BY_INT.values():
|
|
112
|
+
if name.lower() == text.lower():
|
|
113
|
+
return name
|
|
114
|
+
return text
|
|
115
|
+
return 'Unknown'
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def is_terminal_command_status(name: str) -> bool:
|
|
119
|
+
"""True for Completed, Failed, FailedRemovedFromMachine, Cancelled and Deleted."""
|
|
120
|
+
return name in _TERMINAL_STATUSES
|
|
121
|
+
|
|
122
|
+
|
|
80
123
|
class RayfosClient:
|
|
81
124
|
"""
|
|
82
125
|
Client for interacting with RayfosJobServer API
|
|
@@ -740,9 +783,9 @@ class RayfosClient:
|
|
|
740
783
|
|
|
741
784
|
Example:
|
|
742
785
|
>>> version = client.get_project_version(project_id, version_id)
|
|
743
|
-
>>>
|
|
744
|
-
...
|
|
745
|
-
...
|
|
786
|
+
>>> for file_id in version.get('outputFileIds', []):
|
|
787
|
+
... info = client.get_file_info(project_id, version_id, file_id)
|
|
788
|
+
... print(f"Output file: {info.get('fileName')} ({info.get('fileSize')} bytes)")
|
|
746
789
|
"""
|
|
747
790
|
return self._get(f"/api/projects/{project_id}/versions/{version_id}")
|
|
748
791
|
|
|
@@ -1287,7 +1330,7 @@ class RayfosClient:
|
|
|
1287
1330
|
Returns:
|
|
1288
1331
|
File metadata dictionary
|
|
1289
1332
|
"""
|
|
1290
|
-
return self._get(f"/api/projects/{project_id}/versions/{version_id}/files/{file_id}")
|
|
1333
|
+
return self._get(f"/api/projects/{project_id}/versions/{version_id}/files/{file_id}/metadata")
|
|
1291
1334
|
|
|
1292
1335
|
# ========================================================================
|
|
1293
1336
|
# COMMAND OPERATIONS
|
|
@@ -1382,7 +1425,8 @@ class RayfosClient:
|
|
|
1382
1425
|
Ignored when using a local machine service URL.
|
|
1383
1426
|
|
|
1384
1427
|
Returns:
|
|
1385
|
-
Command status dictionary
|
|
1428
|
+
Command status dictionary. The command id is under ``"id"`` and, for convenience,
|
|
1429
|
+
``"commandId"``; the state is under ``"status"`` (read it with ``command_status_name``).
|
|
1386
1430
|
|
|
1387
1431
|
Example:
|
|
1388
1432
|
>>> command = client.create_command(project_id, version_id,
|
|
@@ -1390,9 +1434,17 @@ class RayfosClient:
|
|
|
1390
1434
|
>>> print(f"Command created: {command['id']}")
|
|
1391
1435
|
"""
|
|
1392
1436
|
if self.config.machine_service_url:
|
|
1393
|
-
|
|
1437
|
+
result = self._create_command_machine_service(project_id, version_id)
|
|
1394
1438
|
else:
|
|
1395
|
-
|
|
1439
|
+
result = self._create_command_cloud(project_id, version_id, engine_preference, use_gpu, preferred_machine_id)
|
|
1440
|
+
# The server names the command id "id"; other API calls and version records use "commandId".
|
|
1441
|
+
# Return both so either spelling works.
|
|
1442
|
+
if isinstance(result, dict):
|
|
1443
|
+
cid = result.get('id') or result.get('commandId') or result.get('Id')
|
|
1444
|
+
if cid:
|
|
1445
|
+
result.setdefault('id', cid)
|
|
1446
|
+
result.setdefault('commandId', cid)
|
|
1447
|
+
return result
|
|
1396
1448
|
|
|
1397
1449
|
def _create_command_machine_service(
|
|
1398
1450
|
self,
|
|
@@ -1439,35 +1491,8 @@ class RayfosClient:
|
|
|
1439
1491
|
|
|
1440
1492
|
@staticmethod
|
|
1441
1493
|
def _parse_command_status(status_raw) -> str:
|
|
1442
|
-
"""
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
Args:
|
|
1446
|
-
status_raw: Either integer enum value or string name
|
|
1447
|
-
|
|
1448
|
-
Returns:
|
|
1449
|
-
String name of the status
|
|
1450
|
-
"""
|
|
1451
|
-
# CommandStatus enum from C# RayfosCommandModels
|
|
1452
|
-
COMMAND_STATUS_MAP = {
|
|
1453
|
-
0: 'New',
|
|
1454
|
-
1: 'Assigned',
|
|
1455
|
-
2: 'SentToMachine',
|
|
1456
|
-
3: 'SendToMachineForceCancellation',
|
|
1457
|
-
4: 'SendToMachineForceDeleteTask',
|
|
1458
|
-
5: 'RequestedForceCancellationInMachine',
|
|
1459
|
-
6: 'RequestedForceStopTaskInMachine',
|
|
1460
|
-
7: 'Cancelled',
|
|
1461
|
-
8: 'Deleted',
|
|
1462
|
-
9: 'Running',
|
|
1463
|
-
10: 'Completed',
|
|
1464
|
-
11: 'Failed',
|
|
1465
|
-
12: 'FailedRemovedFromMachine'
|
|
1466
|
-
}
|
|
1467
|
-
|
|
1468
|
-
if isinstance(status_raw, int):
|
|
1469
|
-
return COMMAND_STATUS_MAP.get(status_raw, f'Unknown({status_raw})')
|
|
1470
|
-
return status_raw
|
|
1494
|
+
"""Command state name from an integer (legacy ``commandStatus``) or a name (current ``status``)."""
|
|
1495
|
+
return command_status_name({"status": status_raw})
|
|
1471
1496
|
|
|
1472
1497
|
@staticmethod
|
|
1473
1498
|
def _parse_progress_data(progress_obj) -> Dict[str, Any]:
|
|
@@ -1542,16 +1567,16 @@ class RayfosClient:
|
|
|
1542
1567
|
command_id: Command UUID
|
|
1543
1568
|
|
|
1544
1569
|
Returns:
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1570
|
+
Command-status dict: ``id``, ``status`` (enum name such as ``"Running"`` or
|
|
1571
|
+
``"Completed"``), ``statusText``, ``progressValue``. Older servers sent
|
|
1572
|
+
the state as the integer ``commandStatus``; :func:`command_status_name` reads either.
|
|
1573
|
+
|
|
1548
1574
|
Example:
|
|
1549
1575
|
>>> status = client.get_command_status("command-uuid")
|
|
1550
|
-
>>>
|
|
1551
|
-
>>> print(f"
|
|
1552
|
-
>>>
|
|
1553
|
-
|
|
1554
|
-
... print(f"Output files: {status.get('outputFileIds', [])}")
|
|
1576
|
+
>>> name = command_status_name(status) # "Running", "Completed", ...
|
|
1577
|
+
>>> print(f"{name}: {status.get('statusText')} ({status.get('progressValue', 0)}%)")
|
|
1578
|
+
>>> if name == "Completed":
|
|
1579
|
+
... files = client.get_project_version(project_id, version_id)["outputFileIds"]
|
|
1555
1580
|
"""
|
|
1556
1581
|
# Always call RayfosJobServer directly for command status (user endpoint)
|
|
1557
1582
|
# Machine service is only for file operations, not command status
|
|
@@ -1561,113 +1586,73 @@ class RayfosClient:
|
|
|
1561
1586
|
self,
|
|
1562
1587
|
command_id: str,
|
|
1563
1588
|
poll_interval: float = 2.0,
|
|
1564
|
-
timeout: Optional[float] =
|
|
1589
|
+
timeout: Optional[float] = 24 * 3600,
|
|
1565
1590
|
progress_callback: Optional[callable] = None
|
|
1566
1591
|
) -> Dict[str, Any]:
|
|
1567
1592
|
"""
|
|
1568
|
-
Wait for a command to
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1593
|
+
Wait for a command to finish (synchronous execution).
|
|
1594
|
+
|
|
1595
|
+
Polls :meth:`get_command_status` until the command reaches a terminal state and returns the
|
|
1596
|
+
final status dict on ``Completed``. The state is read with :func:`command_status_name`, which
|
|
1597
|
+
understands both the current ``status`` field (enum name) and the legacy ``commandStatus``
|
|
1598
|
+
integer, so this works against every server version.
|
|
1599
|
+
|
|
1573
1600
|
Args:
|
|
1574
|
-
command_id: Command UUID
|
|
1575
|
-
poll_interval: Seconds between status checks (default
|
|
1576
|
-
timeout: Maximum seconds to wait
|
|
1577
|
-
progress_callback: Optional callback(status_dict) called on each
|
|
1578
|
-
|
|
1601
|
+
command_id: Command UUID (``create_command(...)["id"]``, also available as ``["commandId"]``)
|
|
1602
|
+
poll_interval: Seconds between status checks (default 2.0)
|
|
1603
|
+
timeout: Maximum seconds to wait (default 24 h; ``None`` waits forever)
|
|
1604
|
+
progress_callback: Optional ``callback(status_dict)`` called on each poll
|
|
1605
|
+
|
|
1579
1606
|
Returns:
|
|
1580
|
-
Final
|
|
1581
|
-
|
|
1607
|
+
Final command-status dict (``status == "Completed"``). To get the result files, list the
|
|
1608
|
+
version's ``outputFileIds`` (``get_project_version(project_id, version_id)["outputFileIds"]``,
|
|
1609
|
+
then ``download_file``); the ``outputFileIds`` field
|
|
1610
|
+
of the status is not filled by current servers.
|
|
1611
|
+
|
|
1582
1612
|
Raises:
|
|
1583
|
-
TimeoutError:
|
|
1584
|
-
RayfosAPIError:
|
|
1585
|
-
|
|
1613
|
+
TimeoutError: the command did not finish within ``timeout`` seconds
|
|
1614
|
+
RayfosAPIError: the command failed, was cancelled or was deleted
|
|
1615
|
+
|
|
1586
1616
|
Example:
|
|
1587
|
-
>>>
|
|
1588
|
-
>>>
|
|
1589
|
-
|
|
1590
|
-
>>> # Wait for completion with progress updates
|
|
1591
|
-
>>> def show_progress(status):
|
|
1592
|
-
... print(f"Progress: {status.get('progressValue', 0)}% - {status.get('statusText', 'Running')}")
|
|
1593
|
-
>>>
|
|
1594
|
-
>>> final_status = client.wait_for_command_completion(
|
|
1595
|
-
... command['id'],
|
|
1596
|
-
... progress_callback=show_progress,
|
|
1597
|
-
... timeout=3600 # 1 hour timeout
|
|
1598
|
-
... )
|
|
1599
|
-
>>> print(f"Command completed! Output files: {final_status.get('outputFileIds', [])}")
|
|
1617
|
+
>>> command = client.create_command(project_id, version_id)
|
|
1618
|
+
>>> final = client.wait_for_command_completion(command["id"], timeout=3600,
|
|
1619
|
+
... progress_callback=lambda s: print(command_status_name(s), s.get("statusText")))
|
|
1600
1620
|
"""
|
|
1601
1621
|
import time
|
|
1602
|
-
|
|
1603
|
-
# CommandStatus enum values from C# (serialized as integers)
|
|
1604
|
-
# New=0, Assigned=1, SentToMachine=2, etc.
|
|
1605
|
-
COMMAND_STATUS = {
|
|
1606
|
-
0: 'New',
|
|
1607
|
-
1: 'Assigned',
|
|
1608
|
-
2: 'SentToMachine',
|
|
1609
|
-
3: 'SendToMachineForceCancellation',
|
|
1610
|
-
4: 'SendToMachineForceDeleteTask',
|
|
1611
|
-
5: 'RequestedForceCancellationInMachine',
|
|
1612
|
-
6: 'RequestedForceStopTaskInMachine',
|
|
1613
|
-
7: 'Cancelled',
|
|
1614
|
-
8: 'Deleted',
|
|
1615
|
-
9: 'Running',
|
|
1616
|
-
10: 'Completed',
|
|
1617
|
-
11: 'Failed',
|
|
1618
|
-
12: 'FailedRemovedFromMachine'
|
|
1619
|
-
}
|
|
1620
|
-
|
|
1621
|
-
# Terminal states (by integer value and name)
|
|
1622
|
-
TERMINAL_STATUS_INTS = {7, 8, 10, 11, 12} # Cancelled, Deleted, Completed, Failed, FailedRemovedFromMachine
|
|
1623
|
-
TERMINAL_STATUS_NAMES = {'Cancelled', 'Deleted', 'Completed', 'Failed', 'FailedRemovedFromMachine'}
|
|
1624
|
-
|
|
1622
|
+
|
|
1625
1623
|
start_time = time.time()
|
|
1626
|
-
|
|
1627
1624
|
while True:
|
|
1628
|
-
# Check timeout
|
|
1629
|
-
if timeout and (time.time() - start_time) > timeout:
|
|
1630
|
-
raise TimeoutError(f"Command {command_id} did not complete within {timeout} seconds")
|
|
1631
|
-
|
|
1632
|
-
# Get current status
|
|
1633
1625
|
status = self.get_command_status(command_id)
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
# Handle both integer (from C# enum) and string formats
|
|
1637
|
-
if isinstance(command_status_raw, int):
|
|
1638
|
-
command_status = COMMAND_STATUS.get(command_status_raw, f'Unknown({command_status_raw})')
|
|
1639
|
-
is_terminal = command_status_raw in TERMINAL_STATUS_INTS
|
|
1640
|
-
else:
|
|
1641
|
-
command_status = command_status_raw
|
|
1642
|
-
is_terminal = command_status in TERMINAL_STATUS_NAMES
|
|
1643
|
-
|
|
1644
|
-
# Call progress callback if provided
|
|
1626
|
+
name = command_status_name(status)
|
|
1627
|
+
|
|
1645
1628
|
if progress_callback:
|
|
1646
1629
|
progress_callback(status)
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
if command_status in ('Completed', 10):
|
|
1630
|
+
|
|
1631
|
+
if is_terminal_command_status(name):
|
|
1632
|
+
if name == 'Completed':
|
|
1651
1633
|
return status
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
raise RayfosAPIError(f"Command {command_id} failed: {
|
|
1655
|
-
|
|
1634
|
+
detail = status.get('statusText') or ''
|
|
1635
|
+
if name in ('Failed', 'FailedRemovedFromMachine'):
|
|
1636
|
+
raise RayfosAPIError(f"Command {command_id} failed: {detail or name}")
|
|
1637
|
+
if name == 'Cancelled':
|
|
1656
1638
|
raise RayfosAPIError(f"Command {command_id} was cancelled")
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1639
|
+
raise RayfosAPIError(f"Command {command_id} was deleted")
|
|
1640
|
+
|
|
1641
|
+
if timeout is not None and (time.time() - start_time) > timeout:
|
|
1642
|
+
raise TimeoutError(f"Command {command_id} did not finish within {timeout:.0f} s (last status: {name})")
|
|
1643
|
+
|
|
1661
1644
|
time.sleep(poll_interval)
|
|
1662
|
-
|
|
1645
|
+
|
|
1663
1646
|
def create_command_and_wait(
|
|
1664
1647
|
self,
|
|
1665
1648
|
project_id: str,
|
|
1666
1649
|
version_id: str,
|
|
1667
1650
|
local_machine_service_id: Optional[str] = None,
|
|
1668
1651
|
poll_interval: float = 2.0,
|
|
1669
|
-
timeout: Optional[float] =
|
|
1670
|
-
progress_callback: Optional[callable] = None
|
|
1652
|
+
timeout: Optional[float] = 24 * 3600,
|
|
1653
|
+
progress_callback: Optional[callable] = None,
|
|
1654
|
+
engine_preference: str = 'Any',
|
|
1655
|
+
use_gpu: Optional[bool] = None,
|
|
1671
1656
|
) -> Dict[str, Any]:
|
|
1672
1657
|
"""
|
|
1673
1658
|
Create a command and wait for it to complete (synchronous execution)
|
|
@@ -1706,8 +1691,11 @@ class RayfosClient:
|
|
|
1706
1691
|
... client.download_file(project_id, version_id, file_id, f"output/{file_id}")
|
|
1707
1692
|
"""
|
|
1708
1693
|
# Create the command (async)
|
|
1709
|
-
|
|
1710
|
-
|
|
1694
|
+
# local_machine_service_id pins the run to that compute node (it used to be passed, by
|
|
1695
|
+
# position, as the engine preference, which the server rejects).
|
|
1696
|
+
command_status = self.create_command(project_id, version_id, engine_preference=engine_preference,
|
|
1697
|
+
use_gpu=use_gpu, preferred_machine_id=local_machine_service_id)
|
|
1698
|
+
command_id = command_status.get('id') or command_status.get('commandId')
|
|
1711
1699
|
|
|
1712
1700
|
if not command_id:
|
|
1713
1701
|
raise RayfosAPIError("Failed to create command: no command ID returned")
|
|
@@ -1898,46 +1886,7 @@ class RayfosClient:
|
|
|
1898
1886
|
)
|
|
1899
1887
|
return self._handle_response(response)
|
|
1900
1888
|
|
|
1901
|
-
def parse_command_status(self, status_value
|
|
1902
|
-
"""
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
Args:
|
|
1906
|
-
status_value: Integer status code from CommandStatusDto
|
|
1907
|
-
|
|
1908
|
-
Returns:
|
|
1909
|
-
Human-readable status name
|
|
1910
|
-
|
|
1911
|
-
CommandStatus enum values from C# RayfosCommandModels:
|
|
1912
|
-
0 = New
|
|
1913
|
-
1 = Assigned
|
|
1914
|
-
2 = SentToMachine
|
|
1915
|
-
3 = SendToMachineForceCancellation
|
|
1916
|
-
4 = SendToMachineForceDeleteTask
|
|
1917
|
-
5 = RequestedForceCancellationInMachine
|
|
1918
|
-
6 = RequestedForceStopTaskInMachine
|
|
1919
|
-
7 = Cancelled
|
|
1920
|
-
8 = Deleted
|
|
1921
|
-
9 = Running
|
|
1922
|
-
10 = Completed
|
|
1923
|
-
11 = Failed
|
|
1924
|
-
12 = FailedRemovedFromMachine
|
|
1925
|
-
"""
|
|
1926
|
-
status_map = {
|
|
1927
|
-
0: "New",
|
|
1928
|
-
1: "Assigned",
|
|
1929
|
-
2: "SentToMachine",
|
|
1930
|
-
3: "SendToMachineForceCancellation",
|
|
1931
|
-
4: "SendToMachineForceDeleteTask",
|
|
1932
|
-
5: "RequestedForceCancellationInMachine",
|
|
1933
|
-
6: "RequestedForceStopTaskInMachine",
|
|
1934
|
-
7: "Cancelled",
|
|
1935
|
-
8: "Deleted",
|
|
1936
|
-
9: "Running",
|
|
1937
|
-
10: "Completed",
|
|
1938
|
-
11: "Failed",
|
|
1939
|
-
12: "FailedRemovedFromMachine",
|
|
1940
|
-
}
|
|
1941
|
-
|
|
1942
|
-
return status_map.get(status_value, f"Unknown({status_value})")
|
|
1889
|
+
def parse_command_status(self, status_value) -> str:
|
|
1890
|
+
"""Command state name from an integer (legacy ``commandStatus``) or a name (current ``status``)."""
|
|
1891
|
+
return command_status_name({"status": status_value})
|
|
1943
1892
|
|