diff --git a/.flake8 b/.flake8 deleted file mode 100644 index 0e71576..0000000 --- a/.flake8 +++ /dev/null @@ -1,14 +0,0 @@ -[flake8] -# Recommend matching the black line length (default 88), -# rather than using the flake8 default of 79: -max-line-length = 88 -# Q003 Change outer quotes to avoid escaping inner quotes -# W503 Line break before binary operator (preferred way) -# E203 See https://github.com/PyCQA/pycodestyle/issues/373 -extend-ignore = - Q003, - W503, - E203 -# flake8-use-fstring -percent-greedy = 0 -format-greedy = 2 diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..7c2c4ca --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,27 @@ +version: 2 +updates: + # Python dependencies (uv/pip ecosystem) + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + commit-message: + prefix: "deps" + groups: + python-dependencies: + patterns: + - "*" + + # GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + commit-message: + prefix: "ci" + groups: + github-actions: + patterns: + - "*" diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4f668dc..91a4b9f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -13,21 +13,22 @@ jobs: matrix: # https://www.python.org/downloads/ python-version: [ - "3.10", # EOL: 2026-10 - "3.11", # EOL: 2027-10 - "3.12", # EOL: 2028-10 - "3.13", # EOL: 2029-10 - ] + "3.10", # EOL: 2026-10 + "3.11", # EOL: 2027-10 + "3.12", # EOL: 2028-10 + "3.13", # EOL: 2029-10 + ] name: ${{ matrix.python-version }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - - name: Setup python - uses: actions/setup-python@v3 + - name: Setup uv + uses: astral-sh/setup-uv@v4 with: - python-version: "${{ matrix.python-version }}" + enable-cache: true + + - name: Setup Python ${{ matrix.python-version }} + run: uv python install ${{ matrix.python-version }} - name: CI checks - run: | - python -m pip install wheel uv - make + run: make diff --git a/.gitignore b/.gitignore index be4bf69..ddff74f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,106 +1,27 @@ -Pipfile.lock - # Byte-compiled / optimized / DLL files *.py[cod] __pycache__/ *$py.class -# C extensions -*.so - # Distribution / packaging .Python -build/ -develop-eggs/ dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ *.egg-info/ -.installed.cfg *.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt # Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -.hypothesis/ .pytest_cache/ -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - -# Jupyter Notebook -.ipynb_checkpoints - # pyenv .python-version -# celery beat schedule file -celerybeat-schedule - -# SageMath parsed files -*.sage.py - # Environments .venv -env/ -venv/ -ENV/ -env.bak/ venv.bak/ -# Spyder project settings -.spyderproject -.spyproject - # Rope project settings .ropeproject -# mkdocs documentation -/site - # mypy .mypy_cache/ @@ -110,3 +31,8 @@ venv.bak/ # Other .DS_Store +# Ruff linter +.ruff_cache/ + +# AI tooling +.claude/ diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 134d173..02fefe2 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -2,9 +2,8 @@ "recommendations": [ "ms-python.python", "ms-python.vscode-pylance", - "ms-python.flake8", "ms-python.mypy-type-checker", - "ms-python.black-formatter" + "charliermarsh.ruff" ], "unwantedRecommendations": [] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 6f84653..8a4da6f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -16,6 +16,6 @@ "python.testing.pytestEnabled": true, "python.testing.pytestArgs": [], "[python]": { - "editor.defaultFormatter": "ms-python.black-formatter" - }, + "editor.defaultFormatter": "charliermarsh.ruff" + } } diff --git a/CHANGELOG.md b/CHANGELOG.md index 322e267..66b9a8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## 0.6.0 + +- Simplify .gitignore +- Migrate from black, flake8, and isort to https://github.com/astral-sh/ruff +- Improve CI flow +- Add dependabot config +- Add security scanning +- Add help section to Makefile + +[#20](https://github.com/BastiTee/python-boilerplate/pull/20) + ## 0.5.1 - Upgrade all dependencies diff --git a/CLAUDE.md b/CLAUDE.md index 6a9952e..8c2b90f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,82 +4,48 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project Overview -This is a Python boilerplate/template project designed as a best-practices starting point for Python modules. It uses `uv` for dependency management and virtual environments (not Poetry or pip-tools). The project name is currently `my_module` and is meant to be renamed via `rename_template.sh` when used as a template. +Python boilerplate/template project using `uv` for dependency management. The project name `my_module` is meant to be renamed via `./rename_template.sh` when used as a template. ## Development Commands -All operations are managed through the Makefile, which acts as a facade for `uv` commands: +All operations are managed through the Makefile (facade for `uv` commands): -### Initial Setup -```bash -make # Clean, create venv, sync dependencies, and run full build -``` - -### Testing ```bash +make # Full setup: clean, create venv, sync deps, run build make test # Run pytest test suite -uv run py.test tests # Run all tests -uv run py.test tests/test_code.py # Run a single test file -``` - -### Code Quality -```bash -make build # Run full build: test, mypy, isort, black, lint, then package -make mypy # Type checking with mypy -make isort # Check import sorting (read-only) -make isort-apply # Fix import sorting -make black # Format code with black -make lint # Run flake8 linting +make build # Run test, mypy, lint, format, then package +make mypy # Type checking +make lint # Ruff linting +make lint-fix # Auto-fix lint issues +make format # Format code with ruff +make run-venv # Run module: uv run python -m my_module ``` -### Execution +### Running Single Tests ```bash -make run-venv # Run the module directly: uv run python -m my_module -make install-run # Install package and run via CLI as my_module_cli +uv run py.test tests/test_code.py # Single file +uv run py.test tests/test_code.py::TestCode # Single class +uv run py.test tests/test_code.py -k "test_code" # By name pattern ``` ### Environment Management ```bash -make clean # Remove all generated files (.venv, caches, build artifacts) -make venv # Clean and recreate virtual environment with uv sync -make clear-cache # Clear uv dependency cache -make outdated # Show outdated dependencies -make update # Update outdated dependencies -``` - -### Template Setup -```bash -./rename_template.sh # Rename the template project (interactive script) +make clean # Remove .venv, caches, build artifacts +make venv # Recreate virtual environment +make update # Update dependencies ``` ## Architecture -### Package Structure -- `my_module/` - Main module code - - `__init__.py` - Package initialization - - `__main__.py` - Entry point with `main()` function -- `tests/` - Test suite using pytest -- CLI entry point defined in pyproject.toml as `my_module_cli` - -### Build System -- Uses `uv` as the build backend (specified in pyproject.toml) -- `module-root = ""` in uv build config means the package is at repository root -- Package structure follows packaging.python.org standards +- `my_module/` - Main package with `__main__.py` entry point +- `tests/` - pytest test suite (class-based structure) +- CLI entry point: `my_module_cli` (defined in pyproject.toml) -### Code Quality Configuration -- **Black**: Line length 88, `skip-string-normalization = true` -- **Flake8**: Max line length 88 (matches black), ignores Q003, W503, E203 -- **Mypy**: Strict mode with `disallow_untyped_defs = true`, `check_untyped_defs = true` -- **isort**: Configured to work with black and flake8 -- Python versions supported: 3.10, 3.11, 3.12, 3.13 (requires-python: ">=3.10,<3.14") +## Code Quality -### Testing -- Uses pytest with `-p no:warnings` to suppress warnings -- Tests should follow the class-based structure in `tests/test_code.py` +- **Ruff**: Linting + formatting (line-length 88, quote-style preserve) + - Includes security scanning via flake8-bandit (S) rules +- **Mypy**: Strict mode (`disallow_untyped_defs = true`) +- **Python**: 3.10, 3.11, 3.12, 3.13 -### VSCode Integration -- Python formatter: black-formatter (format on save enabled) -- Pytest discovery enabled -- Tab size: 4 spaces -- Word wrap: 88 columns -- Trailing whitespace automatically trimmed +All config is centralized in `pyproject.toml`. diff --git a/Makefile b/Makefile index 0ef3982..ed59789 100644 --- a/Makefile +++ b/Makefile @@ -10,20 +10,28 @@ export LC_ALL = C export LANG = C.UTF-8 PY_FILES := my_module tests +.PHONY: help all clean clear-cache venv build test mypy lint lint-fix format format-check outdated update run-venv install-run +.DEFAULT_GOAL := all + +help: ## Show this help message + @echo 'Usage: make [target]' + @echo '' + @echo 'Targets:' + @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " %-15s %s\n", $$1, $$2}' $(MAKEFILE_LIST) + # Bundle tasks -all: clean venv build +all: clean venv build ## Clean, create venv, and run full build (default) @echo Executed default build pipeline -# Clean up and set up - -clean: +clean: ## Remove .venv, caches, and build artifacts @echo Clean project base find . -type d \ -name ".venv" -o \ -name ".tox" -o \ -name ".ropeproject" -o \ -name ".mypy_cache" -o \ + -name ".ruff_cache" -o \ -name ".pytest_cache" -o \ -name "__pycache__" -o \ -iname "*.egg-info" -o \ @@ -31,60 +39,43 @@ clean: -name "dist" \ |xargs rm -rfv -clear-cache: - @echo Clear dependency cache +clear-cache: ## Clear uv dependency cache uv cache clean -venv: clean - @echo Initialize virtualenv, i.e., install required packages etc. +venv: clean ## Clean and recreate virtual environment uv sync -# Building software - -build: test mypy isort black lint - @echo Run build process to package application +build: test mypy lint format ## Run the entire build chain uv build -test: - @echo Run all tests suites +test: ## Run pytest test suite uv run py.test tests -mypy: - @echo Run static code checks against source code base +mypy: ## Run mypy type checking uv run mypy $(PY_FILES) -isort: - @echo Check for incorrectly sorted imports - uv run isort --check-only $(PY_FILES) +lint: ## Run ruff linting + uv run ruff check $(PY_FILES) -isort-apply: - @echo Check and correct incorrectly sorted imports - uv run isort $(PY_FILES) +lint-fix: ## Auto-fix linting issues with ruff + uv run ruff check --fix $(PY_FILES) -black: - @echo Run code formatting using black - uv run black $(PY_FILES) +format: ## Format code with ruff + uv run ruff format $(PY_FILES) -lint: - @echo Run code formatting checks against source code base - uv run flake8 $(PY_FILES) +format-check: ## Check code formatting (no changes) + uv run ruff format --check $(PY_FILES) -outdated: - @echo Show outdated dependencies +outdated: ## Show outdated dependencies uv pip list --outdated --exclude-editable -update: - @echo Update outdated dependencies +update: ## Update all dependencies uv sync --upgrade -# Executing - -run-venv: - @echo Execute package directly in virtual environment +run-venv: ## Run module directly in venv uv run python -m my_module -install-run: - @echo Install and run package via CLI using the activated Python env +install-run: ## Install package and run CLI python -m pip install --upgrade . @echo --- Note: The next command might fail before you reload your shell my_module_cli diff --git a/README.md b/README.md index df8a66a..8659e0d 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ > A best-practices template project for Python modules +This boilerplate gives you a production-ready Python project structure with modern tooling. Clone it, rename it, and start coding - linting, formatting, type checking, testing, and CI are already configured. No more setting up the same tools for every new project. + ## Setup - Create a new repository [using this template](https://github.com/BastiTee/python-boilerplate/generate). @@ -16,14 +18,15 @@ - Basic project/module organization according to - Makefile bootstrapping script - [uv](https://github.com/astral-sh/uv) with virtual environments and project builds -- [black](https://github.com/psf/black) code formatting +- [Ruff](https://github.com/astral-sh/ruff) for linting and code formatting (replaces black, flake8, isort) +- Security scanning via Ruff's [flake8-bandit](https://docs.astral.sh/ruff/rules/#flake8-bandit-s) rules - Unit testing with [pytest](https://docs.pytest.org/en/latest/) -- Linting ([flake8](http://flake8.pycqa.org)) and code formatting ([autopep8](https://github.com/hhatto/autopep8)) support -- [isort](https://pypi.org/project/isort/) support for automated import sorting -- [mpyp](https://pypi.org/project/mypy/) support for type checking +- [mypy](https://pypi.org/project/mypy/) for static type checking +- PEP 561 compliant (`py.typed` marker included) - Publishing to PyPi.org - [vscode](https://code.visualstudio.com/) editor configuration including plugin recommendations, debugging support, unit test discovery and on-save formatting - [Github actions](https://github.com/BastiTee/python-boilerplate/actions) continuous integration with multi-python testing +- [Dependabot](https://docs.github.com/en/code-security/dependabot) for automated dependency updates - Executable script so after package installation you can run from the CLI using `my_module_cli` ## Resources @@ -37,11 +40,6 @@ - - -## Future ideas and todos - -- Embed flake8 config into `pyproject.toml` once they support it ([see](https://github.com/PyCQA/flake8/issues/234)) -- Use cookiecutter instead of [shell script](./rename_template.sh) (it does work fine at the moment though) - ## Licensing This project is licensed under [Apache License 2.0](LICENSE.txt). diff --git a/my_module/__init__.py b/my_module/__init__.py index d74d677..074cc4a 100644 --- a/my_module/__init__.py +++ b/my_module/__init__.py @@ -1,6 +1,24 @@ # -*- coding: utf-8 -*- -"""Module init-file. +"""my_module - A Python module template. -The __init__.py files are required to make Python treat directories -containing the file as packages. +This module provides example functions demonstrating proper typing, +documentation, and testing patterns. """ + +__all__ = ['greet'] + + +def greet(name: str) -> str: + """Return a greeting message for the given name. + + Args: + name: The name of the person to greet. + + Returns: + A personalized greeting string. + + Example: + >>> greet("World") + 'Hello, World!' + """ + return f'Hello, {name}!' diff --git a/my_module/__main__.py b/my_module/__main__.py index 182c514..5483fd8 100644 --- a/my_module/__main__.py +++ b/my_module/__main__.py @@ -5,9 +5,12 @@ a script, or from an interactive prompt. """ +from my_module import greet -def main() -> None: # noqa: D103 - print('Hello world!') + +def main() -> None: + """Entry point for the CLI.""" + print(greet('World')) if __name__ == '__main__': diff --git a/my_module/py.typed b/my_module/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/pyproject.toml b/pyproject.toml index 1273dc5..66d2a97 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "uv_build" [project] name = "my_module" -version = "0.5.1" +version = "0.6.0" description = "A best-practices template project for Python modules" license = "Apache-2.0" readme = "README.md" @@ -40,19 +40,9 @@ module-root = "" [dependency-groups] dev = [ - "autopep8>=2", - "black>=25", - "flake8>=7", - "flake8-blind-except>=0", - "flake8-builtins>=3", - "flake8-docstrings>=1", - "flake8-isort>=6", - "flake8-quotes>=3", - "flake8-use-fstring>=1", - "isort>=7", "mypy>=1", - "pep8-naming>=0", "pytest>=9", + "ruff>=0.8", "typing-extensions>=4", ] @@ -78,5 +68,28 @@ addopts = [ "no:warnings" ] -[tool.black] -skip-string-normalization = true +[tool.ruff] +line-length = 88 +target-version = "py310" + +[tool.ruff.lint] +select = [ + "E", # pycodestyle errors + "W", # pycodestyle warnings + "F", # pyflakes + "I", # isort + "N", # pep8-naming + "BLE", # flake8-blind-except + "A", # flake8-builtins + "S", # flake8-bandit (security) +] +ignore = [ + "E203", # whitespace before colon (Black-compatible) + "S101", # allow assert in tests +] + +[tool.ruff.lint.per-file-ignores] +"tests/**/*.py" = ["S101"] # allow assert in test files + +[tool.ruff.format] +quote-style = "preserve" diff --git a/tests/test_code.py b/tests/test_code.py index 2d6ba55..70fca43 100644 --- a/tests/test_code.py +++ b/tests/test_code.py @@ -1,19 +1,18 @@ # -*- coding: utf-8 -*- -"""Basic test suite. +"""Test suite for my_module.""" -There are some 'noqa: F401' in this file to just test the isort import sorting -along with the code formatter. -""" +from my_module import greet -import __future__ # noqa: F401 -import json # noqa: F401 -from os import path # noqa: F401 -from re import IGNORECASE, sub # noqa: F401 +class TestGreet: + """Tests for the greet function.""" -import my_module # noqa: F401 + def test_greet_returns_greeting(self) -> None: + """Test that greet returns a properly formatted greeting.""" + result = greet('Alice') + assert result == 'Hello, Alice!' - -class TestCode: # noqa: D101 - def test_code(self) -> None: # noqa: D102 - assert True + def test_greet_with_empty_string(self) -> None: + """Test that greet handles empty string input.""" + result = greet('') + assert result == 'Hello, !' diff --git a/uv.lock b/uv.lock index 54002fe..10b5795 100644 --- a/uv.lock +++ b/uv.lock @@ -2,70 +2,6 @@ version = 1 revision = 3 requires-python = ">=3.10, <3.14" -[[package]] -name = "autopep8" -version = "2.3.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pycodestyle" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/50/d8/30873d2b7b57dee9263e53d142da044c4600a46f2d28374b3e38b023df16/autopep8-2.3.2.tar.gz", hash = "sha256:89440a4f969197b69a995e4ce0661b031f455a9f776d2c5ba3dbd83466931758", size = 92210, upload-time = "2025-01-14T14:46:18.454Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/43/53afb8ba17218f19b77c7834128566c5bbb100a0ad9ba2e8e89d089d7079/autopep8-2.3.2-py2.py3-none-any.whl", hash = "sha256:ce8ad498672c845a0c3de2629c15b635ec2b05ef8177a6e7c91c74f3e9b51128", size = 45807, upload-time = "2025-01-14T14:46:15.466Z" }, -] - -[[package]] -name = "black" -version = "25.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "click" }, - { name = "mypy-extensions" }, - { name = "packaging" }, - { name = "pathspec" }, - { name = "platformdirs" }, - { name = "pytokens" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/c4/d9/07b458a3f1c525ac392b5edc6b191ff140b596f9d77092429417a54e249d/black-25.12.0.tar.gz", hash = "sha256:8d3dd9cea14bff7ddc0eb243c811cdb1a011ebb4800a5f0335a01a68654796a7", size = 659264, upload-time = "2025-12-08T01:40:52.501Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/37/d5/8d3145999d380e5d09bb00b0f7024bf0a8ccb5c07b5648e9295f02ec1d98/black-25.12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f85ba1ad15d446756b4ab5f3044731bf68b777f8f9ac9cdabd2425b97cd9c4e8", size = 1895720, upload-time = "2025-12-08T01:46:58.197Z" }, - { url = "https://files.pythonhosted.org/packages/06/97/7acc85c4add41098f4f076b21e3e4e383ad6ed0a3da26b2c89627241fc11/black-25.12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:546eecfe9a3a6b46f9d69d8a642585a6eaf348bcbbc4d87a19635570e02d9f4a", size = 1727193, upload-time = "2025-12-08T01:52:26.674Z" }, - { url = "https://files.pythonhosted.org/packages/24/f0/fdf0eb8ba907ddeb62255227d29d349e8256ef03558fbcadfbc26ecfe3b2/black-25.12.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:17dcc893da8d73d8f74a596f64b7c98ef5239c2cd2b053c0f25912c4494bf9ea", size = 1774506, upload-time = "2025-12-08T01:46:25.721Z" }, - { url = "https://files.pythonhosted.org/packages/e4/f5/9203a78efe00d13336786b133c6180a9303d46908a9aa72d1104ca214222/black-25.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:09524b0e6af8ba7a3ffabdfc7a9922fb9adef60fed008c7cd2fc01f3048e6e6f", size = 1416085, upload-time = "2025-12-08T01:46:06.073Z" }, - { url = "https://files.pythonhosted.org/packages/ba/cc/7a6090e6b081c3316282c05c546e76affdce7bf7a3b7d2c3a2a69438bd01/black-25.12.0-cp310-cp310-win_arm64.whl", hash = "sha256:b162653ed89eb942758efeb29d5e333ca5bb90e5130216f8369857db5955a7da", size = 1226038, upload-time = "2025-12-08T01:45:29.388Z" }, - { url = "https://files.pythonhosted.org/packages/60/ad/7ac0d0e1e0612788dbc48e62aef8a8e8feffac7eb3d787db4e43b8462fa8/black-25.12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d0cfa263e85caea2cff57d8f917f9f51adae8e20b610e2b23de35b5b11ce691a", size = 1877003, upload-time = "2025-12-08T01:43:29.967Z" }, - { url = "https://files.pythonhosted.org/packages/e8/dd/a237e9f565f3617a88b49284b59cbca2a4f56ebe68676c1aad0ce36a54a7/black-25.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1a2f578ae20c19c50a382286ba78bfbeafdf788579b053d8e4980afb079ab9be", size = 1712639, upload-time = "2025-12-08T01:52:46.756Z" }, - { url = "https://files.pythonhosted.org/packages/12/80/e187079df1ea4c12a0c63282ddd8b81d5107db6d642f7d7b75a6bcd6fc21/black-25.12.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e1b65634b0e471d07ff86ec338819e2ef860689859ef4501ab7ac290431f9b", size = 1758143, upload-time = "2025-12-08T01:45:29.137Z" }, - { url = "https://files.pythonhosted.org/packages/93/b5/3096ccee4f29dc2c3aac57274326c4d2d929a77e629f695f544e159bfae4/black-25.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:a3fa71e3b8dd9f7c6ac4d818345237dfb4175ed3bf37cd5a581dbc4c034f1ec5", size = 1420698, upload-time = "2025-12-08T01:45:53.379Z" }, - { url = "https://files.pythonhosted.org/packages/7e/39/f81c0ffbc25ffbe61c7d0385bf277e62ffc3e52f5ee668d7369d9854fadf/black-25.12.0-cp311-cp311-win_arm64.whl", hash = "sha256:51e267458f7e650afed8445dc7edb3187143003d52a1b710c7321aef22aa9655", size = 1229317, upload-time = "2025-12-08T01:46:35.606Z" }, - { url = "https://files.pythonhosted.org/packages/d1/bd/26083f805115db17fda9877b3c7321d08c647df39d0df4c4ca8f8450593e/black-25.12.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:31f96b7c98c1ddaeb07dc0f56c652e25bdedaac76d5b68a059d998b57c55594a", size = 1924178, upload-time = "2025-12-08T01:49:51.048Z" }, - { url = "https://files.pythonhosted.org/packages/89/6b/ea00d6651561e2bdd9231c4177f4f2ae19cc13a0b0574f47602a7519b6ca/black-25.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:05dd459a19e218078a1f98178c13f861fe6a9a5f88fc969ca4d9b49eb1809783", size = 1742643, upload-time = "2025-12-08T01:49:59.09Z" }, - { url = "https://files.pythonhosted.org/packages/6d/f3/360fa4182e36e9875fabcf3a9717db9d27a8d11870f21cff97725c54f35b/black-25.12.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c1f68c5eff61f226934be6b5b80296cf6939e5d2f0c2f7d543ea08b204bfaf59", size = 1800158, upload-time = "2025-12-08T01:44:27.301Z" }, - { url = "https://files.pythonhosted.org/packages/f8/08/2c64830cb6616278067e040acca21d4f79727b23077633953081c9445d61/black-25.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:274f940c147ddab4442d316b27f9e332ca586d39c85ecf59ebdea82cc9ee8892", size = 1426197, upload-time = "2025-12-08T01:45:51.198Z" }, - { url = "https://files.pythonhosted.org/packages/d4/60/a93f55fd9b9816b7432cf6842f0e3000fdd5b7869492a04b9011a133ee37/black-25.12.0-cp312-cp312-win_arm64.whl", hash = "sha256:169506ba91ef21e2e0591563deda7f00030cb466e747c4b09cb0a9dae5db2f43", size = 1237266, upload-time = "2025-12-08T01:45:10.556Z" }, - { url = "https://files.pythonhosted.org/packages/c8/52/c551e36bc95495d2aa1a37d50566267aa47608c81a53f91daa809e03293f/black-25.12.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a05ddeb656534c3e27a05a29196c962877c83fa5503db89e68857d1161ad08a5", size = 1923809, upload-time = "2025-12-08T01:46:55.126Z" }, - { url = "https://files.pythonhosted.org/packages/a0/f7/aac9b014140ee56d247e707af8db0aae2e9efc28d4a8aba92d0abd7ae9d1/black-25.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9ec77439ef3e34896995503865a85732c94396edcc739f302c5673a2315e1e7f", size = 1742384, upload-time = "2025-12-08T01:49:37.022Z" }, - { url = "https://files.pythonhosted.org/packages/74/98/38aaa018b2ab06a863974c12b14a6266badc192b20603a81b738c47e902e/black-25.12.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e509c858adf63aa61d908061b52e580c40eae0dfa72415fa47ac01b12e29baf", size = 1798761, upload-time = "2025-12-08T01:46:05.386Z" }, - { url = "https://files.pythonhosted.org/packages/16/3a/a8ac542125f61574a3f015b521ca83b47321ed19bb63fe6d7560f348bfe1/black-25.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:252678f07f5bac4ff0d0e9b261fbb029fa530cfa206d0a636a34ab445ef8ca9d", size = 1429180, upload-time = "2025-12-08T01:45:34.903Z" }, - { url = "https://files.pythonhosted.org/packages/e6/2d/bdc466a3db9145e946762d52cd55b1385509d9f9004fec1c97bdc8debbfb/black-25.12.0-cp313-cp313-win_arm64.whl", hash = "sha256:bc5b1c09fe3c931ddd20ee548511c64ebf964ada7e6f0763d443947fd1c603ce", size = 1239350, upload-time = "2025-12-08T01:46:09.458Z" }, - { url = "https://files.pythonhosted.org/packages/68/11/21331aed19145a952ad28fca2756a1433ee9308079bd03bd898e903a2e53/black-25.12.0-py3-none-any.whl", hash = "sha256:48ceb36c16dbc84062740049eef990bb2ce07598272e673c17d1a7720c71c828", size = 206191, upload-time = "2025-12-08T01:40:50.963Z" }, -] - -[[package]] -name = "click" -version = "8.3.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" }, -] - [[package]] name = "colorama" version = "0.4.6" @@ -87,83 +23,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, ] -[[package]] -name = "flake8" -version = "7.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "mccabe" }, - { name = "pycodestyle" }, - { name = "pyflakes" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9b/af/fbfe3c4b5a657d79e5c47a2827a362f9e1b763336a52f926126aa6dc7123/flake8-7.3.0.tar.gz", hash = "sha256:fe044858146b9fc69b551a4b490d69cf960fcb78ad1edcb84e7fbb1b4a8e3872", size = 48326, upload-time = "2025-06-20T19:31:35.838Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9f/56/13ab06b4f93ca7cac71078fbe37fcea175d3216f31f85c3168a6bbd0bb9a/flake8-7.3.0-py2.py3-none-any.whl", hash = "sha256:b9696257b9ce8beb888cdbe31cf885c90d31928fe202be0889a7cdafad32f01e", size = 57922, upload-time = "2025-06-20T19:31:34.425Z" }, -] - -[[package]] -name = "flake8-blind-except" -version = "0.2.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/be/f8/cb5e0b8c948cb347b8942cb7ce73292a77ec8f9eafb8d5db999c5c1ac62f/flake8-blind-except-0.2.1.tar.gz", hash = "sha256:f25a575a9dcb3eeb3c760bf9c22db60b8b5a23120224ed1faa9a43f75dd7dd16", size = 3729, upload-time = "2022-03-18T16:49:13.002Z" } - -[[package]] -name = "flake8-builtins" -version = "3.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "flake8" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/49/d9/8c7f3e411e65ec169c95f5069a5307b2c5b3f258d3a3d2ecf0a09ecc63ad/flake8_builtins-3.1.0.tar.gz", hash = "sha256:10d29d7a81ba6546a26461e83ce1936644764a678ff6337b97a894ebc4db07c1", size = 18378, upload-time = "2025-10-25T13:45:58.587Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ba/a9/bf3f0e4b18002c0dc4ebe58c2950ae30a83ae82cf05a96d569818c7f0eb6/flake8_builtins-3.1.0-py3-none-any.whl", hash = "sha256:ac09803ddfcce7f67899d24b1ecf4f4bff303d8ebc07a0dde4a2a587cf9d9363", size = 18892, upload-time = "2025-10-25T13:45:56.881Z" }, -] - -[[package]] -name = "flake8-docstrings" -version = "1.7.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "flake8" }, - { name = "pydocstyle" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/93/24/f839e3a06e18f4643ccb81370909a497297909f15106e6af2fecdef46894/flake8_docstrings-1.7.0.tar.gz", hash = "sha256:4c8cc748dc16e6869728699e5d0d685da9a10b0ea718e090b1ba088e67a941af", size = 5995, upload-time = "2023-01-25T14:27:13.903Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/7d/76a278fa43250441ed9300c344f889c7fb1817080c8fb8996b840bf421c2/flake8_docstrings-1.7.0-py2.py3-none-any.whl", hash = "sha256:51f2344026da083fc084166a9353f5082b01f72901df422f74b4d953ae88ac75", size = 4994, upload-time = "2023-01-25T14:27:12.32Z" }, -] - -[[package]] -name = "flake8-isort" -version = "7.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "flake8" }, - { name = "isort" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/57/a4/4b170983fd2e9b5ecc40c88738db6dfb77e069c71be9a81f9893cdb8a0cc/flake8_isort-7.0.0.tar.gz", hash = "sha256:a677199d1197f826eb69084e7ac272f208f4583363285f43111c34272abe7e5d", size = 17796, upload-time = "2025-10-25T13:31:08.768Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/17/7d/907ef4135f6ede5187930d9ddd1f36564e07c6cdcd15ae8fb9849c9517e0/flake8_isort-7.0.0-py3-none-any.whl", hash = "sha256:c301a0e55fc77582348e636194b84b1a0baf0dfdaa6eddf3b0eeea75f8be7f36", size = 18383, upload-time = "2025-10-25T13:31:06.914Z" }, -] - -[[package]] -name = "flake8-quotes" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "flake8" }, - { name = "setuptools" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/dd/57/a173e3eb86072b7ee77650aca496b15d6886367d257f58ea9de5276e330a/flake8-quotes-3.4.0.tar.gz", hash = "sha256:aad8492fb710a2d3eabe68c5f86a1428de650c8484127e14c43d0504ba30276c", size = 14107, upload-time = "2024-02-10T21:58:22.357Z" } - -[[package]] -name = "flake8-use-fstring" -version = "1.4" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "flake8" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5c/40/ec0437e5ec5b107df0b6ee967b60e6f575a4d38c8b0639d78c555987fa39/flake8-use-fstring-1.4.tar.gz", hash = "sha256:6550bf722585eb97dffa8343b0f1c372101f5c4ab5b07ebf0edd1c79880cdd39", size = 5751, upload-time = "2022-07-27T21:00:35.404Z" } - [[package]] name = "iniconfig" version = "2.3.0" @@ -173,15 +32,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, ] -[[package]] -name = "isort" -version = "7.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/63/53/4f3c058e3bace40282876f9b553343376ee687f3c35a525dc79dbd450f88/isort-7.0.0.tar.gz", hash = "sha256:5513527951aadb3ac4292a41a16cbc50dd1642432f5e8c20057d414bdafb4187", size = 805049, upload-time = "2025-10-11T13:30:59.107Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7f/ed/e3705d6d02b4f7aea715a353c8ce193efd0b5db13e204df895d38734c244/isort-7.0.0-py3-none-any.whl", hash = "sha256:1bcabac8bc3c36c7fb7b98a76c8abb18e0f841a3ba81decac7691008592499c1", size = 94672, upload-time = "2025-10-11T13:30:57.665Z" }, -] - [[package]] name = "librt" version = "0.7.8" @@ -233,15 +83,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/fc/b9/973455cec0a1ec592395250c474164c4a58ebf3e0651ee920fef1a2623f1/librt-0.7.8-cp313-cp313-win_arm64.whl", hash = "sha256:43d4e71b50763fcdcf64725ac680d8cfa1706c928b844794a7aa0fa9ac8e5f09", size = 43600, upload-time = "2026-01-14T12:55:34.054Z" }, ] -[[package]] -name = "mccabe" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e7/ff/0ffefdcac38932a54d2b5eed4e0ba8a408f215002cd178ad1df0f2806ff8/mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", size = 9658, upload-time = "2022-01-24T01:14:51.113Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/1a/1f68f9ba0c207934b35b86a8ca3aad8395a3d6dd7921c0686e23853ff5a9/mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e", size = 7350, upload-time = "2022-01-24T01:14:49.62Z" }, -] - [[package]] name = "my-module" version = "0.5.1" @@ -249,19 +90,9 @@ source = { editable = "." } [package.dev-dependencies] dev = [ - { name = "autopep8" }, - { name = "black" }, - { name = "flake8" }, - { name = "flake8-blind-except" }, - { name = "flake8-builtins" }, - { name = "flake8-docstrings" }, - { name = "flake8-isort" }, - { name = "flake8-quotes" }, - { name = "flake8-use-fstring" }, - { name = "isort" }, { name = "mypy" }, - { name = "pep8-naming" }, { name = "pytest" }, + { name = "ruff" }, { name = "typing-extensions" }, ] @@ -269,19 +100,9 @@ dev = [ [package.metadata.requires-dev] dev = [ - { name = "autopep8", specifier = ">=2" }, - { name = "black", specifier = ">=25" }, - { name = "flake8", specifier = ">=7" }, - { name = "flake8-blind-except", specifier = ">=0" }, - { name = "flake8-builtins", specifier = ">=3" }, - { name = "flake8-docstrings", specifier = ">=1" }, - { name = "flake8-isort", specifier = ">=6" }, - { name = "flake8-quotes", specifier = ">=3" }, - { name = "flake8-use-fstring", specifier = ">=1" }, - { name = "isort", specifier = ">=7" }, { name = "mypy", specifier = ">=1" }, - { name = "pep8-naming", specifier = ">=0" }, { name = "pytest", specifier = ">=9" }, + { name = "ruff", specifier = ">=0.8" }, { name = "typing-extensions", specifier = ">=4" }, ] @@ -352,27 +173,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/32/2b/121e912bd60eebd623f873fd090de0e84f322972ab25a7f9044c056804ed/pathspec-1.0.3-py3-none-any.whl", hash = "sha256:e80767021c1cc524aa3fb14bedda9c34406591343cc42797b386ce7b9354fb6c", size = 55021, upload-time = "2026-01-09T15:46:44.652Z" }, ] -[[package]] -name = "pep8-naming" -version = "0.15.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "flake8" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/8d/59/c32862134635ba231d45f1711035550dc38246396c27269a4cde4bfe18d2/pep8_naming-0.15.1.tar.gz", hash = "sha256:f6f4a499aba2deeda93c1f26ccc02f3da32b035c8b2db9696b730ef2c9639d29", size = 17640, upload-time = "2025-05-05T20:43:12.555Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a6/78/25281540f1121acaa78926f599a17ce102b8971bc20b096fa7fb6b5b59c1/pep8_naming-0.15.1-py3-none-any.whl", hash = "sha256:eb63925e7fd9e028c7f7ee7b1e413ec03d1ee5de0e627012102ee0222c273c86", size = 9561, upload-time = "2025-05-05T20:43:11.626Z" }, -] - -[[package]] -name = "platformdirs" -version = "4.5.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, -] - [[package]] name = "pluggy" version = "1.6.0" @@ -382,36 +182,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] -[[package]] -name = "pycodestyle" -version = "2.14.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/11/e0/abfd2a0d2efe47670df87f3e3a0e2edda42f055053c85361f19c0e2c1ca8/pycodestyle-2.14.0.tar.gz", hash = "sha256:c4b5b517d278089ff9d0abdec919cd97262a3367449ea1c8b49b91529167b783", size = 39472, upload-time = "2025-06-20T18:49:48.75Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/d7/27/a58ddaf8c588a3ef080db9d0b7e0b97215cee3a45df74f3a94dbbf5c893a/pycodestyle-2.14.0-py2.py3-none-any.whl", hash = "sha256:dd6bf7cb4ee77f8e016f9c8e74a35ddd9f67e1d5fd4184d86c3b98e07099f42d", size = 31594, upload-time = "2025-06-20T18:49:47.491Z" }, -] - -[[package]] -name = "pydocstyle" -version = "6.3.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "snowballstemmer" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/e9/5c/d5385ca59fd065e3c6a5fe19f9bc9d5ea7f2509fa8c9c22fb6b2031dd953/pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1", size = 36796, upload-time = "2023-01-17T20:29:19.838Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/36/ea/99ddefac41971acad68f14114f38261c1f27dac0b3ec529824ebc739bdaa/pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019", size = 38038, upload-time = "2023-01-17T20:29:18.094Z" }, -] - -[[package]] -name = "pyflakes" -version = "3.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/45/dc/fd034dc20b4b264b3d015808458391acbf9df40b1e54750ef175d39180b1/pyflakes-3.4.0.tar.gz", hash = "sha256:b24f96fafb7d2ab0ec5075b7350b3d2d2218eab42003821c06344973d3ea2f58", size = 64669, upload-time = "2025-06-20T18:45:27.834Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/2f/81d580a0fb83baeb066698975cb14a618bdbed7720678566f1b046a95fe8/pyflakes-3.4.0-py2.py3-none-any.whl", hash = "sha256:f742a7dbd0d9cb9ea41e9a24a918996e8170c799fa528688d40dd582c8265f4f", size = 63551, upload-time = "2025-06-20T18:45:26.937Z" }, -] - [[package]] name = "pygments" version = "2.19.2" @@ -440,30 +210,29 @@ wheels = [ ] [[package]] -name = "pytokens" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4e/8d/a762be14dae1c3bf280202ba3172020b2b0b4c537f94427435f19c413b72/pytokens-0.3.0.tar.gz", hash = "sha256:2f932b14ed08de5fcf0b391ace2642f858f1394c0857202959000b68ed7a458a", size = 17644, upload-time = "2025-11-05T13:36:35.34Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/84/25/d9db8be44e205a124f6c98bc0324b2bb149b7431c53877fc6d1038dddaf5/pytokens-0.3.0-py3-none-any.whl", hash = "sha256:95b2b5eaf832e469d141a378872480ede3f251a5a5041b8ec6e581d3ac71bbf3", size = 12195, upload-time = "2025-11-05T13:36:33.183Z" }, -] - -[[package]] -name = "setuptools" -version = "80.9.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" }, -] - -[[package]] -name = "snowballstemmer" -version = "3.0.1" +name = "ruff" +version = "0.14.11" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/75/a7/9810d872919697c9d01295633f5d574fb416d47e535f258272ca1f01f447/snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895", size = 105575, upload-time = "2025-05-09T16:34:51.843Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d4/77/9a7fe084d268f8855d493e5031ea03fa0af8cc05887f638bf1c4e3363eb8/ruff-0.14.11.tar.gz", hash = "sha256:f6dc463bfa5c07a59b1ff2c3b9767373e541346ea105503b4c0369c520a66958", size = 5993417, upload-time = "2026-01-08T19:11:58.322Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064", size = 103274, upload-time = "2025-05-09T16:34:50.371Z" }, + { url = "https://files.pythonhosted.org/packages/f0/a6/a4c40a5aaa7e331f245d2dc1ac8ece306681f52b636b40ef87c88b9f7afd/ruff-0.14.11-py3-none-linux_armv6l.whl", hash = "sha256:f6ff2d95cbd335841a7217bdfd9c1d2e44eac2c584197ab1385579d55ff8830e", size = 12951208, upload-time = "2026-01-08T19:12:09.218Z" }, + { url = "https://files.pythonhosted.org/packages/5c/5c/360a35cb7204b328b685d3129c08aca24765ff92b5a7efedbdd6c150d555/ruff-0.14.11-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6f6eb5c1c8033680f4172ea9c8d3706c156223010b8b97b05e82c59bdc774ee6", size = 13330075, upload-time = "2026-01-08T19:12:02.549Z" }, + { url = "https://files.pythonhosted.org/packages/1b/9e/0cc2f1be7a7d33cae541824cf3f95b4ff40d03557b575912b5b70273c9ec/ruff-0.14.11-py3-none-macosx_11_0_arm64.whl", hash = "sha256:f2fc34cc896f90080fca01259f96c566f74069a04b25b6205d55379d12a6855e", size = 12257809, upload-time = "2026-01-08T19:12:00.366Z" }, + { url = "https://files.pythonhosted.org/packages/a7/e5/5faab97c15bb75228d9f74637e775d26ac703cc2b4898564c01ab3637c02/ruff-0.14.11-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53386375001773ae812b43205d6064dae49ff0968774e6befe16a994fc233caa", size = 12678447, upload-time = "2026-01-08T19:12:13.899Z" }, + { url = "https://files.pythonhosted.org/packages/1b/33/e9767f60a2bef779fb5855cab0af76c488e0ce90f7bb7b8a45c8a2ba4178/ruff-0.14.11-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a697737dce1ca97a0a55b5ff0434ee7205943d4874d638fe3ae66166ff46edbe", size = 12758560, upload-time = "2026-01-08T19:11:42.55Z" }, + { url = "https://files.pythonhosted.org/packages/eb/84/4c6cf627a21462bb5102f7be2a320b084228ff26e105510cd2255ea868e5/ruff-0.14.11-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6845ca1da8ab81ab1dce755a32ad13f1db72e7fba27c486d5d90d65e04d17b8f", size = 13599296, upload-time = "2026-01-08T19:11:30.371Z" }, + { url = "https://files.pythonhosted.org/packages/88/e1/92b5ed7ea66d849f6157e695dc23d5d6d982bd6aa8d077895652c38a7cae/ruff-0.14.11-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e36ce2fd31b54065ec6f76cb08d60159e1b32bdf08507862e32f47e6dde8bcbf", size = 15048981, upload-time = "2026-01-08T19:12:04.742Z" }, + { url = "https://files.pythonhosted.org/packages/61/df/c1bd30992615ac17c2fb64b8a7376ca22c04a70555b5d05b8f717163cf9f/ruff-0.14.11-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:590bcc0e2097ecf74e62a5c10a6b71f008ad82eb97b0a0079e85defe19fe74d9", size = 14633183, upload-time = "2026-01-08T19:11:40.069Z" }, + { url = "https://files.pythonhosted.org/packages/04/e9/fe552902f25013dd28a5428a42347d9ad20c4b534834a325a28305747d64/ruff-0.14.11-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:53fe71125fc158210d57fe4da26e622c9c294022988d08d9347ec1cf782adafe", size = 14050453, upload-time = "2026-01-08T19:11:37.555Z" }, + { url = "https://files.pythonhosted.org/packages/ae/93/f36d89fa021543187f98991609ce6e47e24f35f008dfe1af01379d248a41/ruff-0.14.11-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a35c9da08562f1598ded8470fcfef2afb5cf881996e6c0a502ceb61f4bc9c8a3", size = 13757889, upload-time = "2026-01-08T19:12:07.094Z" }, + { url = "https://files.pythonhosted.org/packages/b7/9f/c7fb6ecf554f28709a6a1f2a7f74750d400979e8cd47ed29feeaa1bd4db8/ruff-0.14.11-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:0f3727189a52179393ecf92ec7057c2210203e6af2676f08d92140d3e1ee72c1", size = 13955832, upload-time = "2026-01-08T19:11:55.064Z" }, + { url = "https://files.pythonhosted.org/packages/db/a0/153315310f250f76900a98278cf878c64dfb6d044e184491dd3289796734/ruff-0.14.11-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:eb09f849bd37147a789b85995ff734a6c4a095bed5fd1608c4f56afc3634cde2", size = 12586522, upload-time = "2026-01-08T19:11:35.356Z" }, + { url = "https://files.pythonhosted.org/packages/2f/2b/a73a2b6e6d2df1d74bf2b78098be1572191e54bec0e59e29382d13c3adc5/ruff-0.14.11-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:c61782543c1231bf71041461c1f28c64b961d457d0f238ac388e2ab173d7ecb7", size = 12724637, upload-time = "2026-01-08T19:11:47.796Z" }, + { url = "https://files.pythonhosted.org/packages/f0/41/09100590320394401cd3c48fc718a8ba71c7ddb1ffd07e0ad6576b3a3df2/ruff-0.14.11-py3-none-musllinux_1_2_i686.whl", hash = "sha256:82ff352ea68fb6766140381748e1f67f83c39860b6446966cff48a315c3e2491", size = 13145837, upload-time = "2026-01-08T19:11:32.87Z" }, + { url = "https://files.pythonhosted.org/packages/3b/d8/e035db859d1d3edf909381eb8ff3e89a672d6572e9454093538fe6f164b0/ruff-0.14.11-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:728e56879df4ca5b62a9dde2dd0eb0edda2a55160c0ea28c4025f18c03f86984", size = 13850469, upload-time = "2026-01-08T19:12:11.694Z" }, + { url = "https://files.pythonhosted.org/packages/4e/02/bb3ff8b6e6d02ce9e3740f4c17dfbbfb55f34c789c139e9cd91985f356c7/ruff-0.14.11-py3-none-win32.whl", hash = "sha256:337c5dd11f16ee52ae217757d9b82a26400be7efac883e9e852646f1557ed841", size = 12851094, upload-time = "2026-01-08T19:11:45.163Z" }, + { url = "https://files.pythonhosted.org/packages/58/f1/90ddc533918d3a2ad628bc3044cdfc094949e6d4b929220c3f0eb8a1c998/ruff-0.14.11-py3-none-win_amd64.whl", hash = "sha256:f981cea63d08456b2c070e64b79cb62f951aa1305282974d4d5216e6e0178ae6", size = 14001379, upload-time = "2026-01-08T19:11:52.591Z" }, + { url = "https://files.pythonhosted.org/packages/c4/1c/1dbe51782c0e1e9cfce1d1004752672d2d4629ea46945d19d731ad772b3b/ruff-0.14.11-py3-none-win_arm64.whl", hash = "sha256:649fb6c9edd7f751db276ef42df1f3df41c38d67d199570ae2a7bd6cbc3590f0", size = 12938644, upload-time = "2026-01-08T19:11:50.027Z" }, ] [[package]]