splime 0.2.4__tar.gz → 0.2.5__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.
Files changed (87) hide show
  1. {splime-0.2.4/src/splime.egg-info → splime-0.2.5}/PKG-INFO +1 -1
  2. {splime-0.2.4 → splime-0.2.5}/pyproject.toml +1 -1
  3. {splime-0.2.4 → splime-0.2.5}/src/spl/_client.py +128 -63
  4. splime-0.2.5/src/spl/_views.py +685 -0
  5. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/worker.py +180 -16
  6. {splime-0.2.4 → splime-0.2.5}/src/spl/pipeline_widget.py +16 -1
  7. {splime-0.2.4 → splime-0.2.5}/src/spl/server_client.py +140 -45
  8. {splime-0.2.4 → splime-0.2.5/src/splime.egg-info}/PKG-INFO +1 -1
  9. {splime-0.2.4 → splime-0.2.5}/src/splime.egg-info/SOURCES.txt +1 -0
  10. {splime-0.2.4 → splime-0.2.5}/LICENSE +0 -0
  11. {splime-0.2.4 → splime-0.2.5}/NOTICE +0 -0
  12. {splime-0.2.4 → splime-0.2.5}/README.md +0 -0
  13. {splime-0.2.4 → splime-0.2.5}/setup.cfg +0 -0
  14. {splime-0.2.4 → splime-0.2.5}/src/spl/__init__.py +0 -0
  15. {splime-0.2.4 → splime-0.2.5}/src/spl/_deprecate.py +0 -0
  16. {splime-0.2.4 → splime-0.2.5}/src/spl/_http.py +0 -0
  17. {splime-0.2.4 → splime-0.2.5}/src/spl/client.py +0 -0
  18. {splime-0.2.4 → splime-0.2.5}/src/spl/core/__init__.py +0 -0
  19. {splime-0.2.4 → splime-0.2.5}/src/spl/core/_common.py +0 -0
  20. {splime-0.2.4 → splime-0.2.5}/src/spl/core/common.py +0 -0
  21. {splime-0.2.4 → splime-0.2.5}/src/spl/core/entities/__init__.py +0 -0
  22. {splime-0.2.4 → splime-0.2.5}/src/spl/core/entities/adapter.py +0 -0
  23. {splime-0.2.4 → splime-0.2.5}/src/spl/core/entities/artifact.py +0 -0
  24. {splime-0.2.4 → splime-0.2.5}/src/spl/core/entities/control.py +0 -0
  25. {splime-0.2.4 → splime-0.2.5}/src/spl/core/entities/distribution.py +0 -0
  26. {splime-0.2.4 → splime-0.2.5}/src/spl/core/entities/function.py +0 -0
  27. {splime-0.2.4 → splime-0.2.5}/src/spl/core/entities/local_function.py +0 -0
  28. {splime-0.2.4 → splime-0.2.5}/src/spl/core/entities/misc.py +0 -0
  29. {splime-0.2.4 → splime-0.2.5}/src/spl/core/entities/module.py +0 -0
  30. {splime-0.2.4 → splime-0.2.5}/src/spl/core/entities/node.py +0 -0
  31. {splime-0.2.4 → splime-0.2.5}/src/spl/core/entities/node_function.py +0 -0
  32. {splime-0.2.4 → splime-0.2.5}/src/spl/core/entities/node_remote.py +0 -0
  33. {splime-0.2.4 → splime-0.2.5}/src/spl/core/entities/pipeline.py +0 -0
  34. {splime-0.2.4 → splime-0.2.5}/src/spl/core/entities/scalar.py +0 -0
  35. {splime-0.2.4 → splime-0.2.5}/src/spl/core/ir/__init__.py +0 -0
  36. {splime-0.2.4 → splime-0.2.5}/src/spl/core/ir/common.py +0 -0
  37. {splime-0.2.4 → splime-0.2.5}/src/spl/core/ir/parse.py +0 -0
  38. {splime-0.2.4 → splime-0.2.5}/src/spl/core/ir/unparse.py +0 -0
  39. {splime-0.2.4 → splime-0.2.5}/src/spl/core/ir/utils.py +0 -0
  40. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/__init__.py +0 -0
  41. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/__main__.py +0 -0
  42. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/canonical.py +0 -0
  43. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/cli.py +0 -0
  44. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/client.py +0 -0
  45. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/docker_environment.py +0 -0
  46. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/docker_pool.py +0 -0
  47. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/doctor.py +0 -0
  48. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/environment.py +0 -0
  49. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/environment_base.py +0 -0
  50. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/heartbeat_service.py +0 -0
  51. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/metadata.py +0 -0
  52. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/remote_client.py +0 -0
  53. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/repositories/__init__.py +0 -0
  54. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/repositories/env.py +0 -0
  55. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/repositories/library.py +0 -0
  56. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/repositories/object.py +0 -0
  57. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/repositories/run.py +0 -0
  58. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/repositories/server_connection.py +0 -0
  59. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/repositories/sync_event.py +0 -0
  60. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/routes/__init__.py +0 -0
  61. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/routes/_helpers.py +0 -0
  62. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/routes/artifacts.py +0 -0
  63. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/routes/diagnostics.py +0 -0
  64. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/routes/envs.py +0 -0
  65. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/routes/libraries.py +0 -0
  66. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/routes/objects.py +0 -0
  67. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/routes/remote.py +0 -0
  68. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/routes/runs.py +0 -0
  69. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/routes/server_connections.py +0 -0
  70. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/run_progress.py +0 -0
  71. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/runtime_backend.py +0 -0
  72. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/runtime_config.py +0 -0
  73. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/runtime_dependencies.py +0 -0
  74. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/secret_store.py +0 -0
  75. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/server.py +0 -0
  76. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/server_connection.py +0 -0
  77. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/services/__init__.py +0 -0
  78. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/services/sync.py +0 -0
  79. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/signature.py +0 -0
  80. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/storage_base.py +0 -0
  81. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon/store.py +0 -0
  82. {splime-0.2.4 → splime-0.2.5}/src/spl/daemon_client.py +0 -0
  83. {splime-0.2.4 → splime-0.2.5}/src/spl/py.typed +0 -0
  84. {splime-0.2.4 → splime-0.2.5}/src/splime.egg-info/dependency_links.txt +0 -0
  85. {splime-0.2.4 → splime-0.2.5}/src/splime.egg-info/entry_points.txt +0 -0
  86. {splime-0.2.4 → splime-0.2.5}/src/splime.egg-info/requires.txt +0 -0
  87. {splime-0.2.4 → splime-0.2.5}/src/splime.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: splime
3
- Version: 0.2.4
3
+ Version: 0.2.5
4
4
  Summary: Reuse Python functions across projects without rewriting or redeploying them.
5
5
  Author-email: Yastrebov Kirill <yastrebovks@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "splime"
7
- version = "0.2.4"
7
+ version = "0.2.5"
8
8
 
9
9
  description = "Reuse Python functions across projects without rewriting or redeploying them."
10
10
  readme = "README.md"
@@ -23,6 +23,21 @@ from html import escape
23
23
  from pathlib import Path
24
24
  from typing import Any, Literal, cast, overload
25
25
 
26
+ from spl._views import (
27
+ ConnectionStatusView,
28
+ DecompositionView,
29
+ EnvTableView,
30
+ EnvironmentBuildListView,
31
+ HealthView,
32
+ InputListView,
33
+ LibraryListView,
34
+ MachineListView,
35
+ OutputListView,
36
+ RunListView,
37
+ RunRecordView,
38
+ SignatureView,
39
+ wrap_action,
40
+ )
26
41
  from spl.core.entities.node import DEFAULT_PORT
27
42
  from spl.daemon_client import (
28
43
  DEFAULT_DAEMON_HOST,
@@ -353,7 +368,7 @@ class RemoteRun:
353
368
  server_side: bool = False,
354
369
  ):
355
370
  self._client = client
356
- self.state = state
371
+ self.state = RunRecordView(state)
357
372
  self.server_side = server_side
358
373
 
359
374
  @property
@@ -378,9 +393,9 @@ class RemoteRun:
378
393
  """Refresh and return the run state from the daemon."""
379
394
 
380
395
  if self.server_side:
381
- self.state = self._client._daemon.get_remote_run(self.id)
396
+ self.state = RunRecordView(self._client._daemon.get_remote_run(self.id))
382
397
  else:
383
- self.state = self._client._daemon.get_run(self.id)
398
+ self.state = RunRecordView(self._client._daemon.get_run(self.id))
384
399
  return self.state
385
400
 
386
401
  def wait(
@@ -400,18 +415,22 @@ class RemoteRun:
400
415
 
401
416
  on_state = _progress_callback(progress)
402
417
  if self.server_side:
403
- self.state = self._client._daemon.wait_remote_run(
404
- self.id,
405
- poll_interval=poll_interval,
406
- timeout_seconds=timeout_seconds,
407
- on_state=on_state,
418
+ self.state = RunRecordView(
419
+ self._client._daemon.wait_remote_run(
420
+ self.id,
421
+ poll_interval=poll_interval,
422
+ timeout_seconds=timeout_seconds,
423
+ on_state=on_state,
424
+ )
408
425
  )
409
426
  else:
410
- self.state = self._client._daemon.wait_run(
411
- self.id,
412
- poll_interval=poll_interval,
413
- timeout_seconds=timeout_seconds,
414
- on_state=on_state,
427
+ self.state = RunRecordView(
428
+ self._client._daemon.wait_run(
429
+ self.id,
430
+ poll_interval=poll_interval,
431
+ timeout_seconds=timeout_seconds,
432
+ on_state=on_state,
433
+ )
415
434
  )
416
435
  return self.state
417
436
 
@@ -522,13 +541,16 @@ class _LibraryAdmin:
522
541
  payload["default_machine_id"] = default_machine
523
542
  if execution is not None:
524
543
  payload["execution"] = execution
525
- return self._c._daemon.create_server_library(payload)
544
+ return wrap_action(
545
+ self._c._daemon.create_server_library(payload),
546
+ "library created",
547
+ )
526
548
 
527
549
  def get(self, ref: str) -> dict[str, Any]:
528
550
  """Return one central-server library by slug or id."""
529
551
 
530
552
  self._c._require_server_connection("reading a library")
531
- return self._c._daemon.get_server_library(ref)
553
+ return wrap_action(self._c._daemon.get_server_library(ref), "library")
532
554
 
533
555
  def update(
534
556
  self,
@@ -554,7 +576,10 @@ class _LibraryAdmin:
554
576
  payload["default_machine_id"] = default_machine
555
577
  if execution is not None:
556
578
  payload["execution"] = execution
557
- return self._c._daemon.update_server_library(ref, payload)
579
+ return wrap_action(
580
+ self._c._daemon.update_server_library(ref, payload),
581
+ "library updated",
582
+ )
558
583
 
559
584
  def delete(self, ref: str) -> dict[str, Any]:
560
585
  """Raise a clear error because server-side library delete is unsupported."""
@@ -579,13 +604,19 @@ class _LibraryAdmin:
579
604
  }
580
605
  if scopes is not None:
581
606
  payload["scopes"] = scopes
582
- return self._c._daemon.grant_server_library(ref, payload)
607
+ return wrap_action(
608
+ self._c._daemon.grant_server_library(ref, payload),
609
+ "library grant",
610
+ )
583
611
 
584
612
  def revoke(self, ref: str, grantee: str) -> dict[str, Any]:
585
613
  """Revoke a grantee's access to one central-server library."""
586
614
 
587
615
  self._c._require_server_connection("revoking library access")
588
- return self._c._daemon.revoke_server_library_grant(ref, grantee)
616
+ return wrap_action(
617
+ self._c._daemon.revoke_server_library_grant(ref, grantee),
618
+ "library grant revoked",
619
+ )
589
620
 
590
621
  def add_reference(
591
622
  self,
@@ -610,7 +641,10 @@ class _LibraryAdmin:
610
641
  payload["version"] = version
611
642
  if alias is not None:
612
643
  payload["alias"] = alias
613
- return self._c._daemon.add_server_library_reference(into_library, payload)
644
+ return wrap_action(
645
+ self._c._daemon.add_server_library_reference(into_library, payload),
646
+ "library reference",
647
+ )
614
648
 
615
649
  def copy_object(
616
650
  self,
@@ -635,13 +669,19 @@ class _LibraryAdmin:
635
669
  payload["version"] = version
636
670
  if new_name is not None:
637
671
  payload["new_name"] = new_name
638
- return self._c._daemon.copy_server_library_object(into_library, payload)
672
+ return wrap_action(
673
+ self._c._daemon.copy_server_library_object(into_library, payload),
674
+ "library copy",
675
+ )
639
676
 
640
677
  def remove_entry(self, library: str, name: str) -> dict[str, Any]:
641
678
  """Remove an owned object or reference entry from a central-server library."""
642
679
 
643
680
  self._c._require_server_connection("removing a library entry")
644
- return self._c._daemon.remove_server_library_entry(library, name)
681
+ return wrap_action(
682
+ self._c._daemon.remove_server_library_entry(library, name),
683
+ "library entry removed",
684
+ )
645
685
 
646
686
 
647
687
  class SPLClient:
@@ -688,7 +728,7 @@ class SPLClient:
688
728
  def health(self) -> dict[str, Any]:
689
729
  """Check that the local daemon is reachable."""
690
730
 
691
- return self._daemon.health()
731
+ return HealthView(self._daemon.health())
692
732
 
693
733
  def connect_server(
694
734
  self,
@@ -718,7 +758,7 @@ class SPLClient:
718
758
  )
719
759
  self._user_token = user_token
720
760
  self.server_connection = connection
721
- return self.server_connection
761
+ return ConnectionStatusView(self.server_connection)
722
762
 
723
763
  def disconnect_server(self) -> dict[str, Any]:
724
764
  """Gracefully disconnect the local daemon from the central server."""
@@ -760,24 +800,28 @@ class SPLClient:
760
800
  state = self._daemon.server_connection()
761
801
  except Exception as exc:
762
802
  if _is_missing_server_connection(exc):
763
- return {"connected": False, "offline": False, "connection": None}
803
+ return ConnectionStatusView(
804
+ {"connected": False, "offline": False, "connection": None}
805
+ )
764
806
  raise
765
807
  state.setdefault("connected", bool(state.get("server_url")))
766
- return state
808
+ return ConnectionStatusView(state)
767
809
 
768
810
  def machines(self) -> dict[str, Any]:
769
811
  """Return the user's machines, or an empty listing when not connected."""
770
812
 
771
813
  if not self._has_server_connection():
772
- return {"current_machine_id": None, "machines": []}
773
- return self._daemon.server_machines()
814
+ return MachineListView({"current_machine_id": None, "machines": []})
815
+ return MachineListView(self._daemon.server_machines())
774
816
 
775
817
  def libraries(self, *, include_accessible: bool = True) -> list[dict[str, Any]]:
776
818
  """Return visible libraries, or an empty list when not connected."""
777
819
 
778
820
  if not self._has_server_connection():
779
- return []
780
- return self._daemon.server_libraries(include_accessible=include_accessible)
821
+ return LibraryListView([])
822
+ return LibraryListView(
823
+ self._daemon.server_libraries(include_accessible=include_accessible)
824
+ )
781
825
 
782
826
  # The flat library aliases (create_library, get_library, update_library,
783
827
  # delete_library, grant_library, revoke_library_grant, add_reference,
@@ -795,7 +839,7 @@ class SPLClient:
795
839
  client.publish(my_function, env="default")
796
840
  """
797
841
 
798
- return self._daemon.register_env(name, python)
842
+ return wrap_action(self._daemon.register_env(name, python), "env registered")
799
843
 
800
844
  def publish(
801
845
  self,
@@ -1021,7 +1065,10 @@ class SPLClient:
1021
1065
  ) -> dict[str, Any]:
1022
1066
  """Remove one local daemon object without requiring a server connection."""
1023
1067
 
1024
- return self._daemon.forget(name, owner_id=owner, library=library)
1068
+ return wrap_action(
1069
+ self._daemon.forget(name, owner_id=owner, library=library),
1070
+ "object forgotten",
1071
+ )
1025
1072
 
1026
1073
  def remove_local(
1027
1074
  self,
@@ -1044,11 +1091,14 @@ class SPLClient:
1044
1091
  ) -> dict[str, Any]:
1045
1092
  """Remove one local object version without contacting the server."""
1046
1093
 
1047
- return self._daemon.forget_version(
1048
- name,
1049
- version,
1050
- owner_id=owner,
1051
- library=library,
1094
+ return wrap_action(
1095
+ self._daemon.forget_version(
1096
+ name,
1097
+ version,
1098
+ owner_id=owner,
1099
+ library=library,
1100
+ ),
1101
+ "object version forgotten",
1052
1102
  )
1053
1103
 
1054
1104
  def prune_stale_mirrors(
@@ -1059,7 +1109,10 @@ class SPLClient:
1059
1109
  ) -> dict[str, Any]:
1060
1110
  """Remove locally cached server-origin mirror rows."""
1061
1111
 
1062
- return self._daemon.prune_stale_mirrors(owner_id=owner, library=library)
1112
+ return wrap_action(
1113
+ self._daemon.prune_stale_mirrors(owner_id=owner, library=library),
1114
+ "stale mirrors pruned",
1115
+ )
1063
1116
 
1064
1117
  def _has_server_connection(self) -> bool:
1065
1118
  if self.server_connection is not None:
@@ -1104,12 +1157,14 @@ class SPLClient:
1104
1157
  ) -> dict[str, Any]:
1105
1158
  """Return a concise call/read signature for one daemon object."""
1106
1159
 
1107
- return self._daemon.signature(
1108
- name,
1109
- version=version,
1110
- owner_id=owner,
1111
- library=library,
1112
- function=function,
1160
+ return SignatureView(
1161
+ self._daemon.signature(
1162
+ name,
1163
+ version=version,
1164
+ owner_id=owner,
1165
+ library=library,
1166
+ function=function,
1167
+ )
1113
1168
  )
1114
1169
 
1115
1170
  def inputs(
@@ -1123,12 +1178,14 @@ class SPLClient:
1123
1178
  ) -> list[dict[str, Any]]:
1124
1179
  """Return the inputs that can be passed through ``kwargs``."""
1125
1180
 
1126
- return self._daemon.inputs(
1127
- name,
1128
- version=version,
1129
- owner_id=owner,
1130
- library=library,
1131
- function=function,
1181
+ return InputListView(
1182
+ self._daemon.inputs(
1183
+ name,
1184
+ version=version,
1185
+ owner_id=owner,
1186
+ library=library,
1187
+ function=function,
1188
+ )
1132
1189
  )
1133
1190
 
1134
1191
  def outputs(
@@ -1142,12 +1199,14 @@ class SPLClient:
1142
1199
  ) -> list[dict[str, Any]]:
1143
1200
  """Return output selectors and how to read ``result.value``."""
1144
1201
 
1145
- return self._daemon.outputs(
1146
- name,
1147
- version=version,
1148
- owner_id=owner,
1149
- library=library,
1150
- function=function,
1202
+ return OutputListView(
1203
+ self._daemon.outputs(
1204
+ name,
1205
+ version=version,
1206
+ owner_id=owner,
1207
+ library=library,
1208
+ function=function,
1209
+ )
1151
1210
  )
1152
1211
 
1153
1212
  def decomposition(
@@ -1161,17 +1220,20 @@ class SPLClient:
1161
1220
  """Return normalized function/node/link metadata for one object."""
1162
1221
 
1163
1222
  if self._is_node_remote(name):
1164
- return self._remote_decomposition_response(name, version=version)["decomposition"]
1223
+ return DecompositionView(
1224
+ self._remote_decomposition_response(name, version=version)["decomposition"]
1225
+ )
1165
1226
  if owner is not None or library is not None:
1166
- return self._remote_decomposition_response(
1227
+ response = self._remote_decomposition_response(
1167
1228
  {
1168
1229
  "name": str(name),
1169
1230
  "version": version,
1170
1231
  "owner_id": owner,
1171
1232
  "library": library,
1172
1233
  }
1173
- )["decomposition"]
1174
- return self._daemon.decomposition(str(name), version=version)
1234
+ )
1235
+ return DecompositionView(response["decomposition"])
1236
+ return DecompositionView(self._daemon.decomposition(str(name), version=version))
1175
1237
 
1176
1238
  def pipeline_widget(
1177
1239
  self,
@@ -1364,12 +1426,12 @@ class SPLClient:
1364
1426
  def envs(self) -> dict[str, Any]:
1365
1427
  """Return registered daemon environments."""
1366
1428
 
1367
- return self._daemon.list_envs()
1429
+ return EnvTableView(self._daemon.list_envs())
1368
1430
 
1369
1431
  def environment_builds(self) -> list[dict[str, Any]]:
1370
1432
  """Return cached daemon venv builds."""
1371
1433
 
1372
- return self._daemon.list_environment_builds()
1434
+ return EnvironmentBuildListView(self._daemon.list_environment_builds())
1373
1435
 
1374
1436
  def rebuild_environment(
1375
1437
  self,
@@ -1379,12 +1441,15 @@ class SPLClient:
1379
1441
  ) -> dict[str, Any]:
1380
1442
  """Force a cached daemon venv build to be recreated."""
1381
1443
 
1382
- return self._daemon.rebuild_environment_build(spec_hash, wait=wait)
1444
+ return wrap_action(
1445
+ self._daemon.rebuild_environment_build(spec_hash, wait=wait),
1446
+ "environment build",
1447
+ )
1383
1448
 
1384
1449
  def runs(self) -> list[dict[str, Any]]:
1385
1450
  """Return known daemon runs, newest first."""
1386
1451
 
1387
- return self._daemon.list_runs()
1452
+ return RunListView(self._daemon.list_runs())
1388
1453
 
1389
1454
  def _start_run(
1390
1455
  self,