Skip to content

Commit

Permalink
Revert percent encoding in URLs. (#4470)
Browse files Browse the repository at this point in the history
* Revert "Fix `AnyUrl.build` doesn't do percent encoding (#3061) (#4224)"

This reverts commit e34ff92.

* change and warning
  • Loading branch information
samuelcolvin committed Sep 5, 2022
1 parent 02cf7f5 commit a4367c1
Show file tree
Hide file tree
Showing 5 changed files with 10 additions and 57 deletions.
1 change: 1 addition & 0 deletions changes/4470-samuelcolvin.md
@@ -0,0 +1 @@
**Revert Change:** Revert percent encoding of URL parts which was originally added in #4224.
10 changes: 0 additions & 10 deletions docs/examples/types_url_building.py

This file was deleted.

20 changes: 5 additions & 15 deletions docs/usage/types.md
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down
17 changes: 4 additions & 13 deletions pydantic/networks.py
Expand Up @@ -26,7 +26,6 @@
cast,
no_type_check,
)
from urllib.parse import quote, quote_plus

from . import errors
from .utils import Representation, update_not_none
Expand Down Expand Up @@ -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')

Expand Down Expand Up @@ -241,19 +239,18 @@ def build(

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
Expand Down Expand Up @@ -394,10 +391,6 @@ def apply_default_parts(cls, parts: 'Parts') -> 'Parts':
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})'
Expand Down Expand Up @@ -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(
Expand All @@ -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)

Expand Down
19 changes: 0 additions & 19 deletions tests/test_networks.py
Expand Up @@ -679,31 +679,12 @@ class Model2(BaseModel):
(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',
[
Expand Down

0 comments on commit a4367c1

Please sign in to comment.