killpy 0.24.0__tar.gz → 0.24.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.
Files changed (44) hide show
  1. {killpy-0.24.0 → killpy-0.24.1}/PKG-INFO +5 -2
  2. {killpy-0.24.0 → killpy-0.24.1}/README.md +4 -1
  3. {killpy-0.24.0 → killpy-0.24.1}/killpy/__main__.py +10 -2
  4. {killpy-0.24.0 → killpy-0.24.1}/killpy/cli.py +52 -22
  5. {killpy-0.24.0 → killpy-0.24.1}/killpy/commands/delete.py +10 -2
  6. {killpy-0.24.0 → killpy-0.24.1}/killpy/intelligence/tracker.py +29 -1
  7. {killpy-0.24.0 → killpy-0.24.1}/killpy.egg-info/PKG-INFO +5 -2
  8. {killpy-0.24.0 → killpy-0.24.1}/pyproject.toml +1 -1
  9. {killpy-0.24.0 → killpy-0.24.1}/killpy/__init__.py +0 -0
  10. {killpy-0.24.0 → killpy-0.24.1}/killpy/cleaner.py +0 -0
  11. {killpy-0.24.0 → killpy-0.24.1}/killpy/cleaners/__init__.py +0 -0
  12. {killpy-0.24.0 → killpy-0.24.1}/killpy/commands/__init__.py +0 -0
  13. {killpy-0.24.0 → killpy-0.24.1}/killpy/commands/_utils.py +0 -0
  14. {killpy-0.24.0 → killpy-0.24.1}/killpy/commands/clean.py +0 -0
  15. {killpy-0.24.0 → killpy-0.24.1}/killpy/commands/doctor.py +0 -0
  16. {killpy-0.24.0 → killpy-0.24.1}/killpy/commands/find.py +0 -0
  17. {killpy-0.24.0 → killpy-0.24.1}/killpy/commands/list.py +0 -0
  18. {killpy-0.24.0 → killpy-0.24.1}/killpy/commands/stats.py +0 -0
  19. {killpy-0.24.0 → killpy-0.24.1}/killpy/detectors/__init__.py +0 -0
  20. {killpy-0.24.0 → killpy-0.24.1}/killpy/detectors/artifacts.py +0 -0
  21. {killpy-0.24.0 → killpy-0.24.1}/killpy/detectors/base.py +0 -0
  22. {killpy-0.24.0 → killpy-0.24.1}/killpy/detectors/cache.py +0 -0
  23. {killpy-0.24.0 → killpy-0.24.1}/killpy/detectors/conda.py +0 -0
  24. {killpy-0.24.0 → killpy-0.24.1}/killpy/detectors/hatch.py +0 -0
  25. {killpy-0.24.0 → killpy-0.24.1}/killpy/detectors/pipenv.py +0 -0
  26. {killpy-0.24.0 → killpy-0.24.1}/killpy/detectors/pipx.py +0 -0
  27. {killpy-0.24.0 → killpy-0.24.1}/killpy/detectors/poetry.py +0 -0
  28. {killpy-0.24.0 → killpy-0.24.1}/killpy/detectors/pyenv.py +0 -0
  29. {killpy-0.24.0 → killpy-0.24.1}/killpy/detectors/tox.py +0 -0
  30. {killpy-0.24.0 → killpy-0.24.1}/killpy/detectors/uv.py +0 -0
  31. {killpy-0.24.0 → killpy-0.24.1}/killpy/detectors/venv.py +0 -0
  32. {killpy-0.24.0 → killpy-0.24.1}/killpy/files/__init__.py +0 -0
  33. {killpy-0.24.0 → killpy-0.24.1}/killpy/intelligence/__init__.py +0 -0
  34. {killpy-0.24.0 → killpy-0.24.1}/killpy/intelligence/git_analyzer.py +0 -0
  35. {killpy-0.24.0 → killpy-0.24.1}/killpy/intelligence/scoring.py +0 -0
  36. {killpy-0.24.0 → killpy-0.24.1}/killpy/intelligence/suggestions.py +0 -0
  37. {killpy-0.24.0 → killpy-0.24.1}/killpy/models.py +0 -0
  38. {killpy-0.24.0 → killpy-0.24.1}/killpy/scanner.py +0 -0
  39. {killpy-0.24.0 → killpy-0.24.1}/killpy.egg-info/SOURCES.txt +0 -0
  40. {killpy-0.24.0 → killpy-0.24.1}/killpy.egg-info/dependency_links.txt +0 -0
  41. {killpy-0.24.0 → killpy-0.24.1}/killpy.egg-info/entry_points.txt +0 -0
  42. {killpy-0.24.0 → killpy-0.24.1}/killpy.egg-info/requires.txt +0 -0
  43. {killpy-0.24.0 → killpy-0.24.1}/killpy.egg-info/top_level.txt +0 -0
  44. {killpy-0.24.0 → killpy-0.24.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: killpy
3
- Version: 0.24.0
3
+ Version: 0.24.1
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/
@@ -31,6 +31,8 @@ Find and delete old `.venv`, conda, poetry, pipenv, uv and more — safely, in s
31
31
  uvx killpy --path ~
32
32
  ```
33
33
 
34
+ > ⭐ Featured in [awesome-python](https://github.com/vinta/awesome-python) — a curated list of the best Python tools
35
+
34
36
  [Documentation](https://tlaloc-es.github.io/killpy/)
35
37
 
36
38
  [![PyPI](https://img.shields.io/pypi/v/killpy.svg)](https://pypi.org/project/killpy/)
@@ -40,6 +42,7 @@ uvx killpy --path ~
40
42
  [![Coverage](https://codecov.io/gh/Tlaloc-Es/killpy/branch/master/graph/badge.svg)](https://codecov.io/gh/Tlaloc-Es/killpy)
41
43
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
42
44
  [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](<https://twitter.com/intent/tweet?text=%F0%9F%90%8D%20KillPy%20helps%20you%20reclaim%20disk%20space%20by%20detecting%20unused%20Python%20environments%20(.venv,%20poetry%20env,%20conda%20env)%20and%20pipx%20packages.%20Clean,%20organize%20and%20free%20up%20space%20effortlessly!%20%F0%9F%9A%80&url=https://github.com/Tlaloc-Es/KillPy>)
45
+ [![Awesome Python](https://awesome.re/badge.svg)](https://github.com/vinta/awesome-python)
43
46
 
44
47
  ![killpy in action](https://raw.githubusercontent.com/Tlaloc-Es/killpy/master/show.gif)
45
48
 
@@ -584,7 +587,7 @@ Typical minimal setup (safe for daily use):
584
587
 
585
588
  ```yaml
586
589
  - repo: https://github.com/Tlaloc-Es/KillPy
587
- rev: 0.24.0
590
+ rev: 0.24.1
588
591
  hooks:
589
592
  - id: killpy
590
593
  ```
@@ -10,6 +10,8 @@ Find and delete old `.venv`, conda, poetry, pipenv, uv and more — safely, in s
10
10
  uvx killpy --path ~
11
11
  ```
12
12
 
13
+ > ⭐ Featured in [awesome-python](https://github.com/vinta/awesome-python) — a curated list of the best Python tools
14
+
13
15
  [Documentation](https://tlaloc-es.github.io/killpy/)
14
16
 
15
17
  [![PyPI](https://img.shields.io/pypi/v/killpy.svg)](https://pypi.org/project/killpy/)
@@ -19,6 +21,7 @@ uvx killpy --path ~
19
21
  [![Coverage](https://codecov.io/gh/Tlaloc-Es/killpy/branch/master/graph/badge.svg)](https://codecov.io/gh/Tlaloc-Es/killpy)
20
22
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
21
23
  [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](<https://twitter.com/intent/tweet?text=%F0%9F%90%8D%20KillPy%20helps%20you%20reclaim%20disk%20space%20by%20detecting%20unused%20Python%20environments%20(.venv,%20poetry%20env,%20conda%20env)%20and%20pipx%20packages.%20Clean,%20organize%20and%20free%20up%20space%20effortlessly!%20%F0%9F%9A%80&url=https://github.com/Tlaloc-Es/KillPy>)
24
+ [![Awesome Python](https://awesome.re/badge.svg)](https://github.com/vinta/awesome-python)
22
25
 
23
26
  ![killpy in action](https://raw.githubusercontent.com/Tlaloc-Es/killpy/master/show.gif)
24
27
 
@@ -563,7 +566,7 @@ Typical minimal setup (safe for daily use):
563
566
 
564
567
  ```yaml
565
568
  - repo: https://github.com/Tlaloc-Es/KillPy
566
- rev: 0.24.0
569
+ rev: 0.24.1
567
570
  hooks:
568
571
  - id: killpy
569
572
  ```
@@ -52,6 +52,14 @@ def _run_delete_all(path: Path, excluded: set[str], yes: bool) -> None:
52
52
  console.print("Aborted.")
53
53
  return
54
54
 
55
+ # Record this cleanup session up-front so `killpy stats --history` reflects
56
+ # it; record_deletion() below updates the same record with the freed bytes.
57
+ tracker = UsageTracker()
58
+ try:
59
+ tracker.record_scan_result(envs, path)
60
+ except Exception: # noqa: BLE001
61
+ pass
62
+
55
63
  deleted = 0
56
64
  freed = 0
57
65
  for env in envs:
@@ -67,9 +75,9 @@ def _run_delete_all(path: Path, excluded: set[str], yes: bool) -> None:
67
75
  f"freed [bold]{format_size(freed)}[/bold]."
68
76
  )
69
77
 
70
- # Best-effort: record deletion in history.
78
+ # Best-effort: update the history record created above with freed bytes.
71
79
  try:
72
- UsageTracker().record_deletion(freed)
80
+ tracker.record_deletion(freed)
73
81
  except Exception: # noqa: BLE001
74
82
  pass
75
83
 
@@ -11,6 +11,7 @@ from typing import Any, TypedDict
11
11
  from rich.text import Text
12
12
  from textual.app import App, ComposeResult
13
13
  from textual.binding import Binding
14
+ from textual.coordinate import Coordinate
14
15
  from textual.widgets import (
15
16
  DataTable,
16
17
  Footer,
@@ -25,7 +26,7 @@ from textual.widgets import (
25
26
  from killpy.cleaner import Cleaner, CleanerError
26
27
  from killpy.cleaners import remove_pycache
27
28
  from killpy.files import format_size
28
- from killpy.intelligence import SuggestionEngine, score_all
29
+ from killpy.intelligence import SuggestionEngine, UsageTracker, score_all
29
30
  from killpy.models import Environment
30
31
  from killpy.scanner import Scanner
31
32
 
@@ -142,9 +143,10 @@ class TableApp(App):
142
143
  self._filter_query: str = ""
143
144
  self._venv_display_indices: list[int] = []
144
145
  self._multi_select_mode: bool = False
145
- self._selected_venv_indices: set[int] = set()
146
+ self._selected_venv_paths: set[str] = set()
146
147
  self._health_by_path: dict[str, str] = {}
147
148
  self.cleaner = Cleaner()
149
+ self.tracker = UsageTracker()
148
150
  self.scanner = Scanner(
149
151
  types={
150
152
  "venv",
@@ -395,7 +397,7 @@ class TableApp(App):
395
397
  row["size"],
396
398
  row["size_human"],
397
399
  _health_text(row["health"]),
398
- self._compute_row_status(i, row),
400
+ self._compute_row_status(row),
399
401
  )
400
402
 
401
403
  def render_pipx_table(self) -> None:
@@ -416,18 +418,18 @@ class TableApp(App):
416
418
  return data_index, self.venv_rows[data_index]
417
419
  return None
418
420
 
419
- def _compute_row_status(self, data_index: int, row: VenvRow) -> str:
421
+ def _compute_row_status(self, row: VenvRow) -> str:
420
422
  """Return the status string to display, taking multi-select into account."""
421
423
  if row["status"] == EnvStatus.DELETED.value:
422
424
  return EnvStatus.DELETED.value
423
- if self._multi_select_mode and data_index in self._selected_venv_indices:
425
+ if self._multi_select_mode and row["path"] in self._selected_venv_paths:
424
426
  return "\u25cf SELECTED"
425
427
  return row["status"]
426
428
 
427
429
  def _update_multi_select_label(self) -> None:
428
430
  label = self.query_one("#multi-select-label", Label)
429
431
  if self._multi_select_mode:
430
- n = len(self._selected_venv_indices)
432
+ n = len(self._selected_venv_paths)
431
433
  label.add_class("visible")
432
434
  label.update(
433
435
  f"[bold yellow]Multi-select[/bold yellow] — "
@@ -572,8 +574,27 @@ class TableApp(App):
572
574
  status_label.update(
573
575
  f"Found {venv_count} virtual environments and {pipx_count} pipx packages"
574
576
  )
577
+ self._record_scan()
575
578
  await self._compute_health_scores()
576
579
 
580
+ def _record_scan(self) -> None:
581
+ """Persist this scan session so ``killpy stats --history`` reflects it."""
582
+ try:
583
+ envs = [row["environment"] for row in self.venv_rows]
584
+ envs += [row["environment"] for row in self.pipx_rows]
585
+ self.tracker.record_scan_result(envs, self.root_dir)
586
+ except Exception: # noqa: BLE001
587
+ pass
588
+
589
+ def _record_deletion(self, freed: int) -> None:
590
+ """Best-effort: add *freed* bytes to the current history record."""
591
+ if freed <= 0:
592
+ return
593
+ try:
594
+ self.tracker.record_deletion(freed)
595
+ except Exception: # noqa: BLE001
596
+ pass
597
+
577
598
  async def _compute_health_scores(self) -> None:
578
599
  """Score venv environments and populate the Health column."""
579
600
  if not self.venv_rows:
@@ -614,25 +635,32 @@ class TableApp(App):
614
635
 
615
636
  @is_venv_tab
616
637
  def action_confirm_delete(self):
617
- if self._multi_select_mode and self._selected_venv_indices:
618
- # Multi-select mode: delete all selected rows
619
- for data_index in list(self._selected_venv_indices):
620
- row = self.venv_rows[data_index]
638
+ freed_now = 0
639
+ if self._multi_select_mode and self._selected_venv_paths:
640
+ # Multi-select mode: delete all selected rows. Selection is
641
+ # tracked by path (not by list position) so it stays valid even
642
+ # after the rows have been re-sorted.
643
+ for row in self.venv_rows:
644
+ if row["path"] not in self._selected_venv_paths:
645
+ continue
621
646
  if row["status"] == EnvStatus.DELETED.value:
622
647
  continue
623
648
  if self.delete_environment(row["environment"]):
649
+ freed_now += int(row["size"])
624
650
  self.bytes_release += int(row["size"])
625
651
  row["status"] = EnvStatus.DELETED.value
626
- self._selected_venv_indices.clear()
652
+ self._selected_venv_paths.clear()
627
653
  else:
628
654
  # Normal mode: delete all rows marked for deletion
629
655
  for row in self.venv_rows:
630
656
  if row["status"] != EnvStatus.MARKED_TO_DELETE.value:
631
657
  continue
632
658
  if self.delete_environment(row["environment"]):
659
+ freed_now += int(row["size"])
633
660
  self.bytes_release += int(row["size"])
634
661
  row["status"] = EnvStatus.DELETED.value
635
662
 
663
+ self._record_deletion(freed_now)
636
664
  self.render_venv_table()
637
665
  self.query_one("#status-label", Label).update(
638
666
  f"{format_size(self.bytes_release)} deleted"
@@ -675,6 +703,7 @@ class TableApp(App):
675
703
  return
676
704
  if self.delete_environment(row["environment"]):
677
705
  self.bytes_release += int(row["size"])
706
+ self._record_deletion(int(row["size"]))
678
707
  row["status"] = EnvStatus.DELETED.value
679
708
  table.update_cell_at(
680
709
  (cursor_cell.row, self.VENV_COL_STATUS),
@@ -706,6 +735,7 @@ class TableApp(App):
706
735
  row["status"] = EnvStatus.DELETED.value
707
736
  table.update_cell_at((cursor_cell.row, 3), EnvStatus.DELETED.value)
708
737
  self.bytes_release += int(row["size"])
738
+ self._record_deletion(int(row["size"]))
709
739
  self.query_one("#status-label", Label).update(
710
740
  f"{format_size(self.bytes_release)} deleted"
711
741
  )
@@ -794,7 +824,7 @@ class TableApp(App):
794
824
  """Toggle multi-select mode on/off (T key)."""
795
825
  self._multi_select_mode = not self._multi_select_mode
796
826
  if not self._multi_select_mode:
797
- self._selected_venv_indices.clear()
827
+ self._selected_venv_paths.clear()
798
828
  self._update_multi_select_label()
799
829
  self.render_venv_table()
800
830
 
@@ -810,17 +840,17 @@ class TableApp(App):
810
840
  resolved = self._resolve_venv_row(cursor_cell.row)
811
841
  if not resolved:
812
842
  return
813
- data_index, row = resolved
843
+ _, row = resolved
814
844
  if row["status"] == EnvStatus.DELETED.value:
815
845
  return
816
- if data_index in self._selected_venv_indices:
817
- self._selected_venv_indices.discard(data_index)
846
+ if row["path"] in self._selected_venv_paths:
847
+ self._selected_venv_paths.discard(row["path"])
818
848
  else:
819
- self._selected_venv_indices.add(data_index)
849
+ self._selected_venv_paths.add(row["path"])
820
850
  self._update_multi_select_label()
821
851
  table.update_cell_at(
822
- (cursor_cell.row, self.VENV_COL_STATUS),
823
- self._compute_row_status(data_index, row),
852
+ Coordinate(cursor_cell.row, self.VENV_COL_STATUS),
853
+ self._compute_row_status(row),
824
854
  )
825
855
 
826
856
  @is_venv_tab
@@ -829,13 +859,13 @@ class TableApp(App):
829
859
  if not self._multi_select_mode:
830
860
  return
831
861
  non_deleted = {
832
- i
862
+ self.venv_rows[i]["path"]
833
863
  for i in self._venv_display_indices
834
864
  if self.venv_rows[i]["status"] != EnvStatus.DELETED.value
835
865
  }
836
- if non_deleted == self._selected_venv_indices:
837
- self._selected_venv_indices.clear()
866
+ if non_deleted == self._selected_venv_paths:
867
+ self._selected_venv_paths.clear()
838
868
  else:
839
- self._selected_venv_indices = non_deleted
869
+ self._selected_venv_paths = non_deleted
840
870
  self._update_multi_select_label()
841
871
  self.render_venv_table()
@@ -96,6 +96,14 @@ def delete_cmd(
96
96
  abort=True,
97
97
  )
98
98
 
99
+ # Record this cleanup session up-front so `killpy stats --history` reflects
100
+ # it; record_deletion() below updates the same record with the freed bytes.
101
+ tracker = UsageTracker()
102
+ try:
103
+ tracker.record_scan_result(envs, path)
104
+ except Exception: # noqa: BLE001
105
+ pass
106
+
99
107
  cleaner = Cleaner(dry_run=False)
100
108
  freed = 0
101
109
  errors = 0
@@ -114,9 +122,9 @@ def delete_cmd(
114
122
  + (f" — [red]{errors} error(s)[/red]" if errors else "")
115
123
  )
116
124
 
117
- # Best-effort: record deletion in history.
125
+ # Best-effort: update the history record created above with freed bytes.
118
126
  try:
119
- UsageTracker().record_deletion(freed)
127
+ tracker.record_deletion(freed)
120
128
  except Exception: # noqa: BLE001
121
129
  pass
122
130
 
@@ -6,9 +6,11 @@ import json
6
6
  import logging
7
7
  import os
8
8
  import tempfile
9
+ from collections.abc import Iterable
10
+ from datetime import datetime, timezone
9
11
  from pathlib import Path
10
12
 
11
- from killpy.models import ScanRecord, ScoredEnvironment
13
+ from killpy.models import Environment, ScanRecord, ScoredEnvironment
12
14
 
13
15
  logger = logging.getLogger(__name__)
14
16
 
@@ -35,6 +37,32 @@ class UsageTracker:
35
37
  history.append(record.to_dict())
36
38
  self._save(history)
37
39
 
40
+ def record_scan_result(
41
+ self,
42
+ environments: Iterable[Environment],
43
+ scan_path: Path | str,
44
+ *,
45
+ deleted_bytes: int = 0,
46
+ ) -> None:
47
+ """Persist a :class:`~killpy.models.ScanRecord` built from a scan result.
48
+
49
+ Convenience wrapper around :meth:`record_scan` so callers don't have to
50
+ assemble a :class:`ScanRecord` by hand. Records the number of
51
+ environments found and their total size; ``deleted_bytes`` can be set
52
+ when the freed amount is already known, otherwise use
53
+ :meth:`record_deletion` afterwards to update the same record.
54
+ """
55
+ envs = list(environments)
56
+ self.record_scan(
57
+ ScanRecord(
58
+ timestamp=datetime.now(tz=timezone.utc),
59
+ total_space_found=sum(e.size_bytes for e in envs),
60
+ total_space_deleted=deleted_bytes,
61
+ environments_count=len(envs),
62
+ scan_path=str(scan_path),
63
+ )
64
+ )
65
+
38
66
  def record_deletion(self, size_bytes: int) -> None:
39
67
  """Increment the ``total_space_deleted`` counter of the last scan record."""
40
68
  history = self._load()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: killpy
3
- Version: 0.24.0
3
+ Version: 0.24.1
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/
@@ -31,6 +31,8 @@ Find and delete old `.venv`, conda, poetry, pipenv, uv and more — safely, in s
31
31
  uvx killpy --path ~
32
32
  ```
33
33
 
34
+ > ⭐ Featured in [awesome-python](https://github.com/vinta/awesome-python) — a curated list of the best Python tools
35
+
34
36
  [Documentation](https://tlaloc-es.github.io/killpy/)
35
37
 
36
38
  [![PyPI](https://img.shields.io/pypi/v/killpy.svg)](https://pypi.org/project/killpy/)
@@ -40,6 +42,7 @@ uvx killpy --path ~
40
42
  [![Coverage](https://codecov.io/gh/Tlaloc-Es/killpy/branch/master/graph/badge.svg)](https://codecov.io/gh/Tlaloc-Es/killpy)
41
43
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
42
44
  [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](<https://twitter.com/intent/tweet?text=%F0%9F%90%8D%20KillPy%20helps%20you%20reclaim%20disk%20space%20by%20detecting%20unused%20Python%20environments%20(.venv,%20poetry%20env,%20conda%20env)%20and%20pipx%20packages.%20Clean,%20organize%20and%20free%20up%20space%20effortlessly!%20%F0%9F%9A%80&url=https://github.com/Tlaloc-Es/KillPy>)
45
+ [![Awesome Python](https://awesome.re/badge.svg)](https://github.com/vinta/awesome-python)
43
46
 
44
47
  ![killpy in action](https://raw.githubusercontent.com/Tlaloc-Es/killpy/master/show.gif)
45
48
 
@@ -584,7 +587,7 @@ Typical minimal setup (safe for daily use):
584
587
 
585
588
  ```yaml
586
589
  - repo: https://github.com/Tlaloc-Es/KillPy
587
- rev: 0.24.0
590
+ rev: 0.24.1
588
591
  hooks:
589
592
  - id: killpy
590
593
  ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "killpy"
3
- version = "0.24.0"
3
+ version = "0.24.1"
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
File without changes
File without changes
File without changes