plain.dev 0.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plain_dev-0.0.0/LICENSE +28 -0
- plain_dev-0.0.0/PKG-INFO +178 -0
- plain_dev-0.0.0/README.md +150 -0
- plain_dev-0.0.0/plain/dev/README.md +148 -0
- plain_dev-0.0.0/plain/dev/__init__.py +5 -0
- plain_dev-0.0.0/plain/dev/cli.py +166 -0
- plain_dev-0.0.0/plain/dev/config.py +20 -0
- plain_dev-0.0.0/plain/dev/contribute/__init__.py +3 -0
- plain_dev-0.0.0/plain/dev/contribute/cli.py +114 -0
- plain_dev-0.0.0/plain/dev/db/__init__.py +3 -0
- plain_dev-0.0.0/plain/dev/db/cli.py +113 -0
- plain_dev-0.0.0/plain/dev/db/container.py +151 -0
- plain_dev-0.0.0/plain/dev/debug.py +12 -0
- plain_dev-0.0.0/plain/dev/default_settings.py +5 -0
- plain_dev-0.0.0/plain/dev/pid.py +20 -0
- plain_dev-0.0.0/plain/dev/precommit/__init__.py +3 -0
- plain_dev-0.0.0/plain/dev/precommit/cli.py +123 -0
- plain_dev-0.0.0/plain/dev/requests.py +224 -0
- plain_dev-0.0.0/plain/dev/services.py +80 -0
- plain_dev-0.0.0/plain/dev/templates/dev/requests.html +134 -0
- plain_dev-0.0.0/plain/dev/urls.py +9 -0
- plain_dev-0.0.0/plain/dev/utils.py +14 -0
- plain_dev-0.0.0/plain/dev/views.py +37 -0
- plain_dev-0.0.0/pyproject.toml +45 -0
plain_dev-0.0.0/LICENSE
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
BSD 3-Clause License
|
2
|
+
|
3
|
+
Copyright (c) 2023, Dropseed, LLC
|
4
|
+
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
7
|
+
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
9
|
+
list of conditions and the following disclaimer.
|
10
|
+
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
13
|
+
and/or other materials provided with the distribution.
|
14
|
+
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
16
|
+
contributors may be used to endorse or promote products derived from
|
17
|
+
this software without specific prior written permission.
|
18
|
+
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
plain_dev-0.0.0/PKG-INFO
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: plain.dev
|
3
|
+
Version: 0.0.0
|
4
|
+
Summary: Work library for Plain
|
5
|
+
Home-page: https://www.plainpackages.com/
|
6
|
+
License: MIT
|
7
|
+
Author: Dave Gaeddert
|
8
|
+
Author-email: dave.gaeddert@dropseed.dev
|
9
|
+
Requires-Python: >=3.8,<4.0
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
12
|
+
Classifier: Programming Language :: Python :: 3.8
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
17
|
+
Requires-Dist: click (>=8.0.0)
|
18
|
+
Requires-Dist: debugpy (>=1.6.3,<2.0.0)
|
19
|
+
Requires-Dist: gunicorn (>20)
|
20
|
+
Requires-Dist: honcho (>=1.1.0,<2.0.0)
|
21
|
+
Requires-Dist: psycopg2-binary (>=2.9.3,<3.0.0)
|
22
|
+
Requires-Dist: requests (>=2.0.0)
|
23
|
+
Requires-Dist: tomli (>=2.0.1,<3.0.0) ; python_version < "3.11"
|
24
|
+
Project-URL: Documentation, https://www.plainpackages.com/docs/
|
25
|
+
Project-URL: Repository, https://github.com/plainpackages/plain-dev
|
26
|
+
Description-Content-Type: text/markdown
|
27
|
+
|
28
|
+
<!-- This file is compiled from plain-dev/plain/dev/README.md. Do not edit this file directly. -->
|
29
|
+
|
30
|
+
# plain-dev
|
31
|
+
|
32
|
+
A single command to run everything you need for local Plain development.
|
33
|
+
|
34
|
+

|
35
|
+
|
36
|
+
The `plain dev` command runs a combination of local commands + a Docker container for your database.
|
37
|
+
|
38
|
+
The following processes will run simultaneously (some will only run if they are detected as available):
|
39
|
+
|
40
|
+
<!-- - [`manage.py runserver` (and migrations)](#runserver)
|
41
|
+
- [`plain-models start --logs`](#plain-models)
|
42
|
+
- [`plain-tailwind compile --watch`](#plain-tailwind)
|
43
|
+
- [`npm run watch`](#package-json)
|
44
|
+
- [`stripe listen --forward-to`](#stripe)
|
45
|
+
- [`ngrok http --subdomain`](#ngrok)
|
46
|
+
|
47
|
+
It also comes with [debugging](#debugging) tools to make local debugging easier with VS Code. -->
|
48
|
+
|
49
|
+
## Installation
|
50
|
+
|
51
|
+
```sh
|
52
|
+
pip install plain-dev
|
53
|
+
```
|
54
|
+
|
55
|
+
If you have `plain-models` installed (i.e. you're using a database),
|
56
|
+
then add `DATABASE_URL` to your `.env` file.
|
57
|
+
|
58
|
+
```sh
|
59
|
+
DATABASE_URL=postgres://postgres:postgres@localhost:54321/postgres
|
60
|
+
```
|
61
|
+
|
62
|
+
```toml
|
63
|
+
# pyproject.toml
|
64
|
+
[tool.plain.dev.services]
|
65
|
+
postgres = {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"}
|
66
|
+
```
|
67
|
+
|
68
|
+
```sh
|
69
|
+
plain dev
|
70
|
+
```
|
71
|
+
|
72
|
+
## `plain dev`
|
73
|
+
|
74
|
+
### Default processes
|
75
|
+
|
76
|
+
- plain preflight
|
77
|
+
- gunicorn
|
78
|
+
- migrations
|
79
|
+
- tailwind
|
80
|
+
|
81
|
+
### Custom processes
|
82
|
+
|
83
|
+
- package.json "dev" script
|
84
|
+
- pyproject.toml `tool.plain.dev.run = {command = "..."}`
|
85
|
+
|
86
|
+
### GitHub Codespaces
|
87
|
+
|
88
|
+
The `BASE_URL` setting is automatically set to the Codespace URL.
|
89
|
+
|
90
|
+
TODO
|
91
|
+
|
92
|
+
## `plain dev db`
|
93
|
+
|
94
|
+
Only supports Postgres currently.
|
95
|
+
|
96
|
+
- snapshot
|
97
|
+
- import
|
98
|
+
- export
|
99
|
+
|
100
|
+
|
101
|
+
## Development processes
|
102
|
+
|
103
|
+
### Gunicorn
|
104
|
+
|
105
|
+
The key process here is still `manage.py runserver`.
|
106
|
+
But, before that runs, it will also wait for the database to be available and run `manage.py migrate`.
|
107
|
+
|
108
|
+
### plain-models
|
109
|
+
|
110
|
+
If [`plain-models`](https://github.com/plainpackages/plain-models) is installed, it will automatically start and show the logs of the running database container.
|
111
|
+
|
112
|
+
### plain-tailwind
|
113
|
+
|
114
|
+
If [`plain-tailwind`](https://github.com/plainpackages/plain-tailwind) is installed, it will automatically run the Tailwind `compile --watch` process.
|
115
|
+
|
116
|
+
## Debugging
|
117
|
+
|
118
|
+
[View on YouTube →](https://www.youtube.com/watch?v=pG0KaJSVyBw)
|
119
|
+
|
120
|
+
Since `plain work` runs multiple processes at once, the regular [pdb](https://docs.python.org/3/library/pdb.html) debuggers can be hard to use.
|
121
|
+
Instead, we include [microsoft/debugpy](https://github.com/microsoft/debugpy) and an `attach` function to make it even easier to use VS Code's debugger.
|
122
|
+
|
123
|
+
First, import and run the `debug.attach()` function:
|
124
|
+
|
125
|
+
```python
|
126
|
+
class HomeView(TemplateView):
|
127
|
+
template_name = "home.html"
|
128
|
+
|
129
|
+
def get_context(self, **kwargs):
|
130
|
+
context = super().get_context(**kwargs)
|
131
|
+
|
132
|
+
# Make sure the debugger is attached (will need to be if runserver reloads)
|
133
|
+
from plain.work import debug; debug.attach()
|
134
|
+
|
135
|
+
# Add a breakpoint (or use the gutter in VSCode to add one)
|
136
|
+
breakpoint()
|
137
|
+
|
138
|
+
return context
|
139
|
+
```
|
140
|
+
|
141
|
+
When you load the page, you'll see "Waiting for debugger to attach...".
|
142
|
+
|
143
|
+
Add a new VS Code debug configuration (using localhost and port 5768) by saving this to `.vscode/launch.json` or using the GUI:
|
144
|
+
|
145
|
+
```json
|
146
|
+
// .vscode/launch.json
|
147
|
+
{
|
148
|
+
// Use IntelliSense to learn about possible attributes.
|
149
|
+
// Hover to view descriptions of existing attributes.
|
150
|
+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
151
|
+
"version": "0.2.0",
|
152
|
+
"configurations": [
|
153
|
+
{
|
154
|
+
"name": "Plain: Attach to Django",
|
155
|
+
"type": "python",
|
156
|
+
"request": "attach",
|
157
|
+
"connect": {
|
158
|
+
"host": "localhost",
|
159
|
+
"port": 5678
|
160
|
+
},
|
161
|
+
"pathMappings": [
|
162
|
+
{
|
163
|
+
"localRoot": "${workspaceFolder}",
|
164
|
+
"remoteRoot": "."
|
165
|
+
}
|
166
|
+
],
|
167
|
+
"justMyCode": true,
|
168
|
+
"django": true
|
169
|
+
}
|
170
|
+
]
|
171
|
+
}
|
172
|
+
```
|
173
|
+
|
174
|
+
Then in the "Run and Debug" tab, you can click the green arrow next to "Plain: Attach to Django" to start the debugger.
|
175
|
+
|
176
|
+
In your terminal is should tell you it was attached, and when you hit a breakpoint you'll see the debugger information in VS Code.
|
177
|
+
If Django's runserver reloads, you'll be prompted to reattach by clicking the green arrow again.
|
178
|
+
|
@@ -0,0 +1,150 @@
|
|
1
|
+
<!-- This file is compiled from plain-dev/plain/dev/README.md. Do not edit this file directly. -->
|
2
|
+
|
3
|
+
# plain-dev
|
4
|
+
|
5
|
+
A single command to run everything you need for local Plain development.
|
6
|
+
|
7
|
+

|
8
|
+
|
9
|
+
The `plain dev` command runs a combination of local commands + a Docker container for your database.
|
10
|
+
|
11
|
+
The following processes will run simultaneously (some will only run if they are detected as available):
|
12
|
+
|
13
|
+
<!-- - [`manage.py runserver` (and migrations)](#runserver)
|
14
|
+
- [`plain-models start --logs`](#plain-models)
|
15
|
+
- [`plain-tailwind compile --watch`](#plain-tailwind)
|
16
|
+
- [`npm run watch`](#package-json)
|
17
|
+
- [`stripe listen --forward-to`](#stripe)
|
18
|
+
- [`ngrok http --subdomain`](#ngrok)
|
19
|
+
|
20
|
+
It also comes with [debugging](#debugging) tools to make local debugging easier with VS Code. -->
|
21
|
+
|
22
|
+
## Installation
|
23
|
+
|
24
|
+
```sh
|
25
|
+
pip install plain-dev
|
26
|
+
```
|
27
|
+
|
28
|
+
If you have `plain-models` installed (i.e. you're using a database),
|
29
|
+
then add `DATABASE_URL` to your `.env` file.
|
30
|
+
|
31
|
+
```sh
|
32
|
+
DATABASE_URL=postgres://postgres:postgres@localhost:54321/postgres
|
33
|
+
```
|
34
|
+
|
35
|
+
```toml
|
36
|
+
# pyproject.toml
|
37
|
+
[tool.plain.dev.services]
|
38
|
+
postgres = {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"}
|
39
|
+
```
|
40
|
+
|
41
|
+
```sh
|
42
|
+
plain dev
|
43
|
+
```
|
44
|
+
|
45
|
+
## `plain dev`
|
46
|
+
|
47
|
+
### Default processes
|
48
|
+
|
49
|
+
- plain preflight
|
50
|
+
- gunicorn
|
51
|
+
- migrations
|
52
|
+
- tailwind
|
53
|
+
|
54
|
+
### Custom processes
|
55
|
+
|
56
|
+
- package.json "dev" script
|
57
|
+
- pyproject.toml `tool.plain.dev.run = {command = "..."}`
|
58
|
+
|
59
|
+
### GitHub Codespaces
|
60
|
+
|
61
|
+
The `BASE_URL` setting is automatically set to the Codespace URL.
|
62
|
+
|
63
|
+
TODO
|
64
|
+
|
65
|
+
## `plain dev db`
|
66
|
+
|
67
|
+
Only supports Postgres currently.
|
68
|
+
|
69
|
+
- snapshot
|
70
|
+
- import
|
71
|
+
- export
|
72
|
+
|
73
|
+
|
74
|
+
## Development processes
|
75
|
+
|
76
|
+
### Gunicorn
|
77
|
+
|
78
|
+
The key process here is still `manage.py runserver`.
|
79
|
+
But, before that runs, it will also wait for the database to be available and run `manage.py migrate`.
|
80
|
+
|
81
|
+
### plain-models
|
82
|
+
|
83
|
+
If [`plain-models`](https://github.com/plainpackages/plain-models) is installed, it will automatically start and show the logs of the running database container.
|
84
|
+
|
85
|
+
### plain-tailwind
|
86
|
+
|
87
|
+
If [`plain-tailwind`](https://github.com/plainpackages/plain-tailwind) is installed, it will automatically run the Tailwind `compile --watch` process.
|
88
|
+
|
89
|
+
## Debugging
|
90
|
+
|
91
|
+
[View on YouTube →](https://www.youtube.com/watch?v=pG0KaJSVyBw)
|
92
|
+
|
93
|
+
Since `plain work` runs multiple processes at once, the regular [pdb](https://docs.python.org/3/library/pdb.html) debuggers can be hard to use.
|
94
|
+
Instead, we include [microsoft/debugpy](https://github.com/microsoft/debugpy) and an `attach` function to make it even easier to use VS Code's debugger.
|
95
|
+
|
96
|
+
First, import and run the `debug.attach()` function:
|
97
|
+
|
98
|
+
```python
|
99
|
+
class HomeView(TemplateView):
|
100
|
+
template_name = "home.html"
|
101
|
+
|
102
|
+
def get_context(self, **kwargs):
|
103
|
+
context = super().get_context(**kwargs)
|
104
|
+
|
105
|
+
# Make sure the debugger is attached (will need to be if runserver reloads)
|
106
|
+
from plain.work import debug; debug.attach()
|
107
|
+
|
108
|
+
# Add a breakpoint (or use the gutter in VSCode to add one)
|
109
|
+
breakpoint()
|
110
|
+
|
111
|
+
return context
|
112
|
+
```
|
113
|
+
|
114
|
+
When you load the page, you'll see "Waiting for debugger to attach...".
|
115
|
+
|
116
|
+
Add a new VS Code debug configuration (using localhost and port 5768) by saving this to `.vscode/launch.json` or using the GUI:
|
117
|
+
|
118
|
+
```json
|
119
|
+
// .vscode/launch.json
|
120
|
+
{
|
121
|
+
// Use IntelliSense to learn about possible attributes.
|
122
|
+
// Hover to view descriptions of existing attributes.
|
123
|
+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
124
|
+
"version": "0.2.0",
|
125
|
+
"configurations": [
|
126
|
+
{
|
127
|
+
"name": "Plain: Attach to Django",
|
128
|
+
"type": "python",
|
129
|
+
"request": "attach",
|
130
|
+
"connect": {
|
131
|
+
"host": "localhost",
|
132
|
+
"port": 5678
|
133
|
+
},
|
134
|
+
"pathMappings": [
|
135
|
+
{
|
136
|
+
"localRoot": "${workspaceFolder}",
|
137
|
+
"remoteRoot": "."
|
138
|
+
}
|
139
|
+
],
|
140
|
+
"justMyCode": true,
|
141
|
+
"django": true
|
142
|
+
}
|
143
|
+
]
|
144
|
+
}
|
145
|
+
```
|
146
|
+
|
147
|
+
Then in the "Run and Debug" tab, you can click the green arrow next to "Plain: Attach to Django" to start the debugger.
|
148
|
+
|
149
|
+
In your terminal is should tell you it was attached, and when you hit a breakpoint you'll see the debugger information in VS Code.
|
150
|
+
If Django's runserver reloads, you'll be prompted to reattach by clicking the green arrow again.
|
@@ -0,0 +1,148 @@
|
|
1
|
+
# plain-dev
|
2
|
+
|
3
|
+
A single command to run everything you need for local Plain development.
|
4
|
+
|
5
|
+

|
6
|
+
|
7
|
+
The `plain dev` command runs a combination of local commands + a Docker container for your database.
|
8
|
+
|
9
|
+
The following processes will run simultaneously (some will only run if they are detected as available):
|
10
|
+
|
11
|
+
<!-- - [`manage.py runserver` (and migrations)](#runserver)
|
12
|
+
- [`plain-models start --logs`](#plain-models)
|
13
|
+
- [`plain-tailwind compile --watch`](#plain-tailwind)
|
14
|
+
- [`npm run watch`](#package-json)
|
15
|
+
- [`stripe listen --forward-to`](#stripe)
|
16
|
+
- [`ngrok http --subdomain`](#ngrok)
|
17
|
+
|
18
|
+
It also comes with [debugging](#debugging) tools to make local debugging easier with VS Code. -->
|
19
|
+
|
20
|
+
## Installation
|
21
|
+
|
22
|
+
```sh
|
23
|
+
pip install plain-dev
|
24
|
+
```
|
25
|
+
|
26
|
+
If you have `plain-models` installed (i.e. you're using a database),
|
27
|
+
then add `DATABASE_URL` to your `.env` file.
|
28
|
+
|
29
|
+
```sh
|
30
|
+
DATABASE_URL=postgres://postgres:postgres@localhost:54321/postgres
|
31
|
+
```
|
32
|
+
|
33
|
+
```toml
|
34
|
+
# pyproject.toml
|
35
|
+
[tool.plain.dev.services]
|
36
|
+
postgres = {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"}
|
37
|
+
```
|
38
|
+
|
39
|
+
```sh
|
40
|
+
plain dev
|
41
|
+
```
|
42
|
+
|
43
|
+
## `plain dev`
|
44
|
+
|
45
|
+
### Default processes
|
46
|
+
|
47
|
+
- plain preflight
|
48
|
+
- gunicorn
|
49
|
+
- migrations
|
50
|
+
- tailwind
|
51
|
+
|
52
|
+
### Custom processes
|
53
|
+
|
54
|
+
- package.json "dev" script
|
55
|
+
- pyproject.toml `tool.plain.dev.run = {command = "..."}`
|
56
|
+
|
57
|
+
### GitHub Codespaces
|
58
|
+
|
59
|
+
The `BASE_URL` setting is automatically set to the Codespace URL.
|
60
|
+
|
61
|
+
TODO
|
62
|
+
|
63
|
+
## `plain dev db`
|
64
|
+
|
65
|
+
Only supports Postgres currently.
|
66
|
+
|
67
|
+
- snapshot
|
68
|
+
- import
|
69
|
+
- export
|
70
|
+
|
71
|
+
|
72
|
+
## Development processes
|
73
|
+
|
74
|
+
### Gunicorn
|
75
|
+
|
76
|
+
The key process here is still `manage.py runserver`.
|
77
|
+
But, before that runs, it will also wait for the database to be available and run `manage.py migrate`.
|
78
|
+
|
79
|
+
### plain-models
|
80
|
+
|
81
|
+
If [`plain-models`](https://github.com/plainpackages/plain-models) is installed, it will automatically start and show the logs of the running database container.
|
82
|
+
|
83
|
+
### plain-tailwind
|
84
|
+
|
85
|
+
If [`plain-tailwind`](https://github.com/plainpackages/plain-tailwind) is installed, it will automatically run the Tailwind `compile --watch` process.
|
86
|
+
|
87
|
+
## Debugging
|
88
|
+
|
89
|
+
[View on YouTube →](https://www.youtube.com/watch?v=pG0KaJSVyBw)
|
90
|
+
|
91
|
+
Since `plain work` runs multiple processes at once, the regular [pdb](https://docs.python.org/3/library/pdb.html) debuggers can be hard to use.
|
92
|
+
Instead, we include [microsoft/debugpy](https://github.com/microsoft/debugpy) and an `attach` function to make it even easier to use VS Code's debugger.
|
93
|
+
|
94
|
+
First, import and run the `debug.attach()` function:
|
95
|
+
|
96
|
+
```python
|
97
|
+
class HomeView(TemplateView):
|
98
|
+
template_name = "home.html"
|
99
|
+
|
100
|
+
def get_context(self, **kwargs):
|
101
|
+
context = super().get_context(**kwargs)
|
102
|
+
|
103
|
+
# Make sure the debugger is attached (will need to be if runserver reloads)
|
104
|
+
from plain.work import debug; debug.attach()
|
105
|
+
|
106
|
+
# Add a breakpoint (or use the gutter in VSCode to add one)
|
107
|
+
breakpoint()
|
108
|
+
|
109
|
+
return context
|
110
|
+
```
|
111
|
+
|
112
|
+
When you load the page, you'll see "Waiting for debugger to attach...".
|
113
|
+
|
114
|
+
Add a new VS Code debug configuration (using localhost and port 5768) by saving this to `.vscode/launch.json` or using the GUI:
|
115
|
+
|
116
|
+
```json
|
117
|
+
// .vscode/launch.json
|
118
|
+
{
|
119
|
+
// Use IntelliSense to learn about possible attributes.
|
120
|
+
// Hover to view descriptions of existing attributes.
|
121
|
+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
122
|
+
"version": "0.2.0",
|
123
|
+
"configurations": [
|
124
|
+
{
|
125
|
+
"name": "Plain: Attach to Django",
|
126
|
+
"type": "python",
|
127
|
+
"request": "attach",
|
128
|
+
"connect": {
|
129
|
+
"host": "localhost",
|
130
|
+
"port": 5678
|
131
|
+
},
|
132
|
+
"pathMappings": [
|
133
|
+
{
|
134
|
+
"localRoot": "${workspaceFolder}",
|
135
|
+
"remoteRoot": "."
|
136
|
+
}
|
137
|
+
],
|
138
|
+
"justMyCode": true,
|
139
|
+
"django": true
|
140
|
+
}
|
141
|
+
]
|
142
|
+
}
|
143
|
+
```
|
144
|
+
|
145
|
+
Then in the "Run and Debug" tab, you can click the green arrow next to "Plain: Attach to Django" to start the debugger.
|
146
|
+
|
147
|
+
In your terminal is should tell you it was attached, and when you hit a breakpoint you'll see the debugger information in VS Code.
|
148
|
+
If Django's runserver reloads, you'll be prompted to reattach by clicking the green arrow again.
|
@@ -0,0 +1,166 @@
|
|
1
|
+
import json
|
2
|
+
import os
|
3
|
+
import subprocess
|
4
|
+
import sys
|
5
|
+
from importlib.util import find_spec
|
6
|
+
from pathlib import Path
|
7
|
+
|
8
|
+
import click
|
9
|
+
from honcho.manager import Manager as HonchoManager
|
10
|
+
|
11
|
+
from plain.runtime import APP_PATH
|
12
|
+
|
13
|
+
from .db import cli as db_cli
|
14
|
+
from .pid import Pid
|
15
|
+
from .services import Services
|
16
|
+
from .utils import has_pyproject_toml, plainpackage_installed
|
17
|
+
|
18
|
+
try:
|
19
|
+
import tomllib
|
20
|
+
except ModuleNotFoundError:
|
21
|
+
import tomli as tomllib
|
22
|
+
|
23
|
+
|
24
|
+
@click.group(invoke_without_command=True)
|
25
|
+
@click.pass_context
|
26
|
+
@click.option(
|
27
|
+
"--port",
|
28
|
+
"-p",
|
29
|
+
default=8000,
|
30
|
+
type=int,
|
31
|
+
help="Port to run the web server on",
|
32
|
+
envvar="PORT",
|
33
|
+
)
|
34
|
+
def cli(ctx, port):
|
35
|
+
"""Start local development"""
|
36
|
+
|
37
|
+
if ctx.invoked_subcommand:
|
38
|
+
return
|
39
|
+
|
40
|
+
returncode = Dev(port=port).run()
|
41
|
+
if returncode:
|
42
|
+
sys.exit(returncode)
|
43
|
+
|
44
|
+
|
45
|
+
@cli.command()
|
46
|
+
def services():
|
47
|
+
"""Start additional services defined in pyproject.toml"""
|
48
|
+
Services().run()
|
49
|
+
|
50
|
+
|
51
|
+
class Dev:
|
52
|
+
def __init__(self, *, port):
|
53
|
+
self.manager = HonchoManager()
|
54
|
+
self.port = port
|
55
|
+
self.plain_env = {
|
56
|
+
**os.environ,
|
57
|
+
"PYTHONUNBUFFERED": "true",
|
58
|
+
}
|
59
|
+
self.custom_process_env = {
|
60
|
+
**self.plain_env,
|
61
|
+
"PORT": str(self.port),
|
62
|
+
"PYTHONPATH": os.path.join(APP_PATH.parent, "app"),
|
63
|
+
}
|
64
|
+
|
65
|
+
def run(self):
|
66
|
+
pid = Pid()
|
67
|
+
pid.write()
|
68
|
+
|
69
|
+
try:
|
70
|
+
self.add_csrf_trusted_origins()
|
71
|
+
self.run_preflight()
|
72
|
+
self.add_gunicorn()
|
73
|
+
self.add_tailwind()
|
74
|
+
self.add_pyproject_run()
|
75
|
+
self.add_services()
|
76
|
+
|
77
|
+
self.manager.loop()
|
78
|
+
|
79
|
+
return self.manager.returncode
|
80
|
+
finally:
|
81
|
+
pid.rm()
|
82
|
+
|
83
|
+
def add_csrf_trusted_origins(self):
|
84
|
+
if "PLAIN_CSRF_TRUSTED_ORIGINS" in os.environ:
|
85
|
+
return
|
86
|
+
|
87
|
+
csrf_trusted_origins = json.dumps(
|
88
|
+
[f"http://localhost:{self.port}", f"http://127.0.0.1:{self.port}"]
|
89
|
+
)
|
90
|
+
|
91
|
+
click.secho(
|
92
|
+
f"Automatically set PLAIN_CSRF_TRUSTED_ORIGINS={click.style(csrf_trusted_origins, underline=True)}",
|
93
|
+
bold=True,
|
94
|
+
)
|
95
|
+
|
96
|
+
# Set BASE_URL for plain and custom processes
|
97
|
+
self.plain_env["PLAIN_CSRF_TRUSTED_ORIGINS"] = csrf_trusted_origins
|
98
|
+
self.custom_process_env["PLAIN_CSRF_TRUSTED_ORIGINS"] = csrf_trusted_origins
|
99
|
+
|
100
|
+
def run_preflight(self):
|
101
|
+
if subprocess.run(["plain", "preflight"], env=self.plain_env).returncode:
|
102
|
+
click.secho("Preflight check failed!", fg="red")
|
103
|
+
sys.exit(1)
|
104
|
+
|
105
|
+
def add_gunicorn(self):
|
106
|
+
plain_db_installed = find_spec("plain.models") is not None
|
107
|
+
|
108
|
+
# TODO not necessarily watching the right .env...
|
109
|
+
# could return path from env.load?
|
110
|
+
extra_watch_files = []
|
111
|
+
for f in os.listdir(APP_PATH.parent):
|
112
|
+
if f.startswith(".env"):
|
113
|
+
# Will include some extra, but good enough for now
|
114
|
+
extra_watch_files.append(f)
|
115
|
+
|
116
|
+
reload_extra = " ".join(f"--reload-extra-file {f}" for f in extra_watch_files)
|
117
|
+
gunicorn = f"gunicorn --bind 127.0.0.1:{self.port} --reload plain.wsgi:app --timeout 60 --access-logfile - --error-logfile - {reload_extra} --access-logformat '\"%(r)s\" status=%(s)s length=%(b)s dur=%(M)sms'"
|
118
|
+
|
119
|
+
if plain_db_installed:
|
120
|
+
runserver_cmd = (
|
121
|
+
f"plain models db-wait && plain legacy migrate && {gunicorn}"
|
122
|
+
)
|
123
|
+
else:
|
124
|
+
runserver_cmd = gunicorn
|
125
|
+
|
126
|
+
if "WEB_CONCURRENCY" not in self.plain_env:
|
127
|
+
# Default to two workers so request log etc are less
|
128
|
+
# likely to get locked up
|
129
|
+
self.plain_env["WEB_CONCURRENCY"] = "2"
|
130
|
+
|
131
|
+
self.manager.add_process("plain", runserver_cmd, env=self.plain_env)
|
132
|
+
|
133
|
+
def add_tailwind(self):
|
134
|
+
if not plainpackage_installed("tailwind"):
|
135
|
+
return
|
136
|
+
|
137
|
+
self.manager.add_process("tailwind", "plain tailwind compile --watch")
|
138
|
+
|
139
|
+
def add_pyproject_run(self):
|
140
|
+
if not has_pyproject_toml(APP_PATH.parent):
|
141
|
+
return
|
142
|
+
|
143
|
+
with open(Path(APP_PATH.parent, "pyproject.toml"), "rb") as f:
|
144
|
+
pyproject = tomllib.load(f)
|
145
|
+
|
146
|
+
for name, data in (
|
147
|
+
pyproject.get("tool", {}).get("plain", {}).get("dev", {}).get("run", {})
|
148
|
+
).items():
|
149
|
+
env = {
|
150
|
+
**self.custom_process_env,
|
151
|
+
**data.get("env", {}),
|
152
|
+
}
|
153
|
+
self.manager.add_process(name, data["cmd"], env=env)
|
154
|
+
|
155
|
+
def add_services(self):
|
156
|
+
services = Services.get_services(APP_PATH.parent)
|
157
|
+
for name, data in services.items():
|
158
|
+
env = {
|
159
|
+
**os.environ,
|
160
|
+
"PYTHONUNBUFFERED": "true",
|
161
|
+
**data.get("env", {}),
|
162
|
+
}
|
163
|
+
self.manager.add_process(name, data["cmd"], env=env)
|
164
|
+
|
165
|
+
|
166
|
+
cli.add_command(db_cli)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import importlib
|
2
|
+
from pathlib import Path
|
3
|
+
|
4
|
+
from plain.packages import PackageConfig
|
5
|
+
from plain.runtime import settings
|
6
|
+
|
7
|
+
|
8
|
+
class Config(PackageConfig):
|
9
|
+
name = "plain.dev"
|
10
|
+
|
11
|
+
def ready(self):
|
12
|
+
# Symlink the plain package into .plain so we can look at it easily
|
13
|
+
plain_path = Path(
|
14
|
+
importlib.util.find_spec("plain.runtime").origin
|
15
|
+
).parent.parent
|
16
|
+
if not settings.PLAIN_TEMP_PATH.exists():
|
17
|
+
settings.PLAIN_TEMP_PATH.mkdir()
|
18
|
+
src_path = settings.PLAIN_TEMP_PATH / "src"
|
19
|
+
if plain_path.exists() and not src_path.exists():
|
20
|
+
src_path.symlink_to(plain_path)
|