snakemake-interface-software-deployment-plugins 0.7.2__tar.gz → 0.7.4__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.
- {snakemake_interface_software_deployment_plugins-0.7.2 → snakemake_interface_software_deployment_plugins-0.7.4}/PKG-INFO +1 -1
- {snakemake_interface_software_deployment_plugins-0.7.2 → snakemake_interface_software_deployment_plugins-0.7.4}/pyproject.toml +1 -1
- {snakemake_interface_software_deployment_plugins-0.7.2 → snakemake_interface_software_deployment_plugins-0.7.4}/snakemake_interface_software_deployment_plugins/__init__.py +42 -16
- {snakemake_interface_software_deployment_plugins-0.7.2 → snakemake_interface_software_deployment_plugins-0.7.4}/snakemake_interface_software_deployment_plugins/tests.py +16 -6
- {snakemake_interface_software_deployment_plugins-0.7.2 → snakemake_interface_software_deployment_plugins-0.7.4}/LICENSE +0 -0
- {snakemake_interface_software_deployment_plugins-0.7.2 → snakemake_interface_software_deployment_plugins-0.7.4}/README.md +0 -0
- {snakemake_interface_software_deployment_plugins-0.7.2 → snakemake_interface_software_deployment_plugins-0.7.4}/snakemake_interface_software_deployment_plugins/_common.py +0 -0
- {snakemake_interface_software_deployment_plugins-0.7.2 → snakemake_interface_software_deployment_plugins-0.7.4}/snakemake_interface_software_deployment_plugins/registry/__init__.py +0 -0
- {snakemake_interface_software_deployment_plugins-0.7.2 → snakemake_interface_software_deployment_plugins-0.7.4}/snakemake_interface_software_deployment_plugins/registry/plugin.py +0 -0
- {snakemake_interface_software_deployment_plugins-0.7.2 → snakemake_interface_software_deployment_plugins-0.7.4}/snakemake_interface_software_deployment_plugins/settings.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: snakemake-interface-software-deployment-plugins
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.4
|
|
4
4
|
Summary: This package provides a stable interface for interactions between Snakemake and its software deployment plugins.
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: Johannes Köster
|
|
@@ -5,7 +5,7 @@ license = "MIT"
|
|
|
5
5
|
name = "snakemake-interface-software-deployment-plugins"
|
|
6
6
|
packages = [{include = "snakemake_interface_software_deployment_plugins"}]
|
|
7
7
|
readme = "README.md"
|
|
8
|
-
version = "0.7.
|
|
8
|
+
version = "0.7.4"
|
|
9
9
|
|
|
10
10
|
[tool.poetry.dependencies]
|
|
11
11
|
argparse-dataclass = "^2.0.0"
|
|
@@ -9,7 +9,17 @@ from dataclasses import dataclass, field
|
|
|
9
9
|
import hashlib
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
import shutil
|
|
12
|
-
from typing import
|
|
12
|
+
from typing import (
|
|
13
|
+
Any,
|
|
14
|
+
ClassVar,
|
|
15
|
+
Dict,
|
|
16
|
+
Iterable,
|
|
17
|
+
Optional,
|
|
18
|
+
Self,
|
|
19
|
+
Tuple,
|
|
20
|
+
Type,
|
|
21
|
+
Union,
|
|
22
|
+
)
|
|
13
23
|
import subprocess as sp
|
|
14
24
|
|
|
15
25
|
from snakemake_interface_software_deployment_plugins.settings import (
|
|
@@ -124,7 +134,18 @@ class EnvSpecBase(ABC):
|
|
|
124
134
|
|
|
125
135
|
|
|
126
136
|
class EnvBase(ABC):
|
|
127
|
-
_cache: Dict[Tuple[Type["EnvBase"], Optional["EnvBase"]], Any] = {}
|
|
137
|
+
_cache: ClassVar[Dict[Tuple[Type["EnvBase"], Optional["EnvBase"]], Any]] = {}
|
|
138
|
+
spec: EnvSpecBase
|
|
139
|
+
within: Optional["EnvBase"]
|
|
140
|
+
settings: Optional[SoftwareDeploymentSettingsBase]
|
|
141
|
+
shell_executable: str
|
|
142
|
+
tempdir: Path
|
|
143
|
+
_cache_prefix: Path
|
|
144
|
+
_deployment_prefix: Path
|
|
145
|
+
_pinfile_prefix: Path
|
|
146
|
+
_managed_hash_store: Optional[str] = None
|
|
147
|
+
_managed_deployment_hash_store: Optional[str] = None
|
|
148
|
+
_obj_hash: Optional[int] = None
|
|
128
149
|
|
|
129
150
|
def __init__(
|
|
130
151
|
self,
|
|
@@ -142,9 +163,6 @@ class EnvBase(ABC):
|
|
|
142
163
|
self.settings: Optional[SoftwareDeploymentSettingsBase] = settings
|
|
143
164
|
self.shell_executable: str = shell_executable
|
|
144
165
|
self.tempdir = tempdir
|
|
145
|
-
self._managed_hash_store: Optional[str] = None
|
|
146
|
-
self._managed_deployment_hash_store: Optional[str] = None
|
|
147
|
-
self._obj_hash: Optional[int] = None
|
|
148
166
|
self._deployment_prefix: Path = deployment_prefix
|
|
149
167
|
self._cache_prefix: Path = cache_prefix
|
|
150
168
|
self._pinfile_prefix: Path = pinfile_prefix
|
|
@@ -177,6 +195,21 @@ class EnvBase(ABC):
|
|
|
177
195
|
"""
|
|
178
196
|
...
|
|
179
197
|
|
|
198
|
+
def is_deployable(self) -> bool:
|
|
199
|
+
"""Overwrite this in case the deployability of the environment depends on
|
|
200
|
+
the spec or settings."""
|
|
201
|
+
return isinstance(self, DeployableEnvBase)
|
|
202
|
+
|
|
203
|
+
def is_pinnable(self) -> bool:
|
|
204
|
+
"""Overwrite this in case the pinability of the environment depends on
|
|
205
|
+
the spec or settings."""
|
|
206
|
+
return isinstance(self, PinnableEnvBase)
|
|
207
|
+
|
|
208
|
+
def is_cacheable(self) -> bool:
|
|
209
|
+
"""Overwrite this in case the cacheability of the environment depends on
|
|
210
|
+
the spec or settings."""
|
|
211
|
+
return isinstance(self, CacheableEnvBase)
|
|
212
|
+
|
|
180
213
|
@abstractmethod
|
|
181
214
|
def record_hash(self, hash_object) -> None:
|
|
182
215
|
"""Update given hash object (using hash_object.update()) such that it changes
|
|
@@ -241,7 +274,7 @@ class EnvBase(ABC):
|
|
|
241
274
|
)
|
|
242
275
|
|
|
243
276
|
|
|
244
|
-
class PinnableEnvBase(ABC):
|
|
277
|
+
class PinnableEnvBase(EnvBase, ABC):
|
|
245
278
|
@classmethod
|
|
246
279
|
@abstractmethod
|
|
247
280
|
def pinfile_extension(cls) -> str: ...
|
|
@@ -256,7 +289,6 @@ class PinnableEnvBase(ABC):
|
|
|
256
289
|
|
|
257
290
|
@property
|
|
258
291
|
def pinfile(self) -> Path:
|
|
259
|
-
assert isinstance(self, EnvBase)
|
|
260
292
|
ext = self.pinfile_extension()
|
|
261
293
|
if not ext.startswith("."):
|
|
262
294
|
raise ValueError("pinfile_extension must start with a dot.")
|
|
@@ -265,7 +297,7 @@ class PinnableEnvBase(ABC):
|
|
|
265
297
|
)
|
|
266
298
|
|
|
267
299
|
|
|
268
|
-
class CacheableEnvBase(ABC):
|
|
300
|
+
class CacheableEnvBase(EnvBase, ABC):
|
|
269
301
|
async def get_cache_assets(self) -> Iterable[str]: ...
|
|
270
302
|
|
|
271
303
|
@abstractmethod
|
|
@@ -277,12 +309,10 @@ class CacheableEnvBase(ABC):
|
|
|
277
309
|
|
|
278
310
|
@property
|
|
279
311
|
def cache_path(self) -> Path:
|
|
280
|
-
assert isinstance(self, EnvBase)
|
|
281
312
|
return self._cache_prefix
|
|
282
313
|
|
|
283
314
|
async def remove_cache(self) -> None:
|
|
284
315
|
"""Remove the cached environment assets."""
|
|
285
|
-
assert isinstance(self, EnvBase)
|
|
286
316
|
for asset in await self.get_cache_assets():
|
|
287
317
|
asset_path = self.cache_path / asset
|
|
288
318
|
if asset_path.exists():
|
|
@@ -297,7 +327,7 @@ class CacheableEnvBase(ABC):
|
|
|
297
327
|
)
|
|
298
328
|
|
|
299
329
|
|
|
300
|
-
class DeployableEnvBase(ABC):
|
|
330
|
+
class DeployableEnvBase(EnvBase, ABC):
|
|
301
331
|
@abstractmethod
|
|
302
332
|
def is_deployment_path_portable(self) -> bool:
|
|
303
333
|
"""Return whether the deployment path matters for the environment, i.e.
|
|
@@ -325,7 +355,6 @@ class DeployableEnvBase(ABC):
|
|
|
325
355
|
deployment is senstivive to the path (e.g. in case of conda, which patches
|
|
326
356
|
the RPATH in binaries).
|
|
327
357
|
"""
|
|
328
|
-
assert isinstance(self, EnvBase)
|
|
329
358
|
self.record_hash(hash_object)
|
|
330
359
|
if not self.is_deployment_path_portable():
|
|
331
360
|
hash_object.update(str(self._deployment_prefix).encode())
|
|
@@ -337,24 +366,21 @@ class DeployableEnvBase(ABC):
|
|
|
337
366
|
|
|
338
367
|
def managed_remove(self) -> None:
|
|
339
368
|
"""Remove the deployed environment, handling exceptions."""
|
|
340
|
-
assert isinstance(self, EnvBase)
|
|
341
369
|
try:
|
|
342
370
|
self.remove()
|
|
343
371
|
except Exception as e:
|
|
344
372
|
raise WorkflowError(f"Removal of {self.spec} failed: {e}")
|
|
345
373
|
|
|
346
374
|
async def managed_deploy(self) -> None:
|
|
347
|
-
assert isinstance(self, EnvBase)
|
|
348
375
|
try:
|
|
349
376
|
await self.deploy()
|
|
350
377
|
except Exception as e:
|
|
351
378
|
raise WorkflowError(f"Deployment of {self.spec} failed: {e}")
|
|
352
379
|
|
|
353
380
|
def deployment_hash(self) -> str:
|
|
354
|
-
assert isinstance(self, EnvBase)
|
|
355
381
|
return self._managed_generic_hash("deployment_hash")
|
|
356
382
|
|
|
357
383
|
@property
|
|
358
384
|
def deployment_path(self) -> Path:
|
|
359
|
-
assert
|
|
385
|
+
assert self._deployment_prefix is not None
|
|
360
386
|
return self._deployment_prefix / self.deployment_hash()
|
|
@@ -90,11 +90,13 @@ class TestSoftwareDeploymentBase(ABC):
|
|
|
90
90
|
cmd = env.managed_decorate_shellcmd(self.get_test_cmd())
|
|
91
91
|
assert sp.run(cmd, shell=True, executable=self.shell_executable).returncode == 0
|
|
92
92
|
|
|
93
|
-
def
|
|
93
|
+
def test_cache(self, tmp_path):
|
|
94
94
|
env = self._get_env(tmp_path)
|
|
95
|
-
if not
|
|
95
|
+
if not env.is_cacheable():
|
|
96
96
|
pytest.skip("Environment either not deployable or not cacheable.")
|
|
97
97
|
|
|
98
|
+
assert isinstance(env, CacheableEnvBase)
|
|
99
|
+
|
|
98
100
|
asyncio.run(env.cache_assets())
|
|
99
101
|
|
|
100
102
|
self._deploy(env, tmp_path)
|
|
@@ -103,9 +105,11 @@ class TestSoftwareDeploymentBase(ABC):
|
|
|
103
105
|
|
|
104
106
|
def test_pin(self, tmp_path):
|
|
105
107
|
env = self._get_env(tmp_path)
|
|
106
|
-
if not
|
|
108
|
+
if not env.is_pinnable():
|
|
107
109
|
pytest.skip("Environment is not pinnable.")
|
|
108
110
|
|
|
111
|
+
assert isinstance(env, PinnableEnvBase)
|
|
112
|
+
|
|
109
113
|
asyncio.run(env.pin())
|
|
110
114
|
assert env.pinfile.exists()
|
|
111
115
|
print("Pinfile content:", env.pinfile.read_text(), sep="\n")
|
|
@@ -125,10 +129,15 @@ class TestSoftwareDeploymentBase(ABC):
|
|
|
125
129
|
|
|
126
130
|
def test_source_path_attributes(self):
|
|
127
131
|
spec = self.get_env_spec()
|
|
132
|
+
|
|
133
|
+
def is_source_file_or_none(attr: str) -> bool:
|
|
134
|
+
val = getattr(spec, attr)
|
|
135
|
+
return val is None or isinstance(val, EnvSpecSourceFile)
|
|
136
|
+
|
|
128
137
|
assert all(
|
|
129
138
|
isinstance(attr, str)
|
|
130
139
|
and hasattr(spec, attr)
|
|
131
|
-
and
|
|
140
|
+
and is_source_file_or_none(attr)
|
|
132
141
|
for attr in spec.source_path_attributes()
|
|
133
142
|
), "bug in plugin: all source path attributes must be of type EnvSpecSourceFile"
|
|
134
143
|
|
|
@@ -164,9 +173,10 @@ class TestSoftwareDeploymentBase(ABC):
|
|
|
164
173
|
pinfile_prefix=pinfile_prefix,
|
|
165
174
|
)
|
|
166
175
|
|
|
167
|
-
def _deploy(self, env:
|
|
168
|
-
if not
|
|
176
|
+
def _deploy(self, env: EnvBase, tmp_path):
|
|
177
|
+
if not env.is_deployable():
|
|
169
178
|
pytest.skip("Environment is not deployable.")
|
|
170
179
|
|
|
180
|
+
assert isinstance(env, DeployableEnvBase)
|
|
171
181
|
asyncio.run(env.deploy())
|
|
172
182
|
assert any((tmp_path / "deployments").iterdir())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|