powercli 0.3.4__tar.gz → 0.3.6__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 (38) hide show
  1. {powercli-0.3.4/src/powercli.egg-info → powercli-0.3.6}/PKG-INFO +8 -6
  2. {powercli-0.3.4 → powercli-0.3.6}/README.md +1 -1
  3. {powercli-0.3.4 → powercli-0.3.6}/pyproject.toml +6 -5
  4. {powercli-0.3.4 → powercli-0.3.6}/src/cli/__init__.py +0 -1
  5. {powercli-0.3.4 → powercli-0.3.6}/src/cli/commands/__init__.py +0 -1
  6. {powercli-0.3.4 → powercli-0.3.6}/src/cli/commands/commands.py +10 -9
  7. {powercli-0.3.4 → powercli-0.3.6}/src/cli/commands/open_.py +1 -1
  8. {powercli-0.3.4 → powercli-0.3.6}/src/cli/commands/runner.py +6 -5
  9. {powercli-0.3.4 → powercli-0.3.6}/src/cli/output/message.py +1 -1
  10. {powercli-0.3.4 → powercli-0.3.6}/src/cli/output/progress.py +2 -2
  11. {powercli-0.3.4 → powercli-0.3.6}/src/cli/output/rich.py +1 -1
  12. {powercli-0.3.4 → powercli-0.3.6}/src/cli/output/status.py +2 -3
  13. {powercli-0.3.4 → powercli-0.3.6/src/powercli.egg-info}/PKG-INFO +8 -6
  14. {powercli-0.3.4 → powercli-0.3.6}/src/powercli.egg-info/SOURCES.txt +3 -1
  15. {powercli-0.3.4 → powercli-0.3.6}/src/powercli.egg-info/requires.txt +3 -2
  16. powercli-0.3.6/tests/test_cli_entry_point.py +16 -0
  17. powercli-0.3.6/tests/test_input.py +25 -0
  18. powercli-0.3.6/tests/test_output.py +30 -0
  19. {powercli-0.3.4 → powercli-0.3.6}/tests/test_progress.py +1 -1
  20. powercli-0.3.6/tests/test_run.py +93 -0
  21. {powercli-0.3.4 → powercli-0.3.6}/tests/test_runner.py +20 -23
  22. powercli-0.3.4/src/cli/commands/install.py +0 -31
  23. powercli-0.3.4/tests/test_cli_entry_point.py +0 -11
  24. {powercli-0.3.4 → powercli-0.3.6}/LICENSE +0 -0
  25. {powercli-0.3.4 → powercli-0.3.6}/setup.cfg +0 -0
  26. {powercli-0.3.4 → powercli-0.3.6}/src/cli/cli/__init__.py +0 -0
  27. {powercli-0.3.4 → powercli-0.3.6}/src/cli/cli/entry_point.py +0 -0
  28. {powercli-0.3.4 → powercli-0.3.6}/src/cli/commands/run.py +0 -0
  29. {powercli-0.3.4 → powercli-0.3.6}/src/cli/input.py +0 -0
  30. {powercli-0.3.4 → powercli-0.3.6}/src/cli/models/__init__.py +0 -0
  31. {powercli-0.3.4 → powercli-0.3.6}/src/cli/models/models.py +0 -0
  32. {powercli-0.3.4 → powercli-0.3.6}/src/cli/output/__init__.py +0 -0
  33. {powercli-0.3.4 → powercli-0.3.6}/src/cli/output/console.py +0 -0
  34. {powercli-0.3.4 → powercli-0.3.6}/src/cli/py.typed +0 -0
  35. {powercli-0.3.4 → powercli-0.3.6}/src/powercli.egg-info/dependency_links.txt +0 -0
  36. {powercli-0.3.4 → powercli-0.3.6}/src/powercli.egg-info/entry_points.txt +0 -0
  37. {powercli-0.3.4 → powercli-0.3.6}/src/powercli.egg-info/top_level.txt +0 -0
  38. {powercli-0.3.4 → powercli-0.3.6}/tests/test_message.py +0 -0
@@ -1,26 +1,28 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: powercli
3
- Version: 0.3.4
3
+ Version: 0.3.6
4
4
  Summary: High-level CLI interaction
5
5
  Author-email: Quinten Roets <qdr2104@columbia.edu>
6
- License: MIT
6
+ License-Expression: MIT
7
7
  Project-URL: Source Code, https://github.com/quintenroets/cli
8
8
  Requires-Python: >=3.10
9
9
  Description-Content-Type: text/markdown
10
10
  License-File: LICENSE
11
- Requires-Dist: rich<14,>=13.7.1
11
+ Requires-Dist: pexpect<5,>=4.9.0
12
+ Requires-Dist: rich<15,>=14.0.1
12
13
  Provides-Extra: dev
13
14
  Requires-Dist: hypothesis<7,>=6.97.1; extra == "dev"
14
- Requires-Dist: package-dev-tools<1,>=0.5.11; extra == "dev"
15
+ Requires-Dist: package-dev-tools<1,>=0.7.1; extra == "dev"
15
16
  Requires-Dist: package-dev-utils<1,>=0.1.6; extra == "dev"
16
17
  Requires-Dist: superpathlib<3,>=2.0.2; extra == "dev"
18
+ Dynamic: license-file
17
19
 
18
20
  # PowerCLI
19
21
  [![PyPI version](https://badge.fury.io/py/powercli.svg)](https://badge.fury.io/py/powercli)
20
22
  ![PyPI downloads](https://img.shields.io/pypi/dm/powercli)
21
23
  ![Python version](https://img.shields.io/badge/python-3.10+-brightgreen)
22
24
  ![Operating system](https://img.shields.io/badge/os-linux%20%7c%20macOS%20%7c%20windows-brightgreen)
23
- ![Coverage](https://img.shields.io/badge/coverage-82%25-brightgreen)
25
+ ![Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen)
24
26
 
25
27
  High-level CLI:
26
28
  * Run commands
@@ -3,7 +3,7 @@
3
3
  ![PyPI downloads](https://img.shields.io/pypi/dm/powercli)
4
4
  ![Python version](https://img.shields.io/badge/python-3.10+-brightgreen)
5
5
  ![Operating system](https://img.shields.io/badge/os-linux%20%7c%20macOS%20%7c%20windows-brightgreen)
6
- ![Coverage](https://img.shields.io/badge/coverage-82%25-brightgreen)
6
+ ![Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen)
7
7
 
8
8
  High-level CLI:
9
9
  * Run commands
@@ -1,19 +1,20 @@
1
1
  [project]
2
2
  name = "powercli"
3
- version = "0.3.4"
3
+ version = "0.3.6"
4
4
  description = "High-level CLI interaction"
5
5
  authors = [{name = "Quinten Roets", email = "qdr2104@columbia.edu"}]
6
- license = {text = "MIT"}
6
+ license = "MIT"
7
7
  readme = "README.md"
8
8
  requires-python = ">=3.10"
9
9
  dependencies = [
10
- "rich >=13.7.1, <14",
10
+ "pexpect >=4.9.0, <5",
11
+ "rich >=14.0.1, <15",
11
12
  ]
12
13
 
13
14
  [project.optional-dependencies]
14
15
  dev = [
15
16
  "hypothesis >=6.97.1, <7",
16
- "package-dev-tools >=0.5.11, <1",
17
+ "package-dev-tools >=0.7.1, <1",
17
18
  "package-dev-utils >=0.1.6, <1",
18
19
  "superpathlib >=2.0.2, <3",
19
20
  ]
@@ -33,7 +34,7 @@ command_line = "-m pytest tests"
33
34
 
34
35
  [tool.coverage.report]
35
36
  precision = 4
36
- fail_under = 75
37
+ fail_under = 100
37
38
 
38
39
  [tool.mypy]
39
40
  strict = true
@@ -3,7 +3,6 @@ from .commands import (
3
3
  capture_output_lines,
4
4
  capture_return_code,
5
5
  completes_successfully,
6
- install,
7
6
  launch,
8
7
  launch_commands,
9
8
  open_urls,
@@ -1,4 +1,3 @@
1
- from .install import install
2
1
  from .open_ import open_urls
3
2
  from .run import (
4
3
  capture_output,
@@ -1,19 +1,20 @@
1
1
  import os
2
2
  import shlex
3
- import types
4
3
  import typing
5
- from collections.abc import Iterable, Iterator
4
+ from collections.abc import Iterable, Iterator, Sequence
6
5
  from dataclasses import dataclass
7
6
  from functools import cached_property
8
7
  from pathlib import Path
9
- from typing import Protocol
8
+ from typing import Protocol, cast
10
9
 
11
10
 
12
11
  class StringLike(Protocol):
13
12
  def __str__(self) -> str: ...
14
13
 
15
14
 
16
- CommandItem = StringLike | dict[str, StringLike] | set[StringLike] | list[StringLike]
15
+ CommandItem = (
16
+ StringLike | dict[str, StringLike] | Sequence[StringLike] | Iterator[StringLike]
17
+ )
17
18
 
18
19
 
19
20
  @dataclass
@@ -42,7 +43,7 @@ class CommandPreparer:
42
43
  commands: tuple[str, ...]
43
44
  if self.should_use_root(command):
44
45
  if not command.startswith(self.root_keyword):
45
- command = f"{self.root_keyword } {command}"
46
+ command = f"{self.root_keyword} {command}"
46
47
  if self.askpass_is_available:
47
48
  command = command.replace(self.root_keyword, f"{self.root_keyword} -A")
48
49
  if self.use_console:
@@ -75,7 +76,7 @@ class CommandPreparer:
75
76
  if os.name == "posix":
76
77
  should_use_root = self.use_root or self.root_keyword in first_command_part
77
78
  else:
78
- should_use_root = False
79
+ should_use_root = False # pragma: nocover
79
80
  return should_use_root
80
81
 
81
82
  def generate_command_parts(self) -> Iterator[str]:
@@ -111,12 +112,12 @@ class CommandPreparer:
111
112
 
112
113
  @classmethod
113
114
  def extract_items(cls, item: CommandItem) -> Iterator[StringLike]:
114
- collection_types = list, tuple, types.GeneratorType
115
+ collection_types = list, tuple, Iterator
115
116
  is_collection = any(
116
117
  isinstance(item, collection) for collection in collection_types
117
118
  )
118
119
  if is_collection:
119
- yield from typing.cast(Iterable[StringLike], item)
120
+ yield from typing.cast("Iterable[StringLike]", item)
120
121
  elif isinstance(item, dict):
121
122
  for key, value in item.items():
122
123
  yield f"--{key}"
@@ -126,4 +127,4 @@ class CommandPreparer:
126
127
  for part in item:
127
128
  yield f"--{part}"
128
129
  elif hasattr(item, "__str__"):
129
- yield item
130
+ yield cast("str", item)
@@ -7,6 +7,6 @@ from .run import launch
7
7
  def open_urls(*urls: StringLike) -> None:
8
8
  for url in urls:
9
9
  if os.name == "nt":
10
- os.startfile(url) # type: ignore[attr-defined] # noqa: S606
10
+ os.startfile(url) # type: ignore[attr-defined] # noqa: S606 # pragma: nocover
11
11
  else:
12
12
  launch("xdg-open", url)
@@ -59,7 +59,7 @@ class Runner(Generic[T1]):
59
59
  import tempfile
60
60
 
61
61
  with tempfile.TemporaryFile() as untyped_log_file:
62
- log_file = typing.cast(io.TextIOWrapper, untyped_log_file)
62
+ log_file = typing.cast("io.TextIOWrapper", untyped_log_file)
63
63
  self.run_in_tty(log_file)
64
64
  log_file.seek(0)
65
65
  return log_file.read()
@@ -72,7 +72,7 @@ class Runner(Generic[T1]):
72
72
  if self.capture_output is not None:
73
73
  child.expect(pexpect.EOF)
74
74
  else:
75
- child.interact()
75
+ child.interact() # pragma: nocover
76
76
 
77
77
  def capture_output(self) -> str:
78
78
  return self.run(capture_output=True).stdout.strip()
@@ -135,9 +135,10 @@ class Runner(Generic[T1]):
135
135
  try:
136
136
  return runner(*args, **kwargs)
137
137
  except subprocess.CalledProcessError as exception:
138
- verbose = self.verbose_errors
139
- verbose_exception = CalledProcessError(exception.stderr or exception)
140
- raise verbose_exception from exception if verbose else exception
138
+ if self.verbose_errors:
139
+ verbose_exception = CalledProcessError(exception.stderr or exception)
140
+ raise verbose_exception from exception
141
+ raise
141
142
 
142
143
  def prepare_console_command(self) -> None:
143
144
  self.console = True
@@ -40,7 +40,7 @@ class Message:
40
40
  return self._message
41
41
 
42
42
  @message.setter
43
- def message(self, message: str) -> None:
43
+ def message(self, message: str | None) -> None:
44
44
  if message is not None:
45
45
  self.show(message)
46
46
 
@@ -5,9 +5,9 @@ from functools import cached_property
5
5
  from typing import TYPE_CHECKING, TypeVar
6
6
 
7
7
  if TYPE_CHECKING:
8
- from collections.abc import Iterable, Iterator
8
+ from collections.abc import Iterable, Iterator # pragma: nocover
9
9
 
10
- from rich.progress import Progress
10
+ from rich.progress import Progress # pragma: nocover
11
11
 
12
12
 
13
13
  T = TypeVar("T")
@@ -7,7 +7,7 @@ import rich
7
7
 
8
8
  T = TypeVar("T")
9
9
  if TYPE_CHECKING:
10
- from rich.console import Console
10
+ from rich.console import Console # pragma: nocover
11
11
 
12
12
 
13
13
  class ObjectProxy(Generic[T]):
@@ -6,11 +6,10 @@ from typing import TYPE_CHECKING, Any
6
6
  from .rich import console
7
7
 
8
8
  if TYPE_CHECKING:
9
- from rich.console import Status
9
+ from rich.console import Status # pragma: nocover
10
10
 
11
11
 
12
12
  def status(*args: Any, **kwargs: Any) -> Status | contextlib.nullcontext[None]:
13
- console.clear_live()
14
13
  use_status = not is_running_in_notebook()
15
14
  return console.status(*args, **kwargs) if use_status else contextlib.nullcontext()
16
15
 
@@ -21,5 +20,5 @@ def is_running_in_notebook() -> bool:
21
20
  except NameError:
22
21
  in_notebook = False
23
22
  else:
24
- in_notebook = True
23
+ in_notebook = True # pragma: nocover
25
24
  return in_notebook
@@ -1,26 +1,28 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: powercli
3
- Version: 0.3.4
3
+ Version: 0.3.6
4
4
  Summary: High-level CLI interaction
5
5
  Author-email: Quinten Roets <qdr2104@columbia.edu>
6
- License: MIT
6
+ License-Expression: MIT
7
7
  Project-URL: Source Code, https://github.com/quintenroets/cli
8
8
  Requires-Python: >=3.10
9
9
  Description-Content-Type: text/markdown
10
10
  License-File: LICENSE
11
- Requires-Dist: rich<14,>=13.7.1
11
+ Requires-Dist: pexpect<5,>=4.9.0
12
+ Requires-Dist: rich<15,>=14.0.1
12
13
  Provides-Extra: dev
13
14
  Requires-Dist: hypothesis<7,>=6.97.1; extra == "dev"
14
- Requires-Dist: package-dev-tools<1,>=0.5.11; extra == "dev"
15
+ Requires-Dist: package-dev-tools<1,>=0.7.1; extra == "dev"
15
16
  Requires-Dist: package-dev-utils<1,>=0.1.6; extra == "dev"
16
17
  Requires-Dist: superpathlib<3,>=2.0.2; extra == "dev"
18
+ Dynamic: license-file
17
19
 
18
20
  # PowerCLI
19
21
  [![PyPI version](https://badge.fury.io/py/powercli.svg)](https://badge.fury.io/py/powercli)
20
22
  ![PyPI downloads](https://img.shields.io/pypi/dm/powercli)
21
23
  ![Python version](https://img.shields.io/badge/python-3.10+-brightgreen)
22
24
  ![Operating system](https://img.shields.io/badge/os-linux%20%7c%20macOS%20%7c%20windows-brightgreen)
23
- ![Coverage](https://img.shields.io/badge/coverage-82%25-brightgreen)
25
+ ![Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen)
24
26
 
25
27
  High-level CLI:
26
28
  * Run commands
@@ -8,7 +8,6 @@ src/cli/cli/__init__.py
8
8
  src/cli/cli/entry_point.py
9
9
  src/cli/commands/__init__.py
10
10
  src/cli/commands/commands.py
11
- src/cli/commands/install.py
12
11
  src/cli/commands/open_.py
13
12
  src/cli/commands/run.py
14
13
  src/cli/commands/runner.py
@@ -27,6 +26,9 @@ src/powercli.egg-info/entry_points.txt
27
26
  src/powercli.egg-info/requires.txt
28
27
  src/powercli.egg-info/top_level.txt
29
28
  tests/test_cli_entry_point.py
29
+ tests/test_input.py
30
30
  tests/test_message.py
31
+ tests/test_output.py
31
32
  tests/test_progress.py
33
+ tests/test_run.py
32
34
  tests/test_runner.py
@@ -1,7 +1,8 @@
1
- rich<14,>=13.7.1
1
+ pexpect<5,>=4.9.0
2
+ rich<15,>=14.0.1
2
3
 
3
4
  [dev]
4
5
  hypothesis<7,>=6.97.1
5
- package-dev-tools<1,>=0.5.11
6
+ package-dev-tools<1,>=0.7.1
6
7
  package-dev-utils<1,>=0.1.6
7
8
  superpathlib<3,>=2.0.2
@@ -0,0 +1,16 @@
1
+ import contextlib
2
+ from unittest.mock import MagicMock, patch
3
+
4
+ from package_dev_utils.tests.args import cli_args
5
+
6
+ from cli import cli
7
+
8
+
9
+ @patch("subprocess.run")
10
+ @patch("subprocess.Popen")
11
+ @cli_args("ls")
12
+ def test_entry_point(mocked_popen: MagicMock, mocked_run: MagicMock) -> None:
13
+ with contextlib.suppress(FileNotFoundError):
14
+ cli.entry_point()
15
+ mocked_popen.assert_called_once()
16
+ mocked_run.assert_called_once()
@@ -0,0 +1,25 @@
1
+ from unittest.mock import MagicMock, patch
2
+
3
+ from rich.prompt import Confirm, Prompt
4
+
5
+ import cli
6
+
7
+ word = "hello"
8
+
9
+
10
+ @patch("builtins.input")
11
+ def test_ask(mocked_input: MagicMock) -> None:
12
+ cli.ask(word)
13
+ mocked_input.assert_called_once()
14
+
15
+
16
+ @patch.object(Prompt, "ask")
17
+ def test_prompt(mocked_ask: MagicMock) -> None:
18
+ cli.prompt(word)
19
+ mocked_ask.assert_called_once()
20
+
21
+
22
+ @patch.object(Confirm, "ask")
23
+ def test_confirm(mocked_confirm: MagicMock) -> None:
24
+ cli.confirm(word)
25
+ mocked_confirm.assert_called_once()
@@ -0,0 +1,30 @@
1
+ from collections.abc import Iterator
2
+ from unittest.mock import MagicMock, patch
3
+
4
+ import pytest
5
+
6
+ from cli.output.message import Message
7
+
8
+ from .test_progress import ITERATIONS
9
+
10
+
11
+ @pytest.fixture
12
+ def message() -> Iterator[Message]:
13
+ message = Message("hello")
14
+ with message:
15
+ yield message
16
+
17
+
18
+ def test_message(message: Message) -> None:
19
+ for i in range(ITERATIONS):
20
+ message.message = str(i)
21
+
22
+
23
+ @patch("os.get_terminal_size")
24
+ def test_create_header(mocked_terminal_size: MagicMock, message: Message) -> None:
25
+ mocked_terminal_size.columns = 100
26
+ message.create_header()
27
+
28
+
29
+ def test_extract_message(message: Message) -> None:
30
+ assert message.message
@@ -6,7 +6,7 @@ import pytest
6
6
  import cli
7
7
 
8
8
  SLEEP_INTERVAL = 0.01
9
- ITERATIONS = 200
9
+ ITERATIONS = 50
10
10
 
11
11
 
12
12
  # Best to inspect these tests manually
@@ -0,0 +1,93 @@
1
+ import subprocess
2
+ from unittest.mock import MagicMock, patch
3
+
4
+ import pytest
5
+ from hypothesis import given
6
+ from superpathlib import Path
7
+
8
+ import cli
9
+ from cli.commands.commands import CommandPreparer
10
+ from cli.output.console import set_title
11
+
12
+ from .test_runner import linux_only_test, text_strategy
13
+
14
+
15
+ @linux_only_test
16
+ def test_exception_handling() -> None:
17
+ with pytest.raises(cli.CalledProcessError):
18
+ cli.run("exit 1", shell=True) # noqa: S604
19
+
20
+
21
+ @linux_only_test
22
+ def test_non_verbose_exception_handling() -> None:
23
+ with pytest.raises(subprocess.CalledProcessError):
24
+ cli.run("exit 1", shell=True, verbose_errors=False) # noqa: S604
25
+
26
+
27
+ def test_command_not_found_exception_handling() -> None:
28
+ with pytest.raises(FileNotFoundError):
29
+ cli.run("non_existing_command")
30
+
31
+
32
+ def test_command_and_argument_combination() -> None:
33
+ cli.run("ls -l", "-a")
34
+
35
+
36
+ def test_cwd() -> None:
37
+ with Path.tempdir() as folder:
38
+ extracted_folder_name = cli.capture_output("pwd", cwd=folder).split("/")[-1]
39
+ assert extracted_folder_name == folder.name
40
+
41
+
42
+ @given(value=text_strategy())
43
+ @linux_only_test
44
+ def test_extra_subprocess_kwarg(value: str) -> None:
45
+ env = {"name": value}
46
+ assert cli.capture_output("echo", "$name", shell=True, env=env) == value # noqa: S604
47
+
48
+
49
+ def test_set_parsing() -> None:
50
+ commands = "python", {"version"}
51
+ cli.run(*commands)
52
+
53
+
54
+ def test_iterator_parsing() -> None:
55
+ commands = ("python", iter(["--version"]))
56
+ cli.run(*commands)
57
+
58
+
59
+ def test_dict_parsing() -> None:
60
+ commands = "git", {"work-tree": "."}, "status"
61
+ cli.run(*commands)
62
+
63
+
64
+ @patch("subprocess.run")
65
+ def test_title(mocked_popen: MagicMock) -> None:
66
+ cli.run("ls", title="ls", console=True)
67
+ mocked_popen.assert_called_once()
68
+
69
+
70
+ def test_set_title() -> None:
71
+ set_title(title="ls")
72
+
73
+
74
+ def test_sudo() -> None:
75
+ cli.run("sudo ls")
76
+
77
+
78
+ def test_root() -> None:
79
+ cli.run("ls", root=True)
80
+
81
+
82
+ @patch.object(CommandPreparer, "askpass_is_available", new=True)
83
+ def test_root_with_askpass_enabled() -> None:
84
+ cli.run("ls", root=True)
85
+
86
+
87
+ def test_root_in_shell() -> None:
88
+ cli.run("ls", root=True, shell=True) # noqa: S604
89
+
90
+
91
+ @patch.object(CommandPreparer, "askpass_is_available", new=True)
92
+ def test_root_in_shell_with_askpass_enabled() -> None:
93
+ cli.run("ls", root=True, shell=True) # noqa: S604
@@ -1,12 +1,13 @@
1
1
  import os
2
2
  import string
3
+ from unittest.mock import patch
3
4
 
4
5
  import pytest
5
6
  from hypothesis import given, settings, strategies
6
7
  from hypothesis.strategies import SearchStrategy
7
- from superpathlib import Path
8
8
 
9
9
  import cli
10
+ from cli.commands.runner import Runner
10
11
 
11
12
  linux_only_test = pytest.mark.skipif(
12
13
  os.name != "posix",
@@ -28,7 +29,7 @@ def test_capture_output_lines(message: str) -> None:
28
29
  assert cli.capture_output_lines("printf", "%s", message) == message.splitlines()
29
30
 
30
31
 
31
- @settings(deadline=1000)
32
+ @settings(deadline=3000)
32
33
  @given(message=text_strategy())
33
34
  def test_pipe_output_and_capture(message: str) -> None:
34
35
  commands = (
@@ -51,34 +52,30 @@ def test_completes_successfully(return_code: int) -> None:
51
52
  assert cli.completes_successfully("exit", return_code, shell=True) == success # noqa: S604
52
53
 
53
54
 
54
- @linux_only_test
55
- def test_exception_handling() -> None:
56
- with pytest.raises(cli.CalledProcessError):
57
- cli.run("exit 1", shell=True) # noqa: S604
55
+ def test_run_commands() -> None:
56
+ commands = ("ls", "pwd")
57
+ cli.run_commands(*commands)
58
58
 
59
59
 
60
- def test_command_not_found_exception_handling() -> None:
61
- with pytest.raises(FileNotFoundError):
62
- cli.run("non_existing_command")
60
+ def test_launch() -> None:
61
+ cli.launch("ls")
63
62
 
64
63
 
65
- def test_command_and_argument_combination() -> None:
66
- cli.run("ls -l", "-a")
64
+ def test_launch_commands() -> None:
65
+ cli.launch_commands("ls")
67
66
 
68
67
 
69
- def test_run_commands() -> None:
70
- commands = ("ls", "pwd")
71
- cli.run_commands(*commands)
68
+ def test_run_commands_in_shell() -> None:
69
+ cli.run_commands_in_shell("ls")
72
70
 
73
71
 
74
- def test_cwd() -> None:
75
- with Path.tempdir() as folder:
76
- extracted_folder_name = cli.capture_output("pwd", cwd=folder).split("/")[-1]
77
- assert extracted_folder_name == folder.name
72
+ def test_open() -> None:
73
+ open_function = "os.startfile" if os.name == "nt" else "subprocess.Popen"
74
+ with patch(open_function) as mocked_open:
75
+ cli.open_urls("pwd")
76
+ mocked_open.assert_called_once()
78
77
 
79
78
 
80
- @given(value=text_strategy())
81
- @linux_only_test
82
- def test_extra_subprocess_kwarg(value: str) -> None:
83
- env = {"name": value}
84
- assert cli.capture_output("echo", "$name", shell=True, env=env) == value # noqa: S604
79
+ def test_tty() -> None:
80
+ if os.name != "nt": # not supported on Windows
81
+ Runner(items=["ls"]).capture_tty_output()
@@ -1,31 +0,0 @@
1
- import platform
2
- import shlex
3
- import warnings
4
- from collections.abc import Iterator
5
-
6
- from .run import completes_successfully, run
7
-
8
-
9
- def install(*packages: str, install_command: str | None = None) -> None:
10
- is_linux = platform.system() == "Linux"
11
- if is_linux:
12
- _install(*packages, install_command=install_command)
13
- else:
14
- message = "Required packages can only be installed on Linux"
15
- warnings.warn(message, stacklevel=2)
16
-
17
-
18
- def _install(*packages: str, install_command: str | None = None) -> None:
19
- if install_command is None:
20
- install_command = next(extract_package_manager_command(), None)
21
- assert install_command is not None
22
- for package in packages:
23
- args = shlex.split(package)
24
- run(install_command, *args, root=True, check=False)
25
-
26
-
27
- def extract_package_manager_command() -> Iterator[str]:
28
- commands = {"apt": "apt install -y", "pacman": "pacman -S --noconfirm"}
29
- for package_manager, command in commands.items():
30
- if completes_successfully("which", package_manager):
31
- yield command
@@ -1,11 +0,0 @@
1
- import contextlib
2
-
3
- from package_dev_utils.tests.args import cli_args
4
-
5
- from cli import cli
6
-
7
-
8
- @cli_args("ls")
9
- def test_entry_point() -> None:
10
- with contextlib.suppress(FileNotFoundError):
11
- cli.entry_point()
File without changes
File without changes
File without changes
File without changes
File without changes