jolt 0.9.354__py3-none-any.whl → 0.9.370__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.
jolt/cli.py CHANGED
@@ -24,7 +24,6 @@ from jolt import utils
24
24
  from jolt.influence import HashInfluenceRegistry
25
25
  from jolt.options import JoltOptions
26
26
  from jolt import hooks
27
- from jolt.manifest import JoltManifest
28
27
  from jolt.error import JoltError
29
28
  from jolt.error import raise_error
30
29
  from jolt.error import raise_error_if
@@ -156,23 +155,9 @@ def cli(ctx, verbose, config_file, debugger, profile,
156
155
  print(ctx.get_help())
157
156
  sys.exit(0)
158
157
 
159
- manifest = JoltManifest()
160
- utils.call_and_catch(manifest.parse)
161
- manifest.process_import()
162
- ctx.obj["manifest"] = manifest
163
-
164
- if manifest.version:
165
- from jolt.version_utils import requirement, version
166
- req = requirement(manifest.version)
167
- ver = version(__version__)
168
- raise_error_if(not req.satisfied(ver),
169
- "This project requires Jolt version {} (running {})",
170
- req, __version__)
171
-
158
+ registry = TaskRegistry.get()
172
159
  loader = JoltLoader.get()
173
- tasks = loader.load()
174
- for cls in tasks:
175
- TaskRegistry.get().add_task_class(cls)
160
+ loader.load(registry)
176
161
 
177
162
  if ctx.invoked_subcommand in ["build", "clean"] and loader.joltdir:
178
163
  ctx.obj["workspace_lock"] = utils.LockFile(
@@ -185,7 +170,7 @@ def cli(ctx, verbose, config_file, debugger, profile,
185
170
  if ctx.invoked_subcommand is None:
186
171
  task = config.get("jolt", "default", "default")
187
172
  taskname, _ = utils.parse_task_name(task)
188
- if TaskRegistry.get().get_task_class(taskname) is not None:
173
+ if registry.get_task_class(taskname) is not None:
189
174
  ctx.invoke(build, task=[task], force=force, salt=salt, debug=debug,
190
175
  network=network, local=local, keep_going=keep_going, jobs=jobs)
191
176
  else:
@@ -194,11 +179,8 @@ def cli(ctx, verbose, config_file, debugger, profile,
194
179
 
195
180
 
196
181
  def _autocomplete_tasks(ctx, args, incomplete):
197
- manifest = JoltManifest()
198
- utils.call_and_catch(manifest.parse)
199
- manifest.process_import()
200
-
201
- tasks = JoltLoader.get().load()
182
+ loader = JoltLoader.get()
183
+ tasks = loader.load()
202
184
  tasks = [task.name for task in tasks if task.name.startswith(incomplete or '')]
203
185
  return sorted(tasks)
204
186
 
@@ -356,26 +338,20 @@ def build(ctx, task, network, keep_going, default, local,
356
338
  for params in default:
357
339
  registry.set_default_parameters(params)
358
340
 
359
- manifest = ctx.obj["manifest"]
360
-
361
- for mb in manifest.builds:
362
- for mt in mb.tasks:
363
- task.append(mt.name)
364
- for mt in mb.defaults:
365
- registry.set_default_parameters(mt.name)
366
-
367
- if force:
368
- for goal in task:
369
- registry.get_task(goal, manifest=manifest).taint = uuid.uuid4()
370
-
371
341
  log.info("Started: {}", datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
372
342
 
373
- gb = graph.GraphBuilder(registry, acache, manifest, options, progress=True)
343
+ gb = graph.GraphBuilder(registry, acache, options, progress=True)
374
344
  dag = gb.build(task)
375
345
 
346
+ # If asked to force rebuild, taint all goal tasks
347
+ if force:
348
+ for goal in dag.goals:
349
+ goal.taint()
350
+
376
351
  # Collect information about artifact presence before starting prune or build
377
352
  acache.precheck(dag.persistent_artifacts, remote=not local)
378
353
 
354
+ # Prune the graph to remove tasks that are already available locally or remotely
379
355
  if not no_prune:
380
356
  gp = graph.GraphPruner(acache, strategy)
381
357
  dag = gp.prune(dag)
@@ -384,7 +360,7 @@ def build(ctx, task, network, keep_going, default, local,
384
360
  goal_task_duration = 0
385
361
 
386
362
  session = executors.create_session(dag) if options.network else {}
387
- queue = scheduler.TaskQueue(strategy, acache, session)
363
+ queue = scheduler.TaskQueue()
388
364
 
389
365
  try:
390
366
  if not dag.has_tasks():
@@ -407,7 +383,8 @@ def build(ctx, task, network, keep_going, default, local,
407
383
 
408
384
  while leafs:
409
385
  task = leafs.pop()
410
- queue.submit(task)
386
+ executor = strategy.create_executor(session, task)
387
+ queue.submit(executor)
411
388
 
412
389
  task, error = queue.wait()
413
390
 
@@ -430,6 +407,7 @@ def build(ctx, task, network, keep_going, default, local,
430
407
 
431
408
  if not keep_going and error is not None:
432
409
  queue.abort()
410
+ executors.shutdown()
433
411
  task.raise_for_status()
434
412
  raise error
435
413
 
@@ -448,6 +426,7 @@ def build(ctx, task, network, keep_going, default, local,
448
426
  log.warning("Interrupted by user")
449
427
  try:
450
428
  queue.abort()
429
+ executors.shutdown()
451
430
  sys.exit(1)
452
431
  except KeyboardInterrupt:
453
432
  print()
@@ -502,7 +481,7 @@ def clean(ctx, task, deps, expired):
502
481
  if task:
503
482
  task = [utils.stable_task_name(t) for t in task]
504
483
  registry = TaskRegistry.get()
505
- dag = graph.GraphBuilder(registry, acache, ctx.obj["manifest"]).build(task)
484
+ dag = graph.GraphBuilder(registry, acache).build(task)
506
485
  if deps:
507
486
  tasks = dag.tasks
508
487
  else:
@@ -649,7 +628,7 @@ def display(ctx, task, reverse=None, show_cache=False, prune=False):
649
628
  registry = TaskRegistry.get()
650
629
  options = JoltOptions()
651
630
  acache = cache.ArtifactCache.get(options)
652
- gb = graph.GraphBuilder(registry, acache, ctx.obj["manifest"])
631
+ gb = graph.GraphBuilder(registry, acache)
653
632
  dag = gb.build(task, influence=show_cache)
654
633
 
655
634
  if reverse:
@@ -746,15 +725,14 @@ def download(ctx, task, deps, copy, copy_all):
746
725
  if copy_all:
747
726
  deps = True
748
727
 
749
- manifest = ctx.obj["manifest"]
750
728
  options = JoltOptions()
751
729
  acache = cache.ArtifactCache.get(options)
752
730
  hooks.TaskHookRegistry.get(options)
753
731
  executors = scheduler.ExecutorRegistry.get(options)
754
732
  registry = TaskRegistry.get()
755
733
  strategy = scheduler.DownloadStrategy(executors, acache)
756
- queue = scheduler.TaskQueue(strategy, acache, {})
757
- gb = graph.GraphBuilder(registry, acache, manifest, options, progress=True)
734
+ queue = scheduler.TaskQueue()
735
+ gb = graph.GraphBuilder(registry, acache, options, progress=True)
758
736
  dag = gb.build(task)
759
737
 
760
738
  if not deps:
@@ -772,7 +750,8 @@ def download(ctx, task, deps, copy, copy_all):
772
750
 
773
751
  while leafs:
774
752
  task = leafs.pop()
775
- queue.submit(task)
753
+ executor = strategy.create_executor({}, task)
754
+ queue.submit(executor)
776
755
 
777
756
  task, error = queue.wait()
778
757
  p.update(1)
@@ -795,6 +774,7 @@ def download(ctx, task, deps, copy, copy_all):
795
774
  log.warning("Interrupted by user")
796
775
  try:
797
776
  queue.abort()
777
+ executors.shutdown()
798
778
  sys.exit(1)
799
779
  except KeyboardInterrupt:
800
780
  print()
@@ -809,54 +789,6 @@ def download(ctx, task, deps, copy, copy_all):
809
789
  queue.shutdown()
810
790
 
811
791
 
812
- @cli.command(hidden=True)
813
- @click.argument("task", type=str, nargs=-1, required=True)
814
- @click.option("-r", "--remove", is_flag=True, help="Remove tasks from existing manifest.")
815
- @click.option("-d", "--default", type=str, multiple=True, help="Override default parameter values.")
816
- @click.option("-o", "--output", type=str, default="default.joltxmanifest", help="Manifest filename.")
817
- @click.pass_context
818
- def freeze(ctx, task, default, output, remove):
819
- """
820
- Freeze the identity of a task.
821
-
822
- <WIP>
823
- """
824
- manifest = ctx.obj["manifest"]
825
-
826
- options = JoltOptions(default=default)
827
- acache = cache.ArtifactCache.get(options)
828
- scheduler.ExecutorRegistry.get(options)
829
- registry = TaskRegistry.get()
830
-
831
- for params in default:
832
- registry.set_default_parameters(params)
833
-
834
- gb = graph.GraphBuilder(registry, acache, manifest)
835
- dag = gb.build(task)
836
-
837
- available, missing = acache.availability(dag.persistent_artifacts)
838
-
839
- for artifact in missing:
840
- raise_task_error_if(
841
- not remove, artifact.get_task(),
842
- "Task artifact is not available in any cache, build it first")
843
-
844
- for task in dag.tasks:
845
- if task.is_resource() or not task.is_cacheable():
846
- continue
847
- manifest_task = manifest.find_task(task)
848
- if remove and manifest_task:
849
- manifest.remove_task(manifest_task)
850
- continue
851
- if not remove:
852
- if not manifest_task:
853
- manifest_task = manifest.create_task()
854
- manifest_task.name = task.qualified_name
855
- manifest_task.identity = task.identity
856
-
857
- manifest.write(fs.path.join(JoltLoader.get().joltdir, output))
858
-
859
-
860
792
  @cli.command(name="list")
861
793
  @click.argument("task", type=str, nargs=-1, required=False, shell_complete=_autocomplete_tasks)
862
794
  @click.option("-a", "--all", is_flag=True, help="List all direct and indirect dependencies of TASK.")
@@ -892,7 +824,7 @@ def _list(ctx, task=None, all=False, reverse=None):
892
824
  reverse = [utils.stable_task_name(t) for t in utils.as_list(reverse or [])]
893
825
 
894
826
  try:
895
- dag = graph.GraphBuilder(registry, acache, ctx.obj["manifest"]).build(task, influence=False)
827
+ dag = graph.GraphBuilder(registry, acache).build(task, influence=False)
896
828
  except JoltError as e:
897
829
  raise e
898
830
  except Exception:
@@ -992,9 +924,8 @@ def inspect(ctx, task, influence=False, artifact=False, salt=None):
992
924
 
993
925
  print()
994
926
  print(" Requirements")
995
- manifest = ctx.obj["manifest"]
996
927
  try:
997
- task = task_registry.get_task(task_name, manifest=manifest)
928
+ task = task_registry.get_task(task_name)
998
929
  for req in sorted(utils.as_list(utils.call_or_return(task, task.requires))):
999
930
  print(" {0}".format(task.tools.expand(req)))
1000
931
  if not task.requires:
@@ -1016,7 +947,7 @@ def inspect(ctx, task, influence=False, artifact=False, salt=None):
1016
947
  if artifact:
1017
948
  options = JoltOptions(salt=salt)
1018
949
  acache = cache.ArtifactCache.get()
1019
- builder = graph.GraphBuilder(task_registry, acache, manifest, options)
950
+ builder = graph.GraphBuilder(task_registry, acache, options)
1020
951
  dag = builder.build([task.qualified_name])
1021
952
  tasks = dag.select(lambda graph, node: node.task is task)
1022
953
  assert len(tasks) == 1, "graph produced multiple tasks, one expected"
@@ -1080,7 +1011,7 @@ def _export(ctx, task):
1080
1011
  executors = scheduler.ExecutorRegistry.get()
1081
1012
  strategy = scheduler.LocalStrategy(executors, acache)
1082
1013
 
1083
- dag = graph.GraphBuilder(registry, acache, ctx.obj["manifest"])
1014
+ dag = graph.GraphBuilder(registry, acache)
1084
1015
  dag = dag.build(task)
1085
1016
 
1086
1017
  gp = graph.GraphPruner(acache, strategy)
@@ -1110,7 +1041,7 @@ def _export(ctx, task):
1110
1041
  for task in context.tasks:
1111
1042
  for artifact in task.artifacts:
1112
1043
  raise_task_error_if(
1113
- artifact.is_temporary(), task,
1044
+ not task.is_resource() and artifact.is_temporary(), task,
1114
1045
  "Task artifact not found in local cache, build it first")
1115
1046
 
1116
1047
  visitor = Export()
jolt/config.py CHANGED
@@ -7,7 +7,6 @@ from jolt import common_pb2 as common_pb
7
7
  from jolt import filesystem as fs
8
8
  from jolt import utils
9
9
  from jolt.error import raise_error_if
10
- from jolt.manifest import ManifestExtension, ManifestExtensionRegistry
11
10
 
12
11
 
13
12
  _workdir = os.getcwd()
@@ -318,39 +317,28 @@ def split(string):
318
317
  return section, key
319
318
 
320
319
 
321
- class ConfigExtension(ManifestExtension):
322
- def export_manifest(self, manifest, _):
323
- manifest.config = get("network", "config", "", expand=False)
324
-
325
- for key, value in options("params"):
326
- p = manifest.create_parameter()
327
- p.key = "config." + key
328
- p.value = value
329
-
330
- def import_manifest(self, manifest):
331
- if manifest.config:
332
- _manifest.read_string(manifest.config)
333
- from jolt.loader import JoltLoader
334
- JoltLoader.get().load_plugins()
335
-
336
- for param in manifest.parameters:
337
- if param.key.startswith("config."):
338
- set("params", param.key.split(".", 1)[1], param.value)
339
-
340
- def import_protobuf(self, pb):
341
- self.import_manifest(pb)
320
+ def import_config(snippet: str):
321
+ """ Apply extra configuration for the worker, provided by the client. """
322
+ _manifest.read_string(snippet)
323
+ from jolt.loader import JoltLoader
324
+ JoltLoader.get().load_plugins()
342
325
 
343
326
 
344
327
  def export_config():
328
+ """ Get extra configuration for the worker. """
345
329
  return get("network", "config", "", expand=False)
346
330
 
347
331
 
332
+ def import_params(params: dict):
333
+ """ Apply user-defined parameters (-c params.key=value). """
334
+ for key, value in params.items():
335
+ if key.startswith("config."):
336
+ set("params", key.split(".", 1)[1], value)
337
+
338
+
348
339
  def export_params():
340
+ """ Get user-defined parameters (-c params.key=value). """
349
341
  parameters = []
350
342
  for key, value in options("params"):
351
343
  parameters.append(common_pb.Property(key="config." + key, value=value))
352
344
  return parameters
353
-
354
-
355
- # High priority so that plugins are loaded before resources are acquired.
356
- ManifestExtensionRegistry.add(ConfigExtension(), -10)
jolt/graph.py CHANGED
@@ -424,9 +424,8 @@ class TaskProxy(object):
424
424
  self._owner = owner
425
425
  self.task.exported_name = f"{self.short_qualified_name}@@{owner.short_qualified_name}"
426
426
 
427
- def finalize(self, dag, manifest):
427
+ def finalize(self, dag):
428
428
  log.debug("Finalizing: " + self.short_qualified_name)
429
- self.manifest = manifest
430
429
 
431
430
  # Find all direct and transitive dependencies
432
431
  self.ancestors = set()
@@ -468,11 +467,23 @@ class TaskProxy(object):
468
467
  self._artifacts.extend(self.task._artifacts(self.cache, self))
469
468
 
470
469
  def taint(self, salt=None):
471
- self.task.taint = self.task.taint or salt or uuid.uuid4()
470
+ self.task.taint = salt or uuid.uuid4()
472
471
  if salt is None:
473
472
  # Only recalculate identity when build is forced, not when salted
474
473
  self.identity = None
475
474
  self.identity
475
+ # Recreate artifacts
476
+ self._artifacts = []
477
+ self.finalize_artifacts()
478
+
479
+ # If this is an alias, taint all children
480
+ if self.is_alias():
481
+ for child in self.children:
482
+ child.taint()
483
+
484
+ # Taint all extensions
485
+ for extension in self.extensions:
486
+ extension.taint()
476
487
 
477
488
  def queued(self, remote=True):
478
489
  self.task.verbose("Task queued " + self.log_name)
@@ -749,7 +760,10 @@ class TaskProxy(object):
749
760
  try:
750
761
  # Acquire resource dependencies in reverse order.
751
762
  for resource in reversed(resource_deps):
752
- with resource.lock_artifacts(discard=False) if not resource.is_workspace_resource() else nullcontext():
763
+ # Always discard resource artifacts before acquiring the resource.
764
+ # They should not exist in the cache when the resource is acquired,
765
+ # but may exist if the resource was previously acquired by an interrupted build.
766
+ with resource.lock_artifacts(discard=True) if not resource.is_workspace_resource() else nullcontext():
753
767
  resource.deps = self.cache.get_context(resource)
754
768
  exitstack.enter_context(resource.deps)
755
769
 
@@ -779,7 +793,6 @@ class TaskProxy(object):
779
793
  self._run_task(env, force_upload, force_build)
780
794
 
781
795
  def _run_task(self, env, force_upload=False, force_build=False):
782
- cache = env.cache
783
796
  queue = env.queue
784
797
 
785
798
  with self.tools:
@@ -793,7 +806,7 @@ class TaskProxy(object):
793
806
  self.skipped()
794
807
  return
795
808
 
796
- available_remotely = cache.download_enabled() and self.is_available_remotely()
809
+ available_remotely = self.cache.download_enabled() and self.is_available_remotely()
797
810
  if not available_locally and available_remotely:
798
811
  available_locally = self.download()
799
812
 
@@ -812,7 +825,7 @@ class TaskProxy(object):
812
825
  upload_session_artifacts = False
813
826
 
814
827
  try:
815
- context = cache.get_context(self)
828
+ context = self.cache.get_context(self)
816
829
  exitstack.enter_context(context)
817
830
 
818
831
  self.running_execution()
@@ -881,7 +894,7 @@ class TaskProxy(object):
881
894
  if force_upload or force_build or not available_remotely:
882
895
  raise_task_error_if(
883
896
  not self.upload(force=force_upload, locked=False, persistent_only=True) \
884
- and cache.upload_enabled(),
897
+ and self.cache.upload_enabled(),
885
898
  self, "Failed to upload task artifact")
886
899
 
887
900
  finally:
@@ -889,14 +902,14 @@ class TaskProxy(object):
889
902
  raise_task_error_if(
890
903
  upload_session_artifacts \
891
904
  and not self.upload(force=force_upload, locked=False, session_only=True, artifacts=upload_session_artifacts) \
892
- and cache.upload_enabled(),
905
+ and self.cache.upload_enabled(),
893
906
  self, "Failed to upload session artifact")
894
907
 
895
908
  elif force_upload or not available_remotely:
896
909
  self.started_upload()
897
910
  raise_task_error_if(
898
911
  not self.upload(force=force_upload, persistent_only=True) \
899
- and cache.upload_enabled(),
912
+ and self.cache.upload_enabled(),
900
913
  self, "Failed to upload task artifact")
901
914
  self.finished_upload()
902
915
 
@@ -1091,12 +1104,11 @@ class Graph(object):
1091
1104
 
1092
1105
 
1093
1106
  class GraphBuilder(object):
1094
- def __init__(self, registry, cache, manifest=None, options=None, progress=False, buildenv=None):
1107
+ def __init__(self, registry, cache, options=None, progress=False, buildenv=None):
1095
1108
  self.cache = cache
1096
1109
  self.graph = Graph()
1097
1110
  self.nodes = {}
1098
1111
  self.registry = registry
1099
- self.manifest = manifest
1100
1112
  self.buildenv = buildenv
1101
1113
  self.progress = progress
1102
1114
  self.options = options or JoltOptions()
@@ -1105,7 +1117,7 @@ class GraphBuilder(object):
1105
1117
  name = utils.stable_task_name(name)
1106
1118
  node = self.nodes.get(name)
1107
1119
  if not node:
1108
- task = self.registry.get_task(name, manifest=self.manifest, buildenv=self.buildenv)
1120
+ task = self.registry.get_task(name, buildenv=self.buildenv)
1109
1121
  node = self.nodes.get(task.qualified_name, None)
1110
1122
  if node is not None:
1111
1123
  return node
@@ -1137,7 +1149,7 @@ class GraphBuilder(object):
1137
1149
  parent = node
1138
1150
 
1139
1151
  for requirement in node.task.requires:
1140
- alias, artifact, task, name = utils.parse_aliased_task_name(requirement)
1152
+ alias, _, task, name = utils.parse_aliased_task_name(requirement)
1141
1153
  child = self._get_node(progress, utils.format_task_name(task, name), parent=node)
1142
1154
 
1143
1155
  # Create direct edges from alias parents to alias children
@@ -1172,7 +1184,7 @@ class GraphBuilder(object):
1172
1184
  topological_nodes = self.graph.topological_nodes
1173
1185
  with self._progress("Collecting task influence", len(self.graph.tasks), "tasks") as p:
1174
1186
  for node in reversed(topological_nodes):
1175
- node.finalize(self.graph, self.manifest)
1187
+ node.finalize(self.graph)
1176
1188
  p.update(1)
1177
1189
 
1178
1190
  # Create artifacts in forward order so that parent identities are available