jolt 0.9.354__tar.gz → 0.9.370__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.
- {jolt-0.9.354 → jolt-0.9.370}/PKG-INFO +2 -2
- {jolt-0.9.354 → jolt-0.9.370}/jolt/__init__.py +47 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/cache.py +339 -159
- {jolt-0.9.354 → jolt-0.9.370}/jolt/cli.py +29 -98
- {jolt-0.9.354 → jolt-0.9.370}/jolt/config.py +14 -26
- {jolt-0.9.354 → jolt-0.9.370}/jolt/graph.py +27 -15
- {jolt-0.9.354 → jolt-0.9.370}/jolt/loader.py +141 -180
- {jolt-0.9.354 → jolt-0.9.370}/jolt/manifest.py +0 -46
- jolt-0.9.370/jolt/options.py +39 -0
- jolt-0.9.370/jolt/plugins/conan.py +479 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/docker.py +1 -1
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/environ.py +11 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/gdb.py +6 -5
- jolt-0.9.370/jolt/plugins/linux.py +943 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/ninja-compdb.py +7 -6
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/podman.py +4 -4
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/scheduler.py +18 -14
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/selfdeploy.py +1 -22
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/strings.py +16 -6
- {jolt-0.9.354 → jolt-0.9.370}/jolt/scheduler.py +428 -138
- {jolt-0.9.354 → jolt-0.9.370}/jolt/tasks.py +23 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/tools.py +15 -8
- jolt-0.9.370/jolt/version.py +1 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt.egg-info/PKG-INFO +2 -2
- {jolt-0.9.354 → jolt-0.9.370}/jolt.egg-info/SOURCES.txt +1 -2
- {jolt-0.9.354 → jolt-0.9.370}/jolt.egg-info/requires.txt +1 -1
- {jolt-0.9.354 → jolt-0.9.370}/setup.py +1 -1
- jolt-0.9.354/jolt/options.py +0 -16
- jolt-0.9.354/jolt/plugins/conan.py +0 -241
- jolt-0.9.354/jolt/plugins/debian.py +0 -338
- jolt-0.9.354/jolt/plugins/repo.py +0 -253
- jolt-0.9.354/jolt/version.py +0 -1
- {jolt-0.9.354 → jolt-0.9.370}/README.rst +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/__main__.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/bin/fstree-darwin-x86_64 +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/bin/fstree-linux-x86_64 +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/chroot.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/colors.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/common_pb2.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/common_pb2_grpc.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/error.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/expires.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/filesystem.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/hooks.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/influence.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/inspection.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/log.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/pkgs/__init__.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/pkgs/golang.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/pkgs/nodejs.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/__init__.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/alias.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/allure.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/autoweight.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/cache.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/cmake.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/cxx.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/cxxinfo.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/dashboard.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/email.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/email.xslt +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/gerrit.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/git.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/golang.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/googletest.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/http.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/junit.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/logstash.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/ninja.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/nodejs.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/paths.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/python.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/remote_execution/__init__.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/remote_execution/administration_pb2.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/remote_execution/administration_pb2_grpc.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/remote_execution/log_pb2.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/remote_execution/log_pb2_grpc.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/remote_execution/scheduler_pb2.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/remote_execution/scheduler_pb2_grpc.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/remote_execution/worker_pb2.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/remote_execution/worker_pb2_grpc.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/report.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/symlinks.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/telemetry.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/timeline.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/volume.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/yaml-ninja.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/plugins/yamltask.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/templates/cxxexecutable.cmake.template +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/templates/cxxlibrary.cmake.template +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/templates/export.sh.template +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/templates/timeline.html.template +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/timer.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/utils.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/version_utils.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt/xmldom.py +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt.egg-info/dependency_links.txt +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt.egg-info/entry_points.txt +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/jolt.egg-info/top_level.txt +0 -0
- {jolt-0.9.354 → jolt-0.9.370}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: jolt
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.370
|
|
4
4
|
Summary: A task executor
|
|
5
5
|
Home-page: https://github.com/srand/jolt
|
|
6
6
|
Author: Robert Andersson
|
|
@@ -58,7 +58,7 @@ Requires-Dist: zstandard==0.23.0
|
|
|
58
58
|
Provides-Extra: allure
|
|
59
59
|
Requires-Dist: allure-python-commons; extra == "allure"
|
|
60
60
|
Provides-Extra: conan
|
|
61
|
-
Requires-Dist: conan
|
|
61
|
+
Requires-Dist: conan>=2.0; extra == "conan"
|
|
62
62
|
Provides-Extra: dev
|
|
63
63
|
Requires-Dist: check-manifest; extra == "dev"
|
|
64
64
|
Provides-Extra: doc
|
|
@@ -93,3 +93,50 @@ def include(joltfile, joltdir=None):
|
|
|
93
93
|
except Exception as e:
|
|
94
94
|
from jolt.error import raise_error
|
|
95
95
|
raise_error("Failed to load '{0}': {1}", joltfile, str(e))
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def require_resource(name: str, ):
|
|
99
|
+
""" Require a resource task.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
name (str): The name of the resource task.
|
|
103
|
+
|
|
104
|
+
Example:
|
|
105
|
+
|
|
106
|
+
.. code-block:: python
|
|
107
|
+
|
|
108
|
+
from jolt import require_resource
|
|
109
|
+
|
|
110
|
+
require_resource("git:url=https://github.com/srand/jolt.git")
|
|
111
|
+
|
|
112
|
+
"""
|
|
113
|
+
from jolt.tasks import TaskRegistry
|
|
114
|
+
registry = TaskRegistry.get()
|
|
115
|
+
registry.require_workspace_resource(name)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def require_version(verstr: str):
|
|
119
|
+
""" Require a specific version of Jolt.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
verstr (str): The version to require. Must be a string.
|
|
123
|
+
Operators such as ``>=`` and ``<=`` are supported.
|
|
124
|
+
|
|
125
|
+
Example:
|
|
126
|
+
|
|
127
|
+
.. code-block:: python
|
|
128
|
+
|
|
129
|
+
from jolt import require_version
|
|
130
|
+
|
|
131
|
+
require_version("1.0.0")
|
|
132
|
+
|
|
133
|
+
"""
|
|
134
|
+
from jolt.error import raise_error_if
|
|
135
|
+
from jolt.version_utils import requirement, version
|
|
136
|
+
|
|
137
|
+
req = requirement(verstr)
|
|
138
|
+
ver = version(__version__)
|
|
139
|
+
raise_error_if(
|
|
140
|
+
not req.satisfied(ver),
|
|
141
|
+
"This project requires Jolt version {} (running {})",
|
|
142
|
+
req, __version__)
|
|
@@ -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
|
-
|
|
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
|
-
|
|
167
|
-
|
|
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
|
|
80
|
+
class ArtifactAttribute(object):
|
|
81
|
+
"""
|
|
82
|
+
An artifact attribute.
|
|
186
83
|
|
|
187
|
-
|
|
188
|
-
|
|
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.
|
|
189
88
|
|
|
190
|
-
|
|
191
|
-
pass
|
|
192
|
-
|
|
193
|
-
def unapply_deps(self, task, deps):
|
|
194
|
-
pass
|
|
89
|
+
Artifact attributes can also perform actions when the artifact is consumed.
|
|
195
90
|
|
|
196
|
-
|
|
197
|
-
pass
|
|
198
|
-
|
|
199
|
-
|
|
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
|
|
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,13 @@ 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
|
|
295
|
-
|
|
296
217
|
|
|
297
218
|
class ArtifactFileAttribute(object):
|
|
219
|
+
""" An attribute that stores a list of source and destination path tuples for files collected into the artifact. """
|
|
220
|
+
|
|
298
221
|
def __init__(self):
|
|
299
222
|
self._files = []
|
|
300
223
|
|
|
301
|
-
def apply(self, task, artifact):
|
|
302
|
-
pass
|
|
303
|
-
|
|
304
|
-
def unapply(self, task, artifact):
|
|
305
|
-
pass
|
|
306
|
-
|
|
307
224
|
def append(self, src, dst):
|
|
308
225
|
self._files.append((fs.as_posix(src), fs.as_posix(dst)))
|
|
309
226
|
|
|
@@ -314,8 +231,129 @@ class ArtifactFileAttribute(object):
|
|
|
314
231
|
return self._files
|
|
315
232
|
|
|
316
233
|
|
|
234
|
+
class ArtifactAttributeSet(object):
|
|
235
|
+
"""
|
|
236
|
+
A set of artifact attributes.
|
|
237
|
+
|
|
238
|
+
An attribute set is a collection of attributes. Each attribute is
|
|
239
|
+
accessed using the attribute name as an attribute of the set. For
|
|
240
|
+
example, to access an attribute named 'version' in an attribute set
|
|
241
|
+
named 'strings', you would write:
|
|
242
|
+
|
|
243
|
+
.. code-block:: python
|
|
244
|
+
|
|
245
|
+
artifact.strings.version = "1.0"
|
|
246
|
+
|
|
247
|
+
"""
|
|
248
|
+
|
|
249
|
+
def __init__(self):
|
|
250
|
+
super(ArtifactAttributeSet, self).__setattr__("_attributes", {})
|
|
251
|
+
|
|
252
|
+
def _get_attributes(self):
|
|
253
|
+
return self._attributes
|
|
254
|
+
|
|
255
|
+
def __getattr__(self, name) -> ArtifactAttribute:
|
|
256
|
+
"""
|
|
257
|
+
Get or create an attribute by name.
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
name (str): The name of the attribute.
|
|
261
|
+
|
|
262
|
+
Returns:
|
|
263
|
+
An attribute object.
|
|
264
|
+
"""
|
|
265
|
+
attributes = self._get_attributes()
|
|
266
|
+
if name not in attributes:
|
|
267
|
+
attributes[name] = self.create(name)
|
|
268
|
+
return attributes[name]
|
|
269
|
+
|
|
270
|
+
def __setattr__(self, name, value):
|
|
271
|
+
"""
|
|
272
|
+
Set an attribute by name.
|
|
273
|
+
|
|
274
|
+
Args:
|
|
275
|
+
name (str): The name of the attribute.
|
|
276
|
+
value: The value to set.
|
|
277
|
+
"""
|
|
278
|
+
attributes = self._get_attributes()
|
|
279
|
+
if name not in attributes:
|
|
280
|
+
attributes[name] = self.create(name)
|
|
281
|
+
attributes[name].set_value(value)
|
|
282
|
+
return attributes[name]
|
|
283
|
+
|
|
284
|
+
def __dict__(self):
|
|
285
|
+
""" Get a dictionary representation of the attribute set. """
|
|
286
|
+
return {key: str(value) for key, value in self.items()}
|
|
287
|
+
|
|
288
|
+
def items(self):
|
|
289
|
+
""" Get a list of tuples containing the attribute name and value. """
|
|
290
|
+
return self._get_attributes().items()
|
|
291
|
+
|
|
292
|
+
def apply(self, task, artifact):
|
|
293
|
+
""" Perform attribute actions when the artifact is being used. """
|
|
294
|
+
for _, value in self.items():
|
|
295
|
+
value.apply(task, artifact)
|
|
296
|
+
|
|
297
|
+
def unapply(self, task, artifact):
|
|
298
|
+
""" Undo attribute actions when the artifact is no longer being used. """
|
|
299
|
+
for _, value in self.items():
|
|
300
|
+
value.unapply(task, artifact)
|
|
301
|
+
|
|
302
|
+
def visit(self, task, artifact, visitor):
|
|
303
|
+
""" Visit all attributes in the set. """
|
|
304
|
+
for _, value in self.items():
|
|
305
|
+
value.visit(task, artifact, visitor)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
class ArtifactAttributeSetProvider(object):
|
|
309
|
+
""" Base class for artifact attribute set providers.
|
|
310
|
+
|
|
311
|
+
An artifact attribute set provider is a factory for creating and managing
|
|
312
|
+
attribute sets in an artifact.
|
|
313
|
+
"""
|
|
314
|
+
|
|
315
|
+
@staticmethod
|
|
316
|
+
def Register(cls):
|
|
317
|
+
""" Decorator for registering a provider class. """
|
|
318
|
+
ArtifactAttributeSetRegistry.providers.append(cls)
|
|
319
|
+
|
|
320
|
+
def create(self, artifact):
|
|
321
|
+
""" Create an attribute set for an artifact. """
|
|
322
|
+
raise NotImplementedError()
|
|
323
|
+
|
|
324
|
+
def parse(self, artifact, content):
|
|
325
|
+
"""
|
|
326
|
+
Parse an attribute set from a dictionary.
|
|
327
|
+
|
|
328
|
+
The dictionary is loaded from a JSON file embedded in the artifact.
|
|
329
|
+
"""
|
|
330
|
+
raise NotImplementedError()
|
|
331
|
+
|
|
332
|
+
def format(self, artifact, content):
|
|
333
|
+
"""
|
|
334
|
+
Format an attribute set to a dictionary.
|
|
335
|
+
|
|
336
|
+
The dictionary is saved to a JSON file embedded in the artifact.
|
|
337
|
+
"""
|
|
338
|
+
raise NotImplementedError()
|
|
339
|
+
|
|
340
|
+
def apply(self, task, artifact):
|
|
341
|
+
""" Perform actions when the artifact is being used. """
|
|
342
|
+
pass
|
|
343
|
+
|
|
344
|
+
def unapply(self, task, artifact):
|
|
345
|
+
""" Undo actions when the artifact is no longer being used. """
|
|
346
|
+
pass
|
|
347
|
+
|
|
348
|
+
def visit(self, task, artifact, visitor):
|
|
349
|
+
""" Visit all attributes in the set. """
|
|
350
|
+
pass
|
|
351
|
+
|
|
352
|
+
|
|
317
353
|
@ArtifactAttributeSetProvider.Register
|
|
318
354
|
class ArtifactFileAttributeProvider(ArtifactAttributeSetProvider):
|
|
355
|
+
""" Provider for the artifact 'files' attribute set. """
|
|
356
|
+
|
|
319
357
|
def create(self, artifact):
|
|
320
358
|
setattr(artifact, "files", ArtifactFileAttribute())
|
|
321
359
|
|
|
@@ -329,22 +367,19 @@ class ArtifactFileAttributeProvider(ArtifactAttributeSetProvider):
|
|
|
329
367
|
def format(self, artifact, content):
|
|
330
368
|
content["files"] = [{"src": src, "dst": dst} for src, dst in artifact.files.items()]
|
|
331
369
|
|
|
332
|
-
def apply(self, task, artifact):
|
|
333
|
-
pass
|
|
334
370
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
def visit(self, task, artifact, visitor):
|
|
339
|
-
pass
|
|
371
|
+
def visit_artifact(task, artifact, visitor):
|
|
372
|
+
ArtifactAttributeSetRegistry.visit_all(task, artifact, visitor)
|
|
340
373
|
|
|
341
374
|
|
|
342
375
|
def json_serializer(obj):
|
|
376
|
+
""" JSON serializer for datetime objects. """
|
|
343
377
|
if isinstance(obj, datetime):
|
|
344
378
|
return dict(type="datetime", value=obj.strftime("%Y-%m-%d %H:%M:%S.%f"))
|
|
345
379
|
|
|
346
380
|
|
|
347
381
|
def json_deserializer(dct):
|
|
382
|
+
""" JSON deserializer for datetime objects. """
|
|
348
383
|
if dct.get("type") == "datetime":
|
|
349
384
|
return datetime.strptime(dct["value"], "%Y-%m-%d %H:%M:%S.%f")
|
|
350
385
|
return dct
|
|
@@ -880,6 +915,16 @@ class Artifact(object):
|
|
|
880
915
|
|
|
881
916
|
|
|
882
917
|
class ArtifactToolsProxy(object):
|
|
918
|
+
"""
|
|
919
|
+
An artifact proxy that uses a specific tools object.
|
|
920
|
+
|
|
921
|
+
Used when artifacts are consumed by tasks. The proxy allows the
|
|
922
|
+
task to access the artifact's methods and attributes using the
|
|
923
|
+
task's own tools object. This is useful when the consumer task
|
|
924
|
+
wishes to copy files, read files, etc, using the current working
|
|
925
|
+
directory and environment of the task.
|
|
926
|
+
"""
|
|
927
|
+
|
|
883
928
|
def __init__(self, artifact, tools):
|
|
884
929
|
self._artifact = artifact
|
|
885
930
|
self._tools = tools
|
|
@@ -945,10 +990,8 @@ class Context(object):
|
|
|
945
990
|
self._artifacts_index[artifact.name + "@" + dep.short_qualified_name] = artifact
|
|
946
991
|
artifact.apply()
|
|
947
992
|
ArtifactAttributeSetRegistry.apply_all(self._node.task, artifact)
|
|
948
|
-
ArtifactAttributeSetRegistry.apply_all_deps(self._node.task, self)
|
|
949
993
|
except (Exception, KeyboardInterrupt) as e:
|
|
950
994
|
# Rollback all attributes/resources except the last failing one
|
|
951
|
-
ArtifactAttributeSetRegistry.unapply_all_deps(self._node.task, self)
|
|
952
995
|
for name, artifact in reversed(list(self._artifacts.items())[:-1]):
|
|
953
996
|
with utils.ignore_exception():
|
|
954
997
|
ArtifactAttributeSetRegistry.unapply_all(self._node.task, artifact)
|
|
@@ -957,7 +1000,6 @@ class Context(object):
|
|
|
957
1000
|
return self
|
|
958
1001
|
|
|
959
1002
|
def __exit__(self, type, value, tb):
|
|
960
|
-
ArtifactAttributeSetRegistry.unapply_all_deps(self._node.task, self)
|
|
961
1003
|
for name, artifact in reversed(self._artifacts.items()):
|
|
962
1004
|
ArtifactAttributeSetRegistry.unapply_all(self._node.task, artifact)
|
|
963
1005
|
artifact.unapply()
|
|
@@ -1025,6 +1067,144 @@ class PidProvider(object):
|
|
|
1025
1067
|
return pid
|
|
1026
1068
|
|
|
1027
1069
|
|
|
1070
|
+
class StorageProvider(object):
|
|
1071
|
+
"""
|
|
1072
|
+
Base class for remote artifact storage providers.
|
|
1073
|
+
|
|
1074
|
+
A storage provider is responsible for uploading and downloading
|
|
1075
|
+
artifacts to and from a remote storage location. The storage
|
|
1076
|
+
location can be a file system path, a cloud storage service, or
|
|
1077
|
+
any other type of storage.
|
|
1078
|
+
|
|
1079
|
+
"""
|
|
1080
|
+
|
|
1081
|
+
def download(self, artifact: Artifact, force: bool = False) -> bool:
|
|
1082
|
+
"""
|
|
1083
|
+
Download an artifact from the storage location.
|
|
1084
|
+
|
|
1085
|
+
The should be downloaded to the path returned by the artifact's
|
|
1086
|
+
:func:`~jolt.Artifact.get_archive_path` method. The downloaded artifact
|
|
1087
|
+
must be in the format specified by DEFAULT_ARCHIVE_TYPE.
|
|
1088
|
+
|
|
1089
|
+
The download should be retried if it fails due to network issues.
|
|
1090
|
+
The method may raise an exception on errors.
|
|
1091
|
+
|
|
1092
|
+
Args:
|
|
1093
|
+
artifact (Artifact): The artifact to download.
|
|
1094
|
+
force (bool, optional): If True, the download should be forced,
|
|
1095
|
+
even if the artifact is already present locally, or if the
|
|
1096
|
+
download is disabled. The default is False.
|
|
1097
|
+
|
|
1098
|
+
Returns:
|
|
1099
|
+
bool: True if the download was successful, False otherwise.
|
|
1100
|
+
|
|
1101
|
+
"""
|
|
1102
|
+
return False
|
|
1103
|
+
|
|
1104
|
+
def download_enabled(self) -> bool:
|
|
1105
|
+
""" Return True if downloading is enabled. Default is True. """
|
|
1106
|
+
return True
|
|
1107
|
+
|
|
1108
|
+
def upload(self, artifact: Artifact, force: bool = False) -> bool:
|
|
1109
|
+
"""
|
|
1110
|
+
Upload an artifact to the storage location.
|
|
1111
|
+
|
|
1112
|
+
The artifact to be uploaded is located at the path returned by
|
|
1113
|
+
the artifact's :func:`~jolt.Artifact.get_archive_path` method. The
|
|
1114
|
+
uploaded artifact is in the format specified by DEFAULT_ARCHIVE_TYPE.
|
|
1115
|
+
The provider may choose to upload the artifact using a different
|
|
1116
|
+
format, but it must be able to download the artifact in the
|
|
1117
|
+
DEFAULT_ARCHIVE_TYPE format.
|
|
1118
|
+
|
|
1119
|
+
The upload should be retried if it fails due to network issues.
|
|
1120
|
+
The method may raise an exception on errors.
|
|
1121
|
+
|
|
1122
|
+
Args:
|
|
1123
|
+
artifact (Artifact): The artifact to upload.
|
|
1124
|
+
force (bool, optional): If True, the upload should be forced,
|
|
1125
|
+
even if the artifact is already present remotely, or if the
|
|
1126
|
+
upload is disabled. The default is False.
|
|
1127
|
+
|
|
1128
|
+
Returns:
|
|
1129
|
+
bool: True if the upload was successful, False otherwise.
|
|
1130
|
+
|
|
1131
|
+
"""
|
|
1132
|
+
return False
|
|
1133
|
+
|
|
1134
|
+
def upload_enabled(self) -> bool:
|
|
1135
|
+
""" Return True if uploading is enabled. Default is True. """
|
|
1136
|
+
return True
|
|
1137
|
+
|
|
1138
|
+
def location(self, artifact) -> str:
|
|
1139
|
+
"""
|
|
1140
|
+
Return the URL of the artifact in the storage location.
|
|
1141
|
+
|
|
1142
|
+
This method is sometimes used to identify if an artifact is
|
|
1143
|
+
present in the storage location. The URL should point to the
|
|
1144
|
+
artifact if present, or an empty string if the artifact is
|
|
1145
|
+
absent.
|
|
1146
|
+
|
|
1147
|
+
Args:
|
|
1148
|
+
artifact (Artifact): The artifact to locate.
|
|
1149
|
+
"""
|
|
1150
|
+
return '' # URL
|
|
1151
|
+
|
|
1152
|
+
def availability(self, artifacts: list) -> tuple:
|
|
1153
|
+
"""
|
|
1154
|
+
Check the availability of a list of artifacts.
|
|
1155
|
+
|
|
1156
|
+
This method is used to determine which artifacts are present in the
|
|
1157
|
+
storage location. The method should return a tuple of two lists:
|
|
1158
|
+
the first list contains the artifacts that are present, and the
|
|
1159
|
+
second list contains the artifacts that are missing.
|
|
1160
|
+
|
|
1161
|
+
The default implementation of this method calls the :func:`~jolt.StorageProvider.location`
|
|
1162
|
+
method for each artifact in the list. Subclasses may override this
|
|
1163
|
+
method to provide a more efficient implementation.
|
|
1164
|
+
|
|
1165
|
+
Args:
|
|
1166
|
+
artifacts (list): A list of artifacts to check.
|
|
1167
|
+
|
|
1168
|
+
Returns:
|
|
1169
|
+
tuple: A tuple of two lists: the first list contains the artifacts
|
|
1170
|
+
that are present, and the second list contains the artifacts
|
|
1171
|
+
that are missing.
|
|
1172
|
+
|
|
1173
|
+
"""
|
|
1174
|
+
# Ensure artifacts is a list
|
|
1175
|
+
artifacts = utils.as_list(artifacts)
|
|
1176
|
+
|
|
1177
|
+
present = set()
|
|
1178
|
+
missing = set()
|
|
1179
|
+
|
|
1180
|
+
for artifact in artifacts:
|
|
1181
|
+
if self.location(artifact):
|
|
1182
|
+
present.add(artifact)
|
|
1183
|
+
else:
|
|
1184
|
+
missing.add(artifact)
|
|
1185
|
+
|
|
1186
|
+
return list(present), list(missing)
|
|
1187
|
+
|
|
1188
|
+
|
|
1189
|
+
class StorageProviderFactory(StorageProvider):
|
|
1190
|
+
""" A factory for store providers. """
|
|
1191
|
+
|
|
1192
|
+
def create(self) -> StorageProvider:
|
|
1193
|
+
"""
|
|
1194
|
+
Create a new storage provider.
|
|
1195
|
+
|
|
1196
|
+
This method should return a new instance of a storage provider,
|
|
1197
|
+
which must be a subclass of :class:`~jolt.StorageProvider`.
|
|
1198
|
+
|
|
1199
|
+
"""
|
|
1200
|
+
pass
|
|
1201
|
+
|
|
1202
|
+
|
|
1203
|
+
def RegisterStorage(cls):
|
|
1204
|
+
""" Decorator used to register a storage provider factory. """
|
|
1205
|
+
ArtifactCache.storage_provider_factories.append(cls)
|
|
1206
|
+
|
|
1207
|
+
|
|
1028
1208
|
@utils.Singleton
|
|
1029
1209
|
class ArtifactCache(StorageProvider):
|
|
1030
1210
|
"""
|