54 changed files with 4042 additions and 7890 deletions
@ -1,25 +1,25 @@ |
|||
Copyright (c) 2004-2016, CherryPy Team (team@cherrypy.org) |
|||
All rights reserved. |
|||
|
|||
Redistribution and use in source and binary forms, with or without modification, |
|||
are permitted provided that the following conditions are met: |
|||
|
|||
* Redistributions of source code must retain the above copyright notice, |
|||
this list of conditions and the following disclaimer. |
|||
* Redistributions in binary form must reproduce the above copyright notice, |
|||
this list of conditions and the following disclaimer in the documentation |
|||
and/or other materials provided with the distribution. |
|||
* Neither the name of the CherryPy Team nor the names of its contributors |
|||
may be used to endorse or promote products derived from this software |
|||
without specific prior written permission. |
|||
|
|||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
|||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE |
|||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
|||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
|||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
|||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|||
Copyright (c) 2004-2016, CherryPy Team (team@cherrypy.org) |
|||
All rights reserved. |
|||
|
|||
Redistribution and use in source and binary forms, with or without modification, |
|||
are permitted provided that the following conditions are met: |
|||
|
|||
* Redistributions of source code must retain the above copyright notice, |
|||
this list of conditions and the following disclaimer. |
|||
* Redistributions in binary form must reproduce the above copyright notice, |
|||
this list of conditions and the following disclaimer in the documentation |
|||
and/or other materials provided with the distribution. |
|||
* Neither the name of the CherryPy Team nor the names of its contributors |
|||
may be used to endorse or promote products derived from this software |
|||
without specific prior written permission. |
|||
|
|||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
|||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE |
|||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
|||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
|||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
|||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|||
|
@ -1,4 +1,5 @@ |
|||
CherryPy 6.0.2 Official distribution: https://pypi.python.org/packages/source/C/CherryPy/CherryPy-6.0.2.tar.gz |
|||
CherryPy 8.1.0 |
|||
Official distribution: https://github.com/cherrypy/cherrypy/releases |
|||
The folders 'tutorial', 'test' and 'scaffold' have been removed. |
|||
This file has been added. |
|||
|
|||
|
File diff suppressed because it is too large
@ -1,241 +0,0 @@ |
|||
# This is a backport of Python-2.4's threading.local() implementation |
|||
|
|||
"""Thread-local objects |
|||
|
|||
(Note that this module provides a Python version of thread |
|||
threading.local class. Depending on the version of Python you're |
|||
using, there may be a faster one available. You should always import |
|||
the local class from threading.) |
|||
|
|||
Thread-local objects support the management of thread-local data. |
|||
If you have data that you want to be local to a thread, simply create |
|||
a thread-local object and use its attributes: |
|||
|
|||
>>> mydata = local() |
|||
>>> mydata.number = 42 |
|||
>>> mydata.number |
|||
42 |
|||
|
|||
You can also access the local-object's dictionary: |
|||
|
|||
>>> mydata.__dict__ |
|||
{'number': 42} |
|||
>>> mydata.__dict__.setdefault('widgets', []) |
|||
[] |
|||
>>> mydata.widgets |
|||
[] |
|||
|
|||
What's important about thread-local objects is that their data are |
|||
local to a thread. If we access the data in a different thread: |
|||
|
|||
>>> log = [] |
|||
>>> def f(): |
|||
... items = mydata.__dict__.items() |
|||
... items.sort() |
|||
... log.append(items) |
|||
... mydata.number = 11 |
|||
... log.append(mydata.number) |
|||
|
|||
>>> import threading |
|||
>>> thread = threading.Thread(target=f) |
|||
>>> thread.start() |
|||
>>> thread.join() |
|||
>>> log |
|||
[[], 11] |
|||
|
|||
we get different data. Furthermore, changes made in the other thread |
|||
don't affect data seen in this thread: |
|||
|
|||
>>> mydata.number |
|||
42 |
|||
|
|||
Of course, values you get from a local object, including a __dict__ |
|||
attribute, are for whatever thread was current at the time the |
|||
attribute was read. For that reason, you generally don't want to save |
|||
these values across threads, as they apply only to the thread they |
|||
came from. |
|||
|
|||
You can create custom local objects by subclassing the local class: |
|||
|
|||
>>> class MyLocal(local): |
|||
... number = 2 |
|||
... initialized = False |
|||
... def __init__(self, **kw): |
|||
... if self.initialized: |
|||
... raise SystemError('__init__ called too many times') |
|||
... self.initialized = True |
|||
... self.__dict__.update(kw) |
|||
... def squared(self): |
|||
... return self.number ** 2 |
|||
|
|||
This can be useful to support default values, methods and |
|||
initialization. Note that if you define an __init__ method, it will be |
|||
called each time the local object is used in a separate thread. This |
|||
is necessary to initialize each thread's dictionary. |
|||
|
|||
Now if we create a local object: |
|||
|
|||
>>> mydata = MyLocal(color='red') |
|||
|
|||
Now we have a default number: |
|||
|
|||
>>> mydata.number |
|||
2 |
|||
|
|||
an initial color: |
|||
|
|||
>>> mydata.color |
|||
'red' |
|||
>>> del mydata.color |
|||
|
|||
And a method that operates on the data: |
|||
|
|||
>>> mydata.squared() |
|||
4 |
|||
|
|||
As before, we can access the data in a separate thread: |
|||
|
|||
>>> log = [] |
|||
>>> thread = threading.Thread(target=f) |
|||
>>> thread.start() |
|||
>>> thread.join() |
|||
>>> log |
|||
[[('color', 'red'), ('initialized', True)], 11] |
|||
|
|||
without affecting this thread's data: |
|||
|
|||
>>> mydata.number |
|||
2 |
|||
>>> mydata.color |
|||
Traceback (most recent call last): |
|||
... |
|||
AttributeError: 'MyLocal' object has no attribute 'color' |
|||
|
|||
Note that subclasses can define slots, but they are not thread |
|||
local. They are shared across threads: |
|||
|
|||
>>> class MyLocal(local): |
|||
... __slots__ = 'number' |
|||
|
|||
>>> mydata = MyLocal() |
|||
>>> mydata.number = 42 |
|||
>>> mydata.color = 'red' |
|||
|
|||
So, the separate thread: |
|||
|
|||
>>> thread = threading.Thread(target=f) |
|||
>>> thread.start() |
|||
>>> thread.join() |
|||
|
|||
affects what we see: |
|||
|
|||
>>> mydata.number |
|||
11 |
|||
|
|||
>>> del mydata |
|||
""" |
|||
|
|||
# Threading import is at end |
|||
|
|||
|
|||
class _localbase(object): |
|||
__slots__ = '_local__key', '_local__args', '_local__lock' |
|||
|
|||
def __new__(cls, *args, **kw): |
|||
self = object.__new__(cls) |
|||
key = 'thread.local.' + str(id(self)) |
|||
object.__setattr__(self, '_local__key', key) |
|||
object.__setattr__(self, '_local__args', (args, kw)) |
|||
object.__setattr__(self, '_local__lock', RLock()) |
|||
|
|||
if args or kw and (cls.__init__ is object.__init__): |
|||
raise TypeError("Initialization arguments are not supported") |
|||
|
|||
# We need to create the thread dict in anticipation of |
|||
# __init__ being called, to make sure we don't call it |
|||
# again ourselves. |
|||
dict = object.__getattribute__(self, '__dict__') |
|||
currentThread().__dict__[key] = dict |
|||
|
|||
return self |
|||
|
|||
|
|||
def _patch(self): |
|||
key = object.__getattribute__(self, '_local__key') |
|||
d = currentThread().__dict__.get(key) |
|||
if d is None: |
|||
d = {} |
|||
currentThread().__dict__[key] = d |
|||
object.__setattr__(self, '__dict__', d) |
|||
|
|||
# we have a new instance dict, so call out __init__ if we have |
|||
# one |
|||
cls = type(self) |
|||
if cls.__init__ is not object.__init__: |
|||
args, kw = object.__getattribute__(self, '_local__args') |
|||
cls.__init__(self, *args, **kw) |
|||
else: |
|||
object.__setattr__(self, '__dict__', d) |
|||
|
|||
|
|||
class local(_localbase): |
|||
|
|||
def __getattribute__(self, name): |
|||
lock = object.__getattribute__(self, '_local__lock') |
|||
lock.acquire() |
|||
try: |
|||
_patch(self) |
|||
return object.__getattribute__(self, name) |
|||
finally: |
|||
lock.release() |
|||
|
|||
def __setattr__(self, name, value): |
|||
lock = object.__getattribute__(self, '_local__lock') |
|||
lock.acquire() |
|||
try: |
|||
_patch(self) |
|||
return object.__setattr__(self, name, value) |
|||
finally: |
|||
lock.release() |
|||
|
|||
def __delattr__(self, name): |
|||
lock = object.__getattribute__(self, '_local__lock') |
|||
lock.acquire() |
|||
try: |
|||
_patch(self) |
|||
return object.__delattr__(self, name) |
|||
finally: |
|||
lock.release() |
|||
|
|||
def __del__(): |
|||
threading_enumerate = enumerate |
|||
__getattribute__ = object.__getattribute__ |
|||
|
|||
def __del__(self): |
|||
key = __getattribute__(self, '_local__key') |
|||
|
|||
try: |
|||
threads = list(threading_enumerate()) |
|||
except: |
|||
# if enumerate fails, as it seems to do during |
|||
# shutdown, we'll skip cleanup under the assumption |
|||
# that there is nothing to clean up |
|||
return |
|||
|
|||
for thread in threads: |
|||
try: |
|||
__dict__ = thread.__dict__ |
|||
except AttributeError: |
|||
# Thread is dying, rest in peace |
|||
continue |
|||
|
|||
if key in __dict__: |
|||
try: |
|||
del __dict__[key] |
|||
except KeyError: |
|||
pass # didn't have anything in this thread |
|||
|
|||
return __del__ |
|||
__del__ = __del__() |
|||
|
|||
from threading import currentThread, enumerate, RLock |
@ -1,6 +0,0 @@ |
|||
import warnings |
|||
warnings.warn('cherrypy.lib.http has been deprecated and will be removed ' |
|||
'in CherryPy 3.3 use cherrypy.lib.httputil instead.', |
|||
DeprecationWarning) |
|||
|
|||
from cherrypy.lib.httputil import * |
@ -1,147 +1,142 @@ |
|||
""" |
|||
Platform-independent file locking. Inspired by and modeled after zc.lockfile. |
|||
""" |
|||
|
|||
import os |
|||
|
|||
try: |
|||
import msvcrt |
|||
except ImportError: |
|||
pass |
|||
|
|||
try: |
|||
import fcntl |
|||
except ImportError: |
|||
pass |
|||
|
|||
|
|||
class LockError(Exception): |
|||
|
|||
"Could not obtain a lock" |
|||
|
|||
msg = "Unable to lock %r" |
|||
|
|||
def __init__(self, path): |
|||
super(LockError, self).__init__(self.msg % path) |
|||
|
|||
|
|||
class UnlockError(LockError): |
|||
|
|||
"Could not release a lock" |
|||
|
|||
msg = "Unable to unlock %r" |
|||
|
|||
|
|||
# first, a default, naive locking implementation |
|||
class LockFile(object): |
|||
|
|||
""" |
|||
A default, naive locking implementation. Always fails if the file |
|||
already exists. |
|||
""" |
|||
|
|||
def __init__(self, path): |
|||
self.path = path |
|||
try: |
|||
fd = os.open(path, os.O_CREAT | os.O_WRONLY | os.O_EXCL) |
|||
except OSError: |
|||
raise LockError(self.path) |
|||
os.close(fd) |
|||
|
|||
def release(self): |
|||
os.remove(self.path) |
|||
|
|||
def remove(self): |
|||
pass |
|||
|
|||
|
|||
class SystemLockFile(object): |
|||
|
|||
""" |
|||
An abstract base class for platform-specific locking. |
|||
""" |
|||
|
|||
def __init__(self, path): |
|||
self.path = path |
|||
|
|||
try: |
|||
# Open lockfile for writing without truncation: |
|||
self.fp = open(path, 'r+') |
|||
except IOError: |
|||
# If the file doesn't exist, IOError is raised; Use a+ instead. |
|||
# Note that there may be a race here. Multiple processes |
|||
# could fail on the r+ open and open the file a+, but only |
|||
# one will get the the lock and write a pid. |
|||
self.fp = open(path, 'a+') |
|||
|
|||
try: |
|||
self._lock_file() |
|||
except: |
|||
self.fp.seek(1) |
|||
self.fp.close() |
|||
del self.fp |
|||
raise |
|||
|
|||
self.fp.write(" %s\n" % os.getpid()) |
|||
self.fp.truncate() |
|||
self.fp.flush() |
|||
|
|||
def release(self): |
|||
if not hasattr(self, 'fp'): |
|||
return |
|||
self._unlock_file() |
|||
self.fp.close() |
|||
del self.fp |
|||
|
|||
def remove(self): |
|||
""" |
|||
Attempt to remove the file |
|||
""" |
|||
try: |
|||
os.remove(self.path) |
|||
except: |
|||
pass |
|||
|
|||
#@abc.abstract_method |
|||
# def _lock_file(self): |
|||
# """Attempt to obtain the lock on self.fp. Raise LockError if not |
|||
# acquired.""" |
|||
|
|||
def _unlock_file(self): |
|||
"""Attempt to obtain the lock on self.fp. Raise UnlockError if not |
|||
released.""" |
|||
|
|||
|
|||
class WindowsLockFile(SystemLockFile): |
|||
|
|||
def _lock_file(self): |
|||
# Lock just the first byte |
|||
try: |
|||
msvcrt.locking(self.fp.fileno(), msvcrt.LK_NBLCK, 1) |
|||
except IOError: |
|||
raise LockError(self.fp.name) |
|||
|
|||
def _unlock_file(self): |
|||
try: |
|||
self.fp.seek(0) |
|||
msvcrt.locking(self.fp.fileno(), msvcrt.LK_UNLCK, 1) |
|||
except IOError: |
|||
raise UnlockError(self.fp.name) |
|||
|
|||
if 'msvcrt' in globals(): |
|||
LockFile = WindowsLockFile |
|||
|
|||
|
|||
class UnixLockFile(SystemLockFile): |
|||
|
|||
def _lock_file(self): |
|||
flags = fcntl.LOCK_EX | fcntl.LOCK_NB |
|||
try: |
|||
fcntl.flock(self.fp.fileno(), flags) |
|||
except IOError: |
|||
raise LockError(self.fp.name) |
|||
|
|||
# no need to implement _unlock_file, it will be unlocked on close() |
|||
|
|||
if 'fcntl' in globals(): |
|||
LockFile = UnixLockFile |
|||
""" |
|||
Platform-independent file locking. Inspired by and modeled after zc.lockfile. |
|||
""" |
|||
|
|||
import os |
|||
|
|||
try: |
|||
import msvcrt |
|||
except ImportError: |
|||
pass |
|||
|
|||
try: |
|||
import fcntl |
|||
except ImportError: |
|||
pass |
|||
|
|||
|
|||
class LockError(Exception): |
|||
|
|||
'Could not obtain a lock' |
|||
|
|||
msg = 'Unable to lock %r' |
|||
|
|||
def __init__(self, path): |
|||
super(LockError, self).__init__(self.msg % path) |
|||
|
|||
|
|||
class UnlockError(LockError): |
|||
|
|||
'Could not release a lock' |
|||
|
|||
msg = 'Unable to unlock %r' |
|||
|
|||
|
|||
# first, a default, naive locking implementation |
|||
class LockFile(object): |
|||
|
|||
""" |
|||
A default, naive locking implementation. Always fails if the file |
|||
already exists. |
|||
""" |
|||
|
|||
def __init__(self, path): |
|||
self.path = path |
|||
try: |
|||
fd = os.open(path, os.O_CREAT | os.O_WRONLY | os.O_EXCL) |
|||
except OSError: |
|||
raise LockError(self.path) |
|||
os.close(fd) |
|||
|
|||
def release(self): |
|||
os.remove(self.path) |
|||
|
|||
def remove(self): |
|||
pass |
|||
|
|||
|
|||
class SystemLockFile(object): |
|||
|
|||
""" |
|||
An abstract base class for platform-specific locking. |
|||
""" |
|||
|
|||
def __init__(self, path): |
|||
self.path = path |
|||
|
|||
try: |
|||
# Open lockfile for writing without truncation: |
|||
self.fp = open(path, 'r+') |
|||
except IOError: |
|||
# If the file doesn't exist, IOError is raised; Use a+ instead. |
|||
# Note that there may be a race here. Multiple processes |
|||
# could fail on the r+ open and open the file a+, but only |
|||
# one will get the the lock and write a pid. |
|||
self.fp = open(path, 'a+') |
|||
|
|||
try: |
|||
self._lock_file() |
|||
except: |
|||
self.fp.seek(1) |
|||
self.fp.close() |
|||
del self.fp |
|||
raise |
|||
|
|||
self.fp.write(' %s\n' % os.getpid()) |
|||
self.fp.truncate() |
|||
self.fp.flush() |
|||
|
|||
def release(self): |
|||
if not hasattr(self, 'fp'): |
|||
return |
|||
self._unlock_file() |
|||
self.fp.close() |
|||
del self.fp |
|||
|
|||
def remove(self): |
|||
""" |
|||
Attempt to remove the file |
|||
""" |
|||
try: |
|||
os.remove(self.path) |
|||
except: |
|||
pass |
|||
|
|||
def _unlock_file(self): |
|||
"""Attempt to obtain the lock on self.fp. Raise UnlockError if not |
|||
released.""" |
|||
|
|||
|
|||
class WindowsLockFile(SystemLockFile): |
|||
|
|||
def _lock_file(self): |
|||
# Lock just the first byte |
|||
try: |
|||
msvcrt.locking(self.fp.fileno(), msvcrt.LK_NBLCK, 1) |
|||
except IOError: |
|||
raise LockError(self.fp.name) |
|||
|
|||
def _unlock_file(self): |
|||
try: |
|||
self.fp.seek(0) |
|||
msvcrt.locking(self.fp.fileno(), msvcrt.LK_UNLCK, 1) |
|||
except IOError: |
|||
raise UnlockError(self.fp.name) |
|||
|
|||
if 'msvcrt' in globals(): |
|||
LockFile = WindowsLockFile |
|||
|
|||
|
|||
class UnixLockFile(SystemLockFile): |
|||
|
|||
def _lock_file(self): |
|||
flags = fcntl.LOCK_EX | fcntl.LOCK_NB |
|||
try: |
|||
fcntl.flock(self.fp.fileno(), flags) |
|||
except IOError: |
|||
raise LockError(self.fp.name) |
|||
|
|||
# no need to implement _unlock_file, it will be unlocked on close() |
|||
|
|||
if 'fcntl' in globals(): |
|||
LockFile = UnixLockFile |
|||
|
File diff suppressed because it is too large
@ -0,0 +1,16 @@ |
|||
import six |
|||
|
|||
import mock |
|||
|
|||
from cherrypy import wsgiserver |
|||
|
|||
|
|||
class TestWSGIGateway_u0: |
|||
@mock.patch('cherrypy.wsgiserver.WSGIGateway_10.get_environ', |
|||
lambda self: {'foo': 'bar'}) |
|||
def test_decodes_items(self): |
|||
req = mock.MagicMock(path=b'/', qs=b'') |
|||
gw = wsgiserver.WSGIGateway_u0(req=req) |
|||
env = gw.get_environ() |
|||
assert env['foo'] == 'bar' |
|||
assert isinstance(env['foo'], six.text_type) |
File diff suppressed because it is too large
File diff suppressed because it is too large
Loading…
Reference in new issue