splime 0.2.1__tar.gz → 0.2.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.
Files changed (86) hide show
  1. {splime-0.2.1/src/splime.egg-info → splime-0.2.2}/PKG-INFO +2 -1
  2. {splime-0.2.1 → splime-0.2.2}/pyproject.toml +2 -1
  3. splime-0.2.2/src/spl/_http.py +23 -0
  4. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/environment.py +35 -1
  5. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/remote_client.py +14 -9
  6. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/repositories/object.py +13 -1
  7. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/server.py +7 -2
  8. {splime-0.2.1 → splime-0.2.2}/src/spl/server_client.py +5 -3
  9. {splime-0.2.1 → splime-0.2.2/src/splime.egg-info}/PKG-INFO +2 -1
  10. {splime-0.2.1 → splime-0.2.2}/src/splime.egg-info/SOURCES.txt +1 -0
  11. {splime-0.2.1 → splime-0.2.2}/src/splime.egg-info/requires.txt +1 -0
  12. {splime-0.2.1 → splime-0.2.2}/LICENSE +0 -0
  13. {splime-0.2.1 → splime-0.2.2}/NOTICE +0 -0
  14. {splime-0.2.1 → splime-0.2.2}/README.md +0 -0
  15. {splime-0.2.1 → splime-0.2.2}/setup.cfg +0 -0
  16. {splime-0.2.1 → splime-0.2.2}/src/spl/__init__.py +0 -0
  17. {splime-0.2.1 → splime-0.2.2}/src/spl/_client.py +0 -0
  18. {splime-0.2.1 → splime-0.2.2}/src/spl/_deprecate.py +0 -0
  19. {splime-0.2.1 → splime-0.2.2}/src/spl/client.py +0 -0
  20. {splime-0.2.1 → splime-0.2.2}/src/spl/core/__init__.py +0 -0
  21. {splime-0.2.1 → splime-0.2.2}/src/spl/core/_common.py +0 -0
  22. {splime-0.2.1 → splime-0.2.2}/src/spl/core/common.py +0 -0
  23. {splime-0.2.1 → splime-0.2.2}/src/spl/core/entities/__init__.py +0 -0
  24. {splime-0.2.1 → splime-0.2.2}/src/spl/core/entities/adapter.py +0 -0
  25. {splime-0.2.1 → splime-0.2.2}/src/spl/core/entities/artifact.py +0 -0
  26. {splime-0.2.1 → splime-0.2.2}/src/spl/core/entities/control.py +0 -0
  27. {splime-0.2.1 → splime-0.2.2}/src/spl/core/entities/distribution.py +0 -0
  28. {splime-0.2.1 → splime-0.2.2}/src/spl/core/entities/function.py +0 -0
  29. {splime-0.2.1 → splime-0.2.2}/src/spl/core/entities/local_function.py +0 -0
  30. {splime-0.2.1 → splime-0.2.2}/src/spl/core/entities/misc.py +0 -0
  31. {splime-0.2.1 → splime-0.2.2}/src/spl/core/entities/module.py +0 -0
  32. {splime-0.2.1 → splime-0.2.2}/src/spl/core/entities/node.py +0 -0
  33. {splime-0.2.1 → splime-0.2.2}/src/spl/core/entities/node_function.py +0 -0
  34. {splime-0.2.1 → splime-0.2.2}/src/spl/core/entities/node_remote.py +0 -0
  35. {splime-0.2.1 → splime-0.2.2}/src/spl/core/entities/pipeline.py +0 -0
  36. {splime-0.2.1 → splime-0.2.2}/src/spl/core/entities/scalar.py +0 -0
  37. {splime-0.2.1 → splime-0.2.2}/src/spl/core/ir/__init__.py +0 -0
  38. {splime-0.2.1 → splime-0.2.2}/src/spl/core/ir/common.py +0 -0
  39. {splime-0.2.1 → splime-0.2.2}/src/spl/core/ir/parse.py +0 -0
  40. {splime-0.2.1 → splime-0.2.2}/src/spl/core/ir/unparse.py +0 -0
  41. {splime-0.2.1 → splime-0.2.2}/src/spl/core/ir/utils.py +0 -0
  42. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/__init__.py +0 -0
  43. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/__main__.py +0 -0
  44. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/canonical.py +0 -0
  45. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/cli.py +0 -0
  46. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/client.py +0 -0
  47. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/docker_environment.py +0 -0
  48. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/docker_pool.py +0 -0
  49. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/doctor.py +0 -0
  50. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/environment_base.py +0 -0
  51. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/heartbeat_service.py +0 -0
  52. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/metadata.py +0 -0
  53. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/repositories/__init__.py +0 -0
  54. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/repositories/env.py +0 -0
  55. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/repositories/library.py +0 -0
  56. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/repositories/run.py +0 -0
  57. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/repositories/server_connection.py +0 -0
  58. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/repositories/sync_event.py +0 -0
  59. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/routes/__init__.py +0 -0
  60. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/routes/_helpers.py +0 -0
  61. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/routes/artifacts.py +0 -0
  62. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/routes/diagnostics.py +0 -0
  63. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/routes/envs.py +0 -0
  64. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/routes/libraries.py +0 -0
  65. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/routes/objects.py +0 -0
  66. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/routes/remote.py +0 -0
  67. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/routes/runs.py +0 -0
  68. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/routes/server_connections.py +0 -0
  69. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/run_progress.py +0 -0
  70. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/runtime_backend.py +0 -0
  71. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/runtime_config.py +0 -0
  72. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/runtime_dependencies.py +0 -0
  73. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/secret_store.py +0 -0
  74. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/server_connection.py +0 -0
  75. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/services/__init__.py +0 -0
  76. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/services/sync.py +0 -0
  77. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/signature.py +0 -0
  78. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/storage_base.py +0 -0
  79. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/store.py +0 -0
  80. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon/worker.py +0 -0
  81. {splime-0.2.1 → splime-0.2.2}/src/spl/daemon_client.py +0 -0
  82. {splime-0.2.1 → splime-0.2.2}/src/spl/pipeline_widget.py +0 -0
  83. {splime-0.2.1 → splime-0.2.2}/src/spl/py.typed +0 -0
  84. {splime-0.2.1 → splime-0.2.2}/src/splime.egg-info/dependency_links.txt +0 -0
  85. {splime-0.2.1 → splime-0.2.2}/src/splime.egg-info/entry_points.txt +0 -0
  86. {splime-0.2.1 → splime-0.2.2}/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.1
3
+ Version: 0.2.2
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
@@ -21,6 +21,7 @@ Requires-Python: >=3.13
21
21
  Description-Content-Type: text/markdown
22
22
  License-File: LICENSE
23
23
  License-File: NOTICE
24
+ Requires-Dist: certifi==2026.6.17
24
25
  Requires-Dist: pyyaml==6.0.3
25
26
  Requires-Dist: keyring==25.7.0
26
27
  Requires-Dist: quart==0.20.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "splime"
7
- version = "0.2.1"
7
+ version = "0.2.2"
8
8
 
9
9
  description = "Reuse Python functions across projects without rewriting or redeploying them."
10
10
  readme = "README.md"
@@ -18,6 +18,7 @@ authors = [
18
18
  requires-python = ">= 3.13"
19
19
 
20
20
  dependencies = [
21
+ "certifi==2026.6.17",
21
22
  "pyyaml==6.0.3",
22
23
  "keyring==25.7.0",
23
24
  "quart==0.20.0",
@@ -0,0 +1,23 @@
1
+ """HTTP helpers shared by central-server clients."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import ssl
6
+ from functools import lru_cache
7
+ from typing import Any
8
+ from urllib.request import Request, urlopen
9
+
10
+
11
+ @lru_cache(maxsize=1)
12
+ def verified_https_context() -> ssl.SSLContext:
13
+ """Return a certificate-verifying HTTPS context backed by certifi."""
14
+
15
+ import certifi
16
+
17
+ return ssl.create_default_context(cafile=certifi.where())
18
+
19
+
20
+ def urlopen_verified(request: Request) -> Any:
21
+ """Open a URL request with the bundled certifi CA bundle."""
22
+
23
+ return urlopen(request, context=verified_https_context())
@@ -11,6 +11,7 @@ from __future__ import annotations
11
11
  import os
12
12
  import shutil
13
13
  import subprocess
14
+ import sys
14
15
  from pathlib import Path
15
16
  from typing import Any
16
17
 
@@ -41,7 +42,7 @@ class EnvironmentManager(BaseEnvironmentManager):
41
42
  def build_spec(self, object_record: dict[str, Any]) -> dict[str, Any]:
42
43
  """Build a deterministic venv specification from an object version."""
43
44
 
44
- base_python = str(Path(object_record["env_python"]).expanduser().absolute())
45
+ base_python = self._base_python_for_object(object_record)
45
46
  distributions = self._normalize_distributions(object_record["distributions"])
46
47
  runtime_packages = self._runtime_packages(distributions)
47
48
  python_version = self._python_version(base_python)
@@ -74,6 +75,39 @@ class EnvironmentManager(BaseEnvironmentManager):
74
75
  "force_rebuild": False,
75
76
  }
76
77
 
78
+ def _base_python_for_object(self, object_record: dict[str, Any]) -> str:
79
+ stored_python = Path(str(object_record["env_python"])).expanduser().absolute()
80
+ if stored_python.exists():
81
+ return str(stored_python)
82
+
83
+ env_name = object_record.get("env")
84
+ if env_name:
85
+ try:
86
+ current_env = self.store.get_env(str(env_name))
87
+ except KeyError:
88
+ current_env = None
89
+ if current_env is not None:
90
+ env_python = Path(str(current_env.get("python"))).expanduser().absolute()
91
+ if env_python.exists():
92
+ return str(env_python)
93
+
94
+ if object_record.get("origin") == "server":
95
+ try:
96
+ default_env = self.store.get_env("default")
97
+ except KeyError:
98
+ default_env = None
99
+ if default_env is not None:
100
+ default_python = Path(
101
+ str(default_env.get("python"))
102
+ ).expanduser().absolute()
103
+ if default_python.exists():
104
+ return str(default_python)
105
+ executable = Path(sys.executable).expanduser().absolute()
106
+ if executable.exists():
107
+ return str(executable)
108
+
109
+ return str(stored_python)
110
+
77
111
  def _build_environment(self, spec: dict[str, Any]) -> None:
78
112
  env_dir = Path(spec["venv_path"]).parent
79
113
  venv_path = Path(spec["venv_path"])
@@ -9,7 +9,9 @@ from pathlib import Path
9
9
  from typing import Any
10
10
  from urllib.error import HTTPError, URLError
11
11
  from urllib.parse import quote, urlencode, urlparse
12
- from urllib.request import Request, urlopen
12
+ from urllib.request import Request
13
+
14
+ from spl._http import urlopen_verified, verified_https_context
13
15
 
14
16
  DEFAULT_SERVER_URL = "https://splime.io/api"
15
17
  DEFAULT_HEARTBEAT_INTERVAL_SECONDS = 60.0
@@ -66,7 +68,7 @@ class ServerClient:
66
68
  method=method,
67
69
  )
68
70
  try:
69
- with urlopen(request) as response: # noqa: S310 - configured server URL.
71
+ with urlopen_verified(request) as response:
70
72
  raw = response.read().decode("utf-8")
71
73
  except HTTPError as exc:
72
74
  raw = exc.read().decode("utf-8")
@@ -95,7 +97,7 @@ class ServerClient:
95
97
  def _bytes_request(self, path: str) -> bytes:
96
98
  request = Request(f"{self.base_url}{path}", headers=self._headers())
97
99
  try:
98
- with urlopen(request) as response: # noqa: S310 - configured server URL.
100
+ with urlopen_verified(request) as response:
99
101
  return response.read()
100
102
  except HTTPError as exc:
101
103
  raw = exc.read().decode("utf-8")
@@ -128,15 +130,18 @@ class ServerClient:
128
130
  url = urlparse(self.base_url)
129
131
  if url.scheme not in {"http", "https"}:
130
132
  raise ServerClientError(400, f"unsupported server URL scheme: {url.scheme}")
131
- connection_class = (
132
- http.client.HTTPSConnection
133
- if url.scheme == "https"
134
- else http.client.HTTPConnection
135
- )
136
133
  host = url.hostname
137
134
  if not host:
138
135
  raise ServerClientError(400, f"invalid server URL: {self.base_url}")
139
- connection = connection_class(host, url.port, timeout=300)
136
+ if url.scheme == "https":
137
+ connection = http.client.HTTPSConnection(
138
+ host,
139
+ url.port,
140
+ timeout=300,
141
+ context=verified_https_context(),
142
+ )
143
+ else:
144
+ connection = http.client.HTTPConnection(host, url.port, timeout=300)
140
145
  request_path = f"{url.path.rstrip('/')}{path}"
141
146
  request_headers = {
142
147
  **self._headers(),
@@ -129,8 +129,20 @@ class ObjectRepository(RepositoryBase):
129
129
  (remote_version_id,),
130
130
  ).fetchone()
131
131
  if existing_remote is not None:
132
+ version_id = existing_remote["id"]
133
+ env_python = str(Path(env_record["python"]).expanduser().absolute())
134
+ if origin == "server" and Path(env_python).exists():
135
+ self._conn.execute(
136
+ """
137
+ UPDATE object_versions
138
+ SET env = ?,
139
+ env_python = ?
140
+ WHERE id = ?
141
+ """,
142
+ (env, env_python, version_id),
143
+ )
132
144
  return self.get_object_version(
133
- existing_remote["id"],
145
+ version_id,
134
146
  include_yaml=False,
135
147
  )
136
148
 
@@ -1268,6 +1268,7 @@ class DaemonRuntime:
1268
1268
  include_yaml=False,
1269
1269
  **server_scope,
1270
1270
  )
1271
+ self._ensure_server_object_envs([remote_current])
1271
1272
  remote_object_id = remote_current["id"]
1272
1273
  remote_version_id = remote_current["version_id"]
1273
1274
  existing_current = self.store.get_object_by_remote_version(
@@ -1375,6 +1376,8 @@ class DaemonRuntime:
1375
1376
  def _register_auto_server_env(self, name: str) -> dict[str, Any]:
1376
1377
  try:
1377
1378
  base_python = self.store.get_env("default")["python"]
1379
+ if not Path(str(base_python)).expanduser().exists():
1380
+ raise KeyError("default environment python is missing")
1378
1381
  except KeyError:
1379
1382
  base_python = sys.executable
1380
1383
  return self.store.register_env(name, base_python)
@@ -1420,10 +1423,11 @@ class DaemonRuntime:
1420
1423
 
1421
1424
  def _has_local_env(self, name: str) -> bool:
1422
1425
  try:
1423
- self.store.get_env(name)
1426
+ env = self.store.get_env(name)
1424
1427
  except KeyError:
1425
1428
  return False
1426
- return True
1429
+ python = env.get("python")
1430
+ return bool(python) and Path(str(python)).expanduser().exists()
1427
1431
 
1428
1432
  def sync_once(
1429
1433
  self,
@@ -1583,6 +1587,7 @@ class DaemonRuntime:
1583
1587
  status="fetching_object",
1584
1588
  message="registering object bundle in local daemon",
1585
1589
  )
1590
+ self._ensure_server_object_envs([version])
1586
1591
  object_record = self.register_object(
1587
1592
  local_name,
1588
1593
  version["entrypoint"],
@@ -14,7 +14,9 @@ from pathlib import Path
14
14
  from typing import Any, Literal
15
15
  from urllib.error import HTTPError, URLError
16
16
  from urllib.parse import quote, urlencode
17
- from urllib.request import Request, urlopen
17
+ from urllib.request import Request
18
+
19
+ from spl._http import urlopen_verified
18
20
 
19
21
  DEFAULT_SERVER_URL = "https://splime.io/api"
20
22
  TERMINAL_REMOTE_RUN_STATUSES = {"succeeded", "failed", "cancelled", "stale"}
@@ -208,7 +210,7 @@ class SPLServerClient:
208
210
  method=method,
209
211
  )
210
212
  try:
211
- with urlopen(request) as response: # noqa: S310 - configured server URL.
213
+ with urlopen_verified(request) as response:
212
214
  raw = response.read().decode("utf-8")
213
215
  except HTTPError as exc:
214
216
  raw = exc.read().decode("utf-8")
@@ -232,7 +234,7 @@ class SPLServerClient:
232
234
  def _bytes_request(self, path: str) -> bytes:
233
235
  request = Request(f"{self.base_url}{path}", headers=self._headers())
234
236
  try:
235
- with urlopen(request) as response: # noqa: S310 - configured server URL.
237
+ with urlopen_verified(request) as response:
236
238
  return response.read()
237
239
  except HTTPError as exc:
238
240
  raw = exc.read().decode("utf-8")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: splime
3
- Version: 0.2.1
3
+ Version: 0.2.2
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
@@ -21,6 +21,7 @@ Requires-Python: >=3.13
21
21
  Description-Content-Type: text/markdown
22
22
  License-File: LICENSE
23
23
  License-File: NOTICE
24
+ Requires-Dist: certifi==2026.6.17
24
25
  Requires-Dist: pyyaml==6.0.3
25
26
  Requires-Dist: keyring==25.7.0
26
27
  Requires-Dist: quart==0.20.0
@@ -5,6 +5,7 @@ pyproject.toml
5
5
  src/spl/__init__.py
6
6
  src/spl/_client.py
7
7
  src/spl/_deprecate.py
8
+ src/spl/_http.py
8
9
  src/spl/client.py
9
10
  src/spl/daemon_client.py
10
11
  src/spl/pipeline_widget.py
@@ -1,3 +1,4 @@
1
+ certifi==2026.6.17
1
2
  pyyaml==6.0.3
2
3
  keyring==25.7.0
3
4
  quart==0.20.0
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes