mirror of
https://github.com/Tautulli/Tautulli.git
synced 2025-08-14 02:26:58 -07:00
Update cherrypy-18.6.1
This commit is contained in:
parent
b3ae6bd695
commit
ebffd124f6
57 changed files with 1269 additions and 1509 deletions
|
@ -70,6 +70,11 @@ class file_generator(object):
|
|||
raise StopIteration()
|
||||
next = __next__
|
||||
|
||||
def __del__(self):
|
||||
"""Close input on descturct."""
|
||||
if hasattr(self.input, 'close'):
|
||||
self.input.close()
|
||||
|
||||
|
||||
def file_generator_limited(fileobj, count, chunk_size=65536):
|
||||
"""Yield the given file object in chunks.
|
||||
|
|
|
@ -23,8 +23,7 @@ of plaintext passwords as the credentials store::
|
|||
import time
|
||||
import functools
|
||||
from hashlib import md5
|
||||
|
||||
from six.moves.urllib.request import parse_http_list, parse_keqv_list
|
||||
from urllib.request import parse_http_list, parse_keqv_list
|
||||
|
||||
import cherrypy
|
||||
from cherrypy._cpcompat import ntob, tonative
|
||||
|
|
|
@ -37,11 +37,8 @@ import sys
|
|||
import threading
|
||||
import time
|
||||
|
||||
import six
|
||||
|
||||
import cherrypy
|
||||
from cherrypy.lib import cptools, httputil
|
||||
from cherrypy._cpcompat import Event
|
||||
|
||||
|
||||
class Cache(object):
|
||||
|
@ -82,7 +79,7 @@ class AntiStampedeCache(dict):
|
|||
If timeout is None, no waiting is performed nor sentinels used.
|
||||
"""
|
||||
value = self.get(key)
|
||||
if isinstance(value, Event):
|
||||
if isinstance(value, threading.Event):
|
||||
if timeout is None:
|
||||
# Ignore the other thread and recalc it ourselves.
|
||||
if debug:
|
||||
|
@ -122,7 +119,7 @@ class AntiStampedeCache(dict):
|
|||
"""Set the cached value for the given key."""
|
||||
existing = self.get(key)
|
||||
dict.__setitem__(self, key, value)
|
||||
if isinstance(existing, Event):
|
||||
if isinstance(existing, threading.Event):
|
||||
# Set Event.result so other threads waiting on it have
|
||||
# immediate access without needing to poll the cache again.
|
||||
existing.result = value
|
||||
|
@ -199,8 +196,7 @@ class MemoryCache(Cache):
|
|||
now = time.time()
|
||||
# Must make a copy of expirations so it doesn't change size
|
||||
# during iteration
|
||||
items = list(six.iteritems(self.expirations))
|
||||
for expiration_time, objects in items:
|
||||
for expiration_time, objects in self.expirations.copy().items():
|
||||
if expiration_time <= now:
|
||||
for obj_size, uri, sel_header_values in objects:
|
||||
try:
|
||||
|
|
|
@ -25,8 +25,7 @@ import sys
|
|||
import cgi
|
||||
import os
|
||||
import os.path
|
||||
|
||||
from six.moves import urllib
|
||||
import urllib.parse
|
||||
|
||||
import cherrypy
|
||||
|
||||
|
|
|
@ -193,10 +193,8 @@ import sys
|
|||
import threading
|
||||
import time
|
||||
|
||||
import six
|
||||
|
||||
import cherrypy
|
||||
from cherrypy._cpcompat import json
|
||||
from cherrypy._json import json
|
||||
|
||||
# ------------------------------- Statistics -------------------------------- #
|
||||
|
||||
|
@ -207,7 +205,7 @@ if not hasattr(logging, 'statistics'):
|
|||
def extrapolate_statistics(scope):
|
||||
"""Return an extrapolated copy of the given scope."""
|
||||
c = {}
|
||||
for k, v in list(scope.items()):
|
||||
for k, v in scope.copy().items():
|
||||
if isinstance(v, dict):
|
||||
v = extrapolate_statistics(v)
|
||||
elif isinstance(v, (list, tuple)):
|
||||
|
@ -366,8 +364,8 @@ class StatsTool(cherrypy.Tool):
|
|||
w['Bytes Written'] = cl
|
||||
appstats['Total Bytes Written'] += cl
|
||||
|
||||
w['Response Status'] = getattr(
|
||||
resp, 'output_status', None) or resp.status
|
||||
w['Response Status'] = \
|
||||
getattr(resp, 'output_status', resp.status).decode()
|
||||
|
||||
w['End Time'] = time.time()
|
||||
p = w['End Time'] - w['Start Time']
|
||||
|
@ -613,7 +611,7 @@ table.stats2 th {
|
|||
"""Return ([headers], [rows]) for the given collection."""
|
||||
# E.g., the 'Requests' dict.
|
||||
headers = []
|
||||
vals = six.itervalues(v)
|
||||
vals = v.values()
|
||||
for record in vals:
|
||||
for k3 in record:
|
||||
format = formatting.get(k3, missing)
|
||||
|
@ -679,7 +677,7 @@ table.stats2 th {
|
|||
def data(self):
|
||||
s = extrapolate_statistics(logging.statistics)
|
||||
cherrypy.response.headers['Content-Type'] = 'application/json'
|
||||
return json.dumps(s, sort_keys=True, indent=4)
|
||||
return json.dumps(s, sort_keys=True, indent=4).encode('utf-8')
|
||||
|
||||
@cherrypy.expose
|
||||
def pause(self, namespace):
|
||||
|
|
|
@ -3,9 +3,7 @@
|
|||
import logging
|
||||
import re
|
||||
from hashlib import md5
|
||||
|
||||
import six
|
||||
from six.moves import urllib
|
||||
import urllib.parse
|
||||
|
||||
import cherrypy
|
||||
from cherrypy._cpcompat import text_or_bytes
|
||||
|
@ -307,7 +305,7 @@ class SessionAuth(object):
|
|||
|
||||
def login_screen(self, from_page='..', username='', error_msg='',
|
||||
**kwargs):
|
||||
return (six.text_type("""<html><body>
|
||||
return (str("""<html><body>
|
||||
Message: %(error_msg)s
|
||||
<form method="post" action="do_login">
|
||||
Login: <input type="text" name="username" value="%(username)s" size="10" />
|
||||
|
@ -406,23 +404,22 @@ Message: %(error_msg)s
|
|||
|
||||
|
||||
def session_auth(**kwargs):
|
||||
"""Session authentication hook.
|
||||
|
||||
Any attribute of the SessionAuth class may be overridden
|
||||
via a keyword arg to this function:
|
||||
|
||||
""" + '\n '.join(
|
||||
'{!s}: {!s}'.format(k, type(getattr(SessionAuth, k)).__name__)
|
||||
for k in dir(SessionAuth)
|
||||
if not k.startswith('__')
|
||||
)
|
||||
sa = SessionAuth()
|
||||
for k, v in kwargs.items():
|
||||
setattr(sa, k, v)
|
||||
return sa.run()
|
||||
|
||||
|
||||
session_auth.__doc__ = (
|
||||
"""Session authentication hook.
|
||||
|
||||
Any attribute of the SessionAuth class may be overridden via a keyword arg
|
||||
to this function:
|
||||
|
||||
""" + '\n'.join(['%s: %s' % (k, type(getattr(SessionAuth, k)).__name__)
|
||||
for k in dir(SessionAuth) if not k.startswith('__')])
|
||||
)
|
||||
|
||||
|
||||
def log_traceback(severity=logging.ERROR, debug=False):
|
||||
"""Write the last error's traceback to the cherrypy error log."""
|
||||
cherrypy.log('', 'HTTP', severity=severity, traceback=True)
|
||||
|
|
|
@ -2,8 +2,6 @@ import struct
|
|||
import time
|
||||
import io
|
||||
|
||||
import six
|
||||
|
||||
import cherrypy
|
||||
from cherrypy._cpcompat import text_or_bytes
|
||||
from cherrypy.lib import file_generator
|
||||
|
@ -11,6 +9,10 @@ from cherrypy.lib import is_closable_iterator
|
|||
from cherrypy.lib import set_vary_header
|
||||
|
||||
|
||||
_COMPRESSION_LEVEL_FAST = 1
|
||||
_COMPRESSION_LEVEL_BEST = 9
|
||||
|
||||
|
||||
def decode(encoding=None, default_encoding='utf-8'):
|
||||
"""Replace or extend the list of charsets used to decode a request entity.
|
||||
|
||||
|
@ -50,7 +52,7 @@ class UTF8StreamEncoder:
|
|||
|
||||
def __next__(self):
|
||||
res = next(self._iterator)
|
||||
if isinstance(res, six.text_type):
|
||||
if isinstance(res, str):
|
||||
res = res.encode('utf-8')
|
||||
return res
|
||||
|
||||
|
@ -99,7 +101,7 @@ class ResponseEncoder:
|
|||
|
||||
def encoder(body):
|
||||
for chunk in body:
|
||||
if isinstance(chunk, six.text_type):
|
||||
if isinstance(chunk, str):
|
||||
chunk = chunk.encode(encoding, self.errors)
|
||||
yield chunk
|
||||
self.body = encoder(self.body)
|
||||
|
@ -112,7 +114,7 @@ class ResponseEncoder:
|
|||
self.attempted_charsets.add(encoding)
|
||||
body = []
|
||||
for chunk in self.body:
|
||||
if isinstance(chunk, six.text_type):
|
||||
if isinstance(chunk, str):
|
||||
try:
|
||||
chunk = chunk.encode(encoding, self.errors)
|
||||
except (LookupError, UnicodeError):
|
||||
|
@ -287,13 +289,29 @@ def compress(body, compress_level):
|
|||
"""Compress 'body' at the given compress_level."""
|
||||
import zlib
|
||||
|
||||
# See http://www.gzip.org/zlib/rfc-gzip.html
|
||||
# See https://tools.ietf.org/html/rfc1952
|
||||
yield b'\x1f\x8b' # ID1 and ID2: gzip marker
|
||||
yield b'\x08' # CM: compression method
|
||||
yield b'\x00' # FLG: none set
|
||||
# MTIME: 4 bytes
|
||||
yield struct.pack('<L', int(time.time()) & int('FFFFFFFF', 16))
|
||||
yield b'\x02' # XFL: max compression, slowest algo
|
||||
|
||||
# RFC 1952, section 2.3.1:
|
||||
#
|
||||
# XFL (eXtra FLags)
|
||||
# These flags are available for use by specific compression
|
||||
# methods. The "deflate" method (CM = 8) sets these flags as
|
||||
# follows:
|
||||
#
|
||||
# XFL = 2 - compressor used maximum compression,
|
||||
# slowest algorithm
|
||||
# XFL = 4 - compressor used fastest algorithm
|
||||
if compress_level == _COMPRESSION_LEVEL_BEST:
|
||||
yield b'\x02' # XFL: max compression, slowest algo
|
||||
elif compress_level == _COMPRESSION_LEVEL_FAST:
|
||||
yield b'\x04' # XFL: min compression, fastest algo
|
||||
else:
|
||||
yield b'\x00' # XFL: compression unset/tradeoff
|
||||
yield b'\xff' # OS: unknown
|
||||
|
||||
crc = zlib.crc32(b'')
|
||||
|
|
|
@ -10,17 +10,17 @@ to a public caning.
|
|||
import functools
|
||||
import email.utils
|
||||
import re
|
||||
import builtins
|
||||
from binascii import b2a_base64
|
||||
from cgi import parse_header
|
||||
from email.header import decode_header
|
||||
from http.server import BaseHTTPRequestHandler
|
||||
from urllib.parse import unquote_plus
|
||||
|
||||
import six
|
||||
from six.moves import range, builtins, map
|
||||
from six.moves.BaseHTTPServer import BaseHTTPRequestHandler
|
||||
import jaraco.collections
|
||||
|
||||
import cherrypy
|
||||
from cherrypy._cpcompat import ntob, ntou
|
||||
from cherrypy._cpcompat import unquote_plus
|
||||
|
||||
response_codes = BaseHTTPRequestHandler.responses.copy()
|
||||
|
||||
|
@ -143,7 +143,7 @@ class HeaderElement(object):
|
|||
return self.value < other.value
|
||||
|
||||
def __str__(self):
|
||||
p = [';%s=%s' % (k, v) for k, v in six.iteritems(self.params)]
|
||||
p = [';%s=%s' % (k, v) for k, v in self.params.items()]
|
||||
return str('%s%s' % (self.value, ''.join(p)))
|
||||
|
||||
def __bytes__(self):
|
||||
|
@ -209,14 +209,11 @@ class AcceptElement(HeaderElement):
|
|||
|
||||
Ref: https://github.com/cherrypy/cherrypy/issues/1370
|
||||
"""
|
||||
six.raise_from(
|
||||
cherrypy.HTTPError(
|
||||
400,
|
||||
'Malformed HTTP header: `{}`'.
|
||||
format(str(self)),
|
||||
),
|
||||
val_err,
|
||||
)
|
||||
raise cherrypy.HTTPError(
|
||||
400,
|
||||
'Malformed HTTP header: `{}`'.
|
||||
format(str(self)),
|
||||
) from val_err
|
||||
|
||||
def __cmp__(self, other):
|
||||
diff = builtins.cmp(self.qvalue, other.qvalue)
|
||||
|
@ -283,11 +280,11 @@ def valid_status(status):
|
|||
If status has no reason-phrase is supplied, a default reason-
|
||||
phrase will be provided.
|
||||
|
||||
>>> from six.moves import http_client
|
||||
>>> from six.moves.BaseHTTPServer import BaseHTTPRequestHandler
|
||||
>>> valid_status(http_client.ACCEPTED) == (
|
||||
... int(http_client.ACCEPTED),
|
||||
... ) + BaseHTTPRequestHandler.responses[http_client.ACCEPTED]
|
||||
>>> import http.client
|
||||
>>> from http.server import BaseHTTPRequestHandler
|
||||
>>> valid_status(http.client.ACCEPTED) == (
|
||||
... int(http.client.ACCEPTED),
|
||||
... ) + BaseHTTPRequestHandler.responses[http.client.ACCEPTED]
|
||||
True
|
||||
"""
|
||||
|
||||
|
@ -295,7 +292,7 @@ def valid_status(status):
|
|||
status = 200
|
||||
|
||||
code, reason = status, None
|
||||
if isinstance(status, six.string_types):
|
||||
if isinstance(status, str):
|
||||
code, _, reason = status.partition(' ')
|
||||
reason = reason.strip() or None
|
||||
|
||||
|
@ -390,77 +387,19 @@ def parse_query_string(query_string, keep_blank_values=True, encoding='utf-8'):
|
|||
return pm
|
||||
|
||||
|
||||
####
|
||||
# Inlined from jaraco.collections 1.5.2
|
||||
# Ref #1673
|
||||
class KeyTransformingDict(dict):
|
||||
"""
|
||||
A dict subclass that transforms the keys before they're used.
|
||||
Subclasses may override the default transform_key to customize behavior.
|
||||
"""
|
||||
@staticmethod
|
||||
def transform_key(key):
|
||||
return key
|
||||
|
||||
def __init__(self, *args, **kargs):
|
||||
super(KeyTransformingDict, self).__init__()
|
||||
# build a dictionary using the default constructs
|
||||
d = dict(*args, **kargs)
|
||||
# build this dictionary using transformed keys.
|
||||
for item in d.items():
|
||||
self.__setitem__(*item)
|
||||
|
||||
def __setitem__(self, key, val):
|
||||
key = self.transform_key(key)
|
||||
super(KeyTransformingDict, self).__setitem__(key, val)
|
||||
|
||||
def __getitem__(self, key):
|
||||
key = self.transform_key(key)
|
||||
return super(KeyTransformingDict, self).__getitem__(key)
|
||||
|
||||
def __contains__(self, key):
|
||||
key = self.transform_key(key)
|
||||
return super(KeyTransformingDict, self).__contains__(key)
|
||||
|
||||
def __delitem__(self, key):
|
||||
key = self.transform_key(key)
|
||||
return super(KeyTransformingDict, self).__delitem__(key)
|
||||
|
||||
def get(self, key, *args, **kwargs):
|
||||
key = self.transform_key(key)
|
||||
return super(KeyTransformingDict, self).get(key, *args, **kwargs)
|
||||
|
||||
def setdefault(self, key, *args, **kwargs):
|
||||
key = self.transform_key(key)
|
||||
return super(KeyTransformingDict, self).setdefault(
|
||||
key, *args, **kwargs)
|
||||
|
||||
def pop(self, key, *args, **kwargs):
|
||||
key = self.transform_key(key)
|
||||
return super(KeyTransformingDict, self).pop(key, *args, **kwargs)
|
||||
|
||||
def matching_key_for(self, key):
|
||||
"""
|
||||
Given a key, return the actual key stored in self that matches.
|
||||
Raise KeyError if the key isn't found.
|
||||
"""
|
||||
try:
|
||||
return next(e_key for e_key in self.keys() if e_key == key)
|
||||
except StopIteration:
|
||||
raise KeyError(key)
|
||||
####
|
||||
|
||||
|
||||
class CaseInsensitiveDict(KeyTransformingDict):
|
||||
class CaseInsensitiveDict(jaraco.collections.KeyTransformingDict):
|
||||
|
||||
"""A case-insensitive dict subclass.
|
||||
|
||||
Each key is changed on entry to str(key).title().
|
||||
Each key is changed on entry to title case.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def transform_key(key):
|
||||
return str(key).title()
|
||||
if key is None:
|
||||
# TODO(#1830): why?
|
||||
return 'None'
|
||||
return key.title()
|
||||
|
||||
|
||||
# TEXT = <any OCTET except CTLs, but including LWS>
|
||||
|
@ -499,9 +438,7 @@ class HeaderMap(CaseInsensitiveDict):
|
|||
|
||||
def elements(self, key):
|
||||
"""Return a sorted list of HeaderElements for the given header."""
|
||||
key = str(key).title()
|
||||
value = self.get(key)
|
||||
return header_elements(key, value)
|
||||
return header_elements(self.transform_key(key), self.get(key))
|
||||
|
||||
def values(self, key):
|
||||
"""Return a sorted list of HeaderElement.value for the given header."""
|
||||
|
@ -518,15 +455,14 @@ class HeaderMap(CaseInsensitiveDict):
|
|||
transmitting on the wire for HTTP.
|
||||
"""
|
||||
for k, v in header_items:
|
||||
if not isinstance(v, six.string_types) and \
|
||||
not isinstance(v, six.binary_type):
|
||||
v = six.text_type(v)
|
||||
if not isinstance(v, str) and not isinstance(v, bytes):
|
||||
v = str(v)
|
||||
|
||||
yield tuple(map(cls.encode_header_item, (k, v)))
|
||||
|
||||
@classmethod
|
||||
def encode_header_item(cls, item):
|
||||
if isinstance(item, six.text_type):
|
||||
if isinstance(item, str):
|
||||
item = cls.encode(item)
|
||||
|
||||
# See header_translate_* constants above.
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import cherrypy
|
||||
from cherrypy._cpcompat import text_or_bytes, ntou, json_encode, json_decode
|
||||
from cherrypy import _json as json
|
||||
from cherrypy._cpcompat import text_or_bytes, ntou
|
||||
|
||||
|
||||
def json_processor(entity):
|
||||
|
@ -9,7 +10,7 @@ def json_processor(entity):
|
|||
|
||||
body = entity.fp.read()
|
||||
with cherrypy.HTTPError.handle(ValueError, 400, 'Invalid JSON document'):
|
||||
cherrypy.serving.request.json = json_decode(body.decode('utf-8'))
|
||||
cherrypy.serving.request.json = json.decode(body.decode('utf-8'))
|
||||
|
||||
|
||||
def json_in(content_type=[ntou('application/json'), ntou('text/javascript')],
|
||||
|
@ -56,7 +57,7 @@ def json_in(content_type=[ntou('application/json'), ntou('text/javascript')],
|
|||
|
||||
def json_handler(*args, **kwargs):
|
||||
value = cherrypy.serving.request._json_inner_handler(*args, **kwargs)
|
||||
return json_encode(value)
|
||||
return json.encode(value)
|
||||
|
||||
|
||||
def json_out(content_type='application/json', debug=False,
|
||||
|
|
|
@ -18,13 +18,13 @@ by adding a named handler to Config.namespaces. The name can be any string,
|
|||
and the handler must be either a callable or a context manager.
|
||||
"""
|
||||
|
||||
from cherrypy._cpcompat import text_or_bytes
|
||||
from six.moves import configparser
|
||||
from six.moves import builtins
|
||||
|
||||
import builtins
|
||||
import configparser
|
||||
import operator
|
||||
import sys
|
||||
|
||||
from cherrypy._cpcompat import text_or_bytes
|
||||
|
||||
|
||||
class NamespaceSet(dict):
|
||||
|
||||
|
@ -36,7 +36,7 @@ class NamespaceSet(dict):
|
|||
namespace removed) and the config value.
|
||||
|
||||
Namespace handlers may be any Python callable; they may also be
|
||||
Python 2.5-style 'context managers', in which case their __enter__
|
||||
context managers, in which case their __enter__
|
||||
method should return a callable to be used as the handler.
|
||||
See cherrypy.tools (the Toolbox class) for an example.
|
||||
"""
|
||||
|
@ -61,10 +61,10 @@ class NamespaceSet(dict):
|
|||
bucket[name] = config[k]
|
||||
|
||||
# I chose __enter__ and __exit__ so someday this could be
|
||||
# rewritten using Python 2.5's 'with' statement:
|
||||
# for ns, handler in six.iteritems(self):
|
||||
# rewritten using 'with' statement:
|
||||
# for ns, handler in self.items():
|
||||
# with handler as callable:
|
||||
# for k, v in six.iteritems(ns_confs.get(ns, {})):
|
||||
# for k, v in ns_confs.get(ns, {}).items():
|
||||
# callable(k, v)
|
||||
for ns, handler in self.items():
|
||||
exit = getattr(handler, '__exit__', None)
|
||||
|
@ -211,122 +211,7 @@ class Parser(configparser.ConfigParser):
|
|||
# public domain "unrepr" implementation, found on the web and then improved.
|
||||
|
||||
|
||||
class _Builder2:
|
||||
|
||||
def build(self, o):
|
||||
m = getattr(self, 'build_' + o.__class__.__name__, None)
|
||||
if m is None:
|
||||
raise TypeError('unrepr does not recognize %s' %
|
||||
repr(o.__class__.__name__))
|
||||
return m(o)
|
||||
|
||||
def astnode(self, s):
|
||||
"""Return a Python2 ast Node compiled from a string."""
|
||||
try:
|
||||
import compiler
|
||||
except ImportError:
|
||||
# Fallback to eval when compiler package is not available,
|
||||
# e.g. IronPython 1.0.
|
||||
return eval(s)
|
||||
|
||||
p = compiler.parse('__tempvalue__ = ' + s)
|
||||
return p.getChildren()[1].getChildren()[0].getChildren()[1]
|
||||
|
||||
def build_Subscript(self, o):
|
||||
expr, flags, subs = o.getChildren()
|
||||
expr = self.build(expr)
|
||||
subs = self.build(subs)
|
||||
return expr[subs]
|
||||
|
||||
def build_CallFunc(self, o):
|
||||
children = o.getChildren()
|
||||
# Build callee from first child
|
||||
callee = self.build(children[0])
|
||||
# Build args and kwargs from remaining children
|
||||
args = []
|
||||
kwargs = {}
|
||||
for child in children[1:]:
|
||||
class_name = child.__class__.__name__
|
||||
# None is ignored
|
||||
if class_name == 'NoneType':
|
||||
continue
|
||||
# Keywords become kwargs
|
||||
if class_name == 'Keyword':
|
||||
kwargs.update(self.build(child))
|
||||
# Everything else becomes args
|
||||
else:
|
||||
args.append(self.build(child))
|
||||
|
||||
return callee(*args, **kwargs)
|
||||
|
||||
def build_Keyword(self, o):
|
||||
key, value_obj = o.getChildren()
|
||||
value = self.build(value_obj)
|
||||
kw_dict = {key: value}
|
||||
return kw_dict
|
||||
|
||||
def build_List(self, o):
|
||||
return map(self.build, o.getChildren())
|
||||
|
||||
def build_Const(self, o):
|
||||
return o.value
|
||||
|
||||
def build_Dict(self, o):
|
||||
d = {}
|
||||
i = iter(map(self.build, o.getChildren()))
|
||||
for el in i:
|
||||
d[el] = i.next()
|
||||
return d
|
||||
|
||||
def build_Tuple(self, o):
|
||||
return tuple(self.build_List(o))
|
||||
|
||||
def build_Name(self, o):
|
||||
name = o.name
|
||||
if name == 'None':
|
||||
return None
|
||||
if name == 'True':
|
||||
return True
|
||||
if name == 'False':
|
||||
return False
|
||||
|
||||
# See if the Name is a package or module. If it is, import it.
|
||||
try:
|
||||
return modules(name)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
# See if the Name is in builtins.
|
||||
try:
|
||||
return getattr(builtins, name)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
raise TypeError('unrepr could not resolve the name %s' % repr(name))
|
||||
|
||||
def build_Add(self, o):
|
||||
left, right = map(self.build, o.getChildren())
|
||||
return left + right
|
||||
|
||||
def build_Mul(self, o):
|
||||
left, right = map(self.build, o.getChildren())
|
||||
return left * right
|
||||
|
||||
def build_Getattr(self, o):
|
||||
parent = self.build(o.expr)
|
||||
return getattr(parent, o.attrname)
|
||||
|
||||
def build_NoneType(self, o):
|
||||
return None
|
||||
|
||||
def build_UnarySub(self, o):
|
||||
return -self.build(o.getChildren()[0])
|
||||
|
||||
def build_UnaryAdd(self, o):
|
||||
return self.build(o.getChildren()[0])
|
||||
|
||||
|
||||
class _Builder3:
|
||||
class _Builder:
|
||||
|
||||
def build(self, o):
|
||||
m = getattr(self, 'build_' + o.__class__.__name__, None)
|
||||
|
@ -441,7 +326,6 @@ class _Builder3:
|
|||
|
||||
# See if the Name is in builtins.
|
||||
try:
|
||||
import builtins
|
||||
return getattr(builtins, name)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
@ -482,10 +366,7 @@ def unrepr(s):
|
|||
"""Return a Python object compiled from a string."""
|
||||
if not s:
|
||||
return s
|
||||
if sys.version_info < (3, 0):
|
||||
b = _Builder2()
|
||||
else:
|
||||
b = _Builder3()
|
||||
b = _Builder()
|
||||
obj = b.astnode(s)
|
||||
return b.build(obj)
|
||||
|
||||
|
|
|
@ -106,10 +106,7 @@ import os
|
|||
import time
|
||||
import threading
|
||||
import binascii
|
||||
|
||||
import six
|
||||
from six.moves import cPickle as pickle
|
||||
import contextlib2
|
||||
import pickle
|
||||
|
||||
import zc.lockfile
|
||||
|
||||
|
@ -119,10 +116,6 @@ from cherrypy.lib import locking
|
|||
from cherrypy.lib import is_iterator
|
||||
|
||||
|
||||
if six.PY2:
|
||||
FileNotFoundError = OSError
|
||||
|
||||
|
||||
missing = object()
|
||||
|
||||
|
||||
|
@ -410,7 +403,7 @@ class RamSession(Session):
|
|||
"""Clean up expired sessions."""
|
||||
|
||||
now = self.now()
|
||||
for _id, (data, expiration_time) in list(six.iteritems(self.cache)):
|
||||
for _id, (data, expiration_time) in self.cache.copy().items():
|
||||
if expiration_time <= now:
|
||||
try:
|
||||
del self.cache[_id]
|
||||
|
@ -572,8 +565,6 @@ class FileSession(Session):
|
|||
def release_lock(self, path=None):
|
||||
"""Release the lock on the currently-loaded session data."""
|
||||
self.lock.close()
|
||||
with contextlib2.suppress(FileNotFoundError):
|
||||
os.remove(self.lock._path)
|
||||
self.locked = False
|
||||
|
||||
def clean_up(self):
|
||||
|
@ -624,7 +615,7 @@ class MemcachedSession(Session):
|
|||
# This is a separate set of locks per session id.
|
||||
locks = {}
|
||||
|
||||
servers = ['127.0.0.1:11211']
|
||||
servers = ['localhost:11211']
|
||||
|
||||
@classmethod
|
||||
def setup(cls, **kwargs):
|
||||
|
|
|
@ -5,12 +5,12 @@ import platform
|
|||
import re
|
||||
import stat
|
||||
import mimetypes
|
||||
import urllib.parse
|
||||
import unicodedata
|
||||
|
||||
from email.generator import _make_boundary as make_boundary
|
||||
from io import UnsupportedOperation
|
||||
|
||||
from six.moves import urllib
|
||||
|
||||
import cherrypy
|
||||
from cherrypy._cpcompat import ntob
|
||||
from cherrypy.lib import cptools, httputil, file_generator_limited
|
||||
|
@ -29,6 +29,30 @@ def _setup_mimetypes():
|
|||
_setup_mimetypes()
|
||||
|
||||
|
||||
def _make_content_disposition(disposition, file_name):
|
||||
"""Create HTTP header for downloading a file with a UTF-8 filename.
|
||||
|
||||
This function implements the recommendations of :rfc:`6266#appendix-D`.
|
||||
See this and related answers: https://stackoverflow.com/a/8996249/2173868.
|
||||
"""
|
||||
# As normalization algorithm for `unicodedata` is used composed form (NFC
|
||||
# and NFKC) with compatibility equivalence criteria (NFK), so "NFKC" is the
|
||||
# one. It first applies the compatibility decomposition, followed by the
|
||||
# canonical composition. Should be displayed in the same manner, should be
|
||||
# treated in the same way by applications such as alphabetizing names or
|
||||
# searching, and may be substituted for each other.
|
||||
# See: https://en.wikipedia.org/wiki/Unicode_equivalence.
|
||||
ascii_name = (
|
||||
unicodedata.normalize('NFKC', file_name).
|
||||
encode('ascii', errors='ignore').decode()
|
||||
)
|
||||
header = '{}; filename="{}"'.format(disposition, ascii_name)
|
||||
if ascii_name != file_name:
|
||||
quoted_name = urllib.parse.quote(file_name)
|
||||
header += '; filename*=UTF-8\'\'{}'.format(quoted_name)
|
||||
return header
|
||||
|
||||
|
||||
def serve_file(path, content_type=None, disposition=None, name=None,
|
||||
debug=False):
|
||||
"""Set status, headers, and body in order to serve the given path.
|
||||
|
@ -38,9 +62,10 @@ def serve_file(path, content_type=None, disposition=None, name=None,
|
|||
of the 'path' argument.
|
||||
|
||||
If disposition is not None, the Content-Disposition header will be set
|
||||
to "<disposition>; filename=<name>". If name is None, it will be set
|
||||
to the basename of path. If disposition is None, no Content-Disposition
|
||||
header will be written.
|
||||
to "<disposition>; filename=<name>; filename*=utf-8''<name>"
|
||||
as described in :rfc:`6266#appendix-D`.
|
||||
If name is None, it will be set to the basename of path.
|
||||
If disposition is None, no Content-Disposition header will be written.
|
||||
"""
|
||||
response = cherrypy.serving.response
|
||||
|
||||
|
@ -93,7 +118,7 @@ def serve_file(path, content_type=None, disposition=None, name=None,
|
|||
if disposition is not None:
|
||||
if name is None:
|
||||
name = os.path.basename(path)
|
||||
cd = '%s; filename="%s"' % (disposition, name)
|
||||
cd = _make_content_disposition(disposition, name)
|
||||
response.headers['Content-Disposition'] = cd
|
||||
if debug:
|
||||
cherrypy.log('Content-Disposition: %r' % cd, 'TOOLS.STATIC')
|
||||
|
@ -112,9 +137,10 @@ def serve_fileobj(fileobj, content_type=None, disposition=None, name=None,
|
|||
The Content-Type header will be set to the content_type arg, if provided.
|
||||
|
||||
If disposition is not None, the Content-Disposition header will be set
|
||||
to "<disposition>; filename=<name>". If name is None, 'filename' will
|
||||
not be set. If disposition is None, no Content-Disposition header will
|
||||
be written.
|
||||
to "<disposition>; filename=<name>; filename*=utf-8''<name>"
|
||||
as described in :rfc:`6266#appendix-D`.
|
||||
If name is None, 'filename' will not be set.
|
||||
If disposition is None, no Content-Disposition header will be written.
|
||||
|
||||
CAUTION: If the request contains a 'Range' header, one or more seek()s will
|
||||
be performed on the file object. This may cause undesired behavior if
|
||||
|
@ -150,7 +176,7 @@ def serve_fileobj(fileobj, content_type=None, disposition=None, name=None,
|
|||
if name is None:
|
||||
cd = disposition
|
||||
else:
|
||||
cd = '%s; filename="%s"' % (disposition, name)
|
||||
cd = _make_content_disposition(disposition, name)
|
||||
response.headers['Content-Disposition'] = cd
|
||||
if debug:
|
||||
cherrypy.log('Content-Disposition: %r' % cd, 'TOOLS.STATIC')
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
"""XML-RPC tool helpers."""
|
||||
import sys
|
||||
|
||||
from six.moves.xmlrpc_client import (
|
||||
from xmlrpc.client import (
|
||||
loads as xmlrpc_loads, dumps as xmlrpc_dumps,
|
||||
Fault as XMLRPCFault
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue