Update cherrpy to 17.4.2

This commit is contained in:
JonnyWong16 2019-11-23 18:55:19 -08:00
parent f28e741ad7
commit 4d6279a626
131 changed files with 15864 additions and 10389 deletions

View file

@ -2,14 +2,15 @@
import os
import six
import cherrypy
from cherrypy._cpcompat import ntou, py3k
from cherrypy._cpcompat import ntou
from cherrypy import _cpconfig, _cplogging, _cprequest, _cpwsgi, tools
from cherrypy.lib import httputil
from cherrypy.lib import httputil, reprconf
class Application(object):
"""A CherryPy Application.
Servers and gateways should not instantiate Request objects directly.
@ -30,7 +31,7 @@ class Application(object):
"""A dict of {path: pathconf} pairs, where 'pathconf' is itself a dict
of {key: value} pairs."""
namespaces = _cpconfig.NamespaceSet()
namespaces = reprconf.NamespaceSet()
toolboxes = {'tools': cherrypy.tools}
log = None
@ -44,22 +45,24 @@ class Application(object):
relative_urls = False
def __init__(self, root, script_name="", config=None):
def __init__(self, root, script_name='', config=None):
"""Initialize Application with given root."""
self.log = _cplogging.LogManager(id(self), cherrypy.log.logger_root)
self.root = root
self.script_name = script_name
self.wsgiapp = _cpwsgi.CPWSGIApp(self)
self.namespaces = self.namespaces.copy()
self.namespaces["log"] = lambda k, v: setattr(self.log, k, v)
self.namespaces["wsgi"] = self.wsgiapp.namespace_handler
self.namespaces['log'] = lambda k, v: setattr(self.log, k, v)
self.namespaces['wsgi'] = self.wsgiapp.namespace_handler
self.config = self.__class__.config.copy()
if config:
self.merge(config)
def __repr__(self):
return "%s.%s(%r, %r)" % (self.__module__, self.__class__.__name__,
"""Generate a representation of the Application instance."""
return '%s.%s(%r, %r)' % (self.__module__, self.__class__.__name__,
self.root, self.script_name)
script_name_doc = """The URI "mount point" for this app. A mount point
@ -78,42 +81,58 @@ class Application(object):
provided for each call from request.wsgi_environ['SCRIPT_NAME'].
"""
def _get_script_name(self):
@property
def script_name(self): # noqa: D401; irrelevant for properties
"""The URI "mount point" for this app.
A mount point is that portion of the URI which is constant for all URIs
that are serviced by this application; it does not include scheme,
host, or proxy ("virtual host") portions of the URI.
For example, if script_name is "/my/cool/app", then the URL
"http://www.example.com/my/cool/app/page1" might be handled by a
"page1" method on the root object.
The value of script_name MUST NOT end in a slash. If the script_name
refers to the root of the URI, it MUST be an empty string (not "/").
If script_name is explicitly set to None, then the script_name will be
provided for each call from request.wsgi_environ['SCRIPT_NAME'].
"""
if self._script_name is not None:
return self._script_name
# A `_script_name` with a value of None signals that the script name
# should be pulled from WSGI environ.
return cherrypy.serving.request.wsgi_environ['SCRIPT_NAME'].rstrip("/")
return cherrypy.serving.request.wsgi_environ['SCRIPT_NAME'].rstrip('/')
def _set_script_name(self, value):
@script_name.setter
def script_name(self, value):
if value:
value = value.rstrip("/")
value = value.rstrip('/')
self._script_name = value
script_name = property(fget=_get_script_name, fset=_set_script_name,
doc=script_name_doc)
def merge(self, config):
"""Merge the given config into self.config."""
_cpconfig.merge(self.config, config)
# Handle namespaces specified in config.
self.namespaces(self.config.get("/", {}))
self.namespaces(self.config.get('/', {}))
def find_config(self, path, key, default=None):
"""Return the most-specific value for key along path, or default."""
trail = path or "/"
trail = path or '/'
while trail:
nodeconf = self.config.get(trail, {})
if key in nodeconf:
return nodeconf[key]
lastslash = trail.rfind("/")
lastslash = trail.rfind('/')
if lastslash == -1:
break
elif lastslash == 0 and trail != "/":
trail = "/"
elif lastslash == 0 and trail != '/':
trail = '/'
else:
trail = trail[:lastslash]
@ -142,17 +161,17 @@ class Application(object):
try:
req.close()
except:
except Exception:
cherrypy.log(traceback=True, severity=40)
cherrypy.serving.clear()
def __call__(self, environ, start_response):
"""Call a WSGI-callable."""
return self.wsgiapp(environ, start_response)
class Tree(object):
"""A registry of CherryPy applications, mounted at diverse points.
An instance of this class may also be used as a WSGI callable
@ -168,9 +187,10 @@ class Tree(object):
WSGI callable if you happen to be using a WSGI server)."""
def __init__(self):
"""Initialize registry Tree."""
self.apps = {}
def mount(self, root, script_name="", config=None):
def mount(self, root, script_name='', config=None):
"""Mount a new app from a root object, script_name, and config.
root
@ -195,29 +215,36 @@ class Tree(object):
if script_name is None:
raise TypeError(
"The 'script_name' argument may not be None. Application "
"objects may, however, possess a script_name of None (in "
"order to inpect the WSGI environ for SCRIPT_NAME upon each "
"request). You cannot mount such Applications on this Tree; "
"you must pass them to a WSGI server interface directly.")
'objects may, however, possess a script_name of None (in '
'order to inpect the WSGI environ for SCRIPT_NAME upon each '
'request). You cannot mount such Applications on this Tree; '
'you must pass them to a WSGI server interface directly.')
# Next line both 1) strips trailing slash and 2) maps "/" -> "".
script_name = script_name.rstrip("/")
script_name = script_name.rstrip('/')
if isinstance(root, Application):
app = root
if script_name != "" and script_name != app.script_name:
if script_name != '' and script_name != app.script_name:
raise ValueError(
"Cannot specify a different script name and pass an "
"Application instance to cherrypy.mount")
'Cannot specify a different script name and pass an '
'Application instance to cherrypy.mount')
script_name = app.script_name
else:
app = Application(root, script_name)
# If mounted at "", add favicon.ico
if (script_name == "" and root is not None
and not hasattr(root, "favicon_ico")):
favicon = os.path.join(os.getcwd(), os.path.dirname(__file__),
"favicon.ico")
needs_favicon = (
script_name == ''
and root is not None
and not hasattr(root, 'favicon_ico')
)
if needs_favicon:
favicon = os.path.join(
os.getcwd(),
os.path.dirname(__file__),
'favicon.ico',
)
root.favicon_ico = tools.staticfile.handler(favicon)
if config:
@ -227,14 +254,14 @@ class Tree(object):
return app
def graft(self, wsgi_callable, script_name=""):
def graft(self, wsgi_callable, script_name=''):
"""Mount a wsgi callable at the given script_name."""
# Next line both 1) strips trailing slash and 2) maps "/" -> "".
script_name = script_name.rstrip("/")
script_name = script_name.rstrip('/')
self.apps[script_name] = wsgi_callable
def script_name(self, path=None):
"""The script_name of the app at the given path, or None.
"""Return the script_name of the app at the given path, or None.
If path is None, cherrypy.request is used.
"""
@ -250,22 +277,23 @@ class Tree(object):
if path in self.apps:
return path
if path == "":
if path == '':
return None
# Move one node up the tree and try again.
path = path[:path.rfind("/")]
path = path[:path.rfind('/')]
def __call__(self, environ, start_response):
"""Pre-initialize WSGI env and call WSGI-callable."""
# If you're calling this, then you're probably setting SCRIPT_NAME
# to '' (some WSGI servers always set SCRIPT_NAME to '').
# Try to look up the app using the full path.
env1x = environ
if environ.get(ntou('wsgi.version')) == (ntou('u'), 0):
if six.PY2 and environ.get(ntou('wsgi.version')) == (ntou('u'), 0):
env1x = _cpwsgi.downgrade_wsgi_ux_to_1x(environ)
path = httputil.urljoin(env1x.get('SCRIPT_NAME', ''),
env1x.get('PATH_INFO', ''))
sn = self.script_name(path or "/")
sn = self.script_name(path or '/')
if sn is None:
start_response('404 Not Found', [])
return []
@ -274,26 +302,12 @@ class Tree(object):
# Correct the SCRIPT_NAME and PATH_INFO environ entries.
environ = environ.copy()
if not py3k:
if environ.get(ntou('wsgi.version')) == (ntou('u'), 0):
# Python 2/WSGI u.0: all strings MUST be of type unicode
enc = environ[ntou('wsgi.url_encoding')]
environ[ntou('SCRIPT_NAME')] = sn.decode(enc)
environ[ntou('PATH_INFO')] = path[
len(sn.rstrip("/")):].decode(enc)
else:
# Python 2/WSGI 1.x: all strings MUST be of type str
environ['SCRIPT_NAME'] = sn
environ['PATH_INFO'] = path[len(sn.rstrip("/")):]
if six.PY2 and environ.get(ntou('wsgi.version')) == (ntou('u'), 0):
# Python 2/WSGI u.0: all strings MUST be of type unicode
enc = environ[ntou('wsgi.url_encoding')]
environ[ntou('SCRIPT_NAME')] = sn.decode(enc)
environ[ntou('PATH_INFO')] = path[len(sn.rstrip('/')):].decode(enc)
else:
if environ.get(ntou('wsgi.version')) == (ntou('u'), 0):
# Python 3/WSGI u.0: all strings MUST be full unicode
environ['SCRIPT_NAME'] = sn
environ['PATH_INFO'] = path[len(sn.rstrip("/")):]
else:
# Python 3/WSGI 1.x: all strings MUST be ISO-8859-1 str
environ['SCRIPT_NAME'] = sn.encode(
'utf-8').decode('ISO-8859-1')
environ['PATH_INFO'] = path[
len(sn.rstrip("/")):].encode('utf-8').decode('ISO-8859-1')
environ['SCRIPT_NAME'] = sn
environ['PATH_INFO'] = path[len(sn.rstrip('/')):]
return app(environ, start_response)