Compare commits
18 Commits
v2.34.0.de
...
v2.34.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7b549b545 | ||
|
|
e511bc7277 | ||
|
|
5691f59613 | ||
|
|
2144213c30 | ||
|
|
6404f345e5 | ||
|
|
0b401c76b6 | ||
|
|
86b378d3f6 | ||
|
|
a4f9a5999b | ||
|
|
3816cfa1ab | ||
|
|
b684dcb9bb | ||
|
|
dc9dbdfb34 | ||
|
|
25340ebad0 | ||
|
|
fd628095d7 | ||
|
|
e8d2c015ee | ||
|
|
eb173bc819 | ||
|
|
d60f4773b1 | ||
|
|
f64a0d782b | ||
|
|
04d750509b |
3
.github/workflows/run-tests.yml
vendored
3
.github/workflows/run-tests.yml
vendored
@@ -8,12 +8,11 @@ permissions:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
continue-on-error: ${{ matrix.python-version == '3.15-dev' }}
|
||||
timeout-minutes: 10
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.15-dev", "pypy-3.11"]
|
||||
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.14t", "3.15-dev", "pypy-3.11"]
|
||||
os: [ubuntu-22.04, macOS-latest, windows-latest]
|
||||
# Pypy-3.11 can't install openssl-sys with rust
|
||||
# which prevents us from testing in GHA.
|
||||
|
||||
@@ -13,7 +13,7 @@ repos:
|
||||
args: [--fix=auto]
|
||||
- id: trailing-whitespace
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.15.11
|
||||
rev: v0.15.12
|
||||
hooks:
|
||||
- id: ruff-check
|
||||
args: [--fix]
|
||||
|
||||
41
HISTORY.md
41
HISTORY.md
@@ -6,19 +6,48 @@ dev
|
||||
|
||||
- \[Short description of non-trivial change.\]
|
||||
|
||||
2.34.0 (2026-05-??)
|
||||
2.34.1 (2026-05-13)
|
||||
-------------------
|
||||
|
||||
**Improvements**
|
||||
* Requests 2.34.0 introduces inline types, replacing those provided by
|
||||
**Bugfixes**
|
||||
- Widened `json` input type from `dict` and `list` to `Mapping`
|
||||
and `Sequence`. (#7436)
|
||||
- Changed `headers` input type to MutableMapping and removed `None` from
|
||||
`Request.headers` typing to improve handling for users. (#7431)
|
||||
- `Response.reason` moved from `str | None` to `str` to improve handling
|
||||
for users. (#7437)
|
||||
- Fixed a bug where some bodies with custom `__getattr__` implementations
|
||||
weren't being properly detected as Iterables. (#7433)
|
||||
|
||||
|
||||
2.34.0 (2026-05-11)
|
||||
-------------------
|
||||
|
||||
**Announcements**
|
||||
- Requests 2.34.0 introduces inline types, replacing those provided by
|
||||
typeshed. Public API types should be fully compatible with mypy, pyright,
|
||||
and ty. (#7272)
|
||||
* Digest Auth hashing algorithms have added `usedforsecurity=False` to clarify
|
||||
and ty. We believe types are comprehensive but if you find issues, please
|
||||
report them to the pinned tracking issue.
|
||||
|
||||
Special thanks to @bastimeyer, @cthoyt, @edgarrmondragon, and @srittau for
|
||||
helping review and test the types ahead of the release. (#7272)
|
||||
|
||||
**Improvements**
|
||||
- Digest Auth hashing algorithms have added `usedforsecurity=False` to clarify
|
||||
security considerations. (#7310)
|
||||
- Requests added support for Python 3.15 based on beta1. Downstream projects
|
||||
should be able to start testing prior to its release in October. (#7422)
|
||||
- Requests added support for Python 3.14t. (#7419)
|
||||
|
||||
**Bugfixes**
|
||||
* ``Response.history`` no longer contains a reference to itself, preventing
|
||||
- ``Response.history`` no longer contains a reference to itself, preventing
|
||||
accidental looping when traversing the history list. (#7328)
|
||||
- Requests no longer performs greedy matching on no_proxy domains. The
|
||||
proxy_bypass implementation has been updated with CPython's fix from
|
||||
bpo-39057. (#7427)
|
||||
- Requests no longer incorrectly strips duplicate leading slashes in
|
||||
URI paths. This should address user issues with specific presigned
|
||||
URLs. Note the full fix requires urllib3 2.7.0+. (#7315)
|
||||
|
||||
|
||||
2.33.1 (2026-03-30)
|
||||
|
||||
@@ -9,7 +9,7 @@ BUILDDIR = _build
|
||||
|
||||
# User-friendly check for sphinx-build
|
||||
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
|
||||
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
|
||||
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from https://www.sphinx-doc.org/)
|
||||
endif
|
||||
|
||||
# Internal variables.
|
||||
|
||||
@@ -30,7 +30,7 @@ Why not Httplib2?
|
||||
-----------------
|
||||
|
||||
Chris Adams gave an excellent summary on
|
||||
`Hacker News <http://news.ycombinator.com/item?id=2884406>`_:
|
||||
`Hacker News <https://news.ycombinator.com/item?id=2884406>`_:
|
||||
|
||||
httplib2 is part of why you should use requests: it's far more respectable
|
||||
as a client but not as well documented and it still takes way too much code
|
||||
@@ -44,7 +44,7 @@ Chris Adams gave an excellent summary on
|
||||
Disclosure: I'm listed in the requests AUTHORS file but can claim credit
|
||||
for, oh, about 0.0001% of the awesomeness.
|
||||
|
||||
1. http://code.google.com/p/httplib2/issues/detail?id=96 is a good example:
|
||||
1. https://code.google.com/p/httplib2/issues/detail?id=96 is a good example:
|
||||
an annoying bug that affected many people, there was a fix available for
|
||||
months, which worked great when I applied it in a fork and pounded a couple
|
||||
TB of data through it, but it took over a year to make it into trunk and
|
||||
|
||||
@@ -7,4 +7,4 @@ Articles & Talks
|
||||
- `Issac Kelly's 'Consuming Web APIs' talk <https://issackelly.github.io/Consuming-Web-APIs-with-Python-Talk/slides/slides.html>`_
|
||||
- `Blog post about Requests via Yum <https://arunsag.wordpress.com/2011/08/17/new-package-python-requests-http-for-humans/>`_
|
||||
- `Russian blog post introducing Requests <https://habr.com/post/126262/>`_
|
||||
- `Sending JSON in Requests <http://www.coglib.com/~icordasc/blog/2014/11/sending-json-in-requests.html>`_
|
||||
- `Sending JSON in Requests <https://www.coglib.com/~icordasc/blog/2014/11/sending-json-in-requests.html>`_
|
||||
|
||||
@@ -14,7 +14,7 @@ getting a feel for how contributing to this project works. If you have any
|
||||
questions, feel free to reach out to either `Nate Prewitt`_, `Ian Cordasco`_,
|
||||
or `Seth Michael Larson`_, the primary maintainers.
|
||||
|
||||
.. _Ian Cordasco: http://www.coglib.com/~icordasc/
|
||||
.. _Ian Cordasco: https://www.coglib.com/~icordasc/
|
||||
.. _Nate Prewitt: https://www.nateprewitt.com/
|
||||
.. _Seth Michael Larson: https://sethmlarson.dev/
|
||||
|
||||
@@ -132,8 +132,8 @@ files and a semi-formal, yet friendly and approachable, prose style.
|
||||
When presenting Python code, use single-quoted strings (``'hello'`` instead of
|
||||
``"hello"``).
|
||||
|
||||
.. _reStructuredText: http://docutils.sourceforge.net/rst.html
|
||||
.. _Sphinx: http://sphinx-doc.org/index.html
|
||||
.. _reStructuredText: https://docutils.sourceforge.net/rst.html
|
||||
.. _Sphinx: https://www.sphinx-doc.org/en/master/
|
||||
|
||||
|
||||
.. _bug-reports:
|
||||
|
||||
@@ -65,7 +65,7 @@ if errorlevel 9009 (
|
||||
echo.may add the Sphinx directory to PATH.
|
||||
echo.
|
||||
echo.If you don't have Sphinx installed, grab it from
|
||||
echo.http://sphinx-doc.org/
|
||||
echo.https://www.sphinx-doc.org/
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
|
||||
@@ -467,6 +467,7 @@ Let's print some request method arguments at runtime::
|
||||
You can add multiple hooks to a single request. Let's call two hooks at once::
|
||||
|
||||
>>> r = requests.get('https://httpbin.org/', hooks={'response': [print_url, record_hook]})
|
||||
https://httpbin.org/
|
||||
>>> r.hook_called
|
||||
True
|
||||
|
||||
|
||||
@@ -37,9 +37,11 @@ classifiers = [
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Programming Language :: Python :: 3.14",
|
||||
"Programming Language :: Python :: 3.15",
|
||||
"Programming Language :: Python :: 3 :: Only",
|
||||
"Programming Language :: Python :: Implementation :: CPython",
|
||||
"Programming Language :: Python :: Implementation :: PyPy",
|
||||
"Programming Language :: Python :: Free Threading :: 2 - Beta",
|
||||
"Topic :: Internet :: WWW/HTTP",
|
||||
"Topic :: Software Development :: Libraries"
|
||||
]
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
__title__ = "requests"
|
||||
__description__ = "Python HTTP for Humans."
|
||||
__url__ = "https://requests.readthedocs.io"
|
||||
__version__ = "2.34.0.dev1"
|
||||
__build__ = 0x023400
|
||||
__version__ = "2.34.1"
|
||||
__build__ = 0x023401
|
||||
__author__ = "Kenneth Reitz"
|
||||
__author_email__ = "me@kennethreitz.org"
|
||||
__license__ = "Apache-2.0"
|
||||
|
||||
@@ -9,7 +9,7 @@ by external code.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable, Iterable, Mapping, MutableMapping
|
||||
from collections.abc import Callable, Iterable, Mapping, MutableMapping, Sequence
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
@@ -20,6 +20,8 @@ from typing import (
|
||||
)
|
||||
|
||||
_T_co = TypeVar("_T_co", covariant=True)
|
||||
_KT_co = TypeVar("_KT_co", covariant=True)
|
||||
_VT_co = TypeVar("_VT_co", covariant=True)
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
@@ -28,8 +30,8 @@ class SupportsRead(Protocol[_T_co]):
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class SupportsItems(Protocol):
|
||||
def items(self) -> Iterable[tuple[Any, Any]]: ...
|
||||
class SupportsItems(Protocol[_KT_co, _VT_co]):
|
||||
def items(self) -> Iterable[tuple[_KT_co, _VT_co]]: ...
|
||||
|
||||
|
||||
# These are needed at runtime for default_hooks() return type
|
||||
@@ -79,7 +81,7 @@ if TYPE_CHECKING:
|
||||
str | bytes | int | float | Iterable[str | bytes | int | float] | None
|
||||
)
|
||||
ParamsType: TypeAlias = (
|
||||
Mapping[_ParamsMappingKeyType, _ParamsMappingValueType]
|
||||
SupportsItems[_ParamsMappingKeyType, _ParamsMappingValueType]
|
||||
| tuple[tuple[_ParamsMappingKeyType, _ParamsMappingValueType], ...]
|
||||
| Iterable[tuple[_ParamsMappingKeyType, _ParamsMappingValueType]]
|
||||
| str
|
||||
@@ -87,7 +89,7 @@ if TYPE_CHECKING:
|
||||
| None
|
||||
)
|
||||
|
||||
KVDataType: TypeAlias = Iterable[tuple[Any, Any]] | Mapping[Any, Any]
|
||||
KVDataType: TypeAlias = Iterable[tuple[Any, Any]] | SupportsItems[Any, Any]
|
||||
|
||||
RawDataType: TypeAlias = KVDataType | str | bytes
|
||||
StreamDataType: TypeAlias = SupportsRead[str | bytes]
|
||||
@@ -107,8 +109,7 @@ if TYPE_CHECKING:
|
||||
bytes | str | Iterable[bytes | str] | SupportsRead[bytes | str] | None
|
||||
)
|
||||
|
||||
HeadersType: TypeAlias = CaseInsensitiveDict[str] | Mapping[str, str | bytes]
|
||||
HeadersUpdateType: TypeAlias = Mapping[str, str | bytes | None]
|
||||
HeadersType: TypeAlias = MutableMapping[str, str | bytes] | None
|
||||
|
||||
CookiesType: TypeAlias = RequestsCookieJar | Mapping[str, str]
|
||||
|
||||
@@ -137,20 +138,26 @@ if TYPE_CHECKING:
|
||||
VerifyType: TypeAlias = bool | str
|
||||
CertType: TypeAlias = str | tuple[str, str] | None
|
||||
JsonType: TypeAlias = (
|
||||
None | bool | int | float | str | list["JsonType"] | dict[str, "JsonType"]
|
||||
None
|
||||
| bool
|
||||
| int
|
||||
| float
|
||||
| str
|
||||
| Sequence["JsonType"]
|
||||
| Mapping[str, "JsonType"]
|
||||
)
|
||||
|
||||
# TypedDicts for Unpack kwargs (PEP 692)
|
||||
|
||||
class BaseRequestKwargs(TypedDict, total=False):
|
||||
headers: Mapping[str, str | bytes] | None
|
||||
headers: HeadersType
|
||||
cookies: RequestsCookieJar | CookieJar | dict[str, str] | None
|
||||
files: FilesType
|
||||
auth: AuthType
|
||||
timeout: TimeoutType
|
||||
allow_redirects: bool
|
||||
proxies: dict[str, str] | None
|
||||
hooks: HooksType
|
||||
hooks: HooksInputType | None
|
||||
stream: bool | None
|
||||
verify: VerifyType | None
|
||||
cert: CertType
|
||||
|
||||
@@ -590,8 +590,6 @@ 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)
|
||||
|
||||
@@ -83,6 +83,7 @@ from .utils import (
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import MutableMapping
|
||||
from http.cookiejar import CookieJar
|
||||
|
||||
from typing_extensions import Self
|
||||
@@ -310,7 +311,7 @@ class Request(RequestHooksMixin):
|
||||
|
||||
method: str | None
|
||||
url: _t.UriType | None
|
||||
headers: CaseInsensitiveDict[str] | Mapping[str, str | bytes] | None
|
||||
headers: MutableMapping[str, str | bytes]
|
||||
files: _t.FilesType
|
||||
data: _t.DataType
|
||||
json: _t.JsonType
|
||||
@@ -322,7 +323,7 @@ class Request(RequestHooksMixin):
|
||||
self,
|
||||
method: str | None = None,
|
||||
url: _t.UriType | None = None,
|
||||
headers: Mapping[str, str | bytes] | None = None,
|
||||
headers: _t.HeadersType = None,
|
||||
files: _t.FilesType = None,
|
||||
data: _t.DataType = None,
|
||||
params: _t.ParamsType = None,
|
||||
@@ -596,9 +597,9 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):
|
||||
if not isinstance(body, bytes):
|
||||
body = body.encode("utf-8")
|
||||
|
||||
if isinstance(data, Iterable) and not isinstance(
|
||||
data, (str, bytes, list, tuple, Mapping)
|
||||
):
|
||||
# data that proxies attributes to underlying objects needs hasattr
|
||||
is_iterable = isinstance(data, Iterable) or hasattr(data, "__iter__")
|
||||
if is_iterable and not isinstance(data, (str, bytes, list, tuple, Mapping)):
|
||||
try:
|
||||
length = super_len(data)
|
||||
except (TypeError, AttributeError, UnsupportedOperation):
|
||||
@@ -741,7 +742,7 @@ class Response:
|
||||
url: str
|
||||
encoding: str | None
|
||||
history: list[Response]
|
||||
reason: str | None
|
||||
reason: str
|
||||
cookies: RequestsCookieJar
|
||||
elapsed: datetime.timedelta
|
||||
request: PreparedRequest
|
||||
@@ -790,7 +791,7 @@ class Response:
|
||||
self.history = []
|
||||
|
||||
#: Textual reason of responded HTTP Status, e.g. "Not Found" or "OK".
|
||||
self.reason = None
|
||||
self.reason = None # type: ignore[assignment]
|
||||
|
||||
#: A CookieJar of Cookies the server sent back.
|
||||
self.cookies = cookiejar_from_dict({})
|
||||
|
||||
@@ -560,14 +560,14 @@ class Session(SessionRedirectMixin):
|
||||
url: _t.UriType,
|
||||
params: _t.ParamsType = None,
|
||||
data: _t.DataType = None,
|
||||
headers: Mapping[str, str | bytes] | None = None,
|
||||
headers: _t.HeadersType = None,
|
||||
cookies: RequestsCookieJar | CookieJar | dict[str, str] | None = None,
|
||||
files: _t.FilesType = None,
|
||||
auth: _t.AuthType = None,
|
||||
timeout: _t.TimeoutType = None,
|
||||
allow_redirects: bool = True,
|
||||
proxies: dict[str, str] | None = None,
|
||||
hooks: _t.HooksType = None,
|
||||
hooks: _t.HooksInputType | None = None,
|
||||
stream: bool | None = None,
|
||||
verify: _t.VerifyType | None = None,
|
||||
cert: _t.CertType = None,
|
||||
@@ -652,16 +652,23 @@ class Session(SessionRedirectMixin):
|
||||
|
||||
return resp
|
||||
|
||||
def get(self, url: _t.UriType, **kwargs: Unpack[_t.GetKwargs]) -> Response:
|
||||
def get(
|
||||
self,
|
||||
url: _t.UriType,
|
||||
params: _t.ParamsType = None,
|
||||
**kwargs: Unpack[_t.GetKwargs],
|
||||
) -> Response:
|
||||
r"""Sends a GET request. Returns :class:`Response` object.
|
||||
|
||||
:param url: URL for the new :class:`Request` object.
|
||||
:param params: (optional) Dictionary, list of tuples or bytes to send
|
||||
in the query string for the :class:`Request`.
|
||||
:param \*\*kwargs: Optional arguments that ``request`` takes.
|
||||
:rtype: requests.Response
|
||||
"""
|
||||
|
||||
kwargs.setdefault("allow_redirects", True)
|
||||
return self.request("GET", url, **kwargs)
|
||||
return self.request("GET", url, params=params, **kwargs)
|
||||
|
||||
def options(self, url: _t.UriType, **kwargs: Unpack[_t.RequestKwargs]) -> Response:
|
||||
r"""Sends a OPTIONS request. Returns :class:`Response` object.
|
||||
|
||||
@@ -147,7 +147,7 @@ if sys.platform == "win32":
|
||||
|
||||
|
||||
def dict_to_sequence(
|
||||
d: _t.SupportsItems | Iterable[tuple[Any, Any]],
|
||||
d: _t.SupportsItems[Any, Any] | Iterable[tuple[Any, Any]],
|
||||
) -> Iterable[tuple[Any, Any]]:
|
||||
"""Returns an internal sequence dictionary update."""
|
||||
|
||||
@@ -371,10 +371,10 @@ def from_key_val_list(
|
||||
def to_key_val_list(value: None) -> None: ...
|
||||
@overload
|
||||
def to_key_val_list(
|
||||
value: Mapping[_KT, _VT] | Iterable[tuple[_KT, _VT]],
|
||||
value: _t.SupportsItems[_KT, _VT] | Iterable[tuple[_KT, _VT]],
|
||||
) -> list[tuple[_KT, _VT]]: ...
|
||||
def to_key_val_list(
|
||||
value: Mapping[_KT, _VT] | Iterable[tuple[_KT, _VT]] | None,
|
||||
value: _t.SupportsItems[_KT, _VT] | Iterable[tuple[_KT, _VT]] | None,
|
||||
) -> list[tuple[_KT, _VT]] | None:
|
||||
"""Take an object and test to see if it can be represented as a
|
||||
dictionary. If it can be, return a list of tuples, e.g.,
|
||||
@@ -851,9 +851,11 @@ def should_bypass_proxies(url: str, no_proxy: str | None) -> bool:
|
||||
host_with_port += f":{parsed.port}"
|
||||
|
||||
for host in no_proxy_hosts:
|
||||
host = host.lstrip(".")
|
||||
if hostname == host or host_with_port == host:
|
||||
return True
|
||||
host = "." + host
|
||||
if hostname.endswith(host) or host_with_port.endswith(host):
|
||||
# The URL does match something in no_proxy, so we don't want
|
||||
# to apply the proxies on this URL.
|
||||
return True
|
||||
|
||||
with set_environ("no_proxy", no_proxy_arg):
|
||||
|
||||
@@ -22,6 +22,15 @@ def prepare_url(value):
|
||||
return inner
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def clean_proxy_environ(monkeypatch):
|
||||
"""Remove proxy related environment variables for every test."""
|
||||
proxy_vars = ("http_proxy", "https_proxy", "no_proxy", "ftp_proxy", "all_proxy")
|
||||
for var in proxy_vars:
|
||||
monkeypatch.delenv(var, raising=False)
|
||||
monkeypatch.delenv(var.upper(), raising=False)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def httpbin(httpbin):
|
||||
return prepare_url(httpbin)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import requests.adapters
|
||||
|
||||
|
||||
def test_request_url_trims_leading_path_separators():
|
||||
def test_request_url_handles_leading_path_separators():
|
||||
"""See also https://github.com/psf/requests/issues/6643."""
|
||||
a = requests.adapters.HTTPAdapter()
|
||||
p = requests.Request(method="GET", url="http://127.0.0.1:10000//v:h").prepare()
|
||||
assert "/v:h" == a.request_url(p, {})
|
||||
assert "//v:h" == a.request_url(p, {})
|
||||
|
||||
@@ -2073,6 +2073,21 @@ class TestRequests:
|
||||
|
||||
assert "Unable to rewind request body" in str(e)
|
||||
|
||||
def test_getattr_proxy_stream_follows_redirect(self, httpbin):
|
||||
"""Ensure stream wrappers that don't implement __iter__ directly are still detected."""
|
||||
|
||||
class AttrProxy:
|
||||
def __init__(self):
|
||||
self._file = io.BytesIO(b"data")
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self._file, name)
|
||||
|
||||
r = requests.post(
|
||||
httpbin("redirect-to?url=/post&status_code=307"), data=AttrProxy()
|
||||
)
|
||||
assert r.json()["data"] == "data"
|
||||
|
||||
def _patch_adapter_gzipped_redirect(self, session, url):
|
||||
adapter = session.get_adapter(url=url)
|
||||
org_build_response = adapter.build_response
|
||||
|
||||
@@ -844,6 +844,29 @@ def test_should_bypass_proxies_no_proxy(url, expected, monkeypatch):
|
||||
assert should_bypass_proxies(url, no_proxy=no_proxy) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"url, expected",
|
||||
(
|
||||
("http://localhost/", True),
|
||||
("http://anotherdomain.com:8888/", True),
|
||||
("http://newdomain.com:1234/", True),
|
||||
("http://www.newdomain.com:1234/", True),
|
||||
("http://foo.d.o.t/", True),
|
||||
("http://d.o.t/", True),
|
||||
("http://prelocalhost/", False),
|
||||
("http://newdomain.com/", False),
|
||||
("http://newdomain.com:1235/", False),
|
||||
),
|
||||
)
|
||||
def test_should_bypass_proxies_no_proxy_domain_boundary(url, expected):
|
||||
"""Ensure no_proxy matching respects domain boundaries and does not
|
||||
greedily match domains that merely endswith the no_proxy entry.
|
||||
See CPython bpo-39057.
|
||||
"""
|
||||
no_proxy = "localhost, anotherdomain.com, newdomain.com:1234, .d.o.t"
|
||||
assert should_bypass_proxies(url, no_proxy=no_proxy) == expected
|
||||
|
||||
|
||||
@pytest.mark.skipif(os.name != "nt", reason="Test only on Windows")
|
||||
@pytest.mark.parametrize(
|
||||
"url, expected, override",
|
||||
|
||||
Reference in New Issue
Block a user