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.
Files changed (44) hide show
  1. {killpy-0.22.0 → killpy-0.23.0}/PKG-INFO +3 -2
  2. {killpy-0.22.0 → killpy-0.23.0}/README.md +1 -1
  3. {killpy-0.22.0 → killpy-0.23.0}/killpy/__main__.py +2 -0
  4. killpy-0.23.0/killpy/commands/find.py +197 -0
  5. {killpy-0.22.0 → killpy-0.23.0}/killpy.egg-info/PKG-INFO +3 -2
  6. {killpy-0.22.0 → killpy-0.23.0}/killpy.egg-info/SOURCES.txt +1 -0
  7. {killpy-0.22.0 → killpy-0.23.0}/killpy.egg-info/requires.txt +1 -0
  8. {killpy-0.22.0 → killpy-0.23.0}/pyproject.toml +7 -2
  9. {killpy-0.22.0 → killpy-0.23.0}/killpy/__init__.py +0 -0
  10. {killpy-0.22.0 → killpy-0.23.0}/killpy/cleaner.py +0 -0
  11. {killpy-0.22.0 → killpy-0.23.0}/killpy/cleaners/__init__.py +0 -0
  12. {killpy-0.22.0 → killpy-0.23.0}/killpy/cli.py +0 -0
  13. {killpy-0.22.0 → killpy-0.23.0}/killpy/commands/__init__.py +0 -0
  14. {killpy-0.22.0 → killpy-0.23.0}/killpy/commands/_utils.py +0 -0
  15. {killpy-0.22.0 → killpy-0.23.0}/killpy/commands/clean.py +0 -0
  16. {killpy-0.22.0 → killpy-0.23.0}/killpy/commands/delete.py +0 -0
  17. {killpy-0.22.0 → killpy-0.23.0}/killpy/commands/doctor.py +0 -0
  18. {killpy-0.22.0 → killpy-0.23.0}/killpy/commands/list.py +0 -0
  19. {killpy-0.22.0 → killpy-0.23.0}/killpy/commands/stats.py +0 -0
  20. {killpy-0.22.0 → killpy-0.23.0}/killpy/detectors/__init__.py +0 -0
  21. {killpy-0.22.0 → killpy-0.23.0}/killpy/detectors/artifacts.py +0 -0
  22. {killpy-0.22.0 → killpy-0.23.0}/killpy/detectors/base.py +0 -0
  23. {killpy-0.22.0 → killpy-0.23.0}/killpy/detectors/cache.py +0 -0
  24. {killpy-0.22.0 → killpy-0.23.0}/killpy/detectors/conda.py +0 -0
  25. {killpy-0.22.0 → killpy-0.23.0}/killpy/detectors/hatch.py +0 -0
  26. {killpy-0.22.0 → killpy-0.23.0}/killpy/detectors/pipenv.py +0 -0
  27. {killpy-0.22.0 → killpy-0.23.0}/killpy/detectors/pipx.py +0 -0
  28. {killpy-0.22.0 → killpy-0.23.0}/killpy/detectors/poetry.py +0 -0
  29. {killpy-0.22.0 → killpy-0.23.0}/killpy/detectors/pyenv.py +0 -0
  30. {killpy-0.22.0 → killpy-0.23.0}/killpy/detectors/tox.py +0 -0
  31. {killpy-0.22.0 → killpy-0.23.0}/killpy/detectors/uv.py +0 -0
  32. {killpy-0.22.0 → killpy-0.23.0}/killpy/detectors/venv.py +0 -0
  33. {killpy-0.22.0 → killpy-0.23.0}/killpy/files/__init__.py +0 -0
  34. {killpy-0.22.0 → killpy-0.23.0}/killpy/intelligence/__init__.py +0 -0
  35. {killpy-0.22.0 → killpy-0.23.0}/killpy/intelligence/git_analyzer.py +0 -0
  36. {killpy-0.22.0 → killpy-0.23.0}/killpy/intelligence/scoring.py +0 -0
  37. {killpy-0.22.0 → killpy-0.23.0}/killpy/intelligence/suggestions.py +0 -0
  38. {killpy-0.22.0 → killpy-0.23.0}/killpy/intelligence/tracker.py +0 -0
  39. {killpy-0.22.0 → killpy-0.23.0}/killpy/models.py +0 -0
  40. {killpy-0.22.0 → killpy-0.23.0}/killpy/scanner.py +0 -0
  41. {killpy-0.22.0 → killpy-0.23.0}/killpy.egg-info/dependency_links.txt +0 -0
  42. {killpy-0.22.0 → killpy-0.23.0}/killpy.egg-info/entry_points.txt +0 -0
  43. {killpy-0.22.0 → killpy-0.23.0}/killpy.egg-info/top_level.txt +0 -0
  44. {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.22.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.22.0
554
+ rev: 0.23.0
554
555
  hooks:
555
556
  - id: killpy
556
557
  ```
@@ -530,7 +530,7 @@ Typical minimal setup (safe for daily use):
530
530
 
531
531
  ```yaml
532
532
  - repo: https://github.com/Tlaloc-Es/KillPy
533
- rev: 0.22.0
533
+ rev: 0.23.0
534
534
  hooks:
535
535
  - id: killpy
536
536
  ```
@@ -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.22.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.22.0
554
+ rev: 0.23.0
554
555
  hooks:
555
556
  - id: killpy
556
557
  ```
@@ -18,6 +18,7 @@ killpy/commands/_utils.py
18
18
  killpy/commands/clean.py
19
19
  killpy/commands/delete.py
20
20
  killpy/commands/doctor.py
21
+ killpy/commands/find.py
21
22
  killpy/commands/list.py
22
23
  killpy/commands/stats.py
23
24
  killpy/detectors/__init__.py
@@ -1,3 +1,4 @@
1
1
  click>=8.1.8
2
+ packaging>=24.0
2
3
  rich>=13.9.4
3
4
  textual>=1.0.0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "killpy"
3
- version = "0.22.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 = ["click>=8.1.8", "rich>=13.9.4", "textual>=1.0.0"]
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