looper 2.0.0a2__py3-none-any.whl → 2.0.1__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 +1 -1
- looper/cli_pydantic.py +1 -1
- looper/command_models/__init__.py +1 -1
- looper/command_models/arguments.py +6 -0
- looper/command_models/commands.py +4 -1
- looper/conductor.py +3 -3
- looper/const.py +1 -1
- looper/divvy.py +18 -8
- looper/exceptions.py +9 -1
- looper/looper.py +50 -16
- looper/parser_types.py +1 -1
- looper/pipeline_interface.py +1 -1
- looper/project.py +1 -1
- looper/utils.py +1 -3
- {looper-2.0.0a2.dist-info → looper-2.0.1.dist-info}/METADATA +14 -5
- {looper-2.0.0a2.dist-info → looper-2.0.1.dist-info}/RECORD +20 -20
- {looper-2.0.0a2.dist-info → looper-2.0.1.dist-info}/WHEEL +1 -1
- {looper-2.0.0a2.dist-info → looper-2.0.1.dist-info}/LICENSE.txt +0 -0
- {looper-2.0.0a2.dist-info → looper-2.0.1.dist-info}/entry_points.txt +0 -0
- {looper-2.0.0a2.dist-info → looper-2.0.1.dist-info}/top_level.txt +0 -0
looper/_version.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
__version__ = "2.0.
|
1
|
+
__version__ = "2.0.1"
|
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.
|
343
|
+
version="2.0.1",
|
344
344
|
)
|
345
345
|
|
346
346
|
parser = add_short_arguments(parser, ArgumentEnum)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
"""
|
2
2
|
This package holds `pydantic` models that describe commands and their arguments.
|
3
3
|
|
4
|
-
These can be used either by an HTTP API or with the `pydantic-argparse`
|
4
|
+
These can be used either by an HTTP API or with the `pydantic-argparse`
|
5
5
|
library to build a CLI.
|
6
6
|
"""
|
@@ -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/conductor.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
"""
|
1
|
+
"""Pipeline job submission orchestration"""
|
2
2
|
|
3
3
|
import importlib
|
4
4
|
import logging
|
@@ -18,7 +18,7 @@ from eido import read_schema, get_input_files_size
|
|
18
18
|
from eido.const import INPUT_FILE_SIZE_KEY, MISSING_KEY
|
19
19
|
from jinja2.exceptions import UndefinedError
|
20
20
|
|
21
|
-
from peppy.const import CONFIG_KEY,
|
21
|
+
from peppy.const import CONFIG_KEY, SAMPLE_YAML_EXT
|
22
22
|
from peppy.exceptions import RemoteYAMLError
|
23
23
|
from pipestat import PipestatError
|
24
24
|
from ubiquerg import expandpath
|
@@ -77,7 +77,7 @@ def _get_yaml_path(namespaces, template_key, default_name_appendix="", filename=
|
|
77
77
|
# default YAML location
|
78
78
|
f = (
|
79
79
|
filename
|
80
|
-
or f"{namespaces['sample'][
|
80
|
+
or f"{namespaces['sample'][namespaces['sample']['_project'].sample_table_index]}"
|
81
81
|
f"{default_name_appendix}"
|
82
82
|
f"{SAMPLE_YAML_EXT[0]}"
|
83
83
|
)
|
looper/const.py
CHANGED
looper/divvy.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
"""
|
1
|
+
"""Computing configuration representation"""
|
2
2
|
|
3
3
|
import logging
|
4
4
|
import os
|
@@ -111,9 +111,12 @@ class ComputingConfiguration(YAMLConfigManager):
|
|
111
111
|
|
112
112
|
:return str: path to folder with default submission templates
|
113
113
|
"""
|
114
|
-
|
115
|
-
os.path.dirname(
|
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.
|
160
|
-
os.path.
|
161
|
-
|
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
@@ -1,4 +1,4 @@
|
|
1
|
-
"""
|
1
|
+
"""Exceptions for specific looper issues."""
|
2
2
|
|
3
3
|
from abc import ABCMeta
|
4
4
|
from collections.abc import Iterable
|
@@ -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
|
-
|
98
|
-
|
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][
|
101
|
-
_LOGGER.debug(
|
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
|
-
|
569
|
-
|
570
|
-
|
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
|
-
|
579
|
-
|
580
|
-
|
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
|
-
|
583
|
-
|
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
|
-
|
627
|
-
|
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
|
-
|
632
|
-
|
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
|
|
looper/parser_types.py
CHANGED
looper/pipeline_interface.py
CHANGED
looper/project.py
CHANGED
looper/utils.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
"""
|
1
|
+
"""Helpers without an obvious logical home."""
|
2
2
|
|
3
3
|
import argparse
|
4
4
|
from collections import defaultdict
|
@@ -1112,7 +1112,6 @@ def inspect_looper_config_file(looper_config_dict) -> None:
|
|
1112
1112
|
|
1113
1113
|
|
1114
1114
|
def expand_nested_var_templates(var_templates_dict, namespaces):
|
1115
|
-
|
1116
1115
|
"Takes all var_templates as a dict and recursively expands any paths."
|
1117
1116
|
|
1118
1117
|
result = {}
|
@@ -1127,7 +1126,6 @@ def expand_nested_var_templates(var_templates_dict, namespaces):
|
|
1127
1126
|
|
1128
1127
|
|
1129
1128
|
def render_nested_var_templates(var_templates_dict, namespaces):
|
1130
|
-
|
1131
1129
|
"Takes all var_templates as a dict and recursively renders the jinja templates."
|
1132
1130
|
|
1133
1131
|
result = {}
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.2
|
2
2
|
Name: looper
|
3
|
-
Version: 2.0.
|
3
|
+
Version: 2.0.1
|
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
|
@@ -16,19 +16,28 @@ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
|
|
16
16
|
Description-Content-Type: text/markdown
|
17
17
|
License-File: LICENSE.txt
|
18
18
|
Requires-Dist: colorama>=0.3.9
|
19
|
-
Requires-Dist: eido>=0.2.
|
19
|
+
Requires-Dist: eido>=0.2.4
|
20
20
|
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.
|
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
|
28
|
-
Requires-Dist: ubiquerg>=0.8.
|
28
|
+
Requires-Dist: ubiquerg>=0.8.1
|
29
29
|
Requires-Dist: yacman==0.9.3
|
30
30
|
Requires-Dist: pydantic-argparse>=0.9.0
|
31
31
|
Requires-Dist: psutil
|
32
|
+
Dynamic: author
|
33
|
+
Dynamic: classifier
|
34
|
+
Dynamic: description
|
35
|
+
Dynamic: description-content-type
|
36
|
+
Dynamic: home-page
|
37
|
+
Dynamic: keywords
|
38
|
+
Dynamic: license
|
39
|
+
Dynamic: requires-dist
|
40
|
+
Dynamic: summary
|
32
41
|
|
33
42
|
# <img src="docs/img/looper_logo.svg" alt="looper logo" height="70">
|
34
43
|
|
@@ -1,24 +1,24 @@
|
|
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=
|
3
|
+
looper/_version.py,sha256=DW9L8zOJPcNY9ZbgS_iQmVE_rIc1YpAHFt8cZRRXFZU,119
|
4
4
|
looper/cli_divvy.py,sha256=_VGbOFLkXtKdkZA6omlzgXbXkuUM5aLQ50aTTtbTrVI,5975
|
5
|
-
looper/cli_pydantic.py,sha256=
|
6
|
-
looper/conductor.py,sha256=
|
7
|
-
looper/const.py,sha256=
|
8
|
-
looper/divvy.py,sha256=
|
9
|
-
looper/exceptions.py,sha256
|
10
|
-
looper/looper.py,sha256=
|
11
|
-
looper/parser_types.py,sha256=
|
12
|
-
looper/pipeline_interface.py,sha256=
|
5
|
+
looper/cli_pydantic.py,sha256=KS26gh_vF9fxjVpuBbI1K4sgokT1PWme4klwwcphLWQ,14423
|
6
|
+
looper/conductor.py,sha256=EvVSZ9czG8-cRDLl2aXHZPeQ02J4xM0T_Y6mYO_2jTQ,35131
|
7
|
+
looper/const.py,sha256=eClmwc0c1r7zjQKrqBPa_OUOnoCqNNe0e64xElvLQHA,8701
|
8
|
+
looper/divvy.py,sha256=KQSIxgeiFJf73c7xpAV8CoxUIuXjcfMphNWKSZXmuP0,15858
|
9
|
+
looper/exceptions.py,sha256=-VEExHqjAtOSacBx45DA_9c-2i4EqxqGRLttTKxXXgg,3570
|
10
|
+
looper/looper.py,sha256=zNszsV50ZkC5ioaLHgMg9H3hSTEHAAV-jBpLra3OIKw,31624
|
11
|
+
looper/parser_types.py,sha256=PgAdrtCGvXhXcX7UbW8E3oC414uv1mKV4sTnXVHtIzE,2325
|
12
|
+
looper/pipeline_interface.py,sha256=NSAHx1ITmODy--Eh_b2oO4hP4mSHvs_7lkwuFLXbjsE,14535
|
13
13
|
looper/plugins.py,sha256=MaMdPmK9U_4FkNJE5kccohBbY1i2qj1NTEucubFOJek,5747
|
14
14
|
looper/processed_project.py,sha256=jZxoMYafvr-OHFxylc5ivGty1VwXBZhl0kgoFkY-174,9837
|
15
|
-
looper/project.py,sha256=
|
16
|
-
looper/utils.py,sha256
|
15
|
+
looper/project.py,sha256=uoh8zGP4pdo630pcxLW5hdB5a2cP-YK8-CFd6F26Yus,34337
|
16
|
+
looper/utils.py,sha256=42YP5Qz41TDUW45ptDio_2w8f3iEi2judeBo9d5yJh0,39765
|
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
|
-
looper/command_models/__init__.py,sha256=
|
20
|
-
looper/command_models/arguments.py,sha256=
|
21
|
-
looper/command_models/commands.py,sha256=
|
19
|
+
looper/command_models/__init__.py,sha256=suzlc6tiNFQlv9b--aZdE9uyklTcKQ_z4YGyLfO0Ru0,186
|
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.
|
64
|
-
looper-2.0.
|
65
|
-
looper-2.0.
|
66
|
-
looper-2.0.
|
67
|
-
looper-2.0.
|
68
|
-
looper-2.0.
|
63
|
+
looper-2.0.1.dist-info/LICENSE.txt,sha256=oB6ZGDa4kcznznJKJsLLFFcOZyi8Y6e2Jv0rJozgp-I,1269
|
64
|
+
looper-2.0.1.dist-info/METADATA,sha256=GPVKXrQreBCtmdWPvAiXb1Hci6InnMu3OgawD_IDcF0,1943
|
65
|
+
looper-2.0.1.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
|
66
|
+
looper-2.0.1.dist-info/entry_points.txt,sha256=iHltI2_Jdved27vccmWhvmcHWUZ7Mf6CfDV6QkY1Lc8,91
|
67
|
+
looper-2.0.1.dist-info/top_level.txt,sha256=I0Yf7djsoQAMzwHBbDiQi9hGtq4Z41_Ma5CX8qXG8Y8,7
|
68
|
+
looper-2.0.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|