killpy 0.21.0__tar.gz → 0.22.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 (43) hide show
  1. {killpy-0.21.0 → killpy-0.22.0}/PKG-INFO +37 -7
  2. {killpy-0.21.0 → killpy-0.22.0}/README.md +36 -6
  3. {killpy-0.21.0 → killpy-0.22.0}/killpy/__main__.py +2 -0
  4. killpy-0.22.0/killpy/commands/_utils.py +65 -0
  5. {killpy-0.21.0 → killpy-0.22.0}/killpy/commands/clean.py +2 -4
  6. {killpy-0.21.0 → killpy-0.22.0}/killpy/commands/delete.py +2 -22
  7. {killpy-0.21.0 → killpy-0.22.0}/killpy/commands/doctor.py +3 -9
  8. {killpy-0.21.0 → killpy-0.22.0}/killpy/commands/list.py +3 -25
  9. {killpy-0.21.0 → killpy-0.22.0}/killpy/detectors/artifacts.py +2 -2
  10. {killpy-0.21.0 → killpy-0.22.0}/killpy/detectors/conda.py +0 -1
  11. {killpy-0.21.0 → killpy-0.22.0}/killpy/detectors/hatch.py +2 -2
  12. {killpy-0.21.0 → killpy-0.22.0}/killpy/detectors/pipenv.py +2 -2
  13. {killpy-0.21.0 → killpy-0.22.0}/killpy/detectors/poetry.py +2 -2
  14. {killpy-0.21.0 → killpy-0.22.0}/killpy/detectors/pyenv.py +2 -2
  15. {killpy-0.21.0 → killpy-0.22.0}/killpy/detectors/tox.py +2 -2
  16. {killpy-0.21.0 → killpy-0.22.0}/killpy/detectors/uv.py +2 -2
  17. {killpy-0.21.0 → killpy-0.22.0}/killpy/detectors/venv.py +0 -1
  18. {killpy-0.21.0 → killpy-0.22.0}/killpy.egg-info/PKG-INFO +37 -7
  19. {killpy-0.21.0 → killpy-0.22.0}/killpy.egg-info/SOURCES.txt +1 -0
  20. {killpy-0.21.0 → killpy-0.22.0}/pyproject.toml +1 -1
  21. {killpy-0.21.0 → killpy-0.22.0}/killpy/__init__.py +0 -0
  22. {killpy-0.21.0 → killpy-0.22.0}/killpy/cleaner.py +0 -0
  23. {killpy-0.21.0 → killpy-0.22.0}/killpy/cleaners/__init__.py +0 -0
  24. {killpy-0.21.0 → killpy-0.22.0}/killpy/cli.py +0 -0
  25. {killpy-0.21.0 → killpy-0.22.0}/killpy/commands/__init__.py +0 -0
  26. {killpy-0.21.0 → killpy-0.22.0}/killpy/commands/stats.py +0 -0
  27. {killpy-0.21.0 → killpy-0.22.0}/killpy/detectors/__init__.py +0 -0
  28. {killpy-0.21.0 → killpy-0.22.0}/killpy/detectors/base.py +0 -0
  29. {killpy-0.21.0 → killpy-0.22.0}/killpy/detectors/cache.py +0 -0
  30. {killpy-0.21.0 → killpy-0.22.0}/killpy/detectors/pipx.py +0 -0
  31. {killpy-0.21.0 → killpy-0.22.0}/killpy/files/__init__.py +0 -0
  32. {killpy-0.21.0 → killpy-0.22.0}/killpy/intelligence/__init__.py +0 -0
  33. {killpy-0.21.0 → killpy-0.22.0}/killpy/intelligence/git_analyzer.py +0 -0
  34. {killpy-0.21.0 → killpy-0.22.0}/killpy/intelligence/scoring.py +0 -0
  35. {killpy-0.21.0 → killpy-0.22.0}/killpy/intelligence/suggestions.py +0 -0
  36. {killpy-0.21.0 → killpy-0.22.0}/killpy/intelligence/tracker.py +0 -0
  37. {killpy-0.21.0 → killpy-0.22.0}/killpy/models.py +0 -0
  38. {killpy-0.21.0 → killpy-0.22.0}/killpy/scanner.py +0 -0
  39. {killpy-0.21.0 → killpy-0.22.0}/killpy.egg-info/dependency_links.txt +0 -0
  40. {killpy-0.21.0 → killpy-0.22.0}/killpy.egg-info/entry_points.txt +0 -0
  41. {killpy-0.21.0 → killpy-0.22.0}/killpy.egg-info/requires.txt +0 -0
  42. {killpy-0.21.0 → killpy-0.22.0}/killpy.egg-info/top_level.txt +0 -0
  43. {killpy-0.21.0 → killpy-0.22.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: killpy
3
- Version: 0.21.0
3
+ Version: 0.22.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/
@@ -22,9 +22,13 @@ Requires-Dist: textual>=1.0.0
22
22
 
23
23
  ![Logo](https://raw.githubusercontent.com/Tlaloc-Es/killpy/master/logo.png)
24
24
 
25
- ### Reclaim disk space by finding and deleting Python environments you no longer use
25
+ ### Free GBs of disk space by removing unused Python environments
26
26
 
27
- Python environment cleaner for virtualenv, Conda, Poetry, pipx, pyenv, tox, Pipenv, Hatch, uv, caches, and build artifacts.
27
+ Find and delete old `.venv`, conda, poetry, pipenv, uv and more safely, in seconds.
28
+
29
+ ```bash
30
+ uvx killpy --path ~
31
+ ```
28
32
 
29
33
  [Documentation](https://tlaloc-es.github.io/killpy/)
30
34
 
@@ -515,18 +519,44 @@ Each virtual environment is a full copy (or symlinked tree) of a Python interpre
515
519
 
516
520
  ______________________________________________________________________
517
521
 
518
- ## Pre-commit hook
522
+ ## Pre-commit hooks
523
+
524
+ `killpy` ships four hooks for [pre-commit](https://pre-commit.com/). Add the ones you need to your `.pre-commit-config.yaml`:
525
+
526
+ ```yaml
527
+ - repo: https://github.com/Tlaloc-Es/KillPy
528
+ rev: 0.20.0
529
+ hooks:
530
+ - id: killpy # remove __pycache__ on every commit
531
+ - id: killpy-clean-caches # also removes .mypy_cache, .pytest_cache, .ruff_cache
532
+ - id: killpy-clean-artifacts # remove dist/, build/, *.egg-info before committing
533
+ - id: killpy-remove-venv # remove .venv (manual stage — see below)
534
+ ```
535
+
536
+ | Hook id | What it removes | Default stage |
537
+ |---------|-----------------|---------------|
538
+ | `killpy` | `__pycache__` directories | `pre-commit` |
539
+ | `killpy-clean-caches` | All local cache dirs (`__pycache__`, `.mypy_cache`, `.pytest_cache`, `.ruff_cache`) | `pre-commit` |
540
+ | `killpy-clean-artifacts` | Build artifacts (`dist/`, `build/`, `*.egg-info`) | `pre-commit` |
541
+ | `killpy-remove-venv` | Local `.venv` environments | `manual` |
542
+
543
+ **`killpy-remove-venv`** is staged as `manual` because deleting the environment on every commit would require you to recreate it each time. Run it explicitly when you want a clean slate:
519
544
 
520
- Keep your repo free of `__pycache__` on every commit:
545
+ ```bash
546
+ pre-commit run killpy-remove-venv --hook-stage manual
547
+ ```
548
+
549
+ Typical minimal setup (safe for daily use):
521
550
 
522
551
  ```yaml
523
552
  - repo: https://github.com/Tlaloc-Es/KillPy
524
- rev: 0.21.0
553
+ rev: 0.22.0
525
554
  hooks:
526
555
  - id: killpy
527
- pass_filenames: false
528
556
  ```
529
557
 
558
+ Add `killpy-clean-artifacts` if your project generates `dist/` or `build/` locally and you want to guarantee they are never staged by accident.
559
+
530
560
  ______________________________________________________________________
531
561
 
532
562
  ## Safety
@@ -2,9 +2,13 @@
2
2
 
3
3
  ![Logo](https://raw.githubusercontent.com/Tlaloc-Es/killpy/master/logo.png)
4
4
 
5
- ### Reclaim disk space by finding and deleting Python environments you no longer use
5
+ ### Free GBs of disk space by removing unused Python environments
6
6
 
7
- Python environment cleaner for virtualenv, Conda, Poetry, pipx, pyenv, tox, Pipenv, Hatch, uv, caches, and build artifacts.
7
+ Find and delete old `.venv`, conda, poetry, pipenv, uv and more safely, in seconds.
8
+
9
+ ```bash
10
+ uvx killpy --path ~
11
+ ```
8
12
 
9
13
  [Documentation](https://tlaloc-es.github.io/killpy/)
10
14
 
@@ -495,18 +499,44 @@ Each virtual environment is a full copy (or symlinked tree) of a Python interpre
495
499
 
496
500
  ______________________________________________________________________
497
501
 
498
- ## Pre-commit hook
502
+ ## Pre-commit hooks
503
+
504
+ `killpy` ships four hooks for [pre-commit](https://pre-commit.com/). Add the ones you need to your `.pre-commit-config.yaml`:
505
+
506
+ ```yaml
507
+ - repo: https://github.com/Tlaloc-Es/KillPy
508
+ rev: 0.20.0
509
+ hooks:
510
+ - id: killpy # remove __pycache__ on every commit
511
+ - id: killpy-clean-caches # also removes .mypy_cache, .pytest_cache, .ruff_cache
512
+ - id: killpy-clean-artifacts # remove dist/, build/, *.egg-info before committing
513
+ - id: killpy-remove-venv # remove .venv (manual stage — see below)
514
+ ```
515
+
516
+ | Hook id | What it removes | Default stage |
517
+ |---------|-----------------|---------------|
518
+ | `killpy` | `__pycache__` directories | `pre-commit` |
519
+ | `killpy-clean-caches` | All local cache dirs (`__pycache__`, `.mypy_cache`, `.pytest_cache`, `.ruff_cache`) | `pre-commit` |
520
+ | `killpy-clean-artifacts` | Build artifacts (`dist/`, `build/`, `*.egg-info`) | `pre-commit` |
521
+ | `killpy-remove-venv` | Local `.venv` environments | `manual` |
522
+
523
+ **`killpy-remove-venv`** is staged as `manual` because deleting the environment on every commit would require you to recreate it each time. Run it explicitly when you want a clean slate:
499
524
 
500
- Keep your repo free of `__pycache__` on every commit:
525
+ ```bash
526
+ pre-commit run killpy-remove-venv --hook-stage manual
527
+ ```
528
+
529
+ Typical minimal setup (safe for daily use):
501
530
 
502
531
  ```yaml
503
532
  - repo: https://github.com/Tlaloc-Es/KillPy
504
- rev: 0.21.0
533
+ rev: 0.22.0
505
534
  hooks:
506
535
  - id: killpy
507
- pass_filenames: false
508
536
  ```
509
537
 
538
+ Add `killpy-clean-artifacts` if your project generates `dist/` or `build/` locally and you want to guarantee they are never staged by accident.
539
+
510
540
  ______________________________________________________________________
511
541
 
512
542
  ## Safety
@@ -1,3 +1,4 @@
1
+ import logging
1
2
  from pathlib import Path
2
3
 
3
4
  import click
@@ -102,6 +103,7 @@ def _run_delete_all(path: Path, excluded: set[str], yes: bool) -> None:
102
103
  )
103
104
  @click.pass_context
104
105
  def cli(ctx, path: Path, exclude: str, delete_all: bool, yes: bool):
106
+ logging.basicConfig(level=logging.WARNING)
105
107
  excluded = (
106
108
  {p.strip() for p in exclude.split(",") if p.strip()} if exclude else set()
107
109
  )
@@ -0,0 +1,65 @@
1
+ """Shared helpers for ``killpy`` commands."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from datetime import datetime, timedelta, timezone
6
+
7
+ from killpy.models import Environment
8
+
9
+ # Maps user-facing type names (detector names) to the concrete ``Environment.type``
10
+ # values those detectors produce. Two detectors use sub-type tags instead of
11
+ # their own name: VenvDetector (tags: ".venv", "pyvenv.cfg") and CacheDetector
12
+ # (tags: "__pycache__", ".mypy_cache", ".pytest_cache", ".ruff_cache",
13
+ # "pip-cache", "uv-cache"). Without this mapping, ``--type venv`` and
14
+ # ``--type cache`` would never match anything.
15
+ _TYPE_ALIASES: dict[str, frozenset[str]] = {
16
+ "venv": frozenset({".venv", "pyvenv.cfg"}),
17
+ "cache": frozenset(
18
+ {
19
+ "__pycache__",
20
+ ".mypy_cache",
21
+ ".pytest_cache",
22
+ ".ruff_cache",
23
+ "pip-cache",
24
+ "uv-cache",
25
+ }
26
+ ),
27
+ }
28
+
29
+
30
+ def filter_envs(
31
+ envs: list[Environment],
32
+ types: tuple[str, ...] | None,
33
+ older_than: int | None,
34
+ ) -> list[Environment]:
35
+ """Return a filtered subset of *envs*.
36
+
37
+ Parameters
38
+ ----------
39
+ envs:
40
+ Full list of detected environments.
41
+ types:
42
+ If provided, only environments whose :attr:`~killpy.models.Environment.type`
43
+ matches one of these strings (case-insensitive) are kept. Detector
44
+ names such as ``"venv"`` and ``"cache"`` are automatically expanded to
45
+ their concrete sub-type values via :data:`_TYPE_ALIASES`.
46
+ older_than:
47
+ If provided, only environments not accessed in the last *older_than* days
48
+ are kept.
49
+ """
50
+ now = datetime.now(tz=timezone.utc)
51
+ result = envs
52
+
53
+ if types:
54
+ expanded: set[str] = set()
55
+ for t in types:
56
+ t_lower = t.strip().lower()
57
+ expanded.add(t_lower)
58
+ expanded.update(_TYPE_ALIASES.get(t_lower, frozenset()))
59
+ result = [e for e in result if e.type.lower() in expanded]
60
+
61
+ if older_than is not None:
62
+ cutoff = now - timedelta(days=older_than)
63
+ result = [e for e in result if e.last_accessed < cutoff]
64
+
65
+ return result
@@ -1,4 +1,3 @@
1
- import logging
2
1
  from pathlib import Path
3
2
 
4
3
  import click
@@ -10,8 +9,7 @@ from killpy.files import format_size
10
9
  @click.command()
11
10
  @click.option("--path", default=Path.cwd(), help="Path to the directory to clean")
12
11
  def clean(path):
13
- logging.basicConfig(level=logging.INFO)
14
12
  path = Path(path)
15
- logging.info(f"Executing the clean command in {path}")
13
+ click.echo(f"Cleaning {path}")
16
14
  total_freed_space = remove_pycache(path)
17
- logging.info(f"{format_size(total_freed_space)} deleted")
15
+ click.echo(f"{format_size(total_freed_space)} freed")
@@ -3,38 +3,18 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import sys
6
- from datetime import datetime, timedelta, timezone
7
6
  from pathlib import Path
8
7
 
9
8
  import click
10
9
  from rich.console import Console
11
10
 
12
11
  from killpy.cleaner import Cleaner, CleanerError
12
+ from killpy.commands._utils import filter_envs
13
13
  from killpy.files import format_size
14
14
  from killpy.intelligence.tracker import UsageTracker
15
- from killpy.models import Environment
16
15
  from killpy.scanner import Scanner
17
16
 
18
17
 
19
- def _filter_envs(
20
- envs: list[Environment],
21
- types: tuple[str, ...] | None,
22
- older_than: int | None,
23
- ) -> list[Environment]:
24
- now = datetime.now(tz=timezone.utc)
25
- result = envs
26
-
27
- if types:
28
- type_set = {t.strip().lower() for t in types}
29
- result = [e for e in result if e.type.lower() in type_set]
30
-
31
- if older_than is not None:
32
- cutoff = now - timedelta(days=older_than)
33
- result = [e for e in result if e.last_accessed < cutoff]
34
-
35
- return result
36
-
37
-
38
18
  @click.command("delete")
39
19
  @click.option(
40
20
  "--path",
@@ -88,7 +68,7 @@ def delete_cmd(
88
68
 
89
69
  scanner = Scanner(types=set(types) if types else None)
90
70
  envs = scanner.scan(path)
91
- envs = _filter_envs(envs, types or None, older_than)
71
+ envs = filter_envs(envs, types or None, older_than)
92
72
 
93
73
  if not envs:
94
74
  console.print("[yellow]No environments found matching the criteria.[/yellow]")
@@ -10,6 +10,7 @@ import click
10
10
  from rich.console import Console
11
11
  from rich.table import Table
12
12
 
13
+ from killpy.detectors import ALL_DETECTORS
13
14
  from killpy.files import format_size
14
15
  from killpy.intelligence import SuggestionEngine, UsageTracker, score_all
15
16
  from killpy.models import Suggestion
@@ -29,16 +30,9 @@ _TOP_N = 5
29
30
 
30
31
  # Detectors that surface virtual environments. Cache / artifact detectors
31
32
  # are intentionally excluded from doctor – use `killpy clean` for those.
33
+ _CACHE_ARTIFACT_TYPES: frozenset[str] = frozenset({"cache", "artifacts"})
32
34
  _ENV_TYPES: set[str] = {
33
- "venv",
34
- "poetry",
35
- "conda",
36
- "pipx",
37
- "pyenv",
38
- "pipenv",
39
- "hatch",
40
- "uv",
41
- "tox",
35
+ cls.name for cls in ALL_DETECTORS if cls.name not in _CACHE_ARTIFACT_TYPES
42
36
  }
43
37
 
44
38
 
@@ -3,39 +3,17 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import json
6
- from datetime import datetime, timedelta, timezone
7
6
  from pathlib import Path
8
7
 
9
8
  import click
10
9
  from rich.console import Console
11
10
  from rich.table import Table
12
11
 
12
+ from killpy.commands._utils import filter_envs
13
13
  from killpy.files import format_size
14
- from killpy.models import Environment
15
14
  from killpy.scanner import Scanner
16
15
 
17
16
 
18
- def _filter_envs(
19
- envs: list[Environment],
20
- types: tuple[str, ...] | None,
21
- older_than: int | None,
22
- ) -> list[Environment]:
23
- now = datetime.now(tz=timezone.utc)
24
- result = envs
25
-
26
- if types:
27
- type_set = {t.strip().lower() for t in types}
28
- result = [e for e in result if e.type.lower() in type_set]
29
-
30
- if older_than is not None:
31
- cutoff = now - timedelta(days=older_than)
32
- result = [
33
- e for e in result if e.last_accessed < cutoff
34
- ]
35
-
36
- return result
37
-
38
-
39
17
  @click.command("list")
40
18
  @click.option(
41
19
  "--path",
@@ -87,14 +65,14 @@ def list_cmd(
87
65
  if as_json_stream:
88
66
 
89
67
  def _stream_progress(_detector, envs):
90
- for env in _filter_envs(envs, types or None, older_than):
68
+ for env in filter_envs(envs, types or None, older_than):
91
69
  click.echo(json.dumps(env.to_dict()))
92
70
 
93
71
  scanner.scan(path, on_progress=_stream_progress)
94
72
  return
95
73
 
96
74
  envs = scanner.scan(path)
97
- envs = _filter_envs(envs, types or None, older_than)
75
+ envs = filter_envs(envs, types or None, older_than)
98
76
 
99
77
  if as_json:
100
78
  click.echo(json.dumps([e.to_dict() for e in envs], indent=2))
@@ -50,7 +50,8 @@ class ArtifactsDetector(AbstractDetector):
50
50
  stat = artifact_path.stat()
51
51
  size = get_total_size(artifact_path)
52
52
  mtime = datetime.fromtimestamp(
53
- stat.st_mtime, tz=timezone.utc,
53
+ stat.st_mtime,
54
+ tz=timezone.utc,
54
55
  )
55
56
  envs.append(
56
57
  Environment(
@@ -66,5 +67,4 @@ class ArtifactsDetector(AbstractDetector):
66
67
  pruned.add(d) # don't recurse inside
67
68
  directories[:] = [d for d in directories if d not in pruned]
68
69
 
69
- envs.sort(key=lambda e: e.size_bytes, reverse=True)
70
70
  return envs
@@ -81,5 +81,4 @@ class CondaDetector(AbstractDetector):
81
81
  except (FileNotFoundError, OSError) as exc:
82
82
  logger.debug("Skipping inaccessible conda env %s: %s", env_path, exc)
83
83
 
84
- envs.sort(key=lambda e: e.size_bytes, reverse=True)
85
84
  return envs
@@ -56,7 +56,8 @@ class HatchDetector(AbstractDetector):
56
56
  stat = env_dir.stat()
57
57
  size = get_total_size(env_dir)
58
58
  mtime = datetime.fromtimestamp(
59
- stat.st_mtime, tz=timezone.utc,
59
+ stat.st_mtime,
60
+ tz=timezone.utc,
60
61
  )
61
62
  envs.append(
62
63
  Environment(
@@ -73,5 +74,4 @@ class HatchDetector(AbstractDetector):
73
74
  logger.error("Cannot inspect hatch envs root: %s", exc)
74
75
  return []
75
76
 
76
- envs.sort(key=lambda e: e.size_bytes, reverse=True)
77
77
  return envs
@@ -49,7 +49,8 @@ class PipenvDetector(AbstractDetector):
49
49
  stat = venv_path.stat()
50
50
  size = get_total_size(venv_path)
51
51
  mtime = datetime.fromtimestamp(
52
- stat.st_mtime, tz=timezone.utc,
52
+ stat.st_mtime,
53
+ tz=timezone.utc,
53
54
  )
54
55
  envs.append(
55
56
  Environment(
@@ -66,5 +67,4 @@ class PipenvDetector(AbstractDetector):
66
67
  logger.error("Cannot inspect pipenv venvs root: %s", exc)
67
68
  return []
68
69
 
69
- envs.sort(key=lambda e: e.size_bytes, reverse=True)
70
70
  return envs
@@ -49,7 +49,8 @@ class PoetryDetector(AbstractDetector):
49
49
  stat = venv_path.stat()
50
50
  size = get_total_size(venv_path)
51
51
  mtime = datetime.fromtimestamp(
52
- stat.st_mtime, tz=timezone.utc,
52
+ stat.st_mtime,
53
+ tz=timezone.utc,
53
54
  )
54
55
  envs.append(
55
56
  Environment(
@@ -69,5 +70,4 @@ class PoetryDetector(AbstractDetector):
69
70
  logger.error("Cannot inspect poetry virtualenvs dir: %s", exc)
70
71
  return []
71
72
 
72
- envs.sort(key=lambda e: e.size_bytes, reverse=True)
73
73
  return envs
@@ -50,7 +50,8 @@ class PyenvDetector(AbstractDetector):
50
50
  stat = version_dir.stat()
51
51
  size = get_total_size(version_dir)
52
52
  mtime = datetime.fromtimestamp(
53
- stat.st_mtime, tz=timezone.utc,
53
+ stat.st_mtime,
54
+ tz=timezone.utc,
54
55
  )
55
56
  envs.append(
56
57
  Environment(
@@ -67,5 +68,4 @@ class PyenvDetector(AbstractDetector):
67
68
  logger.error("Cannot inspect pyenv versions root: %s", exc)
68
69
  return []
69
70
 
70
- envs.sort(key=lambda e: e.size_bytes, reverse=True)
71
71
  return envs
@@ -38,7 +38,8 @@ class ToxDetector(AbstractDetector):
38
38
  stat = tox_path.stat()
39
39
  size = get_total_size(tox_path)
40
40
  mtime = datetime.fromtimestamp(
41
- stat.st_mtime, tz=timezone.utc,
41
+ stat.st_mtime,
42
+ tz=timezone.utc,
42
43
  )
43
44
  envs.append(
44
45
  Environment(
@@ -54,5 +55,4 @@ class ToxDetector(AbstractDetector):
54
55
  pruned.add(d)
55
56
  directories[:] = [d for d in directories if d not in pruned]
56
57
 
57
- envs.sort(key=lambda e: e.size_bytes, reverse=True)
58
58
  return envs
@@ -44,7 +44,8 @@ class UvDetector(AbstractDetector):
44
44
  stat = uv_path.stat()
45
45
  size = get_total_size(uv_path)
46
46
  mtime = datetime.fromtimestamp(
47
- stat.st_mtime, tz=timezone.utc,
47
+ stat.st_mtime,
48
+ tz=timezone.utc,
48
49
  )
49
50
  envs.append(
50
51
  Environment(
@@ -60,5 +61,4 @@ class UvDetector(AbstractDetector):
60
61
  pruned.add(d)
61
62
  directories[:] = [d for d in directories if d not in pruned]
62
63
 
63
- envs.sort(key=lambda e: e.size_bytes, reverse=True)
64
64
  return envs
@@ -89,7 +89,6 @@ class VenvDetector(AbstractDetector):
89
89
  except (FileNotFoundError, OSError) as exc:
90
90
  logger.debug("Skipping %s: %s", venv_dir, exc)
91
91
 
92
- envs.sort(key=lambda e: e.size_bytes, reverse=True)
93
92
  return envs
94
93
 
95
94
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: killpy
3
- Version: 0.21.0
3
+ Version: 0.22.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/
@@ -22,9 +22,13 @@ Requires-Dist: textual>=1.0.0
22
22
 
23
23
  ![Logo](https://raw.githubusercontent.com/Tlaloc-Es/killpy/master/logo.png)
24
24
 
25
- ### Reclaim disk space by finding and deleting Python environments you no longer use
25
+ ### Free GBs of disk space by removing unused Python environments
26
26
 
27
- Python environment cleaner for virtualenv, Conda, Poetry, pipx, pyenv, tox, Pipenv, Hatch, uv, caches, and build artifacts.
27
+ Find and delete old `.venv`, conda, poetry, pipenv, uv and more safely, in seconds.
28
+
29
+ ```bash
30
+ uvx killpy --path ~
31
+ ```
28
32
 
29
33
  [Documentation](https://tlaloc-es.github.io/killpy/)
30
34
 
@@ -515,18 +519,44 @@ Each virtual environment is a full copy (or symlinked tree) of a Python interpre
515
519
 
516
520
  ______________________________________________________________________
517
521
 
518
- ## Pre-commit hook
522
+ ## Pre-commit hooks
523
+
524
+ `killpy` ships four hooks for [pre-commit](https://pre-commit.com/). Add the ones you need to your `.pre-commit-config.yaml`:
525
+
526
+ ```yaml
527
+ - repo: https://github.com/Tlaloc-Es/KillPy
528
+ rev: 0.20.0
529
+ hooks:
530
+ - id: killpy # remove __pycache__ on every commit
531
+ - id: killpy-clean-caches # also removes .mypy_cache, .pytest_cache, .ruff_cache
532
+ - id: killpy-clean-artifacts # remove dist/, build/, *.egg-info before committing
533
+ - id: killpy-remove-venv # remove .venv (manual stage — see below)
534
+ ```
535
+
536
+ | Hook id | What it removes | Default stage |
537
+ |---------|-----------------|---------------|
538
+ | `killpy` | `__pycache__` directories | `pre-commit` |
539
+ | `killpy-clean-caches` | All local cache dirs (`__pycache__`, `.mypy_cache`, `.pytest_cache`, `.ruff_cache`) | `pre-commit` |
540
+ | `killpy-clean-artifacts` | Build artifacts (`dist/`, `build/`, `*.egg-info`) | `pre-commit` |
541
+ | `killpy-remove-venv` | Local `.venv` environments | `manual` |
542
+
543
+ **`killpy-remove-venv`** is staged as `manual` because deleting the environment on every commit would require you to recreate it each time. Run it explicitly when you want a clean slate:
519
544
 
520
- Keep your repo free of `__pycache__` on every commit:
545
+ ```bash
546
+ pre-commit run killpy-remove-venv --hook-stage manual
547
+ ```
548
+
549
+ Typical minimal setup (safe for daily use):
521
550
 
522
551
  ```yaml
523
552
  - repo: https://github.com/Tlaloc-Es/KillPy
524
- rev: 0.21.0
553
+ rev: 0.22.0
525
554
  hooks:
526
555
  - id: killpy
527
- pass_filenames: false
528
556
  ```
529
557
 
558
+ Add `killpy-clean-artifacts` if your project generates `dist/` or `build/` locally and you want to guarantee they are never staged by accident.
559
+
530
560
  ______________________________________________________________________
531
561
 
532
562
  ## Safety
@@ -14,6 +14,7 @@ killpy.egg-info/requires.txt
14
14
  killpy.egg-info/top_level.txt
15
15
  killpy/cleaners/__init__.py
16
16
  killpy/commands/__init__.py
17
+ killpy/commands/_utils.py
17
18
  killpy/commands/clean.py
18
19
  killpy/commands/delete.py
19
20
  killpy/commands/doctor.py
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "killpy"
3
- version = "0.21.0"
3
+ version = "0.22.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 = [
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes