Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add HTTPX url #156

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
70 changes: 70 additions & 0 deletions pydantic_extra_types/httpx_url.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"""convenient URL type compatible with httpx and pydantic mostly useful in settings for base url of httpx client"""
from typing import Any

from pydantic import GetCoreSchemaHandler, GetJsonSchemaHandler
from pydantic_core import core_schema

try:
from httpx import URL as URL_HTTPX
from httpx._exceptions import InvalidURL
from httpx._urlparse import MAX_URL_LENGTH
except ImportError:
raise RuntimeError(
'The `httpx` module requires "httpx" to be installed. You can install it with "pip install ' 'httpx".'
)


class URL(URL_HTTPX):
"""URL parses URL in the [httpx.URL](https://www.python-httpx.org/api/#url) format.

```py
from pydantic import BaseModel

from pydantic_extra_types.httpx_url import URL

class Website(BaseModel):
url: URL

website = Website(url='https://www.example.com')
print(website)
# > url='https://www.example.com'
```
"""

@classmethod
def _validate(cls, url: str, _: core_schema.ValidationInfo) -> 'URL':
"""
Validate a URL from the provided str value.

Args:
url: The str value to be validated.
_: The Pydantic ValidationInfo.

Returns:
The validated URL.
"""
try:
return cls(url)
except InvalidURL as e:
raise ValueError(str(e))

@classmethod
def __get_pydantic_core_schema__(cls, type: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
"""
duplicate of the `AnyUrl` schema, but with the `URL` type from httpx.
"""
return core_schema.with_info_after_validator_function(
cls._validate,
core_schema.str_schema(min_length=1, max_length=MAX_URL_LENGTH),
serialization=core_schema.to_string_ser_schema(),
)

@classmethod
def __get_pydantic_json_schema__(
cls, schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler
) -> dict[str, Any]:
"""
duplicate of the `AnyUrl` schema, but with the `URL` type from httpx.
"""
json_schema = handler(schema)
return json_schema
1 change: 1 addition & 0 deletions requirements/testing.in
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ pytest
codecov
pytest-cov
pytest-pretty
httpx
1 change: 1 addition & 0 deletions requirements/testing.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,4 @@ rich==13.7.0
# via pytest-pretty
urllib3==2.2.1
# via requests
httpx
24 changes: 24 additions & 0 deletions tests/test_httpx_url.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import pytest
from httpx import URL as URL_HTTPX
from pydantic import BaseModel, ValidationError

from pydantic_extra_types.httpx_url import URL

from .too_long_url import TOO_LONG_URL


class CheckingModel(BaseModel):
url: URL


def test_url_ok():
model = CheckingModel(url='https://www.example.com')
assert isinstance(model.url, URL_HTTPX)
assert model.url == 'https://www.example.com'
assert model.model_dump() == {'url': 'https://www.example.com'} # test serialization


@pytest.mark.parametrize('invalid_url', ['', '\0', TOO_LONG_URL])
def test_url_fail(invalid_url: str):
with pytest.raises(ValidationError):
CheckingModel(url='\0')
1 change: 1 addition & 0 deletions tests/too_long_url.py

Large diffs are not rendered by default.