8000 Fix host required enforcement for urls to be compatible with v2.9 behavior by sydney-runkle · Pull Request #11027 · pydantic/pydantic · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
initial commit - reverting to 2.9 behavior in terms of host required …
…vs not required
  • Loading branch information
sydney-runkle committed Dec 2, 2024
commit e46677a0425f5e57fb884a0548bc1c207a35a48a
80 changes: 32 additions & 48 deletions pydantic/networks.py
< 8000 /tr>
Original file line number Diff line number Diff line change
Expand Up @@ -456,13 +456,13 @@ class AnyUrl(_BaseUrl):

* Any scheme allowed
* Top-level domain (TLD) not required
* Host required
* Host not required

Assuming an input URL of `http://samuel:pass@example.com:8000/the/path/?query=here#fragment=is;this=bit`,
the types export the following properties:

- `scheme`: the URL scheme (`http`), always set.
- `host`: the URL host (`example.com`), always set.
- `host`: the URL host (`example.com`).
- `username`: optional username if included (`samuel`).
- `password`: optional password if included (`pass`).
- `port`: optional port (`8000`).
Expand All @@ -471,13 +471,6 @@ class AnyUrl(_BaseUrl):
- `fragment`: optional fragment (`fragment=is;this=bit`).
"""

_constraints = UrlConstraints(host_required=True)

@property
def host(self) -> str:
"""The required URL host."""
return self._url.host # pyright: ignore[reportReturnType]


# Note: all single host urls inherit from `AnyUrl` to preserve compatibility with pre-v2.10 code
# Where urls were annotated variants of `AnyUrl`, which was an alias to `pydantic_core.Url`
Expand All @@ -487,17 +480,17 @@ class AnyHttpUrl(AnyUrl):
"""A type that will accept any http or https URL.

* TLD not required
* Host required
* Host not required
"""

_constraints = UrlConstraints(host_required=True, allowed_schemes=['http', 'https'])
_constraints = UrlConstraints(allowed_schemes=['http', 'https'])


class HttpUrl(AnyUrl):
"""A type that will accept any http or https URL.

* TLD not required
* Host required
* Host not required
* Max length 2083

```python
Expand Down Expand Up @@ -571,33 +564,28 @@ class MyModel(BaseModel):
(or at least big) company.
"""

_constraints = UrlConstraints(max_length=2083, allowed_schemes=['http', 'https'], host_required=True)
_constraints = UrlConstraints(max_length=2083, allowed_schemes=['http', 'https'])


class AnyWebsocketUrl(AnyUrl):
"""A type that will accept any ws or wss URL.

* TLD not required
* Host required
* Host not required
"""

_constraints = UrlConstraints(allowed_schemes=['ws', 'wss'], host_required=True)
_constraints = UrlConstraints(allowed_schemes=['ws', 'wss'])


class WebsocketUrl(AnyUrl):
"""A type that will accept any ws or wss URL.

* TLD not required
* Host required
* Host not required
* Max length 2083
"""

_constraints = UrlConstraints(max_length=2083, allowed_schemes=['ws', 'wss'], host_required=True)

@property
def host(self) -> str:
"""The required URL host."""
return self._url.host # type: ignore
_constraints = UrlConstraints(max_length=2083, allowed_schemes=['ws', 'wss'])


class FileUrl(AnyUrl):
Expand All @@ -608,25 +596,15 @@ class FileUrl(AnyUrl):

_constraints = UrlConstraints(allowed_schemes=['file'])

@property
def host(self) -> str | None: # pyright: ignore[reportIncompatibleMethodOverride]
"""The host part of the URL, or `None`."""
return self._url.host


class FtpUrl(AnyUrl):
"""A type that will accept ftp URL.

* TLD not required
* Host required
* Host not required
"""

_constraints = UrlConstraints(allowed_schemes=['ftp'], host_required=True)

@property
def host(self) -> str | None: # pyright: ignore[reportIncompatibleMethodOverride]
"""The host part of the URL, or `None`."""
return self._url.host
_constraints = UrlConstraints(allowed_schemes=['ftp'])


class PostgresDsn(_BaseMultiHostUrl):
Expand Down Expand Up @@ -727,6 +705,11 @@ class CockroachDsn(AnyUrl):
],
)

@property
def host(self) -> str:
"""The required URL host."""
return self._url.host # pyright: ignore[reportReturnType]


class AmqpDsn(AnyUrl):
"""A type that will accept any AMQP DSN.
Expand All @@ -738,11 +721,6 @@ class AmqpDsn(AnyUrl):

_constraints = UrlConstraints(allowed_schemes=['amqp', 'amqps'])

@property
def host(self) -> str | None: # pyright: ignore[reportIncompatibleMethodOverride]
"""The host part of the URL, or `None`."""
return self._url.host


class RedisDsn(AnyUrl):
"""A type that will accept any Redis DSN.
Expand All @@ -760,6 +738,11 @@ class RedisDsn(AnyUrl):
host_required=True,
)

@property
def host(self) -> str:
"""The required URL host."""
return self._url.host # pyright: ignore[reportReturnType]


class MongoDsn(_BaseMultiHostUrl):
"""A type that will accept any MongoDB DSN.
Expand All @@ -778,12 +761,10 @@ class KafkaDsn(AnyUrl):

* User info required
* TLD not required
* Host required
* Host not required
"""

_constraints = UrlConstraints(
allowed_schemes=['kafka'], default_host='localhost', default_port=9092, host_required=True
)
_constraints = UrlConstraints(allowed_schemes=['kafka'], default_host='localhost', default_port=9092)


class NatsDsn(_BaseMultiHostUrl):
Expand All @@ -805,7 +786,7 @@ class MySQLDsn(AnyUrl):

* User info required
* TLD not required
* Host required
* Host not required
"""

_constraints = UrlConstraints(
Expand All @@ -829,13 +810,12 @@ class MariaDBDsn(AnyUrl):

* User info required
* TLD not required
* Host required
* Host not required
"""

_constraints = UrlConstraints(
allowed_schemes=['mariadb', 'mariadb+mariadbconnector', 'mariadb+pymysql'],
default_port=3306,
host_required=True,
)


Expand All @@ -844,14 +824,13 @@ class ClickHouseDsn(AnyUrl):

* User info required
* TLD not required
* Host required
* Host not required
"""

_constraints = UrlConstraints(
allowed_schemes=['clickhouse+native', 'clickhouse+asynch'],
default_host='localhost',
default_port=9000,
host_required=True,
)


Expand All @@ -868,6 +847,11 @@ class SnowflakeDsn(AnyUrl):
host_required=True,
)

@property
def host(self) -> str:
"""The required URL host."""
return self._url.host # pyright: ignore[reportReturnType]


def import_email_validator() -> None:
global email_validator
Expand Down
6 changes: 6 additions & 0 deletions tests/test_networks.py
Original file line number Diff line number Diff line change
Expand Up @@ -1129,3 +1129,9 @@ def test_any_url_hashable() -> None:
assert hash(example_multi_host_url_1a) == hash(example_multi_host_url_1b)
assert hash(example_multi_host_url_1a) != hash(example_multi_host_url_2)
assert len({example_multi_host_url_1a, example_multi_host_url_1b, example_multi_host_url_2}) == 2


def test_host_not_required_for_2_9_compatibility() -> None:
data_uri = 'file:///path/to/data'
url = AnyUrl(data_uri)
assert url.host is None
Loading
0