Skip to content

Commit b01687b

Browse files
committed
Poetry replaced with uv.
1 parent 5a13aae commit b01687b

File tree

9 files changed

+108
-93
lines changed

9 files changed

+108
-93
lines changed

README.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@
77

88
## Usage
99

10-
⚠️ [Git](https://git-scm.com/downloads), [Python](https://www.python.org/) and [Poetry](https://python-poetry.org/) must be installed and accessible ⚠️
11-
12-
Poetry version must be greater or equal than 1.1.8. Otherwise it won't be able to install SQLAlchemy.
10+
⚠️ [Git](https://git-scm.com/downloads), [Python](https://www.python.org/) and [UV](https://docs.astral.sh/uv/) must be installed and accessible ⚠️
1311

1412
<div align="center">
1513
<a href="https://asciinema.org/a/ig0oi0fOq1hxqnW5X49XaaHIT" target="_blank"><img src="https://asciinema.org/a/ig0oi0fOq1hxqnW5X49XaaHIT.svg" /></a>
@@ -29,7 +27,7 @@ docker-compose up --build
2927

3028
If you want to install it from sources, try this:
3129
```shell
32-
python3 -m pip install poetry
30+
python3 -m pip install uv
3331
python3 -m pip install .
3432
python3 -m fastapi_template
3533
```

fastapi_template/cli.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,7 @@ def run_command(callback: Callable[[BuilderContext], None]) -> None:
699699
for menu in menus:
700700
cmd.params.extend(menu.get_cli_options())
701701
required_commands = {
702-
"poetry": "https://python-poetry.org/docs/#installation",
702+
"uv": "https://docs.astral.sh/uv/",
703703
"git": "https://git-scm.com/",
704704
}
705705
for prog, link in required_commands.items():

fastapi_template/template/hooks/post_gen_project.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,10 @@ def init_repo():
6161
cprint("Git repository initialized.", "green")
6262
subprocess.run(["git", "add", "."], stdout=subprocess.PIPE)
6363
cprint("Added files to index.", "green")
64-
subprocess.run(["poetry", "install", "-n"])
65-
subprocess.run(["poetry", "run", "pre-commit", "install"])
64+
subprocess.run(["uv", "sync"])
65+
subprocess.run(["uv", "run", "pre-commit", "install"])
6666
cprint("pre-commit installed.", "green")
67-
subprocess.run(["poetry", "run", "pre-commit", "run", "-a"])
67+
subprocess.run(["uv", "run", "pre-commit", "run", "-a"])
6868
subprocess.run(["git", "add", "."], stdout=subprocess.PIPE)
6969
subprocess.run(["git", "commit", "-m", "Initial commit"], stdout=subprocess.PIPE)
7070

fastapi_template/template/{{cookiecutter.project_name}}/.gitlab-ci.yml

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,21 @@ stages:
33

44
.test-template:
55
stage: test
6-
image: python:3.11.4-slim-bullseye
6+
image: ghcr.io/astral-sh/uv:0.9.12-python3.13-bookworm-slim
77
tags:
88
- kubernetes-runner
99
- docker-runner
1010
except:
1111
- tags
1212
before_script:
1313
- apt update && apt install -y git
14-
- pip install poetry==1.8.2
15-
- poetry config virtualenvs.create false
16-
- poetry install
14+
- uv sync
1715

1816
black:
1917
extends:
2018
- .test-template
2119
script:
22-
- pre-commit run black -a
20+
- pre-commit run ruff-format -a
2321

2422
ruff:
2523
extends:
@@ -31,5 +29,5 @@ mypy:
3129
extends:
3230
- .test-template
3331
script:
34-
- pre-commit run ruff -a
32+
- pre-commit run mypy -a
3533

fastapi_template/template/{{cookiecutter.project_name}}/.pre-commit-config.yaml

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,27 @@ repos:
55
- repo: local
66
hooks:
77

8-
- id: black
9-
name: Format with Black
10-
entry: poetry run black
8+
- id: ruff-format
9+
name: Format with Ruff
10+
entry: uv run ruff format
1111
language: system
12-
types: [python]
12+
pass_filenames: false
13+
always_run: true
1314

1415
- id: ruff
1516
name: Check with Ruff
16-
entry: poetry run ruff
17+
entry: uv run ruff
1718
language: system
1819
pass_filenames: false
1920
always_run: true
2021
args: ["check", "{{cookiecutter.project_name}}", "tests", "--fix"]
2122

2223
- id: mypy
2324
name: Validate types with MyPy
24-
entry: poetry run mypy
25+
entry: uv run mypy
2526
language: system
2627
types: [python]
2728
pass_filenames: false
2829
args:
2930
- "{{cookiecutter.project_name}}"
31+
- "tests"
Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
FROM python:3.11.4-slim-bullseye AS prod
1+
FROM ghcr.io/astral-sh/uv:0.9.12-bookworm AS uv
2+
3+
# -----------------------------------
4+
# STAGE 1: prod stage
5+
# Only install main dependencies
6+
# -----------------------------------
7+
FROM python:3.13-slim-bookworm AS prod
28

39
{%- if cookiecutter.db_info.name == "mysql" %}
410
RUN apt-get update && apt-get install -y \
@@ -15,33 +21,32 @@ RUN apt-get update && apt-get install -y \
1521
&& rm -rf /var/lib/apt/lists/*
1622
{%- endif %}
1723

24+
ENV UV_COMPILE_BYTECODE=1
25+
ENV UV_LINK_MODE=copy
26+
ENV UV_PROJECT_ENVIRONMENT=/usr/local
1827

19-
RUN pip install poetry==1.8.2
20-
21-
# Configuring poetry
22-
RUN poetry config virtualenvs.create false
23-
RUN poetry config cache-dir /tmp/poetry_cache
24-
25-
# Copying requirements of a project
26-
COPY pyproject.toml poetry.lock /app/src/
2728
WORKDIR /app/src
2829

29-
# Installing requirements
30-
RUN --mount=type=cache,target=/tmp/poetry_cache poetry install --only main
30+
RUN --mount=from=uv,source=/uv,target=/bin/uv \
31+
--mount=type=cache,target=/root/.cache/uv \
32+
--mount=type=bind,source=uv.lock,target=uv.lock \
33+
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
34+
uv sync --locked --no-install-project --no-dev --managed-python
3135

32-
{%- if cookiecutter.db_info.name == "mysql" or cookiecutter.db_info.name == "postgresql" %}
33-
# Removing gcc
34-
RUN apt-get purge -y \
35-
gcc \
36-
&& rm -rf /var/lib/apt/lists/*
37-
{%- endif %}
36+
COPY . .
3837

39-
# Copying actuall application
40-
COPY . /app/src/
41-
RUN --mount=type=cache,target=/tmp/poetry_cache poetry install --only main
38+
RUN --mount=from=uv,source=/uv,target=/bin/uv \
39+
--mount=type=cache,target=/root/.cache/uv \
40+
uv sync --locked --no-dev --managed-python
4241

4342
CMD ["/usr/local/bin/python", "-m", "{{cookiecutter.project_name}}"]
4443

44+
# -----------------------------------
45+
# STAGE 3: development build
46+
# Includes dev dependencies
47+
# -----------------------------------
4548
FROM prod AS dev
4649

47-
RUN --mount=type=cache,target=/tmp/poetry_cache poetry install
50+
RUN --mount=from=uv,source=/uv,target=/bin/uv \
51+
--mount=type=cache,target=/root/.cache/uv \
52+
uv sync --locked --managed-python --all-groups

fastapi_template/template/{{cookiecutter.project_name}}/README.md

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,23 @@
22

33
This project was generated using fastapi_template.
44

5-
## Poetry
5+
## UV
66

7-
This project uses poetry. It's a modern dependency management
7+
This project uses uv. It's a modern dependency management
88
tool.
99

1010
To run the project use this set of commands:
1111

1212
```bash
13-
poetry install
14-
poetry run python -m {{cookiecutter.project_name}}
13+
uv sync --locked
14+
uv run -m {{cookiecutter.project_name}}
1515
```
1616

1717
This will start the server on the configured host.
1818

1919
You can find swagger documentation at `/api/docs`.
2020

21-
You can read more about poetry here: https://python-poetry.org/
21+
You can read more about uv here: https://docs.astral.sh/ruff/
2222

2323
## Docker
2424

@@ -37,7 +37,7 @@ docker-compose -f docker-compose.yml -f deploy/docker-compose.dev.yml --project-
3737

3838
This command exposes the web application on port 8000, mounts current directory and enables autoreload.
3939

40-
But you have to rebuild image every time you modify `poetry.lock` or `pyproject.toml` with this command:
40+
But you have to rebuild image every time you modify `uv.lock` or `pyproject.toml` with this command:
4141

4242
```bash
4343
docker-compose build
@@ -122,7 +122,6 @@ pre-commit is very useful to check your code before publishing it.
122122
It's configured using .pre-commit-config.yaml file.
123123
124124
By default it runs:
125-
* black (formats your code);
126125
* mypy (validates types);
127126
* ruff (spots possible bugs);
128127

fastapi_template/template/{{cookiecutter.project_name}}/pyproject.toml

Lines changed: 57 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -152,33 +152,39 @@ asyncpg = {version = "^0.30.0", extras = ["sa"]}
152152
{%- endif %}
153153
]
154154

155-
[tool.poetry.group.dev.dependencies]
156-
pytest = "^8"
157-
ruff = "^0.5.0"
158-
mypy = "^1.10.1"
159-
pre-commit = "^3.7.1"
160-
black = "^24.4.2"
161-
pytest-cov = "^5"
162-
anyio = "^4"
163-
pytest-env = "^1.1.3"
155+
[dependency-groups]
156+
dev = [
157+
"pytest>=8,<9",
158+
"ruff>=0.5.0,<1",
159+
"mypy>=1.10.1,<2",
160+
"pre-commit>=3.7.1,<4",
161+
"pytest-cov>=5,<6",
162+
"anyio>=4,<5",
163+
"pytest-env>=1.1.3,<2",
164164
{%- if cookiecutter.enable_redis == "True" %}
165-
fakeredis = "^2.23.3"
165+
"fakeredis>=2.23.3,<3",
166166
{%- endif %}
167167
{%- if cookiecutter.orm == "tortoise" %}
168-
asynctest = "^0.13.0"
169-
nest-asyncio = "^1.6.0"
168+
"asynctest>=0.13.0,<1",
169+
"nest-asyncio>=1.6.0,<2",
170170
{%- endif %}
171-
httpx = "^0.27.0"
171+
"httpx>=0.27.0,<1",
172172
{%- if cookiecutter.enable_taskiq == "True" %}
173-
taskiq = { version = "^0", extras = ["reload"] }
173+
"taskiq[reload]>=0,<1",
174174
{%- endif %}
175+
]
176+
177+
178+
[tool.uv]
179+
default-groups = ["dev"]
175180

176181
[tool.isort]
177182
profile = "black"
178183
multi_line_output = 3
179184
src_paths = ["{{cookiecutter.project_name}}",]
180185

181186
[tool.mypy]
187+
python_version = "3.12"
182188
strict = true
183189
ignore_missing_imports = true
184190
allow_subclassing_any = true
@@ -242,34 +248,36 @@ src_folder = "./{{cookiecutter.project_name}}"
242248
{%- endif %}
243249

244250
[tool.ruff]
251+
target-version = "py312"
245252
# List of enabled rulsets.
246253
# See https://docs.astral.sh/ruff/rules/ for more information.
247-
lint.select = [
248-
"E", # Error
249-
"F", # Pyflakes
250-
"W", # Pycodestyle
251-
"C90", # McCabe complexity
252-
"I", # Isort
253-
"N", # pep8-naming
254-
"D", # Pydocstyle
255-
"ANN", # Pytype annotations
256-
"S", # Bandit
257-
"B", # Bugbear
258-
"COM", # Commas
259-
"C4", # Comprehensions
260-
"ISC", # Implicit string concat
261-
"PIE", # Unnecessary code
262-
"T20", # Catch prints
263-
"PYI", # validate pyi files
264-
"Q", # Checks for quotes
265-
"RSE", # Checks raise statements
266-
"RET", # Checks return statements
267-
"SLF", # Self checks
268-
"SIM", # Simplificator
269-
"PTH", # Pathlib checks
270-
"ERA", # Checks for commented out code
271-
"PL", # PyLint checks
272-
"RUF", # Specific to Ruff checks
254+
lint.select = [ "E", # Error
255+
"F", # Pyflakes
256+
"W", # Pycodestyle
257+
"C90", # McCabe complexity
258+
"I", # Isort
259+
"N", # pep8-naming
260+
"D", # Pydocstyle
261+
"ANN", # Pytype annotations
262+
"S", # Bandit
263+
"B", # Bugbear
264+
"COM", # Commas
265+
"C4", # Comprehensions
266+
"ISC", # Implicit string concat
267+
"PIE", # Unnecessary code
268+
"T20", # Catch prints
269+
"PYI", # validate pyi files
270+
"Q", # Checks for quotes
271+
"RSE", # Checks raise statements
272+
"RET", # Checks return statements
273+
"SLF", # Self checks
274+
"SIM", # Simplificator
275+
"PTH", # Pathlib checks
276+
"ERA", # Checks for commented out code
277+
"PL", # PyLint checks
278+
"RUF", # Specific to Ruff checks
279+
"FA102", # Future annotations
280+
"UP", # Python updates
273281
]
274282
lint.ignore = [
275283
"D105", # Missing docstring in magic method
@@ -308,14 +316,19 @@ ignore-decorators = ["typing.overload"]
308316
[tool.ruff.lint.pylint]
309317
allow-magic-value-types = ["int", "str", "float", "bytes"]
310318

319+
[tool.ruff.lint.flake8-bugbear]
320+
extend-immutable-calls = [
321+
{%- if cookiecutter.enable_taskiq == "True" %}
322+
"taskiq_dependencies.Depends",
323+
"taskiq.TaskiqDepends",
324+
{%- endif %}
325+
"fastapi.Depends",
326+
"fastapi.Query",
327+
]
311328

312329
[fastapi-template.options]
313330
{%- for key, value in cookiecutter.items() %}
314331
{%- if not key.startswith("_") and not key == "db_info" %}
315332
{{key}} = "{{value}}"
316333
{%- endif %}
317334
{%- endfor %}
318-
319-
[build-system]
320-
requires = ["poetry-core>=1.0.0"]
321-
build-backend = "poetry.core.masonry.api"

fastapi_template/tests/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,6 @@ def docker_module_shutdown(
103103
if not project_dir.exists():
104104
return
105105
os.chdir(project_dir)
106-
Path("poetry.lock").unlink(missing_ok=True)
106+
Path("uv.lock").unlink(missing_ok=True)
107107
run_docker_compose_command("down -v")
108108
os.chdir(cwd)

0 commit comments

Comments
 (0)