jolt 0.9.342__py3-none-any.whl → 0.9.429__py3-none-any.whl

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.

Potentially problematic release.


This version of jolt might be problematic. Click here for more details.

Files changed (158) hide show
  1. jolt/__init__.py +47 -0
  2. jolt/cache.py +358 -159
  3. jolt/cli.py +71 -104
  4. jolt/config.py +14 -26
  5. jolt/filesystem.py +2 -2
  6. jolt/graph.py +56 -28
  7. jolt/influence.py +67 -2
  8. jolt/loader.py +150 -186
  9. jolt/log.py +12 -2
  10. jolt/manifest.py +0 -46
  11. jolt/options.py +35 -12
  12. jolt/pkgs/abseil.py +42 -0
  13. jolt/pkgs/asio.py +25 -0
  14. jolt/pkgs/autoconf.py +41 -0
  15. jolt/pkgs/automake.py +41 -0
  16. jolt/pkgs/b2.py +31 -0
  17. jolt/pkgs/boost.py +111 -0
  18. jolt/pkgs/boringssl.py +32 -0
  19. jolt/pkgs/busybox.py +39 -0
  20. jolt/pkgs/bzip2.py +43 -0
  21. jolt/pkgs/cares.py +29 -0
  22. jolt/pkgs/catch2.py +36 -0
  23. jolt/pkgs/cbindgen.py +17 -0
  24. jolt/pkgs/cista.py +19 -0
  25. jolt/pkgs/clang.py +44 -0
  26. jolt/pkgs/cli11.py +23 -0
  27. jolt/pkgs/cmake.py +48 -0
  28. jolt/pkgs/cpython.py +196 -0
  29. jolt/pkgs/crun.py +29 -0
  30. jolt/pkgs/curl.py +38 -0
  31. jolt/pkgs/dbus.py +18 -0
  32. jolt/pkgs/double_conversion.py +24 -0
  33. jolt/pkgs/fastfloat.py +21 -0
  34. jolt/pkgs/ffmpeg.py +28 -0
  35. jolt/pkgs/flatbuffers.py +29 -0
  36. jolt/pkgs/fmt.py +27 -0
  37. jolt/pkgs/fstree.py +20 -0
  38. jolt/pkgs/gflags.py +18 -0
  39. jolt/pkgs/glib.py +18 -0
  40. jolt/pkgs/glog.py +25 -0
  41. jolt/pkgs/glslang.py +21 -0
  42. jolt/pkgs/golang.py +16 -11
  43. jolt/pkgs/googlebenchmark.py +18 -0
  44. jolt/pkgs/googletest.py +46 -0
  45. jolt/pkgs/gperf.py +15 -0
  46. jolt/pkgs/grpc.py +73 -0
  47. jolt/pkgs/hdf5.py +19 -0
  48. jolt/pkgs/help2man.py +14 -0
  49. jolt/pkgs/inja.py +28 -0
  50. jolt/pkgs/jsoncpp.py +31 -0
  51. jolt/pkgs/libarchive.py +43 -0
  52. jolt/pkgs/libcap.py +44 -0
  53. jolt/pkgs/libdrm.py +44 -0
  54. jolt/pkgs/libedit.py +42 -0
  55. jolt/pkgs/libevent.py +31 -0
  56. jolt/pkgs/libexpat.py +27 -0
  57. jolt/pkgs/libfastjson.py +21 -0
  58. jolt/pkgs/libffi.py +16 -0
  59. jolt/pkgs/libglvnd.py +30 -0
  60. jolt/pkgs/libogg.py +28 -0
  61. jolt/pkgs/libpciaccess.py +18 -0
  62. jolt/pkgs/libseccomp.py +21 -0
  63. jolt/pkgs/libtirpc.py +24 -0
  64. jolt/pkgs/libtool.py +42 -0
  65. jolt/pkgs/libunwind.py +35 -0
  66. jolt/pkgs/libva.py +18 -0
  67. jolt/pkgs/libvorbis.py +33 -0
  68. jolt/pkgs/libxml2.py +35 -0
  69. jolt/pkgs/libxslt.py +17 -0
  70. jolt/pkgs/libyajl.py +16 -0
  71. jolt/pkgs/llvm.py +81 -0
  72. jolt/pkgs/lua.py +54 -0
  73. jolt/pkgs/lz4.py +26 -0
  74. jolt/pkgs/m4.py +14 -0
  75. jolt/pkgs/make.py +17 -0
  76. jolt/pkgs/mesa.py +81 -0
  77. jolt/pkgs/meson.py +17 -0
  78. jolt/pkgs/mstch.py +28 -0
  79. jolt/pkgs/mysql.py +60 -0
  80. jolt/pkgs/nasm.py +49 -0
  81. jolt/pkgs/ncurses.py +30 -0
  82. jolt/pkgs/ng_log.py +25 -0
  83. jolt/pkgs/ninja.py +45 -0
  84. jolt/pkgs/nlohmann_json.py +25 -0
  85. jolt/pkgs/nodejs.py +19 -11
  86. jolt/pkgs/opencv.py +24 -0
  87. jolt/pkgs/openjdk.py +26 -0
  88. jolt/pkgs/openssl.py +103 -0
  89. jolt/pkgs/paho.py +76 -0
  90. jolt/pkgs/patchelf.py +16 -0
  91. jolt/pkgs/perl.py +42 -0
  92. jolt/pkgs/pkgconfig.py +64 -0
  93. jolt/pkgs/poco.py +39 -0
  94. jolt/pkgs/protobuf.py +77 -0
  95. jolt/pkgs/pugixml.py +27 -0
  96. jolt/pkgs/python.py +19 -0
  97. jolt/pkgs/qt.py +35 -0
  98. jolt/pkgs/rapidjson.py +26 -0
  99. jolt/pkgs/rapidyaml.py +28 -0
  100. jolt/pkgs/re2.py +30 -0
  101. jolt/pkgs/re2c.py +17 -0
  102. jolt/pkgs/readline.py +15 -0
  103. jolt/pkgs/rust.py +41 -0
  104. jolt/pkgs/sdl.py +28 -0
  105. jolt/pkgs/simdjson.py +27 -0
  106. jolt/pkgs/soci.py +46 -0
  107. jolt/pkgs/spdlog.py +29 -0
  108. jolt/pkgs/spirv_llvm.py +21 -0
  109. jolt/pkgs/spirv_tools.py +24 -0
  110. jolt/pkgs/sqlite.py +83 -0
  111. jolt/pkgs/ssl.py +12 -0
  112. jolt/pkgs/texinfo.py +15 -0
  113. jolt/pkgs/tomlplusplus.py +22 -0
  114. jolt/pkgs/wayland.py +26 -0
  115. jolt/pkgs/x11.py +58 -0
  116. jolt/pkgs/xerces_c.py +20 -0
  117. jolt/pkgs/xorg.py +360 -0
  118. jolt/pkgs/xz.py +29 -0
  119. jolt/pkgs/yamlcpp.py +30 -0
  120. jolt/pkgs/zeromq.py +47 -0
  121. jolt/pkgs/zlib.py +69 -0
  122. jolt/pkgs/zstd.py +33 -0
  123. jolt/plugins/autotools.py +66 -0
  124. jolt/plugins/cmake.py +74 -6
  125. jolt/plugins/conan.py +238 -0
  126. jolt/plugins/cxxinfo.py +7 -0
  127. jolt/plugins/docker.py +3 -3
  128. jolt/plugins/environ.py +11 -0
  129. jolt/plugins/fetch.py +141 -0
  130. jolt/plugins/gdb.py +10 -6
  131. jolt/plugins/git.py +60 -11
  132. jolt/plugins/libtool.py +63 -0
  133. jolt/plugins/linux.py +990 -0
  134. jolt/plugins/meson.py +61 -0
  135. jolt/plugins/ninja-compdb.py +11 -7
  136. jolt/plugins/ninja.py +245 -26
  137. jolt/plugins/paths.py +11 -1
  138. jolt/plugins/pkgconfig.py +219 -0
  139. jolt/plugins/podman.py +15 -41
  140. jolt/plugins/python.py +137 -0
  141. jolt/plugins/rust.py +25 -0
  142. jolt/plugins/scheduler.py +18 -14
  143. jolt/plugins/selfdeploy/setup.py +2 -1
  144. jolt/plugins/selfdeploy.py +21 -30
  145. jolt/plugins/strings.py +19 -10
  146. jolt/scheduler.py +428 -138
  147. jolt/tasks.py +159 -7
  148. jolt/tools.py +105 -51
  149. jolt/utils.py +16 -1
  150. jolt/version.py +1 -1
  151. {jolt-0.9.342.dist-info → jolt-0.9.429.dist-info}/METADATA +64 -9
  152. jolt-0.9.429.dist-info/RECORD +207 -0
  153. {jolt-0.9.342.dist-info → jolt-0.9.429.dist-info}/WHEEL +1 -1
  154. jolt/plugins/debian.py +0 -338
  155. jolt/plugins/repo.py +0 -253
  156. jolt-0.9.342.dist-info/RECORD +0 -93
  157. {jolt-0.9.342.dist-info → jolt-0.9.429.dist-info}/entry_points.txt +0 -0
  158. {jolt-0.9.342.dist-info → jolt-0.9.429.dist-info}/top_level.txt +0 -0
jolt/cache.py CHANGED
@@ -33,194 +33,122 @@ def locked(func):
33
33
  return _f
34
34
 
35
35
 
36
- class StorageProvider(object):
37
- def download(self, artifact, force=False):
38
- return False
39
-
40
- def download_enabled(self):
41
- return True
42
-
43
- def upload(self, artifact, force=False):
44
- return False
45
-
46
- def upload_enabled(self):
47
- return True
48
-
49
- def location(self, artifact):
50
- return '' # URL
51
-
52
- def availability(self, artifacts):
53
- # Ensure artifacts is a list
54
- artifacts = utils.as_list(artifacts)
55
-
56
- present = set()
57
- missing = set()
58
-
59
- for artifact in artifacts:
60
- if self.location(artifact):
61
- present.add(artifact)
62
- else:
63
- missing.add(artifact)
64
-
65
- return list(present), list(missing)
66
-
67
-
68
- class StorageProviderFactory(StorageProvider):
69
- def create(self):
70
- pass
71
-
72
-
73
- def RegisterStorage(cls):
74
- ArtifactCache.storage_provider_factories.append(cls)
75
-
76
-
77
- class ArtifactAttributeSet(object):
78
- def __init__(self):
79
- super(ArtifactAttributeSet, self).__setattr__("_attributes", {})
80
-
81
- def _get_attributes(self):
82
- return self._attributes
83
-
84
- def __getattr__(self, name):
85
- attributes = self._get_attributes()
86
- if name not in attributes:
87
- attributes[name] = self.create(name)
88
- return attributes[name]
89
-
90
- def __setattr__(self, name, value):
91
- attributes = self._get_attributes()
92
- if name not in attributes:
93
- attributes[name] = self.create(name)
94
- attributes[name].set_value(value)
95
- return attributes[name]
96
-
97
- def __dict__(self):
98
- return {key: str(value) for key, value in self.items()}
99
-
100
- def items(self):
101
- return self._get_attributes().items()
102
-
103
- def apply(self, task, artifact):
104
- for _, value in self.items():
105
- value.apply(task, artifact)
106
-
107
- def apply_deps(self, task, deps):
108
- pass
109
-
110
- def unapply(self, task, artifact):
111
- for _, value in self.items():
112
- value.unapply(task, artifact)
113
-
114
- def unapply_deps(self, task, deps):
115
- pass
116
-
117
- def visit(self, task, artifact, visitor):
118
- for _, value in self.items():
119
- value.visit(task, artifact, visitor)
120
-
121
-
122
36
  class ArtifactAttributeSetRegistry(object):
123
- providers = []
37
+ """
38
+ Registry for providers of artifact attribute sets.
39
+ """
40
+
41
+ providers = [] # List of objects that implement ArtifactAttributeSetProvider
124
42
 
125
43
  @staticmethod
126
44
  def create_all(artifact):
45
+ """ Create all artifact attribute sets. """
127
46
  for provider in ArtifactAttributeSetRegistry.providers:
128
47
  provider().create(artifact)
129
48
 
130
49
  @staticmethod
131
50
  def parse_all(artifact, content):
51
+ """ Parse all artifact attribute sets. """
132
52
  for provider in ArtifactAttributeSetRegistry.providers:
133
53
  provider().parse(artifact, content)
134
54
 
135
55
  @staticmethod
136
56
  def format_all(artifact, content):
57
+ """ Format all artifact attribute sets. """
137
58
  for provider in ArtifactAttributeSetRegistry.providers:
138
59
  provider().format(artifact, content)
139
60
 
140
61
  @staticmethod
141
62
  def apply_all(task, artifact):
63
+ """ Apply all artifact attribute sets. """
142
64
  for provider in ArtifactAttributeSetRegistry.providers:
143
65
  provider().apply(task, artifact)
144
66
 
145
- @staticmethod
146
- def apply_all_deps(task, deps):
147
- for provider in ArtifactAttributeSetRegistry.providers:
148
- provider().apply_deps(task, deps)
149
-
150
67
  @staticmethod
151
68
  def unapply_all(task, artifact):
69
+ """ Unapply all artifact attribute sets. """
152
70
  for provider in ArtifactAttributeSetRegistry.providers:
153
71
  provider().unapply(task, artifact)
154
72
 
155
- @staticmethod
156
- def unapply_all_deps(task, deps):
157
- for provider in ArtifactAttributeSetRegistry.providers:
158
- provider().unapply_deps(task, deps)
159
-
160
73
  @staticmethod
161
74
  def visit_all(task, artifact, visitor):
75
+ """ Visit all artifact attribute sets. """
162
76
  for provider in ArtifactAttributeSetRegistry.providers:
163
77
  provider().visit(task, artifact, visitor)
164
78
 
165
79
 
166
- def visit_artifact(task, artifact, visitor):
167
- ArtifactAttributeSetRegistry.visit_all(task, artifact, visitor)
168
-
169
-
170
- class ArtifactAttributeSetProvider(object):
171
- @staticmethod
172
- def Register(cls):
173
- ArtifactAttributeSetRegistry.providers.append(cls)
174
-
175
- def create(self, artifact):
176
- raise NotImplementedError()
177
-
178
- def parse(self, artifact, content):
179
- raise NotImplementedError()
180
-
181
- def format(self, artifact, content):
182
- raise NotImplementedError()
183
-
184
- def apply(self, task, artifact):
185
- pass
186
-
187
- def apply_deps(self, task, deps):
188
- pass
189
-
190
- def unapply(self, task, artifact):
191
- pass
192
-
193
- def unapply_deps(self, task, deps):
194
- pass
80
+ class ArtifactAttribute(object):
81
+ """
82
+ An artifact attribute.
195
83
 
196
- def visit(self, task, artifact, visitor):
197
- pass
84
+ An artifact attribute is a key-value pair that can be set and retrieved
85
+ from an artifact attribute set. Attributes are used to store metadata and other
86
+ information that is associated with an artifact. They communicate information
87
+ between tasks and store information that is used by tasks when they consume an artifact.
198
88
 
89
+ Artifact attributes can also perform actions when the artifact is consumed.
199
90
 
200
- class ArtifactAttribute(object):
91
+ """
201
92
  def __init__(self, name):
202
93
  self._name = name
203
94
 
204
95
  def get_name(self):
96
+ """ Get the name of the attribute. """
205
97
  return self._name
206
98
 
207
99
  def set_value(self, value, expand=True):
100
+ """
101
+ Set the value of the attribute.
102
+
103
+ Must be implemented by subclasses.
104
+
105
+ Args:
106
+ value: The value to set.
107
+ expand: If True, the value is macro expanded using the tools.expand() method.
108
+ """
208
109
  raise NotImplementedError()
209
110
 
210
111
  def get_value(self):
112
+ """
113
+ Get the value of the attribute.
114
+
115
+ Must be implemented by subclasses.
116
+ """
211
117
  raise NotImplementedError()
212
118
 
213
119
  def apply(self, task, artifact):
120
+ """
121
+ Perform an action when the artifact is being used.
122
+
123
+ Args:
124
+ task (Task): The task that is using the artifact.
125
+ artifact (Artifact): The artifact that is being used.
126
+
127
+ """
214
128
  pass
215
129
 
216
130
  def unapply(self, task, artifact):
131
+ """
132
+ Undo an action when the artifact is no longer being used.
133
+
134
+ Args:
135
+ task (Task): The task that is no longer using the artifact.
136
+ artifact (Artifact): The artifact that is no longer being used.
137
+ """
217
138
  pass
218
139
 
219
- def __str__(self):
140
+ def __str__(self) -> str:
141
+ """
142
+ Get a string representation of the attribute.
143
+
144
+ Must be implemented by subclasses.
145
+ """
220
146
  raise NotImplementedError()
221
147
 
222
148
 
223
149
  class ArtifactStringAttribute(ArtifactAttribute):
150
+ """ An artifact attribute that stores a string value. """
151
+
224
152
  def __init__(self, artifact, name):
225
153
  self._artifact = artifact
226
154
  self._name = name
@@ -235,17 +163,13 @@ class ArtifactStringAttribute(ArtifactAttribute):
235
163
  def get_value(self):
236
164
  return self._value
237
165
 
238
- def apply(self, task, artifact):
239
- pass
240
-
241
- def unapply(self, task, artifact):
242
- pass
243
-
244
- def __str__(self):
166
+ def __str__(self) -> str:
245
167
  return str(self._value)
246
168
 
247
169
 
248
170
  class ArtifactListAttribute(ArtifactAttribute):
171
+ """ An artifact attribute that stores a list of values. """
172
+
249
173
  def __init__(self, artifact, name):
250
174
  self._artifact = artifact
251
175
  self._name = name
@@ -257,6 +181,9 @@ class ArtifactListAttribute(ArtifactAttribute):
257
181
  def __getslice__(self, i, j):
258
182
  return self._value[i:j]
259
183
 
184
+ def __len__(self):
185
+ return len(self._value)
186
+
260
187
  def get_name(self):
261
188
  return self._name
262
189
 
@@ -287,23 +214,16 @@ class ArtifactListAttribute(ArtifactAttribute):
287
214
  def count(self):
288
215
  return len(self.items())
289
216
 
290
- def apply(self, task, artifact):
291
- pass
292
-
293
- def unapply(self, task, artifact):
294
- pass
217
+ def __str__(self) -> str:
218
+ return fs.pathsep.join(str(v) for v in self._value)
295
219
 
296
220
 
297
221
  class ArtifactFileAttribute(object):
222
+ """ An attribute that stores a list of source and destination path tuples for files collected into the artifact. """
223
+
298
224
  def __init__(self):
299
225
  self._files = []
300
226
 
301
- def apply(self, task, artifact):
302
- pass
303
-
304
- def unapply(self, task, artifact):
305
- pass
306
-
307
227
  def append(self, src, dst):
308
228
  self._files.append((fs.as_posix(src), fs.as_posix(dst)))
309
229
 
@@ -314,8 +234,145 @@ class ArtifactFileAttribute(object):
314
234
  return self._files
315
235
 
316
236
 
237
+ class ArtifactAttributeSet(object):
238
+ """
239
+ A set of artifact attributes.
240
+
241
+ An attribute set is a collection of attributes. Each attribute is
242
+ accessed using the attribute name as an attribute of the set. For
243
+ example, to access an attribute named 'version' in an attribute set
244
+ named 'strings', you would write:
245
+
246
+ .. code-block:: python
247
+
248
+ artifact.strings.version = "1.0"
249
+
250
+ """
251
+
252
+ def __init__(self):
253
+ super(ArtifactAttributeSet, self).__setattr__("_attributes", {})
254
+
255
+ def _get_attributes(self):
256
+ return self._attributes
257
+
258
+ def __getattr__(self, name) -> ArtifactAttribute:
259
+ """
260
+ Get or create an attribute by name.
261
+
262
+ Args:
263
+ name (str): The name of the attribute.
264
+
265
+ Returns:
266
+ An attribute object.
267
+ """
268
+ attributes = self._get_attributes()
269
+ if name not in attributes:
270
+ attributes[name] = self.create(name)
271
+ return attributes[name]
272
+
273
+ def __setattr__(self, name, value):
274
+ """
275
+ Set an attribute by name.
276
+
277
+ Args:
278
+ name (str): The name of the attribute.
279
+ value: The value to set.
280
+ """
281
+ attributes = self._get_attributes()
282
+ if name not in attributes:
283
+ attributes[name] = self.create(name)
284
+ attributes[name].set_value(value)
285
+ return attributes[name]
286
+
287
+ def __dict__(self):
288
+ """ Get a dictionary representation of the attribute set. """
289
+ return {key: str(value) for key, value in self.items()}
290
+
291
+ def __iter__(self):
292
+ """ Iterate over the attribute set. """
293
+ return iter(self.items())
294
+
295
+ def get(self, name, default=None):
296
+ """ Get an attribute by name.
297
+
298
+ Args:
299
+ name (str): The name of the attribute.
300
+
301
+ Returns:
302
+ The attribute object, or None if it does not exist.
303
+ """
304
+ attributes = self._get_attributes()
305
+ return attributes.get(name, default)
306
+
307
+ def items(self):
308
+ """ Get a list of tuples containing the attribute name and value. """
309
+ return self._get_attributes().items()
310
+
311
+ def apply(self, task, artifact):
312
+ """ Perform attribute actions when the artifact is being used. """
313
+ for _, value in self.items():
314
+ value.apply(task, artifact)
315
+
316
+ def unapply(self, task, artifact):
317
+ """ Undo attribute actions when the artifact is no longer being used. """
318
+ for _, value in self.items():
319
+ value.unapply(task, artifact)
320
+
321
+ def visit(self, task, artifact, visitor):
322
+ """ Visit all attributes in the set. """
323
+ for _, value in self.items():
324
+ value.visit(task, artifact, visitor)
325
+
326
+
327
+ class ArtifactAttributeSetProvider(object):
328
+ """ Base class for artifact attribute set providers.
329
+
330
+ An artifact attribute set provider is a factory for creating and managing
331
+ attribute sets in an artifact.
332
+ """
333
+
334
+ @staticmethod
335
+ def Register(cls):
336
+ """ Decorator for registering a provider class. """
337
+ ArtifactAttributeSetRegistry.providers.append(cls)
338
+
339
+ def create(self, artifact):
340
+ """ Create an attribute set for an artifact. """
341
+ raise NotImplementedError()
342
+
343
+ def parse(self, artifact, content):
344
+ """
345
+ Parse an attribute set from a dictionary.
346
+
347
+ The dictionary is loaded from a JSON file embedded in the artifact.
348
+ """
349
+ raise NotImplementedError()
350
+
351
+ def format(self, artifact, content):
352
+ """
353
+ Format an attribute set to a dictionary.
354
+
355
+ The dictionary is saved to a JSON file embedded in the artifact.
356
+ """
357
+ raise NotImplementedError()
358
+
359
+ def apply(self, task, artifact):
360
+ """ Perform actions when the artifact is being used. """
361
+ pass
362
+
363
+ def unapply(self, task, artifact):
364
+ """ Undo actions when the artifact is no longer being used. """
365
+ pass
366
+
367
+ def visit(self, task, artifact, visitor):
368
+ """ Visit all attributes in the set. """
369
+ pass
370
+
371
+
317
372
  @ArtifactAttributeSetProvider.Register
318
373
  class ArtifactFileAttributeProvider(ArtifactAttributeSetProvider):
374
+ """ Provider for the artifact 'files' attribute set. """
375
+
319
376
  def create(self, artifact):
320
377
  setattr(artifact, "files", ArtifactFileAttribute())
321
378
 
@@ -329,22 +386,19 @@ class ArtifactFileAttributeProvider(ArtifactAttributeSetProvider):
329
386
  def format(self, artifact, content):
330
387
  content["files"] = [{"src": src, "dst": dst} for src, dst in artifact.files.items()]
331
388
 
332
- def apply(self, task, artifact):
333
- pass
334
389
 
335
- def unapply(self, task, artifact):
336
- pass
337
-
338
- def visit(self, task, artifact, visitor):
339
- pass
390
+ def visit_artifact(task, artifact, visitor):
391
+ ArtifactAttributeSetRegistry.visit_all(task, artifact, visitor)
340
392
 
341
393
 
342
394
  def json_serializer(obj):
395
+ """ JSON serializer for datetime objects. """
343
396
  if isinstance(obj, datetime):
344
397
  return dict(type="datetime", value=obj.strftime("%Y-%m-%d %H:%M:%S.%f"))
345
398
 
346
399
 
347
400
  def json_deserializer(dct):
401
+ """ JSON deserializer for datetime objects. """
348
402
  if dct.get("type") == "datetime":
349
403
  return datetime.strptime(dct["value"], "%Y-%m-%d %H:%M:%S.%f")
350
404
  return dct
@@ -880,6 +934,16 @@ class Artifact(object):
880
934
 
881
935
 
882
936
  class ArtifactToolsProxy(object):
937
+ """
938
+ An artifact proxy that uses a specific tools object.
939
+
940
+ Used when artifacts are consumed by tasks. The proxy allows the
941
+ task to access the artifact's methods and attributes using the
942
+ task's own tools object. This is useful when the consumer task
943
+ wishes to copy files, read files, etc, using the current working
944
+ directory and environment of the task.
945
+ """
946
+
883
947
  def __init__(self, artifact, tools):
884
948
  self._artifact = artifact
885
949
  self._tools = tools
@@ -945,10 +1009,8 @@ class Context(object):
945
1009
  self._artifacts_index[artifact.name + "@" + dep.short_qualified_name] = artifact
946
1010
  artifact.apply()
947
1011
  ArtifactAttributeSetRegistry.apply_all(self._node.task, artifact)
948
- ArtifactAttributeSetRegistry.apply_all_deps(self._node.task, self)
949
1012
  except (Exception, KeyboardInterrupt) as e:
950
1013
  # Rollback all attributes/resources except the last failing one
951
- ArtifactAttributeSetRegistry.unapply_all_deps(self._node.task, self)
952
1014
  for name, artifact in reversed(list(self._artifacts.items())[:-1]):
953
1015
  with utils.ignore_exception():
954
1016
  ArtifactAttributeSetRegistry.unapply_all(self._node.task, artifact)
@@ -957,7 +1019,6 @@ class Context(object):
957
1019
  return self
958
1020
 
959
1021
  def __exit__(self, type, value, tb):
960
- ArtifactAttributeSetRegistry.unapply_all_deps(self._node.task, self)
961
1022
  for name, artifact in reversed(self._artifacts.items()):
962
1023
  ArtifactAttributeSetRegistry.unapply_all(self._node.task, artifact)
963
1024
  artifact.unapply()
@@ -1025,6 +1086,144 @@ class PidProvider(object):
1025
1086
  return pid
1026
1087
 
1027
1088
 
1089
+ class StorageProvider(object):
1090
+ """
1091
+ Base class for remote artifact storage providers.
1092
+
1093
+ A storage provider is responsible for uploading and downloading
1094
+ artifacts to and from a remote storage location. The storage
1095
+ location can be a file system path, a cloud storage service, or
1096
+ any other type of storage.
1097
+
1098
+ """
1099
+
1100
+ def download(self, artifact: Artifact, force: bool = False) -> bool:
1101
+ """
1102
+ Download an artifact from the storage location.
1103
+
1104
+ The should be downloaded to the path returned by the artifact's
1105
+ :func:`~jolt.Artifact.get_archive_path` method. The downloaded artifact
1106
+ must be in the format specified by DEFAULT_ARCHIVE_TYPE.
1107
+
1108
+ The download should be retried if it fails due to network issues.
1109
+ The method may raise an exception on errors.
1110
+
1111
+ Args:
1112
+ artifact (Artifact): The artifact to download.
1113
+ force (bool, optional): If True, the download should be forced,
1114
+ even if the artifact is already present locally, or if the
1115
+ download is disabled. The default is False.
1116
+
1117
+ Returns:
1118
+ bool: True if the download was successful, False otherwise.
1119
+
1120
+ """
1121
+ return False
1122
+
1123
+ def download_enabled(self) -> bool:
1124
+ """ Return True if downloading is enabled. Default is True. """
1125
+ return True
1126
+
1127
+ def upload(self, artifact: Artifact, force: bool = False) -> bool:
1128
+ """
1129
+ Upload an artifact to the storage location.
1130
+
1131
+ The artifact to be uploaded is located at the path returned by
1132
+ the artifact's :func:`~jolt.Artifact.get_archive_path` method. The
1133
+ uploaded artifact is in the format specified by DEFAULT_ARCHIVE_TYPE.
1134
+ The provider may choose to upload the artifact using a different
1135
+ format, but it must be able to download the artifact in the
1136
+ DEFAULT_ARCHIVE_TYPE format.
1137
+
1138
+ The upload should be retried if it fails due to network issues.
1139
+ The method may raise an exception on errors.
1140
+
1141
+ Args:
1142
+ artifact (Artifact): The artifact to upload.
1143
+ force (bool, optional): If True, the upload should be forced,
1144
+ even if the artifact is already present remotely, or if the
1145
+ upload is disabled. The default is False.
1146
+
1147
+ Returns:
1148
+ bool: True if the upload was successful, False otherwise.
1149
+
1150
+ """
1151
+ return False
1152
+
1153
+ def upload_enabled(self) -> bool:
1154
+ """ Return True if uploading is enabled. Default is True. """
1155
+ return True
1156
+
1157
+ def location(self, artifact) -> str:
1158
+ """
1159
+ Return the URL of the artifact in the storage location.
1160
+
1161
+ This method is sometimes used to identify if an artifact is
1162
+ present in the storage location. The URL should point to the
1163
+ artifact if present, or an empty string if the artifact is
1164
+ absent.
1165
+
1166
+ Args:
1167
+ artifact (Artifact): The artifact to locate.
1168
+ """
1169
+ return '' # URL
1170
+
1171
+ def availability(self, artifacts: list) -> tuple:
1172
+ """
1173
+ Check the availability of a list of artifacts.
1174
+
1175
+ This method is used to determine which artifacts are present in the
1176
+ storage location. The method should return a tuple of two lists:
1177
+ the first list contains the artifacts that are present, and the
1178
+ second list contains the artifacts that are missing.
1179
+
1180
+ The default implementation of this method calls the :func:`~jolt.StorageProvider.location`
1181
+ method for each artifact in the list. Subclasses may override this
1182
+ method to provide a more efficient implementation.
1183
+
1184
+ Args:
1185
+ artifacts (list): A list of artifacts to check.
1186
+
1187
+ Returns:
1188
+ tuple: A tuple of two lists: the first list contains the artifacts
1189
+ that are present, and the second list contains the artifacts
1190
+ that are missing.
1191
+
1192
+ """
1193
+ # Ensure artifacts is a list
1194
+ artifacts = utils.as_list(artifacts)
1195
+
1196
+ present = set()
1197
+ missing = set()
1198
+
1199
+ for artifact in artifacts:
1200
+ if self.location(artifact):
1201
+ present.add(artifact)
1202
+ else:
1203
+ missing.add(artifact)
1204
+
1205
+ return list(present), list(missing)
1206
+
1207
+
1208
+ class StorageProviderFactory(StorageProvider):
1209
+ """ A factory for store providers. """
1210
+
1211
+ def create(self) -> StorageProvider:
1212
+ """
1213
+ Create a new storage provider.
1214
+
1215
+ This method should return a new instance of a storage provider,
1216
+ which must be a subclass of :class:`~jolt.StorageProvider`.
1217
+
1218
+ """
1219
+ pass
1220
+
1221
+
1222
+ def RegisterStorage(cls):
1223
+ """ Decorator used to register a storage provider factory. """
1224
+ ArtifactCache.storage_provider_factories.append(cls)
1225
+
1226
+
1028
1227
  @utils.Singleton
1029
1228
  class ArtifactCache(StorageProvider):
1030
1229
  """
@@ -1095,7 +1294,7 @@ class ArtifactCache(StorageProvider):
1095
1294
 
1096
1295
  # Read configuration
1097
1296
  self._max_size = config.getsize(
1098
- "jolt", "cachesize", os.environ.get("JOLT_CACHESIZE", 1 * 1024 ** 3))
1297
+ "jolt", "cachesize", os.environ.get("JOLT_CACHE_SIZE", 1 * 1024 ** 3))
1099
1298
 
1100
1299
  # Create cache directory
1101
1300
  self._fs_create_cachedir()