looper 2.0.0a2__py3-none-any.whl → 2.0.0a3__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/_version.py CHANGED
@@ -1,2 +1,2 @@
1
- __version__ = "2.0.0a2"
1
+ __version__ = "2.0.0a3"
2
2
  # You must change the version in parser = pydantic_argparse.ArgumentParser in cli_pydantic.py!!!
looper/cli_pydantic.py CHANGED
@@ -340,7 +340,7 @@ def main(test_args=None) -> dict:
340
340
  prog="looper",
341
341
  description="Looper: A job submitter for Portable Encapsulated Projects",
342
342
  add_help=True,
343
- version="2.0.0a2",
343
+ version="2.0.0a3",
344
344
  )
345
345
 
346
346
  parser = add_short_arguments(parser, ArgumentEnum)
@@ -184,6 +184,12 @@ class ArgumentEnum(enum.Enum):
184
184
  default=(str, None),
185
185
  description="Output directory",
186
186
  )
187
+ REPORT_OUTPUT_DIR = Argument(
188
+ name="report_dir",
189
+ alias="-r",
190
+ default=(str, None),
191
+ description="Set location for looper report and looper table outputs",
192
+ )
187
193
 
188
194
  GENERIC = Argument(
189
195
  name="generic",
@@ -124,7 +124,9 @@ RunProjectParser = Command(
124
124
  TableParser = Command(
125
125
  "table",
126
126
  MESSAGE_BY_SUBCOMMAND["table"],
127
- [],
127
+ [
128
+ ArgumentEnum.REPORT_OUTPUT_DIR.value,
129
+ ],
128
130
  )
129
131
 
130
132
 
@@ -134,6 +136,7 @@ ReportParser = Command(
134
136
  MESSAGE_BY_SUBCOMMAND["report"],
135
137
  [
136
138
  ArgumentEnum.PORTABLE.value,
139
+ ArgumentEnum.REPORT_OUTPUT_DIR.value,
137
140
  ],
138
141
  )
139
142
 
looper/divvy.py CHANGED
@@ -111,9 +111,12 @@ class ComputingConfiguration(YAMLConfigManager):
111
111
 
112
112
  :return str: path to folder with default submission templates
113
113
  """
114
- return os.path.join(
115
- os.path.dirname(__file__), "default_config", "divvy_templates"
116
- )
114
+ if self.filepath:
115
+ return os.path.join(os.path.dirname(self.filepath), "divvy_templates")
116
+ else:
117
+ return os.path.join(
118
+ os.path.dirname(__file__), "default_config", "divvy_templates"
119
+ )
117
120
 
118
121
  def activate_package(self, package_name):
119
122
  """
@@ -155,11 +158,18 @@ class ComputingConfiguration(YAMLConfigManager):
155
158
  # but now, it makes more sense to do it here so we can piggyback on
156
159
  # the default update() method and not even have to do that.
157
160
  if not os.path.isabs(self.compute["submission_template"]):
161
+
158
162
  try:
159
- self.compute["submission_template"] = os.path.join(
160
- os.path.dirname(self.default_config_file),
161
- self.compute["submission_template"],
162
- )
163
+ if self.filepath:
164
+ self.compute["submission_template"] = os.path.join(
165
+ os.path.dirname(self.filepath),
166
+ self.compute["submission_template"],
167
+ )
168
+ else:
169
+ self.compute["submission_template"] = os.path.join(
170
+ os.path.dirname(self.default_config_file),
171
+ self.compute["submission_template"],
172
+ )
163
173
  except AttributeError as e:
164
174
  # Environment and environment compute should at least have been
165
175
  # set as null-valued attributes, so execution here is an error.
looper/exceptions.py CHANGED
@@ -15,6 +15,7 @@ _all__ = [
15
15
  "PipelineInterfaceConfigError",
16
16
  "PipelineInterfaceRequirementsError",
17
17
  "MisconfigurationException",
18
+ "LooperReportError",
18
19
  ]
19
20
 
20
21
 
@@ -109,3 +110,10 @@ class PipelineInterfaceRequirementsError(LooperError):
109
110
  )
110
111
  )
111
112
  self.error_specs = typename_by_requirement
113
+
114
+
115
+ class LooperReportError(LooperError):
116
+ """Looper reporting errors"""
117
+
118
+ def __init__(self, reason):
119
+ super(LooperReportError, self).__init__(reason)
looper/looper.py CHANGED
@@ -46,6 +46,7 @@ from .utils import (
46
46
  sample_folder,
47
47
  )
48
48
  from pipestat.reports import get_file_for_table
49
+ from pipestat.exceptions import PipestatSummarizeError
49
50
 
50
51
  _PKGNAME = "looper"
51
52
  _LOGGER = logging.getLogger(_PKGNAME)
@@ -94,11 +95,19 @@ class Checker(Executor):
94
95
 
95
96
  for piface in self.prj.project_pipeline_interfaces:
96
97
  if piface.psm.pipeline_type == PipelineLevel.PROJECT.value:
97
- psms[piface.psm.pipeline_name] = piface.psm
98
- s = piface.psm.get_status() or "unknown"
98
+ if piface.psm.pipeline_name not in psms:
99
+ psms[piface.psm.pipeline_name] = piface.psm
100
+ for pl_name, psm in psms.items():
101
+ all_project_level_records = psm.select_records()
102
+ for record in all_project_level_records["records"]:
103
+ s = piface.psm.get_status(
104
+ record_identifier=record["record_identifier"]
105
+ )
99
106
  status.setdefault(piface.psm.pipeline_name, {})
100
- status[piface.psm.pipeline_name][self.prj.name] = s
101
- _LOGGER.debug(f"{self.prj.name} ({piface.psm.pipeline_name}): {s}")
107
+ status[piface.psm.pipeline_name][record["record_identifier"]] = s
108
+ _LOGGER.debug(
109
+ f"{self.prj.name} ({record['record_identifier']}): {s}"
110
+ )
102
111
 
103
112
  else:
104
113
  for sample in self.prj.samples:
@@ -559,15 +568,26 @@ class Reporter(Executor):
559
568
 
560
569
  portable = args.portable
561
570
 
571
+ report_dir = getattr(args, "report_dir", None)
572
+
562
573
  psms = {}
563
574
 
564
575
  if project_level:
565
576
 
566
577
  for piface in self.prj.project_pipeline_interfaces:
567
578
  if piface.psm.pipeline_type == PipelineLevel.PROJECT.value:
568
- psms[piface.psm.pipeline_name] = piface.psm
569
- report_directory = piface.psm.summarize(
570
- looper_samples=self.prj.samples, portable=portable
579
+ if piface.psm.pipeline_name not in psms:
580
+ psms[piface.psm.pipeline_name] = piface.psm
581
+ for pl_name, psm in psms.items():
582
+ try:
583
+ report_directory = psm.summarize(
584
+ looper_samples=self.prj.samples,
585
+ portable=portable,
586
+ output_dir=report_dir,
587
+ )
588
+ except PipestatSummarizeError as e:
589
+ raise LooperReportError(
590
+ f"Looper report error due to the following exception: {e}"
571
591
  )
572
592
  print(f"Report directory: {report_directory}")
573
593
  self.debug["report_directory"] = report_directory
@@ -575,12 +595,21 @@ class Reporter(Executor):
575
595
  else:
576
596
  for piface in self.prj.pipeline_interfaces:
577
597
  if piface.psm.pipeline_type == PipelineLevel.SAMPLE.value:
578
- psms[piface.psm.pipeline_name] = piface.psm
579
- report_directory = piface.psm.summarize(
580
- looper_samples=self.prj.samples, portable=portable
598
+ if piface.psm.pipeline_name not in psms:
599
+ psms[piface.psm.pipeline_name] = piface.psm
600
+ for pl_name, psm in psms.items():
601
+ try:
602
+ report_directory = psm.summarize(
603
+ looper_samples=self.prj.samples,
604
+ portable=portable,
605
+ output_dir=report_dir,
606
+ )
607
+ except PipestatSummarizeError as e:
608
+ raise LooperReportError(
609
+ f"Looper report error due to the following exception: {e}"
581
610
  )
582
- print(f"Report directory: {report_directory}")
583
- self.debug["report_directory"] = report_directory
611
+ print(f"Report directory: {report_directory}")
612
+ self.debug["report_directory"] = report_directory
584
613
  return self.debug
585
614
 
586
615
 
@@ -618,18 +647,23 @@ class Tabulator(Executor):
618
647
  def __call__(self, args):
619
648
  # p = self.prj
620
649
  project_level = getattr(args, "project", None)
650
+ report_dir = getattr(args, "report_dir", None)
621
651
  results = []
622
652
  psms = {}
623
653
  if project_level:
624
654
  for piface in self.prj.project_pipeline_interfaces:
625
655
  if piface.psm.pipeline_type == PipelineLevel.PROJECT.value:
626
- psms[piface.psm.pipeline_name] = piface.psm
627
- results = piface.psm.table()
656
+ if piface.psm.pipeline_name not in psms:
657
+ psms[piface.psm.pipeline_name] = piface.psm
658
+ for pl_name, psm in psms.items():
659
+ results = psm.table(output_dir=report_dir)
628
660
  else:
629
661
  for piface in self.prj.pipeline_interfaces:
630
662
  if piface.psm.pipeline_type == PipelineLevel.SAMPLE.value:
631
- psms[piface.psm.pipeline_name] = piface.psm
632
- results = piface.psm.table()
663
+ if piface.psm.pipeline_name not in psms:
664
+ psms[piface.psm.pipeline_name] = piface.psm
665
+ for pl_name, psm in psms.items():
666
+ results = psm.table(output_dir=report_dir)
633
667
  # Results contains paths to stats and object summaries.
634
668
  return results
635
669
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: looper
3
- Version: 2.0.0a2
3
+ Version: 2.0.0a3
4
4
  Summary: A pipeline submission engine that parses sample inputs and submits pipelines for each sample.
5
5
  Home-page: https://github.com/pepkit/looper
6
6
  Author: Nathan Sheffield, Vince Reuter, Michal Stolarczyk, Johanna Klughammer, Andre Rendeiro
@@ -21,7 +21,7 @@ Requires-Dist: jinja2
21
21
  Requires-Dist: logmuse>=0.2.0
22
22
  Requires-Dist: pandas>=2.0.2
23
23
  Requires-Dist: pephubclient>=0.4.0
24
- Requires-Dist: pipestat>=0.10.2
24
+ Requires-Dist: pipestat>=0.12.0a1
25
25
  Requires-Dist: peppy>=0.40.6
26
26
  Requires-Dist: pyyaml>=3.12
27
27
  Requires-Dist: rich>=9.10.0
@@ -1,13 +1,13 @@
1
1
  looper/__init__.py,sha256=f_z9YY4ibOk7eyWoaViH_VaCXMlPQeiftbnibSFj-3E,1333
2
2
  looper/__main__.py,sha256=OOCmI-dPUvInnJHkHNMf54cblNJ3Yl9ELOwZcfOXmD8,240
3
- looper/_version.py,sha256=mX3fglKqsJRTjP938X3Xm2eAJT9fve28fdydFnJFimc,121
3
+ looper/_version.py,sha256=MwJwmoXPcF0oLwdO3af_PLuTBNXWHfGxew5Q7p37mTQ,121
4
4
  looper/cli_divvy.py,sha256=_VGbOFLkXtKdkZA6omlzgXbXkuUM5aLQ50aTTtbTrVI,5975
5
- looper/cli_pydantic.py,sha256=uysPJfTrfG7-k7u6oDpaLG-3hD6U8YVrDiOW54ySniI,14425
5
+ looper/cli_pydantic.py,sha256=BA-jq6lyFnGoNNLCKAFrfqMQ7qRR3qxYK62PfMEoKA4,14425
6
6
  looper/conductor.py,sha256=lzY6Gzsb8oX-KLzLkRa0XrYWSxLeiy6jRmmD15WNAkw,35116
7
7
  looper/const.py,sha256=OscEELQsyLKlSrmwuXfyLRwpAUJUEpGD2UxBeLJDXgw,8703
8
- looper/divvy.py,sha256=eiYhcp8ZgQ0uzlk0c5yJp0QaiVrQYfSeEtmQBJiScOM,15417
9
- looper/exceptions.py,sha256=AxYCTLxKb_fJFgU9VBnPYaRX2lGhmMEYaCbJOi-27Wk,3384
10
- looper/looper.py,sha256=ZWTulMz6NobnYFUjev513TJwXqknrb4_gZrV-a_fT9g,30041
8
+ looper/divvy.py,sha256=yokD0--xN0kaxPuPRZrRPgKinl0Sqt0cvkmNMvwe94A,15860
9
+ looper/exceptions.py,sha256=weSXsPadikAHVJvUUOT1uE56C_nKqgC3iBOvDebPDVE,3572
10
+ looper/looper.py,sha256=zNszsV50ZkC5ioaLHgMg9H3hSTEHAAV-jBpLra3OIKw,31624
11
11
  looper/parser_types.py,sha256=d3FHt54f9jo9VZMr5SQkbghcAdABqiYZW2JBGO5EBnw,2327
12
12
  looper/pipeline_interface.py,sha256=mN4-XICyZzuVLTOq3b0ijppYe6ib_Ljlyf6KxZCJh2A,14537
13
13
  looper/plugins.py,sha256=MaMdPmK9U_4FkNJE5kccohBbY1i2qj1NTEucubFOJek,5747
@@ -17,8 +17,8 @@ looper/utils.py,sha256=-4QlScIB7eewIbmEJdAv2d0ZE0qr_q9acm2XUOiMEek,39769
17
17
  looper/command_models/DEVELOPER.md,sha256=eRxnrO-vqNJjExzamXKEq5wr_-Zw6PQEwkS9RPinYrk,2775
18
18
  looper/command_models/README.md,sha256=3RGegeZlTZYnhcHXRu6bdI_81WZom2q7QYMV-KGYY7U,588
19
19
  looper/command_models/__init__.py,sha256=6QWC2TewowEL7dATli5YpMmFWuXaLEPktofJCXkYUBI,187
20
- looper/command_models/arguments.py,sha256=wGGc5tleHC31F0etbiYaumHwP5xlJsSXapJ10lhltHg,8830
21
- looper/command_models/commands.py,sha256=RMTiHg0txBGDb_fHfhTWujP3VnnB6ROCHqByiwgMS4c,9716
20
+ looper/command_models/arguments.py,sha256=I9IUdDcklfctA9gdYvxyy29WawSCYPO5FwozgisCO94,9025
21
+ looper/command_models/commands.py,sha256=nBD8uOxpjby2G0n-PuMKvRBM6m5tra_Mz_nlZALwmRE,9813
22
22
  looper/default_config/divvy_config.yaml,sha256=wK5kLDGBV2wwoyqg2rl3X8SXjds4x0mwBUjUzF1Ln7g,1705
23
23
  looper/default_config/divvy_templates/localhost_bulker_template.sub,sha256=yn5VB9Brt7Hck9LT17hD2o8Kn-76gYJQk_A-8C1Gr4k,164
24
24
  looper/default_config/divvy_templates/localhost_docker_template.sub,sha256=XRr7AlR7-TP1L3hyBMfka_RgWRL9vzOlS5Kd1xSNwT0,183
@@ -60,9 +60,9 @@ looper/schemas/divvy_config_schema.yaml,sha256=7GJfKLc3VX4RGjHnOE1zxwsHXhj_ur9za
60
60
  looper/schemas/pipeline_interface_schema_generic.yaml,sha256=3YfKFyRUIwxG41FEidR1dXe9IU6ye51LSUBfSpmMuss,1773
61
61
  looper/schemas/pipeline_interface_schema_project.yaml,sha256=-ZWyA0lKXWik3obuLNVk3IsAZYfbLVbCDvJnD-Fcluo,1567
62
62
  looper/schemas/pipeline_interface_schema_sample.yaml,sha256=x0OwVnijJpvm50DscvvJujdK4UAI7d71pqVemQS-D-0,1564
63
- looper-2.0.0a2.dist-info/LICENSE.txt,sha256=oB6ZGDa4kcznznJKJsLLFFcOZyi8Y6e2Jv0rJozgp-I,1269
64
- looper-2.0.0a2.dist-info/METADATA,sha256=s4IlCgd597EhgR8H0LIB-3Qz02absf63sudpuFG1rKM,1760
65
- looper-2.0.0a2.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
66
- looper-2.0.0a2.dist-info/entry_points.txt,sha256=iHltI2_Jdved27vccmWhvmcHWUZ7Mf6CfDV6QkY1Lc8,91
67
- looper-2.0.0a2.dist-info/top_level.txt,sha256=I0Yf7djsoQAMzwHBbDiQi9hGtq4Z41_Ma5CX8qXG8Y8,7
68
- looper-2.0.0a2.dist-info/RECORD,,
63
+ looper-2.0.0a3.dist-info/LICENSE.txt,sha256=oB6ZGDa4kcznznJKJsLLFFcOZyi8Y6e2Jv0rJozgp-I,1269
64
+ looper-2.0.0a3.dist-info/METADATA,sha256=tP0InSt3Xn7098v19ZDWawfATd2Ez2zoMsecZKv6SCM,1762
65
+ looper-2.0.0a3.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
66
+ looper-2.0.0a3.dist-info/entry_points.txt,sha256=iHltI2_Jdved27vccmWhvmcHWUZ7Mf6CfDV6QkY1Lc8,91
67
+ looper-2.0.0a3.dist-info/top_level.txt,sha256=I0Yf7djsoQAMzwHBbDiQi9hGtq4Z41_Ma5CX8qXG8Y8,7
68
+ looper-2.0.0a3.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.1.0)
2
+ Generator: setuptools (75.6.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5