Spaces:
Sleeping
feat: add comprehensive CI/CD automation and modern Python tooling
Browse files- Add pre-commit configuration with ruff (replaces black/isort/flake8), mypy, and bandit
- Add GitHub Actions workflows for CI testing and automated HF Spaces deployment
- Add pyproject.toml with modern Python packaging and centralized tool configuration
- Update requirements.txt with development dependencies including ruff and pytest plugins
- Add comprehensive ruff rule set: pycodestyle, pyflakes, bugbear, comprehensions, pyupgrade
- Configure multi-Python version testing (3.10, 3.11, 3.12) with parallel execution
- Add security scanning with bandit and Trivy vulnerability scanner
- Update README with development workflow and CI/CD pipeline documentation
Performance improvements:
- Ruff provides 10-100x faster linting compared to traditional tools
- pytest-xdist enables parallel test execution
- Single tool replaces multiple separate linters
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- .github/workflows/ci.yml +112 -0
- .github/workflows/deploy.yml +25 -0
- .pre-commit-config.yaml +31 -0
- README.md +31 -0
- pyproject.toml +138 -0
- requirements.txt +7 -1
|
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: CI
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
push:
|
| 5 |
+
branches: [ main, develop ]
|
| 6 |
+
pull_request:
|
| 7 |
+
branches: [ main ]
|
| 8 |
+
|
| 9 |
+
jobs:
|
| 10 |
+
test:
|
| 11 |
+
runs-on: ubuntu-latest
|
| 12 |
+
strategy:
|
| 13 |
+
matrix:
|
| 14 |
+
python-version: [3.10.x, 3.11.x, 3.12.x]
|
| 15 |
+
|
| 16 |
+
steps:
|
| 17 |
+
- uses: actions/checkout@v4
|
| 18 |
+
|
| 19 |
+
- name: Set up Python ${{ matrix.python-version }}
|
| 20 |
+
uses: actions/setup-python@v4
|
| 21 |
+
with:
|
| 22 |
+
python-version: ${{ matrix.python-version }}
|
| 23 |
+
|
| 24 |
+
- name: Cache pip packages
|
| 25 |
+
uses: actions/cache@v3
|
| 26 |
+
with:
|
| 27 |
+
path: ~/.cache/pip
|
| 28 |
+
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
|
| 29 |
+
restore-keys: |
|
| 30 |
+
${{ runner.os }}-pip-
|
| 31 |
+
|
| 32 |
+
- name: Install dependencies
|
| 33 |
+
run: |
|
| 34 |
+
python -m pip install --upgrade pip
|
| 35 |
+
pip install -r requirements.txt
|
| 36 |
+
pip install pytest-cov pytest-xdist
|
| 37 |
+
|
| 38 |
+
- name: Run tests with coverage
|
| 39 |
+
run: |
|
| 40 |
+
python -m pytest tests/ -v --cov=domains --cov=ui --cov-report=xml --cov-report=html -n auto
|
| 41 |
+
|
| 42 |
+
- name: Upload coverage to Codecov
|
| 43 |
+
uses: codecov/codecov-action@v3
|
| 44 |
+
with:
|
| 45 |
+
file: ./coverage.xml
|
| 46 |
+
flags: unittests
|
| 47 |
+
name: codecov-umbrella
|
| 48 |
+
|
| 49 |
+
lint:
|
| 50 |
+
runs-on: ubuntu-latest
|
| 51 |
+
|
| 52 |
+
steps:
|
| 53 |
+
- uses: actions/checkout@v4
|
| 54 |
+
|
| 55 |
+
- name: Set up Python
|
| 56 |
+
uses: actions/setup-python@v4
|
| 57 |
+
with:
|
| 58 |
+
python-version: 3.11.x
|
| 59 |
+
|
| 60 |
+
- name: Install dependencies
|
| 61 |
+
run: |
|
| 62 |
+
python -m pip install --upgrade pip
|
| 63 |
+
pip install ruff mypy bandit
|
| 64 |
+
|
| 65 |
+
- name: Run Ruff linter
|
| 66 |
+
run: ruff check .
|
| 67 |
+
|
| 68 |
+
- name: Run Ruff formatter
|
| 69 |
+
run: ruff format --check .
|
| 70 |
+
|
| 71 |
+
- name: Run mypy
|
| 72 |
+
run: mypy . --ignore-missing-imports
|
| 73 |
+
|
| 74 |
+
- name: Run bandit
|
| 75 |
+
run: bandit -r . -f json -o bandit-report.json || true
|
| 76 |
+
|
| 77 |
+
pre-commit:
|
| 78 |
+
runs-on: ubuntu-latest
|
| 79 |
+
|
| 80 |
+
steps:
|
| 81 |
+
- uses: actions/checkout@v4
|
| 82 |
+
|
| 83 |
+
- name: Set up Python
|
| 84 |
+
uses: actions/setup-python@v4
|
| 85 |
+
with:
|
| 86 |
+
python-version: 3.11.x
|
| 87 |
+
|
| 88 |
+
- name: Install pre-commit
|
| 89 |
+
run: pip install pre-commit
|
| 90 |
+
|
| 91 |
+
- name: Run pre-commit
|
| 92 |
+
run: pre-commit run --all-files
|
| 93 |
+
|
| 94 |
+
security:
|
| 95 |
+
runs-on: ubuntu-latest
|
| 96 |
+
|
| 97 |
+
steps:
|
| 98 |
+
- uses: actions/checkout@v4
|
| 99 |
+
|
| 100 |
+
- name: Run Trivy vulnerability scanner
|
| 101 |
+
uses: aquasecurity/trivy-action@master
|
| 102 |
+
with:
|
| 103 |
+
scan-type: 'fs'
|
| 104 |
+
scan-ref: '.'
|
| 105 |
+
format: 'sarif'
|
| 106 |
+
output: 'trivy-results.sarif'
|
| 107 |
+
|
| 108 |
+
- name: Upload Trivy scan results to GitHub Security tab
|
| 109 |
+
uses: github/codeql-action/upload-sarif@v2
|
| 110 |
+
if: always()
|
| 111 |
+
with:
|
| 112 |
+
sarif_file: 'trivy-results.sarif'
|
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Deploy to Hugging Face Spaces
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
push:
|
| 5 |
+
branches: [ main ]
|
| 6 |
+
workflow_dispatch:
|
| 7 |
+
|
| 8 |
+
jobs:
|
| 9 |
+
deploy:
|
| 10 |
+
runs-on: ubuntu-latest
|
| 11 |
+
|
| 12 |
+
steps:
|
| 13 |
+
- uses: actions/checkout@v4
|
| 14 |
+
with:
|
| 15 |
+
fetch-depth: 0
|
| 16 |
+
lfs: true
|
| 17 |
+
|
| 18 |
+
- name: Push to Hugging Face Spaces
|
| 19 |
+
env:
|
| 20 |
+
HF_TOKEN: ${{ secrets.HF_TOKEN }}
|
| 21 |
+
run: |
|
| 22 |
+
git config --global user.email "action@github.com"
|
| 23 |
+
git config --global user.name "GitHub Action"
|
| 24 |
+
git remote add hf https://thompsonson:$HF_TOKEN@hf.co/spaces/thompsonson/bayesian_game
|
| 25 |
+
git push hf main --force
|
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
repos:
|
| 2 |
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
| 3 |
+
rev: v4.4.0
|
| 4 |
+
hooks:
|
| 5 |
+
- id: trailing-whitespace
|
| 6 |
+
- id: end-of-file-fixer
|
| 7 |
+
- id: check-yaml
|
| 8 |
+
- id: check-added-large-files
|
| 9 |
+
- id: check-merge-conflict
|
| 10 |
+
- id: debug-statements
|
| 11 |
+
|
| 12 |
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
| 13 |
+
rev: v0.1.9
|
| 14 |
+
hooks:
|
| 15 |
+
- id: ruff
|
| 16 |
+
args: [--fix, --exit-non-zero-on-fix]
|
| 17 |
+
- id: ruff-format
|
| 18 |
+
|
| 19 |
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
| 20 |
+
rev: v1.8.0
|
| 21 |
+
hooks:
|
| 22 |
+
- id: mypy
|
| 23 |
+
additional_dependencies: [types-all]
|
| 24 |
+
args: [--ignore-missing-imports]
|
| 25 |
+
|
| 26 |
+
- repo: https://github.com/pycqa/bandit
|
| 27 |
+
rev: 1.7.5
|
| 28 |
+
hooks:
|
| 29 |
+
- id: bandit
|
| 30 |
+
args: [-r, ., -f, json, -o, bandit-report.json]
|
| 31 |
+
exclude: ^tests/
|
|
@@ -85,6 +85,11 @@ uv pip install -r requirements.txt
|
|
| 85 |
pip install -r requirements.txt
|
| 86 |
```
|
| 87 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
### Running the Game
|
| 89 |
|
| 90 |
**Launch the interactive web interface:**
|
|
@@ -210,6 +215,32 @@ This implementation demonstrates:
|
|
| 210 |
- `numpy`: Numerical computations for Bayesian inference
|
| 211 |
- `matplotlib`: Belief distribution visualization
|
| 212 |
- `pytest`: Testing framework
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 213 |
|
| 214 |
### Design Principles
|
| 215 |
1. **Pure Functions**: Domains contain pure, testable functions
|
|
|
|
| 85 |
pip install -r requirements.txt
|
| 86 |
```
|
| 87 |
|
| 88 |
+
4. **Set up pre-commit hooks (optional for development):**
|
| 89 |
+
```bash
|
| 90 |
+
pre-commit install
|
| 91 |
+
```
|
| 92 |
+
|
| 93 |
### Running the Game
|
| 94 |
|
| 95 |
**Launch the interactive web interface:**
|
|
|
|
| 215 |
- `numpy`: Numerical computations for Bayesian inference
|
| 216 |
- `matplotlib`: Belief distribution visualization
|
| 217 |
- `pytest`: Testing framework
|
| 218 |
+
- `pre-commit`: Code quality automation
|
| 219 |
+
- `ruff`: Fast Python linter and formatter (replaces Black, isort, flake8)
|
| 220 |
+
|
| 221 |
+
### Development Workflow
|
| 222 |
+
```bash
|
| 223 |
+
# Install pre-commit hooks
|
| 224 |
+
pre-commit install
|
| 225 |
+
|
| 226 |
+
# Run pre-commit manually
|
| 227 |
+
pre-commit run --all-files
|
| 228 |
+
|
| 229 |
+
# Run tests with coverage
|
| 230 |
+
python -m pytest tests/ --cov=domains --cov=ui --cov-report=html
|
| 231 |
+
|
| 232 |
+
# Code formatting and linting (automatic with pre-commit)
|
| 233 |
+
ruff check --fix .
|
| 234 |
+
ruff format .
|
| 235 |
+
mypy .
|
| 236 |
+
```
|
| 237 |
+
|
| 238 |
+
### CI/CD Pipeline
|
| 239 |
+
- **GitHub Actions**: Automated testing on Python 3.10, 3.11, 3.12
|
| 240 |
+
- **Pre-commit hooks**: Code quality checks (Ruff, mypy, bandit)
|
| 241 |
+
- **Test coverage**: Comprehensive coverage reporting
|
| 242 |
+
- **Security scanning**: Trivy vulnerability scanner
|
| 243 |
+
- **Auto-deployment**: Pushes to Hugging Face Spaces on main branch
|
| 244 |
|
| 245 |
### Design Principles
|
| 246 |
1. **Pure Functions**: Domains contain pure, testable functions
|
|
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[build-system]
|
| 2 |
+
requires = ["setuptools>=45", "wheel", "setuptools_scm[toml]>=6.2"]
|
| 3 |
+
build-backend = "setuptools.build_meta"
|
| 4 |
+
|
| 5 |
+
[project]
|
| 6 |
+
name = "bayesian-game"
|
| 7 |
+
description = "Interactive Bayesian inference game with domain-driven design"
|
| 8 |
+
readme = "README.md"
|
| 9 |
+
license = {text = "MIT"}
|
| 10 |
+
authors = [
|
| 11 |
+
{name = "Thompson", email = "thompsonson@example.com"},
|
| 12 |
+
]
|
| 13 |
+
classifiers = [
|
| 14 |
+
"Development Status :: 4 - Beta",
|
| 15 |
+
"Intended Audience :: Education",
|
| 16 |
+
"License :: OSI Approved :: MIT License",
|
| 17 |
+
"Programming Language :: Python :: 3",
|
| 18 |
+
"Programming Language :: Python :: 3.10",
|
| 19 |
+
"Programming Language :: Python :: 3.11",
|
| 20 |
+
"Programming Language :: Python :: 3.12",
|
| 21 |
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
| 22 |
+
"Topic :: Education",
|
| 23 |
+
]
|
| 24 |
+
requires-python = ">=3.10"
|
| 25 |
+
dependencies = [
|
| 26 |
+
"gradio>=4.0.0",
|
| 27 |
+
"numpy>=1.21.0",
|
| 28 |
+
"matplotlib>=3.5.0",
|
| 29 |
+
]
|
| 30 |
+
dynamic = ["version"]
|
| 31 |
+
|
| 32 |
+
[project.optional-dependencies]
|
| 33 |
+
dev = [
|
| 34 |
+
"pytest>=7.0.0",
|
| 35 |
+
"pytest-cov>=4.0.0",
|
| 36 |
+
"pytest-xdist>=3.0.0",
|
| 37 |
+
"pre-commit>=3.0.0",
|
| 38 |
+
"ruff>=0.1.0",
|
| 39 |
+
"mypy>=1.0.0",
|
| 40 |
+
"bandit>=1.7.0",
|
| 41 |
+
]
|
| 42 |
+
|
| 43 |
+
[project.urls]
|
| 44 |
+
Homepage = "https://github.com/thompsonson/bayesian_game"
|
| 45 |
+
Repository = "https://github.com/thompsonson/bayesian_game"
|
| 46 |
+
"Bug Tracker" = "https://github.com/thompsonson/bayesian_game/issues"
|
| 47 |
+
"Hugging Face Space" = "https://huggingface.co/spaces/thompsonson/bayesian_game"
|
| 48 |
+
|
| 49 |
+
[tool.setuptools_scm]
|
| 50 |
+
|
| 51 |
+
[tool.ruff]
|
| 52 |
+
target-version = "py310"
|
| 53 |
+
line-length = 88
|
| 54 |
+
select = [
|
| 55 |
+
"E", # pycodestyle errors
|
| 56 |
+
"W", # pycodestyle warnings
|
| 57 |
+
"F", # pyflakes
|
| 58 |
+
"I", # isort
|
| 59 |
+
"B", # flake8-bugbear
|
| 60 |
+
"C4", # flake8-comprehensions
|
| 61 |
+
"UP", # pyupgrade
|
| 62 |
+
"ARG", # flake8-unused-arguments
|
| 63 |
+
"SIM", # flake8-simplify
|
| 64 |
+
"TCH", # flake8-type-checking
|
| 65 |
+
"PTH", # flake8-use-pathlib
|
| 66 |
+
"ERA", # eradicate
|
| 67 |
+
"PL", # pylint
|
| 68 |
+
"RUF", # ruff-specific rules
|
| 69 |
+
]
|
| 70 |
+
ignore = [
|
| 71 |
+
"E501", # line too long, handled by formatter
|
| 72 |
+
"B008", # do not perform function calls in argument defaults
|
| 73 |
+
"C901", # too complex
|
| 74 |
+
"PLR0913", # too many arguments
|
| 75 |
+
"PLR0915", # too many statements
|
| 76 |
+
]
|
| 77 |
+
|
| 78 |
+
[tool.ruff.per-file-ignores]
|
| 79 |
+
"tests/**/*" = ["PLR2004", "S101", "ARG001"]
|
| 80 |
+
|
| 81 |
+
[tool.ruff.format]
|
| 82 |
+
quote-style = "double"
|
| 83 |
+
indent-style = "space"
|
| 84 |
+
skip-magic-trailing-comma = false
|
| 85 |
+
line-ending = "auto"
|
| 86 |
+
|
| 87 |
+
[tool.mypy]
|
| 88 |
+
python_version = "3.10"
|
| 89 |
+
check_untyped_defs = true
|
| 90 |
+
disallow_any_generics = true
|
| 91 |
+
disallow_incomplete_defs = true
|
| 92 |
+
disallow_untyped_defs = true
|
| 93 |
+
no_implicit_optional = true
|
| 94 |
+
warn_redundant_casts = true
|
| 95 |
+
warn_unused_ignores = true
|
| 96 |
+
warn_return_any = true
|
| 97 |
+
strict_equality = true
|
| 98 |
+
|
| 99 |
+
[[tool.mypy.overrides]]
|
| 100 |
+
module = [
|
| 101 |
+
"matplotlib.*",
|
| 102 |
+
"gradio.*",
|
| 103 |
+
]
|
| 104 |
+
ignore_missing_imports = true
|
| 105 |
+
|
| 106 |
+
[tool.pytest.ini_options]
|
| 107 |
+
minversion = "7.0"
|
| 108 |
+
addopts = "-ra -q --strict-markers --strict-config"
|
| 109 |
+
testpaths = ["tests"]
|
| 110 |
+
python_files = ["test_*.py"]
|
| 111 |
+
python_classes = ["Test*"]
|
| 112 |
+
python_functions = ["test_*"]
|
| 113 |
+
markers = [
|
| 114 |
+
"slow: marks tests as slow",
|
| 115 |
+
"integration: marks tests as integration tests",
|
| 116 |
+
"unit: marks tests as unit tests",
|
| 117 |
+
]
|
| 118 |
+
|
| 119 |
+
[tool.coverage.run]
|
| 120 |
+
source = ["domains", "ui"]
|
| 121 |
+
omit = [
|
| 122 |
+
"tests/*",
|
| 123 |
+
"app.py",
|
| 124 |
+
]
|
| 125 |
+
|
| 126 |
+
[tool.coverage.report]
|
| 127 |
+
exclude_lines = [
|
| 128 |
+
"pragma: no cover",
|
| 129 |
+
"def __repr__",
|
| 130 |
+
"raise AssertionError",
|
| 131 |
+
"raise NotImplementedError",
|
| 132 |
+
"if __name__ == .__main__.:",
|
| 133 |
+
"if TYPE_CHECKING:",
|
| 134 |
+
]
|
| 135 |
+
|
| 136 |
+
[tool.bandit]
|
| 137 |
+
exclude_dirs = ["tests"]
|
| 138 |
+
skips = ["B101", "B601"]
|
|
@@ -1,4 +1,10 @@
|
|
| 1 |
gradio>=4.0.0
|
| 2 |
numpy>=1.21.0
|
| 3 |
matplotlib>=3.5.0
|
| 4 |
-
pytest>=7.0.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
gradio>=4.0.0
|
| 2 |
numpy>=1.21.0
|
| 3 |
matplotlib>=3.5.0
|
| 4 |
+
pytest>=7.0.0
|
| 5 |
+
pytest-cov>=4.0.0
|
| 6 |
+
pytest-xdist>=3.0.0
|
| 7 |
+
pre-commit>=3.0.0
|
| 8 |
+
ruff>=0.1.0
|
| 9 |
+
mypy>=1.0.0
|
| 10 |
+
bandit>=1.7.0
|