snk-cli 0.5.4__tar.gz → 0.6.0__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.
- {snk_cli-0.5.4 → snk_cli-0.6.0}/PKG-INFO +3 -4
- {snk_cli-0.5.4 → snk_cli-0.6.0}/README.md +1 -1
- {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/__about__.py +1 -1
- {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/cli.py +3 -2
- {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/dynamic_typer.py +26 -9
- {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/options/utils.py +27 -10
- {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/utils.py +14 -4
- {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/validate.py +7 -2
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/workflow/snk.yaml +9 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/test_cli/test_snk_config.py +1 -1
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/test_cli/test_validate.py +12 -0
- snk_cli-0.6.0/tests/test_types.py +198 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/.github/workflows/publish.yml +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/.github/workflows/tests.yml +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/.gitignore +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/LICENSE.txt +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/docs/index.md +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/docs/reference/cli.md +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/docs/reference/config.md +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/docs/reference/dynamic_typer.md +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/docs/reference/options.md +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/docs/reference/subcommands.md +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/docs/reference/testing.md +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/docs/reference/utils.md +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/docs/reference/validate.md +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/docs/reference/workflow.md +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/mkdocs.yml +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/pyproject.toml +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/__init__.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/conda.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/config/__init__.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/config/config.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/config/utils.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/options/__init__.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/options/option.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/subcommands/__init__.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/subcommands/config.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/subcommands/env.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/subcommands/profile.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/subcommands/run.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/subcommands/script.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/testing.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/workflow.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/__init__.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/conftest.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/artic_v4.1.bed +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/config.yaml +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/cov.fasta +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/print_config/Snakefile +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/print_config/cli.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/print_config/config.yaml +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/print_config/snk.yaml +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/workflow/cli.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/workflow/config.yaml +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/workflow/resources/data.txt +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/workflow/things/__about__.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/workflow/workflow/Snakefile +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/workflow/workflow/envs/wget.yml +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/workflow/workflow/profiles/base/config.yaml +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/workflow/workflow/profiles/slurm/config.yaml +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/workflow/workflow/scripts/hello.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/test_SnkConfig.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/test_cli/__init__.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/test_cli/test_dynamic_options.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/test_cli/test_profile.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/test_cli/test_run.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/test_cli/test_subcommands.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/test_cli/test_workflow_cli.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/test_conda_env.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/test_dynamic_typer.py +0 -0
- {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/utils.py +0 -0
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: snk-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Project-URL: Documentation, https://github.com/wytamma/snk-cli#readme
|
|
5
5
|
Project-URL: Issues, https://github.com/wytamma/snk-cli/issues
|
|
6
6
|
Project-URL: Source, https://github.com/wytamma/snk-cli
|
|
7
7
|
Author-email: Wytamma Wirth <wytamma.wirth@me.com>
|
|
8
|
-
License
|
|
9
|
-
License-File: LICENSE.txt
|
|
8
|
+
License: MIT
|
|
10
9
|
Classifier: Development Status :: 4 - Beta
|
|
11
10
|
Classifier: Programming Language :: Python
|
|
12
11
|
Classifier: Programming Language :: Python :: 3.8
|
|
@@ -27,7 +26,7 @@ Requires-Dist: snakemake>=7
|
|
|
27
26
|
Requires-Dist: typer~=0.9
|
|
28
27
|
Description-Content-Type: text/markdown
|
|
29
28
|
|
|
30
|
-
#
|
|
29
|
+
# Snk-CLI
|
|
31
30
|
[](https://pypi.org/project/snk-cli)
|
|
32
31
|
[](https://pypi.org/project/snk-cli)
|
|
33
32
|
[](https://write-the.wytamma.com/)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Snk-CLI
|
|
2
2
|
[](https://pypi.org/project/snk-cli)
|
|
3
3
|
[](https://pypi.org/project/snk-cli)
|
|
4
4
|
[](https://write-the.wytamma.com/)
|
|
@@ -82,10 +82,11 @@ class CLI(DynamicTyper):
|
|
|
82
82
|
):
|
|
83
83
|
os.environ["CONDA_SUBDIR"] = "osx-64"
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
user = os.environ.get("USER")
|
|
86
|
+
if platform.system() in ["Linux", "Darwin"] and not os.environ.get("XDG_CACHE_HOME") and user:
|
|
86
87
|
# this prevents OSError: [Errno 39] Directory not empty: 'envs'
|
|
87
88
|
# on older versions of snakemake
|
|
88
|
-
os.environ["XDG_CACHE_HOME"] = f"/tmp
|
|
89
|
+
os.environ["XDG_CACHE_HOME"] = f"/tmp/snk-{user}-{self.name}"
|
|
89
90
|
|
|
90
91
|
# dynamically create the logo
|
|
91
92
|
self.logo = self._create_logo(
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import typer
|
|
2
|
-
from
|
|
2
|
+
from click import Tuple
|
|
3
|
+
from typing import List, Callable, get_origin
|
|
3
4
|
from inspect import signature, Parameter
|
|
4
5
|
from makefun import with_signature
|
|
5
6
|
from enum import Enum
|
|
@@ -7,7 +8,9 @@ from enum import Enum
|
|
|
7
8
|
from .options import Option
|
|
8
9
|
import sys
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
def parse_colon_separated_pair(value: str):
|
|
12
|
+
return tuple(value.split(sep=':', maxsplit=2))
|
|
13
|
+
|
|
11
14
|
class DynamicTyper:
|
|
12
15
|
app: typer.Typer
|
|
13
16
|
|
|
@@ -149,15 +152,20 @@ class DynamicTyper:
|
|
|
149
152
|
"""
|
|
150
153
|
annotation_type = option.type
|
|
151
154
|
default = option.default
|
|
152
|
-
if option.
|
|
153
|
-
if not option.choices:
|
|
154
|
-
raise ValueError(f"Enum type {option.name} requires choices to be defined.")
|
|
155
|
-
annotation_type = Enum(f'{option.name}', {str(e): str(e) for e in option.choices})
|
|
155
|
+
if option.choices:
|
|
156
156
|
if default:
|
|
157
157
|
try:
|
|
158
158
|
default = annotation_type(default)
|
|
159
159
|
except ValueError:
|
|
160
|
-
raise ValueError(f"Default value {default} for option {option.name} is not a valid choice.")
|
|
160
|
+
raise ValueError(f"Default value '{default}' for option '{option.name}' is not a valid choice.")
|
|
161
|
+
annotation_type = Enum(f'{option.name}', {str(e): annotation_type(e) for e in option.choices})
|
|
162
|
+
metavar, parser = None, None
|
|
163
|
+
if get_origin(annotation_type) is dict or annotation_type is dict:
|
|
164
|
+
metavar = "KEY:VALUE"
|
|
165
|
+
parser = parse_colon_separated_pair
|
|
166
|
+
annotation_type = List[Tuple]
|
|
167
|
+
if type(default) is dict:
|
|
168
|
+
default = [f"{k}:{v}" for k, v in default.items()]
|
|
161
169
|
return Parameter(
|
|
162
170
|
option.name,
|
|
163
171
|
kind=Parameter.POSITIONAL_OR_KEYWORD,
|
|
@@ -167,6 +175,8 @@ class DynamicTyper:
|
|
|
167
175
|
help=f"{option.help}",
|
|
168
176
|
rich_help_panel="Workflow Configuration",
|
|
169
177
|
hidden=option.hidden,
|
|
178
|
+
metavar=metavar,
|
|
179
|
+
parser=parser,
|
|
170
180
|
),
|
|
171
181
|
annotation=annotation_type,
|
|
172
182
|
)
|
|
@@ -238,8 +248,15 @@ class DynamicTyper:
|
|
|
238
248
|
for snk_cli_option in options:
|
|
239
249
|
|
|
240
250
|
def add_option_to_args():
|
|
241
|
-
|
|
242
|
-
|
|
251
|
+
value = kwargs[snk_cli_option.name]
|
|
252
|
+
if (snk_cli_option.type is dict or get_origin(snk_cli_option.type) is dict) and isinstance(value, list):
|
|
253
|
+
# Convert the list of tuples to a dictionary
|
|
254
|
+
value = dict(kwargs[snk_cli_option.name])
|
|
255
|
+
if get_origin(snk_cli_option.type) is dict:
|
|
256
|
+
# get the value type from the type hint
|
|
257
|
+
value_type = snk_cli_option.type.__args__[1]
|
|
258
|
+
value = {k: value_type(v) for k, v in value.items()}
|
|
259
|
+
kwargs["ctx"].args.extend([f"--{snk_cli_option.name}", value])
|
|
243
260
|
passed_via_command_line = self.check_if_option_passed_via_command_line(
|
|
244
261
|
snk_cli_option
|
|
245
262
|
)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import List
|
|
1
|
+
from typing import List, Tuple, get_origin
|
|
2
2
|
from ..config.config import SnkConfig
|
|
3
3
|
from ..utils import get_default_type, flatten
|
|
4
4
|
from .option import Option
|
|
@@ -18,9 +18,21 @@ types = {
|
|
|
18
18
|
"list[str]": List[str],
|
|
19
19
|
"list[path]": List[Path],
|
|
20
20
|
"list[int]": List[int],
|
|
21
|
-
"
|
|
21
|
+
"list[float]": List[float],
|
|
22
|
+
"pair": Tuple[str, str],
|
|
23
|
+
"dict": dict,
|
|
24
|
+
"dict[str, str]": dict[str, str],
|
|
25
|
+
"dict[str, int]": dict[str, int],
|
|
22
26
|
}
|
|
23
27
|
|
|
28
|
+
# Define the basic types for the combinations
|
|
29
|
+
basic_types = [int, str, bool, float]
|
|
30
|
+
|
|
31
|
+
# Add the combinations of the basic types to the `types` dictionary
|
|
32
|
+
for t1 in basic_types:
|
|
33
|
+
for t2 in basic_types:
|
|
34
|
+
types[f"pair[{t1.__name__}, {t2.__name__}]"] = Tuple[t1, t2]
|
|
35
|
+
|
|
24
36
|
def get_keys_from_annotation(annotations):
|
|
25
37
|
# Get the unique keys from the annotations
|
|
26
38
|
# preserving the order
|
|
@@ -50,27 +62,32 @@ def create_option_from_annotation(
|
|
|
50
62
|
Option: An Option object.
|
|
51
63
|
"""
|
|
52
64
|
config_default = default_values.get(annotation_key, None)
|
|
53
|
-
|
|
54
65
|
default = annotation_values.get(f"{annotation_key}:default", config_default)
|
|
55
66
|
updated = False
|
|
56
67
|
if config_default is None or default != config_default:
|
|
57
68
|
updated = True
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
69
|
+
annotation_type = annotation_values.get(f"{annotation_key}:type", None)
|
|
70
|
+
if annotation_type is not None:
|
|
71
|
+
assert annotation_type in types, f"Type '{annotation_type}' not supported."
|
|
72
|
+
annotation_type = annotation_type or get_default_type(default)
|
|
62
73
|
annotation_type = types.get(
|
|
63
|
-
|
|
74
|
+
annotation_type, List[str] if "list" in annotation_type else str
|
|
64
75
|
)
|
|
65
76
|
name = annotation_values.get(
|
|
66
77
|
f"{annotation_key}:name", annotation_key.replace(":", "_")
|
|
67
78
|
).replace("-", "_")
|
|
68
79
|
short = annotation_values.get(f"{annotation_key}:short", None)
|
|
69
80
|
hidden = annotation_values.get(f"{annotation_key}:hidden", False)
|
|
81
|
+
default=annotation_values.get(f"{annotation_key}:default", default)
|
|
82
|
+
if default and get_origin(annotation_type) is tuple:
|
|
83
|
+
assert len(default) == 2, f"Default value ({default}) for '{annotation_key}' should be a list of length 2."
|
|
84
|
+
choices = annotation_values.get(f"{annotation_key}:choices", None)
|
|
85
|
+
if choices:
|
|
86
|
+
assert isinstance(choices, list), f"Choices should be a list for '{annotation_key}'."
|
|
70
87
|
return Option(
|
|
71
88
|
name=name,
|
|
72
89
|
original_key=annotation_key,
|
|
73
|
-
default=
|
|
90
|
+
default=default,
|
|
74
91
|
updated=updated,
|
|
75
92
|
help=annotation_values.get(f"{annotation_key}:help", ""),
|
|
76
93
|
type=annotation_type,
|
|
@@ -97,9 +114,9 @@ def build_dynamic_cli_options(
|
|
|
97
114
|
Returns:
|
|
98
115
|
List[dict]: A list of options.
|
|
99
116
|
"""
|
|
100
|
-
flat_config = flatten(snakemake_config)
|
|
101
117
|
flat_annotations = flatten(snk_config.cli)
|
|
102
118
|
annotation_keys = get_keys_from_annotation(flat_annotations)
|
|
119
|
+
flat_config = flatten(snakemake_config, stop_at=annotation_keys)
|
|
103
120
|
options = {}
|
|
104
121
|
|
|
105
122
|
# For every parameter in the config, create an option from the corresponding annotation
|
|
@@ -30,7 +30,7 @@ def check_command_available(command: str):
|
|
|
30
30
|
return which(command) is not None
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
def flatten(d, parent_key="", sep=":"):
|
|
33
|
+
def flatten(d, parent_key="", sep=":", stop_at=[]):
|
|
34
34
|
"""
|
|
35
35
|
Flattens a nested dictionary.
|
|
36
36
|
|
|
@@ -50,8 +50,11 @@ def flatten(d, parent_key="", sep=":"):
|
|
|
50
50
|
items = []
|
|
51
51
|
for k, v in d.items():
|
|
52
52
|
new_key = parent_key + sep + k if parent_key else k
|
|
53
|
-
if
|
|
54
|
-
|
|
53
|
+
if new_key in stop_at:
|
|
54
|
+
# this allows dict to be flattened up to a certain level so we can have dict types
|
|
55
|
+
items.append((new_key, v))
|
|
56
|
+
elif isinstance(v, MutableMapping):
|
|
57
|
+
items.extend(flatten(v, new_key, sep=sep, stop_at=stop_at).items())
|
|
55
58
|
else:
|
|
56
59
|
items.append((new_key, v))
|
|
57
60
|
return dict(items)
|
|
@@ -97,6 +100,13 @@ def serialise(d):
|
|
|
97
100
|
"""
|
|
98
101
|
if isinstance(d, Path) or isinstance(d, datetime):
|
|
99
102
|
return str(d)
|
|
103
|
+
|
|
104
|
+
# check enum type
|
|
105
|
+
if hasattr(d, "value"):
|
|
106
|
+
return d.value
|
|
107
|
+
|
|
108
|
+
if isinstance(d, tuple):
|
|
109
|
+
return list(d)
|
|
100
110
|
|
|
101
111
|
if isinstance(d, list):
|
|
102
112
|
return [serialise(x) for x in d]
|
|
@@ -155,7 +165,7 @@ def parse_config_args(args: List[str], options: List[Option]):
|
|
|
155
165
|
|
|
156
166
|
def get_default_type(v):
|
|
157
167
|
default_type = type(v)
|
|
158
|
-
if default_type
|
|
168
|
+
if default_type is list and len(v) > 0:
|
|
159
169
|
return f"List[{type(v[0]).__name__}]"
|
|
160
170
|
return str(default_type.__name__)
|
|
161
171
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from pathlib import Path
|
|
2
|
-
from typing import Any, Dict, Union
|
|
2
|
+
from typing import Any, Dict, Union, get_origin
|
|
3
3
|
from .config import SnkConfig
|
|
4
4
|
from .options.utils import types
|
|
5
5
|
import inspect
|
|
@@ -56,11 +56,16 @@ def validate_and_transform_in_place(config: Dict[str, Any], validation: Validati
|
|
|
56
56
|
if val_type is None:
|
|
57
57
|
raise ValueError(f"Unknown type '{val_info['type']}'")
|
|
58
58
|
try:
|
|
59
|
-
if getattr(val_type, "__origin__", None)
|
|
59
|
+
if getattr(val_type, "__origin__", None) is list:
|
|
60
60
|
val_type = val_type.__args__[0]
|
|
61
61
|
if not isinstance(value, list):
|
|
62
62
|
raise ValueError(f"Expected a list for key '{key}'")
|
|
63
63
|
config[key] = [val_type(v) for v in value]
|
|
64
|
+
elif get_origin(val_type) is tuple:
|
|
65
|
+
assert len(value) == 2, f"Expected a list of length 2 for key '{key}'"
|
|
66
|
+
key_type = val_type.__args__[0]
|
|
67
|
+
val_type = val_type.__args__[1]
|
|
68
|
+
config[key] = [key_type(value[0]), val_type(value[1])]
|
|
64
69
|
else:
|
|
65
70
|
config[key] = val_type(value)
|
|
66
71
|
except (ValueError, TypeError) as e:
|
|
@@ -9,8 +9,17 @@ cli:
|
|
|
9
9
|
type: bool
|
|
10
10
|
null_annotation:
|
|
11
11
|
default: null
|
|
12
|
+
KeyValuePair:
|
|
13
|
+
default: ["key", "value"]
|
|
14
|
+
help: A key-value pair
|
|
15
|
+
type: pair
|
|
12
16
|
enum:
|
|
13
17
|
choices: [a, b, c]
|
|
18
|
+
default: "a"
|
|
19
|
+
dict:
|
|
20
|
+
help: A dictionary
|
|
21
|
+
type: dict[str, int]
|
|
22
|
+
default: ["key:1"]
|
|
14
23
|
test:
|
|
15
24
|
another:
|
|
16
25
|
test:
|
|
@@ -44,7 +44,7 @@ def test_non_standard_configfile(tmp_path):
|
|
|
44
44
|
assert "config2" in res.stdout, res.stderr
|
|
45
45
|
|
|
46
46
|
def test_snk_config_with_enums(tmp_path):
|
|
47
|
-
runner = dynamic_runner({}, SnkConfig(cli={"test": {"choices": ["enum1", "enum2"], "type": "
|
|
47
|
+
runner = dynamic_runner({}, SnkConfig(cli={"test": {"choices": ["enum1", "enum2"], "type": "str"}}), tmp_path=tmp_path)
|
|
48
48
|
res = runner.invoke(["run", "--help"])
|
|
49
49
|
assert res.exit_code == 0, res.stderr
|
|
50
50
|
assert "enum1" in res.stdout, res.stderr
|
|
@@ -73,6 +73,18 @@ import pytest
|
|
|
73
73
|
{"example": {"type": "list[path]"}},
|
|
74
74
|
{"example": [Path("1"), Path("2")]}
|
|
75
75
|
),
|
|
76
|
+
# pair type
|
|
77
|
+
(
|
|
78
|
+
{"example": ["1", "2"]},
|
|
79
|
+
{"example": {"type": "pair[int, int]"}},
|
|
80
|
+
{"example": [1, 2]}
|
|
81
|
+
),
|
|
82
|
+
# choices
|
|
83
|
+
(
|
|
84
|
+
{"example": "a"},
|
|
85
|
+
{"example": {"type": "str", "choices": ["a", "b"]}},
|
|
86
|
+
{"example": "a"}
|
|
87
|
+
),
|
|
76
88
|
# nested dictionary
|
|
77
89
|
(
|
|
78
90
|
{"example": {"nested": "1"}},
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# integration tests for the annotation types from the snk config, snakemake config, and the command line interface
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from .utils import dynamic_runner
|
|
5
|
+
from snk_cli.config import SnkConfig
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.mark.parametrize("snakemake_config,annotations,cli_args,expected", [
|
|
9
|
+
# str
|
|
10
|
+
(
|
|
11
|
+
{"example": "1"},
|
|
12
|
+
{"example": {"type": "str"}},
|
|
13
|
+
["--example", "1"],
|
|
14
|
+
"{'example': '1'}"
|
|
15
|
+
)])
|
|
16
|
+
def test_str(snakemake_config, annotations, cli_args, expected):
|
|
17
|
+
snk_config = SnkConfig(cli=annotations)
|
|
18
|
+
runner = dynamic_runner(snakemake_config, snk_config)
|
|
19
|
+
res = runner.invoke(["run"] + cli_args)
|
|
20
|
+
assert res.exit_code == 0, res.stderr
|
|
21
|
+
assert expected in res.stdout, res.stderr
|
|
22
|
+
|
|
23
|
+
@pytest.mark.parametrize("snakemake_config,annotations,cli_args,expected", [
|
|
24
|
+
# int
|
|
25
|
+
(
|
|
26
|
+
{"example": "1"},
|
|
27
|
+
{"example": {"type": "int"}},
|
|
28
|
+
["--example", "1"],
|
|
29
|
+
"{'example': 1}"
|
|
30
|
+
)])
|
|
31
|
+
def test_int(snakemake_config, annotations, cli_args, expected):
|
|
32
|
+
snk_config = SnkConfig(cli=annotations)
|
|
33
|
+
runner = dynamic_runner(snakemake_config, snk_config)
|
|
34
|
+
res = runner.invoke(["run"] + cli_args)
|
|
35
|
+
assert res.exit_code == 0, res.stderr
|
|
36
|
+
assert expected in res.stdout, res.stderr
|
|
37
|
+
|
|
38
|
+
@pytest.mark.parametrize("snakemake_config,annotations,cli_args,expected", [
|
|
39
|
+
# float
|
|
40
|
+
(
|
|
41
|
+
{"example": "1"},
|
|
42
|
+
{"example": {"type": "float"}},
|
|
43
|
+
["--example", "1"],
|
|
44
|
+
"{'example': 1.0}"
|
|
45
|
+
)])
|
|
46
|
+
def test_float(snakemake_config, annotations, cli_args, expected):
|
|
47
|
+
snk_config = SnkConfig(cli=annotations)
|
|
48
|
+
runner = dynamic_runner(snakemake_config, snk_config)
|
|
49
|
+
res = runner.invoke(["run"] + cli_args)
|
|
50
|
+
assert res.exit_code == 0, res.stderr
|
|
51
|
+
assert expected in res.stdout, res.stderr
|
|
52
|
+
|
|
53
|
+
@pytest.mark.parametrize("snakemake_config,annotations,cli_args,expected", [
|
|
54
|
+
# bool
|
|
55
|
+
(
|
|
56
|
+
{"example": "1"},
|
|
57
|
+
{"example": {"type": "bool"}},
|
|
58
|
+
["--example"],
|
|
59
|
+
"{'example': True}"
|
|
60
|
+
)])
|
|
61
|
+
def test_bool(snakemake_config, annotations, cli_args, expected):
|
|
62
|
+
snk_config = SnkConfig(cli=annotations)
|
|
63
|
+
runner = dynamic_runner(snakemake_config, snk_config)
|
|
64
|
+
res = runner.invoke(["run"] + cli_args)
|
|
65
|
+
assert res.exit_code == 0, res.stderr
|
|
66
|
+
assert expected in res.stdout, res.stderr
|
|
67
|
+
|
|
68
|
+
@pytest.mark.parametrize("snakemake_config,annotations,cli_args,expected", [
|
|
69
|
+
# path
|
|
70
|
+
(
|
|
71
|
+
{"example": "file"},
|
|
72
|
+
{"example": {"type": "path"}},
|
|
73
|
+
["--example", "file"],
|
|
74
|
+
"{'example': 'file'}"
|
|
75
|
+
)])
|
|
76
|
+
def test_path(snakemake_config, annotations, cli_args, expected):
|
|
77
|
+
snk_config = SnkConfig(cli=annotations)
|
|
78
|
+
runner = dynamic_runner(snakemake_config, snk_config)
|
|
79
|
+
res = runner.invoke(["run"] + cli_args)
|
|
80
|
+
assert res.exit_code == 0, res.stderr
|
|
81
|
+
assert expected in res.stdout, res.stderr
|
|
82
|
+
|
|
83
|
+
@pytest.mark.parametrize("snakemake_config,annotations,cli_args,expected", [
|
|
84
|
+
# list
|
|
85
|
+
(
|
|
86
|
+
{"example": [1,2,3]},
|
|
87
|
+
{"example": {"type": "list"}},
|
|
88
|
+
["--example", "1", "--example", "2", "--example", "3"],
|
|
89
|
+
"{'example': ['1', '2', '3']}"
|
|
90
|
+
)])
|
|
91
|
+
def test_list(snakemake_config, annotations, cli_args, expected):
|
|
92
|
+
snk_config = SnkConfig(cli=annotations)
|
|
93
|
+
runner = dynamic_runner(snakemake_config, snk_config)
|
|
94
|
+
res = runner.invoke(["run"] + cli_args)
|
|
95
|
+
assert res.exit_code == 0, res.stderr
|
|
96
|
+
assert expected in res.stdout, res.stderr
|
|
97
|
+
|
|
98
|
+
@pytest.mark.parametrize("snakemake_config,annotations,cli_args,expected", [
|
|
99
|
+
# pair
|
|
100
|
+
(
|
|
101
|
+
{"example": [1, 2]},
|
|
102
|
+
{"example": {"type": "pair"}},
|
|
103
|
+
["--example", "1", "2"],
|
|
104
|
+
"{'example': ['1', '2']}"
|
|
105
|
+
),
|
|
106
|
+
(
|
|
107
|
+
{"example": [1, 2]},
|
|
108
|
+
{"example": {"type": "pair[int, int]"}},
|
|
109
|
+
["--example", "1", "2"],
|
|
110
|
+
"{'example': [1, 2]}"
|
|
111
|
+
),
|
|
112
|
+
(
|
|
113
|
+
{"example": ["1", "2"]},
|
|
114
|
+
{"example": {"type": "pair[float, float]"}},
|
|
115
|
+
["--example", "1", "2"],
|
|
116
|
+
"{'example': [1.0, 2.0]}"
|
|
117
|
+
),
|
|
118
|
+
(
|
|
119
|
+
{"example": ["1", "2"]},
|
|
120
|
+
{"example": {"type": "pair[str, str]"}},
|
|
121
|
+
["--example", "1", "2"],
|
|
122
|
+
"{'example': ['1', '2']}"
|
|
123
|
+
),
|
|
124
|
+
(
|
|
125
|
+
{"example": ["1", "2"]},
|
|
126
|
+
{"example": {"type": "pair[str, int]"}},
|
|
127
|
+
["--example", "1", "2"],
|
|
128
|
+
"{'example': ['1', 2]}"
|
|
129
|
+
),
|
|
130
|
+
(
|
|
131
|
+
{"example": ["1", "2"]},
|
|
132
|
+
{"example": {"type": "pair[int, str]"}},
|
|
133
|
+
["--example", "1", "2"],
|
|
134
|
+
"{'example': [1, '2']}"
|
|
135
|
+
)
|
|
136
|
+
])
|
|
137
|
+
def test_pair(snakemake_config, annotations, cli_args, expected):
|
|
138
|
+
snk_config = SnkConfig(cli=annotations)
|
|
139
|
+
runner = dynamic_runner(snakemake_config, snk_config)
|
|
140
|
+
res = runner.invoke(["run"] + cli_args)
|
|
141
|
+
assert res.exit_code == 0, res.stderr
|
|
142
|
+
assert expected in res.stdout, res.stderr
|
|
143
|
+
|
|
144
|
+
@pytest.mark.parametrize("snakemake_config,annotations,cli_args,expected", [
|
|
145
|
+
# choices
|
|
146
|
+
(
|
|
147
|
+
{"example": 1},
|
|
148
|
+
{"example": {"type": "int", "choices": [1, 2, 3]}},
|
|
149
|
+
["--example", 1],
|
|
150
|
+
"{'example': 1}"
|
|
151
|
+
),
|
|
152
|
+
(
|
|
153
|
+
{"example": 1},
|
|
154
|
+
{"example": {"type": "int", "choices": [2, 3], "default": 2}},
|
|
155
|
+
[],
|
|
156
|
+
"{'example': 2}"
|
|
157
|
+
),
|
|
158
|
+
])
|
|
159
|
+
def test_choices(snakemake_config, annotations, cli_args, expected):
|
|
160
|
+
snk_config = SnkConfig(cli=annotations)
|
|
161
|
+
runner = dynamic_runner(snakemake_config, snk_config)
|
|
162
|
+
res = runner.invoke(["run"] + cli_args)
|
|
163
|
+
assert res.exit_code == 0, res.stderr
|
|
164
|
+
assert expected in res.stdout, res.stderr
|
|
165
|
+
|
|
166
|
+
@pytest.mark.parametrize("snakemake_config,annotations,cli_args,expected", [
|
|
167
|
+
# dict
|
|
168
|
+
(
|
|
169
|
+
{"example": {"key": "value"}},
|
|
170
|
+
{"example": {"type": "dict", "default": ["key:value"]}},
|
|
171
|
+
[],
|
|
172
|
+
"{'example': {'key': 'value'}}"
|
|
173
|
+
),
|
|
174
|
+
(
|
|
175
|
+
{"example": {"key": "value"}},
|
|
176
|
+
{"example": {"type": "dict"}},
|
|
177
|
+
[],
|
|
178
|
+
"{'example': {'key': 'value'}}"
|
|
179
|
+
),
|
|
180
|
+
(
|
|
181
|
+
{"example": {"number": 1}},
|
|
182
|
+
{"example": {"type": "dict[str, int]"}},
|
|
183
|
+
[],
|
|
184
|
+
"{'example': {'number': 1}}"
|
|
185
|
+
),
|
|
186
|
+
(
|
|
187
|
+
{},
|
|
188
|
+
{"example": {"type": "dict[str, str]"}},
|
|
189
|
+
["--example", "new:2"],
|
|
190
|
+
"{'example': {'new': '2'}}"
|
|
191
|
+
)
|
|
192
|
+
])
|
|
193
|
+
def test_dict(snakemake_config, annotations, cli_args, expected):
|
|
194
|
+
snk_config = SnkConfig(cli=annotations)
|
|
195
|
+
runner = dynamic_runner(snakemake_config, snk_config)
|
|
196
|
+
res = runner.invoke(["run"] + cli_args)
|
|
197
|
+
assert res.exit_code == 0, res.stderr
|
|
198
|
+
assert expected in res.stdout, res.stderr
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|