splime 0.1.4__tar.gz → 0.2.0__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 (85) hide show
  1. {splime-0.1.4/src/splime.egg-info → splime-0.2.0}/PKG-INFO +3 -2
  2. {splime-0.1.4 → splime-0.2.0}/README.md +2 -1
  3. {splime-0.1.4 → splime-0.2.0}/pyproject.toml +1 -1
  4. {splime-0.1.4 → splime-0.2.0}/src/spl/__init__.py +2 -2
  5. splime-0.1.4/src/spl/client.py → splime-0.2.0/src/spl/_client.py +53 -278
  6. splime-0.2.0/src/spl/_deprecate.py +36 -0
  7. splime-0.2.0/src/spl/client.py +65 -0
  8. splime-0.2.0/src/spl/core/common.py +46 -0
  9. {splime-0.1.4 → splime-0.2.0}/src/spl/core/entities/node_remote.py +29 -5
  10. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/cli.py +43 -2
  11. splime-0.2.0/src/spl/daemon/doctor.py +365 -0
  12. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/repositories/object.py +6 -2
  13. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/routes/runs.py +6 -1
  14. splime-0.2.0/src/spl/daemon/run_progress.py +107 -0
  15. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/server.py +41 -3
  16. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/worker.py +1 -1
  17. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon_client.py +186 -16
  18. {splime-0.1.4 → splime-0.2.0/src/splime.egg-info}/PKG-INFO +3 -2
  19. {splime-0.1.4 → splime-0.2.0}/src/splime.egg-info/SOURCES.txt +5 -0
  20. {splime-0.1.4 → splime-0.2.0}/LICENSE +0 -0
  21. {splime-0.1.4 → splime-0.2.0}/NOTICE +0 -0
  22. {splime-0.1.4 → splime-0.2.0}/setup.cfg +0 -0
  23. {splime-0.1.4 → splime-0.2.0}/src/spl/core/__init__.py +0 -0
  24. /splime-0.1.4/src/spl/core/common.py → /splime-0.2.0/src/spl/core/_common.py +0 -0
  25. {splime-0.1.4 → splime-0.2.0}/src/spl/core/entities/__init__.py +0 -0
  26. {splime-0.1.4 → splime-0.2.0}/src/spl/core/entities/adapter.py +0 -0
  27. {splime-0.1.4 → splime-0.2.0}/src/spl/core/entities/artifact.py +0 -0
  28. {splime-0.1.4 → splime-0.2.0}/src/spl/core/entities/control.py +0 -0
  29. {splime-0.1.4 → splime-0.2.0}/src/spl/core/entities/distribution.py +0 -0
  30. {splime-0.1.4 → splime-0.2.0}/src/spl/core/entities/function.py +0 -0
  31. {splime-0.1.4 → splime-0.2.0}/src/spl/core/entities/local_function.py +0 -0
  32. {splime-0.1.4 → splime-0.2.0}/src/spl/core/entities/misc.py +0 -0
  33. {splime-0.1.4 → splime-0.2.0}/src/spl/core/entities/module.py +0 -0
  34. {splime-0.1.4 → splime-0.2.0}/src/spl/core/entities/node.py +0 -0
  35. {splime-0.1.4 → splime-0.2.0}/src/spl/core/entities/node_function.py +0 -0
  36. {splime-0.1.4 → splime-0.2.0}/src/spl/core/entities/pipeline.py +0 -0
  37. {splime-0.1.4 → splime-0.2.0}/src/spl/core/entities/scalar.py +0 -0
  38. {splime-0.1.4 → splime-0.2.0}/src/spl/core/ir/__init__.py +0 -0
  39. {splime-0.1.4 → splime-0.2.0}/src/spl/core/ir/common.py +0 -0
  40. {splime-0.1.4 → splime-0.2.0}/src/spl/core/ir/parse.py +0 -0
  41. {splime-0.1.4 → splime-0.2.0}/src/spl/core/ir/unparse.py +0 -0
  42. {splime-0.1.4 → splime-0.2.0}/src/spl/core/ir/utils.py +0 -0
  43. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/__init__.py +0 -0
  44. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/__main__.py +0 -0
  45. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/canonical.py +0 -0
  46. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/client.py +0 -0
  47. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/docker_environment.py +0 -0
  48. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/docker_pool.py +0 -0
  49. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/environment.py +0 -0
  50. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/environment_base.py +0 -0
  51. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/heartbeat_service.py +0 -0
  52. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/metadata.py +0 -0
  53. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/remote_client.py +0 -0
  54. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/repositories/__init__.py +0 -0
  55. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/repositories/env.py +0 -0
  56. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/repositories/library.py +0 -0
  57. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/repositories/run.py +0 -0
  58. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/repositories/server_connection.py +0 -0
  59. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/repositories/sync_event.py +0 -0
  60. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/routes/__init__.py +0 -0
  61. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/routes/_helpers.py +0 -0
  62. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/routes/artifacts.py +0 -0
  63. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/routes/diagnostics.py +0 -0
  64. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/routes/envs.py +0 -0
  65. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/routes/libraries.py +0 -0
  66. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/routes/objects.py +0 -0
  67. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/routes/remote.py +0 -0
  68. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/routes/server_connections.py +0 -0
  69. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/runtime_backend.py +0 -0
  70. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/runtime_config.py +0 -0
  71. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/runtime_dependencies.py +0 -0
  72. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/secret_store.py +0 -0
  73. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/server_connection.py +0 -0
  74. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/services/__init__.py +0 -0
  75. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/services/sync.py +0 -0
  76. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/signature.py +0 -0
  77. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/storage_base.py +0 -0
  78. {splime-0.1.4 → splime-0.2.0}/src/spl/daemon/store.py +0 -0
  79. {splime-0.1.4 → splime-0.2.0}/src/spl/pipeline_widget.py +0 -0
  80. {splime-0.1.4 → splime-0.2.0}/src/spl/py.typed +0 -0
  81. {splime-0.1.4 → splime-0.2.0}/src/spl/server_client.py +0 -0
  82. {splime-0.1.4 → splime-0.2.0}/src/splime.egg-info/dependency_links.txt +0 -0
  83. {splime-0.1.4 → splime-0.2.0}/src/splime.egg-info/entry_points.txt +0 -0
  84. {splime-0.1.4 → splime-0.2.0}/src/splime.egg-info/requires.txt +0 -0
  85. {splime-0.1.4 → splime-0.2.0}/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.1.4
3
+ Version: 0.2.0
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
@@ -105,7 +105,8 @@ client.publish(daily_total, name="daily_total")
105
105
 
106
106
  result = client.call("daily_total", kwargs={"date": "2026-06-08"})
107
107
  print(result.mode) # "local"
108
- print(result.output) # 42.0 (unwrapped value; result.value keeps the raw port dict)
108
+ print(result.output) # 42.0 (.output always yields the plain value;
109
+ # for pipelines .value keeps the raw port dict)
109
110
  ```
110
111
 
111
112
  That is the whole loop: define a function, `publish` it as a versioned node, then `call`
@@ -53,7 +53,8 @@ client.publish(daily_total, name="daily_total")
53
53
 
54
54
  result = client.call("daily_total", kwargs={"date": "2026-06-08"})
55
55
  print(result.mode) # "local"
56
- print(result.output) # 42.0 (unwrapped value; result.value keeps the raw port dict)
56
+ print(result.output) # 42.0 (.output always yields the plain value;
57
+ # for pipelines .value keeps the raw port dict)
57
58
  ```
58
59
 
59
60
  That is the whole loop: define a function, `publish` it as a versioned node, then `call`
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "splime"
7
- version = "0.1.4"
7
+ version = "0.2.0"
8
8
 
9
9
  description = "Reuse Python functions across projects without rewriting or redeploying them."
10
10
  readme = "README.md"
@@ -2,9 +2,9 @@ from __future__ import annotations
2
2
 
3
3
  __path__ = __import__("pkgutil").extend_path(__path__, __name__)
4
4
 
5
- from spl.client import PublishedObject, RemoteResult, RemoteRun, SPLClient
5
+ from spl._client import PublishedObject, RemoteResult, RemoteRun, SPLClient
6
6
  from spl.core import spl_export_to_dir, spl_export_to_file, spl_import_from_file
7
- from spl.core.common import Deployment, lift
7
+ from spl.core._common import Deployment, lift
8
8
  from spl.core.entities.distribution import DDistribution
9
9
  from spl.core.entities.node import DEFAULT_PORT, InputPort, OutputPort
10
10
  from spl.core.entities.node_remote import NodeRemote
@@ -4,7 +4,7 @@ This module is the thin "framework side" of the daemon integration. Code that
4
4
  already uses SPL should not need to know about HTTP endpoints, run directories,
5
5
  or worker subprocesses. The intended workflow is:
6
6
 
7
- from spl.client import SPLClient
7
+ from spl import SPLClient
8
8
 
9
9
  client = SPLClient()
10
10
  client.publish(my_function, name="sum", env="default")
@@ -18,7 +18,6 @@ dependencies imported yet.
18
18
 
19
19
  from __future__ import annotations
20
20
 
21
- import warnings
22
21
  from dataclasses import dataclass, field, replace
23
22
  from html import escape
24
23
  from pathlib import Path
@@ -31,12 +30,15 @@ from spl.daemon_client import (
31
30
  DEFAULT_SERVER_URL,
32
31
  Client,
33
32
  ClientError,
33
+ RunProgressPrinter,
34
+ RunStateCallback,
34
35
  )
35
36
  from spl.server_client import SPLServerClient
36
37
 
37
38
  OfflinePolicy = Literal["queue", "wait", "fail_fast"]
38
39
  ObjectScope = Literal["auto", "local", "server", "all"]
39
40
  RunSource = Literal["auto", "local"]
41
+ ProgressOption = bool | RunStateCallback
40
42
  _NO_SERVER_CONNECTION_MESSAGE = "active server connection is not found"
41
43
 
42
44
 
@@ -54,15 +56,18 @@ def _is_missing_server_connection(exc: Exception) -> bool:
54
56
  )
55
57
 
56
58
 
57
- def _warn_deprecated(old: str, new: str) -> None:
58
- """Emit the standard 0.2.0 deprecation warning (see docs/migration-0.2.0.md)."""
59
+ def _progress_callback(progress: ProgressOption) -> RunStateCallback | None:
60
+ """Turn a ``progress`` option into a wait-loop state callback.
59
61
 
60
- warnings.warn(
61
- f"{old} is deprecated and will be removed in splime 0.2.0; "
62
- f"use {new} instead.",
63
- DeprecationWarning,
64
- stacklevel=3,
65
- )
62
+ ``True`` builds a fresh stderr printer (fresh throttling state per wait),
63
+ ``False`` disables feedback, and a callable is passed through as-is.
64
+ """
65
+
66
+ if callable(progress):
67
+ return progress
68
+ if progress:
69
+ return RunProgressPrinter()
70
+ return None
66
71
 
67
72
 
68
73
  @dataclass(frozen=True, repr=False)
@@ -378,20 +383,30 @@ class RemoteRun:
378
383
  *,
379
384
  poll_interval: float = 0.25,
380
385
  timeout_seconds: float | None = None,
386
+ progress: ProgressOption = True,
381
387
  ) -> dict[str, Any]:
382
- """Wait until the run succeeds or fails, then return final state."""
388
+ """Wait until the run succeeds or fails, then return final state.
389
+
390
+ ``progress`` controls feedback during slow phases (first-run
391
+ environment builds, queued server-side runs): ``True`` prints short
392
+ status lines to stderr, ``False`` waits silently, and a callable
393
+ receives every polled state instead.
394
+ """
383
395
 
396
+ on_state = _progress_callback(progress)
384
397
  if self.server_side:
385
398
  self.state = self._client._daemon.wait_remote_run(
386
399
  self.id,
387
400
  poll_interval=poll_interval,
388
401
  timeout_seconds=timeout_seconds,
402
+ on_state=on_state,
389
403
  )
390
404
  else:
391
405
  self.state = self._client._daemon.wait_run(
392
406
  self.id,
393
407
  poll_interval=poll_interval,
394
408
  timeout_seconds=timeout_seconds,
409
+ on_state=on_state,
395
410
  )
396
411
  return self.state
397
412
 
@@ -437,12 +452,17 @@ class RemoteRun:
437
452
  artifacts_dir: str | Path | None = None,
438
453
  poll_interval: float = 0.25,
439
454
  timeout_seconds: float | None = None,
455
+ progress: ProgressOption = True,
440
456
  ) -> RemoteResult:
441
- """Wait for completion, return result, and optionally download artifacts."""
457
+ """Wait for completion, return result, and optionally download artifacts.
458
+
459
+ See :meth:`wait` for the meaning of ``progress``.
460
+ """
442
461
 
443
462
  final_state = self.wait(
444
463
  poll_interval=poll_interval,
445
464
  timeout_seconds=timeout_seconds,
465
+ progress=progress,
446
466
  )
447
467
  if final_state["status"] != "succeeded":
448
468
  error = final_state.get("error") or "run returned no error message"
@@ -754,139 +774,10 @@ class SPLClient:
754
774
  return []
755
775
  return self._daemon.server_libraries(include_accessible=include_accessible)
756
776
 
757
- def create_library(
758
- self,
759
- slug: str,
760
- *,
761
- display_name: str | None = None,
762
- description: str = "",
763
- visibility: str = "private",
764
- default_machine: str | None = None,
765
- execution: dict[str, Any] | None = None,
766
- ) -> dict[str, Any]:
767
- """Deprecated alias of :meth:`library.create` (see docs/migration-0.2.0.md)."""
768
-
769
- _warn_deprecated("SPLClient.create_library()", "SPLClient.library.create()")
770
- return self.library.create(
771
- slug,
772
- display_name=display_name,
773
- description=description,
774
- visibility=visibility,
775
- default_machine=default_machine,
776
- execution=execution,
777
- )
778
-
779
- def get_library(self, ref: str) -> dict[str, Any]:
780
- """Deprecated alias of :meth:`library.get`."""
781
-
782
- _warn_deprecated("SPLClient.get_library()", "SPLClient.library.get()")
783
- return self.library.get(ref)
784
-
785
- def update_library(
786
- self,
787
- ref: str,
788
- *,
789
- display_name: str | None = None,
790
- description: str | None = None,
791
- visibility: str | None = None,
792
- default_machine: str | None = None,
793
- execution: dict[str, Any] | None = None,
794
- ) -> dict[str, Any]:
795
- """Deprecated alias of :meth:`library.update`."""
796
-
797
- _warn_deprecated("SPLClient.update_library()", "SPLClient.library.update()")
798
- return self.library.update(
799
- ref,
800
- display_name=display_name,
801
- description=description,
802
- visibility=visibility,
803
- default_machine=default_machine,
804
- execution=execution,
805
- )
806
-
807
- def delete_library(self, ref: str) -> dict[str, Any]:
808
- """Deprecated alias of :meth:`library.delete`."""
809
-
810
- _warn_deprecated("SPLClient.delete_library()", "SPLClient.library.delete()")
811
- return self.library.delete(ref)
812
-
813
- def grant_library(
814
- self,
815
- ref: str,
816
- grantee: str,
817
- *,
818
- grantee_type: str = "user",
819
- scopes: list[str] | None = None,
820
- ) -> dict[str, Any]:
821
- """Deprecated alias of :meth:`library.grant`."""
822
-
823
- _warn_deprecated("SPLClient.grant_library()", "SPLClient.library.grant()")
824
- return self.library.grant(
825
- ref,
826
- grantee,
827
- grantee_type=grantee_type,
828
- scopes=scopes,
829
- )
830
-
831
- def revoke_library_grant(self, ref: str, grantee: str) -> dict[str, Any]:
832
- """Deprecated alias of :meth:`library.revoke`."""
833
-
834
- _warn_deprecated(
835
- "SPLClient.revoke_library_grant()", "SPLClient.library.revoke()",
836
- )
837
- return self.library.revoke(ref, grantee)
838
-
839
- def add_reference(
840
- self,
841
- into_library: str,
842
- name: str,
843
- *,
844
- owner: str | None = None,
845
- from_library: str = "default",
846
- version: str | int | None = "latest",
847
- alias: str | None = None,
848
- ) -> dict[str, Any]:
849
- """Deprecated alias of :meth:`library.add_reference`."""
850
-
851
- _warn_deprecated(
852
- "SPLClient.add_reference()", "SPLClient.library.add_reference()",
853
- )
854
- return self.library.add_reference(
855
- into_library,
856
- name,
857
- owner=owner,
858
- from_library=from_library,
859
- version=version,
860
- alias=alias,
861
- )
862
-
863
- def copy_object(
864
- self,
865
- name: str,
866
- *,
867
- into_library: str,
868
- from_owner: str | None = None,
869
- from_library: str = "default",
870
- version: str | int | None = "latest",
871
- new_name: str | None = None,
872
- ) -> dict[str, Any]:
873
- """Deprecated alias of :meth:`library.copy_object`."""
874
-
875
- _warn_deprecated("SPLClient.copy_object()", "SPLClient.library.copy_object()")
876
- return self.library.copy_object(
877
- name,
878
- into_library=into_library,
879
- from_owner=from_owner,
880
- from_library=from_library,
881
- version=version,
882
- new_name=new_name,
883
- )
884
-
885
- def remove_entry(self, library: str, name: str) -> dict[str, Any]:
886
- """Deprecated alias of :meth:`library.remove_entry`."""
887
-
888
- _warn_deprecated("SPLClient.remove_entry()", "SPLClient.library.remove_entry()")
889
- return self.library.remove_entry(library, name)
777
+ # The flat library aliases (create_library, get_library, update_library,
778
+ # delete_library, grant_library, revoke_library_grant, add_reference,
779
+ # copy_object, remove_entry) warned through 0.1.4/0.1.5 and were removed
780
+ # in 0.2.0 — use ``client.library.*`` (docs/migration-0.2.0.md).
890
781
 
891
782
  def register_env(self, name: str = "default", python: str | None = None) -> dict[str, Any]:
892
783
  """Register a Python executable as a daemon environment.
@@ -1019,41 +910,8 @@ class SPLClient:
1019
910
  raw=record,
1020
911
  )
1021
912
 
1022
- def local_objects(self, *, compact: bool = False) -> list[dict[str, Any]]:
1023
- """Deprecated alias of :meth:`objects` with ``scope='local'``."""
1024
-
1025
- _warn_deprecated("SPLClient.local_objects()", "SPLClient.objects(scope='local')")
1026
- return self._object_records(self.objects(scope="local", compact=compact))
1027
-
1028
- def server_objects(
1029
- self,
1030
- *,
1031
- owner: str | None = None,
1032
- library: str | None = None,
1033
- compact: bool = False,
1034
- ) -> list[dict[str, Any]]:
1035
- """Deprecated alias of :meth:`objects` with ``scope='server'``."""
1036
-
1037
- _warn_deprecated(
1038
- "SPLClient.server_objects()", "SPLClient.objects(scope='server')",
1039
- )
1040
- return list(
1041
- self.objects(
1042
- scope="server",
1043
- owner=owner,
1044
- library=library,
1045
- compact=compact,
1046
- )
1047
- )
1048
-
1049
- @staticmethod
1050
- def _object_records(records: dict[str, Any] | list[dict[str, Any]]) -> list[dict[str, Any]]:
1051
- if isinstance(records, list):
1052
- return list(records)
1053
- return [
1054
- dict(record) if isinstance(record, dict) else {"name": name, "value": record}
1055
- for name, record in records.items()
1056
- ]
913
+ # ``local_objects()``/``server_objects()`` warned through 0.1.4/0.1.5 and
914
+ # were removed in 0.2.0 — use ``objects(scope='local'/'server')``.
1057
915
 
1058
916
  @overload
1059
917
  def objects(
@@ -1335,7 +1193,7 @@ class SPLClient:
1335
1193
  pipeline = pipeline.name
1336
1194
 
1337
1195
  if isinstance(pipeline, NodeRemote):
1338
- if version is not None and pipeline.version not in {"", "latest", "current", "TODO"}:
1196
+ if version is not None and pipeline.version not in {"", "latest", "current"}:
1339
1197
  raise ValueError("pass the version either on NodeRemote or draw_pipeline(...), not both")
1340
1198
  response = self._remote_decomposition_response(pipeline, version=version)
1341
1199
  decomposition = response["decomposition"]
@@ -1557,11 +1415,8 @@ class SPLClient:
1557
1415
  )
1558
1416
  return RemoteRun(self, state, server_side=remote)
1559
1417
 
1560
- def start(self, name: str, **kwargs: Any) -> RemoteRun:
1561
- """Deprecated alias of :meth:`submit` (same signature and behavior)."""
1562
-
1563
- _warn_deprecated("SPLClient.start()", "SPLClient.submit()")
1564
- return self._start_run(name, **kwargs)
1418
+ # ``start()`` warned through 0.1.4/0.1.5 and was removed in 0.2.0 — use
1419
+ # :meth:`submit` (same signature and behavior).
1565
1420
 
1566
1421
  def submit(
1567
1422
  self,
@@ -1599,38 +1454,8 @@ class SPLClient:
1599
1454
  source=source,
1600
1455
  )
1601
1456
 
1602
- def queue(
1603
- self,
1604
- name: str,
1605
- *,
1606
- args: list[Any] | None = None,
1607
- kwargs: dict[str, Any] | None = None,
1608
- output: str | None = None,
1609
- timeout_seconds: float | None = None,
1610
- target_machine: str,
1611
- owner: str | None = None,
1612
- library: str | None = None,
1613
- function: str | None = None,
1614
- source: RunSource = "auto",
1615
- ) -> RemoteRun:
1616
- """Deprecated alias of :meth:`submit` with ``offline_policy='queue'``."""
1617
-
1618
- _warn_deprecated(
1619
- "SPLClient.queue()", "SPLClient.submit(..., offline_policy='queue')",
1620
- )
1621
- return self._start_run(
1622
- name,
1623
- args=args,
1624
- kwargs=kwargs,
1625
- output=output,
1626
- timeout_seconds=timeout_seconds,
1627
- target_machine=target_machine,
1628
- owner=owner,
1629
- library=library,
1630
- function=function,
1631
- offline_policy="queue",
1632
- source=source,
1633
- )
1457
+ # ``queue()`` warned through 0.1.4/0.1.5 and was removed in 0.2.0 — use
1458
+ # ``submit(..., offline_policy='queue')``.
1634
1459
 
1635
1460
  def call(
1636
1461
  self,
@@ -1647,6 +1472,7 @@ class SPLClient:
1647
1472
  offline_policy: OfflinePolicy | None = None,
1648
1473
  function: str | None = None,
1649
1474
  source: RunSource = "auto",
1475
+ progress: ProgressOption = True,
1650
1476
  ) -> RemoteResult:
1651
1477
  """Run an object, wait for completion, and return result/artifacts.
1652
1478
 
@@ -1654,6 +1480,11 @@ class SPLClient:
1654
1480
  call. Passing ``target_machine``, ``owner``, or ``library`` makes it a
1655
1481
  server-side remote run through the daemon. The returned
1656
1482
  ``RemoteResult.mode`` is therefore either ``"local"`` or ``"server"``.
1483
+
1484
+ While waiting, slow phases (a first-run environment build, a queued
1485
+ server-side run) print short progress lines to stderr. Pass
1486
+ ``progress=False`` to wait silently, or a callable to receive every
1487
+ polled run state instead.
1657
1488
  """
1658
1489
 
1659
1490
  run = self._start_run(
@@ -1672,6 +1503,7 @@ class SPLClient:
1672
1503
  return run.collect(
1673
1504
  artifacts_dir=artifacts_dir,
1674
1505
  timeout_seconds=timeout_seconds,
1506
+ progress=progress,
1675
1507
  )
1676
1508
 
1677
1509
  def _run_node_value(
@@ -1691,66 +1523,9 @@ class SPLClient:
1691
1523
  )
1692
1524
  return response.get("value")
1693
1525
 
1694
- def run_node(
1695
- self,
1696
- node: Any,
1697
- kwargs: dict[str, Any],
1698
- *,
1699
- timeout_seconds: float | None = None,
1700
- ) -> Any:
1701
- """Deprecated: run a ``NodeRemote`` directly.
1702
-
1703
- Wire the node into a pipeline instead:
1704
- ``Deployment(client, lift(node)...).run(...)``.
1705
- """
1706
-
1707
- _warn_deprecated(
1708
- "SPLClient.run_node()",
1709
- "Deployment(client, pipeline).run(...) with NodeRemote.locate(...)",
1710
- )
1711
- return self._run_node_value(node, kwargs, timeout_seconds=timeout_seconds)
1712
-
1713
- def run_node_result(
1714
- self,
1715
- node: Any,
1716
- *,
1717
- kwargs: dict[str, Any] | None = None,
1718
- timeout_seconds: float | None = None,
1719
- ) -> RemoteResult:
1720
- """Deprecated: run a ``NodeRemote`` and return metadata plus the value.
1721
-
1722
- Wire the node into a pipeline instead:
1723
- ``Deployment(client, pipeline).run(...)``.
1724
- """
1725
-
1726
- _warn_deprecated(
1727
- "SPLClient.run_node_result()",
1728
- "Deployment(client, pipeline).run(...) with NodeRemote.locate(...)",
1729
- )
1730
- payload = self._remote_node_payload(node)
1731
- response = self._daemon.run_remote_node(
1732
- payload,
1733
- kwargs=kwargs or {},
1734
- timeout_seconds=timeout_seconds,
1735
- )
1736
- value = response.get("value")
1737
- raw_payload = response.get("payload")
1738
- result_payload = dict(raw_payload) if isinstance(raw_payload, dict) else {}
1739
- result_payload["result"] = value
1740
- result_payload.setdefault("artifacts", response.get("artifacts") or {})
1741
-
1742
- run = response.get("run")
1743
- if not isinstance(run, dict):
1744
- run = {
1745
- "id": response.get("run_id"),
1746
- "status": response.get("status") or "succeeded",
1747
- }
1748
- return RemoteResult(
1749
- run=run,
1750
- payload=result_payload,
1751
- mode="server",
1752
- downloaded_artifacts={},
1753
- )
1526
+ # ``run_node()``/``run_node_result()`` warned through 0.1.4/0.1.5 and were
1527
+ # removed in 0.2.0 — wire the node into a pipeline instead:
1528
+ # ``Deployment(client, lift(NodeRemote.locate(...))...).run(...)``.
1754
1529
 
1755
1530
  def _is_node_remote(self, value: Any) -> bool:
1756
1531
  try:
@@ -0,0 +1,36 @@
1
+ """Uniform deprecation warnings for the 0.2.0 API cleanup (WP-07).
2
+
3
+ Central helper so every deprecated path speaks with one voice and tests can
4
+ match one message shape. See ``docs/migration-0.2.0.md`` for the old → new
5
+ table and the removal schedule.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import warnings
11
+
12
+
13
+ def warn_deprecated(old: str, new: str, *, stacklevel: int = 3) -> None:
14
+ """Emit a uniform ``DeprecationWarning`` pointing callers to ``new``.
15
+
16
+ ``stacklevel=3`` attributes the warning to the caller of the deprecated
17
+ API (helper frame + deprecated function frame are skipped).
18
+ """
19
+
20
+ warnings.warn(
21
+ f"{old} is deprecated; use {new} instead.",
22
+ DeprecationWarning,
23
+ stacklevel=stacklevel,
24
+ )
25
+
26
+
27
+ def warn_deprecated_import(old_module: str, new_module: str) -> None:
28
+ """Emit a ``DeprecationWarning`` from a legacy import-location shim."""
29
+
30
+ warnings.warn(
31
+ f"importing from {old_module} is deprecated; "
32
+ f"import from {new_module} instead. "
33
+ f"The {old_module} shim will be removed in 0.3.0.",
34
+ DeprecationWarning,
35
+ stacklevel=3,
36
+ )
@@ -0,0 +1,65 @@
1
+ """Deprecated import location — the implementation lives in ``spl._client``.
2
+
3
+ ``from spl.client import SPLClient`` keeps working through 0.2.x, but the
4
+ canonical spelling is ``from spl import SPLClient``. This shim warns once on
5
+ import and will be removed in 0.3.0 (see ``docs/migration-0.2.0.md``).
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from typing import Any
11
+
12
+ from spl import _client as _impl
13
+ from spl._client import ( # noqa: F401 - legacy re-exports, kept intentionally.
14
+ ObjectCatalog as ObjectCatalog,
15
+ ObjectList as ObjectList,
16
+ ObjectScope as ObjectScope,
17
+ ObjectTable as ObjectTable,
18
+ OfflinePolicy as OfflinePolicy,
19
+ ProgressOption as ProgressOption,
20
+ PublishedObject as PublishedObject,
21
+ RemoteResult as RemoteResult,
22
+ RemoteRun as RemoteRun,
23
+ RunSource as RunSource,
24
+ SPLClient as SPLClient,
25
+ build_runtime_config as build_runtime_config,
26
+ export_object_to_yaml as export_object_to_yaml,
27
+ export_objects_to_yaml as export_objects_to_yaml,
28
+ prepare_export_object as prepare_export_object,
29
+ read_yaml_input as read_yaml_input,
30
+ )
31
+ from spl._deprecate import warn_deprecated_import
32
+
33
+ # Keep ``from spl.client import *`` scoped to the curated legacy surface
34
+ # (without ``__all__`` the star-import would also leak ``Any`` and
35
+ # ``warn_deprecated_import`` into the caller's namespace).
36
+ __all__ = [
37
+ "ObjectCatalog",
38
+ "ObjectList",
39
+ "ObjectScope",
40
+ "ObjectTable",
41
+ "OfflinePolicy",
42
+ "ProgressOption",
43
+ "PublishedObject",
44
+ "RemoteResult",
45
+ "RemoteRun",
46
+ "RunSource",
47
+ "SPLClient",
48
+ "build_runtime_config",
49
+ "export_object_to_yaml",
50
+ "export_objects_to_yaml",
51
+ "prepare_export_object",
52
+ "read_yaml_input",
53
+ ]
54
+
55
+ warn_deprecated_import("spl.client", "spl")
56
+
57
+
58
+ def __getattr__(name: str) -> Any:
59
+ """Delegate remaining lookups so legacy code keeps working unchanged."""
60
+
61
+ return getattr(_impl, name)
62
+
63
+
64
+ def __dir__() -> list[str]:
65
+ return sorted(set(globals()) | set(dir(_impl)))
@@ -0,0 +1,46 @@
1
+ """Deprecated import location — the implementation lives in ``spl.core._common``.
2
+
3
+ ``from spl.core.common import Deployment, lift`` keeps working through 0.2.x,
4
+ but the canonical spelling is ``from spl import Deployment, lift``. This shim
5
+ warns once on import and will be removed in 0.3.0 (see
6
+ ``docs/migration-0.2.0.md``).
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from typing import Any
12
+
13
+ from spl._deprecate import warn_deprecated_import
14
+ from spl.core import _common as _impl
15
+ from spl.core._common import ( # noqa: F401 - legacy re-exports, kept intentionally.
16
+ Deployment as Deployment,
17
+ PipelineBuilder as PipelineBuilder,
18
+ Run as Run,
19
+ decode as decode,
20
+ encode as encode,
21
+ lift as lift,
22
+ )
23
+
24
+ # Keep ``from spl.core.common import *`` scoped to the curated legacy surface
25
+ # (without ``__all__`` the star-import would also leak ``Any`` and
26
+ # ``warn_deprecated_import`` into the caller's namespace).
27
+ __all__ = [
28
+ "Deployment",
29
+ "PipelineBuilder",
30
+ "Run",
31
+ "decode",
32
+ "encode",
33
+ "lift",
34
+ ]
35
+
36
+ warn_deprecated_import("spl.core.common", "spl")
37
+
38
+
39
+ def __getattr__(name: str) -> Any:
40
+ """Delegate remaining lookups so legacy code keeps working unchanged."""
41
+
42
+ return getattr(_impl, name)
43
+
44
+
45
+ def __dir__() -> list[str]:
46
+ return sorted(set(globals()) | set(dir(_impl)))