snk-cli 0.3.1__tar.gz → 0.3.2__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 (70) hide show
  1. {snk_cli-0.3.1 → snk_cli-0.3.2}/PKG-INFO +4 -4
  2. {snk_cli-0.3.1 → snk_cli-0.3.2}/pyproject.toml +3 -3
  3. {snk_cli-0.3.1 → snk_cli-0.3.2}/src/snk_cli/__about__.py +1 -1
  4. snk_cli-0.3.2/src/snk_cli/subcommands/profile.py +110 -0
  5. snk_cli-0.3.2/tests/test_cli/test_profile.py +69 -0
  6. snk_cli-0.3.1/src/snk_cli/subcommands/profile.py +0 -60
  7. {snk_cli-0.3.1 → snk_cli-0.3.2}/.github/workflows/publish.yml +0 -0
  8. {snk_cli-0.3.1 → snk_cli-0.3.2}/.github/workflows/tests.yml +0 -0
  9. {snk_cli-0.3.1 → snk_cli-0.3.2}/.gitignore +0 -0
  10. {snk_cli-0.3.1 → snk_cli-0.3.2}/LICENSE.txt +0 -0
  11. {snk_cli-0.3.1 → snk_cli-0.3.2}/README.md +0 -0
  12. {snk_cli-0.3.1 → snk_cli-0.3.2}/docs/index.md +0 -0
  13. {snk_cli-0.3.1 → snk_cli-0.3.2}/docs/reference/cli.md +0 -0
  14. {snk_cli-0.3.1 → snk_cli-0.3.2}/docs/reference/config.md +0 -0
  15. {snk_cli-0.3.1 → snk_cli-0.3.2}/docs/reference/dynamic_typer.md +0 -0
  16. {snk_cli-0.3.1 → snk_cli-0.3.2}/docs/reference/options.md +0 -0
  17. {snk_cli-0.3.1 → snk_cli-0.3.2}/docs/reference/subcommands.md +0 -0
  18. {snk_cli-0.3.1 → snk_cli-0.3.2}/docs/reference/testing.md +0 -0
  19. {snk_cli-0.3.1 → snk_cli-0.3.2}/docs/reference/utils.md +0 -0
  20. {snk_cli-0.3.1 → snk_cli-0.3.2}/docs/reference/validate.md +0 -0
  21. {snk_cli-0.3.1 → snk_cli-0.3.2}/docs/reference/workflow.md +0 -0
  22. {snk_cli-0.3.1 → snk_cli-0.3.2}/mkdocs.yml +0 -0
  23. {snk_cli-0.3.1 → snk_cli-0.3.2}/src/snk_cli/__init__.py +0 -0
  24. {snk_cli-0.3.1 → snk_cli-0.3.2}/src/snk_cli/cli.py +0 -0
  25. {snk_cli-0.3.1 → snk_cli-0.3.2}/src/snk_cli/conda.py +0 -0
  26. {snk_cli-0.3.1 → snk_cli-0.3.2}/src/snk_cli/config/__init__.py +0 -0
  27. {snk_cli-0.3.1 → snk_cli-0.3.2}/src/snk_cli/config/config.py +0 -0
  28. {snk_cli-0.3.1 → snk_cli-0.3.2}/src/snk_cli/config/utils.py +0 -0
  29. {snk_cli-0.3.1 → snk_cli-0.3.2}/src/snk_cli/dynamic_typer.py +0 -0
  30. {snk_cli-0.3.1 → snk_cli-0.3.2}/src/snk_cli/options/__init__.py +0 -0
  31. {snk_cli-0.3.1 → snk_cli-0.3.2}/src/snk_cli/options/option.py +0 -0
  32. {snk_cli-0.3.1 → snk_cli-0.3.2}/src/snk_cli/options/utils.py +0 -0
  33. {snk_cli-0.3.1 → snk_cli-0.3.2}/src/snk_cli/subcommands/__init__.py +0 -0
  34. {snk_cli-0.3.1 → snk_cli-0.3.2}/src/snk_cli/subcommands/config.py +0 -0
  35. {snk_cli-0.3.1 → snk_cli-0.3.2}/src/snk_cli/subcommands/env.py +0 -0
  36. {snk_cli-0.3.1 → snk_cli-0.3.2}/src/snk_cli/subcommands/run.py +0 -0
  37. {snk_cli-0.3.1 → snk_cli-0.3.2}/src/snk_cli/subcommands/script.py +0 -0
  38. {snk_cli-0.3.1 → snk_cli-0.3.2}/src/snk_cli/testing.py +0 -0
  39. {snk_cli-0.3.1 → snk_cli-0.3.2}/src/snk_cli/utils.py +0 -0
  40. {snk_cli-0.3.1 → snk_cli-0.3.2}/src/snk_cli/validate.py +0 -0
  41. {snk_cli-0.3.1 → snk_cli-0.3.2}/src/snk_cli/workflow.py +0 -0
  42. {snk_cli-0.3.1 → snk_cli-0.3.2}/tests/__init__.py +0 -0
  43. {snk_cli-0.3.1 → snk_cli-0.3.2}/tests/conftest.py +0 -0
  44. {snk_cli-0.3.1 → snk_cli-0.3.2}/tests/data/artic_v4.1.bed +0 -0
  45. {snk_cli-0.3.1 → snk_cli-0.3.2}/tests/data/config.yaml +0 -0
  46. {snk_cli-0.3.1 → snk_cli-0.3.2}/tests/data/cov.fasta +0 -0
  47. {snk_cli-0.3.1 → snk_cli-0.3.2}/tests/data/print_config/Snakefile +0 -0
  48. {snk_cli-0.3.1 → snk_cli-0.3.2}/tests/data/print_config/cli.py +0 -0
  49. {snk_cli-0.3.1 → snk_cli-0.3.2}/tests/data/print_config/config.yaml +0 -0
  50. {snk_cli-0.3.1 → snk_cli-0.3.2}/tests/data/print_config/snk.yaml +0 -0
  51. {snk_cli-0.3.1 → snk_cli-0.3.2}/tests/data/workflow/cli.py +0 -0
  52. {snk_cli-0.3.1 → snk_cli-0.3.2}/tests/data/workflow/config.yaml +0 -0
  53. {snk_cli-0.3.1 → snk_cli-0.3.2}/tests/data/workflow/resources/data.txt +0 -0
  54. {snk_cli-0.3.1 → snk_cli-0.3.2}/tests/data/workflow/snk.yaml +0 -0
  55. {snk_cli-0.3.1 → snk_cli-0.3.2}/tests/data/workflow/things/__about__.py +0 -0
  56. {snk_cli-0.3.1 → snk_cli-0.3.2}/tests/data/workflow/workflow/Snakefile +0 -0
  57. {snk_cli-0.3.1 → snk_cli-0.3.2}/tests/data/workflow/workflow/envs/wget.yml +0 -0
  58. {snk_cli-0.3.1 → snk_cli-0.3.2}/tests/data/workflow/workflow/profiles/base/config.yaml +0 -0
  59. {snk_cli-0.3.1 → snk_cli-0.3.2}/tests/data/workflow/workflow/profiles/slurm/config.yaml +0 -0
  60. {snk_cli-0.3.1 → snk_cli-0.3.2}/tests/data/workflow/workflow/scripts/hello.py +0 -0
  61. {snk_cli-0.3.1 → snk_cli-0.3.2}/tests/test_cli/__init__.py +0 -0
  62. {snk_cli-0.3.1 → snk_cli-0.3.2}/tests/test_cli/test_dynamic_options.py +0 -0
  63. {snk_cli-0.3.1 → snk_cli-0.3.2}/tests/test_cli/test_run.py +0 -0
  64. {snk_cli-0.3.1 → snk_cli-0.3.2}/tests/test_cli/test_snk_config.py +0 -0
  65. {snk_cli-0.3.1 → snk_cli-0.3.2}/tests/test_cli/test_subcommands.py +0 -0
  66. {snk_cli-0.3.1 → snk_cli-0.3.2}/tests/test_cli/test_validate.py +0 -0
  67. {snk_cli-0.3.1 → snk_cli-0.3.2}/tests/test_cli/test_workflow_cli.py +0 -0
  68. {snk_cli-0.3.1 → snk_cli-0.3.2}/tests/test_conda_env.py +0 -0
  69. {snk_cli-0.3.1 → snk_cli-0.3.2}/tests/test_dynamic_typer.py +0 -0
  70. {snk_cli-0.3.1 → snk_cli-0.3.2}/tests/utils.py +0 -0
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: snk-cli
3
- Version: 0.3.1
4
- Project-URL: Documentation, https://github.com/unknown/snk-cli#readme
5
- Project-URL: Issues, https://github.com/unknown/snk-cli/issues
6
- Project-URL: Source, https://github.com/unknown/snk-cli
3
+ Version: 0.3.2
4
+ Project-URL: Documentation, https://github.com/wytamma/snk-cli#readme
5
+ Project-URL: Issues, https://github.com/wytamma/snk-cli/issues
6
+ Project-URL: Source, https://github.com/wytamma/snk-cli
7
7
  Author-email: Wytamma Wirth <wytamma.wirth@me.com>
8
8
  License-Expression: MIT
9
9
  License-File: LICENSE.txt
@@ -35,9 +35,9 @@ dependencies = [
35
35
  ]
36
36
 
37
37
  [project.urls]
38
- Documentation = "https://github.com/unknown/snk-cli#readme"
39
- Issues = "https://github.com/unknown/snk-cli/issues"
40
- Source = "https://github.com/unknown/snk-cli"
38
+ Documentation = "https://github.com/wytamma/snk-cli#readme"
39
+ Issues = "https://github.com/wytamma/snk-cli/issues"
40
+ Source = "https://github.com/wytamma/snk-cli"
41
41
 
42
42
  [tool.hatch.version]
43
43
  path = "src/snk_cli/__about__.py"
@@ -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.3.1"
4
+ __version__ = "0.3.2"
@@ -0,0 +1,110 @@
1
+ import typer
2
+ from snk_cli.dynamic_typer import DynamicTyper
3
+ from ..workflow import Workflow
4
+ from rich.console import Console
5
+ from rich.syntax import Syntax
6
+ from pathlib import Path
7
+
8
+
9
+ class ProfileApp(DynamicTyper):
10
+ def __init__(
11
+ self,
12
+ workflow: Workflow,
13
+ ):
14
+ self.workflow = workflow
15
+ self.register_command(self.list, help="List the profiles in the workflow.")
16
+ self.register_command(self.show, help="Show the contents of a profile.")
17
+ self.register_command(self.edit, help="Edit the contents of a profile.")
18
+
19
+ def list(
20
+ self,
21
+ verbose: bool = typer.Option(
22
+ False, "--verbose", "-v", help="Show profiles as paths."
23
+ ),
24
+ ):
25
+ from rich.console import Console
26
+ from rich.table import Table
27
+
28
+ table = Table("Name", "CMD", show_header=True, show_lines=True)
29
+ if verbose:
30
+ table.add_column("Path")
31
+ for profile in self.workflow.profiles:
32
+ if verbose:
33
+ path = str(profile.resolve())
34
+ table.add_row(
35
+ profile.stem,
36
+ f"{self.workflow.name} profile show {profile.stem}",
37
+ path,
38
+ )
39
+ else:
40
+ table.add_row(
41
+ profile.stem, f"{self.workflow.name} profile show {profile.stem}"
42
+ )
43
+ console = Console()
44
+ console.print(table)
45
+
46
+ def _get_profile_path(self, name: str) -> Path:
47
+ profile = [
48
+ p for p in self.workflow.profiles if p.name == name or p.stem == name
49
+ ]
50
+ if not profile:
51
+ self.error(f"Profile {name} not found!")
52
+ return profile[0] / "config.yaml"
53
+
54
+ def show(
55
+ self,
56
+ name: str = typer.Argument(..., help="The name of the profile."),
57
+ pretty: bool = typer.Option(
58
+ False, "--pretty", "-p", help="Pretty print the profile."
59
+ ),
60
+ ):
61
+ profile_path = self._get_profile_path(name)
62
+ profile_file_text = profile_path.read_text()
63
+ if pretty:
64
+ syntax = Syntax(profile_file_text, "yaml")
65
+ console = Console()
66
+ console.print(syntax)
67
+ else:
68
+ typer.echo(profile_file_text)
69
+
70
+ def _open_text_editor(self, file_path):
71
+ """
72
+ Opens the system's default text editor to edit the specified file.
73
+
74
+ Parameters:
75
+ file_path (str): The path to the file to be edited.
76
+ """
77
+ import subprocess
78
+ import os
79
+ import platform
80
+
81
+ try:
82
+ if platform.system() == "Windows":
83
+ os.startfile(file_path)
84
+ elif platform.system() == "Darwin": # macOS
85
+ subprocess.call(("open", file_path))
86
+ else: # Linux and other Unix-like systems
87
+ editors = ["nano", "vim", "vi"]
88
+ editor = None
89
+ for e in editors:
90
+ if (
91
+ subprocess.call(
92
+ ["which", e], stdout=subprocess.PIPE, stderr=subprocess.PIPE
93
+ )
94
+ == 0
95
+ ):
96
+ editor = e
97
+ break
98
+ if editor:
99
+ subprocess.call([editor, file_path])
100
+ else:
101
+ self.error(
102
+ "No suitable text editor found. Please install nano or vim."
103
+ )
104
+ except Exception as e:
105
+ print(f"An error occurred: {e}")
106
+
107
+ def edit(self, name: str = typer.Argument(..., help="The name of the profile.")):
108
+ profile_path = self._get_profile_path(name)
109
+ ## open the profile in the system editor
110
+ self._open_text_editor(profile_path)
@@ -0,0 +1,69 @@
1
+ import pytest
2
+ import subprocess
3
+ import os # noqa: F401
4
+ import platform # noqa: F401
5
+ import typer # noqa: F401
6
+ from pathlib import Path
7
+ from unittest.mock import patch, call
8
+
9
+ from ..utils import SnkCliRunner
10
+
11
+ @pytest.fixture
12
+ def mock_platform_system():
13
+ with patch('platform.system') as mock:
14
+ yield mock
15
+
16
+ @pytest.fixture
17
+ def mock_os_startfile():
18
+ if platform.system() == 'Windows':
19
+ with patch('os.startfile') as mock:
20
+ yield mock
21
+ else:
22
+ yield None
23
+
24
+ @pytest.fixture
25
+ def mock_subprocess_call():
26
+ with patch('subprocess.call') as mock:
27
+ yield mock
28
+
29
+ def test_open_text_editor_windows(mock_platform_system, mock_os_startfile, local_runner: SnkCliRunner):
30
+ mock_platform_system.return_value = 'Windows'
31
+ file_path = Path('tests/data/workflow/workflow/profiles/slurm/config.yaml')
32
+
33
+ res = local_runner(["profile", "edit", "slurm"])
34
+ assert res.exit_code == 0, res.stderr
35
+ if mock_os_startfile:
36
+ mock_os_startfile.assert_called_once_with(file_path)
37
+
38
+ def test_open_text_editor_mac(mock_platform_system, mock_subprocess_call, local_runner: SnkCliRunner):
39
+ mock_platform_system.return_value = 'Darwin'
40
+ file_path = Path('tests/data/workflow/workflow/profiles/slurm/config.yaml')
41
+
42
+ res = local_runner(["profile", "edit", "slurm"])
43
+ assert res.exit_code == 0, res.stderr
44
+ mock_subprocess_call.assert_called_once_with(('open', file_path))
45
+
46
+ def test_open_text_editor_linux(mock_platform_system, mock_subprocess_call, local_runner: SnkCliRunner):
47
+ mock_platform_system.return_value = 'Linux'
48
+ file_path = Path('tests/data/workflow/workflow/profiles/slurm/config.yaml')
49
+
50
+ with patch('subprocess.call') as mock_call:
51
+ mock_call.side_effect = [1, 1, 0] # Mocking 'which' command results: nano not found, vim not found, vi found
52
+
53
+ res = local_runner(["profile", "edit", "slurm"])
54
+ assert res.exit_code == 0, res.stderr
55
+ mock_call.assert_has_calls([call(['which', 'nano'], stdout=subprocess.PIPE, stderr=subprocess.PIPE),
56
+ call(['which', 'vim'], stdout=subprocess.PIPE, stderr=subprocess.PIPE),
57
+ call(['which', 'vi'], stdout=subprocess.PIPE, stderr=subprocess.PIPE),
58
+ call(['vi', file_path])])
59
+
60
+ def test_open_text_editor_no_editor_found(mock_platform_system, mock_subprocess_call, local_runner: SnkCliRunner):
61
+ mock_platform_system.return_value = 'Linux'
62
+
63
+ with patch('subprocess.call') as mock_call:
64
+ mock_call.side_effect = [1, 1, 1] # Mocking 'which' command results: none of the editors found
65
+
66
+ with patch('typer.secho') as mock_print:
67
+ res = local_runner(["profile", "edit", "slurm"])
68
+ assert res.exit_code == 0, res.stderr
69
+ mock_print.assert_called_once_with("No suitable text editor found. Please install nano or vim.", fg='red', err=True)
@@ -1,60 +0,0 @@
1
- import typer
2
- from snk_cli.dynamic_typer import DynamicTyper
3
- from ..workflow import Workflow
4
- from rich.console import Console
5
- from rich.syntax import Syntax
6
- from pathlib import Path
7
-
8
-
9
- class ProfileApp(DynamicTyper):
10
- def __init__(
11
- self,
12
- workflow: Workflow,
13
- ):
14
- self.workflow = workflow
15
- self.register_command(self.list, help="List the profiles in the workflow.")
16
- self.register_command(
17
- self.show, help="Show the contents of a profile."
18
- )
19
-
20
- def list(
21
- self,
22
- verbose: bool = typer.Option(False, "--verbose", "-v", help="Show profiles as paths."),
23
- ):
24
- from rich.console import Console
25
- from rich.table import Table
26
- table = Table("Name", "CMD", show_header=True, show_lines=True)
27
- if verbose:
28
- table.add_column("Path")
29
- for profile in self.workflow.profiles:
30
- if verbose:
31
- path = str(profile.resolve())
32
- table.add_row(profile.stem, f"{self.workflow.name} profile show {profile.stem}", path)
33
- else:
34
- table.add_row(profile.stem, f"{self.workflow.name} profile show {profile.stem}")
35
- console = Console()
36
- console.print(table)
37
-
38
-
39
-
40
- def _get_profile_path(self, name: str) -> Path:
41
- profile = [p for p in self.workflow.profiles if p.name == name or p.stem == name]
42
- if not profile:
43
- self.error(f"Profile {name} not found!")
44
- return profile[0] / "config.yaml"
45
-
46
- def show(
47
- self,
48
- name: str = typer.Argument(..., help="The name of the profile."),
49
- pretty: bool = typer.Option(
50
- False, "--pretty", "-p", help="Pretty print the profile."
51
- ),
52
- ):
53
- profile_path = self._get_profile_path(name)
54
- profile_file_text = profile_path.read_text()
55
- if pretty:
56
- syntax = Syntax(profile_file_text, "yaml")
57
- console = Console()
58
- console.print(syntax)
59
- else:
60
- typer.echo(profile_file_text)
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