├── killpy ├── __init__.py ├── commands │ ├── __init__.py │ └── clean.py ├── killers │ ├── __init__.py │ ├── killer.py │ ├── pyenv_killer.py │ ├── venv_killer.py │ ├── conda_killer.py │ ├── poetry_killer.py │ └── pipx_killer.py ├── __main__.py ├── cleaners │ └── __init__.py ├── files │ └── __init__.py └── cli.py ├── .python-version ├── .codespellrc ├── show.gif ├── .pre-commit-hooks.yaml ├── .gitignore ├── .vscode └── settings.json ├── .github ├── workflows │ ├── publish-package.yml │ ├── publish-package-dev.yml │ ├── bump-version-dev.yml │ └── bump-version.yml └── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── question.md │ ├── feature_request.md │ └── task.md ├── .pre-commit-config.yaml ├── LICENSE ├── pyproject.toml ├── .ruff.toml ├── README.md ├── CHANGELOG.md └── uv.lock /killpy/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | 3.12 2 | -------------------------------------------------------------------------------- /killpy/commands/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.codespellrc: -------------------------------------------------------------------------------- 1 | [codespell] 2 | exclude-file=uv.lock 3 | -------------------------------------------------------------------------------- /show.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tlaloc-Es/killpy/HEAD/show.gif -------------------------------------------------------------------------------- /.pre-commit-hooks.yaml: -------------------------------------------------------------------------------- 1 | - id: killpy 2 | name: killpy 3 | description: Clean repo 4 | entry: killpy clean 5 | language: python 6 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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/__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/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/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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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/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 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "killpy" 3 | version = "0.15.5" 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.5" 42 | version_files = [ 43 | "src/__version__.py", 44 | "pyproject.toml:version" 45 | ] 46 | 47 | [project.scripts] 48 | killpy = "killpy.__main__:cli" 49 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /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 | [![PyPI](https://img.shields.io/pypi/v/killpy.svg)](https://pypi.org/project/killpy/) 16 | [![Downloads](https://static.pepy.tech/personalized-badge/killpy?period=month&units=international_system&left_color=grey&right_color=blue&left_text=PyPi%20Downloads)](https://pepy.tech/project/killpy) 17 | [![Stars](https://img.shields.io/github/stars/Tlaloc-Es/killpy?color=yellow&style=flat)](https://github.com/Tlaloc-Es/killpy/stargazers) 18 | [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](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 | ![GitHub issue custom search](https://img.shields.io/github/issues-search?query=repo%3ATlaloc-Es%2Fkillpy%20is%3Aclosed&label=issues%20closed&labelColor=%20%237d89b0&color=%20%235d6b98) 20 | ![killpy in action](show.gif) 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 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.15.5 (2025-07-19) 2 | 3 | ### Fix 4 | 5 | - set logging level to INFO in clean command for better visibility 6 | 7 | ## 0.15.4 (2025-02-07) 8 | 9 | ### Fix 10 | 11 | - set logging level to INFO in clean command for better visibility 12 | 13 | ## 0.15.3 (2025-02-07) 14 | 15 | ### Fix 16 | 17 | - enhance logging in clean command to report freed space 18 | 19 | ## 0.15.2 (2025-02-07) 20 | 21 | ### Fix 22 | 23 | - update killpy entry in pre-commit hooks to use clean command 24 | 25 | ## 0.15.1 (2025-02-07) 26 | 27 | ### Fix 28 | 29 | - update killpy entry point to use CLI 30 | 31 | ## 0.15.0 (2025-02-07) 32 | 33 | ### Feat 34 | 35 | - add clean command to killpy and integrate with CLI 36 | 37 | ## 0.14.1 (2025-01-23) 38 | 39 | ### Fix 40 | 41 | - the app breaks when 'pipx' is not installed. #14 42 | 43 | ## 0.14.0 (2025-01-17) 44 | 45 | ### Fix 46 | 47 | - forcebump 48 | 49 | ## 0.13.0 (2025-01-17) 50 | 51 | ### Feat 52 | 53 | - add GitHub Actions workflow for version bumping and changelog generation 54 | 55 | ### Fix 56 | 57 | - forcebump 58 | - update environment removal methods to use killers dictionary 59 | - update import path for TableApp in main module 60 | 61 | ### Refactor 62 | 63 | - separate entities 64 | 65 | ## 0.11.0b4 (2025-01-17) 66 | 67 | ### Feat 68 | 69 | - add GitHub Actions workflow for version bumping and changelog generation 70 | 71 | ### Fix 72 | 73 | - update environment removal methods to use killers dictionary 74 | - update import path for TableApp in main module 75 | 76 | ### Refactor 77 | 78 | - separate entities 79 | 80 | ## 0.11.0 (2025-01-16) 81 | 82 | ## 0.11.0b3 (2025-01-17) 83 | 84 | ## 0.11.0b2 (2025-01-17) 85 | 86 | ## 0.11.0b1 (2025-01-17) 87 | 88 | ## 0.11.0b0 (2025-01-17) 89 | 90 | ### Feat 91 | 92 | - add GitHub Actions workflow for version bumping and changelog generation 93 | - add support for pipx package management and enhance virtual environment tab functionality 94 | 95 | ### Refactor 96 | 97 | - separate entities 98 | 99 | ## 0.11.0 (2025-01-16) 100 | 101 | ### Feat 102 | 103 | - add support for pipx package management and enhance virtual environment tab functionality 104 | 105 | ## 0.10.0 (2025-01-06) 106 | 107 | ### Feat 108 | 109 | - enhance README and add support for listing Poetry virtual environments 110 | 111 | ## 0.9.0 (2025-01-05) 112 | 113 | ### Feat 114 | 115 | - add functionality to clean up __pycache__ directories and update README 116 | 117 | ## 0.8.4 (2025-01-05) 118 | 119 | ### Fix 120 | 121 | - update sorting logic in find_venvs functions to sort by size 122 | 123 | ## 0.8.3 (2025-01-05) 124 | 125 | ### Fix 126 | 127 | - handle FileNotFoundError in get_total_size and find_venvs functions close #4 128 | 129 | ## 0.8.2 (2025-01-05) 130 | 131 | ### Fix 132 | 133 | - try to execute with pipx 134 | 135 | ## 0.8.1 (2025-01-05) 136 | 137 | ### Fix 138 | 139 | - consolidate environment handling functions into __main__.py and remove envs_handler.py 140 | 141 | ## 0.8.0 (2025-01-05) 142 | 143 | ### Feat 144 | 145 | - update references from 'KillPy' to 'killpy' across the project 146 | 147 | ## 0.7.0 (2025-01-05) 148 | 149 | ### Feat 150 | 151 | - replace 'KillPy' with 'killpy' in workflow and pyproject.toml 152 | 153 | ## 0.6.0 (2025-01-05) 154 | 155 | ### Feat 156 | 157 | - add KillPy script entry point to pyproject.toml 158 | 159 | ## 0.5.0 (2025-01-05) 160 | 161 | ### Feat 162 | 163 | - enhance virtual environment management with deletion features and refactor code structure close #7 164 | 165 | ## 0.4.0 (2025-01-03) 166 | 167 | ### Feat 168 | 169 | - implement asynchronous searching for virtual environments close #2 170 | 171 | ## 0.3.0 (2025-01-03) 172 | 173 | ### Feat 174 | 175 | - add support for finding virtual environments with pyvenv and remove duplicates 176 | 177 | ### Fix 178 | 179 | - remove click dependency and update package version to 0.2.1 180 | 181 | ## 0.2.2 (2025-01-03) 182 | 183 | ### Fix 184 | 185 | - change key binding from 'ctrl+m' to 'space' for deleting .venv close #5 186 | 187 | ## 0.2.1 (2025-01-03) 188 | 189 | ### Fix 190 | 191 | - fails if conda is not installed #3 192 | 193 | ## 0.2.0 (2025-01-03) 194 | 195 | ### Feat 196 | 197 | - add support for listing and removing Conda environments in the app 198 | 199 | ## 0.1.7 (2025-01-02) 200 | 201 | ### Fix 202 | 203 | - add a banner to TableApp for enhanced user interface 204 | 205 | ## 0.1.6 (2025-01-02) 206 | 207 | ### Fix 208 | 209 | - enhance TableApp with improved venv display and deletion feedback 210 | 211 | ## 0.1.5 (2025-01-02) 212 | 213 | ### Refactor 214 | 215 | - reorganize imports and update deleted_cells type annotation in TableApp 216 | - improve find_venvs and get_total_size functions for better performance and readability 217 | 218 | ## 0.1.4 (2025-01-02) 219 | 220 | ### Fix 221 | 222 | - rename script entry point from pykill to killpy in pyproject.toml 223 | 224 | ## 0.1.3 (2025-01-02) 225 | 226 | ### Fix 227 | 228 | - rename script entry point from posewebcam to pykill in pyproject.toml 229 | 230 | ## 0.1.2 (2025-01-02) 231 | 232 | ### Fix 233 | 234 | - prevent find_venvs from traversing subdirectories within `.venv` folders 235 | 236 | ## 0.1.1 (2025-01-02) 237 | 238 | ### Fix 239 | 240 | - **cli**: fix command script 241 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------