Skip to content

Commit 199d66a

Browse files
committed
quick scaffold/prep for testing as intended usage
1 parent 489b9a3 commit 199d66a

File tree

11 files changed

+640
-0
lines changed

11 files changed

+640
-0
lines changed

.github/workflows/main.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
python-version: ["3.10", "3.11", "3.12"]
15+
steps:
16+
- uses: actions/checkout@v4
17+
- name: Set up Python ${{ matrix.python-version }}
18+
uses: actions/setup-python@v5
19+
with:
20+
python-version: ${{ matrix.python-version }}
21+
- name: Install dependencies
22+
run: |
23+
python -m pip install --upgrade pip
24+
python -m pip install uv
25+
uv venv
26+
uv sync
27+
- name: Run pre-commit checks
28+
run

.pre-commit-config.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
repos:
2+
- repo: https://github.com/pre-commit/pre-commit-hooks
3+
rev: v4.5.0
4+
hooks:
5+
- id: trailing-whitespace
6+
- id: end-of-file-fixer
7+
- id: check-yaml
8+
- id: check-added-large-files
9+
- repo: https://github.com/astral-sh/ruff-pre-commit
10+
rev: "v0.1.10"
11+
hooks:
12+
- id: ruff
13+
args: ["--fix"]
14+
- repo: https://github.com/psf/black
15+
rev: 24.2.0
16+
hooks:
17+
- id: black
18+
- repo: https://github.com/pre-commit/mirrors-mypy
19+
rev: v1.9.0
20+
hooks:
21+
- id: mypy

.vscode/settings.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"python.analysis.typeCheckingMode": "basic",
3+
"python.linting.enabled": true,
4+
"python.linting.ruffEnabled": true,
5+
"python.formatting.provider": "none",
6+
"editor.formatOnSave": true,
7+
"editor.defaultFormatter": "charliermarsh.ruff",
8+
"ruff.importStrategy": "fromEnvironment",
9+
"ruff.organizeImports": true,
10+
"ruff.fixAll": true,
11+
"ruff.showSyntaxErrors": true,
12+
"ruff.configurationPreference": "filesystemFirst"
13+
}

README.md

Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
# Modern Python Project Template
2+
3+
This repository provides a modern Python project template, designed for efficient development with `uv`, `ruff`, `mypy`, `pytest`, and Git hooks powered by `pre-commit`. It aims to provide a solid foundation for new Python projects, promoting code quality, consistency, and reduced errors. This template is designed to be used with Linuxbrew for consistent package management across platforms.
4+
5+
## Features
6+
7+
* **`uv` for Dependency Management:** Fast and efficient dependency resolution and virtual environment management.
8+
* **`ruff` for Linting and Formatting:** Enforces consistent code style, catches errors early, and provides auto-fixing capabilities.
9+
* **`mypy` for Type Checking:** Adds static typing to your Python code, preventing type-related errors.
10+
* **`pytest` for Testing:** Provides a robust and flexible testing framework.
11+
* **`pre-commit` for Git Hooks:** Automates code quality checks before every commit.
12+
* **`pyproject.toml` for Configuration:** Centralized project configuration using the official `pyproject.toml` standard.
13+
* **Clean Project Structure:** A well-defined structure with separate `src` and `tests` directories.
14+
* **Cross-Platform Compatibility:** Designed for use with Linuxbrew to provide a consistent development environment across Windows (via WSL), macOS, and Linux.
15+
16+
## Getting Started
17+
18+
Follow these steps to set up this project template for development:
19+
20+
### 1. Platform Specific Initialization
21+
22+
Before cloning the repository, make sure you have Linuxbrew installed (or Homebrew on macOS).
23+
24+
### Windows/WSL Setup
25+
26+
1. **Install WSL:** Follow the official Microsoft instructions to install WSL and choose a Linux distribution (e.g., Ubuntu).
27+
28+
2. **Open a *Direct* WSL Terminal:**
29+
* **Do NOT use `wsl` from a Windows terminal (like PowerShell or Command Prompt).**
30+
* **In VS Code:** Open a *new* integrated terminal by clicking the "+" button on the terminals tab, and select your WSL distribution (e.g., "Ubuntu", "Debian") from the dropdown menu.
31+
* **From Start Menu:** Alternatively, launch your WSL distribution's terminal directly from the Windows Start Menu (e.g., "Ubuntu", "Debian").
32+
33+
**All subsequent steps in this section must be executed within this *direct* WSL terminal.**
34+
35+
3. **Install Base Dependencies (Within WSL):**
36+
```bash
37+
sudo apt update && sudo apt install -y build-essential curl file git
38+
```
39+
4. **Install Linuxbrew:**
40+
```bash
41+
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
42+
```
43+
5. **Add Linuxbrew to `PATH`:**
44+
Add the following lines to your `~/.bashrc` or `~/.zshrc` (depending on your shell):
45+
```bash
46+
export PATH="/home/linuxbrew/.linuxbrew/bin:$PATH"
47+
export MANPATH="/home/linuxbrew/.linuxbrew/share/man:$MANPATH"
48+
export INFOPATH="/home/linuxbrew/.linuxbrew/share/info:$INFOPATH"
49+
```
50+
Then run `source ~/.bashrc` or `source ~/.zshrc`.
51+
6. **Install Python via Linuxbrew:** `brew install python`
52+
53+
#### macOS
54+
55+
1. **Install Homebrew:** If you don't have it already, install Homebrew by running the following command in the terminal:
56+
```bash
57+
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
58+
```
59+
2. **Install Python via Homebrew:** `brew install python`
60+
61+
#### Linux
62+
63+
1. **Install Linuxbrew:** Follow the instructions on the Linuxbrew homepage: [https://brew.sh/](https://brew.sh/)
64+
2. **Install Python via Linuxbrew:** `brew install python`
65+
66+
### 2. Project Setup
67+
68+
Once Linuxbrew (or Homebrew on macOS) and Python are installed, you can continue with the following steps:
69+
70+
1. **Clone the Repository:**
71+
```bash
72+
git clone <repository_url>
73+
cd <project_name>
74+
```
75+
76+
2. **Create and Activate Virtual Environment:**
77+
```bash
78+
uv venv
79+
```
80+
This command creates a virtual environment, if one does not exist already.
81+
82+
3. **Install Dependencies:**
83+
```bash
84+
uv sync
85+
```
86+
This command installs all dependencies from your lock file (`uv.lock`), including development dependencies.
87+
88+
4. **Install Git Hooks:**
89+
```bash
90+
pre-commit install
91+
```
92+
This command installs the pre-commit hooks that ensure formatting and linting happen automatically before each commit.
93+
94+
5. **Create a lock file**:
95+
```bash
96+
uv lock
97+
```
98+
This command creates a lock file which locks all dependencies in the project to a known version.
99+
100+
## Development
101+
102+
* **Code in `src`:** Start adding your Python code inside the `src/my_package` directory.
103+
* **Create tests:** Create your tests in the `tests` directory, and import your code using the name of the package, in this case `my_package`.
104+
* **Run tests:** Use `pytest` in the root folder to run tests.
105+
```bash
106+
pytest
107+
```
108+
109+
## Automatic Linting and Formatting
110+
111+
* `ruff` automatically fixes formatting and linting errors when you save changes.
112+
113+
## Type Checking
114+
115+
* `mypy` is run as part of the pre-commit hooks and will catch any type issues.
116+
117+
## Configuration
118+
119+
### Core Configuration
120+
121+
The project's core configuration is located in the `pyproject.toml` file.
122+
123+
* **Project Metadata:**
124+
* Basic project information like name, version, description, authors, etc.
125+
* Python version specified in `requires-python`
126+
* **Dependencies:**
127+
* Project's dependencies listed in the `[project.dependencies` array.
128+
* Development dependencies listed in the `[project.optional-dependencies.dev]` array, or you can add additional groups.
129+
130+
#### `[tool.uv]`
131+
132+
* `default-groups = ["dev"]`: Sets dev dependencies to be installed by default.
133+
134+
#### `[tool.ruff]`
135+
136+
* `line-length = 100`: Sets the maximum line length.
137+
* `select`: List of rules enabled from `ruff` and different linters.
138+
* `ignore`: List of rules ignored.
139+
* `fix = true`: Automatically fix linting errors.
140+
* `per-file-ignores`: Configure specific files or patterns for ignoring certain rules.
141+
* `quote-style = "single"`: Sets style to use single quotes.
142+
143+
#### `[tool.mypy]`
144+
145+
* `mypy_path = "src"`: The location of your packages for mypy type checking.
146+
* `python_version = "3.10"`: The Python version being used.
147+
* `strict = true`: Enables strict type checking options.
148+
* `plugins = ["pytest_mypy_plugins"]`: Makes pytest plugin available for mypy.
149+
150+
### Git Hooks Configuration (`.pre-commit-config.yaml`)
151+
152+
This file configures `pre-commit`, a tool that runs checks before each commit. It includes:
153+
154+
* **General-Purpose Hooks (`pre-commit-hooks`):** Trailing whitespace removal, end-of-file newline enforcement, YAML validation, etc.
155+
* **`ruff` Hook (`astral-sh/ruff-pre-commit`):** Automatically fixes `Ruff` errors (`ruff --fix`).
156+
* **`black` Hook (`psf/black`):** Automatically reformats your code with the black formatter
157+
* **`mypy` Hook (`pre-commit/mirrors-mypy`):** Checks code for type errors with `mypy`
158+
159+
### VS Code Configuration (`.vscode/settings.json`)
160+
161+
This file configures the VS Code editor.
162+
163+
* **Type Checking:** `python.analysis.typeCheckingMode`: Enables Python's type checker.
164+
* **Linting:** `python.linting.ruffEnabled`: Enables `ruff` as the linter.
165+
* **Formatting:** `python.formatting.provider`: Disables default formatter, since pre-commit is being used.
166+
* **Ruff Settings:** Settings for Ruff language server (e.g., `importStrategy`, `organizeImports`, `fixAll`, `showSyntaxErrors`).
167+
* `ruff.configurationPreference`: prioritize file based configs.
168+
169+
## Common and Useful Configurations (Not in Base Config)
170+
171+
Here are some additional configurations that are often useful and are commonly used or at least helpful if applicable.
172+
173+
### 1. Custom Ruff Rules and Ignores
174+
175+
* **`[tool.ruff.lint.per-file-ignores]`:** Use this section to ignore rules for specific files. For example:
176+
177+
```toml
178+
[tool.ruff.lint.per-file-ignores]
179+
"tests/*" = ["S101", "ANN"] # Allow asserts and skip annotations in tests.
180+
"src/my_package/legacy.py" = ["B001", "B002"] # Ignore specific bugbear rules for legacy code
181+
```
182+
183+
* **`extend-select` and `extend-ignore`:** Add additional rules or ignore rules for the project.
184+
185+
```toml
186+
[tool.ruff.lint
187+
extend-select = ["PT"] # adds rules from flake8-pytest
188+
extend-ignore = ["ANN101", "ANN401"] # extends the ignore rules
189+
```
190+
191+
* **Rule Specific Options:** Certain rules can be further configured, like for example isort options.
192+
193+
```toml
194+
[tool.ruff.lint.isort]
195+
force-wrap-aliases = true
196+
```
197+
198+
### 2. Custom Mypy Options
199+
200+
* **`ignore_missing_imports`:** If you are having issues with dependencies and type checking. Set to `true`.
201+
202+
```toml
203+
[tool.mypy]
204+
ignore_missing_imports = true
205+
```
206+
207+
* **`disallow_untyped_defs`:** If you want to require type annotations in function definitions, use `disallow_untyped_defs = true`.
208+
209+
```toml
210+
[tool.mypy]
211+
disallow_untyped_defs = true
212+
```
213+
214+
* **`disallow_any_explicit`**: If you want to disallow explicit use of `Any` types in the code.
215+
216+
```toml
217+
[tool.mypy]
218+
disallow_any_explicit = true
219+
```
220+
221+
* **`warn_unreachable`:** If you want to enable warning for unreachable code
222+
223+
```toml
224+
[tool.mypy]
225+
warn_unreachable = true
226+
```
227+
228+
See [Mypy configuration docs](https://mypy.readthedocs.io/en/stable/config_file.html) for a complete listing of available settings.
229+
230+
### 3. `uv` Configuration
231+
232+
* **`constraint-dependencies`:** If you want to pin a transitive dependency to a specific version use `constraint-dependencies`.
233+
234+
```toml
235+
[tool.uv]
236+
constraint-dependencies = ["cryptography<42.0.0"]
237+
```
238+
239+
* **`override-dependencies`:** Forces a dependency to use a specific version
240+
241+
```toml
242+
[tool.uv]
243+
override-dependencies = ["cryptography==42.0.0"]
244+
```
245+
246+
* **`index`:** You can specify additional package indexes to get dependencies from
247+
248+
```toml
249+
[[tool.uv.index]]
250+
name = "pytorch"
251+
url = "https://download.pytorch.org/whl/cu121"
252+
explicit = true
253+
254+
[tool.uv.sources]
255+
torch = { index = "pytorch" }
256+
```
257+
258+
### 4. Test Configuration
259+
260+
* `pytest.ini`: Create a `pytest.ini` in your root folder to configure options for `pytest`.
261+
262+
```ini
263+
[pytest]
264+
testpaths = tests
265+
addopts =
266+
--cov=src/my_package
267+
--cov-report term-missing
268+
-vv
269+
```
270+
271+
This will make sure that pytest runs against all tests in the tests folder
272+
273+
It will also add coverage info for `src/my_package`
274+
275+
It will also run in verbose mode
276+
277+
### 5. `pre-commit` Config
278+
279+
You can add additional hooks from other repositories.
280+
281+
You can configure existing hooks as needed. For example if you want to exclude files from ruff you can use:
282+
283+
```yaml
284+
- id: ruff
285+
args: ["--fix", "--exclude", "path/to/exclude"]
286+
```
287+
288+
## Contributing
289+
290+
If you would like to add features to this template, or suggest improvements, feel free to create a pull request.

0 commit comments

Comments
 (0)