about things
1# project setup
2
3consistent structure across projects: src/ layout, pyproject.toml as single source of truth, justfile for commands.
4
5## directory structure
6
7```
8myproject/
9├── src/myproject/
10│ ├── __init__.py
11│ ├── cli.py
12│ ├── settings.py
13│ └── _internal/ # private implementation
14├── tests/
15├── pyproject.toml
16├── justfile
17└── .pre-commit-config.yaml
18```
19
20the `src/` layout prevents accidental imports from the working directory. your package is only importable when properly installed.
21
22## pyproject.toml
23
24```toml
25[project]
26name = "myproject"
27description = "what it does"
28readme = "README.md"
29requires-python = ">=3.10"
30dynamic = ["version"]
31dependencies = [
32 "httpx>=0.27",
33 "pydantic>=2.0",
34]
35
36[project.scripts]
37myproject = "myproject.cli:main"
38
39[build-system]
40requires = ["hatchling", "uv-dynamic-versioning"]
41build-backend = "hatchling.build"
42
43[tool.hatch.version]
44source = "uv-dynamic-versioning"
45
46[tool.uv-dynamic-versioning]
47vcs = "git"
48style = "pep440"
49bump = true
50fallback-version = "0.0.0"
51
52[dependency-groups]
53dev = [
54 "pytest>=8.0",
55 "ruff>=0.8",
56 "ty>=0.0.1a6",
57]
58```
59
60key patterns:
61- `dynamic = ["version"]` - version comes from git tags, not manual editing
62- `[project.scripts]` - CLI entry points
63
64## dependency groups vs optional dependencies
65
66these look similar but serve different purposes.
67
68**dependency groups** (PEP 735) are local-only. they never appear in published package metadata. users who `pip install` your package won't see them:
69
70```toml
71[dependency-groups]
72dev = ["pytest", "ruff"]
73docs = ["mkdocs", "mkdocs-material"]
74```
75
76install with `uv sync --group dev`. CI can install only what it needs.
77
78**optional dependencies** are published in package metadata. users can install them:
79
80```toml
81[project.optional-dependencies]
82aws = ["prefect-aws"]
83mcp = ["fastmcp>=2.0"]
84```
85
86install with `pip install mypackage[aws]` or `uv add 'mypackage[mcp]'`.
87
88use groups for dev/test/CI. use optional deps for features consumers might want.
89
90from [switching a big python library from setup.py to pyproject.toml](https://blog.zzstoatzz.io/switching-a-big-python-library-from-setuppy-to-pyprojecttoml/)
91
92## versioning from git tags
93
94with `uv-dynamic-versioning`, your version is derived from git:
95
96```bash
97git tag v0.1.0
98git push --tags
99```
100
101no more editing `__version__` or `pyproject.toml` for releases.
102
103## justfile
104
105```makefile
106check-uv:
107 #!/usr/bin/env sh
108 if ! command -v uv >/dev/null 2>&1; then
109 echo "uv is not installed. Install: curl -LsSf https://astral.sh/uv/install.sh | sh"
110 exit 1
111 fi
112
113install: check-uv
114 uv sync
115
116test:
117 uv run pytest tests/ -xvs
118
119lint:
120 uv run ruff format src/ tests/
121 uv run ruff check src/ tests/ --fix
122
123check:
124 uv run ty check
125```
126
127run with `just test`, `just lint`, etc.
128
129## multiple entry points
130
131for projects with both CLI and MCP server:
132
133```toml
134[project.scripts]
135myproject = "myproject.cli:main"
136myproject-mcp = "myproject.mcp:main"
137```
138
139## uv workspaces
140
141for multi-package repos (like plyr-python-client):
142
143```
144myproject/
145├── packages/
146│ ├── core/
147│ │ ├── src/core/
148│ │ └── pyproject.toml
149│ └── mcp/
150│ ├── src/mcp/
151│ └── pyproject.toml
152├── pyproject.toml # root workspace config
153└── uv.lock
154```
155
156root pyproject.toml:
157
158```toml
159[tool.uv.workspace]
160members = ["packages/*"]
161
162[tool.uv.sources]
163core = { workspace = true }
164mcp = { workspace = true }
165```
166
167packages can depend on each other. one lockfile for the whole workspace.
168
169## simpler build backend
170
171for projects that don't need dynamic versioning, `uv_build` is lighter:
172
173```toml
174[build-system]
175requires = ["uv_build>=0.9.2,<0.10.0"]
176build-backend = "uv_build"
177```
178
179sources:
180- [pdsx/pyproject.toml](https://github.com/zzstoatzz/pdsx/blob/main/pyproject.toml)
181- [plyr-python-client](https://github.com/zzstoatzz/plyr-python-client)