diff --git a/changes/4458-vetedde.md b/changes/4458-vetedde.md new file mode 100644 index 0000000000..4b3b1c774e --- /dev/null +++ b/changes/4458-vetedde.md @@ -0,0 +1 @@ +Fix AnyUrl escaping problem by preventing re-quoting an already escaped URL. diff --git a/pydantic/networks.py b/pydantic/networks.py index 725e4f8a27..0a6d1735d7 100644 --- a/pydantic/networks.py +++ b/pydantic/networks.py @@ -396,6 +396,10 @@ def apply_default_parts(cls, parts: 'Parts') -> 'Parts': @classmethod def quote(cls, string: str, safe: str = '') -> str: + pattern = r'^([\w]+|(%\d{2}))+$' + if re.match(pattern, string) is not None: + return string + return quote_plus(string, safe) if cls.quote_plus else quote(string, safe) def __repr__(self) -> str: diff --git a/tests/test_networks.py b/tests/test_networks.py index 95af277b35..87927115f6 100644 --- a/tests/test_networks.py +++ b/tests/test_networks.py @@ -683,6 +683,8 @@ class Model2(BaseModel): (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'), + (dict(scheme='http', host='localhost', path='/foo bar'), 'http://localhost/foo%20bar'), + (dict(scheme='http', host='localhost', path='/foo%20bar'), 'http://localhost/foo%20bar'), ], ) def test_build_url(kwargs, expected):