Revert percent encoding in URLs. (#4470)

* Revert "Fix `AnyUrl.build` doesn't do percent encoding (#3061) (#4224)"

This reverts commit e34ff9255f.

* change and warning
This commit is contained in:
Samuel Colvin
2022-09-05 11:02:43 +01:00
committed by GitHub
parent 02cf7f5d7b
commit a4367c1c58
5 changed files with 10 additions and 57 deletions
+1
View File
@@ -0,0 +1 @@
**Revert Change:** Revert percent encoding of URL parts which was originally added in #4224.
-10
View File
@@ -1,10 +0,0 @@
from pydantic import AnyUrl, stricturl
url = AnyUrl.build(scheme='https', host='example.com', query='query=my query')
print(url)
my_url_builder = stricturl(quote_plus=True)
url = my_url_builder.build(
scheme='http', host='example.com', query='query=my query'
)
print(url)
+5 -15
View File
@@ -616,7 +616,11 @@ For URI/URL validation the following types are available:
- `tld_required: bool = True`
- `host_required: bool = True`
- `allowed_schemes: Optional[Set[str]] = None`
- `quote_plus: bool = False`
!!! warning
In V1.10.0 and v1.10.1 `stricturl` also took an optional `quote_plus` argument and URL components were percent
encoded in some cases. This feature was removed in v1.10.2, see
[#4470](https://github.com/pydantic/pydantic/pull/4470) for explanation and more details.
The above types (which all inherit from `AnyUrl`) will attempt to give descriptive errors when invalid URLs are
provided:
@@ -679,20 +683,6 @@ If further validation is required, these properties can be used by validators to
Also, Chrome, Firefox, and Safari all currently accept `http://exam_ple.com` as a URL, so we're in good
(or at least big) company.
#### Building URLs
You can build URLs from separate [URL Properties](#url-properties) using the `build` method in
[Pydantic URL types](#urls) or any type that inherits from them.
By default, *pydantic* percent encodes the following URL properties: `user`, `password`, `path`, `query`
as per [RFC 3986](https://www.ietf.org/rfc/rfc3986.txt) without replacing spaces with `+` but this can
be changed using the `stricturl` method:
!!! note
Percent encoding was added in V1.10
{!.tmp_examples/types_url_building.md!}
### Color Type
You can use the `Color` data type for storing colors as per
+4 -13
View File
@@ -26,7 +26,6 @@ from typing import (
cast,
no_type_check,
)
from urllib.parse import quote, quote_plus
from . import errors
from .utils import Representation, update_not_none
@@ -178,7 +177,6 @@ class AnyUrl(str):
user_required: bool = False
host_required: bool = True
hidden_parts: Set[str] = set()
quote_plus: bool = False
__slots__ = ('scheme', 'user', 'password', 'host', 'tld', 'host_type', 'port', 'path', 'query', 'fragment')
@@ -241,19 +239,18 @@ class AnyUrl(str):
url = scheme + '://'
if user:
url += cls.quote(user)
url += user
if password:
url += ':' + cls.quote(password)
url += ':' + password
if user or password:
url += '@'
url += host
if port and ('port' not in cls.hidden_parts or cls.get_default_parts(parts).get('port') != port):
url += ':' + port
if path:
url += '/'.join(map(cls.quote, path.split('/')))
url += path
if query:
queries = query.split('&')
url += '?' + '&'.join(map(lambda s: '='.join(map(cls.quote, s.split('='))), queries))
url += '?' + query
if fragment:
url += '#' + fragment
return url
@@ -394,10 +391,6 @@ class AnyUrl(str):
parts[key] = value # type: ignore[literal-required]
return parts
@classmethod
def quote(cls, string: str, safe: str = '') -> str:
return quote_plus(string, safe) if cls.quote_plus else quote(string, safe)
def __repr__(self) -> str:
extra = ', '.join(f'{n}={getattr(self, n)!r}' for n in self.__slots__ if getattr(self, n) is not None)
return f'{self.__class__.__name__}({super().__repr__()}, {extra})'
@@ -565,7 +558,6 @@ def stricturl(
tld_required: bool = True,
host_required: bool = True,
allowed_schemes: Optional[Collection[str]] = None,
quote_plus: bool = False,
) -> Type[AnyUrl]:
# use kwargs then define conf in a dict to aid with IDE type hinting
namespace = dict(
@@ -575,7 +567,6 @@ def stricturl(
tld_required=tld_required,
host_required=host_required,
allowed_schemes=allowed_schemes,
quote_plus=quote_plus,
)
return type('UrlValue', (AnyUrl,), namespace)
-19
View File
@@ -679,31 +679,12 @@ def test_custom_schemes():
(dict(scheme='ws', user='foo', password='x', host='example.net'), 'ws://foo:x@example.net'),
(dict(scheme='ws', host='example.net', query='a=b', fragment='c=d'), 'ws://example.net?a=b#c=d'),
(dict(scheme='http', host='example.net', port='1234'), 'http://example.net:1234'),
(dict(scheme='http', user='foo@bar', host='example.net'), 'http://foo%40bar@example.net'),
(dict(scheme='http', user='foo', password='a b', host='example.net'), 'http://foo:a%20b@example.net'),
(dict(scheme='http', host='example.net', query='q=foo bar'), 'http://example.net?q=foo%20bar'),
(dict(scheme='http', host='example.net', path="/m&m's"), 'http://example.net/m%26m%27s'),
],
)
def test_build_url(kwargs, expected):
assert AnyUrl(None, **kwargs) == expected
@pytest.mark.parametrize(
'kwargs,expected',
[
(dict(scheme='https', host='example.com', query='query=my query'), 'https://example.com?query=my+query'),
(
dict(scheme='https', host='example.com', user='my name', password='a password'),
'https://my+name:a+password@example.com',
),
(dict(scheme='https', host='example.com', path='/this is a path'), 'https://example.com/this+is+a+path'),
],
)
def test_build_url_quote_plus(kwargs, expected):
assert stricturl(quote_plus=True).build(**kwargs) == expected
@pytest.mark.parametrize(
'kwargs,expected',
[