From f1d44c051dfa5f6219551dc4ffae04be7952fb31 Mon Sep 17 00:00:00 2001
From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>
Date: Thu, 6 Jun 2024 21:39:33 -0700
Subject: [PATCH 1/8] Fix loading of scheduled tasks when tasks are disabled
---
data/interfaces/default/scheduler_table.html | 7 ++-----
plexpy/common.py | 2 +-
2 files changed, 3 insertions(+), 6 deletions(-)
diff --git a/data/interfaces/default/scheduler_table.html b/data/interfaces/default/scheduler_table.html
index 8d264aa9..c4087f17 100644
--- a/data/interfaces/default/scheduler_table.html
+++ b/data/interfaces/default/scheduler_table.html
@@ -13,8 +13,6 @@ DOCUMENTATION :: END
import datetime
import plexpy
from plexpy import common, helpers
-
- scheduled_jobs = [j.id for j in plexpy.SCHED.get_jobs()]
%>
@@ -29,16 +27,15 @@ DOCUMENTATION :: END
% for job, job_type in common.SCHEDULER_LIST.items():
- % if job in scheduled_jobs:
<%
sched_job = plexpy.SCHED.get_job(job)
- now = datetime.datetime.now(sched_job.next_run_time.tzinfo)
%>
+ % if sched_job:
${sched_job.id} |
Active |
${helpers.format_timedelta_Hms(sched_job.trigger.interval)} |
- ${helpers.format_timedelta_Hms(sched_job.next_run_time - now)} |
+ ${helpers.format_timedelta_Hms(sched_job.next_run_time - datetime.datetime.now(sched_job.next_run_time.tzinfo))} |
${sched_job.next_run_time.astimezone(plexpy.SYS_TIMEZONE).strftime('%Y-%m-%d %H:%M:%S')} |
% elif job_type == 'websocket' and plexpy.WS_CONNECTED:
diff --git a/plexpy/common.py b/plexpy/common.py
index ae6f6c98..fb35beb3 100644
--- a/plexpy/common.py
+++ b/plexpy/common.py
@@ -237,7 +237,7 @@ EXTRA_TYPES = {
}
SCHEDULER_LIST = [
- ('Check GitHub for updates', 'websocket'),
+ ('Check GitHub for updates', 'scheduled'),
('Check for server response', 'websocket'),
('Check for active sessions', 'websocket'),
('Check for recently added items', 'websocket'),
From 55573d26eaa7d9f98a84215c757a5b5b2030ce52 Mon Sep 17 00:00:00 2001
From: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>
Date: Tue, 18 Jun 2024 19:19:18 -0700
Subject: [PATCH 2/8] Ignore shutdown exception in cheroot
---
plexpy/webstart.py | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/plexpy/webstart.py b/plexpy/webstart.py
index 62ab41ae..8a6edc6e 100644
--- a/plexpy/webstart.py
+++ b/plexpy/webstart.py
@@ -19,6 +19,7 @@ import os
import ssl
import sys
+import cheroot.errors
import cherrypy
import plexpy
@@ -260,6 +261,13 @@ def initialize(options):
}
}
+ # Catch shutdown errors that can break cherrypy/cheroot
+ # See https://github.com/cherrypy/cheroot/issues/710
+ try:
+ cheroot.errors.acceptable_sock_shutdown_exceptions += (OSError,)
+ except AttributeError:
+ pass
+
cherrypy.tree.mount(WebInterface(), options['http_root'], config=conf)
if plexpy.HTTP_ROOT != '/':
cherrypy.tree.mount(BaseRedirect(), '/')
From 43e71d836a7e7b21cf96acd9d85f2bd02b1d227b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 19 Jun 2024 00:01:34 -0700
Subject: [PATCH 3/8] Bump requests from 2.31.0 to 2.32.3 (#2338)
* Bump requests from 2.31.0 to 2.32.3
Bumps [requests](https://github.com/psf/requests) from 2.31.0 to 2.32.3.
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.31.0...v2.32.3)
---
updated-dependencies:
- dependency-name: requests
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
* Update requests==2.32.3
---------
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>
[skip ci]
---
lib/requests/__init__.py | 6 +-
lib/requests/__version__.py | 6 +-
lib/requests/adapters.py | 225 +++++++++++++++++++++++++++++++----
lib/requests/api.py | 2 +-
lib/requests/auth.py | 1 -
lib/requests/compat.py | 25 +++-
lib/requests/cookies.py | 16 +--
lib/requests/exceptions.py | 10 ++
lib/requests/models.py | 13 +-
lib/requests/packages.py | 23 ++--
lib/requests/sessions.py | 12 +-
lib/requests/status_codes.py | 10 +-
lib/requests/utils.py | 16 +--
requirements.txt | 2 +-
14 files changed, 287 insertions(+), 80 deletions(-)
diff --git a/lib/requests/__init__.py b/lib/requests/__init__.py
index 300a16c5..051cda13 100644
--- a/lib/requests/__init__.py
+++ b/lib/requests/__init__.py
@@ -83,7 +83,11 @@ def check_compatibility(urllib3_version, chardet_version, charset_normalizer_ver
# charset_normalizer >= 2.0.0 < 4.0.0
assert (2, 0, 0) <= (major, minor, patch) < (4, 0, 0)
else:
- raise Exception("You need either charset_normalizer or chardet installed")
+ warnings.warn(
+ "Unable to find acceptable character detection dependency "
+ "(chardet or charset_normalizer).",
+ RequestsDependencyWarning,
+ )
def _check_cryptography(cryptography_version):
diff --git a/lib/requests/__version__.py b/lib/requests/__version__.py
index 5063c3f8..2c105aca 100644
--- a/lib/requests/__version__.py
+++ b/lib/requests/__version__.py
@@ -5,10 +5,10 @@
__title__ = "requests"
__description__ = "Python HTTP for Humans."
__url__ = "https://requests.readthedocs.io"
-__version__ = "2.31.0"
-__build__ = 0x023100
+__version__ = "2.32.3"
+__build__ = 0x023203
__author__ = "Kenneth Reitz"
__author_email__ = "me@kennethreitz.org"
-__license__ = "Apache 2.0"
+__license__ = "Apache-2.0"
__copyright__ = "Copyright Kenneth Reitz"
__cake__ = "\u2728 \U0001f370 \u2728"
diff --git a/lib/requests/adapters.py b/lib/requests/adapters.py
index 78e3bb6e..9a58b160 100644
--- a/lib/requests/adapters.py
+++ b/lib/requests/adapters.py
@@ -8,6 +8,8 @@ and maintain connections.
import os.path
import socket # noqa: F401
+import typing
+import warnings
from urllib3.exceptions import ClosedPoolError, ConnectTimeoutError
from urllib3.exceptions import HTTPError as _HTTPError
@@ -25,6 +27,7 @@ from urllib3.poolmanager import PoolManager, proxy_from_url
from urllib3.util import Timeout as TimeoutSauce
from urllib3.util import parse_url
from urllib3.util.retry import Retry
+from urllib3.util.ssl_ import create_urllib3_context
from .auth import _basic_auth_str
from .compat import basestring, urlparse
@@ -61,12 +64,76 @@ except ImportError:
raise InvalidSchema("Missing dependencies for SOCKS support.")
+if typing.TYPE_CHECKING:
+ from .models import PreparedRequest
+
+
DEFAULT_POOLBLOCK = False
DEFAULT_POOLSIZE = 10
DEFAULT_RETRIES = 0
DEFAULT_POOL_TIMEOUT = None
+try:
+ import ssl # noqa: F401
+
+ _preloaded_ssl_context = create_urllib3_context()
+ _preloaded_ssl_context.load_verify_locations(
+ extract_zipped_paths(DEFAULT_CA_BUNDLE_PATH)
+ )
+except ImportError:
+ # Bypass default SSLContext creation when Python
+ # interpreter isn't built with the ssl module.
+ _preloaded_ssl_context = None
+
+
+def _urllib3_request_context(
+ request: "PreparedRequest",
+ verify: "bool | str | None",
+ client_cert: "typing.Tuple[str, str] | str | None",
+ poolmanager: "PoolManager",
+) -> "(typing.Dict[str, typing.Any], typing.Dict[str, typing.Any])":
+ host_params = {}
+ pool_kwargs = {}
+ parsed_request_url = urlparse(request.url)
+ scheme = parsed_request_url.scheme.lower()
+ port = parsed_request_url.port
+
+ # Determine if we have and should use our default SSLContext
+ # to optimize performance on standard requests.
+ poolmanager_kwargs = getattr(poolmanager, "connection_pool_kw", {})
+ has_poolmanager_ssl_context = poolmanager_kwargs.get("ssl_context")
+ should_use_default_ssl_context = (
+ _preloaded_ssl_context is not None and not has_poolmanager_ssl_context
+ )
+
+ cert_reqs = "CERT_REQUIRED"
+ if verify is False:
+ cert_reqs = "CERT_NONE"
+ elif verify is True and should_use_default_ssl_context:
+ pool_kwargs["ssl_context"] = _preloaded_ssl_context
+ elif isinstance(verify, str):
+ if not os.path.isdir(verify):
+ pool_kwargs["ca_certs"] = verify
+ else:
+ pool_kwargs["ca_cert_dir"] = verify
+ pool_kwargs["cert_reqs"] = cert_reqs
+ if client_cert is not None:
+ if isinstance(client_cert, tuple) and len(client_cert) == 2:
+ pool_kwargs["cert_file"] = client_cert[0]
+ pool_kwargs["key_file"] = client_cert[1]
+ else:
+ # According to our docs, we allow users to specify just the client
+ # cert path
+ pool_kwargs["cert_file"] = client_cert
+ host_params = {
+ "scheme": scheme,
+ "host": parsed_request_url.hostname,
+ "port": port,
+ }
+ return host_params, pool_kwargs
+
+
class BaseAdapter:
"""The Base Transport Adapter"""
@@ -247,28 +314,26 @@ class HTTPAdapter(BaseAdapter):
:param cert: The SSL certificate to verify.
"""
if url.lower().startswith("https") and verify:
-
- cert_loc = None
-
- # Allow self-specified cert location.
- if verify is not True:
- cert_loc = verify
-
- if not cert_loc:
- cert_loc = extract_zipped_paths(DEFAULT_CA_BUNDLE_PATH)
-
- if not cert_loc or not os.path.exists(cert_loc):
- raise OSError(
- f"Could not find a suitable TLS CA certificate bundle, "
- f"invalid path: {cert_loc}"
- )
-
conn.cert_reqs = "CERT_REQUIRED"
- if not os.path.isdir(cert_loc):
- conn.ca_certs = cert_loc
- else:
- conn.ca_cert_dir = cert_loc
+ # Only load the CA certificates if 'verify' is a string indicating the CA bundle to use.
+ # Otherwise, if verify is a boolean, we don't load anything since
+ # the connection will be using a context with the default certificates already loaded,
+ # and this avoids a call to the slow load_verify_locations()
+ if verify is not True:
+ # `verify` must be a str with a path then
+ cert_loc = verify
+
+ if not os.path.exists(cert_loc):
+ raise OSError(
+ f"Could not find a suitable TLS CA certificate bundle, "
+ f"invalid path: {cert_loc}"
+ )
+
+ if not os.path.isdir(cert_loc):
+ conn.ca_certs = cert_loc
+ else:
+ conn.ca_cert_dir = cert_loc
else:
conn.cert_reqs = "CERT_NONE"
conn.ca_certs = None
@@ -328,8 +393,110 @@ class HTTPAdapter(BaseAdapter):
return response
+ def build_connection_pool_key_attributes(self, request, verify, cert=None):
+ """Build the PoolKey attributes used by urllib3 to return a connection.
+
+ This looks at the PreparedRequest, the user-specified verify value,
+ and the value of the cert parameter to determine what PoolKey values
+ to use to select a connection from a given urllib3 Connection Pool.
+
+ The SSL related pool key arguments are not consistently set. As of
+ this writing, use the following to determine what keys may be in that
+ dictionary:
+
+ * If ``verify`` is ``True``, ``"ssl_context"`` will be set and will be the
+ default Requests SSL Context
+ * If ``verify`` is ``False``, ``"ssl_context"`` will not be set but
+ ``"cert_reqs"`` will be set
+ * If ``verify`` is a string, (i.e., it is a user-specified trust bundle)
+ ``"ca_certs"`` will be set if the string is not a directory recognized
+ by :py:func:`os.path.isdir`, otherwise ``"ca_certs_dir"`` will be
+ set.
+ * If ``"cert"`` is specified, ``"cert_file"`` will always be set. If
+ ``"cert"`` is a tuple with a second item, ``"key_file"`` will also
+ be present
+
+ To override these settings, one may subclass this class, call this
+ method and use the above logic to change parameters as desired. For
+ example, if one wishes to use a custom :py:class:`ssl.SSLContext` one
+ must both set ``"ssl_context"`` and based on what else they require,
+ alter the other keys to ensure the desired behaviour.
+
+ :param request:
+ The PreparedReqest being sent over the connection.
+ :type request:
+ :class:`~requests.models.PreparedRequest`
+ :param verify:
+ Either a boolean, in which case it controls whether
+ we verify the server's TLS certificate, or a string, in which case it
+ must be a path to a CA bundle to use.
+ :param cert:
+ (optional) Any user-provided SSL certificate for client
+ authentication (a.k.a., mTLS). This may be a string (i.e., just
+ the path to a file which holds both certificate and key) or a
+ tuple of length 2 with the certificate file path and key file
+ path.
+ :returns:
+ A tuple of two dictionaries. The first is the "host parameters"
+ portion of the Pool Key including scheme, hostname, and port. The
+ second is a dictionary of SSLContext related parameters.
+ """
+ return _urllib3_request_context(request, verify, cert, self.poolmanager)
+
+ def get_connection_with_tls_context(self, request, verify, proxies=None, cert=None):
+ """Returns a urllib3 connection for the given request and TLS settings.
+ This should not be called from user code, and is only exposed for use
+ when subclassing the :class:`HTTPAdapter `.
+
+ :param request:
+ The :class:`PreparedRequest ` object to be sent
+ over the connection.
+ :param verify:
+ Either a boolean, in which case it controls whether we verify the
+ server's TLS certificate, or a string, in which case it must be a
+ path to a CA bundle to use.
+ :param proxies:
+ (optional) The proxies dictionary to apply to the request.
+ :param cert:
+ (optional) Any user-provided SSL certificate to be used for client
+ authentication (a.k.a., mTLS).
+ :rtype:
+ urllib3.ConnectionPool
+ """
+ proxy = select_proxy(request.url, proxies)
+ try:
+ host_params, pool_kwargs = self.build_connection_pool_key_attributes(
+ request,
+ verify,
+ cert,
+ )
+ except ValueError as e:
+ raise InvalidURL(e, request=request)
+ if proxy:
+ proxy = prepend_scheme_if_needed(proxy, "http")
+ proxy_url = parse_url(proxy)
+ if not proxy_url.host:
+ raise InvalidProxyURL(
+ "Please check proxy URL. It is malformed "
+ "and could be missing the host."
+ )
+ proxy_manager = self.proxy_manager_for(proxy)
+ conn = proxy_manager.connection_from_host(
+ **host_params, pool_kwargs=pool_kwargs
+ )
+ else:
+ # Only scheme should be lower case
+ conn = self.poolmanager.connection_from_host(
+ **host_params, pool_kwargs=pool_kwargs
+ )
+
+ return conn
+
def get_connection(self, url, proxies=None):
- """Returns a urllib3 connection for the given URL. This should not be
+ """DEPRECATED: Users should move to `get_connection_with_tls_context`
+ for all subclasses of HTTPAdapter using Requests>=2.32.2.
+
+ Returns a urllib3 connection for the given URL. This should not be
called from user code, and is only exposed for use when subclassing the
:class:`HTTPAdapter `.
@@ -337,6 +504,15 @@ class HTTPAdapter(BaseAdapter):
:param proxies: (optional) A Requests-style dictionary of proxies used on this request.
:rtype: urllib3.ConnectionPool
"""
+ warnings.warn(
+ (
+ "`get_connection` has been deprecated in favor of "
+ "`get_connection_with_tls_context`. Custom HTTPAdapter subclasses "
+ "will need to migrate for Requests>=2.32.2. Please see "
+ "https://github.com/psf/requests/pull/6710 for more details."
+ ),
+ DeprecationWarning,
+ )
proxy = select_proxy(url, proxies)
if proxy:
@@ -391,6 +567,9 @@ class HTTPAdapter(BaseAdapter):
using_socks_proxy = proxy_scheme.startswith("socks")
url = request.path_url
+ if url.startswith("//"): # Don't confuse urllib3
+ url = f"/{url.lstrip('/')}"
+
if is_proxied_http_request and not using_socks_proxy:
url = urldefragauth(request.url)
@@ -451,7 +630,9 @@ class HTTPAdapter(BaseAdapter):
"""
try:
- conn = self.get_connection(request.url, proxies)
+ conn = self.get_connection_with_tls_context(
+ request, verify, proxies=proxies, cert=cert
+ )
except LocationValueError as e:
raise InvalidURL(e, request=request)
diff --git a/lib/requests/api.py b/lib/requests/api.py
index cd0b3eea..59607445 100644
--- a/lib/requests/api.py
+++ b/lib/requests/api.py
@@ -25,7 +25,7 @@ def request(method, url, **kwargs):
:param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
:param files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart encoding upload.
``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')``
- or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content-type'`` is a string
+ or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content_type'`` is a string
defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers
to add for the file.
:param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth.
diff --git a/lib/requests/auth.py b/lib/requests/auth.py
index 9733686d..4a7ce6dc 100644
--- a/lib/requests/auth.py
+++ b/lib/requests/auth.py
@@ -258,7 +258,6 @@ class HTTPDigestAuth(AuthBase):
s_auth = r.headers.get("www-authenticate", "")
if "digest" in s_auth.lower() and self._thread_local.num_401_calls < 2:
-
self._thread_local.num_401_calls += 1
pat = re.compile(r"digest ", flags=re.IGNORECASE)
self._thread_local.chal = parse_dict_header(pat.sub("", s_auth, count=1))
diff --git a/lib/requests/compat.py b/lib/requests/compat.py
index 6776163c..095de1b6 100644
--- a/lib/requests/compat.py
+++ b/lib/requests/compat.py
@@ -7,13 +7,28 @@ between Python 2 and Python 3. It remains for backwards
compatibility until the next major version.
"""
-try:
- import chardet
-except ImportError:
- import charset_normalizer as chardet
-
+import importlib
import sys
+# -------------------
+# Character Detection
+# -------------------
+
+
+def _resolve_char_detection():
+ """Find supported character detection libraries."""
+ chardet = None
+ for lib in ("chardet", "charset_normalizer"):
+ if chardet is None:
+ try:
+ chardet = importlib.import_module(lib)
+ except ImportError:
+ pass
+ return chardet
+
+
+chardet = _resolve_char_detection()
+
# -------
# Pythons
# -------
diff --git a/lib/requests/cookies.py b/lib/requests/cookies.py
index bf54ab23..f69d0cda 100644
--- a/lib/requests/cookies.py
+++ b/lib/requests/cookies.py
@@ -2,7 +2,7 @@
requests.cookies
~~~~~~~~~~~~~~~~
-Compatibility code to be able to use `cookielib.CookieJar` with requests.
+Compatibility code to be able to use `http.cookiejar.CookieJar` with requests.
requests.utils imports from here, so be careful with imports.
"""
@@ -23,7 +23,7 @@ except ImportError:
class MockRequest:
"""Wraps a `requests.Request` to mimic a `urllib2.Request`.
- The code in `cookielib.CookieJar` expects this interface in order to correctly
+ The code in `http.cookiejar.CookieJar` expects this interface in order to correctly
manage cookie policies, i.e., determine whether a cookie can be set, given the
domains of the request and the cookie.
@@ -76,7 +76,7 @@ class MockRequest:
return self._r.headers.get(name, self._new_headers.get(name, default))
def add_header(self, key, val):
- """cookielib has no legitimate use for this method; add it back if you find one."""
+ """cookiejar has no legitimate use for this method; add it back if you find one."""
raise NotImplementedError(
"Cookie headers should be added with add_unredirected_header()"
)
@@ -104,11 +104,11 @@ class MockResponse:
"""Wraps a `httplib.HTTPMessage` to mimic a `urllib.addinfourl`.
...what? Basically, expose the parsed HTTP headers from the server response
- the way `cookielib` expects to see them.
+ the way `http.cookiejar` expects to see them.
"""
def __init__(self, headers):
- """Make a MockResponse for `cookielib` to read.
+ """Make a MockResponse for `cookiejar` to read.
:param headers: a httplib.HTTPMessage or analogous carrying the headers
"""
@@ -124,7 +124,7 @@ class MockResponse:
def extract_cookies_to_jar(jar, request, response):
"""Extract the cookies from the response into a CookieJar.
- :param jar: cookielib.CookieJar (not necessarily a RequestsCookieJar)
+ :param jar: http.cookiejar.CookieJar (not necessarily a RequestsCookieJar)
:param request: our own requests.Request object
:param response: urllib3.HTTPResponse object
"""
@@ -174,7 +174,7 @@ class CookieConflictError(RuntimeError):
class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
- """Compatibility class; is a cookielib.CookieJar, but exposes a dict
+ """Compatibility class; is a http.cookiejar.CookieJar, but exposes a dict
interface.
This is the CookieJar we create by default for requests and sessions that
@@ -341,7 +341,7 @@ class RequestsCookieJar(cookielib.CookieJar, MutableMapping):
self.set(name, value)
def __delitem__(self, name):
- """Deletes a cookie given a name. Wraps ``cookielib.CookieJar``'s
+ """Deletes a cookie given a name. Wraps ``http.cookiejar.CookieJar``'s
``remove_cookie_by_name()``.
"""
remove_cookie_by_name(self, name)
diff --git a/lib/requests/exceptions.py b/lib/requests/exceptions.py
index e1cedf88..83986b48 100644
--- a/lib/requests/exceptions.py
+++ b/lib/requests/exceptions.py
@@ -41,6 +41,16 @@ class JSONDecodeError(InvalidJSONError, CompatJSONDecodeError):
CompatJSONDecodeError.__init__(self, *args)
InvalidJSONError.__init__(self, *self.args, **kwargs)
+ def __reduce__(self):
+ """
+ The __reduce__ method called when pickling the object must
+ be the one from the JSONDecodeError (be it json/simplejson)
+ as it expects all the arguments for instantiation, not just
+ one like the IOError, and the MRO would by default call the
+ __reduce__ method from the IOError due to the inheritance order.
+ """
+ return CompatJSONDecodeError.__reduce__(self)
+
class HTTPError(RequestException):
"""An HTTP error occurred."""
diff --git a/lib/requests/models.py b/lib/requests/models.py
index 617a4134..8f56ca7d 100644
--- a/lib/requests/models.py
+++ b/lib/requests/models.py
@@ -170,7 +170,7 @@ class RequestEncodingMixin:
)
)
- for (k, v) in files:
+ for k, v in files:
# support for explicit filename
ft = None
fh = None
@@ -268,7 +268,6 @@ class Request(RequestHooksMixin):
hooks=None,
json=None,
):
-
# Default empty dicts for dict params.
data = [] if data is None else data
files = [] if files is None else files
@@ -277,7 +276,7 @@ class Request(RequestHooksMixin):
hooks = {} if hooks is None else hooks
self.hooks = default_hooks()
- for (k, v) in list(hooks.items()):
+ for k, v in list(hooks.items()):
self.register_hook(event=k, hook=v)
self.method = method
@@ -790,7 +789,12 @@ class Response:
@property
def apparent_encoding(self):
"""The apparent encoding, provided by the charset_normalizer or chardet libraries."""
- return chardet.detect(self.content)["encoding"]
+ if chardet is not None:
+ return chardet.detect(self.content)["encoding"]
+ else:
+ # If no character detection library is available, we'll fall back
+ # to a standard Python utf-8 str.
+ return "utf-8"
def iter_content(self, chunk_size=1, decode_unicode=False):
"""Iterates over the response data. When stream=True is set on the
@@ -865,7 +869,6 @@ class Response:
for chunk in self.iter_content(
chunk_size=chunk_size, decode_unicode=decode_unicode
):
-
if pending is not None:
chunk = pending + chunk
diff --git a/lib/requests/packages.py b/lib/requests/packages.py
index 77c45c9e..5ab3d8e2 100644
--- a/lib/requests/packages.py
+++ b/lib/requests/packages.py
@@ -1,13 +1,6 @@
import sys
-try:
- import chardet
-except ImportError:
- import warnings
-
- import charset_normalizer as chardet
-
- warnings.filterwarnings("ignore", "Trying to detect", module="charset_normalizer")
+from .compat import chardet
# This code exists for backwards compatibility reasons.
# I don't like it either. Just look the other way. :)
@@ -20,9 +13,11 @@ for package in ("urllib3", "idna"):
if mod == package or mod.startswith(f"{package}."):
sys.modules[f"requests.packages.{mod}"] = sys.modules[mod]
-target = chardet.__name__
-for mod in list(sys.modules):
- if mod == target or mod.startswith(f"{target}."):
- target = target.replace(target, "chardet")
- sys.modules[f"requests.packages.{target}"] = sys.modules[mod]
-# Kinda cool, though, right?
+if chardet is not None:
+ target = chardet.__name__
+ for mod in list(sys.modules):
+ if mod == target or mod.startswith(f"{target}."):
+ imported_mod = sys.modules[mod]
+ sys.modules[f"requests.packages.{mod}"] = imported_mod
+ mod = mod.replace(target, "chardet")
+ sys.modules[f"requests.packages.{mod}"] = imported_mod
diff --git a/lib/requests/sessions.py b/lib/requests/sessions.py
index dbcf2a7b..b387bc36 100644
--- a/lib/requests/sessions.py
+++ b/lib/requests/sessions.py
@@ -262,7 +262,6 @@ class SessionRedirectMixin:
if yield_requests:
yield req
else:
-
resp = self.send(
req,
stream=stream,
@@ -326,7 +325,7 @@ class SessionRedirectMixin:
# urllib3 handles proxy authorization for us in the standard adapter.
# Avoid appending this to TLS tunneled requests where it may be leaked.
- if not scheme.startswith('https') and username and password:
+ if not scheme.startswith("https") and username and password:
headers["Proxy-Authorization"] = _basic_auth_str(username, password)
return new_proxies
@@ -389,7 +388,6 @@ class Session(SessionRedirectMixin):
]
def __init__(self):
-
#: A case-insensitive dictionary of headers to be sent on each
#: :class:`Request ` sent from this
#: :class:`Session `.
@@ -545,6 +543,8 @@ class Session(SessionRedirectMixin):
:type allow_redirects: bool
:param proxies: (optional) Dictionary mapping protocol or protocol and
hostname to the URL of the proxy.
+ :param hooks: (optional) Dictionary mapping hook name to one event or
+ list of events, event must be callable.
:param stream: (optional) whether to immediately download the response
content. Defaults to ``False``.
:param verify: (optional) Either a boolean, in which case it controls whether we verify
@@ -711,7 +711,6 @@ class Session(SessionRedirectMixin):
# Persist cookies
if r.history:
-
# If the hooks create history then we want those cookies too
for resp in r.history:
extract_cookies_to_jar(self.cookies, resp.request, resp.raw)
@@ -759,7 +758,7 @@ class Session(SessionRedirectMixin):
# Set environment's proxies.
no_proxy = proxies.get("no_proxy") if proxies is not None else None
env_proxies = get_environ_proxies(url, no_proxy=no_proxy)
- for (k, v) in env_proxies.items():
+ for k, v in env_proxies.items():
proxies.setdefault(k, v)
# Look for requests environment configuration
@@ -785,8 +784,7 @@ class Session(SessionRedirectMixin):
:rtype: requests.adapters.BaseAdapter
"""
- for (prefix, adapter) in self.adapters.items():
-
+ for prefix, adapter in self.adapters.items():
if url.lower().startswith(prefix.lower()):
return adapter
diff --git a/lib/requests/status_codes.py b/lib/requests/status_codes.py
index 4bd072be..c7945a2f 100644
--- a/lib/requests/status_codes.py
+++ b/lib/requests/status_codes.py
@@ -24,7 +24,7 @@ _codes = {
# Informational.
100: ("continue",),
101: ("switching_protocols",),
- 102: ("processing",),
+ 102: ("processing", "early-hints"),
103: ("checkpoint",),
122: ("uri_too_long", "request_uri_too_long"),
200: ("ok", "okay", "all_ok", "all_okay", "all_good", "\\o/", "✓"),
@@ -65,8 +65,8 @@ _codes = {
410: ("gone",),
411: ("length_required",),
412: ("precondition_failed", "precondition"),
- 413: ("request_entity_too_large",),
- 414: ("request_uri_too_large",),
+ 413: ("request_entity_too_large", "content_too_large"),
+ 414: ("request_uri_too_large", "uri_too_long"),
415: ("unsupported_media_type", "unsupported_media", "media_type"),
416: (
"requested_range_not_satisfiable",
@@ -76,10 +76,10 @@ _codes = {
417: ("expectation_failed",),
418: ("im_a_teapot", "teapot", "i_am_a_teapot"),
421: ("misdirected_request",),
- 422: ("unprocessable_entity", "unprocessable"),
+ 422: ("unprocessable_entity", "unprocessable", "unprocessable_content"),
423: ("locked",),
424: ("failed_dependency", "dependency"),
- 425: ("unordered_collection", "unordered"),
+ 425: ("unordered_collection", "unordered", "too_early"),
426: ("upgrade_required", "upgrade"),
428: ("precondition_required", "precondition"),
429: ("too_many_requests", "too_many"),
diff --git a/lib/requests/utils.py b/lib/requests/utils.py
index a367417f..ae6c42f6 100644
--- a/lib/requests/utils.py
+++ b/lib/requests/utils.py
@@ -97,6 +97,8 @@ if sys.platform == "win32":
# '' string by the localhost entry and the corresponding
# canonical entry.
proxyOverride = proxyOverride.split(";")
+ # filter out empty strings to avoid re.match return true in the following code.
+ proxyOverride = filter(None, proxyOverride)
# now check if we match one of the registry values.
for test in proxyOverride:
if test == "":
@@ -134,6 +136,9 @@ def super_len(o):
total_length = None
current_position = 0
+ if isinstance(o, str):
+ o = o.encode("utf-8")
+
if hasattr(o, "__len__"):
total_length = len(o)
@@ -466,11 +471,7 @@ def dict_from_cookiejar(cj):
:rtype: dict
"""
- cookie_dict = {}
-
- for cookie in cj:
- cookie_dict[cookie.name] = cookie.value
-
+ cookie_dict = {cookie.name: cookie.value for cookie in cj}
return cookie_dict
@@ -767,6 +768,7 @@ def should_bypass_proxies(url, no_proxy):
:rtype: bool
"""
+
# Prioritize lowercase environment variables over uppercase
# to keep a consistent behaviour with other http projects (curl, wget).
def get_proxy(key):
@@ -862,7 +864,7 @@ def select_proxy(url, proxies):
def resolve_proxies(request, proxies, trust_env=True):
"""This method takes proxy information from a request and configuration
input to resolve a mapping of target proxies. This will consider settings
- such a NO_PROXY to strip proxy configurations.
+ such as NO_PROXY to strip proxy configurations.
:param request: Request or PreparedRequest
:param proxies: A dictionary of schemes or schemes and hosts to proxy URLs
@@ -1054,7 +1056,7 @@ def _validate_header_part(header, header_part, header_validator_index):
if not validator.match(header_part):
header_kind = "name" if header_validator_index == 0 else "value"
raise InvalidHeader(
- f"Invalid leading whitespace, reserved character(s), or return"
+ f"Invalid leading whitespace, reserved character(s), or return "
f"character(s) in header {header_kind}: {header_part!r}"
)
diff --git a/requirements.txt b/requirements.txt
index 6edced4e..5e3825d1 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -34,7 +34,7 @@ pyparsing==3.1.2
python-dateutil==2.9.0.post0
python-twitter==3.5
pytz==2024.1
-requests==2.31.0
+requests==2.32.3
requests-oauthlib==2.0.0
rumps==0.4.0; platform_system == "Darwin"
simplejson==3.19.2
From afa25d45f67cb26191f248e94f2de313775ae829 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 19 Jun 2024 00:01:47 -0700
Subject: [PATCH 4/8] Bump certifi from 2024.2.2 to 2024.6.2 (#2342)
* Bump certifi from 2024.2.2 to 2024.6.2
Bumps [certifi](https://github.com/certifi/python-certifi) from 2024.2.2 to 2024.6.2.
- [Commits](https://github.com/certifi/python-certifi/compare/2024.02.02...2024.06.02)
---
updated-dependencies:
- dependency-name: certifi
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
* Update certifi==2024.6.2
---------
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>
[skip ci]
---
lib/certifi/__init__.py | 2 +-
lib/certifi/cacert.pem | 24 ++++++++++++++++++++++++
requirements.txt | 2 +-
3 files changed, 26 insertions(+), 2 deletions(-)
diff --git a/lib/certifi/__init__.py b/lib/certifi/__init__.py
index 1c91f3ec..62b27b63 100644
--- a/lib/certifi/__init__.py
+++ b/lib/certifi/__init__.py
@@ -1,4 +1,4 @@
from .core import contents, where
__all__ = ["contents", "where"]
-__version__ = "2024.02.02"
+__version__ = "2024.06.02"
diff --git a/lib/certifi/cacert.pem b/lib/certifi/cacert.pem
index fac3c319..6e1f1a67 100644
--- a/lib/certifi/cacert.pem
+++ b/lib/certifi/cacert.pem
@@ -4812,3 +4812,27 @@ X273CXE2whJdV/LItM3z7gLfEdxquVeEHVlNjM7IDiPCtyaaEBRx/pOyiriA8A4Q
ntOoUAw3gi/q4Iqd4Sw5/7W0cwDk90imc6y/st53BIe0o82bNSQ3+pCTE4FCxpgm
dTdmQRCsu/WU48IxK63nI1bMNSWSs1A=
-----END CERTIFICATE-----
+
+# Issuer: CN=FIRMAPROFESIONAL CA ROOT-A WEB O=Firmaprofesional SA
+# Subject: CN=FIRMAPROFESIONAL CA ROOT-A WEB O=Firmaprofesional SA
+# Label: "FIRMAPROFESIONAL CA ROOT-A WEB"
+# Serial: 65916896770016886708751106294915943533
+# MD5 Fingerprint: 82:b2:ad:45:00:82:b0:66:63:f8:5f:c3:67:4e:ce:a3
+# SHA1 Fingerprint: a8:31:11:74:a6:14:15:0d:ca:77:dd:0e:e4:0c:5d:58:fc:a0:72:a5
+# SHA256 Fingerprint: be:f2:56:da:f2:6e:9c:69:bd:ec:16:02:35:97:98:f3:ca:f7:18:21:a0:3e:01:82:57:c5:3c:65:61:7f:3d:4a
+-----BEGIN CERTIFICATE-----
+MIICejCCAgCgAwIBAgIQMZch7a+JQn81QYehZ1ZMbTAKBggqhkjOPQQDAzBuMQsw
+CQYDVQQGEwJFUzEcMBoGA1UECgwTRmlybWFwcm9mZXNpb25hbCBTQTEYMBYGA1UE
+YQwPVkFURVMtQTYyNjM0MDY4MScwJQYDVQQDDB5GSVJNQVBST0ZFU0lPTkFMIENB
+IFJPT1QtQSBXRUIwHhcNMjIwNDA2MDkwMTM2WhcNNDcwMzMxMDkwMTM2WjBuMQsw
+CQYDVQQGEwJFUzEcMBoGA1UECgwTRmlybWFwcm9mZXNpb25hbCBTQTEYMBYGA1UE
+YQwPVkFURVMtQTYyNjM0MDY4MScwJQYDVQQDDB5GSVJNQVBST0ZFU0lPTkFMIENB
+IFJPT1QtQSBXRUIwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARHU+osEaR3xyrq89Zf
+e9MEkVz6iMYiuYMQYneEMy3pA4jU4DP37XcsSmDq5G+tbbT4TIqk5B/K6k84Si6C
+cyvHZpsKjECcfIr28jlgst7L7Ljkb+qbXbdTkBgyVcUgt5SjYzBhMA8GA1UdEwEB
+/wQFMAMBAf8wHwYDVR0jBBgwFoAUk+FDY1w8ndYn81LsF7Kpryz3dvgwHQYDVR0O
+BBYEFJPhQ2NcPJ3WJ/NS7Beyqa8s93b4MA4GA1UdDwEB/wQEAwIBBjAKBggqhkjO
+PQQDAwNoADBlAjAdfKR7w4l1M+E7qUW/Runpod3JIha3RxEL2Jq68cgLcFBTApFw
+hVmpHqTm6iMxoAACMQD94vizrxa5HnPEluPBMBnYfubDl94cT7iJLzPrSA8Z94dG
+XSaQpYXFuXqUPoeovQA=
+-----END CERTIFICATE-----
diff --git a/requirements.txt b/requirements.txt
index 5e3825d1..a0c32ad9 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,7 +3,7 @@ arrow==1.3.0
backports.zoneinfo==0.2.1;python_version<"3.9"
beautifulsoup4==4.12.3
bleach==6.1.0
-certifi==2024.2.2
+certifi==2024.6.2
cheroot==10.0.1
cherrypy==18.9.0
cloudinary==1.40.0
From 5e977c044ae65ad32304a6e1f28480967bcd7ac2 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 19 Jun 2024 00:02:06 -0700
Subject: [PATCH 5/8] Bump zipp from 3.18.2 to 3.19.2 (#2343)
* Bump zipp from 3.18.2 to 3.19.2
Bumps [zipp](https://github.com/jaraco/zipp) from 3.18.2 to 3.19.2.
- [Release notes](https://github.com/jaraco/zipp/releases)
- [Changelog](https://github.com/jaraco/zipp/blob/main/NEWS.rst)
- [Commits](https://github.com/jaraco/zipp/compare/v3.18.2...v3.19.2)
---
updated-dependencies:
- dependency-name: zipp
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
* Update zipp==3.19.2
---------
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>
[skip ci]
---
lib/zipp/__init__.py | 76 +++++++++++++++++++++++++++++++++++++++++---
requirements.txt | 2 +-
2 files changed, 73 insertions(+), 5 deletions(-)
diff --git a/lib/zipp/__init__.py b/lib/zipp/__init__.py
index df3293aa..d65297b8 100644
--- a/lib/zipp/__init__.py
+++ b/lib/zipp/__init__.py
@@ -5,6 +5,7 @@ import itertools
import contextlib
import pathlib
import re
+import stat
import sys
from .compat.py310 import text_encoding
@@ -85,7 +86,69 @@ class InitializedState:
super().__init__(*args, **kwargs)
-class CompleteDirs(InitializedState, zipfile.ZipFile):
+class SanitizedNames:
+ """
+ ZipFile mix-in to ensure names are sanitized.
+ """
+
+ def namelist(self):
+ return list(map(self._sanitize, super().namelist()))
+
+ @staticmethod
+ def _sanitize(name):
+ r"""
+ Ensure a relative path with posix separators and no dot names.
+
+ Modeled after
+ https://github.com/python/cpython/blob/bcc1be39cb1d04ad9fc0bd1b9193d3972835a57c/Lib/zipfile/__init__.py#L1799-L1813
+ but provides consistent cross-platform behavior.
+
+ >>> san = SanitizedNames._sanitize
+ >>> san('/foo/bar')
+ 'foo/bar'
+ >>> san('//foo.txt')
+ 'foo.txt'
+ >>> san('foo/.././bar.txt')
+ 'foo/bar.txt'
+ >>> san('foo../.bar.txt')
+ 'foo../.bar.txt'
+ >>> san('\\foo\\bar.txt')
+ 'foo/bar.txt'
+ >>> san('D:\\foo.txt')
+ 'D/foo.txt'
+ >>> san('\\\\server\\share\\file.txt')
+ 'server/share/file.txt'
+ >>> san('\\\\?\\GLOBALROOT\\Volume3')
+ '?/GLOBALROOT/Volume3'
+ >>> san('\\\\.\\PhysicalDrive1\\root')
+ 'PhysicalDrive1/root'
+
+ Retain any trailing slash.
+ >>> san('abc/')
+ 'abc/'
+
+ Raises a ValueError if the result is empty.
+ >>> san('../..')
+ Traceback (most recent call last):
+ ...
+ ValueError: Empty filename
+ """
+
+ def allowed(part):
+ return part and part not in {'..', '.'}
+
+ # Remove the drive letter.
+ # Don't use ntpath.splitdrive, because that also strips UNC paths
+ bare = re.sub('^([A-Z]):', r'\1', name, flags=re.IGNORECASE)
+ clean = bare.replace('\\', '/')
+ parts = clean.split('/')
+ joined = '/'.join(filter(allowed, parts))
+ if not joined:
+ raise ValueError("Empty filename")
+ return joined + '/' * name.endswith('/')
+
+
+class CompleteDirs(InitializedState, SanitizedNames, zipfile.ZipFile):
"""
A ZipFile subclass that ensures that implied directories
are always included in the namelist.
@@ -188,7 +251,10 @@ def _extract_text_encoding(encoding=None, *args, **kwargs):
class Path:
"""
- A pathlib-compatible interface for zip files.
+ A :class:`importlib.resources.abc.Traversable` interface for zip files.
+
+ Implements many of the features users enjoy from
+ :class:`pathlib.Path`.
Consider a zip file with this structure::
@@ -391,9 +457,11 @@ class Path:
def is_symlink(self):
"""
- Return whether this path is a symlink. Always false (python/cpython#82102).
+ Return whether this path is a symlink.
"""
- return False
+ info = self.root.getinfo(self.at)
+ mode = info.external_attr >> 16
+ return stat.S_ISLNK(mode)
def glob(self, pattern):
if not pattern:
diff --git a/requirements.txt b/requirements.txt
index a0c32ad9..7d8c9cb7 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -47,7 +47,7 @@ urllib3<2
webencodings==0.5.1
websocket-client==1.8.0
xmltodict==0.13.0
-zipp==3.18.2
+zipp==3.19.2
# configobj==5.1.0
# sgmllib3k==1.0.0
From a528f052b99200ffe7ba30fda14e35ac6dcf7400 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 19 Jun 2024 00:02:35 -0700
Subject: [PATCH 6/8] Bump cherrypy from 18.9.0 to 18.10.0 (#2353)
* Bump cherrypy from 18.9.0 to 18.10.0
Bumps [cherrypy](https://github.com/cherrypy/cherrypy) from 18.9.0 to 18.10.0.
- [Changelog](https://github.com/cherrypy/cherrypy/blob/main/CHANGES.rst)
- [Commits](https://github.com/cherrypy/cherrypy/compare/v18.9.0...v18.10.0)
---
updated-dependencies:
- dependency-name: cherrypy
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot]
* Update cherrypy==18.10.0
---------
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: JonnyWong16 <9099342+JonnyWong16@users.noreply.github.com>
[skip ci]
---
lib/backports/__init__.py | 2 +-
lib/backports/tarfile/__init__.py | 125 +++--
lib/cherrypy/__init__.py | 40 +-
lib/cherrypy/_cpchecker.py | 5 +-
lib/cherrypy/_cpconfig.py | 7 +-
lib/cherrypy/_cpdispatch.py | 54 +-
lib/cherrypy/_cperror.py | 44 +-
lib/cherrypy/_cplogging.py | 19 +-
lib/cherrypy/_cpmodpy.py | 2 +-
lib/cherrypy/_cpnative_server.py | 8 +-
lib/cherrypy/_cpreqbody.py | 45 +-
lib/cherrypy/_cprequest.py | 201 ++++---
lib/cherrypy/_cpserver.py | 36 +-
lib/cherrypy/_cptools.py | 47 +-
lib/cherrypy/_cptree.py | 50 +-
lib/cherrypy/_cpwsgi.py | 60 ++-
lib/cherrypy/_cpwsgi_server.py | 15 +-
lib/cherrypy/_helper.py | 18 +-
lib/cherrypy/_json.py | 3 +-
lib/cherrypy/lib/__init__.py | 4 +-
lib/cherrypy/lib/auth_basic.py | 1 -
lib/cherrypy/lib/auth_digest.py | 28 +-
lib/cherrypy/lib/caching.py | 19 +-
lib/cherrypy/lib/covercp.py | 6 +-
lib/cherrypy/lib/cpstats.py | 7 +-
lib/cherrypy/lib/cptools.py | 61 ++-
lib/cherrypy/lib/encoding.py | 5 +-
lib/cherrypy/lib/gctools.py | 2 -
lib/cherrypy/lib/headers.py | 39 ++
lib/cherrypy/lib/httputil.py | 33 +-
lib/cherrypy/lib/locking.py | 20 +-
lib/cherrypy/lib/profiler.py | 1 -
lib/cherrypy/lib/reprconf.py | 25 +-
lib/cherrypy/lib/sessions.py | 38 +-
lib/cherrypy/lib/static.py | 16 +-
lib/cherrypy/process/plugins.py | 54 +-
lib/cherrypy/process/servers.py | 8 +-
lib/cherrypy/process/win32.py | 12 +-
lib/cherrypy/process/wspbus.py | 57 +-
lib/cherrypy/scaffold/__init__.py | 1 -
lib/cherrypy/test/__init__.py | 4 +-
lib/cherrypy/test/_test_decorators.py | 2 +-
lib/cherrypy/test/benchmark.py | 135 +++--
lib/cherrypy/test/checkerdemo.py | 6 +-
lib/cherrypy/test/helper.py | 28 +-
lib/cherrypy/test/logtest.py | 34 +-
lib/cherrypy/test/modwsgi.py | 1 -
lib/cherrypy/test/sessiondemo.py | 8 +-
lib/cherrypy/test/test_core.py | 2 -
.../test/test_dynamicobjectmapping.py | 24 +-
lib/cherrypy/test/test_http.py | 12 +-
lib/cherrypy/test/test_logging.py | 4 +-
lib/cherrypy/test/test_plugins.py | 3 +-
lib/cherrypy/test/test_request_obj.py | 5 +-
lib/cherrypy/test/test_session.py | 15 +-
lib/cherrypy/test/test_states.py | 13 +-
lib/cherrypy/test/test_tools.py | 4 +-
lib/cherrypy/test/test_tutorials.py | 8 +-
lib/cherrypy/test/test_wsgi_unix_socket.py | 4 +-
lib/cherrypy/tutorial/tut01_helloworld.py | 3 +-
lib/more_itertools/__init__.py | 2 +-
lib/more_itertools/more.py | 376 +++++++++----
lib/more_itertools/more.pyi | 14 +
lib/more_itertools/recipes.py | 76 ++-
lib/more_itertools/recipes.pyi | 10 +-
lib/tempora/schedule.py | 53 +-
lib/tempora/utc.py | 12 +-
lib/typeguard/_checkers.py | 117 +++-
lib/typeguard/_pytest_plugin.py | 9 +-
lib/typeguard/_suppression.py | 2 +-
lib/typeguard/_utils.py | 14 +-
lib/typing_extensions.py | 501 ++++++++++++++----
requirements.txt | 2 +-
73 files changed, 1713 insertions(+), 1008 deletions(-)
create mode 100644 lib/cherrypy/lib/headers.py
diff --git a/lib/backports/__init__.py b/lib/backports/__init__.py
index 8db66d3d..0d1f7edf 100644
--- a/lib/backports/__init__.py
+++ b/lib/backports/__init__.py
@@ -1 +1 @@
-__path__ = __import__("pkgutil").extend_path(__path__, __name__)
+__path__ = __import__('pkgutil').extend_path(__path__, __name__) # type: ignore
diff --git a/lib/backports/tarfile/__init__.py b/lib/backports/tarfile/__init__.py
index 6dd498dc..8c16881c 100644
--- a/lib/backports/tarfile/__init__.py
+++ b/lib/backports/tarfile/__init__.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python3
#-------------------------------------------------------------------
# tarfile.py
#-------------------------------------------------------------------
@@ -46,7 +45,6 @@ import time
import struct
import copy
import re
-import warnings
from .compat.py38 import removesuffix
@@ -639,6 +637,10 @@ class _FileInFile(object):
def flush(self):
pass
+ @property
+ def mode(self):
+ return 'rb'
+
def readable(self):
return True
@@ -875,7 +877,7 @@ class TarInfo(object):
pax_headers = ('A dictionary containing key-value pairs of an '
'associated pax extended header.'),
sparse = 'Sparse member information.',
- tarfile = None,
+ _tarfile = None,
_sparse_structs = None,
_link_target = None,
)
@@ -904,6 +906,24 @@ class TarInfo(object):
self.sparse = None # sparse member information
self.pax_headers = {} # pax header information
+ @property
+ def tarfile(self):
+ import warnings
+ warnings.warn(
+ 'The undocumented "tarfile" attribute of TarInfo objects '
+ + 'is deprecated and will be removed in Python 3.16',
+ DeprecationWarning, stacklevel=2)
+ return self._tarfile
+
+ @tarfile.setter
+ def tarfile(self, tarfile):
+ import warnings
+ warnings.warn(
+ 'The undocumented "tarfile" attribute of TarInfo objects '
+ + 'is deprecated and will be removed in Python 3.16',
+ DeprecationWarning, stacklevel=2)
+ self._tarfile = tarfile
+
@property
def path(self):
'In pax headers, "name" is called "path".'
@@ -1198,7 +1218,7 @@ class TarInfo(object):
for keyword, value in pax_headers.items():
keyword = keyword.encode("utf-8")
if binary:
- # Try to restore the original byte representation of `value'.
+ # Try to restore the original byte representation of 'value'.
# Needless to say, that the encoding must match the string.
value = value.encode(encoding, "surrogateescape")
else:
@@ -1643,14 +1663,14 @@ class TarFile(object):
def __init__(self, name=None, mode="r", fileobj=None, format=None,
tarinfo=None, dereference=None, ignore_zeros=None, encoding=None,
errors="surrogateescape", pax_headers=None, debug=None,
- errorlevel=None, copybufsize=None):
- """Open an (uncompressed) tar archive `name'. `mode' is either 'r' to
+ errorlevel=None, copybufsize=None, stream=False):
+ """Open an (uncompressed) tar archive 'name'. 'mode' is either 'r' to
read from an existing archive, 'a' to append data to an existing
- file or 'w' to create a new file overwriting an existing one. `mode'
+ file or 'w' to create a new file overwriting an existing one. 'mode'
defaults to 'r'.
- If `fileobj' is given, it is used for reading or writing data. If it
- can be determined, `mode' is overridden by `fileobj's mode.
- `fileobj' is not closed, when TarFile is closed.
+ If 'fileobj' is given, it is used for reading or writing data. If it
+ can be determined, 'mode' is overridden by 'fileobj's mode.
+ 'fileobj' is not closed, when TarFile is closed.
"""
modes = {"r": "rb", "a": "r+b", "w": "wb", "x": "xb"}
if mode not in modes:
@@ -1675,6 +1695,8 @@ class TarFile(object):
self.name = os.path.abspath(name) if name else None
self.fileobj = fileobj
+ self.stream = stream
+
# Init attributes.
if format is not None:
self.format = format
@@ -1977,7 +1999,7 @@ class TarFile(object):
self.fileobj.close()
def getmember(self, name):
- """Return a TarInfo object for member ``name``. If ``name`` can not be
+ """Return a TarInfo object for member 'name'. If 'name' can not be
found in the archive, KeyError is raised. If a member occurs more
than once in the archive, its last occurrence is assumed to be the
most up-to-date version.
@@ -2005,9 +2027,9 @@ class TarFile(object):
def gettarinfo(self, name=None, arcname=None, fileobj=None):
"""Create a TarInfo object from the result of os.stat or equivalent
- on an existing file. The file is either named by ``name``, or
- specified as a file object ``fileobj`` with a file descriptor. If
- given, ``arcname`` specifies an alternative name for the file in the
+ on an existing file. The file is either named by 'name', or
+ specified as a file object 'fileobj' with a file descriptor. If
+ given, 'arcname' specifies an alternative name for the file in the
archive, otherwise, the name is taken from the 'name' attribute of
'fileobj', or the 'name' argument. The name should be a text
string.
@@ -2031,7 +2053,7 @@ class TarFile(object):
# Now, fill the TarInfo object with
# information specific for the file.
tarinfo = self.tarinfo()
- tarinfo.tarfile = self # Not needed
+ tarinfo._tarfile = self # To be removed in 3.16.
# Use os.stat or os.lstat, depending on if symlinks shall be resolved.
if fileobj is None:
@@ -2103,11 +2125,15 @@ class TarFile(object):
return tarinfo
def list(self, verbose=True, *, members=None):
- """Print a table of contents to sys.stdout. If ``verbose`` is False, only
- the names of the members are printed. If it is True, an `ls -l'-like
- output is produced. ``members`` is optional and must be a subset of the
+ """Print a table of contents to sys.stdout. If 'verbose' is False, only
+ the names of the members are printed. If it is True, an 'ls -l'-like
+ output is produced. 'members' is optional and must be a subset of the
list returned by getmembers().
"""
+ # Convert tarinfo type to stat type.
+ type2mode = {REGTYPE: stat.S_IFREG, SYMTYPE: stat.S_IFLNK,
+ FIFOTYPE: stat.S_IFIFO, CHRTYPE: stat.S_IFCHR,
+ DIRTYPE: stat.S_IFDIR, BLKTYPE: stat.S_IFBLK}
self._check()
if members is None:
@@ -2117,7 +2143,8 @@ class TarFile(object):
if tarinfo.mode is None:
_safe_print("??????????")
else:
- _safe_print(stat.filemode(tarinfo.mode))
+ modetype = type2mode.get(tarinfo.type, 0)
+ _safe_print(stat.filemode(modetype | tarinfo.mode))
_safe_print("%s/%s" % (tarinfo.uname or tarinfo.uid,
tarinfo.gname or tarinfo.gid))
if tarinfo.ischr() or tarinfo.isblk():
@@ -2141,11 +2168,11 @@ class TarFile(object):
print()
def add(self, name, arcname=None, recursive=True, *, filter=None):
- """Add the file ``name`` to the archive. ``name`` may be any type of file
- (directory, fifo, symbolic link, etc.). If given, ``arcname``
+ """Add the file 'name' to the archive. 'name' may be any type of file
+ (directory, fifo, symbolic link, etc.). If given, 'arcname'
specifies an alternative name for the file in the archive.
Directories are added recursively by default. This can be avoided by
- setting ``recursive`` to False. ``filter`` is a function
+ setting 'recursive' to False. 'filter' is a function
that expects a TarInfo object argument and returns the changed
TarInfo object, if it returns None the TarInfo object will be
excluded from the archive.
@@ -2192,13 +2219,16 @@ class TarFile(object):
self.addfile(tarinfo)
def addfile(self, tarinfo, fileobj=None):
- """Add the TarInfo object ``tarinfo`` to the archive. If ``fileobj`` is
- given, it should be a binary file, and tarinfo.size bytes are read
- from it and added to the archive. You can create TarInfo objects
- directly, or by using gettarinfo().
+ """Add the TarInfo object 'tarinfo' to the archive. If 'tarinfo' represents
+ a non zero-size regular file, the 'fileobj' argument should be a binary file,
+ and tarinfo.size bytes are read from it and added to the archive.
+ You can create TarInfo objects directly, or by using gettarinfo().
"""
self._check("awx")
+ if fileobj is None and tarinfo.isreg() and tarinfo.size != 0:
+ raise ValueError("fileobj not provided for non zero-size regular file")
+
tarinfo = copy.copy(tarinfo)
buf = tarinfo.tobuf(self.format, self.encoding, self.errors)
@@ -2220,11 +2250,12 @@ class TarFile(object):
if filter is None:
filter = self.extraction_filter
if filter is None:
+ import warnings
warnings.warn(
'Python 3.14 will, by default, filter extracted tar '
+ 'archives and reject files or modify their metadata. '
+ 'Use the filter argument to control this behavior.',
- DeprecationWarning)
+ DeprecationWarning, stacklevel=3)
return fully_trusted_filter
if isinstance(filter, str):
raise TypeError(
@@ -2243,12 +2274,12 @@ class TarFile(object):
filter=None):
"""Extract all members from the archive to the current working
directory and set owner, modification time and permissions on
- directories afterwards. `path' specifies a different directory
- to extract to. `members' is optional and must be a subset of the
- list returned by getmembers(). If `numeric_owner` is True, only
+ directories afterwards. 'path' specifies a different directory
+ to extract to. 'members' is optional and must be a subset of the
+ list returned by getmembers(). If 'numeric_owner' is True, only
the numbers for user/group names are used and not the names.
- The `filter` function will be called on each member just
+ The 'filter' function will be called on each member just
before extraction.
It can return a changed TarInfo or None to skip the member.
String names of common filters are accepted.
@@ -2288,13 +2319,13 @@ class TarFile(object):
filter=None):
"""Extract a member from the archive to the current working directory,
using its full name. Its file information is extracted as accurately
- as possible. `member' may be a filename or a TarInfo object. You can
- specify a different directory using `path'. File attributes (owner,
- mtime, mode) are set unless `set_attrs' is False. If `numeric_owner`
+ as possible. 'member' may be a filename or a TarInfo object. You can
+ specify a different directory using 'path'. File attributes (owner,
+ mtime, mode) are set unless 'set_attrs' is False. If 'numeric_owner'
is True, only the numbers for user/group names are used and not
the names.
- The `filter` function will be called before extraction.
+ The 'filter' function will be called before extraction.
It can return a changed TarInfo or None to skip the member.
String names of common filters are accepted.
"""
@@ -2359,10 +2390,10 @@ class TarFile(object):
self._dbg(1, "tarfile: %s %s" % (type(e).__name__, e))
def extractfile(self, member):
- """Extract a member from the archive as a file object. ``member`` may be
- a filename or a TarInfo object. If ``member`` is a regular file or
+ """Extract a member from the archive as a file object. 'member' may be
+ a filename or a TarInfo object. If 'member' is a regular file or
a link, an io.BufferedReader object is returned. For all other
- existing members, None is returned. If ``member`` does not appear
+ existing members, None is returned. If 'member' does not appear
in the archive, KeyError is raised.
"""
self._check("r")
@@ -2406,7 +2437,7 @@ class TarFile(object):
if upperdirs and not os.path.exists(upperdirs):
# Create directories that are not part of the archive with
# default permissions.
- os.makedirs(upperdirs)
+ os.makedirs(upperdirs, exist_ok=True)
if tarinfo.islnk() or tarinfo.issym():
self._dbg(1, "%s -> %s" % (tarinfo.name, tarinfo.linkname))
@@ -2559,7 +2590,8 @@ class TarFile(object):
os.lchown(targetpath, u, g)
else:
os.chown(targetpath, u, g)
- except OSError as e:
+ except (OSError, OverflowError) as e:
+ # OverflowError can be raised if an ID doesn't fit in 'id_t'
raise ExtractError("could not change owner") from e
def chmod(self, tarinfo, targetpath):
@@ -2642,7 +2674,9 @@ class TarFile(object):
break
if tarinfo is not None:
- self.members.append(tarinfo)
+ # if streaming the file we do not want to cache the tarinfo
+ if not self.stream:
+ self.members.append(tarinfo)
else:
self._loaded = True
@@ -2693,11 +2727,12 @@ class TarFile(object):
def _load(self):
"""Read through the entire archive file and look for readable
- members.
+ members. This should not run if the file is set to stream.
"""
- while self.next() is not None:
- pass
- self._loaded = True
+ if not self.stream:
+ while self.next() is not None:
+ pass
+ self._loaded = True
def _check(self, mode=None):
"""Check if TarFile is still open, and if the operation's mode
diff --git a/lib/cherrypy/__init__.py b/lib/cherrypy/__init__.py
index 8e27c812..49e955f8 100644
--- a/lib/cherrypy/__init__.py
+++ b/lib/cherrypy/__init__.py
@@ -57,9 +57,11 @@ These API's are described in the `CherryPy specification
"""
try:
- import pkg_resources
+ import importlib.metadata as importlib_metadata
except ImportError:
- pass
+ # fall back for python <= 3.7
+ # This try/except can be removed with py <= 3.7 support
+ import importlib_metadata
from threading import local as _local
@@ -109,7 +111,7 @@ tree = _cptree.Tree()
try:
- __version__ = pkg_resources.require('cherrypy')[0].version
+ __version__ = importlib_metadata.version('cherrypy')
except Exception:
__version__ = 'unknown'
@@ -181,24 +183,28 @@ def quickstart(root=None, script_name='', config=None):
class _Serving(_local):
"""An interface for registering request and response objects.
- Rather than have a separate "thread local" object for the request and
- the response, this class works as a single threadlocal container for
- both objects (and any others which developers wish to define). In this
- way, we can easily dump those objects when we stop/start a new HTTP
- conversation, yet still refer to them as module-level globals in a
- thread-safe way.
+ Rather than have a separate "thread local" object for the request
+ and the response, this class works as a single threadlocal container
+ for both objects (and any others which developers wish to define).
+ In this way, we can easily dump those objects when we stop/start a
+ new HTTP conversation, yet still refer to them as module-level
+ globals in a thread-safe way.
"""
request = _cprequest.Request(_httputil.Host('127.0.0.1', 80),
_httputil.Host('127.0.0.1', 1111))
+ """The request object for the current thread.
+
+ In the main thread, and any threads which are not receiving HTTP
+ requests, this is None.
"""
- The request object for the current thread. In the main thread,
- and any threads which are not receiving HTTP requests, this is None."""
response = _cprequest.Response()
+ """The response object for the current thread.
+
+ In the main thread, and any threads which are not receiving HTTP
+ requests, this is None.
"""
- The response object for the current thread. In the main thread,
- and any threads which are not receiving HTTP requests, this is None."""
def load(self, request, response):
self.request = request
@@ -316,8 +322,8 @@ class _GlobalLogManager(_cplogging.LogManager):
def __call__(self, *args, **kwargs):
"""Log the given message to the app.log or global log.
- Log the given message to the app.log or global
- log as appropriate.
+ Log the given message to the app.log or global log as
+ appropriate.
"""
# Do NOT use try/except here. See
# https://github.com/cherrypy/cherrypy/issues/945
@@ -330,8 +336,8 @@ class _GlobalLogManager(_cplogging.LogManager):
def access(self):
"""Log an access message to the app.log or global log.
- Log the given message to the app.log or global
- log as appropriate.
+ Log the given message to the app.log or global log as
+ appropriate.
"""
try:
return request.app.log.access()
diff --git a/lib/cherrypy/_cpchecker.py b/lib/cherrypy/_cpchecker.py
index f26f319c..096b19c3 100644
--- a/lib/cherrypy/_cpchecker.py
+++ b/lib/cherrypy/_cpchecker.py
@@ -313,7 +313,10 @@ class Checker(object):
# -------------------- Specific config warnings -------------------- #
def check_localhost(self):
- """Warn if any socket_host is 'localhost'. See #711."""
+ """Warn if any socket_host is 'localhost'.
+
+ See #711.
+ """
for k, v in cherrypy.config.items():
if k == 'server.socket_host' and v == 'localhost':
warnings.warn("The use of 'localhost' as a socket host can "
diff --git a/lib/cherrypy/_cpconfig.py b/lib/cherrypy/_cpconfig.py
index 8e3fd612..c22937d3 100644
--- a/lib/cherrypy/_cpconfig.py
+++ b/lib/cherrypy/_cpconfig.py
@@ -1,5 +1,4 @@
-"""
-Configuration system for CherryPy.
+"""Configuration system for CherryPy.
Configuration in CherryPy is implemented via dictionaries. Keys are strings
which name the mapped value, which may be of any type.
@@ -132,8 +131,8 @@ def _if_filename_register_autoreload(ob):
def merge(base, other):
"""Merge one app config (from a dict, file, or filename) into another.
- If the given config is a filename, it will be appended to
- the list of files to monitor for "autoreload" changes.
+ If the given config is a filename, it will be appended to the list
+ of files to monitor for "autoreload" changes.
"""
_if_filename_register_autoreload(other)
diff --git a/lib/cherrypy/_cpdispatch.py b/lib/cherrypy/_cpdispatch.py
index 5c506e99..5a3a8ad6 100644
--- a/lib/cherrypy/_cpdispatch.py
+++ b/lib/cherrypy/_cpdispatch.py
@@ -1,9 +1,10 @@
"""CherryPy dispatchers.
A 'dispatcher' is the object which looks up the 'page handler' callable
-and collects config for the current request based on the path_info, other
-request attributes, and the application architecture. The core calls the
-dispatcher as early as possible, passing it a 'path_info' argument.
+and collects config for the current request based on the path_info,
+other request attributes, and the application architecture. The core
+calls the dispatcher as early as possible, passing it a 'path_info'
+argument.
The default dispatcher discovers the page handler by matching path_info
to a hierarchical arrangement of objects, starting at request.app.root.
@@ -21,7 +22,6 @@ import cherrypy
class PageHandler(object):
-
"""Callable which sets response.body."""
def __init__(self, callable, *args, **kwargs):
@@ -64,8 +64,7 @@ class PageHandler(object):
def test_callable_spec(callable, callable_args, callable_kwargs):
- """
- Inspect callable and test to see if the given args are suitable for it.
+ """Inspect callable and test to see if the given args are suitable for it.
When an error occurs during the handler's invoking stage there are 2
erroneous cases:
@@ -252,16 +251,16 @@ else:
class Dispatcher(object):
-
"""CherryPy Dispatcher which walks a tree of objects to find a handler.
- The tree is rooted at cherrypy.request.app.root, and each hierarchical
- component in the path_info argument is matched to a corresponding nested
- attribute of the root object. Matching handlers must have an 'exposed'
- attribute which evaluates to True. The special method name "index"
- matches a URI which ends in a slash ("/"). The special method name
- "default" may match a portion of the path_info (but only when no longer
- substring of the path_info matches some other object).
+ The tree is rooted at cherrypy.request.app.root, and each
+ hierarchical component in the path_info argument is matched to a
+ corresponding nested attribute of the root object. Matching handlers
+ must have an 'exposed' attribute which evaluates to True. The
+ special method name "index" matches a URI which ends in a slash
+ ("/"). The special method name "default" may match a portion of the
+ path_info (but only when no longer substring of the path_info
+ matches some other object).
This is the default, built-in dispatcher for CherryPy.
"""
@@ -306,9 +305,9 @@ class Dispatcher(object):
The second object returned will be a list of names which are
'virtual path' components: parts of the URL which are dynamic,
- and were not used when looking up the handler.
- These virtual path components are passed to the handler as
- positional arguments.
+ and were not used when looking up the handler. These virtual
+ path components are passed to the handler as positional
+ arguments.
"""
request = cherrypy.serving.request
app = request.app
@@ -448,13 +447,11 @@ class Dispatcher(object):
class MethodDispatcher(Dispatcher):
-
"""Additional dispatch based on cherrypy.request.method.upper().
- Methods named GET, POST, etc will be called on an exposed class.
- The method names must be all caps; the appropriate Allow header
- will be output showing all capitalized method names as allowable
- HTTP verbs.
+ Methods named GET, POST, etc will be called on an exposed class. The
+ method names must be all caps; the appropriate Allow header will be
+ output showing all capitalized method names as allowable HTTP verbs.
Note that the containing class must be exposed, not the methods.
"""
@@ -492,16 +489,14 @@ class MethodDispatcher(Dispatcher):
class RoutesDispatcher(object):
-
"""A Routes based dispatcher for CherryPy."""
def __init__(self, full_result=False, **mapper_options):
- """
- Routes dispatcher
+ """Routes dispatcher.
- Set full_result to True if you wish the controller
- and the action to be passed on to the page handler
- parameters. By default they won't be.
+ Set full_result to True if you wish the controller and the
+ action to be passed on to the page handler parameters. By
+ default they won't be.
"""
import routes
self.full_result = full_result
@@ -617,8 +612,7 @@ def XMLRPCDispatcher(next_dispatcher=Dispatcher()):
def VirtualHost(next_dispatcher=Dispatcher(), use_x_forwarded_host=True,
**domains):
- """
- Select a different handler based on the Host header.
+ """Select a different handler based on the Host header.
This can be useful when running multiple sites within one CP server.
It allows several domains to point to different parts of a single
diff --git a/lib/cherrypy/_cperror.py b/lib/cherrypy/_cperror.py
index f6ff2913..203fabf5 100644
--- a/lib/cherrypy/_cperror.py
+++ b/lib/cherrypy/_cperror.py
@@ -136,19 +136,17 @@ from cherrypy.lib import httputil as _httputil
class CherryPyException(Exception):
-
"""A base class for CherryPy exceptions."""
pass
class InternalRedirect(CherryPyException):
-
"""Exception raised to switch to the handler for a different URL.
- This exception will redirect processing to another path within the site
- (without informing the client). Provide the new path as an argument when
- raising the exception. Provide any params in the querystring for the new
- URL.
+ This exception will redirect processing to another path within the
+ site (without informing the client). Provide the new path as an
+ argument when raising the exception. Provide any params in the
+ querystring for the new URL.
"""
def __init__(self, path, query_string=''):
@@ -173,7 +171,6 @@ class InternalRedirect(CherryPyException):
class HTTPRedirect(CherryPyException):
-
"""Exception raised when the request should be redirected.
This exception will force a HTTP redirect to the URL or URL's you give it.
@@ -202,7 +199,7 @@ class HTTPRedirect(CherryPyException):
"""The list of URL's to emit."""
encoding = 'utf-8'
- """The encoding when passed urls are not native strings"""
+ """The encoding when passed urls are not native strings."""
def __init__(self, urls, status=None, encoding=None):
self.urls = abs_urls = [
@@ -230,8 +227,7 @@ class HTTPRedirect(CherryPyException):
@classproperty
def default_status(cls):
- """
- The default redirect status for the request.
+ """The default redirect status for the request.
RFC 2616 indicates a 301 response code fits our goal; however,
browser support for 301 is quite messy. Use 302/303 instead. See
@@ -249,8 +245,9 @@ class HTTPRedirect(CherryPyException):
"""Modify cherrypy.response status, headers, and body to represent
self.
- CherryPy uses this internally, but you can also use it to create an
- HTTPRedirect object and set its output without *raising* the exception.
+ CherryPy uses this internally, but you can also use it to create
+ an HTTPRedirect object and set its output without *raising* the
+ exception.
"""
response = cherrypy.serving.response
response.status = status = self.status
@@ -339,7 +336,6 @@ def clean_headers(status):
class HTTPError(CherryPyException):
-
"""Exception used to return an HTTP error code (4xx-5xx) to the client.
This exception can be used to automatically send a response using a
@@ -358,7 +354,9 @@ class HTTPError(CherryPyException):
"""
status = None
- """The HTTP status code. May be of type int or str (with a Reason-Phrase).
+ """The HTTP status code.
+
+ May be of type int or str (with a Reason-Phrase).
"""
code = None
@@ -386,8 +384,9 @@ class HTTPError(CherryPyException):
"""Modify cherrypy.response status, headers, and body to represent
self.
- CherryPy uses this internally, but you can also use it to create an
- HTTPError object and set its output without *raising* the exception.
+ CherryPy uses this internally, but you can also use it to create
+ an HTTPError object and set its output without *raising* the
+ exception.
"""
response = cherrypy.serving.response
@@ -426,11 +425,10 @@ class HTTPError(CherryPyException):
class NotFound(HTTPError):
-
"""Exception raised when a URL could not be mapped to any handler (404).
- This is equivalent to raising
- :class:`HTTPError("404 Not Found") `.
+ This is equivalent to raising :class:`HTTPError("404 Not Found")
+ `.
"""
def __init__(self, path=None):
@@ -477,8 +475,8 @@ _HTTPErrorTemplate = ''' cherrypy.server -> HTTPServer.
+ cheroot has been designed to not reference CherryPy in any way, so
+ that it can be used in other frameworks and applications. Therefore,
+ we wrap it here, so we can apply some attributes from config ->
+ cherrypy.server -> HTTPServer.
"""
def __init__(self, server_adapter=cherrypy.server):
diff --git a/lib/cherrypy/_cpreqbody.py b/lib/cherrypy/_cpreqbody.py
index 4d3cefe7..7e0d98be 100644
--- a/lib/cherrypy/_cpreqbody.py
+++ b/lib/cherrypy/_cpreqbody.py
@@ -248,7 +248,10 @@ def process_multipart_form_data(entity):
def _old_process_multipart(entity):
- """The behavior of 3.2 and lower. Deprecated and will be changed in 3.3."""
+ """The behavior of 3.2 and lower.
+
+ Deprecated and will be changed in 3.3.
+ """
process_multipart(entity)
params = entity.params
@@ -277,7 +280,6 @@ def _old_process_multipart(entity):
# -------------------------------- Entities --------------------------------- #
class Entity(object):
-
"""An HTTP request body, or MIME multipart body.
This class collects information about the HTTP request entity. When a
@@ -346,13 +348,15 @@ class Entity(object):
content_type = None
"""The value of the Content-Type request header.
- If the Entity is part of a multipart payload, this will be the Content-Type
- given in the MIME headers for this part.
+ If the Entity is part of a multipart payload, this will be the
+ Content-Type given in the MIME headers for this part.
"""
default_content_type = 'application/x-www-form-urlencoded'
"""This defines a default ``Content-Type`` to use if no Content-Type header
- is given. The empty string is used for RequestBody, which results in the
+ is given.
+
+ The empty string is used for RequestBody, which results in the
request body not being read or parsed at all. This is by design; a missing
``Content-Type`` header in the HTTP request entity is an error at best,
and a security hole at worst. For multipart parts, however, the MIME spec
@@ -402,8 +406,8 @@ class Entity(object):
part_class = None
"""The class used for multipart parts.
- You can replace this with custom subclasses to alter the processing of
- multipart parts.
+ You can replace this with custom subclasses to alter the processing
+ of multipart parts.
"""
def __init__(self, fp, headers, params=None, parts=None):
@@ -509,7 +513,8 @@ class Entity(object):
"""Return a file-like object into which the request body will be read.
By default, this will return a TemporaryFile. Override as needed.
- See also :attr:`cherrypy._cpreqbody.Part.maxrambytes`."""
+ See also :attr:`cherrypy._cpreqbody.Part.maxrambytes`.
+ """
return tempfile.TemporaryFile()
def fullvalue(self):
@@ -525,7 +530,7 @@ class Entity(object):
return value
def decode_entity(self, value):
- """Return a given byte encoded value as a string"""
+ """Return a given byte encoded value as a string."""
for charset in self.attempt_charsets:
try:
value = value.decode(charset)
@@ -569,7 +574,6 @@ class Entity(object):
class Part(Entity):
-
"""A MIME part entity, part of a multipart entity."""
# "The default character set, which must be assumed in the absence of a
@@ -653,8 +657,8 @@ class Part(Entity):
def read_lines_to_boundary(self, fp_out=None):
"""Read bytes from self.fp and return or write them to a file.
- If the 'fp_out' argument is None (the default), all bytes read are
- returned in a single byte string.
+ If the 'fp_out' argument is None (the default), all bytes read
+ are returned in a single byte string.
If the 'fp_out' argument is not None, it must be a file-like
object that supports the 'write' method; all bytes read will be
@@ -755,15 +759,15 @@ class SizedReader:
def read(self, size=None, fp_out=None):
"""Read bytes from the request body and return or write them to a file.
- A number of bytes less than or equal to the 'size' argument are read
- off the socket. The actual number of bytes read are tracked in
- self.bytes_read. The number may be smaller than 'size' when 1) the
- client sends fewer bytes, 2) the 'Content-Length' request header
- specifies fewer bytes than requested, or 3) the number of bytes read
- exceeds self.maxbytes (in which case, 413 is raised).
+ A number of bytes less than or equal to the 'size' argument are
+ read off the socket. The actual number of bytes read are tracked
+ in self.bytes_read. The number may be smaller than 'size' when
+ 1) the client sends fewer bytes, 2) the 'Content-Length' request
+ header specifies fewer bytes than requested, or 3) the number of
+ bytes read exceeds self.maxbytes (in which case, 413 is raised).
- If the 'fp_out' argument is None (the default), all bytes read are
- returned in a single byte string.
+ If the 'fp_out' argument is None (the default), all bytes read
+ are returned in a single byte string.
If the 'fp_out' argument is not None, it must be a file-like
object that supports the 'write' method; all bytes read will be
@@ -918,7 +922,6 @@ class SizedReader:
class RequestBody(Entity):
-
"""The entity of the HTTP request."""
bufsize = 8 * 1024
diff --git a/lib/cherrypy/_cprequest.py b/lib/cherrypy/_cprequest.py
index a661112c..a4ad298b 100644
--- a/lib/cherrypy/_cprequest.py
+++ b/lib/cherrypy/_cprequest.py
@@ -16,7 +16,6 @@ from cherrypy.lib import httputil, reprconf, encoding
class Hook(object):
-
"""A callback and its metadata: failsafe, priority, and kwargs."""
callback = None
@@ -30,10 +29,12 @@ class Hook(object):
from the same call point raise exceptions."""
priority = 50
+ """Defines the order of execution for a list of Hooks.
+
+ Priority numbers should be limited to the closed interval [0, 100],
+ but values outside this range are acceptable, as are fractional
+ values.
"""
- Defines the order of execution for a list of Hooks. Priority numbers
- should be limited to the closed interval [0, 100], but values outside
- this range are acceptable, as are fractional values."""
kwargs = {}
"""
@@ -74,7 +75,6 @@ class Hook(object):
class HookMap(dict):
-
"""A map of call points to lists of callbacks (Hook objects)."""
def __new__(cls, points=None):
@@ -190,23 +190,23 @@ hookpoints = ['on_start_resource', 'before_request_body',
class Request(object):
-
"""An HTTP request.
- This object represents the metadata of an HTTP request message;
- that is, it contains attributes which describe the environment
- in which the request URL, headers, and body were sent (if you
- want tools to interpret the headers and body, those are elsewhere,
- mostly in Tools). This 'metadata' consists of socket data,
- transport characteristics, and the Request-Line. This object
- also contains data regarding the configuration in effect for
- the given URL, and the execution plan for generating a response.
+ This object represents the metadata of an HTTP request message; that
+ is, it contains attributes which describe the environment in which
+ the request URL, headers, and body were sent (if you want tools to
+ interpret the headers and body, those are elsewhere, mostly in
+ Tools). This 'metadata' consists of socket data, transport
+ characteristics, and the Request-Line. This object also contains
+ data regarding the configuration in effect for the given URL, and
+ the execution plan for generating a response.
"""
prev = None
+ """The previous Request object (if any).
+
+ This should be None unless we are processing an InternalRedirect.
"""
- The previous Request object (if any). This should be None
- unless we are processing an InternalRedirect."""
# Conversation/connection attributes
local = httputil.Host('127.0.0.1', 80)
@@ -216,9 +216,10 @@ class Request(object):
'An httputil.Host(ip, port, hostname) object for the client socket.'
scheme = 'http'
+ """The protocol used between client and server.
+
+ In most cases, this will be either 'http' or 'https'.
"""
- The protocol used between client and server. In most cases,
- this will be either 'http' or 'https'."""
server_protocol = 'HTTP/1.1'
"""
@@ -227,25 +228,30 @@ class Request(object):
base = ''
"""The (scheme://host) portion of the requested URL.
+
In some cases (e.g. when proxying via mod_rewrite), this may contain
path segments which cherrypy.url uses when constructing url's, but
- which otherwise are ignored by CherryPy. Regardless, this value
- MUST NOT end in a slash."""
+ which otherwise are ignored by CherryPy. Regardless, this value MUST
+ NOT end in a slash.
+ """
# Request-Line attributes
request_line = ''
+ """The complete Request-Line received from the client.
+
+ This is a single string consisting of the request method, URI, and
+ protocol version (joined by spaces). Any final CRLF is removed.
"""
- The complete Request-Line received from the client. This is a
- single string consisting of the request method, URI, and protocol
- version (joined by spaces). Any final CRLF is removed."""
method = 'GET'
+ """Indicates the HTTP method to be performed on the resource identified by
+ the Request-URI.
+
+ Common methods include GET, HEAD, POST, PUT, and DELETE. CherryPy
+ allows any extension method; however, various HTTP servers and
+ gateways may restrict the set of allowable methods. CherryPy
+ applications SHOULD restrict the set (on a per-URI basis).
"""
- Indicates the HTTP method to be performed on the resource identified
- by the Request-URI. Common methods include GET, HEAD, POST, PUT, and
- DELETE. CherryPy allows any extension method; however, various HTTP
- servers and gateways may restrict the set of allowable methods.
- CherryPy applications SHOULD restrict the set (on a per-URI basis)."""
query_string = ''
"""
@@ -277,22 +283,26 @@ class Request(object):
A dict which combines query string (GET) and request entity (POST)
variables. This is populated in two stages: GET params are added
before the 'on_start_resource' hook, and POST params are added
- between the 'before_request_body' and 'before_handler' hooks."""
+ between the 'before_request_body' and 'before_handler' hooks.
+ """
# Message attributes
header_list = []
+ """A list of the HTTP request headers as (name, value) tuples.
+
+ In general, you should use request.headers (a dict) instead.
"""
- A list of the HTTP request headers as (name, value) tuples.
- In general, you should use request.headers (a dict) instead."""
headers = httputil.HeaderMap()
- """
- A dict-like object containing the request headers. Keys are header
+ """A dict-like object containing the request headers.
+
+ Keys are header
names (in Title-Case format); however, you may get and set them in
a case-insensitive manner. That is, headers['Content-Type'] and
headers['content-type'] refer to the same value. Values are header
values (decoded according to :rfc:`2047` if necessary). See also:
- httputil.HeaderMap, httputil.HeaderElement."""
+ httputil.HeaderMap, httputil.HeaderElement.
+ """
cookie = SimpleCookie()
"""See help(Cookie)."""
@@ -336,7 +346,8 @@ class Request(object):
or multipart, this will be None. Otherwise, this will be an instance
of :class:`RequestBody` (which you
can .read()); this value is set between the 'before_request_body' and
- 'before_handler' hooks (assuming that process_request_body is True)."""
+ 'before_handler' hooks (assuming that process_request_body is True).
+ """
# Dispatch attributes
dispatch = cherrypy.dispatch.Dispatcher()
@@ -347,23 +358,24 @@ class Request(object):
calls the dispatcher as early as possible, passing it a 'path_info'
argument.
- The default dispatcher discovers the page handler by matching path_info
- to a hierarchical arrangement of objects, starting at request.app.root.
- See help(cherrypy.dispatch) for more information."""
+ The default dispatcher discovers the page handler by matching
+ path_info to a hierarchical arrangement of objects, starting at
+ request.app.root. See help(cherrypy.dispatch) for more information.
+ """
script_name = ''
- """
- The 'mount point' of the application which is handling this request.
+ """The 'mount point' of the application which is handling this request.
This attribute MUST NOT end in a slash. If the script_name refers to
the root of the URI, it MUST be an empty string (not "/").
"""
path_info = '/'
+ """The 'relative path' portion of the Request-URI.
+
+ This is relative to the script_name ('mount point') of the
+ application which is handling this request.
"""
- The 'relative path' portion of the Request-URI. This is relative
- to the script_name ('mount point') of the application which is
- handling this request."""
login = None
"""
@@ -391,14 +403,16 @@ class Request(object):
of the form: {Toolbox.namespace: {Tool.name: config dict}}."""
config = None
+ """A flat dict of all configuration entries which apply to the current
+ request.
+
+ These entries are collected from global config, application config
+ (based on request.path_info), and from handler config (exactly how
+ is governed by the request.dispatch object in effect for this
+ request; by default, handler config can be attached anywhere in the
+ tree between request.app.root and the final handler, and inherits
+ downward).
"""
- A flat dict of all configuration entries which apply to the
- current request. These entries are collected from global config,
- application config (based on request.path_info), and from handler
- config (exactly how is governed by the request.dispatch object in
- effect for this request; by default, handler config can be attached
- anywhere in the tree between request.app.root and the final handler,
- and inherits downward)."""
is_index = None
"""
@@ -409,13 +423,14 @@ class Request(object):
the trailing slash. See cherrypy.tools.trailing_slash."""
hooks = HookMap(hookpoints)
- """
- A HookMap (dict-like object) of the form: {hookpoint: [hook, ...]}.
+ """A HookMap (dict-like object) of the form: {hookpoint: [hook, ...]}.
+
Each key is a str naming the hook point, and each value is a list
of hooks which will be called at that hook point during this request.
The list of hooks is generally populated as early as possible (mostly
from Tools specified in config), but may be extended at any time.
- See also: _cprequest.Hook, _cprequest.HookMap, and cherrypy.tools."""
+ See also: _cprequest.Hook, _cprequest.HookMap, and cherrypy.tools.
+ """
error_response = cherrypy.HTTPError(500).set_response
"""
@@ -428,12 +443,11 @@ class Request(object):
error response to the user-agent."""
error_page = {}
- """
- A dict of {error code: response filename or callable} pairs.
+ """A dict of {error code: response filename or callable} pairs.
The error code must be an int representing a given HTTP error code,
- or the string 'default', which will be used if no matching entry
- is found for a given numeric code.
+ or the string 'default', which will be used if no matching entry is
+ found for a given numeric code.
If a filename is provided, the file should contain a Python string-
formatting template, and can expect by default to receive format
@@ -447,8 +461,8 @@ class Request(object):
iterable of strings which will be set to response.body. It may also
override headers or perform any other processing.
- If no entry is given for an error code, and no 'default' entry exists,
- a default template will be used.
+ If no entry is given for an error code, and no 'default' entry
+ exists, a default template will be used.
"""
show_tracebacks = True
@@ -473,9 +487,10 @@ class Request(object):
"""True once the close method has been called, False otherwise."""
stage = None
+ """A string containing the stage reached in the request-handling process.
+
+ This is useful when debugging a live server with hung requests.
"""
- A string containing the stage reached in the request-handling process.
- This is useful when debugging a live server with hung requests."""
unique_id = None
"""A lazy object generating and memorizing UUID4 on ``str()`` render."""
@@ -492,9 +507,10 @@ class Request(object):
server_protocol='HTTP/1.1'):
"""Populate a new Request object.
- local_host should be an httputil.Host object with the server info.
- remote_host should be an httputil.Host object with the client info.
- scheme should be a string, either "http" or "https".
+ local_host should be an httputil.Host object with the server
+ info. remote_host should be an httputil.Host object with the
+ client info. scheme should be a string, either "http" or
+ "https".
"""
self.local = local_host
self.remote = remote_host
@@ -514,7 +530,10 @@ class Request(object):
self.unique_id = LazyUUID4()
def close(self):
- """Run cleanup code. (Core)"""
+ """Run cleanup code.
+
+ (Core)
+ """
if not self.closed:
self.closed = True
self.stage = 'on_end_request'
@@ -551,7 +570,6 @@ class Request(object):
Consumer code (HTTP servers) should then access these response
attributes to build the outbound stream.
-
"""
response = cherrypy.serving.response
self.stage = 'run'
@@ -631,7 +649,10 @@ class Request(object):
return response
def respond(self, path_info):
- """Generate a response for the resource at self.path_info. (Core)"""
+ """Generate a response for the resource at self.path_info.
+
+ (Core)
+ """
try:
try:
try:
@@ -702,7 +723,10 @@ class Request(object):
response.finalize()
def process_query_string(self):
- """Parse the query string into Python structures. (Core)"""
+ """Parse the query string into Python structures.
+
+ (Core)
+ """
try:
p = httputil.parse_query_string(
self.query_string, encoding=self.query_string_encoding)
@@ -715,7 +739,10 @@ class Request(object):
self.params.update(p)
def process_headers(self):
- """Parse HTTP header data into Python structures. (Core)"""
+ """Parse HTTP header data into Python structures.
+
+ (Core)
+ """
# Process the headers into self.headers
headers = self.headers
for name, value in self.header_list:
@@ -751,7 +778,10 @@ class Request(object):
self.base = '%s://%s' % (self.scheme, host)
def get_resource(self, path):
- """Call a dispatcher (which sets self.handler and .config). (Core)"""
+ """Call a dispatcher (which sets self.handler and .config).
+
+ (Core)
+ """
# First, see if there is a custom dispatch at this URI. Custom
# dispatchers can only be specified in app.config, not in _cp_config
# (since custom dispatchers may not even have an app.root).
@@ -762,7 +792,10 @@ class Request(object):
dispatch(path)
def handle_error(self):
- """Handle the last unanticipated exception. (Core)"""
+ """Handle the last unanticipated exception.
+
+ (Core)
+ """
try:
self.hooks.run('before_error_response')
if self.error_response:
@@ -776,7 +809,6 @@ class Request(object):
class ResponseBody(object):
-
"""The body of the HTTP response (the response entity)."""
unicode_err = ('Page handlers MUST return bytes. Use tools.encode '
@@ -802,18 +834,18 @@ class ResponseBody(object):
class Response(object):
-
"""An HTTP Response, including status, headers, and body."""
status = ''
"""The HTTP Status-Code and Reason-Phrase."""
header_list = []
- """
- A list of the HTTP response headers as (name, value) tuples.
+ """A list of the HTTP response headers as (name, value) tuples.
+
In general, you should use response.headers (a dict) instead. This
attribute is generated from response.headers and is not valid until
- after the finalize phase."""
+ after the finalize phase.
+ """
headers = httputil.HeaderMap()
"""
@@ -833,7 +865,10 @@ class Response(object):
"""The body (entity) of the HTTP response."""
time = None
- """The value of time.time() when created. Use in HTTP dates."""
+ """The value of time.time() when created.
+
+ Use in HTTP dates.
+ """
stream = False
"""If False, buffer the response body."""
@@ -861,15 +896,15 @@ class Response(object):
return new_body
def _flush_body(self):
- """
- Discard self.body but consume any generator such that
- any finalization can occur, such as is required by
- caching.tee_output().
- """
+ """Discard self.body but consume any generator such that any
+ finalization can occur, such as is required by caching.tee_output()."""
consume(iter(self.body))
def finalize(self):
- """Transform headers (and cookies) into self.header_list. (Core)"""
+ """Transform headers (and cookies) into self.header_list.
+
+ (Core)
+ """
try:
code, reason, _ = httputil.valid_status(self.status)
except ValueError:
diff --git a/lib/cherrypy/_cpserver.py b/lib/cherrypy/_cpserver.py
index 5f8d98fa..62331673 100644
--- a/lib/cherrypy/_cpserver.py
+++ b/lib/cherrypy/_cpserver.py
@@ -50,7 +50,8 @@ class Server(ServerAdapter):
"""If given, the name of the UNIX socket to use instead of TCP/IP.
When this option is not None, the `socket_host` and `socket_port` options
- are ignored."""
+ are ignored.
+ """
socket_queue_size = 5
"""The 'backlog' argument to socket.listen(); specifies the maximum number
@@ -79,17 +80,24 @@ class Server(ServerAdapter):
"""The number of worker threads to start up in the pool."""
thread_pool_max = -1
- """The maximum size of the worker-thread pool. Use -1 to indicate no limit.
+ """The maximum size of the worker-thread pool.
+
+ Use -1 to indicate no limit.
"""
max_request_header_size = 500 * 1024
"""The maximum number of bytes allowable in the request headers.
- If exceeded, the HTTP server should return "413 Request Entity Too Large".
+
+ If exceeded, the HTTP server should return "413 Request Entity Too
+ Large".
"""
max_request_body_size = 100 * 1024 * 1024
- """The maximum number of bytes allowable in the request body. If exceeded,
- the HTTP server should return "413 Request Entity Too Large"."""
+ """The maximum number of bytes allowable in the request body.
+
+ If exceeded, the HTTP server should return "413 Request Entity Too
+ Large".
+ """
instance = None
"""If not None, this should be an HTTP server instance (such as
@@ -119,7 +127,8 @@ class Server(ServerAdapter):
the builtin WSGI server. Builtin options are: 'builtin' (to
use the SSL library built into recent versions of Python).
You may also register your own classes in the
- cheroot.server.ssl_adapters dict."""
+ cheroot.server.ssl_adapters dict.
+ """
statistics = False
"""Turns statistics-gathering on or off for aware HTTP servers."""
@@ -129,11 +138,13 @@ class Server(ServerAdapter):
wsgi_version = (1, 0)
"""The WSGI version tuple to use with the builtin WSGI server.
- The provided options are (1, 0) [which includes support for PEP 3333,
- which declares it covers WSGI version 1.0.1 but still mandates the
- wsgi.version (1, 0)] and ('u', 0), an experimental unicode version.
- You may create and register your own experimental versions of the WSGI
- protocol by adding custom classes to the cheroot.server.wsgi_gateways dict.
+
+ The provided options are (1, 0) [which includes support for PEP
+ 3333, which declares it covers WSGI version 1.0.1 but still mandates
+ the wsgi.version (1, 0)] and ('u', 0), an experimental unicode
+ version. You may create and register your own experimental versions
+ of the WSGI protocol by adding custom classes to the
+ cheroot.server.wsgi_gateways dict.
"""
peercreds = False
@@ -184,7 +195,8 @@ class Server(ServerAdapter):
def bind_addr(self):
"""Return bind address.
- A (host, port) tuple for TCP sockets or a str for Unix domain sockts.
+ A (host, port) tuple for TCP sockets or a str for Unix domain
+ sockets.
"""
if self.socket_file:
return self.socket_file
diff --git a/lib/cherrypy/_cptools.py b/lib/cherrypy/_cptools.py
index 716f99a4..e47c046e 100644
--- a/lib/cherrypy/_cptools.py
+++ b/lib/cherrypy/_cptools.py
@@ -1,7 +1,7 @@
"""CherryPy tools. A "tool" is any helper, adapted to CP.
-Tools are usually designed to be used in a variety of ways (although some
-may only offer one if they choose):
+Tools are usually designed to be used in a variety of ways (although
+some may only offer one if they choose):
Library calls
All tools are callables that can be used wherever needed.
@@ -48,10 +48,10 @@ _attr_error = (
class Tool(object):
-
"""A registered function for use with CherryPy request-processing hooks.
- help(tool.callable) should give you more information about this Tool.
+ help(tool.callable) should give you more information about this
+ Tool.
"""
namespace = 'tools'
@@ -135,8 +135,8 @@ class Tool(object):
def _setup(self):
"""Hook this tool into cherrypy.request.
- The standard CherryPy request object will automatically call this
- method when the tool is "turned on" in config.
+ The standard CherryPy request object will automatically call
+ this method when the tool is "turned on" in config.
"""
conf = self._merged_args()
p = conf.pop('priority', None)
@@ -147,15 +147,15 @@ class Tool(object):
class HandlerTool(Tool):
-
"""Tool which is called 'before main', that may skip normal handlers.
- If the tool successfully handles the request (by setting response.body),
- if should return True. This will cause CherryPy to skip any 'normal' page
- handler. If the tool did not handle the request, it should return False
- to tell CherryPy to continue on and call the normal page handler. If the
- tool is declared AS a page handler (see the 'handler' method), returning
- False will raise NotFound.
+ If the tool successfully handles the request (by setting
+ response.body), if should return True. This will cause CherryPy to
+ skip any 'normal' page handler. If the tool did not handle the
+ request, it should return False to tell CherryPy to continue on and
+ call the normal page handler. If the tool is declared AS a page
+ handler (see the 'handler' method), returning False will raise
+ NotFound.
"""
def __init__(self, callable, name=None):
@@ -185,8 +185,8 @@ class HandlerTool(Tool):
def _setup(self):
"""Hook this tool into cherrypy.request.
- The standard CherryPy request object will automatically call this
- method when the tool is "turned on" in config.
+ The standard CherryPy request object will automatically call
+ this method when the tool is "turned on" in config.
"""
conf = self._merged_args()
p = conf.pop('priority', None)
@@ -197,7 +197,6 @@ class HandlerTool(Tool):
class HandlerWrapperTool(Tool):
-
"""Tool which wraps request.handler in a provided wrapper function.
The 'newhandler' arg must be a handler wrapper function that takes a
@@ -232,7 +231,6 @@ class HandlerWrapperTool(Tool):
class ErrorTool(Tool):
-
"""Tool which is used to replace the default request.error_response."""
def __init__(self, callable, name=None):
@@ -244,8 +242,8 @@ class ErrorTool(Tool):
def _setup(self):
"""Hook this tool into cherrypy.request.
- The standard CherryPy request object will automatically call this
- method when the tool is "turned on" in config.
+ The standard CherryPy request object will automatically call
+ this method when the tool is "turned on" in config.
"""
cherrypy.serving.request.error_response = self._wrapper
@@ -254,7 +252,6 @@ class ErrorTool(Tool):
class SessionTool(Tool):
-
"""Session Tool for CherryPy.
sessions.locking
@@ -282,8 +279,8 @@ class SessionTool(Tool):
def _setup(self):
"""Hook this tool into cherrypy.request.
- The standard CherryPy request object will automatically call this
- method when the tool is "turned on" in config.
+ The standard CherryPy request object will automatically call
+ this method when the tool is "turned on" in config.
"""
hooks = cherrypy.serving.request.hooks
@@ -325,7 +322,6 @@ class SessionTool(Tool):
class XMLRPCController(object):
-
"""A Controller (page handler collection) for XML-RPC.
To use it, have your controllers subclass this base class (it will
@@ -392,7 +388,6 @@ class SessionAuthTool(HandlerTool):
class CachingTool(Tool):
-
"""Caching Tool for CherryPy."""
def _wrapper(self, **kwargs):
@@ -416,11 +411,11 @@ class CachingTool(Tool):
class Toolbox(object):
-
"""A collection of Tools.
This object also functions as a config namespace handler for itself.
- Custom toolboxes should be added to each Application's toolboxes dict.
+ Custom toolboxes should be added to each Application's toolboxes
+ dict.
"""
def __init__(self, namespace):
diff --git a/lib/cherrypy/_cptree.py b/lib/cherrypy/_cptree.py
index 917c5b1a..3dea1c29 100644
--- a/lib/cherrypy/_cptree.py
+++ b/lib/cherrypy/_cptree.py
@@ -10,19 +10,22 @@ from cherrypy.lib import httputil, reprconf
class Application(object):
"""A CherryPy Application.
- Servers and gateways should not instantiate Request objects directly.
- Instead, they should ask an Application object for a request object.
+ Servers and gateways should not instantiate Request objects
+ directly. Instead, they should ask an Application object for a
+ request object.
- An instance of this class may also be used as a WSGI callable
- (WSGI application object) for itself.
+ An instance of this class may also be used as a WSGI callable (WSGI
+ application object) for itself.
"""
root = None
- """The top-most container of page handlers for this app. Handlers should
- be arranged in a hierarchy of attributes, matching the expected URI
- hierarchy; the default dispatcher then searches this hierarchy for a
- matching handler. When using a dispatcher other than the default,
- this value may be None."""
+ """The top-most container of page handlers for this app.
+
+ Handlers should be arranged in a hierarchy of attributes, matching
+ the expected URI hierarchy; the default dispatcher then searches
+ this hierarchy for a matching handler. When using a dispatcher other
+ than the default, this value may be None.
+ """
config = {}
"""A dict of {path: pathconf} pairs, where 'pathconf' is itself a dict
@@ -32,10 +35,16 @@ class Application(object):
toolboxes = {'tools': cherrypy.tools}
log = None
- """A LogManager instance. See _cplogging."""
+ """A LogManager instance.
+
+ See _cplogging.
+ """
wsgiapp = None
- """A CPWSGIApp instance. See _cpwsgi."""
+ """A CPWSGIApp instance.
+
+ See _cpwsgi.
+ """
request_class = _cprequest.Request
response_class = _cprequest.Response
@@ -82,12 +91,15 @@ class Application(object):
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.
+ 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
+ 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
@@ -171,9 +183,9 @@ class Application(object):
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
- (WSGI application object), in which case it dispatches to all
- mounted apps.
+ An instance of this class may also be used as a WSGI callable (WSGI
+ application object), in which case it dispatches to all mounted
+ apps.
"""
apps = {}
diff --git a/lib/cherrypy/_cpwsgi.py b/lib/cherrypy/_cpwsgi.py
index b4f55fd6..b2a6da52 100644
--- a/lib/cherrypy/_cpwsgi.py
+++ b/lib/cherrypy/_cpwsgi.py
@@ -1,10 +1,10 @@
"""WSGI interface (see PEP 333 and 3333).
Note that WSGI environ keys and values are 'native strings'; that is,
-whatever the type of "" is. For Python 2, that's a byte string; for Python 3,
-it's a unicode string. But PEP 3333 says: "even if Python's str type is
-actually Unicode "under the hood", the content of native strings must
-still be translatable to bytes via the Latin-1 encoding!"
+whatever the type of "" is. For Python 2, that's a byte string; for
+Python 3, it's a unicode string. But PEP 3333 says: "even if Python's
+str type is actually Unicode "under the hood", the content of native
+strings must still be translatable to bytes via the Latin-1 encoding!"
"""
import sys as _sys
@@ -34,7 +34,6 @@ def downgrade_wsgi_ux_to_1x(environ):
class VirtualHost(object):
-
"""Select a different WSGI application based on the Host header.
This can be useful when running multiple sites within one CP server.
@@ -56,7 +55,10 @@ class VirtualHost(object):
cherrypy.tree.graft(vhost)
"""
default = None
- """Required. The default WSGI application."""
+ """Required.
+
+ The default WSGI application.
+ """
use_x_forwarded_host = True
"""If True (the default), any "X-Forwarded-Host"
@@ -65,11 +67,12 @@ class VirtualHost(object):
domains = {}
"""A dict of {host header value: application} pairs.
- The incoming "Host" request header is looked up in this dict,
- and, if a match is found, the corresponding WSGI application
- will be called instead of the default. Note that you often need
- separate entries for "example.com" and "www.example.com".
- In addition, "Host" headers may contain the port number.
+
+ The incoming "Host" request header is looked up in this dict, and,
+ if a match is found, the corresponding WSGI application will be
+ called instead of the default. Note that you often need separate
+ entries for "example.com" and "www.example.com". In addition, "Host"
+ headers may contain the port number.
"""
def __init__(self, default, domains=None, use_x_forwarded_host=True):
@@ -89,7 +92,6 @@ class VirtualHost(object):
class InternalRedirector(object):
-
"""WSGI middleware that handles raised cherrypy.InternalRedirect."""
def __init__(self, nextapp, recursive=False):
@@ -137,7 +139,6 @@ class InternalRedirector(object):
class ExceptionTrapper(object):
-
"""WSGI middleware that traps exceptions."""
def __init__(self, nextapp, throws=(KeyboardInterrupt, SystemExit)):
@@ -226,7 +227,6 @@ class _TrappedResponse(object):
class AppResponse(object):
-
"""WSGI response iterable for CherryPy applications."""
def __init__(self, environ, start_response, cpapp):
@@ -277,7 +277,10 @@ class AppResponse(object):
return next(self.iter_response)
def close(self):
- """Close and de-reference the current request and response. (Core)"""
+ """Close and de-reference the current request and response.
+
+ (Core)
+ """
streaming = _cherrypy.serving.response.stream
self.cpapp.release_serving()
@@ -380,18 +383,20 @@ class AppResponse(object):
class CPWSGIApp(object):
-
"""A WSGI application object for a CherryPy Application."""
pipeline = [
('ExceptionTrapper', ExceptionTrapper),
('InternalRedirector', InternalRedirector),
]
- """A list of (name, wsgiapp) pairs. Each 'wsgiapp' MUST be a
- constructor that takes an initial, positional 'nextapp' argument,
- plus optional keyword arguments, and returns a WSGI application
- (that takes environ and start_response arguments). The 'name' can
- be any you choose, and will correspond to keys in self.config."""
+ """A list of (name, wsgiapp) pairs.
+
+ Each 'wsgiapp' MUST be a constructor that takes an initial,
+ positional 'nextapp' argument, plus optional keyword arguments, and
+ returns a WSGI application (that takes environ and start_response
+ arguments). The 'name' can be any you choose, and will correspond to
+ keys in self.config.
+ """
head = None
"""Rather than nest all apps in the pipeline on each call, it's only
@@ -399,9 +404,12 @@ class CPWSGIApp(object):
this to None again if you change self.pipeline after calling self."""
config = {}
- """A dict whose keys match names listed in the pipeline. Each
- value is a further dict which will be passed to the corresponding
- named WSGI callable (from the pipeline) as keyword arguments."""
+ """A dict whose keys match names listed in the pipeline.
+
+ Each value is a further dict which will be passed to the
+ corresponding named WSGI callable (from the pipeline) as keyword
+ arguments.
+ """
response_class = AppResponse
"""The class to instantiate and return as the next app in the WSGI chain.
@@ -417,8 +425,8 @@ class CPWSGIApp(object):
def tail(self, environ, start_response):
"""WSGI application callable for the actual CherryPy application.
- You probably shouldn't call this; call self.__call__ instead,
- so that any WSGI middleware in self.pipeline can run first.
+ You probably shouldn't call this; call self.__call__ instead, so
+ that any WSGI middleware in self.pipeline can run first.
"""
return self.response_class(environ, start_response, self.cpapp)
diff --git a/lib/cherrypy/_cpwsgi_server.py b/lib/cherrypy/_cpwsgi_server.py
index 11dd846a..b8e96deb 100644
--- a/lib/cherrypy/_cpwsgi_server.py
+++ b/lib/cherrypy/_cpwsgi_server.py
@@ -1,7 +1,7 @@
-"""
-WSGI server interface (see PEP 333).
+"""WSGI server interface (see PEP 333).
-This adds some CP-specific bits to the framework-agnostic cheroot package.
+This adds some CP-specific bits to the framework-agnostic cheroot
+package.
"""
import sys
@@ -35,10 +35,11 @@ class CPWSGIHTTPRequest(cheroot.server.HTTPRequest):
class CPWSGIServer(cheroot.wsgi.Server):
"""Wrapper for cheroot.wsgi.Server.
- cheroot has been designed to not reference CherryPy in any way,
- so that it can be used in other frameworks and applications. Therefore,
- we wrap it here, so we can set our own mount points from cherrypy.tree
- and apply some attributes from config -> cherrypy.server -> wsgi.Server.
+ cheroot has been designed to not reference CherryPy in any way, so
+ that it can be used in other frameworks and applications. Therefore,
+ we wrap it here, so we can set our own mount points from
+ cherrypy.tree and apply some attributes from config ->
+ cherrypy.server -> wsgi.Server.
"""
fmt = 'CherryPy/{cherrypy.__version__} {cheroot.wsgi.Server.version}'
diff --git a/lib/cherrypy/_helper.py b/lib/cherrypy/_helper.py
index d57cd1f9..497438eb 100644
--- a/lib/cherrypy/_helper.py
+++ b/lib/cherrypy/_helper.py
@@ -137,7 +137,6 @@ def popargs(*args, **kwargs):
class Root:
def index(self):
#...
-
"""
# Since keyword arg comes after *args, we have to process it ourselves
# for lower versions of python.
@@ -201,16 +200,17 @@ def url(path='', qs='', script_name=None, base=None, relative=None):
If it does not start with a slash, this returns
(base + script_name [+ request.path_info] + path + qs).
- If script_name is None, cherrypy.request will be used
- to find a script_name, if available.
+ If script_name is None, cherrypy.request will be used to find a
+ script_name, if available.
If base is None, cherrypy.request.base will be used (if available).
Note that you can use cherrypy.tools.proxy to change this.
- Finally, note that this function can be used to obtain an absolute URL
- for the current request path (minus the querystring) by passing no args.
- If you call url(qs=cherrypy.request.query_string), you should get the
- original browser URL (assuming no internal redirections).
+ Finally, note that this function can be used to obtain an absolute
+ URL for the current request path (minus the querystring) by passing
+ no args. If you call url(qs=cherrypy.request.query_string), you
+ should get the original browser URL (assuming no internal
+ redirections).
If relative is None or not provided, request.app.relative_urls will
be used (if available, else False). If False, the output will be an
@@ -320,8 +320,8 @@ def normalize_path(path):
class _ClassPropertyDescriptor(object):
"""Descript for read-only class-based property.
- Turns a classmethod-decorated func into a read-only property of that class
- type (means the value cannot be set).
+ Turns a classmethod-decorated func into a read-only property of that
+ class type (means the value cannot be set).
"""
def __init__(self, fget, fset=None):
diff --git a/lib/cherrypy/_json.py b/lib/cherrypy/_json.py
index 0c2a0f0e..4ef85580 100644
--- a/lib/cherrypy/_json.py
+++ b/lib/cherrypy/_json.py
@@ -1,5 +1,4 @@
-"""
-JSON support.
+"""JSON support.
Expose preferred json module as json and provide encode/decode
convenience functions.
diff --git a/lib/cherrypy/lib/__init__.py b/lib/cherrypy/lib/__init__.py
index 0edaaf20..9788ffdf 100644
--- a/lib/cherrypy/lib/__init__.py
+++ b/lib/cherrypy/lib/__init__.py
@@ -6,8 +6,8 @@ def is_iterator(obj):
(i.e. like a generator).
- This will return False for objects which are iterable,
- but not iterators themselves.
+ This will return False for objects which are iterable, but not
+ iterators themselves.
"""
from types import GeneratorType
if isinstance(obj, GeneratorType):
diff --git a/lib/cherrypy/lib/auth_basic.py b/lib/cherrypy/lib/auth_basic.py
index ad379a26..b938a560 100644
--- a/lib/cherrypy/lib/auth_basic.py
+++ b/lib/cherrypy/lib/auth_basic.py
@@ -18,7 +18,6 @@ as the credentials store::
'tools.auth_basic.accept_charset': 'UTF-8',
}
app_config = { '/' : basic_auth }
-
"""
import binascii
diff --git a/lib/cherrypy/lib/auth_digest.py b/lib/cherrypy/lib/auth_digest.py
index 981e9a5d..46749268 100644
--- a/lib/cherrypy/lib/auth_digest.py
+++ b/lib/cherrypy/lib/auth_digest.py
@@ -55,7 +55,7 @@ def TRACE(msg):
def get_ha1_dict_plain(user_password_dict):
- """Returns a get_ha1 function which obtains a plaintext password from a
+ """Return a get_ha1 function which obtains a plaintext password from a
dictionary of the form: {username : password}.
If you want a simple dictionary-based authentication scheme, with plaintext
@@ -72,7 +72,7 @@ def get_ha1_dict_plain(user_password_dict):
def get_ha1_dict(user_ha1_dict):
- """Returns a get_ha1 function which obtains a HA1 password hash from a
+ """Return a get_ha1 function which obtains a HA1 password hash from a
dictionary of the form: {username : HA1}.
If you want a dictionary-based authentication scheme, but with
@@ -87,7 +87,7 @@ def get_ha1_dict(user_ha1_dict):
def get_ha1_file_htdigest(filename):
- """Returns a get_ha1 function which obtains a HA1 password hash from a
+ """Return a get_ha1 function which obtains a HA1 password hash from a
flat file with lines of the same format as that produced by the Apache
htdigest utility. For example, for realm 'wonderland', username 'alice',
and password '4x5istwelve', the htdigest line would be::
@@ -135,7 +135,7 @@ def synthesize_nonce(s, key, timestamp=None):
def H(s):
- """The hash function H"""
+ """The hash function H."""
return md5_hex(s)
@@ -259,10 +259,11 @@ class HttpDigestAuthorization(object):
return False
def is_nonce_stale(self, max_age_seconds=600):
- """Returns True if a validated nonce is stale. The nonce contains a
- timestamp in plaintext and also a secure hash of the timestamp.
- You should first validate the nonce to ensure the plaintext
- timestamp is not spoofed.
+ """Return True if a validated nonce is stale.
+
+ The nonce contains a timestamp in plaintext and also a secure
+ hash of the timestamp. You should first validate the nonce to
+ ensure the plaintext timestamp is not spoofed.
"""
try:
timestamp, hashpart = self.nonce.split(':', 1)
@@ -275,7 +276,10 @@ class HttpDigestAuthorization(object):
return True
def HA2(self, entity_body=''):
- """Returns the H(A2) string. See :rfc:`2617` section 3.2.2.3."""
+ """Return the H(A2) string.
+
+ See :rfc:`2617` section 3.2.2.3.
+ """
# RFC 2617 3.2.2.3
# If the "qop" directive's value is "auth" or is unspecified,
# then A2 is:
@@ -306,7 +310,6 @@ class HttpDigestAuthorization(object):
4.3. This refers to the entity the user agent sent in the
request which has the Authorization header. Typically GET
requests don't have an entity, and POST requests do.
-
"""
ha2 = self.HA2(entity_body)
# Request-Digest -- RFC 2617 3.2.2.1
@@ -395,7 +398,6 @@ def digest_auth(realm, get_ha1, key, debug=False, accept_charset='utf-8'):
key
A secret string known only to the server, used in the synthesis
of nonces.
-
"""
request = cherrypy.serving.request
@@ -447,9 +449,7 @@ def digest_auth(realm, get_ha1, key, debug=False, accept_charset='utf-8'):
def _respond_401(realm, key, accept_charset, debug, **kwargs):
- """
- Respond with 401 status and a WWW-Authenticate header
- """
+ """Respond with 401 status and a WWW-Authenticate header."""
header = www_authenticate(
realm, key,
accept_charset=accept_charset,
diff --git a/lib/cherrypy/lib/caching.py b/lib/cherrypy/lib/caching.py
index 08d2d8e4..89cb0442 100644
--- a/lib/cherrypy/lib/caching.py
+++ b/lib/cherrypy/lib/caching.py
@@ -42,7 +42,6 @@ from cherrypy.lib import cptools, httputil
class Cache(object):
-
"""Base class for Cache implementations."""
def get(self):
@@ -64,17 +63,16 @@ class Cache(object):
# ------------------------------ Memory Cache ------------------------------- #
class AntiStampedeCache(dict):
-
"""A storage system for cached items which reduces stampede collisions."""
def wait(self, key, timeout=5, debug=False):
"""Return the cached value for the given key, or None.
- If timeout is not None, and the value is already
- being calculated by another thread, wait until the given timeout has
- elapsed. If the value is available before the timeout expires, it is
- returned. If not, None is returned, and a sentinel placed in the cache
- to signal other threads to wait.
+ If timeout is not None, and the value is already being
+ calculated by another thread, wait until the given timeout has
+ elapsed. If the value is available before the timeout expires,
+ it is returned. If not, None is returned, and a sentinel placed
+ in the cache to signal other threads to wait.
If timeout is None, no waiting is performed nor sentinels used.
"""
@@ -127,7 +125,6 @@ class AntiStampedeCache(dict):
class MemoryCache(Cache):
-
"""An in-memory cache for varying response content.
Each key in self.store is a URI, and each value is an AntiStampedeCache.
@@ -381,7 +378,10 @@ def get(invalid_methods=('POST', 'PUT', 'DELETE'), debug=False, **kwargs):
def tee_output():
- """Tee response output to cache storage. Internal."""
+ """Tee response output to cache storage.
+
+ Internal.
+ """
# Used by CachingTool by attaching to request.hooks
request = cherrypy.serving.request
@@ -441,7 +441,6 @@ def expires(secs=0, force=False, debug=False):
* Expires
If any are already present, none of the above response headers are set.
-
"""
response = cherrypy.serving.response
diff --git a/lib/cherrypy/lib/covercp.py b/lib/cherrypy/lib/covercp.py
index 6c3871fc..f22cce74 100644
--- a/lib/cherrypy/lib/covercp.py
+++ b/lib/cherrypy/lib/covercp.py
@@ -22,7 +22,7 @@ it will call ``serve()`` for you.
import re
import sys
-import cgi
+import html
import os
import os.path
import urllib.parse
@@ -352,9 +352,9 @@ class CoverStats(object):
buffer.append((lineno, line))
if empty_the_buffer:
for lno, pastline in buffer:
- yield template % (lno, cgi.escape(pastline))
+ yield template % (lno, html.escape(pastline))
buffer = []
- yield template % (lineno, cgi.escape(line))
+ yield template % (lineno, html.escape(line))
@cherrypy.expose
def report(self, name):
diff --git a/lib/cherrypy/lib/cpstats.py b/lib/cherrypy/lib/cpstats.py
index 111af063..5dff319b 100644
--- a/lib/cherrypy/lib/cpstats.py
+++ b/lib/cherrypy/lib/cpstats.py
@@ -184,7 +184,6 @@ To report statistics::
To format statistics reports::
See 'Reporting', above.
-
"""
import logging
@@ -254,7 +253,6 @@ def proc_time(s):
class ByteCountWrapper(object):
-
"""Wraps a file-like object, counting the number of bytes read."""
def __init__(self, rfile):
@@ -307,7 +305,6 @@ def _get_threading_ident():
class StatsTool(cherrypy.Tool):
-
"""Record various information about the current request."""
def __init__(self):
@@ -316,8 +313,8 @@ class StatsTool(cherrypy.Tool):
def _setup(self):
"""Hook this tool into cherrypy.request.
- The standard CherryPy request object will automatically call this
- method when the tool is "turned on" in config.
+ The standard CherryPy request object will automatically call
+ this method when the tool is "turned on" in config.
"""
if appstats.get('Enabled', False):
cherrypy.Tool._setup(self)
diff --git a/lib/cherrypy/lib/cptools.py b/lib/cherrypy/lib/cptools.py
index 13b4c567..61d4d36b 100644
--- a/lib/cherrypy/lib/cptools.py
+++ b/lib/cherrypy/lib/cptools.py
@@ -94,8 +94,8 @@ def validate_etags(autotags=False, debug=False):
def validate_since():
"""Validate the current Last-Modified against If-Modified-Since headers.
- If no code has set the Last-Modified response header, then no validation
- will be performed.
+ If no code has set the Last-Modified response header, then no
+ validation will be performed.
"""
response = cherrypy.serving.response
lastmod = response.headers.get('Last-Modified')
@@ -123,9 +123,9 @@ def validate_since():
def allow(methods=None, debug=False):
"""Raise 405 if request.method not in methods (default ['GET', 'HEAD']).
- The given methods are case-insensitive, and may be in any order.
- If only one method is allowed, you may supply a single string;
- if more than one, supply a list of strings.
+ The given methods are case-insensitive, and may be in any order. If
+ only one method is allowed, you may supply a single string; if more
+ than one, supply a list of strings.
Regardless of whether the current method is allowed or not, this
also emits an 'Allow' response header, containing the given methods.
@@ -154,22 +154,23 @@ def proxy(base=None, local='X-Forwarded-Host', remote='X-Forwarded-For',
scheme='X-Forwarded-Proto', debug=False):
"""Change the base URL (scheme://host[:port][/path]).
- For running a CP server behind Apache, lighttpd, or other HTTP server.
+ For running a CP server behind Apache, lighttpd, or other HTTP
+ server.
- For Apache and lighttpd, you should leave the 'local' argument at the
- default value of 'X-Forwarded-Host'. For Squid, you probably want to set
- tools.proxy.local = 'Origin'.
+ For Apache and lighttpd, you should leave the 'local' argument at
+ the default value of 'X-Forwarded-Host'. For Squid, you probably
+ want to set tools.proxy.local = 'Origin'.
- If you want the new request.base to include path info (not just the host),
- you must explicitly set base to the full base path, and ALSO set 'local'
- to '', so that the X-Forwarded-Host request header (which never includes
- path info) does not override it. Regardless, the value for 'base' MUST
- NOT end in a slash.
+ If you want the new request.base to include path info (not just the
+ host), you must explicitly set base to the full base path, and ALSO
+ set 'local' to '', so that the X-Forwarded-Host request header
+ (which never includes path info) does not override it. Regardless,
+ the value for 'base' MUST NOT end in a slash.
cherrypy.request.remote.ip (the IP address of the client) will be
- rewritten if the header specified by the 'remote' arg is valid.
- By default, 'remote' is set to 'X-Forwarded-For'. If you do not
- want to rewrite remote.ip, set the 'remote' arg to an empty string.
+ rewritten if the header specified by the 'remote' arg is valid. By
+ default, 'remote' is set to 'X-Forwarded-For'. If you do not want to
+ rewrite remote.ip, set the 'remote' arg to an empty string.
"""
request = cherrypy.serving.request
@@ -217,8 +218,8 @@ def proxy(base=None, local='X-Forwarded-Host', remote='X-Forwarded-For',
def ignore_headers(headers=('Range',), debug=False):
"""Delete request headers whose field names are included in 'headers'.
- This is a useful tool for working behind certain HTTP servers;
- for example, Apache duplicates the work that CP does for 'Range'
+ This is a useful tool for working behind certain HTTP servers; for
+ example, Apache duplicates the work that CP does for 'Range'
headers, and will doubly-truncate the response.
"""
request = cherrypy.serving.request
@@ -281,7 +282,6 @@ def referer(pattern, accept=True, accept_missing=False, error=403,
class SessionAuth(object):
-
"""Assert that the user is logged in."""
session_key = 'username'
@@ -319,7 +319,10 @@ Message: %(error_msg)s