py-wait-until 0.0.4__tar.gz → 0.0.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.
- {py_wait_until-0.0.4 → py_wait_until-0.0.6}/PKG-INFO +12 -1
- {py_wait_until-0.0.4 → py_wait_until-0.0.6}/README.md +11 -0
- {py_wait_until-0.0.4 → py_wait_until-0.0.6}/pyproject.toml +1 -1
- {py_wait_until-0.0.4 → py_wait_until-0.0.6}/pyproject.toml.orig +1 -1
- py_wait_until-0.0.6/src/py_wait_until/braille-chars.txt +15 -0
- {py_wait_until-0.0.4 → py_wait_until-0.0.6}/src/py_wait_until/main.py +60 -10
- {py_wait_until-0.0.4 → py_wait_until-0.0.6}/LICENSE +0 -0
- {py_wait_until-0.0.4 → py_wait_until-0.0.6}/src/py_wait_until/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: py-wait-until
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.6
|
|
4
4
|
Summary: Print a loading spinner while another command is running.
|
|
5
5
|
Author: billwallis
|
|
6
6
|
License-File: LICENSE
|
|
@@ -16,6 +16,7 @@ Description-Content-Type: text/markdown
|
|
|
16
16
|
|
|
17
17
|
[](https://results.pre-commit.ci/latest/github/billwallis/py-wait-until/main)
|
|
18
18
|
[](https://shields.io/badges/git-hub-last-commit)
|
|
19
|
+
[](https://shields.io/badges/py-pi-downloads)
|
|
19
20
|
|
|
20
21
|
</span>
|
|
21
22
|
|
|
@@ -61,3 +62,13 @@ Install [uv](https://docs.astral.sh/uv/getting-started/installation/) and then i
|
|
|
61
62
|
```shell
|
|
62
63
|
uvx --from poethepoet poe install
|
|
63
64
|
```
|
|
65
|
+
|
|
66
|
+
### Choose a spinner
|
|
67
|
+
|
|
68
|
+
There are a selection of spinner animations available which can be specified with the `--spinner` argument; check the CLI help for the latest set of options:
|
|
69
|
+
|
|
70
|
+
```shell
|
|
71
|
+
py-wait-until --help
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
By default, a spinner will be chosen randomly.
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
[](https://results.pre-commit.ci/latest/github/billwallis/py-wait-until/main)
|
|
9
9
|
[](https://shields.io/badges/git-hub-last-commit)
|
|
10
|
+
[](https://shields.io/badges/py-pi-downloads)
|
|
10
11
|
|
|
11
12
|
</span>
|
|
12
13
|
|
|
@@ -52,3 +53,13 @@ Install [uv](https://docs.astral.sh/uv/getting-started/installation/) and then i
|
|
|
52
53
|
```shell
|
|
53
54
|
uvx --from poethepoet poe install
|
|
54
55
|
```
|
|
56
|
+
|
|
57
|
+
### Choose a spinner
|
|
58
|
+
|
|
59
|
+
There are a selection of spinner animations available which can be specified with the `--spinner` argument; check the CLI help for the latest set of options:
|
|
60
|
+
|
|
61
|
+
```shell
|
|
62
|
+
py-wait-until --help
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
By default, a spinner will be chosen randomly.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import argparse
|
|
2
|
+
import contextlib
|
|
2
3
|
import enum
|
|
3
4
|
import random
|
|
4
5
|
import subprocess
|
|
@@ -17,11 +18,16 @@ class Spinner(enum.StrEnum):
|
|
|
17
18
|
LINE = "line"
|
|
18
19
|
DOTS = "dots"
|
|
19
20
|
SAND = "sand"
|
|
21
|
+
CLIMB = "climb"
|
|
22
|
+
EIGHT_WIDTH_2 = "eight-2"
|
|
23
|
+
EIGHT_WIDTH_3 = "eight-3"
|
|
24
|
+
EIGHT_WIDTH_4 = "eight-4"
|
|
20
25
|
|
|
21
26
|
def chars(self) -> Sequence[str]:
|
|
22
27
|
return {
|
|
23
28
|
# Basic/simple option
|
|
24
29
|
self.LINE: ["|", "/", "-", "\\"],
|
|
30
|
+
# https://en.wikipedia.org/wiki/Braille_ASCII#Braille_ASCII_values
|
|
25
31
|
# Inspired by Docker and dbt Fusion's loading indicator
|
|
26
32
|
self.DOTS: [
|
|
27
33
|
*["⠄", "⠆", "⠖", "⠶", "⠲", "⠰"],
|
|
@@ -29,6 +35,21 @@ class Spinner(enum.StrEnum):
|
|
|
29
35
|
*["⠐", "⠰", "⠴", "⠶", "⠦", "⠆"],
|
|
30
36
|
*["⠂", "⠒", "⠲", "⠶", "⠴", "⠤"],
|
|
31
37
|
],
|
|
38
|
+
self.CLIMB: [
|
|
39
|
+
*["⠄", "⠤", "⠴", "⠶", "⠷", "⠿"],
|
|
40
|
+
*["⠾", "⠶", "⠦", "⠤", "⠠", " "],
|
|
41
|
+
],
|
|
42
|
+
self.EIGHT_WIDTH_2: [
|
|
43
|
+
*["⠤", "⠰", "⠒", "⠃"],
|
|
44
|
+
*["⠉", "⠘", "⠒", "⠆"],
|
|
45
|
+
],
|
|
46
|
+
self.EIGHT_WIDTH_3: [
|
|
47
|
+
*["⠦", "⠴", "⠲", "⠓", "⠋", "⠙", "⠚", "⠖"],
|
|
48
|
+
],
|
|
49
|
+
self.EIGHT_WIDTH_4: [
|
|
50
|
+
*["⠛", "⠏", "⠭", "⠼"],
|
|
51
|
+
*["⠶", "⠧", "⠭", "⠹"],
|
|
52
|
+
],
|
|
32
53
|
# https://stackoverflow.com/a/12305221/8213085
|
|
33
54
|
# https://raw.githubusercontent.com/sindresorhus/cli-spinners/master/spinners.json
|
|
34
55
|
self.SAND: [
|
|
@@ -61,19 +82,23 @@ def _spinner(chars: Sequence[str]) -> Generator[str]:
|
|
|
61
82
|
yield chars[index % len(chars)]
|
|
62
83
|
|
|
63
84
|
|
|
85
|
+
@contextlib.contextmanager
|
|
86
|
+
def hide_cursor(is_tty: bool) -> Generator[None]:
|
|
87
|
+
if is_tty:
|
|
88
|
+
_write_line(sys.stdout, ANSI_HIDE_CURSOR)
|
|
89
|
+
|
|
90
|
+
yield
|
|
91
|
+
|
|
92
|
+
if is_tty:
|
|
93
|
+
_write_line(sys.stdout, ANSI_SHOW_CURSOR)
|
|
94
|
+
|
|
95
|
+
|
|
64
96
|
def _wait_for_process(
|
|
65
97
|
cmd: list[str],
|
|
66
98
|
message: str,
|
|
67
99
|
spinner: Spinner,
|
|
100
|
+
is_tty: bool,
|
|
68
101
|
) -> int:
|
|
69
|
-
"""
|
|
70
|
-
Execute a command and display a waiting message until it completes.
|
|
71
|
-
"""
|
|
72
|
-
|
|
73
|
-
# Only show spinner if stdout is a TTY (interactive terminal)
|
|
74
|
-
if is_tty := sys.stdout.isatty():
|
|
75
|
-
_write_line(sys.stdout, ANSI_HIDE_CURSOR)
|
|
76
|
-
|
|
77
102
|
process = subprocess.Popen( # noqa: S603
|
|
78
103
|
cmd,
|
|
79
104
|
stdout=subprocess.PIPE,
|
|
@@ -92,7 +117,6 @@ def _wait_for_process(
|
|
|
92
117
|
|
|
93
118
|
if is_tty:
|
|
94
119
|
_write_line(sys.stdout, f"\r{ANSI_CLEAR_LINE}")
|
|
95
|
-
_write_line(sys.stdout, ANSI_SHOW_CURSOR)
|
|
96
120
|
|
|
97
121
|
stdout, stderr = process.communicate()
|
|
98
122
|
if stdout:
|
|
@@ -103,6 +127,32 @@ def _wait_for_process(
|
|
|
103
127
|
return process.returncode
|
|
104
128
|
|
|
105
129
|
|
|
130
|
+
def _wait_until(
|
|
131
|
+
cmd: list[str],
|
|
132
|
+
message: str,
|
|
133
|
+
spinner: Spinner,
|
|
134
|
+
) -> int:
|
|
135
|
+
"""
|
|
136
|
+
Execute a command and display a waiting message until it completes.
|
|
137
|
+
"""
|
|
138
|
+
|
|
139
|
+
# TODO: If _not_ a TTY, then just run the process; else run `_wait_for_process`?
|
|
140
|
+
# Only show spinner if stdout is a TTY (interactive terminal)
|
|
141
|
+
is_tty = sys.stdout.isatty()
|
|
142
|
+
with hide_cursor(is_tty=is_tty):
|
|
143
|
+
try:
|
|
144
|
+
return _wait_for_process(
|
|
145
|
+
cmd=cmd,
|
|
146
|
+
message=message,
|
|
147
|
+
spinner=spinner,
|
|
148
|
+
is_tty=is_tty,
|
|
149
|
+
)
|
|
150
|
+
# We purposefully want to catch _everything_, including e.g. `KeyboardInterrupt`
|
|
151
|
+
except BaseException as err:
|
|
152
|
+
_write_line(sys.stderr, str(err))
|
|
153
|
+
return 1
|
|
154
|
+
|
|
155
|
+
|
|
106
156
|
def main(argv: Sequence[str] | None = None) -> int:
|
|
107
157
|
"""
|
|
108
158
|
Print a loading spinner while another process is running.
|
|
@@ -138,7 +188,7 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
138
188
|
parser.print_help()
|
|
139
189
|
return 1
|
|
140
190
|
|
|
141
|
-
return
|
|
191
|
+
return _wait_until(
|
|
142
192
|
cmd=args.command,
|
|
143
193
|
message=args.message,
|
|
144
194
|
spinner=args.spinner,
|
|
File without changes
|
|
File without changes
|