1# plain.dev
2
3A single command that runs everything you need for local development.
4
5
6
7The `plain.dev` package can be [installed from PyPI](https://pypi.org/project/plain.dev/), and does _not_ need to be added to `INSTALLED_PACKAGES`.
8
9- [`plain dev`](#plain-dev)
10- [`plain dev services`](#plain-dev-services)
11- [`plain pre-commit`](#plain-pre-commit)
12- [`plain contrib`](#plain-contrib)
13- [VS Code debugging](#vscode-debugging)
14
15## `plain dev`
16
17The `plain dev` command does several things:
18
19- Sets `PLAIN_CSRF_TRUSTED_ORIGINS` to localhost by default
20- Runs `plain preflight` to check for any issues
21- Executes any pending model migrations
22- Starts `gunicorn` with `--reload`
23- Serves HTTPS on port 8443 by default (uses the next free port if 8443 is taken and no port is specified)
24- Runs `plain tailwind build --watch`, if `plain.tailwind` is installed
25- Any custom process defined in `pyproject.toml` at `tool.plain.dev.run`
26- Necessary services (ex. Postgres) defined in `pyproject.toml` at `tool.plain.dev.services`
27
28### Services
29
30Use services to define databases or other processes that your app _needs_ to be functional. The services will be started automatically in `plain dev`, but also in `plain pre-commit` (so preflight and tests have a database).
31
32Ultimately, how you run your development database is up to you. But a recommended starting point is to use Docker:
33
34```toml
35# pyproject.toml
36[tool.plain.dev.services]
37postgres = {cmd = "docker run --name app-postgres --rm -p 54321:5432 -v $(pwd)/.plain/dev/pgdata:/var/lib/postgresql/data -e POSTGRES_PASSWORD=postgres postgres:15 postgres"}
38```
39
40### Custom processes
41
42Unlike [services](#services), custom processes are _only_ run during `plain dev`. This is a good place to run something like [ngrok](https://ngrok.com/) or a [Plain worker](../../../plain-worker), which you might need to use your local site, but don't need running for executing tests, for example.
43
44```toml
45# pyproject.toml
46[tool.plain.dev.run]
47ngrok = {command = "ngrok http $PORT"}
48```
49
50## `plain dev services`
51
52Starts your [services](#services) by themselves.
53
54## `plain pre-commit`
55
56A built-in pre-commit hook that can be installed with `plain pre-commit --install`.
57
58Runs:
59
60- Custom commands defined in `pyproject.toml` at `tool.plain.pre-commit.run`
61- `plain code check`, if [`plain.code`](https://plainframework.com/docs/plain-code/plain/code/) is installed
62- `uv lock --locked`, if using uv
63- `plain preflight --database default`
64- `plain migrate --check`
65- `plain makemigrations --dry-run --check`
66- `plain build`
67- `plain test`
68
69## VS Code debugging
70
71
72
73Since `plain dev` runs multiple processes at once, the regular [pdb](https://docs.python.org/3/library/pdb.html) debuggers don't quite work.
74
75Instead, we include [microsoft/debugpy](https://github.com/microsoft/debugpy) and an `attach` function to make it even easier to use VS Code's debugger.
76
77First, import and run the `debug.attach()` function:
78
79```python
80class HomeView(TemplateView):
81 template_name = "home.html"
82
83 def get_template_context(self):
84 context = super().get_template_context()
85
86 # Make sure the debugger is attached (will need to be if runserver reloads)
87 from plain.dev import debug; debug.attach()
88
89 # Add a breakpoint (or use the gutter in VS Code to add one)
90 breakpoint()
91
92 return context
93```
94
95When you load the page, you'll see "Waiting for debugger to attach...".
96
97You can then run the VS Code debugger and attach to an existing Python process, at localhost:5678.