├── .codespellrc
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── feature_request.md
│ ├── question.md
│ └── task.md
└── workflows
│ ├── bump-version-dev.yml
│ ├── bump-version.yml
│ ├── publish-package-dev.yml
│ └── publish-package.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .pre-commit-hooks.yaml
├── .python-version
├── .ruff.toml
├── .vscode
└── settings.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── killpy
├── __init__.py
├── __main__.py
├── cleaners
│ └── __init__.py
├── cli.py
├── commands
│ ├── __init__.py
│ └── clean.py
├── files
│ └── __init__.py
└── killers
│ ├── __init__.py
│ ├── conda_killer.py
│ ├── killer.py
│ ├── pipx_killer.py
│ ├── poetry_killer.py
│ ├── pyenv_killer.py
│ └── venv_killer.py
├── pyproject.toml
├── show.gif
└── uv.lock
/.codespellrc:
--------------------------------------------------------------------------------
1 | [codespell]
2 | exclude-file=uv.lock
3 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ______________________________________________________________________
2 |
3 | name: Bug report
4 | about: Create a report to help us improve
5 | title: "[BUG]"
6 | labels: bug
7 | assignees: Tlaloc-Es
8 |
9 | ______________________________________________________________________
10 |
11 | **Issue description**
12 | Describe the issue you are experiencing in detail.
13 |
14 | **Steps to reproduce**
15 | Please provide detailed steps to reproduce the issue, including any code snippets, configuration settings, or error messages that you received.
16 |
17 | **Expected behavior**
18 | Explain what you expected to happen when you encountered the issue.
19 |
20 | **Actual behavior**
21 | Explain what actually happened when you encountered the issue.
22 |
23 | **Environment**
24 |
25 | - Python version:
26 | - Library version:
27 | - Operating system:
28 |
29 | **Additional context**
30 | Provide any additional context or information that may be relevant to the issue, such as relevant documentation links, screenshots, or error logs.
31 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ______________________________________________________________________
2 |
3 | name: Feature request
4 | about: Suggest an idea for this project
5 | title: ''
6 | labels: enhancement
7 | assignees: Tlaloc-Es
8 |
9 | ______________________________________________________________________
10 |
11 | **Feature description**
12 | Describe the feature you are requesting in detail.
13 |
14 | **Use case**
15 | Explain how you envision using this feature, and how it would benefit you and/or other users.
16 |
17 | **Proposed implementation**
18 | If you have ideas for how the feature could be implemented, please provide them here. This could include code samples, API designs, or anything else that would help the project maintainers understand your proposal.
19 |
20 | **Alternatives considered**
21 | Have you considered any alternatives to this feature request? If so, please describe them and explain why you believe this feature is a better solution.
22 |
23 | **Additional context**
24 | Provide any additional context or information that may be relevant to the feature request, such as relevant documentation links, screenshots, or examples from other projects.
25 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/question.md:
--------------------------------------------------------------------------------
1 | ______________________________________________________________________
2 |
3 | name: Question
4 | about: Ask a question
5 | title: "[QUESTION]"
6 | labels: question
7 | assignees: Tlaloc-Es
8 |
9 | ______________________________________________________________________
10 |
11 | **Question**
12 | State your question clearly and concisely.
13 |
14 | **Background**
15 | Provide some context or background information to help readers understand your question. This could include code snippets, relevant documentation links, or a brief summary of your project.
16 |
17 | **What I've tried**
18 | Explain what steps you've taken so far to try to answer the question yourself. This can help others understand what you've already done and avoid suggesting solutions that you've already tried.
19 |
20 | **Expected outcome**
21 | Explain what outcome you are expecting from the question. This can help others tailor their answers to your specific needs.
22 |
23 | **Additional context**
24 | Provide any additional context or information that may be relevant to your question, such as relevant documentation links, screenshots, or error logs.
25 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/task.md:
--------------------------------------------------------------------------------
1 | ______________________________________________________________________
2 |
3 | name: Task
4 | about: Use this template to track general tasks, such as improving documentation or minor adjustments
5 | title: "[TASK]"
6 | labels: task
7 | assignees: Tlaloc-Es
8 |
9 | ______________________________________________________________________
10 |
11 | **Task description**
12 | Provide a detailed description of the task to be completed. Be clear and concise about the objective.
13 |
14 | **Objective**
15 | What is the expected outcome of completing this task?
16 | Example: "Complete the missing sections of the project documentation, focusing on the models module."
17 |
18 | **Steps to complete**
19 | List the steps required to complete this task, if applicable.
20 | Example:
21 |
22 | 1. Review the current documentation structure.
23 | 1. Add missing sections for the `models` module.
24 | 1. Validate the documentation with `mkdocs serve`.
25 |
26 | **Priority**
27 |
28 | - [ ] Low
29 | - [ ] Medium
30 | - [ ] High
31 |
32 | **Relevant links/files**
33 | Include any links to documentation, code, or files that might help complete this task.
34 |
35 | **Additional context**
36 | Add any additional information that might be useful, such as dependencies, deadlines, or suggestions.
37 |
--------------------------------------------------------------------------------
/.github/workflows/bump-version-dev.yml:
--------------------------------------------------------------------------------
1 | # This workflow will install Python dependencies, run tests and lint with a single version of Python
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
3 |
4 | name: Bump version dev
5 |
6 | on:
7 | push:
8 | branches:
9 | - dev
10 |
11 | jobs:
12 | bump_version:
13 | if: "!startsWith(github.event.head_commit.message, 'bump:')"
14 | runs-on: ubuntu-latest
15 | name: "Bump version and create changelog with commitizen"
16 | steps:
17 | - name: Check out
18 | uses: actions/checkout@v2
19 | with:
20 | fetch-depth: 0
21 | token: "${{ secrets.GITHUB_TOKEN }}"
22 | - id: cz
23 | name: Create bump and changelog
24 | uses: commitizen-tools/commitizen-action@master
25 | with:
26 | github_token: ${{ secrets.GITHUB_TOKEN }}
27 | prerelease: beta
28 | - name: Print Version
29 | run: echo "Bumped to version ${{ steps.cz.outputs.version }}"
30 |
--------------------------------------------------------------------------------
/.github/workflows/bump-version.yml:
--------------------------------------------------------------------------------
1 | # This workflow will install Python dependencies, run tests and lint with a single version of Python
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
3 |
4 | name: Bump version
5 |
6 | on:
7 | push:
8 | branches:
9 | - master
10 | pull_request:
11 | types: [closed]
12 | branches:
13 | - master
14 |
15 | jobs:
16 | bump_version:
17 | if: "!startsWith(github.event.head_commit.message, 'bump:')"
18 | runs-on: ubuntu-latest
19 | name: "Bump version and create changelog with commitizen"
20 | steps:
21 | - name: Check out
22 | uses: actions/checkout@v2
23 | with:
24 | fetch-depth: 0
25 | token: "${{ secrets.GITHUB_TOKEN }}"
26 | - id: cz
27 | name: Create bump and changelog
28 | uses: commitizen-tools/commitizen-action@master
29 | with:
30 | github_token: ${{ secrets.GITHUB_TOKEN }}
31 | - name: Print Version
32 | run: echo "Bumped to version ${{ steps.cz.outputs.version }}"
33 |
--------------------------------------------------------------------------------
/.github/workflows/publish-package-dev.yml:
--------------------------------------------------------------------------------
1 | name: Publish beta package
2 |
3 | on:
4 | workflow_run:
5 | workflows: ["Bump version dev"]
6 | types:
7 | - completed
8 | workflow_dispatch:
9 |
10 |
11 | jobs:
12 | publish-service-client-package:
13 | runs-on: ubuntu-latest
14 | name: "Publish package at PyPi"
15 | permissions:
16 | contents: write
17 | steps:
18 | - uses: actions/checkout@v2
19 | with:
20 | submodules: recursive
21 | token: ${{ secrets.GITHUB_TOKEN }}
22 | - name: Install uv
23 | uses: astral-sh/setup-uv@v5
24 | - name: "build"
25 | env:
26 | UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN_DEV }}
27 | id: build
28 | run: |
29 | uv build
30 | uv publish --publish-url https://test.pypi.org/legacy/
31 |
--------------------------------------------------------------------------------
/.github/workflows/publish-package.yml:
--------------------------------------------------------------------------------
1 | name: Publish package
2 |
3 | on:
4 | workflow_run:
5 | workflows: ["Bump version"]
6 | types:
7 | - completed
8 |
9 | jobs:
10 | publish-service-client-package:
11 | runs-on: ubuntu-latest
12 | name: "Publish package at PyPi"
13 | permissions:
14 | contents: write
15 | steps:
16 | - uses: actions/checkout@v2
17 | with:
18 | submodules: recursive
19 | token: ${{ secrets.GITHUB_TOKEN }}
20 | - name: Install uv
21 | uses: astral-sh/setup-uv@v5
22 | - name: "build"
23 | env:
24 | UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
25 | id: build
26 | run: |
27 | uv build
28 | uv publish
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Python-generated files
2 | __pycache__/
3 | *.py[oc]
4 | build/
5 | dist/
6 | wheels/
7 | *.egg-info
8 |
9 | # Virtual environments
10 | .venv
11 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | repos:
2 | - repo: https://github.com/pre-commit/pre-commit-hooks
3 | rev: v5.0.0
4 | hooks:
5 | - id: check-yaml
6 | - id: check-json
7 | - id: check-toml
8 | - id: end-of-file-fixer
9 | - id: trailing-whitespace
10 |
11 | - repo: https://github.com/executablebooks/mdformat
12 | rev: 0.7.21
13 | hooks:
14 | - id: mdformat
15 |
16 | - hooks:
17 | - id: commitizen
18 | repo: https://github.com/commitizen-tools/commitizen
19 | rev: v4.1.0
20 |
21 | - repo: https://github.com/astral-sh/ruff-pre-commit
22 | rev: v0.8.5
23 | hooks:
24 | - id: ruff
25 | args: [ --fix]
26 | - id: ruff-format
27 |
28 | - repo: https://github.com/pre-commit/mirrors-mypy
29 | rev: "v1.14.1"
30 | hooks:
31 | - id: mypy
32 | - repo: https://github.com/codespell-project/codespell
33 | rev: v2.3.0
34 | hooks:
35 | - id: codespell
36 | args: ["@.codespellrc"]
37 |
--------------------------------------------------------------------------------
/.pre-commit-hooks.yaml:
--------------------------------------------------------------------------------
1 | - id: killpy
2 | name: killpy
3 | description: Clean repo
4 | entry: killpy clean
5 | language: python
6 |
--------------------------------------------------------------------------------
/.python-version:
--------------------------------------------------------------------------------
1 | 3.12
2 |
--------------------------------------------------------------------------------
/.ruff.toml:
--------------------------------------------------------------------------------
1 | # Exclude a variety of commonly ignored directories.
2 | exclude = [
3 | ".bzr",
4 | ".direnv",
5 | ".eggs",
6 | ".git",
7 | ".git-rewrite",
8 | ".hg",
9 | ".mypy_cache",
10 | ".nox",
11 | ".pants.d",
12 | ".pytype",
13 | ".ruff_cache",
14 | ".svn",
15 | ".tox",
16 | ".venv",
17 | "__pypackages__",
18 | "_build",
19 | "buck-out",
20 | "build",
21 | "dist",
22 | "node_modules",
23 | "venv",
24 | "src/interface",
25 | "tests"
26 | ]
27 |
28 | # Same as Black.
29 | line-length = 88
30 | indent-width = 4
31 |
32 | # Assume Python 3.10
33 | target-version = "py310"
34 |
35 |
36 | [lint.mccabe]
37 | max-complexity = 10
38 |
39 | [lint]
40 | # Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
41 | select = ["E", "F", "I", "N", "C", "W", "C90", "UP", "N", "PLC", "PLE", "PLR", "PLW"]
42 | ignore = []
43 |
44 | # Allow fix for all enabled rules (when `--fix`) is provided.
45 | fixable = ["ALL"]
46 | unfixable = []
47 |
48 | # Allow unused variables when underscore-prefixed.
49 | dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
50 |
51 | [lint.pylint]
52 | max-args = 6
53 |
54 | [lint.pydocstyle]
55 | convention = "pep257"
56 |
57 | [format]
58 | # Like Black, use double quotes for strings.
59 | quote-style = "double"
60 |
61 | # Like Black, indent with spaces, rather than tabs.
62 | indent-style = "space"
63 |
64 | # Like Black, respect magic trailing commas.
65 | skip-magic-trailing-comma = false
66 |
67 | # Like Black, automatically detect the appropriate line ending.
68 | line-ending = "auto"
69 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "files.exclude": {
3 | "**/.venv": true,
4 | "**/__pycache__": true,
5 | "**/.pytest_cache": true,
6 | "**/.ipynb_checkpoints":true,
7 | "**/**.egg-info":true,
8 | "**/build":true,
9 | "**/.ruff_cache":true,
10 | "**/.mypy_cache":true
11 | },
12 | "[python]":{
13 | "editor.defaultFormatter": "ms-python.black-formatter",
14 | "editor.formatOnSave": true
15 | },
16 | "python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python"
17 | }
18 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.15.4 (2025-02-07)
2 |
3 | ### Fix
4 |
5 | - set logging level to INFO in clean command for better visibility
6 |
7 | ## 0.15.3 (2025-02-07)
8 |
9 | ### Fix
10 |
11 | - enhance logging in clean command to report freed space
12 |
13 | ## 0.15.2 (2025-02-07)
14 |
15 | ### Fix
16 |
17 | - update killpy entry in pre-commit hooks to use clean command
18 |
19 | ## 0.15.1 (2025-02-07)
20 |
21 | ### Fix
22 |
23 | - update killpy entry point to use CLI
24 |
25 | ## 0.15.0 (2025-02-07)
26 |
27 | ### Feat
28 |
29 | - add clean command to killpy and integrate with CLI
30 |
31 | ## 0.14.1 (2025-01-23)
32 |
33 | ### Fix
34 |
35 | - the app breaks when 'pipx' is not installed. #14
36 |
37 | ## 0.14.0 (2025-01-17)
38 |
39 | ### Fix
40 |
41 | - forcebump
42 |
43 | ## 0.13.0 (2025-01-17)
44 |
45 | ### Feat
46 |
47 | - add GitHub Actions workflow for version bumping and changelog generation
48 |
49 | ### Fix
50 |
51 | - forcebump
52 | - update environment removal methods to use killers dictionary
53 | - update import path for TableApp in main module
54 |
55 | ### Refactor
56 |
57 | - separate entities
58 |
59 | ## 0.11.0b4 (2025-01-17)
60 |
61 | ### Feat
62 |
63 | - add GitHub Actions workflow for version bumping and changelog generation
64 |
65 | ### Fix
66 |
67 | - update environment removal methods to use killers dictionary
68 | - update import path for TableApp in main module
69 |
70 | ### Refactor
71 |
72 | - separate entities
73 |
74 | ## 0.11.0 (2025-01-16)
75 |
76 | ## 0.11.0b3 (2025-01-17)
77 |
78 | ## 0.11.0b2 (2025-01-17)
79 |
80 | ## 0.11.0b1 (2025-01-17)
81 |
82 | ## 0.11.0b0 (2025-01-17)
83 |
84 | ### Feat
85 |
86 | - add GitHub Actions workflow for version bumping and changelog generation
87 | - add support for pipx package management and enhance virtual environment tab functionality
88 |
89 | ### Refactor
90 |
91 | - separate entities
92 |
93 | ## 0.11.0 (2025-01-16)
94 |
95 | ### Feat
96 |
97 | - add support for pipx package management and enhance virtual environment tab functionality
98 |
99 | ## 0.10.0 (2025-01-06)
100 |
101 | ### Feat
102 |
103 | - enhance README and add support for listing Poetry virtual environments
104 |
105 | ## 0.9.0 (2025-01-05)
106 |
107 | ### Feat
108 |
109 | - add functionality to clean up __pycache__ directories and update README
110 |
111 | ## 0.8.4 (2025-01-05)
112 |
113 | ### Fix
114 |
115 | - update sorting logic in find_venvs functions to sort by size
116 |
117 | ## 0.8.3 (2025-01-05)
118 |
119 | ### Fix
120 |
121 | - handle FileNotFoundError in get_total_size and find_venvs functions close #4
122 |
123 | ## 0.8.2 (2025-01-05)
124 |
125 | ### Fix
126 |
127 | - try to execute with pipx
128 |
129 | ## 0.8.1 (2025-01-05)
130 |
131 | ### Fix
132 |
133 | - consolidate environment handling functions into __main__.py and remove envs_handler.py
134 |
135 | ## 0.8.0 (2025-01-05)
136 |
137 | ### Feat
138 |
139 | - update references from 'KillPy' to 'killpy' across the project
140 |
141 | ## 0.7.0 (2025-01-05)
142 |
143 | ### Feat
144 |
145 | - replace 'KillPy' with 'killpy' in workflow and pyproject.toml
146 |
147 | ## 0.6.0 (2025-01-05)
148 |
149 | ### Feat
150 |
151 | - add KillPy script entry point to pyproject.toml
152 |
153 | ## 0.5.0 (2025-01-05)
154 |
155 | ### Feat
156 |
157 | - enhance virtual environment management with deletion features and refactor code structure close #7
158 |
159 | ## 0.4.0 (2025-01-03)
160 |
161 | ### Feat
162 |
163 | - implement asynchronous searching for virtual environments close #2
164 |
165 | ## 0.3.0 (2025-01-03)
166 |
167 | ### Feat
168 |
169 | - add support for finding virtual environments with pyvenv and remove duplicates
170 |
171 | ### Fix
172 |
173 | - remove click dependency and update package version to 0.2.1
174 |
175 | ## 0.2.2 (2025-01-03)
176 |
177 | ### Fix
178 |
179 | - change key binding from 'ctrl+m' to 'space' for deleting .venv close #5
180 |
181 | ## 0.2.1 (2025-01-03)
182 |
183 | ### Fix
184 |
185 | - fails if conda is not installed #3
186 |
187 | ## 0.2.0 (2025-01-03)
188 |
189 | ### Feat
190 |
191 | - add support for listing and removing Conda environments in the app
192 |
193 | ## 0.1.7 (2025-01-02)
194 |
195 | ### Fix
196 |
197 | - add a banner to TableApp for enhanced user interface
198 |
199 | ## 0.1.6 (2025-01-02)
200 |
201 | ### Fix
202 |
203 | - enhance TableApp with improved venv display and deletion feedback
204 |
205 | ## 0.1.5 (2025-01-02)
206 |
207 | ### Refactor
208 |
209 | - reorganize imports and update deleted_cells type annotation in TableApp
210 | - improve find_venvs and get_total_size functions for better performance and readability
211 |
212 | ## 0.1.4 (2025-01-02)
213 |
214 | ### Fix
215 |
216 | - rename script entry point from pykill to killpy in pyproject.toml
217 |
218 | ## 0.1.3 (2025-01-02)
219 |
220 | ### Fix
221 |
222 | - rename script entry point from posewebcam to pykill in pyproject.toml
223 |
224 | ## 0.1.2 (2025-01-02)
225 |
226 | ### Fix
227 |
228 | - prevent find_venvs from traversing subdirectories within `.venv` folders
229 |
230 | ## 0.1.1 (2025-01-02)
231 |
232 | ### Fix
233 |
234 | - **cli**: fix command script
235 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 Tlaloc-Es
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ```plaintext
6 | █ ▄ ▄ █ █ ▄▄▄▄ ▄ ▄ ____
7 | █▄▀ ▄ █ █ █ █ █ █ .'`_ o `;__,
8 | █ ▀▄ █ █ █ █▄▄▄▀ ▀▀▀█ . .'.'` '---' '
9 | █ █ █ █ █ █ ▄ █ .`-...-'.'Reclaim disk space by cleaning unused Python environments.
10 | ▀ ▀▀▀ `-...-'
11 | ```
12 |
13 |
14 |
15 | [](https://pypi.org/project/killpy/)
16 | [](https://pepy.tech/project/killpy)
17 | [](https://github.com/Tlaloc-Es/killpy/stargazers)
18 | [](https://twitter.com/intent/tweet?text=🐍%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🚀&url=https://github.com/Tlaloc-Es/KillPy)
19 | 
20 | 
21 |
22 |
23 |
24 | # Delete .venv (Virtualenv, Poetry and Conda) Directories
25 |
26 | `killpy` is a simple tool designed to locate and delete `.venv` directories from your projects, including virtual environments created by Poetry and Conda. It can help you quickly clean up unnecessary virtual environments and save disk space.
27 |
28 | ## Features
29 |
30 | - **Automatic search:** Finds all .venv directories and any folders containing a pyvenv.cfg file recursively from the current working directory, as they are considered virtual environment folders.
31 | - **Support for Conda**: Lists all available Conda environments.
32 | - **Safe deletion:** Lists the directories to be deleted and asks for confirmation.
33 | - **Fast and lightweight:** Minimal dependencies for quick execution.
34 |
35 | ## Installation
36 |
37 | To install `killpy`, use pip:
38 |
39 | ```bash
40 | pip install killpy
41 | ```
42 |
43 | ## Usage
44 |
45 | Run the following command to search for .venv directories and any folders containing a pyvenv.cfg file, as well as to list all Conda environments from the current directory and all its subdirectories recursively:
46 |
47 | ```bash
48 | killpy
49 | ```
50 |
51 | With `pipx`
52 |
53 | ```bash
54 | pipx run killpy
55 | ```
56 |
57 | With `uvx`
58 |
59 | ```bash
60 | uvx killpy
61 | ```
62 |
63 | - To **close the application**, press `Ctrl+Q`.
64 | - To **mark a virtual environment for deletion**, press `D`.
65 | - To **confirm deletion of marked virtual environments**, press `Ctrl+D`.
66 | - To **delete a virtual environment immediately**, press `Shift+Delete`.
67 | - To **clean up __pycache__ folders**, press `P`.
68 |
69 | ## Pre-Commit
70 |
71 | To automatically use KillPy on each commit, you can add a pre-commit hook to your project. This will clean cache directories (like `__pycache__`) and other unnecessary files before every commit.
72 |
73 | ```yml
74 | - repo: https://github.com/Tlaloc-Es/KillPy
75 | rev: 0.15.4
76 | hooks:
77 | - id: killpy
78 | pass_filenames: false
79 | ```
80 |
81 | ## Roadmap
82 |
83 | - [x] Delete `__pycache__` Files
84 | - [ ] Remove `dist` Folders and Build Artifacts
85 | - [ ] Clean Up Installed Package Cache
86 | - [ ] Delete `.egg-info` and `.dist-info` Files
87 | - [ ] Analyze and Remove Unused Dependencies
88 | - [ ] Optimize Disk Space in Python Projects
89 |
90 | ## Contributing
91 |
92 | Contributions are welcome! If you'd like to improve this tool, feel free to fork the repository and submit a pull request.
93 |
94 | 1. Fork the repository
95 | 1. Create a new branch for your feature: `git checkout -b my-feature`
96 | 1. Commit your changes: `git commit -m 'Add my feature'`
97 | 1. Push to the branch: `git push origin my-feature`
98 | 1. Submit a pull request
99 |
100 | ## License
101 |
102 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
103 |
104 | ______________________________________________________________________
105 |
106 | Thank you for using `killpy`! If you find it useful, please star the repository on GitHub!
107 |
--------------------------------------------------------------------------------
/killpy/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tlaloc-Es/killpy/a67064c84c83d505a2d5c44f706f1b6a41f93b8a/killpy/__init__.py
--------------------------------------------------------------------------------
/killpy/__main__.py:
--------------------------------------------------------------------------------
1 | import click
2 |
3 | from killpy.cli import TableApp
4 | from killpy.commands.clean import clean
5 |
6 |
7 | @click.group(invoke_without_command=True)
8 | @click.pass_context
9 | def cli(ctx):
10 | if not ctx.invoked_subcommand:
11 | app = TableApp()
12 | app.run()
13 |
14 |
15 | cli.add_command(clean)
16 |
17 |
18 | if __name__ == "__main__":
19 | cli()
20 |
--------------------------------------------------------------------------------
/killpy/cleaners/__init__.py:
--------------------------------------------------------------------------------
1 | import shutil
2 | from pathlib import Path
3 |
4 | from killpy.files import get_total_size
5 |
6 |
7 | def remove_pycache(path: Path) -> int:
8 | total_freed_space = 0
9 | for pycache_dir in path.rglob("__pycache__"):
10 | try:
11 | total_freed_space += get_total_size(pycache_dir)
12 | shutil.rmtree(pycache_dir)
13 | except Exception:
14 | continue
15 | return total_freed_space
16 |
--------------------------------------------------------------------------------
/killpy/cli.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | from enum import Enum
3 | from pathlib import Path
4 |
5 | from textual.app import App, ComposeResult
6 | from textual.binding import Binding
7 | from textual.color import Gradient
8 | from textual.coordinate import Coordinate
9 | from textual.widgets import (
10 | DataTable,
11 | Footer,
12 | Header,
13 | Label,
14 | ProgressBar,
15 | Static,
16 | TabbedContent,
17 | TabPane,
18 | )
19 |
20 | from killpy.cleaners import remove_pycache
21 | from killpy.files import format_size
22 | from killpy.killers import (
23 | CondaKiller,
24 | PipxKiller,
25 | PoetryKiller,
26 | PyenvKiller,
27 | VenvKiller,
28 | )
29 |
30 |
31 | def is_venv_tab(func):
32 | def wrapper(self, *args, **kwargs):
33 | if self.query_one(TabbedContent).active == "venv-tab":
34 | return func(self, *args, **kwargs)
35 |
36 | return wrapper
37 |
38 |
39 | def is_pipx_tab(func):
40 | def wrapper(self, *args, **kwargs):
41 | if self.query_one(TabbedContent).active == "pipx-tab":
42 | return func(self, *args, **kwargs)
43 |
44 | return wrapper
45 |
46 |
47 | def remove_duplicates(venvs):
48 | seen_paths = set()
49 | unique_venvs = []
50 |
51 | for venv in venvs:
52 | venv_path = venv[0]
53 | if venv_path not in seen_paths:
54 | unique_venvs.append(venv)
55 | seen_paths.add(venv_path)
56 |
57 | return unique_venvs
58 |
59 |
60 | class EnvStatus(Enum):
61 | DELETED = "DELETED"
62 | MARKED_TO_DELETE = "MARKED TO DELETE"
63 |
64 |
65 | class TableApp(App):
66 | deleted_cells: Coordinate = []
67 | bytes_release: int = 0
68 |
69 | killers = {
70 | "conda_killer": CondaKiller(),
71 | "pipx_killer": PipxKiller(),
72 | "poetry_killer": PoetryKiller(Path.cwd()),
73 | "venv_killer": VenvKiller(Path.cwd()),
74 | "pyenv_killer": PyenvKiller(Path.cwd()),
75 | }
76 |
77 | BINDINGS = [
78 | Binding(key="ctrl+q", action="quit", description="Exit"),
79 | Binding(
80 | key="d",
81 | action="mark_for_delete",
82 | description="Mark for deletion",
83 | show=True,
84 | ),
85 | Binding(
86 | key="ctrl+d",
87 | action="confirm_delete",
88 | description="Delete marked",
89 | show=True,
90 | ),
91 | Binding(
92 | key="shift+delete",
93 | action="delete_now",
94 | description="Delete immediately",
95 | show=True,
96 | ),
97 | Binding(
98 | key="p",
99 | action="clean_pycache",
100 | description="Clean __pycache__ dirs",
101 | show=True,
102 | ),
103 | Binding(
104 | key="u",
105 | action="uninstall_pipx",
106 | description="Uninstall pipx packages",
107 | show=True,
108 | ),
109 | ]
110 |
111 | CSS = """
112 | #banner {
113 | color: white;
114 | border: heavy green;
115 | }
116 |
117 | TabbedContent #--content-tab-venv-tab {
118 | color: green;
119 | }
120 |
121 | TabbedContent #--content-tab-pipx-tab {
122 | color: yellow;
123 | }
124 | """
125 |
126 | def compose(self) -> ComposeResult:
127 | yield Header()
128 | banner = Static(
129 | """
130 | █ ▄ ▄ █ █ ▄▄▄▄ ▄ ▄ ____
131 | █▄▀ ▄ █ █ █ █ █ █ .'`_ o `;__,
132 | █ ▀▄ █ █ █ █▄▄▄▀ ▀▀▀█ . .'.'` '---' ' A tool to delete
133 | █ █ █ █ █ █ ▄ █ .`-...-'.' .venv, Conda, Poetry environments
134 | ▀ ▀▀▀ `-...-'and clean up __pycache__ and temp files.
135 | """,
136 | id="banner",
137 | )
138 | yield banner
139 | yield Label("Searching for virtual environments...")
140 |
141 | gradient = Gradient.from_colors(
142 | "#881177",
143 | "#aa3355",
144 | "#cc6666",
145 | "#ee9944",
146 | "#eedd00",
147 | "#99dd55",
148 | "#44dd88",
149 | "#22ccbb",
150 | "#00bbcc",
151 | "#0099cc",
152 | "#3366bb",
153 | "#663399",
154 | )
155 | yield ProgressBar(total=100, gradient=gradient, show_eta=False)
156 |
157 | with TabbedContent():
158 | with TabPane("Virtual Env", id="venv-tab"):
159 | yield DataTable(id="venv-table")
160 | with TabPane("Pipx", id="pipx-tab"):
161 | yield DataTable(id="pipx-table")
162 |
163 | yield Footer(show_command_palette=False)
164 |
165 | async def on_mount(self) -> None:
166 | self.title = """killpy"""
167 | await self.find_venvs()
168 | await self.find_pipx()
169 |
170 | def list_environments_of(self, killer: str):
171 | return asyncio.to_thread(self.killers[killer].list_environments)
172 |
173 | async def find_venvs(self):
174 | venvs = await asyncio.gather(
175 | self.list_environments_of("venv_killer"),
176 | self.list_environments_of("conda_killer"),
177 | self.list_environments_of("pyenv_killer"),
178 | self.list_environments_of("poetry_killer"),
179 | )
180 | venvs = [env for sublist in venvs for env in sublist]
181 | venvs = remove_duplicates(venvs)
182 |
183 | table = self.query_one("#venv-table", DataTable)
184 | table.focus()
185 | table.add_columns(
186 | "Path", "Type", "Last Modified", "Size", "Size (Human Readable)", "Status"
187 | )
188 |
189 | for venv in venvs:
190 | table.add_row(*venv)
191 |
192 | table.cursor_type = "row"
193 | table.zebra_stripes = True
194 |
195 | self.query_one(Label).update(f"Found {len(venvs)} .venv directories")
196 |
197 | async def find_pipx(self):
198 | venvs = await asyncio.gather(self.list_environments_of("pipx_killer"))
199 |
200 | venvs = [env for sublist in venvs for env in sublist]
201 |
202 | table = self.query_one("#pipx-table", DataTable)
203 | table.focus()
204 | table.add_columns("Package", "Size", "Size (Human Readable)", "Status")
205 |
206 | for venv in venvs:
207 | table.add_row(*venv)
208 |
209 | table.cursor_type = "row"
210 | table.zebra_stripes = True
211 |
212 | self.query_one(Label).update(f"Found {len(venvs)} .venv directories")
213 |
214 | async def action_clean_pycache(self):
215 | current_directory = Path.cwd()
216 | total_freed_space = await asyncio.to_thread(remove_pycache, current_directory)
217 | self.bytes_release += total_freed_space
218 | self.query_one(Label).update(f"{format_size(self.bytes_release)} deleted")
219 | self.bell()
220 |
221 | @is_venv_tab
222 | def action_confirm_delete(self):
223 | table = self.query_one("#venv-table", DataTable)
224 | for row_index in range(table.row_count):
225 | row_data = table.get_row_at(row_index)
226 | current_status = row_data[5]
227 | if current_status == EnvStatus.MARKED_TO_DELETE.value:
228 | cursor_cell = Coordinate(row_index, 0)
229 | if cursor_cell not in self.deleted_cells:
230 | path = row_data[0]
231 | self.bytes_release += row_data[3]
232 | env_type = row_data[1]
233 | self.delete_environment(path, env_type)
234 | table.update_cell_at((row_index, 5), EnvStatus.DELETED.value)
235 | self.deleted_cells.append(cursor_cell)
236 | self.query_one(Label).update(f"{format_size(self.bytes_release)} deleted")
237 | self.bell()
238 |
239 | @is_venv_tab
240 | def action_mark_for_delete(self):
241 | table = self.query_one("#venv-table", DataTable)
242 |
243 | cursor_cell = table.cursor_coordinate
244 | if cursor_cell:
245 | row_data = table.get_row_at(cursor_cell.row)
246 | current_status = row_data[5]
247 | if current_status == EnvStatus.DELETED.value:
248 | return
249 | elif current_status == EnvStatus.MARKED_TO_DELETE.value:
250 | table.update_cell_at((cursor_cell.row, 5), "")
251 | else:
252 | table.update_cell_at(
253 | (cursor_cell.row, 5), EnvStatus.MARKED_TO_DELETE.value
254 | )
255 |
256 | @is_venv_tab
257 | def action_delete_now(self):
258 | table = self.query_one("#venv-table", DataTable)
259 | cursor_cell = table.cursor_coordinate
260 | if cursor_cell:
261 | if cursor_cell in self.deleted_cells:
262 | return
263 | row_data = table.get_row_at(cursor_cell.row)
264 | path = row_data[0]
265 | self.bytes_release += row_data[3]
266 | env_type = row_data[1]
267 | self.delete_environment(path, env_type)
268 | table.update_cell_at((cursor_cell.row, 5), EnvStatus.DELETED.value)
269 | self.query_one(Label).update(f"{format_size(self.bytes_release)} deleted")
270 | self.deleted_cells.append(cursor_cell)
271 | self.bell()
272 |
273 | @is_venv_tab
274 | def delete_environment(self, path, env_type):
275 | if env_type in {".venv", "pyvenv.cfg", "poetry"}:
276 | self.killers["venv_killer"].remove_environment(path)
277 | else:
278 | self.killers["conda_killer"].remove_environment(path)
279 |
280 | @is_pipx_tab
281 | def action_uninstall_pipx(self):
282 | table = self.query_one("#pipx-table", DataTable)
283 | cursor_cell = table.cursor_coordinate
284 | if cursor_cell:
285 | if cursor_cell in self.deleted_cells:
286 | return
287 | row_data = table.get_row_at(cursor_cell.row)
288 | package = row_data[0]
289 | size = row_data[1]
290 |
291 | self.killers["pipx_killer"].remove_environment(package)
292 |
293 | table.update_cell_at((cursor_cell.row, 3), EnvStatus.DELETED.value)
294 | self.deleted_cells.append(cursor_cell)
295 | self.bytes_release += size
296 | self.query_one(Label).update(f"{format_size(self.bytes_release)} deleted")
297 |
298 | self.bell()
299 |
--------------------------------------------------------------------------------
/killpy/commands/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tlaloc-Es/killpy/a67064c84c83d505a2d5c44f706f1b6a41f93b8a/killpy/commands/__init__.py
--------------------------------------------------------------------------------
/killpy/commands/clean.py:
--------------------------------------------------------------------------------
1 | import logging
2 | from pathlib import Path
3 |
4 | import click
5 |
6 | from killpy.cleaners import remove_pycache
7 | from killpy.files import format_size
8 |
9 | logging.basicConfig(level=logging.INFO)
10 |
11 |
12 | @click.command()
13 | @click.option("--path", default=Path.cwd(), help="Path to the directory to clean")
14 | def clean(path):
15 | path = Path(path)
16 | logging.info(f"Executing the clean command in {path}")
17 | total_freed_space = remove_pycache(path)
18 | logging.info(f"{format_size(total_freed_space)} deleted")
19 |
--------------------------------------------------------------------------------
/killpy/files/__init__.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 |
3 |
4 | def get_total_size(path: Path) -> int:
5 | total_size = 0
6 | for f in path.rglob("*"):
7 | try:
8 | if f.is_file():
9 | total_size += f.stat().st_size
10 | except FileNotFoundError:
11 | continue
12 | return total_size
13 |
14 |
15 | def format_size(size_in_bytes: int):
16 | if size_in_bytes >= 1 << 30:
17 | return f"{size_in_bytes / (1 << 30):.2f} GB"
18 | elif size_in_bytes >= 1 << 20:
19 | return f"{size_in_bytes / (1 << 20):.2f} MB"
20 | elif size_in_bytes >= 1 << 10:
21 | return f"{size_in_bytes / (1 << 10):.2f} KB"
22 | else:
23 | return f"{size_in_bytes} bytes"
24 |
--------------------------------------------------------------------------------
/killpy/killers/__init__.py:
--------------------------------------------------------------------------------
1 | from killpy.killers.conda_killer import CondaKiller
2 | from killpy.killers.pipx_killer import PipxKiller
3 | from killpy.killers.poetry_killer import PoetryKiller
4 | from killpy.killers.pyenv_killer import PyenvKiller
5 | from killpy.killers.venv_killer import VenvKiller
6 |
7 | __all__ = ["CondaKiller", "PipxKiller", "PoetryKiller", "PyenvKiller", "VenvKiller"]
8 |
--------------------------------------------------------------------------------
/killpy/killers/conda_killer.py:
--------------------------------------------------------------------------------
1 | import subprocess
2 | from datetime import datetime
3 | from pathlib import Path
4 |
5 | from killpy.files import format_size, get_total_size
6 | from killpy.killers.killer import BaseKiller
7 |
8 |
9 | class CondaKiller(BaseKiller):
10 | def list_environments(self):
11 | try:
12 | result = subprocess.run(
13 | ["conda", "env", "list"],
14 | capture_output=True,
15 | text=True,
16 | check=True,
17 | )
18 |
19 | venvs = []
20 | for line in result.stdout.splitlines():
21 | if line.strip() and not line.startswith("#"):
22 | env_info = line.strip().split()
23 | env_name = env_info[0]
24 |
25 | if "*" in env_info:
26 | continue
27 |
28 | dir_path = Path(env_info[1])
29 | last_modified_timestamp = dir_path.stat().st_mtime
30 | last_modified = datetime.fromtimestamp(
31 | last_modified_timestamp
32 | ).strftime("%d/%m/%Y")
33 |
34 | size = get_total_size(dir_path)
35 | size_to_show = format_size(size)
36 | venvs.append((env_name, "Conda", last_modified, size, size_to_show))
37 |
38 | venvs.sort(key=lambda x: x[3], reverse=True)
39 | return venvs
40 |
41 | except subprocess.CalledProcessError:
42 | return []
43 | except Exception:
44 | return []
45 |
46 | def remove_environment(self, env_to_delete):
47 | try:
48 | subprocess.run(
49 | ["conda", "env", "remove", "-n", env_to_delete],
50 | check=True,
51 | )
52 | except subprocess.CalledProcessError as e:
53 | print(f"Error: {e}")
54 |
--------------------------------------------------------------------------------
/killpy/killers/killer.py:
--------------------------------------------------------------------------------
1 | from abc import ABC, abstractmethod
2 |
3 |
4 | class BaseKiller(ABC):
5 | @abstractmethod
6 | def list_environments(self) -> list:
7 | raise NotImplementedError("Subclasses must implement this method")
8 |
9 | @abstractmethod
10 | def remove_environment(self, env_to_delete: str):
11 | raise NotImplementedError("Subclasses must implement this method")
12 |
--------------------------------------------------------------------------------
/killpy/killers/pipx_killer.py:
--------------------------------------------------------------------------------
1 | import json
2 | import logging
3 | import subprocess
4 | from pathlib import Path
5 |
6 | from killpy.files import format_size, get_total_size
7 | from killpy.killers.killer import BaseKiller
8 |
9 |
10 | class PipxKiller(BaseKiller):
11 | def list_environments(self):
12 | try:
13 | result = subprocess.run(
14 | ["pipx", "list", "--json"],
15 | capture_output=True,
16 | text=True,
17 | check=True,
18 | )
19 |
20 | installed_packages = json.loads(result.stdout)
21 |
22 | packages_with_size = []
23 | for package_name, package_data in installed_packages.get(
24 | "venvs", {}
25 | ).items():
26 | bin_path = (
27 | package_data.get("metadata", {})
28 | .get("main_package", {})
29 | .get("app_paths", [])[0]
30 | .get("__Path__", "")
31 | )
32 | package_path = Path(bin_path).parent
33 | if package_path.exists():
34 | total_size = get_total_size(package_path)
35 | formatted_size = format_size(total_size)
36 | packages_with_size.append(
37 | (package_name, total_size, formatted_size)
38 | )
39 |
40 | return packages_with_size
41 |
42 | except subprocess.CalledProcessError as e:
43 | logging.error("Error: %s", e)
44 | return []
45 | except Exception as e:
46 | logging.error("An error occurred: %s", e)
47 | return []
48 | except subprocess.CalledProcessError as e:
49 | logging.error("Error: %s", e)
50 | return []
51 | except Exception as e:
52 | logging.error("An error occurred: %s", e)
53 | return []
54 |
55 | def remove_environment(self, env_to_delete):
56 | try:
57 | subprocess.run(
58 | ["pipx", "uninstall", env_to_delete],
59 | check=True,
60 | )
61 | except subprocess.CalledProcessError as e:
62 | logging.error("Error: %s", e)
63 |
--------------------------------------------------------------------------------
/killpy/killers/poetry_killer.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import platform
3 | import subprocess
4 | from datetime import datetime
5 | from pathlib import Path
6 |
7 | from killpy.files import format_size, get_total_size
8 | from killpy.killers.venv_killer import VenvKiller
9 |
10 |
11 | class PoetryKiller(VenvKiller):
12 | def __init__(self, root_dir):
13 | super().__init__(root_dir)
14 |
15 | def list_environments(self):
16 | try:
17 | if platform.system() == "Windows":
18 | poetry_venvs_dir = (
19 | Path.home() / "AppData" / "Local" / "pypoetry" / "virtualenvs"
20 | )
21 | else:
22 | poetry_venvs_dir = Path.home() / ".cache" / "pypoetry" / "virtualenvs"
23 |
24 | if not poetry_venvs_dir.exists():
25 | logging.info(
26 | "No Poetry virtual environments directory found at %s",
27 | poetry_venvs_dir,
28 | )
29 | return []
30 |
31 | venvs = []
32 | for venv_path in poetry_venvs_dir.iterdir():
33 | if venv_path.is_dir():
34 | last_modified_timestamp = venv_path.stat().st_mtime
35 | last_modified = datetime.fromtimestamp(
36 | last_modified_timestamp
37 | ).strftime("%d/%m/%Y")
38 | size = get_total_size(venv_path)
39 | size_to_show = format_size(size)
40 | venvs.append(
41 | (venv_path, "poetry", last_modified, size, size_to_show)
42 | )
43 |
44 | venvs.sort(key=lambda x: x[3], reverse=True)
45 | return venvs
46 |
47 | except Exception as e:
48 | logging.error("An error occurred: %s", e)
49 | return []
50 | except FileNotFoundError as e:
51 | logging.error("An error occurred: %s", e)
52 | return []
53 | except subprocess.CalledProcessError as e:
54 | logging.error("Error while executing 'ls' command: %s", e)
55 | return []
56 |
--------------------------------------------------------------------------------
/killpy/killers/pyenv_killer.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 |
3 | from killpy.files import format_size, get_total_size
4 | from killpy.killers.venv_killer import VenvKiller
5 |
6 |
7 | class PyenvKiller(VenvKiller):
8 | def __init__(self, root_dir):
9 | super().__init__(root_dir)
10 |
11 | def list_environments(self):
12 | venvs = []
13 | for dir_path in self.root_dir.rglob("pyvenv.cfg"):
14 | venv_dir = dir_path.parent
15 | last_modified_timestamp = dir_path.stat().st_mtime
16 | last_modified = datetime.fromtimestamp(last_modified_timestamp).strftime(
17 | "%d/%m/%Y"
18 | )
19 | size = get_total_size(venv_dir)
20 | size_to_show = format_size(size)
21 | venvs.append((venv_dir, "pyvenv.cfg", last_modified, size, size_to_show))
22 |
23 | venvs.sort(key=lambda x: x[3], reverse=True)
24 | return venvs
25 |
--------------------------------------------------------------------------------
/killpy/killers/venv_killer.py:
--------------------------------------------------------------------------------
1 | import shutil
2 | from datetime import datetime
3 |
4 | from killpy.files import format_size, get_total_size
5 | from killpy.killers.killer import BaseKiller
6 |
7 |
8 | class VenvKiller(BaseKiller):
9 | def __init__(self, root_dir):
10 | self.root_dir = root_dir
11 |
12 | def list_environments(self):
13 | venvs = []
14 | for dir_path in self.root_dir.rglob(".venv"):
15 | try:
16 | dir_path.resolve(strict=True)
17 | last_modified_timestamp = dir_path.stat().st_mtime
18 | last_modified = datetime.fromtimestamp(
19 | last_modified_timestamp
20 | ).strftime("%d/%m/%Y")
21 | size = get_total_size(dir_path)
22 | size_to_show = format_size(size)
23 | venvs.append((dir_path, ".venv", last_modified, size, size_to_show))
24 | except FileNotFoundError:
25 | continue
26 | venvs.sort(key=lambda x: x[3], reverse=True)
27 | return venvs
28 |
29 | def remove_environment(self, env_to_delete):
30 | try:
31 | shutil.rmtree(env_to_delete)
32 | except FileNotFoundError:
33 | pass
34 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | name = "killpy"
3 | version = "0.15.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 | readme = "README.md"
6 | classifiers = [
7 | "Environment :: Console",
8 | "License :: OSI Approved :: MIT License",
9 | "Programming Language :: Python :: 3.12"
10 | ]
11 | dependencies = [
12 | "click>=8.1.8",
13 | "rich>=13.9.4",
14 | "textual>=1.0.0",
15 | ]
16 | requires-python = ">=3.12"
17 |
18 | [tool.setuptools]
19 | license-files = []
20 |
21 | [dependency-groups]
22 | dev = [
23 | "commitizen>=4.1.0",
24 | "coverage>=7.6.10",
25 | "mypy>=1.14.0",
26 | "pre-commit>=4.0.1",
27 | "pytest>=8.3.4",
28 | "pytest-cov>=6.0.0",
29 | "ruff>=0.8.4",
30 | ]
31 |
32 |
33 | [tool.uv.workspace]
34 | members = ["q"]
35 |
36 | [tool.uv]
37 | package = true
38 |
39 | [tool.commitizen]
40 | name = "cz_conventional_commits"
41 | version = "0.15.4"
42 | version_files = [
43 | "src/__version__.py",
44 | "pyproject.toml:version"
45 | ]
46 |
47 | [project.scripts]
48 | killpy = "killpy.__main__:cli"
49 |
--------------------------------------------------------------------------------
/show.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Tlaloc-Es/killpy/a67064c84c83d505a2d5c44f706f1b6a41f93b8a/show.gif
--------------------------------------------------------------------------------
/uv.lock:
--------------------------------------------------------------------------------
1 | version = 1
2 | requires-python = ">=3.12"
3 |
4 | [[package]]
5 | name = "argcomplete"
6 | version = "3.5.3"
7 | source = { registry = "https://pypi.org/simple" }
8 | sdist = { url = "https://files.pythonhosted.org/packages/0c/be/6c23d80cb966fb8f83fb1ebfb988351ae6b0554d0c3a613ee4531c026597/argcomplete-3.5.3.tar.gz", hash = "sha256:c12bf50eded8aebb298c7b7da7a5ff3ee24dffd9f5281867dfe1424b58c55392", size = 72999 }
9 | wheels = [
10 | { url = "https://files.pythonhosted.org/packages/c4/08/2a4db06ec3d203124c967fc89295e85a202e5cbbcdc08fd6a64b65217d1e/argcomplete-3.5.3-py3-none-any.whl", hash = "sha256:2ab2c4a215c59fd6caaff41a869480a23e8f6a5f910b266c1808037f4e375b61", size = 43569 },
11 | ]
12 |
13 | [[package]]
14 | name = "cfgv"
15 | version = "3.4.0"
16 | source = { registry = "https://pypi.org/simple" }
17 | sdist = { url = "https://files.pythonhosted.org/packages/11/74/539e56497d9bd1d484fd863dd69cbbfa653cd2aa27abfe35653494d85e94/cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560", size = 7114 }
18 | wheels = [
19 | { url = "https://files.pythonhosted.org/packages/c5/55/51844dd50c4fc7a33b653bfaba4c2456f06955289ca770a5dbd5fd267374/cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9", size = 7249 },
20 | ]
21 |
22 | [[package]]
23 | name = "charset-normalizer"
24 | version = "3.4.1"
25 | source = { registry = "https://pypi.org/simple" }
26 | sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 }
27 | wheels = [
28 | { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105 },
29 | { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404 },
30 | { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423 },
31 | { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184 },
32 | { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268 },
33 | { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601 },
34 | { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098 },
35 | { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520 },
36 | { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852 },
37 | { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488 },
38 | { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192 },
39 | { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550 },
40 | { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785 },
41 | { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698 },
42 | { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162 },
43 | { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263 },
44 | { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966 },
45 | { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992 },
46 | { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162 },
47 | { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972 },
48 | { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095 },
49 | { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668 },
50 | { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073 },
51 | { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 },
52 | { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 },
53 | { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 },
54 | { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 },
55 | ]
56 |
57 | [[package]]
58 | name = "click"
59 | version = "8.1.8"
60 | source = { registry = "https://pypi.org/simple" }
61 | dependencies = [
62 | { name = "colorama", marker = "sys_platform == 'win32'" },
63 | ]
64 | sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 }
65 | wheels = [
66 | { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 },
67 | ]
68 |
69 | [[package]]
70 | name = "colorama"
71 | version = "0.4.6"
72 | source = { registry = "https://pypi.org/simple" }
73 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
74 | wheels = [
75 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
76 | ]
77 |
78 | [[package]]
79 | name = "commitizen"
80 | version = "4.1.0"
81 | source = { registry = "https://pypi.org/simple" }
82 | dependencies = [
83 | { name = "argcomplete" },
84 | { name = "charset-normalizer" },
85 | { name = "colorama" },
86 | { name = "decli" },
87 | { name = "jinja2" },
88 | { name = "packaging" },
89 | { name = "pyyaml" },
90 | { name = "questionary" },
91 | { name = "termcolor" },
92 | { name = "tomlkit" },
93 | ]
94 | sdist = { url = "https://files.pythonhosted.org/packages/7a/c5/66f1b977b48501a33f5fd33253aba14786483b08aba987718d272e99e732/commitizen-4.1.0.tar.gz", hash = "sha256:4f2d9400ec411aec1c738d4c63fc7fd5807cd6ddf6be970869e03e68b88ff718", size = 51252 }
95 | wheels = [
96 | { url = "https://files.pythonhosted.org/packages/48/f7/7f70adfbf3553ffdbe391eaacde72b21dbc1b4226ae56ca32e8ded1bf70b/commitizen-4.1.0-py3-none-any.whl", hash = "sha256:2e6c5fbd442cab4bcc5a04bc86ef2196ef84bcf611317d6c596e87f5bb4c09f5", size = 72282 },
97 | ]
98 |
99 | [[package]]
100 | name = "coverage"
101 | version = "7.6.10"
102 | source = { registry = "https://pypi.org/simple" }
103 | sdist = { url = "https://files.pythonhosted.org/packages/84/ba/ac14d281f80aab516275012e8875991bb06203957aa1e19950139238d658/coverage-7.6.10.tar.gz", hash = "sha256:7fb105327c8f8f0682e29843e2ff96af9dcbe5bab8eeb4b398c6a33a16d80a23", size = 803868 }
104 | wheels = [
105 | { url = "https://files.pythonhosted.org/packages/86/77/19d09ea06f92fdf0487499283b1b7af06bc422ea94534c8fe3a4cd023641/coverage-7.6.10-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:27c6e64726b307782fa5cbe531e7647aee385a29b2107cd87ba7c0105a5d3853", size = 208281 },
106 | { url = "https://files.pythonhosted.org/packages/b6/67/5479b9f2f99fcfb49c0d5cf61912a5255ef80b6e80a3cddba39c38146cf4/coverage-7.6.10-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c56e097019e72c373bae32d946ecf9858fda841e48d82df7e81c63ac25554078", size = 208514 },
107 | { url = "https://files.pythonhosted.org/packages/15/d1/febf59030ce1c83b7331c3546d7317e5120c5966471727aa7ac157729c4b/coverage-7.6.10-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7827a5bc7bdb197b9e066cdf650b2887597ad124dd99777332776f7b7c7d0d0", size = 241537 },
108 | { url = "https://files.pythonhosted.org/packages/4b/7e/5ac4c90192130e7cf8b63153fe620c8bfd9068f89a6d9b5f26f1550f7a26/coverage-7.6.10-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:204a8238afe787323a8b47d8be4df89772d5c1e4651b9ffa808552bdf20e1d50", size = 238572 },
109 | { url = "https://files.pythonhosted.org/packages/dc/03/0334a79b26ecf59958f2fe9dd1f5ab3e2f88db876f5071933de39af09647/coverage-7.6.10-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67926f51821b8e9deb6426ff3164870976fe414d033ad90ea75e7ed0c2e5022", size = 240639 },
110 | { url = "https://files.pythonhosted.org/packages/d7/45/8a707f23c202208d7b286d78ad6233f50dcf929319b664b6cc18a03c1aae/coverage-7.6.10-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e78b270eadb5702938c3dbe9367f878249b5ef9a2fcc5360ac7bff694310d17b", size = 240072 },
111 | { url = "https://files.pythonhosted.org/packages/66/02/603ce0ac2d02bc7b393279ef618940b4a0535b0868ee791140bda9ecfa40/coverage-7.6.10-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:714f942b9c15c3a7a5fe6876ce30af831c2ad4ce902410b7466b662358c852c0", size = 238386 },
112 | { url = "https://files.pythonhosted.org/packages/04/62/4e6887e9be060f5d18f1dd58c2838b2d9646faf353232dec4e2d4b1c8644/coverage-7.6.10-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:abb02e2f5a3187b2ac4cd46b8ced85a0858230b577ccb2c62c81482ca7d18852", size = 240054 },
113 | { url = "https://files.pythonhosted.org/packages/5c/74/83ae4151c170d8bd071924f212add22a0e62a7fe2b149edf016aeecad17c/coverage-7.6.10-cp312-cp312-win32.whl", hash = "sha256:55b201b97286cf61f5e76063f9e2a1d8d2972fc2fcfd2c1272530172fd28c359", size = 210904 },
114 | { url = "https://files.pythonhosted.org/packages/c3/54/de0893186a221478f5880283119fc40483bc460b27c4c71d1b8bba3474b9/coverage-7.6.10-cp312-cp312-win_amd64.whl", hash = "sha256:e4ae5ac5e0d1e4edfc9b4b57b4cbecd5bc266a6915c500f358817a8496739247", size = 211692 },
115 | { url = "https://files.pythonhosted.org/packages/25/6d/31883d78865529257bf847df5789e2ae80e99de8a460c3453dbfbe0db069/coverage-7.6.10-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:05fca8ba6a87aabdd2d30d0b6c838b50510b56cdcfc604d40760dae7153b73d9", size = 208308 },
116 | { url = "https://files.pythonhosted.org/packages/70/22/3f2b129cc08de00c83b0ad6252e034320946abfc3e4235c009e57cfeee05/coverage-7.6.10-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9e80eba8801c386f72e0712a0453431259c45c3249f0009aff537a517b52942b", size = 208565 },
117 | { url = "https://files.pythonhosted.org/packages/97/0a/d89bc2d1cc61d3a8dfe9e9d75217b2be85f6c73ebf1b9e3c2f4e797f4531/coverage-7.6.10-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a372c89c939d57abe09e08c0578c1d212e7a678135d53aa16eec4430adc5e690", size = 241083 },
118 | { url = "https://files.pythonhosted.org/packages/4c/81/6d64b88a00c7a7aaed3a657b8eaa0931f37a6395fcef61e53ff742b49c97/coverage-7.6.10-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ec22b5e7fe7a0fa8509181c4aac1db48f3dd4d3a566131b313d1efc102892c18", size = 238235 },
119 | { url = "https://files.pythonhosted.org/packages/9a/0b/7797d4193f5adb4b837207ed87fecf5fc38f7cc612b369a8e8e12d9fa114/coverage-7.6.10-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26bcf5c4df41cad1b19c84af71c22cbc9ea9a547fc973f1f2cc9a290002c8b3c", size = 240220 },
120 | { url = "https://files.pythonhosted.org/packages/65/4d/6f83ca1bddcf8e51bf8ff71572f39a1c73c34cf50e752a952c34f24d0a60/coverage-7.6.10-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e4630c26b6084c9b3cb53b15bd488f30ceb50b73c35c5ad7871b869cb7365fd", size = 239847 },
121 | { url = "https://files.pythonhosted.org/packages/30/9d/2470df6aa146aff4c65fee0f87f58d2164a67533c771c9cc12ffcdb865d5/coverage-7.6.10-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2396e8116db77789f819d2bc8a7e200232b7a282c66e0ae2d2cd84581a89757e", size = 237922 },
122 | { url = "https://files.pythonhosted.org/packages/08/dd/723fef5d901e6a89f2507094db66c091449c8ba03272861eaefa773ad95c/coverage-7.6.10-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:79109c70cc0882e4d2d002fe69a24aa504dec0cc17169b3c7f41a1d341a73694", size = 239783 },
123 | { url = "https://files.pythonhosted.org/packages/3d/f7/64d3298b2baf261cb35466000628706ce20a82d42faf9b771af447cd2b76/coverage-7.6.10-cp313-cp313-win32.whl", hash = "sha256:9e1747bab246d6ff2c4f28b4d186b205adced9f7bd9dc362051cc37c4a0c7bd6", size = 210965 },
124 | { url = "https://files.pythonhosted.org/packages/d5/58/ec43499a7fc681212fe7742fe90b2bc361cdb72e3181ace1604247a5b24d/coverage-7.6.10-cp313-cp313-win_amd64.whl", hash = "sha256:254f1a3b1eef5f7ed23ef265eaa89c65c8c5b6b257327c149db1ca9d4a35f25e", size = 211719 },
125 | { url = "https://files.pythonhosted.org/packages/ab/c9/f2857a135bcff4330c1e90e7d03446b036b2363d4ad37eb5e3a47bbac8a6/coverage-7.6.10-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2ccf240eb719789cedbb9fd1338055de2761088202a9a0b73032857e53f612fe", size = 209050 },
126 | { url = "https://files.pythonhosted.org/packages/aa/b3/f840e5bd777d8433caa9e4a1eb20503495709f697341ac1a8ee6a3c906ad/coverage-7.6.10-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0c807ca74d5a5e64427c8805de15b9ca140bba13572d6d74e262f46f50b13273", size = 209321 },
127 | { url = "https://files.pythonhosted.org/packages/85/7d/125a5362180fcc1c03d91850fc020f3831d5cda09319522bcfa6b2b70be7/coverage-7.6.10-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bcfa46d7709b5a7ffe089075799b902020b62e7ee56ebaed2f4bdac04c508d8", size = 252039 },
128 | { url = "https://files.pythonhosted.org/packages/a9/9c/4358bf3c74baf1f9bddd2baf3756b54c07f2cfd2535f0a47f1e7757e54b3/coverage-7.6.10-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e0de1e902669dccbf80b0415fb6b43d27edca2fbd48c74da378923b05316098", size = 247758 },
129 | { url = "https://files.pythonhosted.org/packages/cf/c7/de3eb6fc5263b26fab5cda3de7a0f80e317597a4bad4781859f72885f300/coverage-7.6.10-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f7b444c42bbc533aaae6b5a2166fd1a797cdb5eb58ee51a92bee1eb94a1e1cb", size = 250119 },
130 | { url = "https://files.pythonhosted.org/packages/3e/e6/43de91f8ba2ec9140c6a4af1102141712949903dc732cf739167cfa7a3bc/coverage-7.6.10-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b330368cb99ef72fcd2dc3ed260adf67b31499584dc8a20225e85bfe6f6cfed0", size = 249597 },
131 | { url = "https://files.pythonhosted.org/packages/08/40/61158b5499aa2adf9e37bc6d0117e8f6788625b283d51e7e0c53cf340530/coverage-7.6.10-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:9a7cfb50515f87f7ed30bc882f68812fd98bc2852957df69f3003d22a2aa0abf", size = 247473 },
132 | { url = "https://files.pythonhosted.org/packages/50/69/b3f2416725621e9f112e74e8470793d5b5995f146f596f133678a633b77e/coverage-7.6.10-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f93531882a5f68c28090f901b1d135de61b56331bba82028489bc51bdd818d2", size = 248737 },
133 | { url = "https://files.pythonhosted.org/packages/3c/6e/fe899fb937657db6df31cc3e61c6968cb56d36d7326361847440a430152e/coverage-7.6.10-cp313-cp313t-win32.whl", hash = "sha256:89d76815a26197c858f53c7f6a656686ec392b25991f9e409bcef020cd532312", size = 211611 },
134 | { url = "https://files.pythonhosted.org/packages/1c/55/52f5e66142a9d7bc93a15192eba7a78513d2abf6b3558d77b4ca32f5f424/coverage-7.6.10-cp313-cp313t-win_amd64.whl", hash = "sha256:54a5f0f43950a36312155dae55c505a76cd7f2b12d26abeebbe7a0b36dbc868d", size = 212781 },
135 | ]
136 |
137 | [[package]]
138 | name = "decli"
139 | version = "0.6.2"
140 | source = { registry = "https://pypi.org/simple" }
141 | sdist = { url = "https://files.pythonhosted.org/packages/3d/a0/a4658f93ecb589f479037b164dc13c68d108b50bf6594e54c820749f97ac/decli-0.6.2.tar.gz", hash = "sha256:36f71eb55fd0093895efb4f416ec32b7f6e00147dda448e3365cf73ceab42d6f", size = 7424 }
142 | wheels = [
143 | { url = "https://files.pythonhosted.org/packages/bf/70/3ea48dc9e958d7d66c44c9944809181f1ca79aaef25703c023b5092d34ff/decli-0.6.2-py3-none-any.whl", hash = "sha256:2fc84106ce9a8f523ed501ca543bdb7e416c064917c12a59ebdc7f311a97b7ed", size = 7854 },
144 | ]
145 |
146 | [[package]]
147 | name = "distlib"
148 | version = "0.3.9"
149 | source = { registry = "https://pypi.org/simple" }
150 | sdist = { url = "https://files.pythonhosted.org/packages/0d/dd/1bec4c5ddb504ca60fc29472f3d27e8d4da1257a854e1d96742f15c1d02d/distlib-0.3.9.tar.gz", hash = "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403", size = 613923 }
151 | wheels = [
152 | { url = "https://files.pythonhosted.org/packages/91/a1/cf2472db20f7ce4a6be1253a81cfdf85ad9c7885ffbed7047fb72c24cf87/distlib-0.3.9-py2.py3-none-any.whl", hash = "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", size = 468973 },
153 | ]
154 |
155 | [[package]]
156 | name = "filelock"
157 | version = "3.16.1"
158 | source = { registry = "https://pypi.org/simple" }
159 | sdist = { url = "https://files.pythonhosted.org/packages/9d/db/3ef5bb276dae18d6ec2124224403d1d67bccdbefc17af4cc8f553e341ab1/filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435", size = 18037 }
160 | wheels = [
161 | { url = "https://files.pythonhosted.org/packages/b9/f8/feced7779d755758a52d1f6635d990b8d98dc0a29fa568bbe0625f18fdf3/filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0", size = 16163 },
162 | ]
163 |
164 | [[package]]
165 | name = "identify"
166 | version = "2.6.4"
167 | source = { registry = "https://pypi.org/simple" }
168 | sdist = { url = "https://files.pythonhosted.org/packages/49/a5/7de3053524ee006b91099968d7ecb2e0b420f7ae728094394c33e8a2a2b9/identify-2.6.4.tar.gz", hash = "sha256:285a7d27e397652e8cafe537a6cc97dd470a970f48fb2e9d979aa38eae5513ac", size = 99209 }
169 | wheels = [
170 | { url = "https://files.pythonhosted.org/packages/a2/9d/52f036403ae86474804f699c0d084b4b071e333a390b20269bb8accc65e0/identify-2.6.4-py2.py3-none-any.whl", hash = "sha256:993b0f01b97e0568c179bb9196391ff391bfb88a99099dbf5ce392b68f42d0af", size = 99072 },
171 | ]
172 |
173 | [[package]]
174 | name = "iniconfig"
175 | version = "2.0.0"
176 | source = { registry = "https://pypi.org/simple" }
177 | sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 }
178 | wheels = [
179 | { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 },
180 | ]
181 |
182 | [[package]]
183 | name = "jinja2"
184 | version = "3.1.5"
185 | source = { registry = "https://pypi.org/simple" }
186 | dependencies = [
187 | { name = "markupsafe" },
188 | ]
189 | sdist = { url = "https://files.pythonhosted.org/packages/af/92/b3130cbbf5591acf9ade8708c365f3238046ac7cb8ccba6e81abccb0ccff/jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb", size = 244674 }
190 | wheels = [
191 | { url = "https://files.pythonhosted.org/packages/bd/0f/2ba5fbcd631e3e88689309dbe978c5769e883e4b84ebfe7da30b43275c5a/jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb", size = 134596 },
192 | ]
193 |
194 | [[package]]
195 | name = "killpy"
196 | version = "0.14.1"
197 | source = { editable = "." }
198 | dependencies = [
199 | { name = "click" },
200 | { name = "rich" },
201 | { name = "textual" },
202 | ]
203 |
204 | [package.dev-dependencies]
205 | dev = [
206 | { name = "commitizen" },
207 | { name = "coverage" },
208 | { name = "mypy" },
209 | { name = "pre-commit" },
210 | { name = "pytest" },
211 | { name = "pytest-cov" },
212 | { name = "ruff" },
213 | ]
214 |
215 | [package.metadata]
216 | requires-dist = [
217 | { name = "click", specifier = ">=8.1.8" },
218 | { name = "rich", specifier = ">=13.9.4" },
219 | { name = "textual", specifier = ">=1.0.0" },
220 | ]
221 |
222 | [package.metadata.requires-dev]
223 | dev = [
224 | { name = "commitizen", specifier = ">=4.1.0" },
225 | { name = "coverage", specifier = ">=7.6.10" },
226 | { name = "mypy", specifier = ">=1.14.0" },
227 | { name = "pre-commit", specifier = ">=4.0.1" },
228 | { name = "pytest", specifier = ">=8.3.4" },
229 | { name = "pytest-cov", specifier = ">=6.0.0" },
230 | { name = "ruff", specifier = ">=0.8.4" },
231 | ]
232 |
233 | [[package]]
234 | name = "linkify-it-py"
235 | version = "2.0.3"
236 | source = { registry = "https://pypi.org/simple" }
237 | dependencies = [
238 | { name = "uc-micro-py" },
239 | ]
240 | sdist = { url = "https://files.pythonhosted.org/packages/2a/ae/bb56c6828e4797ba5a4821eec7c43b8bf40f69cda4d4f5f8c8a2810ec96a/linkify-it-py-2.0.3.tar.gz", hash = "sha256:68cda27e162e9215c17d786649d1da0021a451bdc436ef9e0fa0ba5234b9b048", size = 27946 }
241 | wheels = [
242 | { url = "https://files.pythonhosted.org/packages/04/1e/b832de447dee8b582cac175871d2f6c3d5077cc56d5575cadba1fd1cccfa/linkify_it_py-2.0.3-py3-none-any.whl", hash = "sha256:6bcbc417b0ac14323382aef5c5192c0075bf8a9d6b41820a2b66371eac6b6d79", size = 19820 },
243 | ]
244 |
245 | [[package]]
246 | name = "markdown-it-py"
247 | version = "3.0.0"
248 | source = { registry = "https://pypi.org/simple" }
249 | dependencies = [
250 | { name = "mdurl" },
251 | ]
252 | sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 }
253 | wheels = [
254 | { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
255 | ]
256 |
257 | [package.optional-dependencies]
258 | linkify = [
259 | { name = "linkify-it-py" },
260 | ]
261 | plugins = [
262 | { name = "mdit-py-plugins" },
263 | ]
264 |
265 | [[package]]
266 | name = "markupsafe"
267 | version = "3.0.2"
268 | source = { registry = "https://pypi.org/simple" }
269 | sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 }
270 | wheels = [
271 | { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 },
272 | { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 },
273 | { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 },
274 | { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 },
275 | { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 },
276 | { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 },
277 | { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 },
278 | { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 },
279 | { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 },
280 | { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 },
281 | { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 },
282 | { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 },
283 | { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 },
284 | { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 },
285 | { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 },
286 | { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 },
287 | { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 },
288 | { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 },
289 | { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 },
290 | { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 },
291 | { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 },
292 | { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 },
293 | { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 },
294 | { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 },
295 | { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 },
296 | { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 },
297 | { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 },
298 | { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 },
299 | { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 },
300 | { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 },
301 | ]
302 |
303 | [[package]]
304 | name = "mdit-py-plugins"
305 | version = "0.4.2"
306 | source = { registry = "https://pypi.org/simple" }
307 | dependencies = [
308 | { name = "markdown-it-py" },
309 | ]
310 | sdist = { url = "https://files.pythonhosted.org/packages/19/03/a2ecab526543b152300717cf232bb4bb8605b6edb946c845016fa9c9c9fd/mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5", size = 43542 }
311 | wheels = [
312 | { url = "https://files.pythonhosted.org/packages/a7/f7/7782a043553ee469c1ff49cfa1cdace2d6bf99a1f333cf38676b3ddf30da/mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636", size = 55316 },
313 | ]
314 |
315 | [[package]]
316 | name = "mdurl"
317 | version = "0.1.2"
318 | source = { registry = "https://pypi.org/simple" }
319 | sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 }
320 | wheels = [
321 | { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
322 | ]
323 |
324 | [[package]]
325 | name = "mypy"
326 | version = "1.14.1"
327 | source = { registry = "https://pypi.org/simple" }
328 | dependencies = [
329 | { name = "mypy-extensions" },
330 | { name = "typing-extensions" },
331 | ]
332 | sdist = { url = "https://files.pythonhosted.org/packages/b9/eb/2c92d8ea1e684440f54fa49ac5d9a5f19967b7b472a281f419e69a8d228e/mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6", size = 3216051 }
333 | wheels = [
334 | { url = "https://files.pythonhosted.org/packages/43/1b/b38c079609bb4627905b74fc6a49849835acf68547ac33d8ceb707de5f52/mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14", size = 11266668 },
335 | { url = "https://files.pythonhosted.org/packages/6b/75/2ed0d2964c1ffc9971c729f7a544e9cd34b2cdabbe2d11afd148d7838aa2/mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9", size = 10254060 },
336 | { url = "https://files.pythonhosted.org/packages/a1/5f/7b8051552d4da3c51bbe8fcafffd76a6823779101a2b198d80886cd8f08e/mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11", size = 11933167 },
337 | { url = "https://files.pythonhosted.org/packages/04/90/f53971d3ac39d8b68bbaab9a4c6c58c8caa4d5fd3d587d16f5927eeeabe1/mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e", size = 12864341 },
338 | { url = "https://files.pythonhosted.org/packages/03/d2/8bc0aeaaf2e88c977db41583559319f1821c069e943ada2701e86d0430b7/mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89", size = 12972991 },
339 | { url = "https://files.pythonhosted.org/packages/6f/17/07815114b903b49b0f2cf7499f1c130e5aa459411596668267535fe9243c/mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b", size = 9879016 },
340 | { url = "https://files.pythonhosted.org/packages/9e/15/bb6a686901f59222275ab228453de741185f9d54fecbaacec041679496c6/mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255", size = 11252097 },
341 | { url = "https://files.pythonhosted.org/packages/f8/b3/8b0f74dfd072c802b7fa368829defdf3ee1566ba74c32a2cb2403f68024c/mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34", size = 10239728 },
342 | { url = "https://files.pythonhosted.org/packages/c5/9b/4fd95ab20c52bb5b8c03cc49169be5905d931de17edfe4d9d2986800b52e/mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a", size = 11924965 },
343 | { url = "https://files.pythonhosted.org/packages/56/9d/4a236b9c57f5d8f08ed346914b3f091a62dd7e19336b2b2a0d85485f82ff/mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9", size = 12867660 },
344 | { url = "https://files.pythonhosted.org/packages/40/88/a61a5497e2f68d9027de2bb139c7bb9abaeb1be1584649fa9d807f80a338/mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd", size = 12969198 },
345 | { url = "https://files.pythonhosted.org/packages/54/da/3d6fc5d92d324701b0c23fb413c853892bfe0e1dbe06c9138037d459756b/mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107", size = 9885276 },
346 | { url = "https://files.pythonhosted.org/packages/a0/b5/32dd67b69a16d088e533962e5044e51004176a9952419de0370cdaead0f8/mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1", size = 2752905 },
347 | ]
348 |
349 | [[package]]
350 | name = "mypy-extensions"
351 | version = "1.0.0"
352 | source = { registry = "https://pypi.org/simple" }
353 | sdist = { url = "https://files.pythonhosted.org/packages/98/a4/1ab47638b92648243faf97a5aeb6ea83059cc3624972ab6b8d2316078d3f/mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782", size = 4433 }
354 | wheels = [
355 | { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 },
356 | ]
357 |
358 | [[package]]
359 | name = "nodeenv"
360 | version = "1.9.1"
361 | source = { registry = "https://pypi.org/simple" }
362 | sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 }
363 | wheels = [
364 | { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 },
365 | ]
366 |
367 | [[package]]
368 | name = "packaging"
369 | version = "24.2"
370 | source = { registry = "https://pypi.org/simple" }
371 | sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 }
372 | wheels = [
373 | { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 },
374 | ]
375 |
376 | [[package]]
377 | name = "platformdirs"
378 | version = "4.3.6"
379 | source = { registry = "https://pypi.org/simple" }
380 | sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 }
381 | wheels = [
382 | { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 },
383 | ]
384 |
385 | [[package]]
386 | name = "pluggy"
387 | version = "1.5.0"
388 | source = { registry = "https://pypi.org/simple" }
389 | sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 }
390 | wheels = [
391 | { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 },
392 | ]
393 |
394 | [[package]]
395 | name = "pre-commit"
396 | version = "4.0.1"
397 | source = { registry = "https://pypi.org/simple" }
398 | dependencies = [
399 | { name = "cfgv" },
400 | { name = "identify" },
401 | { name = "nodeenv" },
402 | { name = "pyyaml" },
403 | { name = "virtualenv" },
404 | ]
405 | sdist = { url = "https://files.pythonhosted.org/packages/2e/c8/e22c292035f1bac8b9f5237a2622305bc0304e776080b246f3df57c4ff9f/pre_commit-4.0.1.tar.gz", hash = "sha256:80905ac375958c0444c65e9cebebd948b3cdb518f335a091a670a89d652139d2", size = 191678 }
406 | wheels = [
407 | { url = "https://files.pythonhosted.org/packages/16/8f/496e10d51edd6671ebe0432e33ff800aa86775d2d147ce7d43389324a525/pre_commit-4.0.1-py2.py3-none-any.whl", hash = "sha256:efde913840816312445dc98787724647c65473daefe420785f885e8ed9a06878", size = 218713 },
408 | ]
409 |
410 | [[package]]
411 | name = "prompt-toolkit"
412 | version = "3.0.48"
413 | source = { registry = "https://pypi.org/simple" }
414 | dependencies = [
415 | { name = "wcwidth" },
416 | ]
417 | sdist = { url = "https://files.pythonhosted.org/packages/2d/4f/feb5e137aff82f7c7f3248267b97451da3644f6cdc218edfe549fb354127/prompt_toolkit-3.0.48.tar.gz", hash = "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90", size = 424684 }
418 | wheels = [
419 | { url = "https://files.pythonhosted.org/packages/a9/6a/fd08d94654f7e67c52ca30523a178b3f8ccc4237fce4be90d39c938a831a/prompt_toolkit-3.0.48-py3-none-any.whl", hash = "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e", size = 386595 },
420 | ]
421 |
422 | [[package]]
423 | name = "pygments"
424 | version = "2.18.0"
425 | source = { registry = "https://pypi.org/simple" }
426 | sdist = { url = "https://files.pythonhosted.org/packages/8e/62/8336eff65bcbc8e4cb5d05b55faf041285951b6e80f33e2bff2024788f31/pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", size = 4891905 }
427 | wheels = [
428 | { url = "https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", size = 1205513 },
429 | ]
430 |
431 | [[package]]
432 | name = "pytest"
433 | version = "8.3.4"
434 | source = { registry = "https://pypi.org/simple" }
435 | dependencies = [
436 | { name = "colorama", marker = "sys_platform == 'win32'" },
437 | { name = "iniconfig" },
438 | { name = "packaging" },
439 | { name = "pluggy" },
440 | ]
441 | sdist = { url = "https://files.pythonhosted.org/packages/05/35/30e0d83068951d90a01852cb1cef56e5d8a09d20c7f511634cc2f7e0372a/pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761", size = 1445919 }
442 | wheels = [
443 | { url = "https://files.pythonhosted.org/packages/11/92/76a1c94d3afee238333bc0a42b82935dd8f9cf8ce9e336ff87ee14d9e1cf/pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6", size = 343083 },
444 | ]
445 |
446 | [[package]]
447 | name = "pytest-cov"
448 | version = "6.0.0"
449 | source = { registry = "https://pypi.org/simple" }
450 | dependencies = [
451 | { name = "coverage" },
452 | { name = "pytest" },
453 | ]
454 | sdist = { url = "https://files.pythonhosted.org/packages/be/45/9b538de8cef30e17c7b45ef42f538a94889ed6a16f2387a6c89e73220651/pytest-cov-6.0.0.tar.gz", hash = "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0", size = 66945 }
455 | wheels = [
456 | { url = "https://files.pythonhosted.org/packages/36/3b/48e79f2cd6a61dbbd4807b4ed46cb564b4fd50a76166b1c4ea5c1d9e2371/pytest_cov-6.0.0-py3-none-any.whl", hash = "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35", size = 22949 },
457 | ]
458 |
459 | [[package]]
460 | name = "pyyaml"
461 | version = "6.0.2"
462 | source = { registry = "https://pypi.org/simple" }
463 | sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 }
464 | wheels = [
465 | { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 },
466 | { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 },
467 | { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 },
468 | { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 },
469 | { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 },
470 | { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 },
471 | { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 },
472 | { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 },
473 | { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 },
474 | { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 },
475 | { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 },
476 | { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 },
477 | { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 },
478 | { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 },
479 | { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 },
480 | { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 },
481 | { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 },
482 | { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 },
483 | ]
484 |
485 | [[package]]
486 | name = "questionary"
487 | version = "2.1.0"
488 | source = { registry = "https://pypi.org/simple" }
489 | dependencies = [
490 | { name = "prompt-toolkit" },
491 | ]
492 | sdist = { url = "https://files.pythonhosted.org/packages/a8/b8/d16eb579277f3de9e56e5ad25280fab52fc5774117fb70362e8c2e016559/questionary-2.1.0.tar.gz", hash = "sha256:6302cdd645b19667d8f6e6634774e9538bfcd1aad9be287e743d96cacaf95587", size = 26775 }
493 | wheels = [
494 | { url = "https://files.pythonhosted.org/packages/ad/3f/11dd4cd4f39e05128bfd20138faea57bec56f9ffba6185d276e3107ba5b2/questionary-2.1.0-py3-none-any.whl", hash = "sha256:44174d237b68bc828e4878c763a9ad6790ee61990e0ae72927694ead57bab8ec", size = 36747 },
495 | ]
496 |
497 | [[package]]
498 | name = "rich"
499 | version = "13.9.4"
500 | source = { registry = "https://pypi.org/simple" }
501 | dependencies = [
502 | { name = "markdown-it-py" },
503 | { name = "pygments" },
504 | ]
505 | sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 }
506 | wheels = [
507 | { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 },
508 | ]
509 |
510 | [[package]]
511 | name = "ruff"
512 | version = "0.8.4"
513 | source = { registry = "https://pypi.org/simple" }
514 | sdist = { url = "https://files.pythonhosted.org/packages/34/37/9c02181ef38d55b77d97c68b78e705fd14c0de0e5d085202bb2b52ce5be9/ruff-0.8.4.tar.gz", hash = "sha256:0d5f89f254836799af1615798caa5f80b7f935d7a670fad66c5007928e57ace8", size = 3402103 }
515 | wheels = [
516 | { url = "https://files.pythonhosted.org/packages/05/67/f480bf2f2723b2e49af38ed2be75ccdb2798fca7d56279b585c8f553aaab/ruff-0.8.4-py3-none-linux_armv6l.whl", hash = "sha256:58072f0c06080276804c6a4e21a9045a706584a958e644353603d36ca1eb8a60", size = 10546415 },
517 | { url = "https://files.pythonhosted.org/packages/eb/7a/5aba20312c73f1ce61814e520d1920edf68ca3b9c507bd84d8546a8ecaa8/ruff-0.8.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ffb60904651c00a1e0b8df594591770018a0f04587f7deeb3838344fe3adabac", size = 10346113 },
518 | { url = "https://files.pythonhosted.org/packages/76/f4/c41de22b3728486f0aa95383a44c42657b2db4062f3234ca36fc8cf52d8b/ruff-0.8.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6ddf5d654ac0d44389f6bf05cee4caeefc3132a64b58ea46738111d687352296", size = 9943564 },
519 | { url = "https://files.pythonhosted.org/packages/0e/f0/afa0d2191af495ac82d4cbbfd7a94e3df6f62a04ca412033e073b871fc6d/ruff-0.8.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e248b1f0fa2749edd3350a2a342b67b43a2627434c059a063418e3d375cfe643", size = 10805522 },
520 | { url = "https://files.pythonhosted.org/packages/12/57/5d1e9a0fd0c228e663894e8e3a8e7063e5ee90f8e8e60cf2085f362bfa1a/ruff-0.8.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf197b98ed86e417412ee3b6c893f44c8864f816451441483253d5ff22c0e81e", size = 10306763 },
521 | { url = "https://files.pythonhosted.org/packages/04/df/f069fdb02e408be8aac6853583572a2873f87f866fe8515de65873caf6b8/ruff-0.8.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c41319b85faa3aadd4d30cb1cffdd9ac6b89704ff79f7664b853785b48eccdf3", size = 11359574 },
522 | { url = "https://files.pythonhosted.org/packages/d3/04/37c27494cd02e4a8315680debfc6dfabcb97e597c07cce0044db1f9dfbe2/ruff-0.8.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:9f8402b7c4f96463f135e936d9ab77b65711fcd5d72e5d67597b543bbb43cf3f", size = 12094851 },
523 | { url = "https://files.pythonhosted.org/packages/81/b1/c5d7fb68506cab9832d208d03ea4668da9a9887a4a392f4f328b1bf734ad/ruff-0.8.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4e56b3baa9c23d324ead112a4fdf20db9a3f8f29eeabff1355114dd96014604", size = 11655539 },
524 | { url = "https://files.pythonhosted.org/packages/ef/38/8f8f2c8898dc8a7a49bc340cf6f00226917f0f5cb489e37075bcb2ce3671/ruff-0.8.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:736272574e97157f7edbbb43b1d046125fce9e7d8d583d5d65d0c9bf2c15addf", size = 12912805 },
525 | { url = "https://files.pythonhosted.org/packages/06/dd/fa6660c279f4eb320788876d0cff4ea18d9af7d9ed7216d7bd66877468d0/ruff-0.8.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5fe710ab6061592521f902fca7ebcb9fabd27bc7c57c764298b1c1f15fff720", size = 11205976 },
526 | { url = "https://files.pythonhosted.org/packages/a8/d7/de94cc89833b5de455750686c17c9e10f4e1ab7ccdc5521b8fe911d1477e/ruff-0.8.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:13e9ec6d6b55f6da412d59953d65d66e760d583dd3c1c72bf1f26435b5bfdbae", size = 10792039 },
527 | { url = "https://files.pythonhosted.org/packages/6d/15/3e4906559248bdbb74854af684314608297a05b996062c9d72e0ef7c7097/ruff-0.8.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:97d9aefef725348ad77d6db98b726cfdb075a40b936c7984088804dfd38268a7", size = 10400088 },
528 | { url = "https://files.pythonhosted.org/packages/a2/21/9ed4c0e8133cb4a87a18d470f534ad1a8a66d7bec493bcb8bda2d1a5d5be/ruff-0.8.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ab78e33325a6f5374e04c2ab924a3367d69a0da36f8c9cb6b894a62017506111", size = 10900814 },
529 | { url = "https://files.pythonhosted.org/packages/0d/5d/122a65a18955bd9da2616b69bc839351f8baf23b2805b543aa2f0aed72b5/ruff-0.8.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8ef06f66f4a05c3ddbc9121a8b0cecccd92c5bf3dd43b5472ffe40b8ca10f0f8", size = 11268828 },
530 | { url = "https://files.pythonhosted.org/packages/43/a9/1676ee9106995381e3d34bccac5bb28df70194167337ed4854c20f27c7ba/ruff-0.8.4-py3-none-win32.whl", hash = "sha256:552fb6d861320958ca5e15f28b20a3d071aa83b93caee33a87b471f99a6c0835", size = 8805621 },
531 | { url = "https://files.pythonhosted.org/packages/10/98/ed6b56a30ee76771c193ff7ceeaf1d2acc98d33a1a27b8479cbdb5c17a23/ruff-0.8.4-py3-none-win_amd64.whl", hash = "sha256:f21a1143776f8656d7f364bd264a9d60f01b7f52243fbe90e7670c0dfe0cf65d", size = 9660086 },
532 | { url = "https://files.pythonhosted.org/packages/13/9f/026e18ca7d7766783d779dae5e9c656746c6ede36ef73c6d934aaf4a6dec/ruff-0.8.4-py3-none-win_arm64.whl", hash = "sha256:9183dd615d8df50defa8b1d9a074053891ba39025cf5ae88e8bcb52edcc4bf08", size = 9074500 },
533 | ]
534 |
535 | [[package]]
536 | name = "termcolor"
537 | version = "2.5.0"
538 | source = { registry = "https://pypi.org/simple" }
539 | sdist = { url = "https://files.pythonhosted.org/packages/37/72/88311445fd44c455c7d553e61f95412cf89054308a1aa2434ab835075fc5/termcolor-2.5.0.tar.gz", hash = "sha256:998d8d27da6d48442e8e1f016119076b690d962507531df4890fcd2db2ef8a6f", size = 13057 }
540 | wheels = [
541 | { url = "https://files.pythonhosted.org/packages/7f/be/df630c387a0a054815d60be6a97eb4e8f17385d5d6fe660e1c02750062b4/termcolor-2.5.0-py3-none-any.whl", hash = "sha256:37b17b5fc1e604945c2642c872a3764b5d547a48009871aea3edd3afa180afb8", size = 7755 },
542 | ]
543 |
544 | [[package]]
545 | name = "textual"
546 | version = "1.0.0"
547 | source = { registry = "https://pypi.org/simple" }
548 | dependencies = [
549 | { name = "markdown-it-py", extra = ["linkify", "plugins"] },
550 | { name = "platformdirs" },
551 | { name = "rich" },
552 | { name = "typing-extensions" },
553 | ]
554 | sdist = { url = "https://files.pythonhosted.org/packages/1f/b6/59b1de04bb4dca0f21ed7ba0b19309ed7f3f5de4396edf20cc2855e53085/textual-1.0.0.tar.gz", hash = "sha256:bec9fe63547c1c552569d1b75d309038b7d456c03f86dfa3706ddb099b151399", size = 1532733 }
555 | wheels = [
556 | { url = "https://files.pythonhosted.org/packages/ac/bb/5fb6656c625019cd653d5215237d7cd6e0b12e7eae4195c3d1c91b2136fc/textual-1.0.0-py3-none-any.whl", hash = "sha256:2d4a701781c05104925e463ae370c630567c70c2880e92ab838052e3e23c986f", size = 660456 },
557 | ]
558 |
559 | [[package]]
560 | name = "tomlkit"
561 | version = "0.13.2"
562 | source = { registry = "https://pypi.org/simple" }
563 | sdist = { url = "https://files.pythonhosted.org/packages/b1/09/a439bec5888f00a54b8b9f05fa94d7f901d6735ef4e55dcec9bc37b5d8fa/tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79", size = 192885 }
564 | wheels = [
565 | { url = "https://files.pythonhosted.org/packages/f9/b6/a447b5e4ec71e13871be01ba81f5dfc9d0af7e473da256ff46bc0e24026f/tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde", size = 37955 },
566 | ]
567 |
568 | [[package]]
569 | name = "typing-extensions"
570 | version = "4.12.2"
571 | source = { registry = "https://pypi.org/simple" }
572 | sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 }
573 | wheels = [
574 | { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 },
575 | ]
576 |
577 | [[package]]
578 | name = "uc-micro-py"
579 | version = "1.0.3"
580 | source = { registry = "https://pypi.org/simple" }
581 | sdist = { url = "https://files.pythonhosted.org/packages/91/7a/146a99696aee0609e3712f2b44c6274566bc368dfe8375191278045186b8/uc-micro-py-1.0.3.tar.gz", hash = "sha256:d321b92cff673ec58027c04015fcaa8bb1e005478643ff4a500882eaab88c48a", size = 6043 }
582 | wheels = [
583 | { url = "https://files.pythonhosted.org/packages/37/87/1f677586e8ac487e29672e4b17455758fce261de06a0d086167bb760361a/uc_micro_py-1.0.3-py3-none-any.whl", hash = "sha256:db1dffff340817673d7b466ec86114a9dc0e9d4d9b5ba229d9d60e5c12600cd5", size = 6229 },
584 | ]
585 |
586 | [[package]]
587 | name = "virtualenv"
588 | version = "20.28.0"
589 | source = { registry = "https://pypi.org/simple" }
590 | dependencies = [
591 | { name = "distlib" },
592 | { name = "filelock" },
593 | { name = "platformdirs" },
594 | ]
595 | sdist = { url = "https://files.pythonhosted.org/packages/bf/75/53316a5a8050069228a2f6d11f32046cfa94fbb6cc3f08703f59b873de2e/virtualenv-20.28.0.tar.gz", hash = "sha256:2c9c3262bb8e7b87ea801d715fae4495e6032450c71d2309be9550e7364049aa", size = 7650368 }
596 | wheels = [
597 | { url = "https://files.pythonhosted.org/packages/10/f9/0919cf6f1432a8c4baa62511f8f8da8225432d22e83e3476f5be1a1edc6e/virtualenv-20.28.0-py3-none-any.whl", hash = "sha256:23eae1b4516ecd610481eda647f3a7c09aea295055337331bb4e6892ecce47b0", size = 4276702 },
598 | ]
599 |
600 | [[package]]
601 | name = "wcwidth"
602 | version = "0.2.13"
603 | source = { registry = "https://pypi.org/simple" }
604 | sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301 }
605 | wheels = [
606 | { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 },
607 | ]
608 |
--------------------------------------------------------------------------------