Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reloader fixes #531

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions werkzeug/_reloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import time
import subprocess
import threading
import pickle
from itertools import chain

from werkzeug._internal import _log
Expand Down Expand Up @@ -98,6 +99,18 @@ def restart_with_reloader(self):
new_environ = os.environ.copy()
new_environ['WERKZEUG_RUN_MAIN'] = 'true'

# if the app was started as python -m werkzeug.serving mod:app
# sys.path[0] is empty (i.e. points to the current directory).
# But because the script directory in args[1] is expanded to
# /<path>/werkzeug/serving.py the restarted app will have
# werkzeug's directory in sys.path[0] instead, and the current
# directory won't be in sys.path at all. To avoid this problem
# here we save the state of sys.path to restore in the restarted
# application.
abs_sys_path = [os.path.abspath(p) for p in sys.path]
new_environ['WERKZEUG_SYS_PATH'] = pickle.dumps(
abs_sys_path, protocol=0).decode('utf-8')

# a weird bug on windows. sometimes unicode strings end up in the
# environment and subprocess.call does not like this, encode them
# to latin1 and continue.
Expand Down Expand Up @@ -213,6 +226,10 @@ def run_with_reloader(main_func, extra_files=None, interval=1,
signal.signal(signal.SIGTERM, lambda *args: sys.exit(0))
try:
if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
# restore original sys.path from parent app
if os.environ.get('WERKZEUG_SYS_PATH'):
sys.path = pickle.loads(
os.environ['WERKZEUG_SYS_PATH'].encode("utf-8"))
t = threading.Thread(target=main_func, args=())
t.setDaemon(True)
t.start()
Expand Down
18 changes: 12 additions & 6 deletions werkzeug/serving.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def _get_openssl_crypto_module():

import werkzeug
from werkzeug._internal import _log
from werkzeug._compat import reraise, wsgi_encoding_dance
from werkzeug._compat import reraise, wsgi_encoding_dance, string_types
from werkzeug.urls import url_parse, url_unquote
from werkzeug.exceptions import InternalServerError

Expand Down Expand Up @@ -555,7 +555,8 @@ def run_simple(hostname, port, application, use_reloader=False,

:param hostname: The host for the application. eg: ``'localhost'``
:param port: The port for the server. eg: ``8080``
:param application: the WSGI application to execute
:param application: the WSGI application to execute, or the dotted name from
where it can be imported.
:param use_reloader: should the server automatically restart the python
process if modules were changed?
:param use_debugger: should the werkzeug debugging system be used?
Expand Down Expand Up @@ -589,6 +590,13 @@ def run_simple(hostname, port, application, use_reloader=False,
the server should automatically create one, or ``None``
to disable SSL (which is the default).
"""
if isinstance(application, string_types):
if use_reloader:

This comment was marked as off-topic.

This comment was marked as off-topic.

This comment was marked as off-topic.

from werkzeug.wsgi import ImportedAppMiddleware
application = ImportedAppMiddleware(application)
else:
from werkzeug.utils import import_string
application = import_string(application)
if use_debugger:
from werkzeug.debug import DebuggedApplication
application = DebuggedApplication(application, use_evalex)
Expand Down Expand Up @@ -617,7 +625,7 @@ def inner():
test_socket.bind((hostname, port))
test_socket.close()

from ._reloader import run_with_reloader
from werkzeug._reloader import run_with_reloader
run_with_reloader(inner, extra_files, reloader_interval,
reloader_type)
else:
Expand All @@ -636,7 +644,6 @@ def main():

# in contrast to argparse, this works at least under Python < 2.7
import optparse
from werkzeug.utils import import_string

parser = optparse.OptionParser(
usage='Usage: %prog [options] app_module:app_object')
Expand All @@ -660,11 +667,10 @@ def main():
if len(args) != 1:
sys.stdout.write('No application supplied, or too much. See --help\n')
sys.exit(1)
app = import_string(args[0])

run_simple(
hostname=(hostname or '127.0.0.1'), port=int(port or 5000),
application=app, use_reloader=options.use_reloader,
application=args[0], use_reloader=options.use_reloader,
use_debugger=options.use_debugger
)

Expand Down
26 changes: 26 additions & 0 deletions werkzeug/wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from time import time, mktime
from datetime import datetime
from functools import partial, update_wrapper
from threading import Lock

from werkzeug._compat import iteritems, text_type, string_types, \
implements_iterator, make_literal_wrapper, to_unicode, to_bytes, \
Expand Down Expand Up @@ -647,6 +648,31 @@ def __call__(self, environ, start_response):
return app(environ, start_response)


class ImportedAppMiddleware(object):
"""Allows lazy loading of an application."""

def __init__(self, app_string):
self.app_string = app_string
self.app = None
self._lock = Lock()

def _load(self):
from werkzeug.utils import import_string
with self._lock:
if self.app is None:
self.app = import_string(self.app_string)

def __getattr__(self, name):
if self.app is None:
self._load()
return self.app.__getattribute__(name)

def __call__(self, environ, start_response):
if self.app is None:
self._load()
return self.app(environ, start_response)


@implements_iterator
class ClosingIterator(object):
"""The WSGI specification requires that all middlewares and gateways
Expand Down