plain.dev 0.26.1__tar.gz → 0.27.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.26.1 → plain_dev-0.27.0}/PKG-INFO +1 -1
- {plain_dev-0.26.1 → plain_dev-0.27.0}/plain/dev/cli.py +84 -55
- {plain_dev-0.26.1 → plain_dev-0.27.0}/plain/dev/poncho/manager.py +15 -7
- {plain_dev-0.26.1 → plain_dev-0.27.0}/pyproject.toml +1 -1
- {plain_dev-0.26.1 → plain_dev-0.27.0}/.gitignore +0 -0
- {plain_dev-0.26.1 → plain_dev-0.27.0}/LICENSE +0 -0
- {plain_dev-0.26.1 → plain_dev-0.27.0}/README.md +0 -0
- {plain_dev-0.26.1 → plain_dev-0.27.0}/plain/dev/README.md +0 -0
- {plain_dev-0.26.1 → plain_dev-0.27.0}/plain/dev/__init__.py +0 -0
- {plain_dev-0.26.1 → plain_dev-0.27.0}/plain/dev/contribute/README.md +0 -0
- {plain_dev-0.26.1 → plain_dev-0.27.0}/plain/dev/contribute/__init__.py +0 -0
- {plain_dev-0.26.1 → plain_dev-0.27.0}/plain/dev/contribute/cli.py +0 -0
- {plain_dev-0.26.1 → plain_dev-0.27.0}/plain/dev/debug.py +0 -0
- {plain_dev-0.26.1 → plain_dev-0.27.0}/plain/dev/default_settings.py +0 -0
- {plain_dev-0.26.1 → plain_dev-0.27.0}/plain/dev/entrypoints.py +0 -0
- {plain_dev-0.26.1 → plain_dev-0.27.0}/plain/dev/gunicorn_logging.json +0 -0
- {plain_dev-0.26.1 → plain_dev-0.27.0}/plain/dev/mkcert.py +0 -0
- {plain_dev-0.26.1 → plain_dev-0.27.0}/plain/dev/pdb.py +0 -0
- {plain_dev-0.26.1 → plain_dev-0.27.0}/plain/dev/poncho/__init__.py +0 -0
- {plain_dev-0.26.1 → plain_dev-0.27.0}/plain/dev/poncho/color.py +0 -0
- {plain_dev-0.26.1 → plain_dev-0.27.0}/plain/dev/poncho/compat.py +0 -0
- {plain_dev-0.26.1 → plain_dev-0.27.0}/plain/dev/poncho/printer.py +0 -0
- {plain_dev-0.26.1 → plain_dev-0.27.0}/plain/dev/poncho/process.py +0 -0
- {plain_dev-0.26.1 → plain_dev-0.27.0}/plain/dev/precommit/__init__.py +0 -0
- {plain_dev-0.26.1 → plain_dev-0.27.0}/plain/dev/precommit/cli.py +0 -0
- {plain_dev-0.26.1 → plain_dev-0.27.0}/plain/dev/requests.py +0 -0
- {plain_dev-0.26.1 → plain_dev-0.27.0}/plain/dev/services.py +0 -0
- {plain_dev-0.26.1 → plain_dev-0.27.0}/plain/dev/templates/dev/requests.html +0 -0
- {plain_dev-0.26.1 → plain_dev-0.27.0}/plain/dev/urls.py +0 -0
- {plain_dev-0.26.1 → plain_dev-0.27.0}/plain/dev/utils.py +0 -0
- {plain_dev-0.26.1 → plain_dev-0.27.0}/plain/dev/views.py +0 -0
- {plain_dev-0.26.1 → plain_dev-0.27.0}/tests/settings.py +0 -0
@@ -2,8 +2,10 @@ import importlib
|
|
2
2
|
import json
|
3
3
|
import os
|
4
4
|
import platform
|
5
|
+
import signal
|
5
6
|
import subprocess
|
6
7
|
import sys
|
8
|
+
import threading
|
7
9
|
import time
|
8
10
|
import tomllib
|
9
11
|
from importlib.metadata import entry_points
|
@@ -21,7 +23,7 @@ from plain.runtime import APP_PATH, settings
|
|
21
23
|
from .mkcert import MkcertManager
|
22
24
|
from .poncho.manager import Manager as PonchoManager
|
23
25
|
from .poncho.printer import Printer
|
24
|
-
from .services import Services
|
26
|
+
from .services import Services, ServicesPid
|
25
27
|
from .utils import has_pyproject_toml
|
26
28
|
|
27
29
|
ENTRYPOINT_GROUP = "plain.dev"
|
@@ -153,30 +155,6 @@ class Dev:
|
|
153
155
|
"PLAIN_DEV_URL": self.url,
|
154
156
|
}
|
155
157
|
|
156
|
-
self.console = Console(markup=False, highlight=False)
|
157
|
-
self.poncho = PonchoManager(printer=Printer(lambda s: self.console.out(s)))
|
158
|
-
|
159
|
-
def run(self):
|
160
|
-
mkcert_manager = MkcertManager()
|
161
|
-
mkcert_manager.setup_mkcert(install_path=Path.home() / ".plain" / "dev")
|
162
|
-
self.ssl_cert_path, self.ssl_key_path = mkcert_manager.generate_certs(
|
163
|
-
domain=self.hostname,
|
164
|
-
storage_path=Path(settings.PLAIN_TEMP_PATH) / "dev" / "certs",
|
165
|
-
)
|
166
|
-
|
167
|
-
self.symlink_plain_src()
|
168
|
-
self.modify_hosts_file()
|
169
|
-
self.set_csrf_and_allowed_hosts()
|
170
|
-
self.run_preflight()
|
171
|
-
|
172
|
-
# Processes for poncho to run simultaneously
|
173
|
-
self.add_gunicorn()
|
174
|
-
self.add_entrypoints()
|
175
|
-
self.add_pyproject_run()
|
176
|
-
self.add_services()
|
177
|
-
|
178
|
-
click.secho("\nStarting dev...", italic=True, dim=True)
|
179
|
-
|
180
158
|
if self.tunnel_url:
|
181
159
|
status_bar = Columns(
|
182
160
|
[
|
@@ -205,12 +183,91 @@ class Dev:
|
|
205
183
|
],
|
206
184
|
expand=True,
|
207
185
|
)
|
186
|
+
self.console = Console(markup=False, highlight=False)
|
187
|
+
self.console_status = self.console.status(status_bar)
|
188
|
+
|
189
|
+
self.poncho = PonchoManager(printer=Printer(lambda s: self.console.out(s)))
|
190
|
+
|
191
|
+
def run(self):
|
192
|
+
mkcert_manager = MkcertManager()
|
193
|
+
mkcert_manager.setup_mkcert(install_path=Path.home() / ".plain" / "dev")
|
194
|
+
self.ssl_cert_path, self.ssl_key_path = mkcert_manager.generate_certs(
|
195
|
+
domain=self.hostname,
|
196
|
+
storage_path=Path(settings.PLAIN_TEMP_PATH) / "dev" / "certs",
|
197
|
+
)
|
208
198
|
|
209
|
-
|
199
|
+
self.symlink_plain_src()
|
200
|
+
self.modify_hosts_file()
|
201
|
+
self.set_csrf_and_allowed_hosts()
|
202
|
+
self.run_preflight()
|
203
|
+
|
204
|
+
# If we start services ourselves, we should manage the pidfile
|
205
|
+
services_pid = None
|
206
|
+
|
207
|
+
# Services start first (or are already running from a separate command)
|
208
|
+
if Services.are_running():
|
209
|
+
click.secho("Services already running", fg="yellow")
|
210
|
+
elif services := Services.get_services(APP_PATH.parent):
|
211
|
+
click.secho("\nStarting services...", italic=True, dim=True)
|
212
|
+
services_pid = ServicesPid()
|
213
|
+
services_pid.write()
|
214
|
+
|
215
|
+
for name, data in services.items():
|
216
|
+
env = {
|
217
|
+
**os.environ,
|
218
|
+
"PYTHONUNBUFFERED": "true",
|
219
|
+
**data.get("env", {}),
|
220
|
+
}
|
221
|
+
self.poncho.add_process(name, data["cmd"], env=env)
|
222
|
+
|
223
|
+
# If plain.models is installed (common) then we
|
224
|
+
# will do a couple extra things before starting all of the app-related
|
225
|
+
# processes (this way they don't all have to db-wait or anything)
|
226
|
+
if find_spec("plain.models") is not None:
|
227
|
+
# Use a custom signal to tell the main thread to add
|
228
|
+
# the app processes once the db is ready
|
229
|
+
signal.signal(signal.SIGUSR1, self.start_app)
|
230
|
+
|
231
|
+
def _thread(env):
|
232
|
+
subprocess.run(["plain", "models", "db-wait"], env=env, check=True)
|
233
|
+
subprocess.run(["plain", "migrate", "--backup"], env=env, check=True)
|
234
|
+
# preflight with db?
|
235
|
+
os.kill(os.getpid(), signal.SIGUSR1)
|
236
|
+
|
237
|
+
thread = threading.Thread(
|
238
|
+
target=_thread, daemon=True, args=(self.plain_env,)
|
239
|
+
)
|
240
|
+
thread.start()
|
241
|
+
else:
|
242
|
+
# Start the app processes immediately
|
243
|
+
self.start_app(None, None)
|
244
|
+
|
245
|
+
try:
|
246
|
+
# Start processes we know about and block the main thread
|
210
247
|
self.poncho.loop()
|
211
248
|
|
249
|
+
# Remove the status bar
|
250
|
+
self.console_status.stop()
|
251
|
+
finally:
|
252
|
+
# Make sure the services pid gets removed if we set it
|
253
|
+
if services_pid:
|
254
|
+
services_pid.rm()
|
255
|
+
|
212
256
|
return self.poncho.returncode
|
213
257
|
|
258
|
+
def start_app(self, signum, frame):
|
259
|
+
# This runs in the main thread when SIGUSR1 is received
|
260
|
+
# (or called directly if no thread).
|
261
|
+
click.secho("\nStarting app...", italic=True, dim=True)
|
262
|
+
|
263
|
+
# Manually start the status bar now so it isn't bungled by
|
264
|
+
# another thread checking db stuff...
|
265
|
+
self.console_status.start()
|
266
|
+
|
267
|
+
self.add_gunicorn()
|
268
|
+
self.add_entrypoints()
|
269
|
+
self.add_pyproject_run()
|
270
|
+
|
214
271
|
def symlink_plain_src(self):
|
215
272
|
"""Symlink the plain package into .plain so we can look at it easily"""
|
216
273
|
plain_path = Path(
|
@@ -318,8 +375,6 @@ class Dev:
|
|
318
375
|
sys.exit(1)
|
319
376
|
|
320
377
|
def add_gunicorn(self):
|
321
|
-
plain_db_installed = find_spec("plain.models") is not None
|
322
|
-
|
323
378
|
# Watch .env files for reload
|
324
379
|
extra_watch_files = []
|
325
380
|
for f in os.listdir(APP_PATH.parent):
|
@@ -355,14 +410,7 @@ class Dev:
|
|
355
410
|
]
|
356
411
|
gunicorn = " ".join(gunicorn_cmd)
|
357
412
|
|
358
|
-
|
359
|
-
runserver_cmd = (
|
360
|
-
f"plain models db-wait && plain migrate --backup && {gunicorn}"
|
361
|
-
)
|
362
|
-
else:
|
363
|
-
runserver_cmd = gunicorn
|
364
|
-
|
365
|
-
self.poncho.add_process("plain", runserver_cmd, env=self.plain_env)
|
413
|
+
self.poncho.add_process("plain", gunicorn, env=self.plain_env)
|
366
414
|
|
367
415
|
def add_entrypoints(self):
|
368
416
|
for entry_point in entry_points().select(group=ENTRYPOINT_GROUP):
|
@@ -389,22 +437,3 @@ class Dev:
|
|
389
437
|
**data.get("env", {}),
|
390
438
|
}
|
391
439
|
self.poncho.add_process(name, data["cmd"], env=env)
|
392
|
-
|
393
|
-
def add_services(self):
|
394
|
-
"""Services are things that also run during tests (like a database), and are critical for the app to function."""
|
395
|
-
|
396
|
-
if Services.are_running():
|
397
|
-
click.secho("Services already running", fg="yellow")
|
398
|
-
return
|
399
|
-
|
400
|
-
# TODO need to set services pid here somehow...
|
401
|
-
|
402
|
-
# Split each service into a separate process
|
403
|
-
services = Services.get_services(APP_PATH.parent)
|
404
|
-
for name, data in services.items():
|
405
|
-
env = {
|
406
|
-
**os.environ,
|
407
|
-
"PYTHONUNBUFFERED": "true",
|
408
|
-
**data.get("env", {}),
|
409
|
-
}
|
410
|
-
self.poncho.add_process(name, data["cmd"], env=env)
|
@@ -76,6 +76,10 @@ class Manager:
|
|
76
76
|
# Update printer width to accommodate this process name
|
77
77
|
self._printer.width = max(self._printer.width, len(name))
|
78
78
|
|
79
|
+
# If the loop is already running, we need to start the process now.
|
80
|
+
if self._is_running():
|
81
|
+
self._start_process(name)
|
82
|
+
|
79
83
|
return proc
|
80
84
|
|
81
85
|
def loop(self):
|
@@ -97,7 +101,8 @@ class Manager:
|
|
97
101
|
signal.signal(signal.SIGTERM, _terminate)
|
98
102
|
signal.signal(signal.SIGINT, _terminate)
|
99
103
|
|
100
|
-
self.
|
104
|
+
for name in self._processes.keys():
|
105
|
+
self._start_process(name)
|
101
106
|
|
102
107
|
exit = False
|
103
108
|
exit_start = None
|
@@ -172,12 +177,15 @@ class Manager:
|
|
172
177
|
else:
|
173
178
|
self._procmgr.terminate(p["pid"])
|
174
179
|
|
175
|
-
def
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
180
|
+
def _start_process(self, name):
|
181
|
+
p = self._processes[name]
|
182
|
+
p["process"] = multiprocessing.Process(
|
183
|
+
name=name, target=p["obj"].run, args=(self.events, True)
|
184
|
+
)
|
185
|
+
p["process"].start()
|
186
|
+
|
187
|
+
def _is_running(self):
|
188
|
+
return any(p.get("pid") is not None for _, p in self._processes.items())
|
181
189
|
|
182
190
|
def _all_started(self):
|
183
191
|
return all(p.get("pid") is not None for _, p in self._processes.items())
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|