Skip to content
Closed
6 changes: 6 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,12 @@ what gets sent over the wire.*
>>> url = URL("https://example.org/")
>>> url.host
'example.org'

>>> from pydantic import HttpUrl
>>> pydantic_url = HttpUrl("https://example.org/")
>>> url = URL(pydantic_url)
>>> url.host
'example.org'
```

* `def __init__(url, **kwargs)`
Expand Down
20 changes: 11 additions & 9 deletions httpx/_urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@
import idna

from ._types import QueryParamTypes
from ._urlparse import urlparse
from ._urlparse import ParseResult, urlparse
from ._utils import primitive_value_to_str

__all__ = ["URL", "QueryParams"]


class HasDunderStr(typing.Protocol):
def __str__(self) -> str: ...


class URL:
"""
url = httpx.URL("HTTPS://jo%40email.com:a%20secret@müller.de:1234/pa%20th?search=ab#anchorlink")
Expand Down Expand Up @@ -74,7 +78,9 @@ class URL:
themselves.
"""

def __init__(self, url: URL | str = "", **kwargs: typing.Any) -> None:
def __init__(
self, url: URL | str | HasDunderStr = "", **kwargs: typing.Any
) -> None:
if kwargs:
allowed = {
"scheme": str,
Expand Down Expand Up @@ -113,15 +119,11 @@ def __init__(self, url: URL | str = "", **kwargs: typing.Any) -> None:
params = kwargs.pop("params")
kwargs["query"] = None if not params else str(QueryParams(params))

if isinstance(url, str):
self._uri_reference = urlparse(url, **kwargs)
elif isinstance(url, URL):
self._uri_reference: ParseResult
if isinstance(url, URL):
self._uri_reference = url._uri_reference.copy_with(**kwargs)
else:
raise TypeError(
"Invalid type for url. Expected str or httpx.URL,"
f" got {type(url)}: {url!r}"
)
self._uri_reference = urlparse(str(url), **kwargs)

@property
def scheme(self) -> str:
Expand Down
18 changes: 8 additions & 10 deletions tests/models/test_url.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,19 +452,17 @@ def test_url_set():
assert all(url in urls for url in url_set)


# Tests for TypeErrors when instantiating `httpx.URL`.

def test_custom_object_url():
class ExternalURLClass:
def __str__(self):
return "https://www.example.com/"

def test_url_invalid_type():
"""
Ensure that invalid types on `httpx.URL()` raise a `TypeError`.
"""
url = ExternalURLClass()
httpx_url = httpx.URL(url)
assert httpx_url == "https://www.example.com/"

class ExternalURLClass: # representing external URL class
pass

with pytest.raises(TypeError):
httpx.URL(ExternalURLClass()) # type: ignore
# Tests for TypeErrors when instantiating `httpx.URL`.


def test_url_with_invalid_component():
Expand Down
Loading