'Django manage.py runserver graceful reloading
I am working on a Django project, which integrates a webcam and OpenCV. For the webcam access, I use following code. The webcam can be released if I use Ctrl + C
to end a running server, but if the server reloads itself after the code change, the webcam can not be released properly and therefore will be not available. How can I detect hot reloading so I can close the webcam properly?
I am aware of the option of forbidding hot reloading but this is rather uncomfortable. Is there any option I can realize programmatically?
class VideoCamera(object):
def __init__(self):
self.video = None
def __del__(self):
if self.video is not None and self.video.isOpened():
self.video.release()
def get_frame(self):
try:
self.video = cv2.VideoCapture(0)
success, image = self.video.read()
self.video.release()
ret, jpeg = cv2.imencode('.jpg', image)
return jpeg.tobytes()
except (SystemExit, KeyboardInterrupt, Exception) as e:
self.video.release()
raise e
Solution 1:[1]
Few ideas
1) You could connect on the file_change
signal that is triggered on file change before reload takes place
def notify_file_changed(self, path):
results = file_changed.send(sender=self, file_path=path)
logger.debug('%s notified as changed. Signal results: %s.', path, results)
if not any(res[1] for res in results):
trigger_reload(path)
2) Simply monkey patch trigger reload function and inject webcam closing code
Solution 2:[2]
If your class is a singleton, which it appears to be, create an instance of your class in its file and then add Django signal handlers to perform the clean up. As it is generally not recommended to use __del__
, I would recommend you add a signal (system signals like SIGINT/ctrl+C) handler which forwards it as a Django signal. This means, listen on file_changed
from django.utils.autoreload
and SIGINT
.
To use the singleton in other files, import it with from my_app.singleton import my_singleton
.
# my_app/singleton.py
class MySingleton:
def __init__(self):
"""Init code here"""
def close(self):
"""Shutdown code here"""
my_singleton = MySingleton()
Then, register your signal handlers for your app. This is the standard AFAIK.
# my_app/apps.py
from django.apps import AppConfig
class MyAppConfig(AppConfig):
name = "my_app"
def ready(self):
import my_app.signals
The signal handler file itself, import the singleton and close it on shutdown. This is called on file_changed and SIGINT.
# my_app/signals.py
from django.dispatch import receiver
from django.utils.autoreload import file_changed
from my_app.signal_definitions import system_shutdown_signal
from my_app.singleton import my_singleton
@receiver(file_changed)
@receiver(system_shutdown_signal)
def my_shutdown_handler(sender, **kwargs):
my_singleton.close()
Register a handler to catch the SIGINT signal from the system.
# my_app/__init__.py
import signal
import sys
from iot.signal_definitions import system_shutdown_signal
def _forward_to_django_shutdown_signal(signal, frame):
print(f"Shutting down Django {sys.argv}")
system_shutdown_signal.send("system")
sys.exit(0)
signal.signal(signal.SIGINT, _forward_to_django_shutdown_signal)
The Django signal used to propagate the system SIGINT signal.
# my_app/signal_definitions.py
from django.dispatch import Signal
system_shutdown_signal = Signal()
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | iklinac |
Solution 2 | Moritz |