gruyere 0.0.1__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.
- gruyere-0.0.1/.python-version +1 -0
- gruyere-0.0.1/PKG-INFO +72 -0
- gruyere-0.0.1/README.md +47 -0
- gruyere-0.0.1/gruyere/__init__.py +3 -0
- gruyere-0.0.1/gruyere/main.py +331 -0
- gruyere-0.0.1/gruyere.egg-info/PKG-INFO +9 -0
- gruyere-0.0.1/gruyere.egg-info/SOURCES.txt +9 -0
- gruyere-0.0.1/gruyere.egg-info/dependency_links.txt +1 -0
- gruyere-0.0.1/gruyere.egg-info/entry_points.txt +2 -0
- gruyere-0.0.1/gruyere.egg-info/requires.txt +3 -0
- gruyere-0.0.1/gruyere.egg-info/top_level.txt +1 -0
- gruyere-0.0.1/gruyere_py.egg-info/PKG-INFO +9 -0
- gruyere-0.0.1/gruyere_py.egg-info/SOURCES.txt +9 -0
- gruyere-0.0.1/gruyere_py.egg-info/dependency_links.txt +1 -0
- gruyere-0.0.1/gruyere_py.egg-info/entry_points.txt +2 -0
- gruyere-0.0.1/gruyere_py.egg-info/requires.txt +3 -0
- gruyere-0.0.1/gruyere_py.egg-info/top_level.txt +1 -0
- gruyere-0.0.1/pyproject.toml +54 -0
- gruyere-0.0.1/uv.lock +208 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.14
|
gruyere-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gruyere
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: A tiny program for viewing and killing processes on ports
|
|
5
|
+
Project-URL: Homepage, https://github.com/yourusername/gruyere
|
|
6
|
+
Project-URL: Repository, https://github.com/yourusername/gruyere
|
|
7
|
+
Project-URL: Issues, https://github.com/yourusername/gruyere/issues
|
|
8
|
+
Author-email: Savannah Ostrowski <savannah@python.org>
|
|
9
|
+
License: MIT
|
|
10
|
+
Keywords: kill,lsof,ports,processes,tui
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: MacOS
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: System :: Monitoring
|
|
20
|
+
Classifier: Topic :: Utilities
|
|
21
|
+
Requires-Python: >=3.12
|
|
22
|
+
Requires-Dist: readchar>=4.2.1
|
|
23
|
+
Requires-Dist: rich>=14.2.0
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# Gruyere
|
|
27
|
+
|
|
28
|
+
A tiny, beautiful TUI program for viewing and killing processes listening on ports.
|
|
29
|
+
|
|
30
|
+
## Features
|
|
31
|
+
|
|
32
|
+
- Beautiful gradient UI with rich colors
|
|
33
|
+
- Filter processes by command name
|
|
34
|
+
- Vim-style navigation (j/k) or arrow keys
|
|
35
|
+
- Kill processes with confirmation dialog
|
|
36
|
+
- Pagination for many processes
|
|
37
|
+
|
|
38
|
+
## Installation
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
uv tool install gruyere
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Or with pipx:
|
|
45
|
+
```bash
|
|
46
|
+
pipx install gruyere
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Usage
|
|
50
|
+
|
|
51
|
+
Simply run:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
gruyere
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Controls
|
|
58
|
+
|
|
59
|
+
- Up/k: Move up
|
|
60
|
+
- Down/j: Move down
|
|
61
|
+
- /: Filter processes
|
|
62
|
+
- ENTER: Kill selected process
|
|
63
|
+
- q: Quit
|
|
64
|
+
|
|
65
|
+
## Requirements
|
|
66
|
+
|
|
67
|
+
- Python 3.12+
|
|
68
|
+
- macOS or Linux (uses lsof)
|
|
69
|
+
|
|
70
|
+
## License
|
|
71
|
+
|
|
72
|
+
MIT
|
gruyere-0.0.1/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Gruyere
|
|
2
|
+
|
|
3
|
+
A tiny, beautiful TUI program for viewing and killing processes listening on ports.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Beautiful gradient UI with rich colors
|
|
8
|
+
- Filter processes by command name
|
|
9
|
+
- Vim-style navigation (j/k) or arrow keys
|
|
10
|
+
- Kill processes with confirmation dialog
|
|
11
|
+
- Pagination for many processes
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
uv tool install gruyere
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or with pipx:
|
|
20
|
+
```bash
|
|
21
|
+
pipx install gruyere
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
Simply run:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
gruyere
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Controls
|
|
33
|
+
|
|
34
|
+
- Up/k: Move up
|
|
35
|
+
- Down/j: Move down
|
|
36
|
+
- /: Filter processes
|
|
37
|
+
- ENTER: Kill selected process
|
|
38
|
+
- q: Quit
|
|
39
|
+
|
|
40
|
+
## Requirements
|
|
41
|
+
|
|
42
|
+
- Python 3.12+
|
|
43
|
+
- macOS or Linux (uses lsof)
|
|
44
|
+
|
|
45
|
+
## License
|
|
46
|
+
|
|
47
|
+
MIT
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
import signal
|
|
3
|
+
import subprocess
|
|
4
|
+
import sys
|
|
5
|
+
from typing import List, Optional
|
|
6
|
+
|
|
7
|
+
from readchar import readkey, key
|
|
8
|
+
from rich.color import Color
|
|
9
|
+
from rich.console import Console, Group
|
|
10
|
+
from rich.live import Live
|
|
11
|
+
from rich.style import Style
|
|
12
|
+
from rich.panel import Panel
|
|
13
|
+
from rich.text import Text
|
|
14
|
+
from rich import box
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class Process:
|
|
19
|
+
pid: int
|
|
20
|
+
port: int | str
|
|
21
|
+
user: str
|
|
22
|
+
command: str
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
SELECTED_COLOR = Style(color="#EE6FF8", bold=True)
|
|
26
|
+
|
|
27
|
+
signal.signal(signal.SIGINT, lambda _, __: sys.exit(0))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _clear_screen():
|
|
31
|
+
"""Clear the entire screen and scroll buffer using ANSI escape codes."""
|
|
32
|
+
sys.stdout.write("\033[2J\033[3J\033[H")
|
|
33
|
+
sys.stdout.flush()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _parse_port(port_str: str) -> int | str:
|
|
37
|
+
try:
|
|
38
|
+
return int(port_str)
|
|
39
|
+
except (ValueError, IndexError):
|
|
40
|
+
return port_str
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _get_processes() -> list[Process]:
|
|
44
|
+
raw_processes = subprocess.run(
|
|
45
|
+
["lsof", "-i", "-P", "-n", "-sTCP:LISTEN"], capture_output=True, text=True
|
|
46
|
+
)
|
|
47
|
+
processes: list[Process] = []
|
|
48
|
+
|
|
49
|
+
for line in raw_processes.stdout.splitlines()[1:]:
|
|
50
|
+
parts = line.split()
|
|
51
|
+
if len(parts) >= 9:
|
|
52
|
+
pid = int(parts[1])
|
|
53
|
+
user = parts[2]
|
|
54
|
+
command = parts[0]
|
|
55
|
+
port = _parse_port(parts[8].split(":")[-1])
|
|
56
|
+
process = Process(pid=pid, port=port, user=user, command=command)
|
|
57
|
+
processes.append(process)
|
|
58
|
+
|
|
59
|
+
return processes
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _kill_process(pid: int):
|
|
63
|
+
subprocess.run(["kill", "-9", str(pid)])
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _show_pagination_indicator(total: int, selected: int, panels: list[Panel | str]):
|
|
67
|
+
"""Add pagination indicator dots to the panels list if needed."""
|
|
68
|
+
if total <= len(panels):
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
page = selected // len(panels) % 4
|
|
72
|
+
indicator = ""
|
|
73
|
+
for i in range((total + len(panels) - 1) // len(panels)):
|
|
74
|
+
if i == page:
|
|
75
|
+
indicator += "● "
|
|
76
|
+
else:
|
|
77
|
+
indicator += "○ "
|
|
78
|
+
|
|
79
|
+
panels.append(f" [dim]{indicator}[/dim]")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _render_processes_table(processes: List[Process], selected: int):
|
|
83
|
+
max_display = 4
|
|
84
|
+
|
|
85
|
+
if len(processes) <= max_display:
|
|
86
|
+
display_processes = processes
|
|
87
|
+
display_selected = selected
|
|
88
|
+
else:
|
|
89
|
+
if selected < max_display // 2:
|
|
90
|
+
display_processes = processes[:max_display]
|
|
91
|
+
display_selected = selected
|
|
92
|
+
elif selected >= len(processes) - max_display // 2:
|
|
93
|
+
display_processes = processes[-max_display:]
|
|
94
|
+
display_selected = selected - (len(processes) - max_display)
|
|
95
|
+
else:
|
|
96
|
+
start = selected - max_display // 2
|
|
97
|
+
display_processes = processes[start : start + max_display]
|
|
98
|
+
display_selected = max_display // 2
|
|
99
|
+
|
|
100
|
+
panels: list[Panel | str] = []
|
|
101
|
+
for i, process in enumerate(display_processes):
|
|
102
|
+
# Add vertical indicator for selected item
|
|
103
|
+
indicator = "▐ " if i == display_selected else " "
|
|
104
|
+
|
|
105
|
+
port_line = f"{indicator}[bold]Port: {process.port} (PID: {process.pid})[/bold]"
|
|
106
|
+
user_line = (
|
|
107
|
+
f"{indicator}[dim]User: {process.user}, Command: {process.command}[/dim]"
|
|
108
|
+
)
|
|
109
|
+
content = f"{port_line}\n{user_line}"
|
|
110
|
+
|
|
111
|
+
# All items get a panel with no border
|
|
112
|
+
panel = Panel(
|
|
113
|
+
content,
|
|
114
|
+
style=SELECTED_COLOR if i == display_selected else "",
|
|
115
|
+
box=box.SIMPLE,
|
|
116
|
+
)
|
|
117
|
+
panels.append(panel)
|
|
118
|
+
|
|
119
|
+
# Add empty panels to always show 4 slots
|
|
120
|
+
while len(panels) < max_display:
|
|
121
|
+
empty_panel = Panel("\n", box=box.SIMPLE, style="")
|
|
122
|
+
panels.append(empty_panel)
|
|
123
|
+
|
|
124
|
+
_show_pagination_indicator(len(processes), selected, panels)
|
|
125
|
+
|
|
126
|
+
help_text = "[bold dim]Commands: ↑/k: Up | ↓/j: Down | ENTER: Kill | /: Filter | q: Quit [/bold dim]"
|
|
127
|
+
return Group(*panels, Panel(help_text, box=box.SIMPLE))
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _colorGrid(x_steps: int, y_steps: int) -> List[List[Style]]:
|
|
131
|
+
"""
|
|
132
|
+
Generate a 2D grid of gradient colors using bilinear interpolation.
|
|
133
|
+
Via https://github.com/charmbracelet/lipgloss/blob/776c15f0da16d2b1058a079ec6a08a2e1170d721/examples/layout/main.go#L338
|
|
134
|
+
"""
|
|
135
|
+
x0y0 = Color.parse("#EE6FF8")
|
|
136
|
+
x1y0 = Color.parse("#EDFF82")
|
|
137
|
+
x0y1 = Color.parse("#643AFF")
|
|
138
|
+
x1y1 = Color.parse("#14F9D5")
|
|
139
|
+
|
|
140
|
+
# Get RGB triplets
|
|
141
|
+
x0y0_rgb = x0y0.get_truecolor()
|
|
142
|
+
x1y0_rgb = x1y0.get_truecolor()
|
|
143
|
+
x0y1_rgb = x0y1.get_truecolor()
|
|
144
|
+
x1y1_rgb = x1y1.get_truecolor()
|
|
145
|
+
|
|
146
|
+
grid: List[List[Style]] = []
|
|
147
|
+
for y in range(y_steps):
|
|
148
|
+
row: List[Style] = []
|
|
149
|
+
for x in range(x_steps):
|
|
150
|
+
rx = x / (x_steps - 1) if x_steps > 1 else 0
|
|
151
|
+
ry = y / (y_steps - 1) if y_steps > 1 else 0
|
|
152
|
+
|
|
153
|
+
r = int(
|
|
154
|
+
(1 - rx) * (1 - ry) * x0y0_rgb.red
|
|
155
|
+
+ rx * (1 - ry) * x1y0_rgb.red
|
|
156
|
+
+ (1 - rx) * ry * x0y1_rgb.red
|
|
157
|
+
+ rx * ry * x1y1_rgb.red
|
|
158
|
+
)
|
|
159
|
+
g = int(
|
|
160
|
+
(1 - rx) * (1 - ry) * x0y0_rgb.green
|
|
161
|
+
+ rx * (1 - ry) * x1y0_rgb.green
|
|
162
|
+
+ (1 - rx) * ry * x0y1_rgb.green
|
|
163
|
+
+ rx * ry * x1y1_rgb.green
|
|
164
|
+
)
|
|
165
|
+
b = int(
|
|
166
|
+
(1 - rx) * (1 - ry) * x0y0_rgb.blue
|
|
167
|
+
+ rx * (1 - ry) * x1y0_rgb.blue
|
|
168
|
+
+ (1 - rx) * ry * x0y1_rgb.blue
|
|
169
|
+
+ rx * ry * x1y1_rgb.blue
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
row.append(Style(color=Color.from_rgb(r, g, b)))
|
|
173
|
+
grid.append(row)
|
|
174
|
+
|
|
175
|
+
return grid
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _render_title():
|
|
179
|
+
colors = _colorGrid(1, 5)
|
|
180
|
+
title = "Gruyere"
|
|
181
|
+
|
|
182
|
+
desc = "A tiny program for viewing + killing ports"
|
|
183
|
+
divider = "─" * len(desc)
|
|
184
|
+
subtext = "Here's what's running..."
|
|
185
|
+
|
|
186
|
+
base_spacing = " " # Space between title and description
|
|
187
|
+
desc_start_col = len(title) + len(base_spacing) # Column where description starts
|
|
188
|
+
|
|
189
|
+
# Create layered text effect with white text on colored backgrounds
|
|
190
|
+
text = Text()
|
|
191
|
+
|
|
192
|
+
right_texts: List[Optional[str]] = [desc, divider, subtext, None, None]
|
|
193
|
+
|
|
194
|
+
for i in range(5):
|
|
195
|
+
indent = " " * (i * 2)
|
|
196
|
+
text.append(indent)
|
|
197
|
+
text.append(title, style=Style(color="white", bgcolor=colors[i][0].color))
|
|
198
|
+
|
|
199
|
+
right_text = right_texts[i]
|
|
200
|
+
if right_text is not None:
|
|
201
|
+
text.append(" " * (desc_start_col - len(indent) - len(title)))
|
|
202
|
+
text.append(right_text, style=Style(dim=True))
|
|
203
|
+
|
|
204
|
+
text.append("\n")
|
|
205
|
+
|
|
206
|
+
return text
|
|
207
|
+
|
|
208
|
+
def _show_confirmation_view(console: Console, process: Process, title: Text) -> bool:
|
|
209
|
+
# Screen should already be cleared before calling this function
|
|
210
|
+
console.print(Panel(title, box=box.SIMPLE))
|
|
211
|
+
console.print(
|
|
212
|
+
Panel(
|
|
213
|
+
f"[bold]Are you sure you want to kill the process?[/bold]\n\n"
|
|
214
|
+
f"[bold]PID:[/bold] {process.pid}\n"
|
|
215
|
+
f"[bold]Port:[/bold] {process.port}\n"
|
|
216
|
+
f"[bold]User:[/bold] {process.user}\n"
|
|
217
|
+
f"[bold]Command:[/bold] {process.command}\n\n"
|
|
218
|
+
f"[dim]Press Y to confirm, N to cancel.[/dim]",
|
|
219
|
+
border_style="#EE6FF8",
|
|
220
|
+
expand=False,
|
|
221
|
+
)
|
|
222
|
+
)
|
|
223
|
+
while True:
|
|
224
|
+
ch = readkey()
|
|
225
|
+
if ch.lower() == "y":
|
|
226
|
+
return True
|
|
227
|
+
elif ch.lower() == "n":
|
|
228
|
+
return False
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def main():
|
|
233
|
+
console = Console()
|
|
234
|
+
text = _render_title()
|
|
235
|
+
processes = _get_processes()
|
|
236
|
+
selected = 0
|
|
237
|
+
filter_text = ""
|
|
238
|
+
is_filtering = False
|
|
239
|
+
running = True
|
|
240
|
+
|
|
241
|
+
console.clear()
|
|
242
|
+
console.print(Panel(text, box=box.SIMPLE))
|
|
243
|
+
|
|
244
|
+
while running:
|
|
245
|
+
process_to_kill = None
|
|
246
|
+
|
|
247
|
+
with Live(
|
|
248
|
+
_render_processes_table(processes, selected),
|
|
249
|
+
console=console,
|
|
250
|
+
refresh_per_second=10,
|
|
251
|
+
) as live:
|
|
252
|
+
while ch := readkey():
|
|
253
|
+
if is_filtering:
|
|
254
|
+
if ch == "/":
|
|
255
|
+
is_filtering = False
|
|
256
|
+
filter_text = ""
|
|
257
|
+
processes = _get_processes()
|
|
258
|
+
live.update(_render_processes_table(processes, selected))
|
|
259
|
+
continue
|
|
260
|
+
elif ch == key.UP or ch == "k":
|
|
261
|
+
selected = max(0, selected - 1)
|
|
262
|
+
elif ch == key.DOWN or ch == "j":
|
|
263
|
+
selected = min(len(processes) - 1, selected + 1)
|
|
264
|
+
elif ch == key.BACKSPACE:
|
|
265
|
+
filter_text = filter_text[:-1]
|
|
266
|
+
processes = [
|
|
267
|
+
p
|
|
268
|
+
for p in _get_processes()
|
|
269
|
+
if filter_text.lower() in p.command.lower()
|
|
270
|
+
]
|
|
271
|
+
selected = 0
|
|
272
|
+
elif ch == key.ENTER:
|
|
273
|
+
is_filtering = False
|
|
274
|
+
process_to_kill = processes[selected] if processes else None
|
|
275
|
+
break
|
|
276
|
+
elif len(ch) == 1 and ch.isprintable():
|
|
277
|
+
filter_text += ch
|
|
278
|
+
processes = [
|
|
279
|
+
p
|
|
280
|
+
for p in _get_processes()
|
|
281
|
+
if filter_text.lower() in p.command.lower()
|
|
282
|
+
]
|
|
283
|
+
selected = 0
|
|
284
|
+
|
|
285
|
+
filter_panel = Panel(
|
|
286
|
+
f"[bold magenta]Filter:[/bold magenta] {filter_text}[blink]_[/blink]",
|
|
287
|
+
title="Press / to cancel, ENTER to apply",
|
|
288
|
+
border_style="magenta",
|
|
289
|
+
)
|
|
290
|
+
display = Group(
|
|
291
|
+
filter_panel, _render_processes_table(processes, selected)
|
|
292
|
+
)
|
|
293
|
+
live.update(display)
|
|
294
|
+
else:
|
|
295
|
+
if ch == key.UP or ch == "k":
|
|
296
|
+
selected = max(0, selected - 1)
|
|
297
|
+
elif ch == key.DOWN or ch == "j":
|
|
298
|
+
selected = min(len(processes) - 1, selected + 1)
|
|
299
|
+
elif ch == "/":
|
|
300
|
+
is_filtering = True
|
|
301
|
+
filter_text = ""
|
|
302
|
+
filter_panel = Panel(
|
|
303
|
+
f"[bold magenta]Filter:[/bold magenta] {filter_text}[blink]_[/blink]",
|
|
304
|
+
title="Press / to cancel, ENTER to apply",
|
|
305
|
+
border_style="magenta",
|
|
306
|
+
)
|
|
307
|
+
display = Group(
|
|
308
|
+
filter_panel, _render_processes_table(processes, selected)
|
|
309
|
+
)
|
|
310
|
+
live.update(display)
|
|
311
|
+
continue # Skip the update at the end of the loop
|
|
312
|
+
elif ch == "q":
|
|
313
|
+
running = False
|
|
314
|
+
break
|
|
315
|
+
elif ch == key.ENTER:
|
|
316
|
+
# Exit live context to show confirmation view
|
|
317
|
+
process_to_kill = processes[selected]
|
|
318
|
+
break
|
|
319
|
+
live.update(_render_processes_table(processes, selected))
|
|
320
|
+
|
|
321
|
+
if process_to_kill is not None:
|
|
322
|
+
_clear_screen()
|
|
323
|
+
|
|
324
|
+
if running and process_to_kill is not None:
|
|
325
|
+
if _show_confirmation_view(console, process_to_kill, text):
|
|
326
|
+
_kill_process(process_to_kill.pid)
|
|
327
|
+
processes = _get_processes()
|
|
328
|
+
selected = min(selected, len(processes) - 1)
|
|
329
|
+
|
|
330
|
+
_clear_screen()
|
|
331
|
+
console.print(Panel(text, box=box.SIMPLE))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
main
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
main
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "gruyere"
|
|
3
|
+
version = "0.0.1"
|
|
4
|
+
description = "A tiny program for viewing and killing processes on ports"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "Savannah Ostrowski", email = "savannah@python.org" }
|
|
8
|
+
]
|
|
9
|
+
license = { text = "MIT" }
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"readchar>=4.2.1",
|
|
13
|
+
"rich>=14.2.0",
|
|
14
|
+
]
|
|
15
|
+
keywords = ["ports", "processes", "kill", "lsof", "tui"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Environment :: Console",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Operating System :: MacOS",
|
|
22
|
+
"Operating System :: POSIX :: Linux",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Topic :: System :: Monitoring",
|
|
26
|
+
"Topic :: Utilities",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://github.com/yourusername/gruyere"
|
|
31
|
+
Repository = "https://github.com/yourusername/gruyere"
|
|
32
|
+
Issues = "https://github.com/yourusername/gruyere/issues"
|
|
33
|
+
|
|
34
|
+
[project.scripts]
|
|
35
|
+
gruyere = "gruyere.main:main"
|
|
36
|
+
|
|
37
|
+
[build-system]
|
|
38
|
+
requires = ["hatchling"]
|
|
39
|
+
build-backend = "hatchling.build"
|
|
40
|
+
|
|
41
|
+
[tool.hatchling.build.targets.wheel]
|
|
42
|
+
# Only include the gruyere package
|
|
43
|
+
packages = ["gruyere"]
|
|
44
|
+
|
|
45
|
+
[tool.hatchling.build.targets.sdist]
|
|
46
|
+
exclude = [
|
|
47
|
+
"/.python-version",
|
|
48
|
+
"/uv.lock",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
[dependency-groups]
|
|
52
|
+
dev = [
|
|
53
|
+
"pre-commit>=4.3.0",
|
|
54
|
+
]
|
gruyere-0.0.1/uv.lock
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
version = 1
|
|
2
|
+
revision = 3
|
|
3
|
+
requires-python = ">=3.12"
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "cfgv"
|
|
7
|
+
version = "3.4.0"
|
|
8
|
+
source = { registry = "https://pypi.org/simple" }
|
|
9
|
+
sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114, upload-time = "2023-08-12T20:38:17.776Z" }
|
|
10
|
+
wheels = [
|
|
11
|
+
{ url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249, upload-time = "2023-08-12T20:38:16.269Z" },
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[[package]]
|
|
15
|
+
name = "distlib"
|
|
16
|
+
version = "0.4.0"
|
|
17
|
+
source = { registry = "https://pypi.org/simple" }
|
|
18
|
+
sdist = { url = "https://files.pythonhosted.org/packages/96/8e/709914eb2b5749865801041647dc7f4e6d00b549cfe88b65ca192995f07c/distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d", size = 614605, upload-time = "2025-07-17T16:52:00.465Z" }
|
|
19
|
+
wheels = [
|
|
20
|
+
{ url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" },
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[[package]]
|
|
24
|
+
name = "filelock"
|
|
25
|
+
version = "3.20.0"
|
|
26
|
+
source = { registry = "https://pypi.org/simple" }
|
|
27
|
+
sdist = { url = "https://files.pythonhosted.org/packages/58/46/0028a82567109b5ef6e4d2a1f04a583fb513e6cf9527fcdd09afd817deeb/filelock-3.20.0.tar.gz", hash = "sha256:711e943b4ec6be42e1d4e6690b48dc175c822967466bb31c0c293f34334c13f4", size = 18922, upload-time = "2025-10-08T18:03:50.056Z" }
|
|
28
|
+
wheels = [
|
|
29
|
+
{ url = "https://files.pythonhosted.org/packages/76/91/7216b27286936c16f5b4d0c530087e4a54eead683e6b0b73dd0c64844af6/filelock-3.20.0-py3-none-any.whl", hash = "sha256:339b4732ffda5cd79b13f4e2711a31b0365ce445d95d243bb996273d072546a2", size = 16054, upload-time = "2025-10-08T18:03:48.35Z" },
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[[package]]
|
|
33
|
+
name = "gruyere"
|
|
34
|
+
version = "0.0.1"
|
|
35
|
+
source = { editable = "." }
|
|
36
|
+
dependencies = [
|
|
37
|
+
{ name = "readchar" },
|
|
38
|
+
{ name = "rich" },
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[package.dev-dependencies]
|
|
42
|
+
dev = [
|
|
43
|
+
{ name = "pre-commit" },
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[package.metadata]
|
|
47
|
+
requires-dist = [
|
|
48
|
+
{ name = "readchar", specifier = ">=4.2.1" },
|
|
49
|
+
{ name = "rich", specifier = ">=14.2.0" },
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
[package.metadata.requires-dev]
|
|
53
|
+
dev = [{ name = "pre-commit", specifier = ">=4.3.0" }]
|
|
54
|
+
|
|
55
|
+
[[package]]
|
|
56
|
+
name = "identify"
|
|
57
|
+
version = "2.6.15"
|
|
58
|
+
source = { registry = "https://pypi.org/simple" }
|
|
59
|
+
sdist = { url = "https://files.pythonhosted.org/packages/ff/e7/685de97986c916a6d93b3876139e00eef26ad5bbbd61925d670ae8013449/identify-2.6.15.tar.gz", hash = "sha256:e4f4864b96c6557ef2a1e1c951771838f4edc9df3a72ec7118b338801b11c7bf", size = 99311, upload-time = "2025-10-02T17:43:40.631Z" }
|
|
60
|
+
wheels = [
|
|
61
|
+
{ url = "https://files.pythonhosted.org/packages/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-py2.py3-none-any.whl", hash = "sha256:1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757", size = 99183, upload-time = "2025-10-02T17:43:39.137Z" },
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
[[package]]
|
|
65
|
+
name = "markdown-it-py"
|
|
66
|
+
version = "4.0.0"
|
|
67
|
+
source = { registry = "https://pypi.org/simple" }
|
|
68
|
+
dependencies = [
|
|
69
|
+
{ name = "mdurl" },
|
|
70
|
+
]
|
|
71
|
+
sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" }
|
|
72
|
+
wheels = [
|
|
73
|
+
{ url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" },
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
[[package]]
|
|
77
|
+
name = "mdurl"
|
|
78
|
+
version = "0.1.2"
|
|
79
|
+
source = { registry = "https://pypi.org/simple" }
|
|
80
|
+
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" }
|
|
81
|
+
wheels = [
|
|
82
|
+
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
[[package]]
|
|
86
|
+
name = "nodeenv"
|
|
87
|
+
version = "1.9.1"
|
|
88
|
+
source = { registry = "https://pypi.org/simple" }
|
|
89
|
+
sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437, upload-time = "2024-06-04T18:44:11.171Z" }
|
|
90
|
+
wheels = [
|
|
91
|
+
{ url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" },
|
|
92
|
+
]
|
|
93
|
+
|
|
94
|
+
[[package]]
|
|
95
|
+
name = "platformdirs"
|
|
96
|
+
version = "4.5.0"
|
|
97
|
+
source = { registry = "https://pypi.org/simple" }
|
|
98
|
+
sdist = { url = "https://files.pythonhosted.org/packages/61/33/9611380c2bdb1225fdef633e2a9610622310fed35ab11dac9620972ee088/platformdirs-4.5.0.tar.gz", hash = "sha256:70ddccdd7c99fc5942e9fc25636a8b34d04c24b335100223152c2803e4063312", size = 21632, upload-time = "2025-10-08T17:44:48.791Z" }
|
|
99
|
+
wheels = [
|
|
100
|
+
{ url = "https://files.pythonhosted.org/packages/73/cb/ac7874b3e5d58441674fb70742e6c374b28b0c7cb988d37d991cde47166c/platformdirs-4.5.0-py3-none-any.whl", hash = "sha256:e578a81bb873cbb89a41fcc904c7ef523cc18284b7e3b3ccf06aca1403b7ebd3", size = 18651, upload-time = "2025-10-08T17:44:47.223Z" },
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
[[package]]
|
|
104
|
+
name = "pre-commit"
|
|
105
|
+
version = "4.3.0"
|
|
106
|
+
source = { registry = "https://pypi.org/simple" }
|
|
107
|
+
dependencies = [
|
|
108
|
+
{ name = "cfgv" },
|
|
109
|
+
{ name = "identify" },
|
|
110
|
+
{ name = "nodeenv" },
|
|
111
|
+
{ name = "pyyaml" },
|
|
112
|
+
{ name = "virtualenv" },
|
|
113
|
+
]
|
|
114
|
+
sdist = { url = "https://files.pythonhosted.org/packages/ff/29/7cf5bbc236333876e4b41f56e06857a87937ce4bf91e117a6991a2dbb02a/pre_commit-4.3.0.tar.gz", hash = "sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16", size = 193792, upload-time = "2025-08-09T18:56:14.651Z" }
|
|
115
|
+
wheels = [
|
|
116
|
+
{ url = "https://files.pythonhosted.org/packages/5b/a5/987a405322d78a73b66e39e4a90e4ef156fd7141bf71df987e50717c321b/pre_commit-4.3.0-py2.py3-none-any.whl", hash = "sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8", size = 220965, upload-time = "2025-08-09T18:56:13.192Z" },
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
[[package]]
|
|
120
|
+
name = "pygments"
|
|
121
|
+
version = "2.19.2"
|
|
122
|
+
source = { registry = "https://pypi.org/simple" }
|
|
123
|
+
sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
|
|
124
|
+
wheels = [
|
|
125
|
+
{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
|
|
126
|
+
]
|
|
127
|
+
|
|
128
|
+
[[package]]
|
|
129
|
+
name = "pyyaml"
|
|
130
|
+
version = "6.0.3"
|
|
131
|
+
source = { registry = "https://pypi.org/simple" }
|
|
132
|
+
sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" }
|
|
133
|
+
wheels = [
|
|
134
|
+
{ url = "https://files.pythonhosted.org/packages/d1/33/422b98d2195232ca1826284a76852ad5a86fe23e31b009c9886b2d0fb8b2/pyyaml-6.0.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196", size = 182063, upload-time = "2025-09-25T21:32:11.445Z" },
|
|
135
|
+
{ url = "https://files.pythonhosted.org/packages/89/a0/6cf41a19a1f2f3feab0e9c0b74134aa2ce6849093d5517a0c550fe37a648/pyyaml-6.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0", size = 173973, upload-time = "2025-09-25T21:32:12.492Z" },
|
|
136
|
+
{ url = "https://files.pythonhosted.org/packages/ed/23/7a778b6bd0b9a8039df8b1b1d80e2e2ad78aa04171592c8a5c43a56a6af4/pyyaml-6.0.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28", size = 775116, upload-time = "2025-09-25T21:32:13.652Z" },
|
|
137
|
+
{ url = "https://files.pythonhosted.org/packages/65/30/d7353c338e12baef4ecc1b09e877c1970bd3382789c159b4f89d6a70dc09/pyyaml-6.0.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c", size = 844011, upload-time = "2025-09-25T21:32:15.21Z" },
|
|
138
|
+
{ url = "https://files.pythonhosted.org/packages/8b/9d/b3589d3877982d4f2329302ef98a8026e7f4443c765c46cfecc8858c6b4b/pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc", size = 807870, upload-time = "2025-09-25T21:32:16.431Z" },
|
|
139
|
+
{ url = "https://files.pythonhosted.org/packages/05/c0/b3be26a015601b822b97d9149ff8cb5ead58c66f981e04fedf4e762f4bd4/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e", size = 761089, upload-time = "2025-09-25T21:32:17.56Z" },
|
|
140
|
+
{ url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" },
|
|
141
|
+
{ url = "https://files.pythonhosted.org/packages/74/93/7baea19427dcfbe1e5a372d81473250b379f04b1bd3c4c5ff825e2327202/pyyaml-6.0.3-cp312-cp312-win32.whl", hash = "sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5", size = 137658, upload-time = "2025-09-25T21:32:20.209Z" },
|
|
142
|
+
{ url = "https://files.pythonhosted.org/packages/86/bf/899e81e4cce32febab4fb42bb97dcdf66bc135272882d1987881a4b519e9/pyyaml-6.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b", size = 154003, upload-time = "2025-09-25T21:32:21.167Z" },
|
|
143
|
+
{ url = "https://files.pythonhosted.org/packages/1a/08/67bd04656199bbb51dbed1439b7f27601dfb576fb864099c7ef0c3e55531/pyyaml-6.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd", size = 140344, upload-time = "2025-09-25T21:32:22.617Z" },
|
|
144
|
+
{ url = "https://files.pythonhosted.org/packages/d1/11/0fd08f8192109f7169db964b5707a2f1e8b745d4e239b784a5a1dd80d1db/pyyaml-6.0.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8", size = 181669, upload-time = "2025-09-25T21:32:23.673Z" },
|
|
145
|
+
{ url = "https://files.pythonhosted.org/packages/b1/16/95309993f1d3748cd644e02e38b75d50cbc0d9561d21f390a76242ce073f/pyyaml-6.0.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1", size = 173252, upload-time = "2025-09-25T21:32:25.149Z" },
|
|
146
|
+
{ url = "https://files.pythonhosted.org/packages/50/31/b20f376d3f810b9b2371e72ef5adb33879b25edb7a6d072cb7ca0c486398/pyyaml-6.0.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c", size = 767081, upload-time = "2025-09-25T21:32:26.575Z" },
|
|
147
|
+
{ url = "https://files.pythonhosted.org/packages/49/1e/a55ca81e949270d5d4432fbbd19dfea5321eda7c41a849d443dc92fd1ff7/pyyaml-6.0.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5", size = 841159, upload-time = "2025-09-25T21:32:27.727Z" },
|
|
148
|
+
{ url = "https://files.pythonhosted.org/packages/74/27/e5b8f34d02d9995b80abcef563ea1f8b56d20134d8f4e5e81733b1feceb2/pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6", size = 801626, upload-time = "2025-09-25T21:32:28.878Z" },
|
|
149
|
+
{ url = "https://files.pythonhosted.org/packages/f9/11/ba845c23988798f40e52ba45f34849aa8a1f2d4af4b798588010792ebad6/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6", size = 753613, upload-time = "2025-09-25T21:32:30.178Z" },
|
|
150
|
+
{ url = "https://files.pythonhosted.org/packages/3d/e0/7966e1a7bfc0a45bf0a7fb6b98ea03fc9b8d84fa7f2229e9659680b69ee3/pyyaml-6.0.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be", size = 794115, upload-time = "2025-09-25T21:32:31.353Z" },
|
|
151
|
+
{ url = "https://files.pythonhosted.org/packages/de/94/980b50a6531b3019e45ddeada0626d45fa85cbe22300844a7983285bed3b/pyyaml-6.0.3-cp313-cp313-win32.whl", hash = "sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26", size = 137427, upload-time = "2025-09-25T21:32:32.58Z" },
|
|
152
|
+
{ url = "https://files.pythonhosted.org/packages/97/c9/39d5b874e8b28845e4ec2202b5da735d0199dbe5b8fb85f91398814a9a46/pyyaml-6.0.3-cp313-cp313-win_amd64.whl", hash = "sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c", size = 154090, upload-time = "2025-09-25T21:32:33.659Z" },
|
|
153
|
+
{ url = "https://files.pythonhosted.org/packages/73/e8/2bdf3ca2090f68bb3d75b44da7bbc71843b19c9f2b9cb9b0f4ab7a5a4329/pyyaml-6.0.3-cp313-cp313-win_arm64.whl", hash = "sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb", size = 140246, upload-time = "2025-09-25T21:32:34.663Z" },
|
|
154
|
+
{ url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" },
|
|
155
|
+
{ url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" },
|
|
156
|
+
{ url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" },
|
|
157
|
+
{ url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" },
|
|
158
|
+
{ url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" },
|
|
159
|
+
{ url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" },
|
|
160
|
+
{ url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" },
|
|
161
|
+
{ url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" },
|
|
162
|
+
{ url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" },
|
|
163
|
+
{ url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" },
|
|
164
|
+
{ url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" },
|
|
165
|
+
{ url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" },
|
|
166
|
+
{ url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" },
|
|
167
|
+
{ url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" },
|
|
168
|
+
{ url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" },
|
|
169
|
+
{ url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" },
|
|
170
|
+
{ url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" },
|
|
171
|
+
{ url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" },
|
|
172
|
+
]
|
|
173
|
+
|
|
174
|
+
[[package]]
|
|
175
|
+
name = "readchar"
|
|
176
|
+
version = "4.2.1"
|
|
177
|
+
source = { registry = "https://pypi.org/simple" }
|
|
178
|
+
sdist = { url = "https://files.pythonhosted.org/packages/dd/f8/8657b8cbb4ebeabfbdf991ac40eca8a1d1bd012011bd44ad1ed10f5cb494/readchar-4.2.1.tar.gz", hash = "sha256:91ce3faf07688de14d800592951e5575e9c7a3213738ed01d394dcc949b79adb", size = 9685, upload-time = "2024-11-04T18:28:07.757Z" }
|
|
179
|
+
wheels = [
|
|
180
|
+
{ url = "https://files.pythonhosted.org/packages/a9/10/e4b1e0e5b6b6745c8098c275b69bc9d73e9542d5c7da4f137542b499ed44/readchar-4.2.1-py3-none-any.whl", hash = "sha256:a769305cd3994bb5fa2764aa4073452dc105a4ec39068ffe6efd3c20c60acc77", size = 9350, upload-time = "2024-11-04T18:28:02.859Z" },
|
|
181
|
+
]
|
|
182
|
+
|
|
183
|
+
[[package]]
|
|
184
|
+
name = "rich"
|
|
185
|
+
version = "14.2.0"
|
|
186
|
+
source = { registry = "https://pypi.org/simple" }
|
|
187
|
+
dependencies = [
|
|
188
|
+
{ name = "markdown-it-py" },
|
|
189
|
+
{ name = "pygments" },
|
|
190
|
+
]
|
|
191
|
+
sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990, upload-time = "2025-10-09T14:16:53.064Z" }
|
|
192
|
+
wheels = [
|
|
193
|
+
{ url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393, upload-time = "2025-10-09T14:16:51.245Z" },
|
|
194
|
+
]
|
|
195
|
+
|
|
196
|
+
[[package]]
|
|
197
|
+
name = "virtualenv"
|
|
198
|
+
version = "20.35.3"
|
|
199
|
+
source = { registry = "https://pypi.org/simple" }
|
|
200
|
+
dependencies = [
|
|
201
|
+
{ name = "distlib" },
|
|
202
|
+
{ name = "filelock" },
|
|
203
|
+
{ name = "platformdirs" },
|
|
204
|
+
]
|
|
205
|
+
sdist = { url = "https://files.pythonhosted.org/packages/a4/d5/b0ccd381d55c8f45d46f77df6ae59fbc23d19e901e2d523395598e5f4c93/virtualenv-20.35.3.tar.gz", hash = "sha256:4f1a845d131133bdff10590489610c98c168ff99dc75d6c96853801f7f67af44", size = 6002907, upload-time = "2025-10-10T21:23:33.178Z" }
|
|
206
|
+
wheels = [
|
|
207
|
+
{ url = "https://files.pythonhosted.org/packages/27/73/d9a94da0e9d470a543c1b9d3ccbceb0f59455983088e727b8a1824ed90fb/virtualenv-20.35.3-py3-none-any.whl", hash = "sha256:63d106565078d8c8d0b206d48080f938a8b25361e19432d2c9db40d2899c810a", size = 5981061, upload-time = "2025-10-10T21:23:30.433Z" },
|
|
208
|
+
]
|