Translation of UI (german and english)
Bugfix for feeds
- removed categories related and up
- load new books now working
- category random now working
login page is free of non accessible elements
boolean custom column is vivible in UI
books with only with certain languages can be shown
book shelfs can be deleted from UI
Anonymous user view is more resticted
Added browse of series in sidebar
Dependencys in vendor folder are updated to newer versions (licencs files are now present)
Bugfix editing Authors names
Made upload on windows working
This commit is contained in:
Vendored
+227
-234
@@ -32,7 +32,7 @@
|
||||
instead of a simple start file.
|
||||
|
||||
|
||||
:copyright: (c) 2013 by the Werkzeug Team, see AUTHORS for more details.
|
||||
:copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
from __future__ import with_statement
|
||||
@@ -40,14 +40,26 @@ from __future__ import with_statement
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
import signal
|
||||
import subprocess
|
||||
|
||||
try:
|
||||
import thread
|
||||
import ssl
|
||||
except ImportError:
|
||||
import _thread as thread
|
||||
class _SslDummy(object):
|
||||
def __getattr__(self, name):
|
||||
raise RuntimeError('SSL support unavailable')
|
||||
ssl = _SslDummy()
|
||||
|
||||
|
||||
def _get_openssl_crypto_module():
|
||||
try:
|
||||
from OpenSSL import crypto
|
||||
except ImportError:
|
||||
raise TypeError('Using ad-hoc certificates requires the pyOpenSSL '
|
||||
'library.')
|
||||
else:
|
||||
return crypto
|
||||
|
||||
|
||||
try:
|
||||
from SocketServer import ThreadingMixIn, ForkingMixIn
|
||||
@@ -56,15 +68,20 @@ except ImportError:
|
||||
from socketserver import ThreadingMixIn, ForkingMixIn
|
||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||
|
||||
# important: do not use relative imports here or python -m will break
|
||||
import werkzeug
|
||||
from werkzeug._internal import _log
|
||||
from werkzeug._compat import iteritems, PY2, reraise, text_type, \
|
||||
wsgi_encoding_dance
|
||||
from werkzeug._compat import PY2, reraise, wsgi_encoding_dance
|
||||
from werkzeug.urls import url_parse, url_unquote
|
||||
from werkzeug.exceptions import InternalServerError, BadRequest
|
||||
from werkzeug.exceptions import InternalServerError
|
||||
|
||||
|
||||
LISTEN_QUEUE = 128
|
||||
can_open_by_fd = hasattr(socket, 'fromfd')
|
||||
|
||||
|
||||
class WSGIRequestHandler(BaseHTTPRequestHandler, object):
|
||||
|
||||
"""A request handler that implements WSGI dispatching."""
|
||||
|
||||
@property
|
||||
@@ -88,8 +105,7 @@ class WSGIRequestHandler(BaseHTTPRequestHandler, object):
|
||||
'wsgi.multithread': self.server.multithread,
|
||||
'wsgi.multiprocess': self.server.multiprocess,
|
||||
'wsgi.run_once': False,
|
||||
'werkzeug.server.shutdown':
|
||||
shutdown_server,
|
||||
'werkzeug.server.shutdown': shutdown_server,
|
||||
'SERVER_SOFTWARE': self.server_version,
|
||||
'REQUEST_METHOD': self.command,
|
||||
'SCRIPT_NAME': '',
|
||||
@@ -97,8 +113,8 @@ class WSGIRequestHandler(BaseHTTPRequestHandler, object):
|
||||
'QUERY_STRING': wsgi_encoding_dance(request_url.query),
|
||||
'CONTENT_TYPE': self.headers.get('Content-Type', ''),
|
||||
'CONTENT_LENGTH': self.headers.get('Content-Length', ''),
|
||||
'REMOTE_ADDR': self.client_address[0],
|
||||
'REMOTE_PORT': self.client_address[1],
|
||||
'REMOTE_ADDR': self.address_string(),
|
||||
'REMOTE_PORT': self.port_integer(),
|
||||
'SERVER_NAME': self.server.server_address[0],
|
||||
'SERVER_PORT': str(self.server.server_address[1]),
|
||||
'SERVER_PROTOCOL': self.request_version
|
||||
@@ -109,7 +125,7 @@ class WSGIRequestHandler(BaseHTTPRequestHandler, object):
|
||||
if key not in ('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH'):
|
||||
environ[key] = value
|
||||
|
||||
if request_url.netloc:
|
||||
if request_url.scheme and request_url.netloc:
|
||||
environ['HTTP_HOST'] = request_url.netloc
|
||||
|
||||
return environ
|
||||
@@ -118,7 +134,7 @@ class WSGIRequestHandler(BaseHTTPRequestHandler, object):
|
||||
if self.headers.get('Expect', '').lower().strip() == '100-continue':
|
||||
self.wfile.write(b'HTTP/1.1 100 Continue\r\n\r\n')
|
||||
|
||||
environ = self.make_environ()
|
||||
self.environ = environ = self.make_environ()
|
||||
headers_set = []
|
||||
headers_sent = []
|
||||
|
||||
@@ -145,7 +161,7 @@ class WSGIRequestHandler(BaseHTTPRequestHandler, object):
|
||||
self.send_header('Date', self.date_time_string())
|
||||
self.end_headers()
|
||||
|
||||
assert type(data) is bytes, 'applications must write bytes'
|
||||
assert isinstance(data, bytes), 'applications must write bytes'
|
||||
self.wfile.write(data)
|
||||
self.wfile.flush()
|
||||
|
||||
@@ -249,6 +265,9 @@ class WSGIRequestHandler(BaseHTTPRequestHandler, object):
|
||||
def address_string(self):
|
||||
return self.client_address[0]
|
||||
|
||||
def port_integer(self):
|
||||
return self.client_address[1]
|
||||
|
||||
def log_request(self, code='-', size='-'):
|
||||
self.log('info', '"%s" %s %s', self.requestline, code, size)
|
||||
|
||||
@@ -270,14 +289,14 @@ BaseRequestHandler = WSGIRequestHandler
|
||||
|
||||
def generate_adhoc_ssl_pair(cn=None):
|
||||
from random import random
|
||||
from OpenSSL import crypto
|
||||
crypto = _get_openssl_crypto_module()
|
||||
|
||||
# pretty damn sure that this is not actually accepted by anyone
|
||||
if cn is None:
|
||||
cn = '*'
|
||||
|
||||
cert = crypto.X509()
|
||||
cert.set_serial_number(int(random() * sys.maxint))
|
||||
cert.set_serial_number(int(random() * sys.maxsize))
|
||||
cert.gmtime_adj_notBefore(0)
|
||||
cert.gmtime_adj_notAfter(60 * 60 * 24 * 365)
|
||||
|
||||
@@ -290,7 +309,7 @@ def generate_adhoc_ssl_pair(cn=None):
|
||||
issuer.O = 'Self-Signed'
|
||||
|
||||
pkey = crypto.PKey()
|
||||
pkey.generate_key(crypto.TYPE_RSA, 768)
|
||||
pkey.generate_key(crypto.TYPE_RSA, 1024)
|
||||
cert.set_pubkey(pkey)
|
||||
cert.sign(pkey, 'md5')
|
||||
|
||||
@@ -323,9 +342,9 @@ def make_ssl_devcert(base_path, host=None, cn=None):
|
||||
cert_file = base_path + '.crt'
|
||||
pkey_file = base_path + '.key'
|
||||
|
||||
with open(cert_file, 'w') as f:
|
||||
with open(cert_file, 'wb') as f:
|
||||
f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
|
||||
with open(pkey_file, 'w') as f:
|
||||
with open(pkey_file, 'wb') as f:
|
||||
f.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey))
|
||||
|
||||
return cert_file, pkey_file
|
||||
@@ -333,48 +352,76 @@ def make_ssl_devcert(base_path, host=None, cn=None):
|
||||
|
||||
def generate_adhoc_ssl_context():
|
||||
"""Generates an adhoc SSL context for the development server."""
|
||||
from OpenSSL import SSL
|
||||
crypto = _get_openssl_crypto_module()
|
||||
import tempfile
|
||||
import atexit
|
||||
|
||||
cert, pkey = generate_adhoc_ssl_pair()
|
||||
ctx = SSL.Context(SSL.SSLv23_METHOD)
|
||||
ctx.use_privatekey(pkey)
|
||||
ctx.use_certificate(cert)
|
||||
cert_handle, cert_file = tempfile.mkstemp()
|
||||
pkey_handle, pkey_file = tempfile.mkstemp()
|
||||
atexit.register(os.remove, pkey_file)
|
||||
atexit.register(os.remove, cert_file)
|
||||
|
||||
os.write(cert_handle, crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
|
||||
os.write(pkey_handle, crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey))
|
||||
os.close(cert_handle)
|
||||
os.close(pkey_handle)
|
||||
ctx = load_ssl_context(cert_file, pkey_file)
|
||||
return ctx
|
||||
|
||||
|
||||
def load_ssl_context(cert_file, pkey_file):
|
||||
"""Loads an SSL context from a certificate and private key file."""
|
||||
from OpenSSL import SSL
|
||||
ctx = SSL.Context(SSL.SSLv23_METHOD)
|
||||
ctx.use_certificate_file(cert_file)
|
||||
ctx.use_privatekey_file(pkey_file)
|
||||
def load_ssl_context(cert_file, pkey_file=None, protocol=None):
|
||||
"""Loads SSL context from cert/private key files and optional protocol.
|
||||
Many parameters are directly taken from the API of
|
||||
:py:class:`ssl.SSLContext`.
|
||||
|
||||
:param cert_file: Path of the certificate to use.
|
||||
:param pkey_file: Path of the private key to use. If not given, the key
|
||||
will be obtained from the certificate file.
|
||||
:param protocol: One of the ``PROTOCOL_*`` constants in the stdlib ``ssl``
|
||||
module. Defaults to ``PROTOCOL_SSLv23``.
|
||||
"""
|
||||
if protocol is None:
|
||||
protocol = ssl.PROTOCOL_SSLv23
|
||||
ctx = _SSLContext(protocol)
|
||||
ctx.load_cert_chain(cert_file, pkey_file)
|
||||
return ctx
|
||||
|
||||
|
||||
class _SSLContext(object):
|
||||
|
||||
'''A dummy class with a small subset of Python3's ``ssl.SSLContext``, only
|
||||
intended to be used with and by Werkzeug.'''
|
||||
|
||||
def __init__(self, protocol):
|
||||
self._protocol = protocol
|
||||
self._certfile = None
|
||||
self._keyfile = None
|
||||
self._password = None
|
||||
|
||||
def load_cert_chain(self, certfile, keyfile=None, password=None):
|
||||
self._certfile = certfile
|
||||
self._keyfile = keyfile or certfile
|
||||
self._password = password
|
||||
|
||||
def wrap_socket(self, sock, **kwargs):
|
||||
return ssl.wrap_socket(sock, keyfile=self._keyfile,
|
||||
certfile=self._certfile,
|
||||
ssl_version=self._protocol, **kwargs)
|
||||
|
||||
|
||||
def is_ssl_error(error=None):
|
||||
"""Checks if the given error (or the current one) is an SSL error."""
|
||||
exc_types = (ssl.SSLError,)
|
||||
try:
|
||||
from OpenSSL.SSL import Error
|
||||
exc_types += (Error,)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
if error is None:
|
||||
error = sys.exc_info()[1]
|
||||
from OpenSSL import SSL
|
||||
return isinstance(error, SSL.Error)
|
||||
|
||||
|
||||
class _SSLConnectionFix(object):
|
||||
"""Wrapper around SSL connection to provide a working makefile()."""
|
||||
|
||||
def __init__(self, con):
|
||||
self._con = con
|
||||
|
||||
def makefile(self, mode, bufsize):
|
||||
return socket._fileobject(self._con, mode, bufsize)
|
||||
|
||||
def __getattr__(self, attrib):
|
||||
return getattr(self._con, attrib)
|
||||
|
||||
def shutdown(self, arg=None):
|
||||
try:
|
||||
self._con.shutdown()
|
||||
except Exception:
|
||||
pass
|
||||
return isinstance(error, exc_types)
|
||||
|
||||
|
||||
def select_ip_version(host, port):
|
||||
@@ -383,46 +430,62 @@ def select_ip_version(host, port):
|
||||
# and various operating systems. Probably this code also is
|
||||
# not supposed to work, but I can't come up with any other
|
||||
# ways to implement this.
|
||||
##try:
|
||||
## info = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
|
||||
## socket.SOCK_STREAM, 0,
|
||||
## socket.AI_PASSIVE)
|
||||
## if info:
|
||||
## return info[0][0]
|
||||
##except socket.gaierror:
|
||||
## pass
|
||||
# try:
|
||||
# info = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
|
||||
# socket.SOCK_STREAM, 0,
|
||||
# socket.AI_PASSIVE)
|
||||
# if info:
|
||||
# return info[0][0]
|
||||
# except socket.gaierror:
|
||||
# pass
|
||||
if ':' in host and hasattr(socket, 'AF_INET6'):
|
||||
return socket.AF_INET6
|
||||
return socket.AF_INET
|
||||
|
||||
|
||||
class BaseWSGIServer(HTTPServer, object):
|
||||
|
||||
"""Simple single-threaded, single-process WSGI server."""
|
||||
multithread = False
|
||||
multiprocess = False
|
||||
request_queue_size = 128
|
||||
request_queue_size = LISTEN_QUEUE
|
||||
|
||||
def __init__(self, host, port, app, handler=None,
|
||||
passthrough_errors=False, ssl_context=None):
|
||||
passthrough_errors=False, ssl_context=None, fd=None):
|
||||
if handler is None:
|
||||
handler = WSGIRequestHandler
|
||||
|
||||
self.address_family = select_ip_version(host, port)
|
||||
|
||||
if fd is not None:
|
||||
real_sock = socket.fromfd(fd, self.address_family,
|
||||
socket.SOCK_STREAM)
|
||||
port = 0
|
||||
HTTPServer.__init__(self, (host, int(port)), handler)
|
||||
self.app = app
|
||||
self.passthrough_errors = passthrough_errors
|
||||
self.shutdown_signal = False
|
||||
self.host = host
|
||||
self.port = port
|
||||
|
||||
# Patch in the original socket.
|
||||
if fd is not None:
|
||||
self.socket.close()
|
||||
self.socket = real_sock
|
||||
self.server_address = self.socket.getsockname()
|
||||
|
||||
if ssl_context is not None:
|
||||
try:
|
||||
from OpenSSL import tsafe
|
||||
except ImportError:
|
||||
raise TypeError('SSL is not available if the OpenSSL '
|
||||
'library is not installed.')
|
||||
if isinstance(ssl_context, tuple):
|
||||
ssl_context = load_ssl_context(*ssl_context)
|
||||
if ssl_context == 'adhoc':
|
||||
ssl_context = generate_adhoc_ssl_context()
|
||||
self.socket = tsafe.Connection(ssl_context, self.socket)
|
||||
# If we are on Python 2 the return value from socket.fromfd
|
||||
# is an internal socket object but what we need for ssl wrap
|
||||
# is the wrapper around it :(
|
||||
sock = self.socket
|
||||
if PY2 and not isinstance(sock, socket.socket):
|
||||
sock = socket.socket(sock.family, sock.type, sock.proto, sock)
|
||||
self.socket = ssl_context.wrap_socket(sock, server_side=True)
|
||||
self.ssl_context = ssl_context
|
||||
else:
|
||||
self.ssl_context = None
|
||||
@@ -436,39 +499,40 @@ class BaseWSGIServer(HTTPServer, object):
|
||||
HTTPServer.serve_forever(self)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
finally:
|
||||
self.server_close()
|
||||
|
||||
def handle_error(self, request, client_address):
|
||||
if self.passthrough_errors:
|
||||
raise
|
||||
else:
|
||||
return HTTPServer.handle_error(self, request, client_address)
|
||||
return HTTPServer.handle_error(self, request, client_address)
|
||||
|
||||
def get_request(self):
|
||||
con, info = self.socket.accept()
|
||||
if self.ssl_context is not None:
|
||||
con = _SSLConnectionFix(con)
|
||||
return con, info
|
||||
|
||||
|
||||
class ThreadedWSGIServer(ThreadingMixIn, BaseWSGIServer):
|
||||
|
||||
"""A WSGI server that does threading."""
|
||||
multithread = True
|
||||
|
||||
|
||||
class ForkingWSGIServer(ForkingMixIn, BaseWSGIServer):
|
||||
|
||||
"""A WSGI server that does forking."""
|
||||
multiprocess = True
|
||||
|
||||
def __init__(self, host, port, app, processes=40, handler=None,
|
||||
passthrough_errors=False, ssl_context=None):
|
||||
passthrough_errors=False, ssl_context=None, fd=None):
|
||||
BaseWSGIServer.__init__(self, host, port, app, handler,
|
||||
passthrough_errors, ssl_context)
|
||||
passthrough_errors, ssl_context, fd)
|
||||
self.max_children = processes
|
||||
|
||||
|
||||
def make_server(host, port, app=None, threaded=False, processes=1,
|
||||
def make_server(host=None, port=None, app=None, threaded=False, processes=1,
|
||||
request_handler=None, passthrough_errors=False,
|
||||
ssl_context=None):
|
||||
ssl_context=None, fd=None):
|
||||
"""Create a new server instance that is either threaded, or forks
|
||||
or just processes one request after another.
|
||||
"""
|
||||
@@ -477,156 +541,32 @@ def make_server(host, port, app=None, threaded=False, processes=1,
|
||||
"multi process server.")
|
||||
elif threaded:
|
||||
return ThreadedWSGIServer(host, port, app, request_handler,
|
||||
passthrough_errors, ssl_context)
|
||||
passthrough_errors, ssl_context, fd=fd)
|
||||
elif processes > 1:
|
||||
return ForkingWSGIServer(host, port, app, processes, request_handler,
|
||||
passthrough_errors, ssl_context)
|
||||
passthrough_errors, ssl_context, fd=fd)
|
||||
else:
|
||||
return BaseWSGIServer(host, port, app, request_handler,
|
||||
passthrough_errors, ssl_context)
|
||||
passthrough_errors, ssl_context, fd=fd)
|
||||
|
||||
|
||||
def _iter_module_files():
|
||||
# The list call is necessary on Python 3 in case the module
|
||||
# dictionary modifies during iteration.
|
||||
for module in list(sys.modules.values()):
|
||||
filename = getattr(module, '__file__', None)
|
||||
if filename:
|
||||
old = None
|
||||
while not os.path.isfile(filename):
|
||||
old = filename
|
||||
filename = os.path.dirname(filename)
|
||||
if filename == old:
|
||||
break
|
||||
else:
|
||||
if filename[-4:] in ('.pyc', '.pyo'):
|
||||
filename = filename[:-1]
|
||||
yield filename
|
||||
def is_running_from_reloader():
|
||||
"""Checks if the application is running from within the Werkzeug
|
||||
reloader subprocess.
|
||||
|
||||
|
||||
def _reloader_stat_loop(extra_files=None, interval=1):
|
||||
"""When this function is run from the main thread, it will force other
|
||||
threads to exit when any modules currently loaded change.
|
||||
|
||||
Copyright notice. This function is based on the autoreload.py from
|
||||
the CherryPy trac which originated from WSGIKit which is now dead.
|
||||
|
||||
:param extra_files: a list of additional files it should watch.
|
||||
.. versionadded:: 0.10
|
||||
"""
|
||||
from itertools import chain
|
||||
mtimes = {}
|
||||
while 1:
|
||||
for filename in chain(_iter_module_files(), extra_files or ()):
|
||||
try:
|
||||
mtime = os.stat(filename).st_mtime
|
||||
except OSError:
|
||||
continue
|
||||
|
||||
old_time = mtimes.get(filename)
|
||||
if old_time is None:
|
||||
mtimes[filename] = mtime
|
||||
continue
|
||||
elif mtime > old_time:
|
||||
_log('info', ' * Detected change in %r, reloading' % filename)
|
||||
sys.exit(3)
|
||||
time.sleep(interval)
|
||||
|
||||
|
||||
def _reloader_inotify(extra_files=None, interval=None):
|
||||
# Mutated by inotify loop when changes occur.
|
||||
changed = [False]
|
||||
|
||||
# Setup inotify watches
|
||||
from pyinotify import WatchManager, Notifier
|
||||
|
||||
# this API changed at one point, support both
|
||||
try:
|
||||
from pyinotify import EventsCodes as ec
|
||||
ec.IN_ATTRIB
|
||||
except (ImportError, AttributeError):
|
||||
import pyinotify as ec
|
||||
|
||||
wm = WatchManager()
|
||||
mask = ec.IN_DELETE_SELF | ec.IN_MOVE_SELF | ec.IN_MODIFY | ec.IN_ATTRIB
|
||||
|
||||
def signal_changed(event):
|
||||
if changed[0]:
|
||||
return
|
||||
_log('info', ' * Detected change in %r, reloading' % event.path)
|
||||
changed[:] = [True]
|
||||
|
||||
for fname in extra_files or ():
|
||||
wm.add_watch(fname, mask, signal_changed)
|
||||
|
||||
# ... And now we wait...
|
||||
notif = Notifier(wm)
|
||||
try:
|
||||
while not changed[0]:
|
||||
# always reiterate through sys.modules, adding them
|
||||
for fname in _iter_module_files():
|
||||
wm.add_watch(fname, mask, signal_changed)
|
||||
notif.process_events()
|
||||
if notif.check_events(timeout=interval):
|
||||
notif.read_events()
|
||||
# TODO Set timeout to something small and check parent liveliness
|
||||
finally:
|
||||
notif.stop()
|
||||
sys.exit(3)
|
||||
|
||||
|
||||
# currently we always use the stat loop reloader for the simple reason
|
||||
# that the inotify one does not respond to added files properly. Also
|
||||
# it's quite buggy and the API is a mess.
|
||||
reloader_loop = _reloader_stat_loop
|
||||
|
||||
|
||||
def restart_with_reloader():
|
||||
"""Spawn a new Python interpreter with the same arguments as this one,
|
||||
but running the reloader thread.
|
||||
"""
|
||||
while 1:
|
||||
_log('info', ' * Restarting with reloader')
|
||||
args = [sys.executable] + sys.argv
|
||||
new_environ = os.environ.copy()
|
||||
new_environ['WERKZEUG_RUN_MAIN'] = 'true'
|
||||
|
||||
# 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.
|
||||
if os.name == 'nt' and PY2:
|
||||
for key, value in iteritems(new_environ):
|
||||
if isinstance(value, text_type):
|
||||
new_environ[key] = value.encode('iso-8859-1')
|
||||
|
||||
exit_code = subprocess.call(args, env=new_environ)
|
||||
if exit_code != 3:
|
||||
return exit_code
|
||||
|
||||
|
||||
def run_with_reloader(main_func, extra_files=None, interval=1):
|
||||
"""Run the given function in an independent python interpreter."""
|
||||
import signal
|
||||
signal.signal(signal.SIGTERM, lambda *args: sys.exit(0))
|
||||
if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
|
||||
thread.start_new_thread(main_func, ())
|
||||
try:
|
||||
reloader_loop(extra_files, interval)
|
||||
except KeyboardInterrupt:
|
||||
return
|
||||
try:
|
||||
sys.exit(restart_with_reloader())
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
return os.environ.get('WERKZEUG_RUN_MAIN') == 'true'
|
||||
|
||||
|
||||
def run_simple(hostname, port, application, use_reloader=False,
|
||||
use_debugger=False, use_evalex=True,
|
||||
extra_files=None, reloader_interval=1, threaded=False,
|
||||
extra_files=None, reloader_interval=1,
|
||||
reloader_type='auto', threaded=False,
|
||||
processes=1, request_handler=None, static_files=None,
|
||||
passthrough_errors=False, ssl_context=None):
|
||||
"""Start an application using wsgiref and with an optional reloader. This
|
||||
wraps `wsgiref` to fix the wrong default reporting of the multithreaded
|
||||
WSGI variable and adds optional multithreading and fork support.
|
||||
"""Start a WSGI application. Optional features include a reloader,
|
||||
multithreading and fork support.
|
||||
|
||||
This function has a command-line interface too::
|
||||
|
||||
@@ -646,6 +586,11 @@ def run_simple(hostname, port, application, use_reloader=False,
|
||||
.. versionadded:: 0.9
|
||||
Added command-line interface.
|
||||
|
||||
.. versionadded:: 0.10
|
||||
Improved the reloader and added support for changing the backend
|
||||
through the `reloader_type` parameter. See :ref:`reloader`
|
||||
for more information.
|
||||
|
||||
: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
|
||||
@@ -657,6 +602,10 @@ def run_simple(hostname, port, application, use_reloader=False,
|
||||
additionally to the modules. For example configuration
|
||||
files.
|
||||
:param reloader_interval: the interval for the reloader in seconds.
|
||||
:param reloader_type: the type of reloader to use. The default is
|
||||
auto detection. Valid values are ``'stat'`` and
|
||||
``'watchdog'``. See :ref:`reloader` for more
|
||||
information.
|
||||
:param threaded: should the process handle each request in a separate
|
||||
thread?
|
||||
:param processes: if greater than 1 then handle each request in a new process
|
||||
@@ -673,11 +622,11 @@ def run_simple(hostname, port, application, use_reloader=False,
|
||||
:param passthrough_errors: set this to `True` to disable the error catching.
|
||||
This means that the server will die on errors but
|
||||
it can be useful to hook debuggers in (pdb etc.)
|
||||
:param ssl_context: an SSL context for the connection. Either an OpenSSL
|
||||
context, a tuple in the form ``(cert_file, pkey_file)``,
|
||||
the string ``'adhoc'`` if the server should
|
||||
automatically create one, or `None` to disable SSL
|
||||
(which is the default).
|
||||
:param ssl_context: an SSL context for the connection. Either an
|
||||
:class:`ssl.SSLContext`, a tuple in the form
|
||||
``(cert_file, pkey_file)``, the string ``'adhoc'`` if
|
||||
the server should automatically create one, or ``None``
|
||||
to disable SSL (which is the default).
|
||||
"""
|
||||
if use_debugger:
|
||||
from werkzeug.debug import DebuggedApplication
|
||||
@@ -686,29 +635,72 @@ def run_simple(hostname, port, application, use_reloader=False,
|
||||
from werkzeug.wsgi import SharedDataMiddleware
|
||||
application = SharedDataMiddleware(application, static_files)
|
||||
|
||||
def inner():
|
||||
make_server(hostname, port, application, threaded,
|
||||
processes, request_handler,
|
||||
passthrough_errors, ssl_context).serve_forever()
|
||||
|
||||
if os.environ.get('WERKZEUG_RUN_MAIN') != 'true':
|
||||
display_hostname = hostname != '*' and hostname or 'localhost'
|
||||
def log_startup(sock):
|
||||
display_hostname = hostname not in ('', '*') and hostname or 'localhost'
|
||||
if ':' in display_hostname:
|
||||
display_hostname = '[%s]' % display_hostname
|
||||
_log('info', ' * Running on %s://%s:%d/', ssl_context is None
|
||||
and 'http' or 'https', display_hostname, port)
|
||||
quit_msg = '(Press CTRL+C to quit)'
|
||||
port = sock.getsockname()[1]
|
||||
_log('info', ' * Running on %s://%s:%d/ %s',
|
||||
ssl_context is None and 'http' or 'https',
|
||||
display_hostname, port, quit_msg)
|
||||
|
||||
def inner():
|
||||
try:
|
||||
fd = int(os.environ['WERKZEUG_SERVER_FD'])
|
||||
except (LookupError, ValueError):
|
||||
fd = None
|
||||
srv = make_server(hostname, port, application, threaded,
|
||||
processes, request_handler,
|
||||
passthrough_errors, ssl_context,
|
||||
fd=fd)
|
||||
if fd is None:
|
||||
log_startup(srv.socket)
|
||||
srv.serve_forever()
|
||||
|
||||
if use_reloader:
|
||||
# Create and destroy a socket so that any exceptions are raised before
|
||||
# we spawn a separate Python interpreter and lose this ability.
|
||||
address_family = select_ip_version(hostname, port)
|
||||
test_socket = socket.socket(address_family, socket.SOCK_STREAM)
|
||||
test_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
test_socket.bind((hostname, port))
|
||||
test_socket.close()
|
||||
run_with_reloader(inner, extra_files, reloader_interval)
|
||||
# If we're not running already in the subprocess that is the
|
||||
# reloader we want to open up a socket early to make sure the
|
||||
# port is actually available.
|
||||
if os.environ.get('WERKZEUG_RUN_MAIN') != 'true':
|
||||
if port == 0 and not can_open_by_fd:
|
||||
raise ValueError('Cannot bind to a random port with enabled '
|
||||
'reloader if the Python interpreter does '
|
||||
'not support socket opening by fd.')
|
||||
|
||||
# Create and destroy a socket so that any exceptions are
|
||||
# raised before we spawn a separate Python interpreter and
|
||||
# lose this ability.
|
||||
address_family = select_ip_version(hostname, port)
|
||||
s = socket.socket(address_family, socket.SOCK_STREAM)
|
||||
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
s.bind((hostname, port))
|
||||
if hasattr(s, 'set_inheritable'):
|
||||
s.set_inheritable(True)
|
||||
|
||||
# If we can open the socket by file descriptor, then we can just
|
||||
# reuse this one and our socket will survive the restarts.
|
||||
if can_open_by_fd:
|
||||
os.environ['WERKZEUG_SERVER_FD'] = str(s.fileno())
|
||||
s.listen(LISTEN_QUEUE)
|
||||
log_startup(s)
|
||||
else:
|
||||
s.close()
|
||||
|
||||
from ._reloader import run_with_reloader
|
||||
run_with_reloader(inner, extra_files, reloader_interval,
|
||||
reloader_type)
|
||||
else:
|
||||
inner()
|
||||
|
||||
|
||||
def run_with_reloader(*args, **kwargs):
|
||||
# People keep using undocumented APIs. Do not use this function
|
||||
# please, we do not guarantee that it continues working.
|
||||
from ._reloader import run_with_reloader
|
||||
return run_with_reloader(*args, **kwargs)
|
||||
|
||||
|
||||
def main():
|
||||
'''A simple command-line interface for :py:func:`run_simple`.'''
|
||||
|
||||
@@ -716,7 +708,8 @@ def main():
|
||||
import optparse
|
||||
from werkzeug.utils import import_string
|
||||
|
||||
parser = optparse.OptionParser(usage='Usage: %prog [options] app_module:app_object')
|
||||
parser = optparse.OptionParser(
|
||||
usage='Usage: %prog [options] app_module:app_object')
|
||||
parser.add_option('-b', '--bind', dest='address',
|
||||
help='The hostname:port the app should listen on.')
|
||||
parser.add_option('-d', '--debug', dest='use_debugger',
|
||||
|
||||
Reference in New Issue
Block a user