looper 1.7.1__py3-none-any.whl → 1.8.0__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.
looper/conductor.py CHANGED
@@ -21,12 +21,13 @@ from peppy.exceptions import RemoteYAMLError
21
21
  from pipestat import PipestatError
22
22
  from ubiquerg import expandpath, is_command_callable
23
23
  from yaml import dump
24
- from yacman import YAMLConfigManager
24
+ from yacman import FutureYAMLConfigManager as YAMLConfigManager
25
25
 
26
26
  from .const import *
27
27
  from .exceptions import JobSubmissionException, SampleFailedException
28
28
  from .processed_project import populate_sample_paths
29
29
  from .utils import fetch_sample_flags, jinja_render_template_strictly
30
+ from .const import PipelineLevel
30
31
 
31
32
 
32
33
  _LOGGER = logging.getLogger(__name__)
@@ -85,11 +86,23 @@ def _get_yaml_path(namespaces, template_key, default_name_appendix="", filename=
85
86
 
86
87
  def write_pipestat_config(looper_pipestat_config_path, pipestat_config_dict):
87
88
  """
88
- This is run at the project level, not at the sample level.
89
+ This writes a combined configuration file to be passed to a PipestatManager.
90
+ :param str looper_pipestat_config_path: path to the created pipestat configuration file
91
+ :param dict pipestat_config_dict: the dict containing key value pairs to be written to the pipestat configutation
92
+ return bool
89
93
  """
94
+
95
+ if not os.path.exists(os.path.dirname(looper_pipestat_config_path)):
96
+ try:
97
+ os.makedirs(os.path.dirname(looper_pipestat_config_path))
98
+ except FileExistsError:
99
+ pass
100
+
90
101
  with open(looper_pipestat_config_path, "w") as f:
91
102
  yaml.dump(pipestat_config_dict, f)
92
- print(f"Initialized looper config file: {looper_pipestat_config_path}")
103
+ _LOGGER.debug(
104
+ msg=f"Initialized pipestat config file: {looper_pipestat_config_path}"
105
+ )
93
106
 
94
107
  return True
95
108
 
@@ -261,8 +274,12 @@ class SubmissionConductor(object):
261
274
 
262
275
  :param bool frorce: whether to force the project submission (ignore status/flags)
263
276
  """
277
+ psms = {}
264
278
  if self.prj.pipestat_configured_project:
265
- psm = self.prj.get_pipestat_managers(project_level=True)[self.pl_name]
279
+ for piface in self.prj.project_pipeline_interfaces:
280
+ if piface.psm.pipeline_type == PipelineLevel.PROJECT.value:
281
+ psms[piface.psm.pipeline_name] = piface.psm
282
+ psm = psms[self.pl_name]
266
283
  status = psm.get_status()
267
284
  if not force and status is not None:
268
285
  _LOGGER.info(f"> Skipping project. Determined status: {status}")
@@ -288,12 +305,11 @@ class SubmissionConductor(object):
288
305
  )
289
306
  )
290
307
  if self.prj.pipestat_configured:
291
- psms = self.prj.get_pipestat_managers(sample_name=sample.sample_name)
292
- sample_statuses = psms[self.pl_name].get_status(
308
+ sample_statuses = self.pl_iface.psm.get_status(
293
309
  record_identifier=sample.sample_name
294
310
  )
295
311
  if sample_statuses == "failed" and rerun is True:
296
- psms[self.pl_name].set_status(
312
+ self.pl_iface.psm.set_status(
297
313
  record_identifier=sample.sample_name, status_identifier="waiting"
298
314
  )
299
315
  sample_statuses = "waiting"
@@ -303,23 +319,27 @@ class SubmissionConductor(object):
303
319
 
304
320
  use_this_sample = True # default to running this sample
305
321
  msg = None
322
+ if rerun and sample_statuses == []:
323
+ msg = f"> Skipping sample because rerun requested, but no failed or waiting flag found."
324
+ use_this_sample = False
306
325
  if sample_statuses:
307
326
  status_str = ", ".join(sample_statuses)
308
327
  failed_flag = any("failed" in x for x in sample_statuses)
328
+ waiting_flag = any("waiting" in x for x in sample_statuses)
309
329
  if self.ignore_flags:
310
330
  msg = f"> Found existing status: {status_str}. Ignoring."
311
331
  else: # this pipeline already has a status
312
332
  msg = f"> Found existing status: {status_str}. Skipping sample."
313
- if failed_flag:
333
+ if failed_flag and not rerun:
314
334
  msg += " Use rerun to ignore failed status." # help guidance
315
335
  use_this_sample = False
316
336
  if rerun:
317
337
  # Rescue the sample if rerun requested, and failed flag is found
318
- if failed_flag:
319
- msg = f"> Re-running failed sample. Status: {status_str}"
338
+ if failed_flag or waiting_flag:
339
+ msg = f"> Re-running sample. Status: {status_str}"
320
340
  use_this_sample = True
321
341
  else:
322
- msg = f"> Skipping sample because rerun requested, but no failed flag found. Status: {status_str}"
342
+ msg = f"> Skipping sample because rerun requested, but no failed or waiting flag found. Status: {status_str}"
323
343
  use_this_sample = False
324
344
  if msg:
325
345
  _LOGGER.info(msg)
@@ -528,12 +548,7 @@ class SubmissionConductor(object):
528
548
  :return yacman.YAMLConfigManager: pipestat namespace
529
549
  """
530
550
  try:
531
- psms = (
532
- self.prj.get_pipestat_managers(sample_name)
533
- if sample_name
534
- else self.prj.get_pipestat_managers(project_level=True)
535
- )
536
- psm = psms[self.pl_iface.pipeline_name]
551
+ psm = self.pl_iface.psm
537
552
  except (PipestatError, AttributeError) as e:
538
553
  # pipestat section faulty or not found in project.looper or sample
539
554
  # or project is missing required pipestat attributes
@@ -621,7 +636,6 @@ class SubmissionConductor(object):
621
636
  argstring = jinja_render_template_strictly(
622
637
  template=templ, namespaces=namespaces
623
638
  )
624
- print(argstring)
625
639
  except UndefinedError as jinja_exception:
626
640
  _LOGGER.warning(NOT_SUB_MSG.format(str(jinja_exception)))
627
641
  except KeyError as e:
looper/const.py CHANGED
@@ -1,6 +1,7 @@
1
1
  """ Shared project constants """
2
2
 
3
3
  import os
4
+ from enum import Enum
4
5
 
5
6
  __author__ = "Databio lab"
6
7
  __email__ = "nathan@code.databio.org"
@@ -268,3 +269,10 @@ MESSAGE_BY_SUBCOMMAND = {
268
269
  "init-piface": "Initialize generic pipeline interface.",
269
270
  "link": "Create directory of symlinks for reported results.",
270
271
  }
272
+
273
+ # Add project/sample enum
274
+
275
+
276
+ class PipelineLevel(Enum):
277
+ SAMPLE = "sample"
278
+ PROJECT = "project"
looper/divvy.py CHANGED
@@ -6,11 +6,14 @@ import os
6
6
  import sys
7
7
  import shutil
8
8
  import yaml
9
- from yaml import SafeLoader
10
- from shutil import copytree
11
9
 
10
+
11
+ from shutil import copytree
12
+ from yacman import FutureYAMLConfigManager as YAMLConfigManager
13
+ from yacman import write_lock, FILEPATH_KEY, load_yaml, select_config
14
+ from yaml import SafeLoader
12
15
  from ubiquerg import is_writable, VersionInHelpParser
13
- import yacman
16
+
14
17
 
15
18
  from .const import (
16
19
  COMPUTE_SETTINGS_VARNAME,
@@ -28,7 +31,7 @@ _LOGGER = logging.getLogger(__name__)
28
31
  # This is the divvy.py submodule from divvy
29
32
 
30
33
 
31
- class ComputingConfiguration(yacman.YAMLConfigManager):
34
+ class ComputingConfiguration(YAMLConfigManager):
32
35
  """
33
36
  Represents computing configuration objects.
34
37
 
@@ -44,36 +47,31 @@ class ComputingConfiguration(yacman.YAMLConfigManager):
44
47
  `DIVCFG` file)
45
48
  """
46
49
 
47
- def __init__(self, entries=None, filepath=None):
48
- if not entries and not filepath:
49
- # Handle the case of an empty one, when we'll use the default
50
- filepath = select_divvy_config(None)
51
-
52
- super(ComputingConfiguration, self).__init__(
53
- entries=entries,
54
- filepath=filepath,
55
- schema_source=DEFAULT_CONFIG_SCHEMA,
56
- validate_on_write=True,
50
+ def __init__(
51
+ self,
52
+ entries=None,
53
+ wait_max=None,
54
+ strict_ro_locks=False,
55
+ schema_source=None,
56
+ validate_on_write=False,
57
+ ):
58
+ super().__init__(
59
+ entries, wait_max, strict_ro_locks, schema_source, validate_on_write
57
60
  )
58
61
 
59
- if not "compute_packages" in self:
60
- raise Exception(
61
- "Your divvy config file is not in divvy config format "
62
- "(it lacks a compute_packages section): '{}'".format(filepath)
63
- )
64
- # We require that compute_packages be present, even if empty
62
+ if "compute_packages" not in self:
65
63
  self["compute_packages"] = {}
66
-
67
64
  # Initialize default compute settings.
68
65
  _LOGGER.debug("Establishing project compute settings")
69
66
  self.compute = None
70
67
  self.setdefault("adapters", None)
71
68
  self.activate_package(DEFAULT_COMPUTE_RESOURCES_NAME)
72
- self.config_file = self.filepath
73
69
 
74
70
  def write(self, filename=None):
75
- super(ComputingConfiguration, self).write(filepath=filename, exclude_case=True)
76
- filename = filename or getattr(self, yacman.FILEPATH_KEY)
71
+ with write_lock(self) as locked_ym:
72
+ locked_ym.rebase()
73
+ locked_ym.write()
74
+ filename = filename or getattr(self, FILEPATH_KEY)
77
75
  filedir = os.path.dirname(filename)
78
76
  # For this object, we *also* have to write the template files
79
77
  for pkg_name, pkg in self["compute_packages"].items():
@@ -151,12 +149,12 @@ class ComputingConfiguration(yacman.YAMLConfigManager):
151
149
  # Augment compute, creating it if needed.
152
150
  if self.compute is None:
153
151
  _LOGGER.debug("Creating Project compute")
154
- self.compute = yacman.YAMLConfigManager()
152
+ self.compute = YAMLConfigManager()
155
153
  _LOGGER.debug(
156
154
  "Adding entries for package_name '{}'".format(package_name)
157
155
  )
158
156
 
159
- self.compute.update(self["compute_packages"][package_name])
157
+ self.compute.update_from_obj(self["compute_packages"][package_name])
160
158
 
161
159
  # Ensure submission template is absolute. This *used to be* handled
162
160
  # at update (so the paths were stored as absolutes in the packages),
@@ -165,7 +163,7 @@ class ComputingConfiguration(yacman.YAMLConfigManager):
165
163
  if not os.path.isabs(self.compute["submission_template"]):
166
164
  try:
167
165
  self.compute["submission_template"] = os.path.join(
168
- os.path.dirname(self.filepath),
166
+ os.path.dirname(self.default_config_file),
169
167
  self.compute["submission_template"],
170
168
  )
171
169
  except AttributeError as e:
@@ -200,11 +198,11 @@ class ComputingConfiguration(yacman.YAMLConfigManager):
200
198
  self.reset_active_settings()
201
199
  return self.activate_package(package_name)
202
200
 
203
- def get_active_package(self):
201
+ def get_active_package(self) -> YAMLConfigManager:
204
202
  """
205
203
  Returns settings for the currently active compute package
206
204
 
207
- :return yacman.YacAttMap: data defining the active compute package
205
+ :return YAMLConfigManager: data defining the active compute package
208
206
  """
209
207
  return self.compute
210
208
 
@@ -222,7 +220,7 @@ class ComputingConfiguration(yacman.YAMLConfigManager):
222
220
 
223
221
  :return bool: success flag
224
222
  """
225
- self.compute = yacman.YacAttMap()
223
+ self.compute = YAMLConfigManager()
226
224
  return True
227
225
 
228
226
  def update_packages(self, config_file):
@@ -235,11 +233,11 @@ class ComputingConfiguration(yacman.YAMLConfigManager):
235
233
 
236
234
  :param str config_file: path to file with new divvy configuration data
237
235
  """
238
- entries = yacman.load_yaml(config_file)
236
+ entries = load_yaml(config_file)
239
237
  self.update(entries)
240
238
  return True
241
239
 
242
- def get_adapters(self):
240
+ def get_adapters(self) -> YAMLConfigManager:
243
241
  """
244
242
  Get current adapters, if defined.
245
243
 
@@ -248,9 +246,9 @@ class ComputingConfiguration(yacman.YAMLConfigManager):
248
246
  package-specific set of adapters, if any defined in 'adapters' section
249
247
  under currently active compute package.
250
248
 
251
- :return yacman.YAMLConfigManager: current adapters mapping
249
+ :return YAMLConfigManager: current adapters mapping
252
250
  """
253
- adapters = yacman.YAMLConfigManager()
251
+ adapters = YAMLConfigManager()
254
252
  if "adapters" in self and self["adapters"] is not None:
255
253
  adapters.update(self["adapters"])
256
254
  if "compute" in self and "adapters" in self.compute:
@@ -376,7 +374,7 @@ def select_divvy_config(filepath):
376
374
  :param str | NoneType filepath: direct file path specification
377
375
  :return str: path to the config file to read
378
376
  """
379
- divcfg = yacman.select_config(
377
+ divcfg = select_config(
380
378
  config_filepath=filepath,
381
379
  config_env_vars=COMPUTE_SETTINGS_VARNAME,
382
380
  default_config_filepath=DEFAULT_CONFIG_FILEPATH,