Skip to content
This repository was archived by the owner on Apr 7, 2026. It is now read-only.

Commit f6e3719

Browse files
committed
feat: Integrate uv package manager and add to tool explanations
- Add use_uv option to cookiecutter.json - Update template README, Makefile, CONTRIBUTING.md to support uv - Add uv commands to generated project CI workflow - Update pyproject.toml and .gitignore for uv compatibility - Add uv to main README tool explanations table - Verified uv sync, uv run, and all dev tools work in generated projects
1 parent 42874e2 commit f6e3719

8 files changed

Lines changed: 292 additions & 1 deletion

File tree

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ This template provides extensive customization options through `cookiecutter.jso
1919
- `license`: License type (MIT, Apache-2.0, BSD-3-Clause, GPL-3.0, etc.)
2020
2121
### Development Tools
22+
- `use_uv`: Modern Python package manager (recommended: y)
2223
- `use_ruff`: Ultra-fast linter and formatter (recommended: y)
2324
- `use_mypy`: Static type checking (recommended: y)
2425
- `use_pytest`: Modern testing framework (recommended: y)
@@ -70,6 +71,7 @@ This template creates a Python package with modern best practices:
7071
- **Safety** - Dependency vulnerability scanning
7172
7273
### 🔧 **Development Tools**
74+
- **uv** - Ultra-fast Python package manager and project manager (optional)
7375
- **pre-commit** - Automated code quality checks before commits
7476
- **Tox** - Testing across multiple Python versions
7577
- **Nox** - Flexible task automation (alternative to Tox)
@@ -183,6 +185,7 @@ cookiecutter https://github.com/s-celles/cookiecutter-python-package.git
183185

184186
| Tool | Purpose | Why Important |
185187
|------|---------|---------------|
188+
| **uv** | Package Management | Ultra-fast Python package installer and resolver |
186189
| **Ruff** | Linting & Formatting | 10-100x faster than flake8/black, combines multiple tools |
187190
| **MyPy** | Type Checking | Catches bugs early, improves code documentation |
188191
| **pytest** | Testing | Modern features, fixtures, parametrization |

cookiecutter.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737

3838
"_project_management": "---",
3939
"_comment_project": "Project management tools",
40+
"use_uv": ["y", "n"],
4041
"use_tox": ["n", "y"],
4142
"use_nox": ["n", "y"],
4243
"use_commitizen": ["n", "y"],

{{cookiecutter.project_slug}}/.github/workflows/ci.yml

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,17 @@ jobs:
3232
with:
3333
python-version: {% raw %}${{ matrix.python-version }}{% endraw %}
3434

35+
{%- if cookiecutter.use_uv == "y" %}
36+
- name: Install uv
37+
uses: astral-sh/setup-uv@v4
38+
with:
39+
version: "latest"
40+
41+
- name: Install dependencies
42+
run: |
43+
uv sync --all-extras
44+
45+
{%- else %}
3546
- name: Cache pip packages
3647
uses: actions/cache@v4
3748
with:
@@ -45,34 +56,57 @@ jobs:
4556
python -m pip install --upgrade pip
4657
pip install -e ".[dev]"
4758
59+
{%- endif %}
60+
4861
{%- if cookiecutter.use_ruff == "y" %}
4962
- name: Lint with Ruff
5063
run: |
64+
{%- if cookiecutter.use_uv == "y" %}
65+
uv run ruff check .
66+
uv run ruff format --check .
67+
{%- else %}
5168
ruff check .
5269
ruff format --check .
5370
{%- endif %}
71+
{%- endif %}
5472

5573
{%- if cookiecutter.use_mypy == "y" %}
5674
- name: Type check with MyPy
5775
run: |
76+
{%- if cookiecutter.use_uv == "y" %}
77+
uv run mypy src/{{ cookiecutter.project_slug }}
78+
{%- else %}
5879
mypy src/{{ cookiecutter.project_slug }}
5980
{%- endif %}
81+
{%- endif %}
6082

6183
{%- if cookiecutter.use_bandit == "y" %}
6284
- name: Security check with Bandit
6385
run: |
86+
{%- if cookiecutter.use_uv == "y" %}
87+
uv run bandit -r src/{{ cookiecutter.project_slug }}/
88+
{%- else %}
6489
bandit -r src/{{ cookiecutter.project_slug }}/
6590
{%- endif %}
91+
{%- endif %}
6692

6793
{%- if cookiecutter.use_pytest == "y" %}
6894
- name: Test with pytest
6995
run: |
7096
{%- if cookiecutter.use_coverage == "y" %}
97+
{%- if cookiecutter.use_uv == "y" %}
98+
uv run pytest --cov=src/{{ cookiecutter.project_slug }} --cov-report=xml --cov-report=html
99+
{%- else %}
71100
pytest --cov=src/{{ cookiecutter.project_slug }} --cov-report=xml --cov-report=html
101+
{%- endif %}
102+
{%- else %}
103+
{%- if cookiecutter.use_uv == "y" %}
104+
uv run pytest
72105
{%- else %}
73106
pytest
74107
{%- endif %}
75108
{%- endif %}
109+
{%- endif %}
76110

77111
{%- if cookiecutter.use_coverage == "y" and cookiecutter.use_codecov == "y" %}
78112
- name: Upload coverage to Codecov
@@ -94,14 +128,25 @@ jobs:
94128
with:
95129
python-version: '3.11'
96130

131+
{%- if cookiecutter.use_uv == "y" %}
132+
- name: Install uv
133+
uses: astral-sh/setup-uv@v4
134+
with:
135+
version: "latest"
136+
137+
- name: Build package
138+
run: uv build
139+
140+
{%- else %}
97141
- name: Install build dependencies
98142
run: |
99143
python -m pip install --upgrade pip
100144
pip install build
101145
102146
- name: Build package
103147
run: python -m build
104-
148+
149+
{%- endif %}
105150
- name: Upload build artifacts
106151
uses: actions/upload-artifact@v4
107152
with:

{{cookiecutter.project_slug}}/.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,12 @@ ENV/
104104
env.bak/
105105
venv.bak/
106106

107+
{%- if cookiecutter.use_uv == "y" %}
108+
# uv
109+
.uv_cache/
110+
uv.lock
111+
{%- endif %}
112+
107113
# Spyder project settings
108114
.spyderproject
109115
.spyproject

{{cookiecutter.project_slug}}/CONTRIBUTING.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,37 @@ Thank you for your interest in contributing to {{ cookiecutter.project_name }}!
1818
cd {{ cookiecutter.project_slug }}
1919
```
2020

21+
{%- if cookiecutter.use_uv == "y" %}
22+
23+
2. **Set up development environment with uv (Recommended)**
24+
25+
Install [uv](https://docs.astral.sh/uv/) if you haven't already:
26+
```bash
27+
# On macOS and Linux
28+
curl -LsSf https://astral.sh/uv/install.sh | sh
29+
30+
# On Windows
31+
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
32+
```
33+
34+
Then set up the project:
35+
```bash
36+
# Install dependencies and create virtual environment
37+
uv sync
38+
39+
# Install the package in development mode
40+
uv pip install -e ".[dev]"
41+
```
42+
43+
3. **Alternative: Create a virtual environment with pip**
44+
```bash
45+
python -m venv venv
46+
source venv/bin/activate # On Windows: venv\Scripts\activate
47+
pip install -e ".[dev]"
48+
```
49+
50+
{%- else %}
51+
2152
2. **Create a virtual environment**
2253
```bash
2354
python -m venv venv
@@ -29,11 +60,17 @@ Thank you for your interest in contributing to {{ cookiecutter.project_name }}!
2960
pip install -e ".[dev]"
3061
```
3162

63+
{%- endif %}
64+
3265
{%- if cookiecutter.use_pre_commit == "y" %}
3366

3467
4. **Install pre-commit hooks**
3568
```bash
69+
{%- if cookiecutter.use_uv == "y" %}
70+
uv run pre-commit install
71+
{%- else %}
3672
pre-commit install
73+
{%- endif %}
3774
```
3875
{%- endif %}
3976
@@ -46,13 +83,23 @@ We use [Ruff](https://github.com/astral-sh/ruff) for code formatting and linting
4683
4784
```bash
4885
# Format code
86+
{%- if cookiecutter.use_uv == "y" %}
87+
uv run ruff format
88+
89+
# Check for linting issues
90+
uv run ruff check
91+
92+
# Fix auto-fixable issues
93+
uv run ruff check --fix
94+
{%- else %}
4995
ruff format
5096
5197
# Check for linting issues
5298
ruff check
5399
54100
# Fix auto-fixable issues
55101
ruff check --fix
102+
{%- endif %}
56103
```
57104
{%- endif %}
58105
@@ -63,7 +110,11 @@ ruff check --fix
63110
We use [MyPy](https://mypy.readthedocs.io/) for static type checking:
64111
65112
```bash
113+
{%- if cookiecutter.use_uv == "y" %}
114+
uv run mypy src/{{ cookiecutter.project_slug }}
115+
{%- else %}
66116
mypy src/{{ cookiecutter.project_slug }}
117+
{%- endif %}
67118
```
68119
{%- endif %}
69120
@@ -75,6 +126,18 @@ We use [pytest](https://pytest.org/) for testing:
75126
76127
```bash
77128
# Run all tests
129+
{%- if cookiecutter.use_uv == "y" %}
130+
uv run pytest
131+
132+
# Run with coverage
133+
uv run pytest --cov={{ cookiecutter.project_slug }}
134+
135+
# Run specific test file
136+
uv run pytest tests/test_core.py
137+
138+
# Run tests in parallel
139+
uv run pytest -n auto
140+
{%- else %}
78141
pytest
79142
80143
# Run with coverage
@@ -85,6 +148,7 @@ pytest tests/test_core.py
85148
86149
# Run tests in parallel
87150
pytest -n auto
151+
{%- endif %}
88152
```
89153
90154
Make sure to write tests for any new functionality you add.
@@ -97,7 +161,11 @@ Make sure to write tests for any new functionality you add.
97161
We use [Bandit](https://bandit.readthedocs.io/) for security checks:
98162
99163
```bash
164+
{%- if cookiecutter.use_uv == "y" %}
165+
uv run bandit -r src/{{ cookiecutter.project_slug }}/
166+
{%- else %}
100167
bandit -r src/{{ cookiecutter.project_slug }}/
168+
{%- endif %}
101169
```
102170
{%- endif %}
103171

{{cookiecutter.project_slug}}/Makefile

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,46 +5,91 @@ help: ## Show this help message
55
@echo "Available commands:"
66
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
77

8+
{%- if cookiecutter.use_uv == "y" %}
9+
install: ## Install the package
10+
uv pip install -e .
11+
12+
install-dev: ## Install package with development dependencies
13+
uv sync
14+
15+
{%- else %}
816
install: ## Install the package
917
pip install -e .
1018

1119
install-dev: ## Install package with development dependencies
1220
pip install -e ".[dev]"
1321

22+
{%- endif %}
23+
1424
{%- if cookiecutter.use_pytest == "y" %}
25+
{%- if cookiecutter.use_uv == "y" %}
26+
test: ## Run tests
27+
uv run pytest
28+
29+
test-cov: ## Run tests with coverage
30+
uv run pytest --cov={{ cookiecutter.project_slug }} --cov-report=html --cov-report=term-missing
31+
{%- else %}
1532
test: ## Run tests
1633
pytest
1734

1835
test-cov: ## Run tests with coverage
1936
pytest --cov={{ cookiecutter.project_slug }} --cov-report=html --cov-report=term-missing
2037
{%- endif %}
38+
{%- endif %}
2139

2240
{%- if cookiecutter.use_ruff == "y" %}
41+
{%- if cookiecutter.use_uv == "y" %}
42+
lint: ## Run linting
43+
uv run ruff check .
44+
45+
format: ## Format code
46+
uv run ruff format .
47+
uv run ruff check --fix .
48+
{%- else %}
2349
lint: ## Run linting
2450
ruff check .
2551

2652
format: ## Format code
2753
ruff format .
2854
ruff check --fix .
2955
{%- endif %}
56+
{%- endif %}
3057

3158
{%- if cookiecutter.use_mypy == "y" %}
59+
{%- if cookiecutter.use_uv == "y" %}
60+
type-check: ## Run type checking
61+
uv run mypy src/{{ cookiecutter.project_slug }}
62+
{%- else %}
3263
type-check: ## Run type checking
3364
mypy src/{{ cookiecutter.project_slug }}
3465
{%- endif %}
66+
{%- endif %}
3567

3668
{%- if cookiecutter.use_bandit == "y" %}
69+
{%- if cookiecutter.use_uv == "y" %}
70+
security: ## Run security checks
71+
uv run bandit -r src/{{ cookiecutter.project_slug }}/
72+
{%- else %}
3773
security: ## Run security checks
3874
bandit -r src/{{ cookiecutter.project_slug }}/
3975
{%- endif %}
76+
{%- endif %}
4077

4178
{%- if cookiecutter.use_pre_commit == "y" %}
79+
{%- if cookiecutter.use_uv == "y" %}
80+
pre-commit: ## Run pre-commit hooks
81+
uv run pre-commit run --all-files
82+
83+
install-hooks: ## Install pre-commit hooks
84+
uv run pre-commit install
85+
{%- else %}
4286
pre-commit: ## Run pre-commit hooks
4387
pre-commit run --all-files
4488

4589
install-hooks: ## Install pre-commit hooks
4690
pre-commit install
4791
{%- endif %}
92+
{%- endif %}
4893

4994
clean: ## Clean build artifacts
5095
rm -rf build/
@@ -53,11 +98,19 @@ clean: ## Clean build artifacts
5398
rm -rf .pytest_cache/
5499
rm -rf .coverage
55100
rm -rf htmlcov/
101+
{%- if cookiecutter.use_uv == "y" %}
102+
rm -rf .venv/
103+
{%- endif %}
56104
find . -type d -name __pycache__ -delete
57105
find . -type f -name "*.pyc" -delete
58106

107+
{%- if cookiecutter.use_uv == "y" %}
108+
build: ## Build package
109+
uv build
110+
{%- else %}
59111
build: ## Build package
60112
python -m build
113+
{%- endif %}
61114

62115
{%- if cookiecutter.use_mkdocs == "y" %}
63116
docs: ## Build documentation

0 commit comments

Comments
 (0)