Update CherryPy to 5.1.0

This commit is contained in:
JonnyWong16 2016-04-23 16:24:41 -07:00
parent f9825410dc
commit b2304992e5
25 changed files with 2383 additions and 130 deletions

View file

@ -56,10 +56,10 @@ with customized or extended components. The core API's are:
These API's are described in the `CherryPy specification <https://bitbucket.org/cherrypy/cherrypy/wiki/CherryPySpec>`_.
"""
__version__ = "3.8.0"
__version__ = "5.1.0"
from cherrypy._cpcompat import urljoin as _urljoin, urlencode as _urlencode
from cherrypy._cpcompat import basestring, unicodestr, set
from cherrypy._cpcompat import basestring, unicodestr
from cherrypy._cperror import HTTPError, HTTPRedirect, InternalRedirect
from cherrypy._cperror import NotFound, CherryPyException, TimeoutError

View file

@ -110,11 +110,6 @@ def assert_native(n):
if not isinstance(n, nativestr):
raise TypeError("n must be a native str (got %s)" % type(n).__name__)
try:
set = set
except NameError:
from sets import Set as set
try:
# Python 3.1+
from base64 import decodebytes as _base64_decodebytes
@ -137,17 +132,6 @@ def base64_decode(n, encoding='ISO-8859-1'):
else:
return b
try:
# Python 2.5+
from hashlib import md5
except ImportError:
from md5 import new as md5
try:
# Python 2.5+
from hashlib import sha1 as sha
except ImportError:
from sha import new as sha
try:
sorted = sorted
@ -333,18 +317,10 @@ except ImportError:
# In Python 3, pickle is the sped-up C version.
import pickle
try:
os.urandom(20)
import binascii
import binascii
def random20():
def random20():
return binascii.hexlify(os.urandom(20)).decode('ascii')
except (AttributeError, NotImplementedError):
import random
# os.urandom not available until Python 2.4. Fall back to random.random.
def random20():
return sha('%s' % random.random()).hexdigest()
try:
from _thread import get_ident as get_thread_ident

View file

@ -883,7 +883,7 @@ class Popen(object):
startupinfo.dwFlags |= _subprocess.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = _subprocess.SW_HIDE
comspec = os.environ.get("COMSPEC", "cmd.exe")
args = '{} /c "{}"'.format(comspec, args)
args = '{0} /c "{1}"'.format(comspec, args)
if (_subprocess.GetVersion() >= 0x80000000 or
os.path.basename(comspec).lower() == "command.com"):
# Win9x, or using command.com on NT. We need to
@ -1029,7 +1029,7 @@ class Popen(object):
elif sig == signal.CTRL_BREAK_EVENT:
os.kill(self.pid, signal.CTRL_BREAK_EVENT)
else:
raise ValueError("Unsupported signal: {}".format(sig))
raise ValueError("Unsupported signal: {0}".format(sig))
def terminate(self):
"""Terminates the process

View file

@ -119,7 +119,7 @@ style) context manager.
"""
import cherrypy
from cherrypy._cpcompat import set, basestring
from cherrypy._cpcompat import basestring
from cherrypy.lib import reprconf
# Deprecated in CherryPy 3.2--remove in 3.3

View file

@ -18,7 +18,6 @@ except AttributeError:
classtype = type
import cherrypy
from cherrypy._cpcompat import set
class PageHandler(object):

View file

@ -296,7 +296,8 @@ class AppResponse(object):
"""Create a Request object using environ."""
env = self.environ.get
local = httputil.Host('', int(env('SERVER_PORT', 80)),
local = httputil.Host('',
int(env('SERVER_PORT', 80) or -1),
env('SERVER_NAME', ''))
remote = httputil.Host(env('REMOTE_ADDR', ''),
int(env('REMOTE_PORT', -1) or -1),

View file

@ -53,15 +53,12 @@ def start(configfiles=None, daemonize=False, environment=None,
cherrypy.server.unsubscribe()
addr = cherrypy.server.bind_addr
if fastcgi:
f = servers.FlupFCGIServer(application=cherrypy.tree,
bindAddress=addr)
elif scgi:
f = servers.FlupSCGIServer(application=cherrypy.tree,
bindAddress=addr)
else:
f = servers.FlupCGIServer(application=cherrypy.tree,
bindAddress=addr)
cls = (
servers.FlupFCGIServer if fastcgi else
servers.FlupSCGIServer if scgi else
servers.FlupCGIServer
)
f = cls(application=cherrypy.tree, bindAddress=addr)
s = servers.ServerAdapter(engine, httpserver=f, bind_addr=addr)
s.subscribe()

View file

@ -23,10 +23,11 @@ __date__ = 'April 2009'
import time
from hashlib import md5
from cherrypy._cpcompat import parse_http_list, parse_keqv_list
import cherrypy
from cherrypy._cpcompat import md5, ntob
from cherrypy._cpcompat import ntob
md5_hex = lambda s: md5(ntob(s)).hexdigest()
qop_auth = 'auth'

View file

@ -210,6 +210,7 @@ def extrapolate_statistics(scope):
# -------------------- CherryPy Applications Statistics --------------------- #
import sys
import threading
import time
@ -294,6 +295,11 @@ class ByteCountWrapper(object):
average_uriset_time = lambda s: s['Count'] and (s['Sum'] / s['Count']) or 0
def _get_threading_ident():
if sys.version_info >= (3, 3):
return threading.get_ident()
return threading._get_ident()
class StatsTool(cherrypy.Tool):
"""Record various information about the current request."""
@ -322,7 +328,7 @@ class StatsTool(cherrypy.Tool):
appstats['Current Requests'] += 1
appstats['Total Requests'] += 1
appstats['Requests'][threading._get_ident()] = {
appstats['Requests'][_get_threading_ident()] = {
'Bytes Read': None,
'Bytes Written': None,
# Use a lambda so the ip gets updated by tools.proxy later
@ -339,7 +345,7 @@ class StatsTool(cherrypy.Tool):
debug=False, **kwargs):
"""Record the end of a request."""
resp = cherrypy.serving.response
w = appstats['Requests'][threading._get_ident()]
w = appstats['Requests'][_get_threading_ident()]
r = cherrypy.request.rfile.bytes_read
w['Bytes Read'] = r
@ -605,7 +611,13 @@ table.stats2 th {
"""Return ([headers], [rows]) for the given collection."""
# E.g., the 'Requests' dict.
headers = []
for record in v.itervalues():
try:
# python2
vals = v.itervalues()
except AttributeError:
# python3
vals = v.values()
for record in vals:
for k3 in record:
format = formatting.get(k3, missing)
if format is None:

View file

@ -2,9 +2,10 @@
import logging
import re
from hashlib import md5
import cherrypy
from cherrypy._cpcompat import basestring, md5, set, unicodestr
from cherrypy._cpcompat import basestring, unicodestr
from cherrypy.lib import httputil as _httputil
from cherrypy.lib import is_iterator
@ -192,11 +193,10 @@ def proxy(base=None, local='X-Forwarded-Host', remote='X-Forwarded-For',
if lbase is not None:
base = lbase.split(',')[0]
if not base:
base = request.headers.get('Host', '127.0.0.1')
port = request.local.port
if port == 80:
base = '127.0.0.1'
else:
base = '127.0.0.1:%s' % port
if port != 80:
base += ':%s' % port
if base.find("://") == -1:
# add http:// or https:// if needed

View file

@ -2,7 +2,7 @@ import struct
import time
import cherrypy
from cherrypy._cpcompat import basestring, BytesIO, ntob, set, unicodestr
from cherrypy._cpcompat import basestring, BytesIO, ntob, unicodestr
from cherrypy.lib import file_generator
from cherrypy.lib import is_closable_iterator
from cherrypy.lib import set_vary_header

View file

@ -62,7 +62,9 @@ __all__ = ("digestAuth", "basicAuth", "doAuth", "checkResponse",
##########################################################################
import time
from cherrypy._cpcompat import base64_decode, ntob, md5
from hashlib import md5
from cherrypy._cpcompat import base64_decode, ntob
from cherrypy._cpcompat import parse_http_list, parse_keqv_list
MD5 = "MD5"

View file

@ -8,7 +8,7 @@ You can profile any of your pages as follows::
from cherrypy.lib import profiler
class Root:
p = profile.Profiler("/path/to/profile/dir")
p = profiler.Profiler("/path/to/profile/dir")
def index(self):
self.p.run(self._index)

View file

@ -281,6 +281,7 @@ class _Builder2:
# Everything else becomes args
else :
args.append(self.build(child))
return callee(*args, **kwargs)
def build_Keyword(self, o):
@ -377,7 +378,39 @@ class _Builder3:
def build_Index(self, o):
return self.build(o.value)
def _build_call35(self, o):
"""
Workaround for python 3.5 _ast.Call signature, docs found here
https://greentreesnakes.readthedocs.org/en/latest/nodes.html
"""
import ast
callee = self.build(o.func)
args = []
if o.args is not None:
for a in o.args:
if isinstance(a, ast.Starred):
args.append(self.build(a.value))
else:
args.append(self.build(a))
kwargs = {}
for kw in o.keywords:
if kw.arg is None: # double asterix `**`
rst = self.build(kw.value)
if not isinstance(rst, dict):
raise TypeError("Invalid argument for call."
"Must be a mapping object.")
# give preference to the keys set directly from arg=value
for k, v in rst.items():
if k not in kwargs:
kwargs[k] = v
else: # defined on the call as: arg=value
kwargs[kw.arg] = self.build(kw.value)
return callee(*args, **kwargs)
def build_Call(self, o):
if sys.version_info >= (3, 5):
return self._build_call35(o)
callee = self.build(o.func)
if o.args is None:
@ -388,13 +421,16 @@ class _Builder3:
if o.starargs is None:
starargs = ()
else:
starargs = self.build(o.starargs)
starargs = tuple(self.build(o.starargs))
if o.kwargs is None:
kwargs = {}
else:
kwargs = self.build(o.kwargs)
if o.keywords is not None: # direct a=b keywords
for kw in o.keywords:
# preference because is a direct keyword against **kwargs
kwargs[kw.arg] = self.build(kw.value)
return callee(*(args + starargs), **kwargs)
def build_List(self, o):

View file

@ -49,7 +49,10 @@ def serve_file(path, content_type=None, disposition=None, name=None,
try:
st = os.stat(path)
except OSError:
except (OSError, TypeError, ValueError):
# OSError when file fails to stat
# TypeError on Python 2 when there's a null byte
# ValueError on Python 3 when there's a null byte
if debug:
cherrypy.log('os.stat(%r) failed' % path, 'TOOLS.STATIC')
raise cherrypy.NotFound()

View file

@ -8,7 +8,7 @@ import time
import threading
from cherrypy._cpcompat import basestring, get_daemon, get_thread_ident
from cherrypy._cpcompat import ntob, set, Timer, SetDaemonProperty
from cherrypy._cpcompat import ntob, Timer, SetDaemonProperty
# _module__file__base is used by Autoreload to make
# absolute any filenames retrieved from sys.modules which are not
@ -109,12 +109,35 @@ class SignalHandler(object):
self.handlers['SIGINT'] = self._jython_SIGINT_handler
self._previous_handlers = {}
# used to determine is the process is a daemon in `self._is_daemonized`
self._original_pid = os.getpid()
def _jython_SIGINT_handler(self, signum=None, frame=None):
# See http://bugs.jython.org/issue1313
self.bus.log('Keyboard Interrupt: shutting down bus')
self.bus.exit()
def _is_daemonized(self):
"""Return boolean indicating if the current process is
running as a daemon.
The criteria to determine the `daemon` condition is to verify
if the current pid is not the same as the one that got used on
the initial construction of the plugin *and* the stdin is not
connected to a terminal.
The sole validation of the tty is not enough when the plugin
is executing inside other process like in a CI tool
(Buildbot, Jenkins).
"""
if (self._original_pid != os.getpid() and
not os.isatty(sys.stdin.fileno())):
return True
else:
return False
def subscribe(self):
"""Subscribe self.handlers to signals."""
for sig, func in self.handlers.items():
@ -180,13 +203,13 @@ class SignalHandler(object):
def handle_SIGHUP(self):
"""Restart if daemonized, else exit."""
if os.isatty(sys.stdin.fileno()):
if self._is_daemonized():
self.bus.log("SIGHUP caught while daemonized. Restarting.")
self.bus.restart()
else:
# not daemonized (may be foreground or background)
self.bus.log("SIGHUP caught but not daemonized. Exiting.")
self.bus.exit()
else:
self.bus.log("SIGHUP caught while daemonized. Restarting.")
self.bus.restart()
try:

View file

@ -183,8 +183,7 @@ class ServerAdapter(object):
if not self.httpserver:
return ''
host, port = self.bind_addr
if getattr(self.httpserver, 'ssl_certificate', None) or \
getattr(self.httpserver, 'ssl_adapter', None):
if getattr(self.httpserver, 'ssl_adapter', None):
scheme = "https"
if port != 443:
host += ":%s" % port

View file

@ -68,8 +68,6 @@ import time
import traceback as _traceback
import warnings
from cherrypy._cpcompat import set
# Here I save the value of os.getcwd(), which, if I am imported early enough,
# will be the directory from which the startup script was run. This is needed
# by _do_execv(), to change back to the original directory before execv()ing a

View file

@ -0,0 +1,22 @@
# Apache2 server conf file for using CherryPy with mod_fcgid.
# This doesn't have to be "C:/", but it has to be a directory somewhere, and
# MUST match the directory used in the FastCgiExternalServer directive, below.
DocumentRoot "C:/"
ServerName 127.0.0.1
Listen 80
LoadModule fastcgi_module modules/mod_fastcgi.dll
LoadModule rewrite_module modules/mod_rewrite.so
Options ExecCGI
SetHandler fastcgi-script
RewriteEngine On
# Send requests for any URI to our fastcgi handler.
RewriteRule ^(.*)$ /fastcgi.pyc [L]
# The FastCgiExternalServer directive defines filename as an external FastCGI application.
# If filename does not begin with a slash (/) then it is assumed to be relative to the ServerRoot.
# The filename does not have to exist in the local filesystem. URIs that Apache resolves to this
# filename will be handled by this external FastCGI application.
FastCgiExternalServer "C:/fastcgi.pyc" -host 127.0.0.1:8088

View file

@ -0,0 +1,3 @@
[/]
log.error_file: "error.log"
log.access_file: "access.log"

View file

@ -0,0 +1,14 @@
[global]
# Uncomment this when you're done developing
#environment: "production"
server.socket_host: "0.0.0.0"
server.socket_port: 8088
# Uncomment the following lines to run on HTTPS at the same time
#server.2.socket_host: "0.0.0.0"
#server.2.socket_port: 8433
#server.2.ssl_certificate: '../test/test.pem'
#server.2.ssl_private_key: '../test/test.pem'
tree.myapp: cherrypy.Application(scaffold.root, "/", "example.conf")

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

View file

@ -68,7 +68,7 @@ class SSL_fileobject(wsgiserver.CP_fileobject):
time.sleep(self.ssl_retry)
except SSL.WantWriteError:
time.sleep(self.ssl_retry)
except SSL.SysCallError, e:
except SSL.SysCallError as e:
if is_reader and e.args == (-1, 'Unexpected EOF'):
return ""
@ -76,7 +76,7 @@ class SSL_fileobject(wsgiserver.CP_fileobject):
if is_reader and errnum in wsgiserver.socket_errors_to_ignore:
return ""
raise socket.error(errnum)
except SSL.Error, e:
except SSL.Error as e:
if is_reader and e.args == (-1, 'Unexpected EOF'):
return ""

View file

@ -75,7 +75,8 @@ __all__ = ['HTTPRequest', 'HTTPConnection', 'HTTPServer',
'WorkerThread', 'ThreadPool', 'SSLAdapter',
'CherryPyWSGIServer',
'Gateway', 'WSGIGateway', 'WSGIGateway_10', 'WSGIGateway_u0',
'WSGIPathInfoDispatcher', 'get_ssl_adapter_class']
'WSGIPathInfoDispatcher', 'get_ssl_adapter_class',
'socket_errors_to_ignore']
import os
try:
@ -83,19 +84,33 @@ try:
except:
import Queue as queue
import re
import rfc822
import email.utils
import socket
import sys
import threading
import time
import traceback as traceback_
import operator
from urllib import unquote
import warnings
import errno
import logging
try:
# prefer slower Python-based io module
import _pyio as io
except ImportError:
# Python 2.6
import io
if 'win' in sys.platform and hasattr(socket, "AF_INET6"):
if not hasattr(socket, 'IPPROTO_IPV6'):
socket.IPPROTO_IPV6 = 41
if not hasattr(socket, 'IPV6_V6ONLY'):
socket.IPV6_V6ONLY = 27
try:
import cStringIO as StringIO
except ImportError:
import StringIO
DEFAULT_BUFFER_SIZE = -1
DEFAULT_BUFFER_SIZE = io.DEFAULT_BUFFER_SIZE
class FauxSocket(object):
@ -109,23 +124,6 @@ _fileobject_uses_str_type = isinstance(
socket._fileobject(FauxSocket())._rbuf, basestring)
del FauxSocket # this class is not longer required for anything.
import threading
import time
import traceback
def format_exc(limit=None):
"""Like print_exc() but return a string. Backport for Python 2.3."""
try:
etype, value, tb = sys.exc_info()
return ''.join(traceback.format_exception(etype, value, tb, limit))
finally:
etype = value = tb = None
import operator
from urllib import unquote
import warnings
if sys.version_info >= (3, 0):
bytestr = bytes
@ -165,8 +163,6 @@ ASTERISK = ntob('*')
FORWARD_SLASH = ntob('/')
quoted_slash = re.compile(ntob("(?i)%2F"))
import errno
def plat_specific_errors(*errnames):
"""Return error numbers for all errors in errnames on this platform.
@ -210,7 +206,6 @@ comma_separated_headers = [
]
import logging
if not hasattr(logging, 'statistics'):
logging.statistics = {}
@ -674,6 +669,10 @@ class HTTPRequest(object):
# uri may be an abs_path (including "http://host.domain.tld");
scheme, authority, path = self.parse_request_uri(uri)
if path is None:
self.simple_response("400 Bad Request",
"Invalid path in Request-URI.")
return False
if NUMBER_SIGN in path:
self.simple_response("400 Bad Request",
"Illegal #fragment in Request-URI.")
@ -970,7 +969,7 @@ class HTTPRequest(object):
self.rfile.read(remaining)
if "date" not in hkeys:
self.outheaders.append(("Date", rfc822.formatdate()))
self.outheaders.append(("Date", email.utils.formatdate()))
if "server" not in hkeys:
self.outheaders.append(("Server", self.server.server_name))
@ -1051,7 +1050,7 @@ class CP_fileobject(socket._fileobject):
if size < 0:
# Read until EOF
# reset _rbuf. we consume it via buf.
self._rbuf = StringIO.StringIO()
self._rbuf = io.BytesIO()
while True:
data = self.recv(rbufsize)
if not data:
@ -1066,12 +1065,12 @@ class CP_fileobject(socket._fileobject):
# return.
buf.seek(0)
rv = buf.read(size)
self._rbuf = StringIO.StringIO()
self._rbuf = io.BytesIO()
self._rbuf.write(buf.read())
return rv
# reset _rbuf. we consume it via buf.
self._rbuf = StringIO.StringIO()
self._rbuf = io.BytesIO()
while True:
left = size - buf_len
# recv() will malloc the amount of memory given as its
@ -1109,7 +1108,7 @@ class CP_fileobject(socket._fileobject):
buf.seek(0)
bline = buf.readline(size)
if bline.endswith('\n') or len(bline) == size:
self._rbuf = StringIO.StringIO()
self._rbuf = io.BytesIO()
self._rbuf.write(buf.read())
return bline
del bline
@ -1120,7 +1119,7 @@ class CP_fileobject(socket._fileobject):
buf.seek(0)
buffers = [buf.read()]
# reset _rbuf. we consume it via buf.
self._rbuf = StringIO.StringIO()
self._rbuf = io.BytesIO()
data = None
recv = self.recv
while data != "\n":
@ -1132,7 +1131,7 @@ class CP_fileobject(socket._fileobject):
buf.seek(0, 2) # seek end
# reset _rbuf. we consume it via buf.
self._rbuf = StringIO.StringIO()
self._rbuf = io.BytesIO()
while True:
data = self.recv(self._rbufsize)
if not data:
@ -1154,11 +1153,11 @@ class CP_fileobject(socket._fileobject):
if buf_len >= size:
buf.seek(0)
rv = buf.read(size)
self._rbuf = StringIO.StringIO()
self._rbuf = io.BytesIO()
self._rbuf.write(buf.read())
return rv
# reset _rbuf. we consume it via buf.
self._rbuf = StringIO.StringIO()
self._rbuf = io.BytesIO()
while True:
data = self.recv(self._rbufsize)
if not data:
@ -1757,7 +1756,7 @@ class HTTPServer(object):
timeout = 10
"""The timeout in seconds for accepted connections (default 10)."""
version = "CherryPy/3.8.0"
version = "CherryPy/5.1.0"
"""A version string for the HTTPServer."""
software = None
@ -1884,25 +1883,6 @@ class HTTPServer(object):
if self.software is None:
self.software = "%s Server" % self.version
# SSL backward compatibility
if (self.ssl_adapter is None and
getattr(self, 'ssl_certificate', None) and
getattr(self, 'ssl_private_key', None)):
warnings.warn(
"SSL attributes are deprecated in CherryPy 3.2, and will "
"be removed in CherryPy 3.3. Use an ssl_adapter attribute "
"instead.",
DeprecationWarning
)
try:
from cherrypy.wsgiserver.ssl_pyopenssl import pyOpenSSLAdapter
except ImportError:
pass
else:
self.ssl_adapter = pyOpenSSLAdapter(
self.ssl_certificate, self.ssl_private_key,
getattr(self, 'ssl_certificate_chain', None))
# Select the appropriate socket
if isinstance(self.bind_addr, basestring):
# AF_UNIX socket
@ -1915,7 +1895,7 @@ class HTTPServer(object):
# So everyone can access the socket...
try:
os.chmod(self.bind_addr, 511) # 0777
os.chmod(self.bind_addr, 0o777)
except:
pass
@ -1984,7 +1964,7 @@ class HTTPServer(object):
sys.stderr.write(msg + '\n')
sys.stderr.flush()
if traceback:
tblines = format_exc()
tblines = traceback_.format_exc()
sys.stderr.write(tblines)
sys.stderr.flush()
@ -2186,7 +2166,7 @@ ssl_adapters = {
}
def get_ssl_adapter_class(name='pyopenssl'):
def get_ssl_adapter_class(name='builtin'):
"""Return an SSL adapter class for the given name."""
adapter = ssl_adapters[name.lower()]
if isinstance(adapter, basestring):

File diff suppressed because it is too large Load diff