Got basic webpage working. Need to make base page, static folders
This commit is contained in:
@@ -0,0 +1,317 @@
|
||||
# Autoreloading launcher.
|
||||
# Borrowed from Peter Hunt and the CherryPy project (http://www.cherrypy.org).
|
||||
# Some taken from Ian Bicking's Paste (http://pythonpaste.org/).
|
||||
#
|
||||
# Portions copyright (c) 2004, CherryPy Team ([email protected])
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of the CherryPy Team nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import os
|
||||
import signal
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
|
||||
import _thread
|
||||
|
||||
from django.apps import apps
|
||||
from django.conf import settings
|
||||
from django.core.signals import request_finished
|
||||
|
||||
# This import does nothing, but it's necessary to avoid some race conditions
|
||||
# in the threading module. See http://code.djangoproject.com/ticket/2330 .
|
||||
try:
|
||||
import threading # NOQA
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
try:
|
||||
import termios
|
||||
except ImportError:
|
||||
termios = None
|
||||
|
||||
USE_INOTIFY = False
|
||||
try:
|
||||
# Test whether inotify is enabled and likely to work
|
||||
import pyinotify
|
||||
|
||||
fd = pyinotify.INotifyWrapper.create().inotify_init()
|
||||
if fd >= 0:
|
||||
USE_INOTIFY = True
|
||||
os.close(fd)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
RUN_RELOADER = True
|
||||
|
||||
FILE_MODIFIED = 1
|
||||
I18N_MODIFIED = 2
|
||||
|
||||
_mtimes = {}
|
||||
_win = (sys.platform == "win32")
|
||||
|
||||
_exception = None
|
||||
_error_files = []
|
||||
_cached_modules = set()
|
||||
_cached_filenames = []
|
||||
|
||||
|
||||
def gen_filenames(only_new=False):
|
||||
"""
|
||||
Return a list of filenames referenced in sys.modules and translation files.
|
||||
"""
|
||||
# N.B. ``list(...)`` is needed, because this runs in parallel with
|
||||
# application code which might be mutating ``sys.modules``, and this will
|
||||
# fail with RuntimeError: cannot mutate dictionary while iterating
|
||||
global _cached_modules, _cached_filenames
|
||||
module_values = set(sys.modules.values())
|
||||
_cached_filenames = clean_files(_cached_filenames)
|
||||
if _cached_modules == module_values:
|
||||
# No changes in module list, short-circuit the function
|
||||
if only_new:
|
||||
return []
|
||||
else:
|
||||
return _cached_filenames + clean_files(_error_files)
|
||||
|
||||
new_modules = module_values - _cached_modules
|
||||
new_filenames = clean_files(
|
||||
[filename.__file__ for filename in new_modules
|
||||
if hasattr(filename, '__file__')])
|
||||
|
||||
if not _cached_filenames and settings.USE_I18N:
|
||||
# Add the names of the .mo files that can be generated
|
||||
# by compilemessages management command to the list of files watched.
|
||||
basedirs = [os.path.join(os.path.dirname(os.path.dirname(__file__)),
|
||||
'conf', 'locale'),
|
||||
'locale']
|
||||
for app_config in reversed(list(apps.get_app_configs())):
|
||||
basedirs.append(os.path.join(app_config.path, 'locale'))
|
||||
basedirs.extend(settings.LOCALE_PATHS)
|
||||
basedirs = [os.path.abspath(basedir) for basedir in basedirs
|
||||
if os.path.isdir(basedir)]
|
||||
for basedir in basedirs:
|
||||
for dirpath, dirnames, locale_filenames in os.walk(basedir):
|
||||
for filename in locale_filenames:
|
||||
if filename.endswith('.mo'):
|
||||
new_filenames.append(os.path.join(dirpath, filename))
|
||||
|
||||
_cached_modules = _cached_modules.union(new_modules)
|
||||
_cached_filenames += new_filenames
|
||||
if only_new:
|
||||
return new_filenames + clean_files(_error_files)
|
||||
else:
|
||||
return _cached_filenames + clean_files(_error_files)
|
||||
|
||||
|
||||
def clean_files(filelist):
|
||||
filenames = []
|
||||
for filename in filelist:
|
||||
if not filename:
|
||||
continue
|
||||
if filename.endswith(".pyc") or filename.endswith(".pyo"):
|
||||
filename = filename[:-1]
|
||||
if filename.endswith("$py.class"):
|
||||
filename = filename[:-9] + ".py"
|
||||
if os.path.exists(filename):
|
||||
filenames.append(filename)
|
||||
return filenames
|
||||
|
||||
|
||||
def reset_translations():
|
||||
import gettext
|
||||
from django.utils.translation import trans_real
|
||||
gettext._translations = {}
|
||||
trans_real._translations = {}
|
||||
trans_real._default = None
|
||||
trans_real._active = threading.local()
|
||||
|
||||
|
||||
def inotify_code_changed():
|
||||
"""
|
||||
Check for changed code using inotify. After being called
|
||||
it blocks until a change event has been fired.
|
||||
"""
|
||||
class EventHandler(pyinotify.ProcessEvent):
|
||||
modified_code = None
|
||||
|
||||
def process_default(self, event):
|
||||
if event.path.endswith('.mo'):
|
||||
EventHandler.modified_code = I18N_MODIFIED
|
||||
else:
|
||||
EventHandler.modified_code = FILE_MODIFIED
|
||||
|
||||
wm = pyinotify.WatchManager()
|
||||
notifier = pyinotify.Notifier(wm, EventHandler())
|
||||
|
||||
def update_watch(sender=None, **kwargs):
|
||||
if sender and getattr(sender, 'handles_files', False):
|
||||
# No need to update watches when request serves files.
|
||||
# (sender is supposed to be a django.core.handlers.BaseHandler subclass)
|
||||
return
|
||||
mask = (
|
||||
pyinotify.IN_MODIFY |
|
||||
pyinotify.IN_DELETE |
|
||||
pyinotify.IN_ATTRIB |
|
||||
pyinotify.IN_MOVED_FROM |
|
||||
pyinotify.IN_MOVED_TO |
|
||||
pyinotify.IN_CREATE |
|
||||
pyinotify.IN_DELETE_SELF |
|
||||
pyinotify.IN_MOVE_SELF
|
||||
)
|
||||
for path in gen_filenames(only_new=True):
|
||||
wm.add_watch(path, mask)
|
||||
|
||||
# New modules may get imported when a request is processed.
|
||||
request_finished.connect(update_watch)
|
||||
|
||||
# Block until an event happens.
|
||||
update_watch()
|
||||
notifier.check_events(timeout=None)
|
||||
notifier.read_events()
|
||||
notifier.process_events()
|
||||
notifier.stop()
|
||||
|
||||
# If we are here the code must have changed.
|
||||
return EventHandler.modified_code
|
||||
|
||||
|
||||
def code_changed():
|
||||
global _mtimes, _win
|
||||
for filename in gen_filenames():
|
||||
stat = os.stat(filename)
|
||||
mtime = stat.st_mtime
|
||||
if _win:
|
||||
mtime -= stat.st_ctime
|
||||
if filename not in _mtimes:
|
||||
_mtimes[filename] = mtime
|
||||
continue
|
||||
if mtime != _mtimes[filename]:
|
||||
_mtimes = {}
|
||||
try:
|
||||
del _error_files[_error_files.index(filename)]
|
||||
except ValueError:
|
||||
pass
|
||||
return I18N_MODIFIED if filename.endswith('.mo') else FILE_MODIFIED
|
||||
return False
|
||||
|
||||
|
||||
def check_errors(fn):
|
||||
def wrapper(*args, **kwargs):
|
||||
global _exception
|
||||
try:
|
||||
fn(*args, **kwargs)
|
||||
except Exception:
|
||||
_exception = sys.exc_info()
|
||||
|
||||
et, ev, tb = _exception
|
||||
|
||||
if getattr(ev, 'filename', None) is None:
|
||||
# get the filename from the last item in the stack
|
||||
filename = traceback.extract_tb(tb)[-1][0]
|
||||
else:
|
||||
filename = ev.filename
|
||||
|
||||
if filename not in _error_files:
|
||||
_error_files.append(filename)
|
||||
|
||||
raise
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def raise_last_exception():
|
||||
global _exception
|
||||
if _exception is not None:
|
||||
raise _exception[1]
|
||||
|
||||
|
||||
def ensure_echo_on():
|
||||
if termios:
|
||||
fd = sys.stdin
|
||||
if fd.isatty():
|
||||
attr_list = termios.tcgetattr(fd)
|
||||
if not attr_list[3] & termios.ECHO:
|
||||
attr_list[3] |= termios.ECHO
|
||||
if hasattr(signal, 'SIGTTOU'):
|
||||
old_handler = signal.signal(signal.SIGTTOU, signal.SIG_IGN)
|
||||
else:
|
||||
old_handler = None
|
||||
termios.tcsetattr(fd, termios.TCSANOW, attr_list)
|
||||
if old_handler is not None:
|
||||
signal.signal(signal.SIGTTOU, old_handler)
|
||||
|
||||
|
||||
def reloader_thread():
|
||||
ensure_echo_on()
|
||||
if USE_INOTIFY:
|
||||
fn = inotify_code_changed
|
||||
else:
|
||||
fn = code_changed
|
||||
while RUN_RELOADER:
|
||||
change = fn()
|
||||
if change == FILE_MODIFIED:
|
||||
sys.exit(3) # force reload
|
||||
elif change == I18N_MODIFIED:
|
||||
reset_translations()
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
def restart_with_reloader():
|
||||
while True:
|
||||
args = [sys.executable] + ['-W%s' % o for o in sys.warnoptions] + sys.argv
|
||||
new_environ = os.environ.copy()
|
||||
new_environ["RUN_MAIN"] = 'true'
|
||||
exit_code = subprocess.call(args, env=new_environ)
|
||||
if exit_code != 3:
|
||||
return exit_code
|
||||
|
||||
|
||||
def python_reloader(main_func, args, kwargs):
|
||||
if os.environ.get("RUN_MAIN") == "true":
|
||||
_thread.start_new_thread(main_func, args, kwargs)
|
||||
try:
|
||||
reloader_thread()
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
exit_code = restart_with_reloader()
|
||||
if exit_code < 0:
|
||||
os.kill(os.getpid(), -exit_code)
|
||||
else:
|
||||
sys.exit(exit_code)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
|
||||
def main(main_func, args=None, kwargs=None):
|
||||
if args is None:
|
||||
args = ()
|
||||
if kwargs is None:
|
||||
kwargs = {}
|
||||
|
||||
wrapped_main_func = check_errors(main_func)
|
||||
python_reloader(wrapped_main_func, args, kwargs)
|
||||
Reference in New Issue
Block a user