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.
Files changed (71) hide show
  1. {snk_cli-0.5.4 → snk_cli-0.6.0}/PKG-INFO +3 -4
  2. {snk_cli-0.5.4 → snk_cli-0.6.0}/README.md +1 -1
  3. {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/__about__.py +1 -1
  4. {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/cli.py +3 -2
  5. {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/dynamic_typer.py +26 -9
  6. {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/options/utils.py +27 -10
  7. {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/utils.py +14 -4
  8. {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/validate.py +7 -2
  9. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/workflow/snk.yaml +9 -0
  10. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/test_cli/test_snk_config.py +1 -1
  11. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/test_cli/test_validate.py +12 -0
  12. snk_cli-0.6.0/tests/test_types.py +198 -0
  13. {snk_cli-0.5.4 → snk_cli-0.6.0}/.github/workflows/publish.yml +0 -0
  14. {snk_cli-0.5.4 → snk_cli-0.6.0}/.github/workflows/tests.yml +0 -0
  15. {snk_cli-0.5.4 → snk_cli-0.6.0}/.gitignore +0 -0
  16. {snk_cli-0.5.4 → snk_cli-0.6.0}/LICENSE.txt +0 -0
  17. {snk_cli-0.5.4 → snk_cli-0.6.0}/docs/index.md +0 -0
  18. {snk_cli-0.5.4 → snk_cli-0.6.0}/docs/reference/cli.md +0 -0
  19. {snk_cli-0.5.4 → snk_cli-0.6.0}/docs/reference/config.md +0 -0
  20. {snk_cli-0.5.4 → snk_cli-0.6.0}/docs/reference/dynamic_typer.md +0 -0
  21. {snk_cli-0.5.4 → snk_cli-0.6.0}/docs/reference/options.md +0 -0
  22. {snk_cli-0.5.4 → snk_cli-0.6.0}/docs/reference/subcommands.md +0 -0
  23. {snk_cli-0.5.4 → snk_cli-0.6.0}/docs/reference/testing.md +0 -0
  24. {snk_cli-0.5.4 → snk_cli-0.6.0}/docs/reference/utils.md +0 -0
  25. {snk_cli-0.5.4 → snk_cli-0.6.0}/docs/reference/validate.md +0 -0
  26. {snk_cli-0.5.4 → snk_cli-0.6.0}/docs/reference/workflow.md +0 -0
  27. {snk_cli-0.5.4 → snk_cli-0.6.0}/mkdocs.yml +0 -0
  28. {snk_cli-0.5.4 → snk_cli-0.6.0}/pyproject.toml +0 -0
  29. {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/__init__.py +0 -0
  30. {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/conda.py +0 -0
  31. {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/config/__init__.py +0 -0
  32. {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/config/config.py +0 -0
  33. {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/config/utils.py +0 -0
  34. {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/options/__init__.py +0 -0
  35. {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/options/option.py +0 -0
  36. {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/subcommands/__init__.py +0 -0
  37. {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/subcommands/config.py +0 -0
  38. {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/subcommands/env.py +0 -0
  39. {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/subcommands/profile.py +0 -0
  40. {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/subcommands/run.py +0 -0
  41. {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/subcommands/script.py +0 -0
  42. {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/testing.py +0 -0
  43. {snk_cli-0.5.4 → snk_cli-0.6.0}/src/snk_cli/workflow.py +0 -0
  44. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/__init__.py +0 -0
  45. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/conftest.py +0 -0
  46. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/artic_v4.1.bed +0 -0
  47. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/config.yaml +0 -0
  48. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/cov.fasta +0 -0
  49. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/print_config/Snakefile +0 -0
  50. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/print_config/cli.py +0 -0
  51. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/print_config/config.yaml +0 -0
  52. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/print_config/snk.yaml +0 -0
  53. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/workflow/cli.py +0 -0
  54. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/workflow/config.yaml +0 -0
  55. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/workflow/resources/data.txt +0 -0
  56. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/workflow/things/__about__.py +0 -0
  57. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/workflow/workflow/Snakefile +0 -0
  58. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/workflow/workflow/envs/wget.yml +0 -0
  59. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/workflow/workflow/profiles/base/config.yaml +0 -0
  60. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/workflow/workflow/profiles/slurm/config.yaml +0 -0
  61. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/data/workflow/workflow/scripts/hello.py +0 -0
  62. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/test_SnkConfig.py +0 -0
  63. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/test_cli/__init__.py +0 -0
  64. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/test_cli/test_dynamic_options.py +0 -0
  65. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/test_cli/test_profile.py +0 -0
  66. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/test_cli/test_run.py +0 -0
  67. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/test_cli/test_subcommands.py +0 -0
  68. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/test_cli/test_workflow_cli.py +0 -0
  69. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/test_conda_env.py +0 -0
  70. {snk_cli-0.5.4 → snk_cli-0.6.0}/tests/test_dynamic_typer.py +0 -0
  71. {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.5.4
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-Expression: MIT
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
- # snk-cli
29
+ # Snk-CLI
31
30
  [![PyPI - Version](https://img.shields.io/pypi/v/snk-cli.svg)](https://pypi.org/project/snk-cli)
32
31
  [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/snk-cli.svg)](https://pypi.org/project/snk-cli)
33
32
  [![write-the - docs](https://badgen.net/badge/write-the/docs/blue?icon=https://raw.githubusercontent.com/Wytamma/write-the/master/images/write-the-icon.svg)](https://write-the.wytamma.com/)
@@ -1,4 +1,4 @@
1
- # snk-cli
1
+ # Snk-CLI
2
2
  [![PyPI - Version](https://img.shields.io/pypi/v/snk-cli.svg)](https://pypi.org/project/snk-cli)
3
3
  [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/snk-cli.svg)](https://pypi.org/project/snk-cli)
4
4
  [![write-the - docs](https://badgen.net/badge/write-the/docs/blue?icon=https://raw.githubusercontent.com/Wytamma/write-the/master/images/write-the-icon.svg)](https://write-the.wytamma.com/)
@@ -1,4 +1,4 @@
1
1
  # SPDX-FileCopyrightText: 2024-present Wytamma Wirth <wytamma.wirth@me.com>
2
2
  #
3
3
  # SPDX-License-Identifier: MIT
4
- __version__ = "0.5.4"
4
+ __version__ = "0.6.0"
@@ -82,10 +82,11 @@ class CLI(DynamicTyper):
82
82
  ):
83
83
  os.environ["CONDA_SUBDIR"] = "osx-64"
84
84
 
85
- if platform.system() in ["Linux", "Darwin"] and not os.environ.get("XDG_CACHE_HOME"):
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/.cache/{self.name}"
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 typing import List, Callable
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.type is Enum or option.choices:
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
- kwargs["ctx"].args.extend([f"--{snk_cli_option.name}", kwargs[snk_cli_option.name]])
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
- "enum": Enum,
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
- type = annotation_values.get(f"{annotation_key}:type", get_default_type(default))
59
- assert (
60
- type is not None
61
- ), f"Type for {annotation_key} should be one of {', '.join(types.keys())}."
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
- type.lower(), List[str] if "list" in type.lower() else str
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=annotation_values.get(f"{annotation_key}:default", 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 isinstance(v, MutableMapping):
54
- items.extend(flatten(v, new_key, sep=sep).items())
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 == list and len(v) > 0:
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) == list:
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": "enum"}}), tmp_path=tmp_path)
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