killpy 0.22.0__tar.gz → 0.23.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.
- {killpy-0.22.0 → killpy-0.23.0}/PKG-INFO +3 -2
- {killpy-0.22.0 → killpy-0.23.0}/README.md +1 -1
- {killpy-0.22.0 → killpy-0.23.0}/killpy/__main__.py +2 -0
- killpy-0.23.0/killpy/commands/find.py +197 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy.egg-info/PKG-INFO +3 -2
- {killpy-0.22.0 → killpy-0.23.0}/killpy.egg-info/SOURCES.txt +1 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy.egg-info/requires.txt +1 -0
- {killpy-0.22.0 → killpy-0.23.0}/pyproject.toml +7 -2
- {killpy-0.22.0 → killpy-0.23.0}/killpy/__init__.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/cleaner.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/cleaners/__init__.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/cli.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/commands/__init__.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/commands/_utils.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/commands/clean.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/commands/delete.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/commands/doctor.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/commands/list.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/commands/stats.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/detectors/__init__.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/detectors/artifacts.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/detectors/base.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/detectors/cache.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/detectors/conda.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/detectors/hatch.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/detectors/pipenv.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/detectors/pipx.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/detectors/poetry.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/detectors/pyenv.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/detectors/tox.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/detectors/uv.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/detectors/venv.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/files/__init__.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/intelligence/__init__.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/intelligence/git_analyzer.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/intelligence/scoring.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/intelligence/suggestions.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/intelligence/tracker.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/models.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy/scanner.py +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy.egg-info/dependency_links.txt +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy.egg-info/entry_points.txt +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/killpy.egg-info/top_level.txt +0 -0
- {killpy-0.22.0 → killpy-0.23.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: killpy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.23.0
|
|
4
4
|
Summary: List all .venv directories and Conda environments 🐍 on your system and check how much space they are using. You can then choose which ones to delete in order to free up space 🧹.
|
|
5
5
|
Project-URL: Homepage, https://github.com/Tlaloc-Es/killpy
|
|
6
6
|
Project-URL: Documentation, https://tlaloc-es.github.io/killpy/
|
|
@@ -15,6 +15,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
15
15
|
Requires-Python: >=3.10
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
Requires-Dist: click>=8.1.8
|
|
18
|
+
Requires-Dist: packaging>=24.0
|
|
18
19
|
Requires-Dist: rich>=13.9.4
|
|
19
20
|
Requires-Dist: textual>=1.0.0
|
|
20
21
|
|
|
@@ -550,7 +551,7 @@ Typical minimal setup (safe for daily use):
|
|
|
550
551
|
|
|
551
552
|
```yaml
|
|
552
553
|
- repo: https://github.com/Tlaloc-Es/KillPy
|
|
553
|
-
rev: 0.
|
|
554
|
+
rev: 0.23.0
|
|
554
555
|
hooks:
|
|
555
556
|
- id: killpy
|
|
556
557
|
```
|
|
@@ -11,6 +11,7 @@ from killpy.cli import TableApp
|
|
|
11
11
|
from killpy.commands.clean import clean
|
|
12
12
|
from killpy.commands.delete import delete_cmd
|
|
13
13
|
from killpy.commands.doctor import doctor_cmd
|
|
14
|
+
from killpy.commands.find import find_cmd
|
|
14
15
|
from killpy.commands.list import list_cmd
|
|
15
16
|
from killpy.commands.stats import stats_cmd
|
|
16
17
|
from killpy.files import format_size
|
|
@@ -120,6 +121,7 @@ cli.add_command(list_cmd, name="list")
|
|
|
120
121
|
cli.add_command(delete_cmd, name="delete")
|
|
121
122
|
cli.add_command(stats_cmd, name="stats")
|
|
122
123
|
cli.add_command(doctor_cmd, name="doctor")
|
|
124
|
+
cli.add_command(find_cmd, name="find")
|
|
123
125
|
|
|
124
126
|
|
|
125
127
|
if __name__ == "__main__":
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"""``killpy find`` – locate environments that have a specific package installed."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
from packaging.requirements import Requirement
|
|
10
|
+
from packaging.version import InvalidVersion
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
from rich.table import Table
|
|
13
|
+
|
|
14
|
+
from killpy.commands._utils import filter_envs
|
|
15
|
+
from killpy.scanner import Scanner
|
|
16
|
+
|
|
17
|
+
# ---------------------------------------------------------------------------
|
|
18
|
+
# Helpers: read installed-package metadata from a venv directory
|
|
19
|
+
# ---------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _site_packages(env_path: Path) -> list[Path]:
|
|
23
|
+
"""Return all site-packages directories found inside *env_path*.
|
|
24
|
+
|
|
25
|
+
Supports the Unix layout (``lib/python3.x/site-packages``) and the
|
|
26
|
+
Windows layout (``Lib/site-packages``).
|
|
27
|
+
"""
|
|
28
|
+
result: list[Path] = []
|
|
29
|
+
|
|
30
|
+
# Unix: lib/python<ver>/site-packages
|
|
31
|
+
lib = env_path / "lib"
|
|
32
|
+
if lib.is_dir():
|
|
33
|
+
for child in lib.iterdir():
|
|
34
|
+
sp = child / "site-packages"
|
|
35
|
+
if sp.is_dir():
|
|
36
|
+
result.append(sp)
|
|
37
|
+
|
|
38
|
+
# Windows: Lib/site-packages
|
|
39
|
+
win_sp = env_path / "Lib" / "site-packages"
|
|
40
|
+
if win_sp.is_dir():
|
|
41
|
+
result.append(win_sp)
|
|
42
|
+
|
|
43
|
+
return result
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _read_metadata_field(metadata_path: Path, field: str) -> str | None:
|
|
47
|
+
"""Return the value of a single RFC 822-style *field* from *metadata_path*."""
|
|
48
|
+
prefix = f"{field}:"
|
|
49
|
+
try:
|
|
50
|
+
with metadata_path.open(encoding="utf-8", errors="replace") as fh:
|
|
51
|
+
for line in fh:
|
|
52
|
+
if line.startswith(prefix):
|
|
53
|
+
return line[len(prefix) :].strip()
|
|
54
|
+
# RFC 822 headers end at the first blank line.
|
|
55
|
+
if not line.strip():
|
|
56
|
+
break
|
|
57
|
+
except OSError:
|
|
58
|
+
pass
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _normalise_name(name: str) -> str:
|
|
63
|
+
"""Normalise a distribution name to a lowercase, underscore form."""
|
|
64
|
+
return name.lower().replace("-", "_").replace(".", "_")
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def installed_packages(env_path: Path) -> dict[str, str]:
|
|
68
|
+
"""Return ``{normalised_name: version}`` for every package installed in *env_path*.
|
|
69
|
+
|
|
70
|
+
Reads ``*.dist-info/METADATA`` files from all site-packages directories
|
|
71
|
+
found under the environment root. Non-venv paths (e.g. conda envs created
|
|
72
|
+
with ``--prefix``) are supported as long as they follow the same layout.
|
|
73
|
+
"""
|
|
74
|
+
packages: dict[str, str] = {}
|
|
75
|
+
for sp in _site_packages(env_path):
|
|
76
|
+
for dist_info in sp.glob("*.dist-info"):
|
|
77
|
+
metadata = dist_info / "METADATA"
|
|
78
|
+
if not metadata.exists():
|
|
79
|
+
continue
|
|
80
|
+
name = _read_metadata_field(metadata, "Name")
|
|
81
|
+
version = _read_metadata_field(metadata, "Version")
|
|
82
|
+
if name and version:
|
|
83
|
+
packages[_normalise_name(name)] = version
|
|
84
|
+
return packages
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def package_version_match(
|
|
88
|
+
packages: dict[str, str], requirement: Requirement
|
|
89
|
+
) -> str | None:
|
|
90
|
+
"""Return the installed version string if it satisfies *requirement*, else ``None``.""" # noqa: E501
|
|
91
|
+
norm_name = _normalise_name(requirement.name)
|
|
92
|
+
installed = packages.get(norm_name)
|
|
93
|
+
if installed is None:
|
|
94
|
+
return None
|
|
95
|
+
try:
|
|
96
|
+
if requirement.specifier.contains(installed, prereleases=True):
|
|
97
|
+
return installed
|
|
98
|
+
except InvalidVersion:
|
|
99
|
+
pass
|
|
100
|
+
return None
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# ---------------------------------------------------------------------------
|
|
104
|
+
# Click command
|
|
105
|
+
# ---------------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@click.command("find")
|
|
109
|
+
@click.argument("package")
|
|
110
|
+
@click.option(
|
|
111
|
+
"--path",
|
|
112
|
+
default=Path.cwd,
|
|
113
|
+
type=click.Path(path_type=Path, exists=True, file_okay=False, dir_okay=True),
|
|
114
|
+
help="Root directory to scan.",
|
|
115
|
+
)
|
|
116
|
+
@click.option(
|
|
117
|
+
"--type",
|
|
118
|
+
"types",
|
|
119
|
+
multiple=True,
|
|
120
|
+
metavar="TYPE",
|
|
121
|
+
help=(
|
|
122
|
+
"Limit the scan to these environment types (repeatable). "
|
|
123
|
+
"Example: --type venv --type conda"
|
|
124
|
+
),
|
|
125
|
+
)
|
|
126
|
+
@click.option(
|
|
127
|
+
"--json",
|
|
128
|
+
"as_json",
|
|
129
|
+
is_flag=True,
|
|
130
|
+
default=False,
|
|
131
|
+
help="Output as a JSON array.",
|
|
132
|
+
)
|
|
133
|
+
def find_cmd(
|
|
134
|
+
package: str,
|
|
135
|
+
path: Path,
|
|
136
|
+
types: tuple[str, ...],
|
|
137
|
+
as_json: bool,
|
|
138
|
+
) -> None:
|
|
139
|
+
"""Find environments that have PACKAGE installed.
|
|
140
|
+
|
|
141
|
+
PACKAGE accepts standard PEP 508 / uv-style version specifiers:
|
|
142
|
+
|
|
143
|
+
\b
|
|
144
|
+
killpy find requests
|
|
145
|
+
killpy find "requests>=2.28"
|
|
146
|
+
killpy find "numpy>=1.24,<2.0"
|
|
147
|
+
killpy find "django==4.2.*"
|
|
148
|
+
killpy find "scipy~=1.11"
|
|
149
|
+
"""
|
|
150
|
+
try:
|
|
151
|
+
req = Requirement(package)
|
|
152
|
+
except Exception as exc: # packaging.requirements.InvalidRequirement
|
|
153
|
+
raise click.BadParameter(str(exc), param_hint="PACKAGE") from exc
|
|
154
|
+
|
|
155
|
+
scanner = Scanner(types=set(types) if types else None)
|
|
156
|
+
envs = scanner.scan(path)
|
|
157
|
+
envs = filter_envs(envs, types or None, None)
|
|
158
|
+
|
|
159
|
+
matches: list[tuple] = [] # (Environment, version_string)
|
|
160
|
+
for env in envs:
|
|
161
|
+
pkgs = installed_packages(env.path)
|
|
162
|
+
version = package_version_match(pkgs, req)
|
|
163
|
+
if version is not None:
|
|
164
|
+
matches.append((env, version))
|
|
165
|
+
|
|
166
|
+
if as_json:
|
|
167
|
+
click.echo(
|
|
168
|
+
json.dumps(
|
|
169
|
+
[{**env.to_dict(), "matched_version": ver} for env, ver in matches],
|
|
170
|
+
indent=2,
|
|
171
|
+
)
|
|
172
|
+
)
|
|
173
|
+
return
|
|
174
|
+
|
|
175
|
+
console = Console()
|
|
176
|
+
|
|
177
|
+
if not matches:
|
|
178
|
+
console.print(
|
|
179
|
+
f"[yellow]No environments found with[/yellow] "
|
|
180
|
+
f"[bold]{package}[/bold] installed."
|
|
181
|
+
)
|
|
182
|
+
return
|
|
183
|
+
|
|
184
|
+
table = Table(title=f"Environments with {package!r}", show_lines=False)
|
|
185
|
+
table.add_column("Path", style="cyan")
|
|
186
|
+
table.add_column("Type", style="magenta")
|
|
187
|
+
table.add_column("Installed version", style="green")
|
|
188
|
+
table.add_column("Env size", style="yellow", justify="right")
|
|
189
|
+
|
|
190
|
+
for env, ver in matches:
|
|
191
|
+
table.add_row(str(env.path), env.type, ver, env.size_human)
|
|
192
|
+
|
|
193
|
+
console.print(table)
|
|
194
|
+
console.print(
|
|
195
|
+
f"\n[bold]{len(matches)}[/bold] environment(s) match "
|
|
196
|
+
f"[bold cyan]{package}[/bold cyan]."
|
|
197
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: killpy
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.23.0
|
|
4
4
|
Summary: List all .venv directories and Conda environments 🐍 on your system and check how much space they are using. You can then choose which ones to delete in order to free up space 🧹.
|
|
5
5
|
Project-URL: Homepage, https://github.com/Tlaloc-Es/killpy
|
|
6
6
|
Project-URL: Documentation, https://tlaloc-es.github.io/killpy/
|
|
@@ -15,6 +15,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
15
15
|
Requires-Python: >=3.10
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
Requires-Dist: click>=8.1.8
|
|
18
|
+
Requires-Dist: packaging>=24.0
|
|
18
19
|
Requires-Dist: rich>=13.9.4
|
|
19
20
|
Requires-Dist: textual>=1.0.0
|
|
20
21
|
|
|
@@ -550,7 +551,7 @@ Typical minimal setup (safe for daily use):
|
|
|
550
551
|
|
|
551
552
|
```yaml
|
|
552
553
|
- repo: https://github.com/Tlaloc-Es/KillPy
|
|
553
|
-
rev: 0.
|
|
554
|
+
rev: 0.23.0
|
|
554
555
|
hooks:
|
|
555
556
|
- id: killpy
|
|
556
557
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "killpy"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.23.0"
|
|
4
4
|
description = "List all .venv directories and Conda environments 🐍 on your system and check how much space they are using. You can then choose which ones to delete in order to free up space 🧹."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
classifiers = [
|
|
@@ -11,7 +11,12 @@ classifiers = [
|
|
|
11
11
|
"Programming Language :: Python :: 3.12",
|
|
12
12
|
"Programming Language :: Python :: 3.13",
|
|
13
13
|
]
|
|
14
|
-
dependencies = [
|
|
14
|
+
dependencies = [
|
|
15
|
+
"click>=8.1.8",
|
|
16
|
+
"packaging>=24.0",
|
|
17
|
+
"rich>=13.9.4",
|
|
18
|
+
"textual>=1.0.0",
|
|
19
|
+
]
|
|
15
20
|
requires-python = ">=3.10"
|
|
16
21
|
|
|
17
22
|
[project.urls]
|
|
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
|
|
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
|