Skip to content

Commit

Permalink
cherrypy update
Browse files Browse the repository at this point in the history
fixes #3348
  • Loading branch information
AdeHub committed Dec 7, 2024
1 parent 94d6243 commit a09e91f
Show file tree
Hide file tree
Showing 28 changed files with 511 additions and 107 deletions.
11 changes: 7 additions & 4 deletions lib/cherrypy/_cpcompat.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,19 @@


def ntob(n, encoding='ISO-8859-1'):
"""Return the given native string as a byte string in the given
encoding.
"""Convert a native :class:`str` to a :class:`bytes` instance.
The encoding can be changed to non-ASCII optionally.
"""
assert_native(n)
# In Python 3, the native string type is unicode
return n.encode(encoding)


def ntou(n, encoding='ISO-8859-1'):
"""Return the given native string as a unicode string with the given
encoding.
"""Convert a native :class:`str` to a :class:`str` instance.
This doesn't actually do anything.
"""
assert_native(n)
# In Python 3, the native string type is unicode
Expand All @@ -48,6 +50,7 @@ def tonative(n, encoding='ISO-8859-1'):


def assert_native(n):
"""Ensure that input is a native :class:`str`."""
if not isinstance(n, str):
raise TypeError('n must be a native str (got %s)' % type(n).__name__)

Expand Down
26 changes: 23 additions & 3 deletions lib/cherrypy/_cpdispatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class PageHandler(object):
"""Callable which sets response.body."""

def __init__(self, callable, *args, **kwargs):
"""Initialize the page handler."""
self.callable = callable
self.args = args
self.kwargs = kwargs
Expand All @@ -36,6 +37,7 @@ def args(self):

@args.setter
def args(self, args):
"""Set the request arguments in order."""
cherrypy.serving.request.args = args
return cherrypy.serving.request.args

Expand All @@ -46,10 +48,12 @@ def kwargs(self):

@kwargs.setter
def kwargs(self, kwargs):
"""Set the named request keyword arguments as :class:`dict`."""
cherrypy.serving.request.kwargs = kwargs
return cherrypy.serving.request.kwargs

def __call__(self):
"""Invoke an HTTP handler callable for :class:`PageHandler`."""
try:
return self.callable(*self.args, **self.kwargs)
except TypeError:
Expand Down Expand Up @@ -203,15 +207,18 @@ def test_callable_spec(callable, callable_args, callable_kwargs):
import inspect
except ImportError:
def test_callable_spec(callable, args, kwargs): # noqa: F811
"""Do nothing as a no-op."""
return None
else:
def getargspec(callable):
"""Get argument specification using :mod:`inspect`."""
return inspect.getfullargspec(callable)[:4]


class LateParamPageHandler(PageHandler):
"""Page handler callable with delayed request parameters binding.
"""When passing cherrypy.request.params to the page handler, we do not
When passing ``cherrypy.request.params`` to the page handler, we do not
want to capture that dict too early; we want to give tools like the
decoding tool a chance to modify the params dict in-between the lookup
of the handler and the actual calling of the handler. This subclass
Expand All @@ -221,14 +228,19 @@ class LateParamPageHandler(PageHandler):

@property
def kwargs(self):
"""Page handler kwargs (with cherrypy.request.params copied in)."""
"""Page handler keyword arguments.
The returned value contains data merged in
from ``cherrypy.request.params``.
"""
kwargs = cherrypy.serving.request.params.copy()
if self._kwargs:
kwargs.update(self._kwargs)
return kwargs

@kwargs.setter
def kwargs(self, kwargs):
"""Set the named request keyword arguments as :class:`dict`."""
cherrypy.serving.request.kwargs = kwargs
self._kwargs = kwargs

Expand All @@ -238,6 +250,7 @@ def kwargs(self, kwargs):
string.punctuation, '_' * len(string.punctuation))

def validate_translator(t):
"""Ensure the translator is of the correct length and size."""
if not isinstance(t, str) or len(t) != 256:
raise ValueError(
'The translate argument must be a str of len 256.')
Expand All @@ -246,6 +259,7 @@ def validate_translator(t):
string.punctuation, '_' * len(string.punctuation))

def validate_translator(t):
"""Ensure the translator is of the correct length and size."""
if not isinstance(t, dict):
raise ValueError('The translate argument must be a dict.')

Expand Down Expand Up @@ -273,6 +287,7 @@ class Dispatcher(object):

def __init__(self, dispatch_method_name=None,
translate=punctuation_to_underscores):
"""Initialize the HTTP request dispatcher."""
validate_translator(translate)
self.translate = translate
if dispatch_method_name:
Expand Down Expand Up @@ -389,7 +404,9 @@ def find_handler(self, path):
object_trail.append([name, node, nodeconf, segleft])

def set_conf():
"""Collapse all object_trail config into cherrypy.request.config.
"""Collapse all ``object_trail`` conf into config.
The config being ``cherrypy.request.config``.
"""
base = cherrypy.config.copy()
# Note that we merge the config from each node
Expand Down Expand Up @@ -505,10 +522,12 @@ def __init__(self, full_result=False, **mapper_options):
self.mapper.controller_scan = self.controllers.keys

def connect(self, name, route, controller, **kwargs):
"""Mount an HTTP handler into the router."""
self.controllers[name] = controller
self.mapper.connect(name, route, controller=name, **kwargs)

def redirect(self, url):
"""Perform an HTTP redirect to the given URL."""
raise cherrypy.HTTPRedirect(url)

def __call__(self, path_info):
Expand Down Expand Up @@ -602,6 +621,7 @@ def merge(nodeconf):


def XMLRPCDispatcher(next_dispatcher=Dispatcher()):
"""Chain an HTTP dispatcher variant implementing XML-RPC."""
from cherrypy.lib import xmlrpcutil

def xmlrpc_dispatch(path_info):
Expand Down
21 changes: 14 additions & 7 deletions lib/cherrypy/_cperror.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,6 @@ class Root:

class CherryPyException(Exception):
"""A base class for CherryPy exceptions."""
pass


class InternalRedirect(CherryPyException):
Expand All @@ -150,6 +149,7 @@ class InternalRedirect(CherryPyException):
"""

def __init__(self, path, query_string=''):
"""Initialize the internal redirect exception."""
self.request = cherrypy.serving.request

self.query_string = query_string
Expand Down Expand Up @@ -202,6 +202,7 @@ class HTTPRedirect(CherryPyException):
"""The encoding when passed urls are not native strings."""

def __init__(self, urls, status=None, encoding=None):
"""Initialize the HTTP redirect exception."""
self.urls = abs_urls = [
# Note that urljoin will "do the right thing" whether url is:
# 1. a complete URL with host (e.g. "http://www.example.com/test")
Expand All @@ -227,7 +228,9 @@ def __init__(self, urls, status=None, encoding=None):

@classproperty
def default_status(cls):
"""The default redirect status for the request.
"""Redirect status for the request.
This is the default handler.
RFC 2616 indicates a 301 response code fits our goal; however,
browser support for 301 is quite messy. Use 302/303 instead. See
Expand All @@ -242,8 +245,9 @@ def status(self):
return status

def set_response(self):
"""Modify cherrypy.response status, headers, and body to represent
self.
"""Modify ``cherrypy.response`` to represent ``self``.
Modifies status, headers, and body.
CherryPy uses this internally, but you can also use it to create
an HTTPRedirect object and set its output without *raising* the
Expand Down Expand Up @@ -366,6 +370,7 @@ class HTTPError(CherryPyException):
"""The HTTP Reason-Phrase string."""

def __init__(self, status=500, message=None):
"""Initialize an HTTP error."""
self.status = status
try:
self.code, self.reason, defaultmsg = _httputil.valid_status(status)
Expand All @@ -381,8 +386,9 @@ def __init__(self, status=500, message=None):
CherryPyException.__init__(self, status, message)

def set_response(self):
"""Modify cherrypy.response status, headers, and body to represent
self.
"""Modify ``cherrypy.response`` to represent ``self``.
Modifies status, headers, and body.
CherryPy uses this internally, but you can also use it to create
an HTTPError object and set its output without *raising* the
Expand All @@ -408,6 +414,7 @@ def set_response(self):
_be_ie_unfriendly(self.code)

def get_error_page(self, *args, **kwargs):
"""Compose an HTML page with error information."""
return get_error_page(*args, **kwargs)

def __call__(self):
Expand All @@ -432,6 +439,7 @@ class NotFound(HTTPError):
"""

def __init__(self, path=None):
"""Initialize an HTTP Not Found error."""
if path is None:
request = cherrypy.serving.request
path = request.script_name + request.path_info
Expand Down Expand Up @@ -600,7 +608,6 @@ def bare_error(extrabody=None):
is set in the body. If extrabody is a string, it will be appended
as-is to the body.
"""

# The whole point of this function is to be a last line-of-defense
# in handling errors. That is, it must not raise any errors itself;
# it cannot be allowed to fail. Therefore, don't add to it!
Expand Down
17 changes: 14 additions & 3 deletions lib/cherrypy/_cplogging.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""
CherryPy logging module.
Simple config
=============
Expand Down Expand Up @@ -126,12 +128,15 @@ class NullHandler(logging.Handler):
"""A no-op logging handler to silence the logging.lastResort handler."""

def handle(self, record):
"""Handle a log record doing no-op."""
pass

def emit(self, record):
"""Emit a log record doing no-op."""
pass

def createLock(self):
"""Lock log write with no-op."""
self.lock = None


Expand Down Expand Up @@ -167,6 +172,7 @@ class LogManager(object):
"""

def __init__(self, appid=None, logger_root='cherrypy'):
"""Initialize a CherryPy log manager."""
self.logger_root = logger_root
self.appid = appid
if appid is None:
Expand Down Expand Up @@ -217,11 +223,11 @@ def error(self, msg='', context='', severity=logging.INFO,
)

def __call__(self, *args, **kwargs):
"""An alias for ``error``."""
"""Record an error log entry."""
return self.error(*args, **kwargs)

def access(self):
"""Write to the access log (in Apache/NCSA Combined Log format).
r"""Write to the access log (in Apache/NCSA Combined Log format).
See the
`apache documentation
Expand Down Expand Up @@ -414,7 +420,10 @@ def wsgi(self, newvalue):


class WSGIErrorHandler(logging.Handler):
"A handler class which writes logging records to environ['wsgi.errors']."
"""A handler class writing logs to WSGI env.
Specifically, the target is ``environ['wsgi.errors']``.
"""

def flush(self):
"""Flushes the stream."""
Expand Down Expand Up @@ -450,6 +459,8 @@ def emit(self, record):


class LazyRfc3339UtcTime(object):
"""A postponed timestamp string retrieval class."""

def __str__(self):
"""Return datetime in RFC3339 UTC Format."""
iso_formatted_now = datetime.datetime.now(
Expand Down
10 changes: 10 additions & 0 deletions lib/cherrypy/_cpmodpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ def setup_server():


def setup(req):
"""Execute pre-initialization functions."""
from mod_python import apache

# Run any setup functions defined by a "PythonOption cherrypy.setup"
Expand Down Expand Up @@ -140,6 +141,7 @@ def __init__(self, req):


def handler(req):
"""Invoke the HTTP handler."""
from mod_python import apache
try:
global _isSetUp
Expand Down Expand Up @@ -251,6 +253,7 @@ def handler(req):


def send_response(req, status, headers, body, stream=False):
"""Send the HTTP response to the client."""
# Set response status
req.status = int(status[:3])

Expand All @@ -276,17 +279,20 @@ def send_response(req, status, headers, body, stream=False):
import subprocess

def popen(fullcmd):
"""Invoke a subprocess via :mod:`subprocess`."""
p = subprocess.Popen(fullcmd, shell=True,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
close_fds=True)
return p.stdout
except ImportError:
def popen(fullcmd):
"""Invoke a subprocess via :mod:`os`."""
pipein, pipeout = os.popen4(fullcmd)
return pipeout


def read_process(cmd, args=''):
"""Return a subprocess standard output."""
fullcmd = '%s %s' % (cmd, args)
pipeout = popen(fullcmd)
try:
Expand All @@ -305,6 +311,7 @@ def read_process(cmd, args=''):


class ModPythonServer(object):
"""A server wrapper for ``mod_python``."""

template = """
# Apache2 server configuration file for running CherryPy with mod_python.
Expand All @@ -323,13 +330,15 @@ class ModPythonServer(object):

def __init__(self, loc='/', port=80, opts=None, apache_path='apache',
handler='cherrypy._cpmodpy::handler'):
"""Initialize a ``mod_python`` server."""
self.loc = loc
self.port = port
self.opts = opts
self.apache_path = apache_path
self.handler = handler

def start(self):
"""Start an Apache2/httpd server."""
opts = ''.join([' PythonOption %s %s\n' % (k, v)
for k, v in self.opts])
conf_data = self.template % {'port': self.port,
Expand All @@ -347,5 +356,6 @@ def start(self):
return response

def stop(self):
"""Stop an Apache2/httpd server."""
os.popen('apache -k stop')
self.ready = False
Loading

0 comments on commit a09e91f

Please sign in to comment.