Skip to content

Commit e526382

Browse files
committed
Subclass all single host url classes from AnyUrl to preserve behavior from v2.9 (#10856)
1 parent c62d2d5 commit e526382

File tree

2 files changed

+45
-61
lines changed

2 files changed

+45
-61
lines changed

pydantic/networks.py

+35-61
Original file line numberDiff line numberDiff line change
@@ -475,10 +475,14 @@ class AnyUrl(_BaseUrl):
475475
@property
476476
def host(self) -> str:
477477
"""The required URL host."""
478-
return self._url.host # type: ignore
478+
return self._url.host # pyright: ignore[reportReturnType]
479+
479480

481+
# Note: all single host urls inherit from `AnyUrl` to preserve compatibility with pre-v2.10 code
482+
# Where urls were annotated variants of `AnyUrl`, which was an alias to `pydantic_core.Url`
480483

481-
class AnyHttpUrl(_BaseUrl):
484+
485+
class AnyHttpUrl(AnyUrl):
482486
"""A type that will accept any http or https URL.
483487
484488
* TLD not required
@@ -487,13 +491,8 @@ class AnyHttpUrl(_BaseUrl):
487491

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

490-
@property
491-
def host(self) -> str:
492-
"""The required URL host."""
493-
return self._url.host # type: ignore
494-
495494

496-
class HttpUrl(_BaseUrl):
495+
class HttpUrl(AnyUrl):
497496
"""A type that will accept any http or https URL.
498497
499498
* TLD not required
@@ -573,13 +572,8 @@ class MyModel(BaseModel):
573572

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

576-
@property
577-
def host(self) -> str:
578-
"""The required URL host."""
579-
return self._url.host # type: ignore
580575

581-
582-
class AnyWebsocketUrl(_BaseUrl):
576+
class AnyWebsocketUrl(AnyUrl):
583577
"""A type that will accept any ws or wss URL.
584578
585579
* TLD not required
@@ -588,13 +582,8 @@ class AnyWebsocketUrl(_BaseUrl):
588582

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

591-
@property
592-
def host(self) -> str:
593-
"""The required URL host."""
594-
return self._url.host # type: ignore
595585

596-
597-
class WebsocketUrl(_BaseUrl):
586+
class WebsocketUrl(AnyUrl):
598587
"""A type that will accept any ws or wss URL.
599588
600589
* TLD not required
@@ -610,16 +599,21 @@ def host(self) -> str:
610599
return self._url.host # type: ignore
611600

612601

613-
class FileUrl(_BaseUrl):
602+
class FileUrl(AnyUrl):
614603
"""A type that will accept any file URL.
615604
616605
* Host not required
617606
"""
618607

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

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

622-
class FtpUrl(_BaseUrl):
616+
class FtpUrl(AnyUrl):
623617
"""A type that will accept ftp URL.
624618
625619
* TLD not required
@@ -628,6 +622,11 @@ class FtpUrl(_BaseUrl):
628622

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

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

632631
class PostgresDsn(_BaseMultiHostUrl):
633632
"""A type that will accept any Postgres DSN.
@@ -707,10 +706,10 @@ def check_db_name(cls, v):
707706
@property
708707
def host(self) -> str:
709708
"""The required URL host."""
710-
return self._url.host # type: ignore
709+
return self._url.host # pyright: ignore[reportAttributeAccessIssue]
711710

712711

713-
class CockroachDsn(_BaseUrl):
712+
class CockroachDsn(AnyUrl):
714713
"""A type that will accept any Cockroach DSN.
715714
716715
* User info required
@@ -727,13 +726,8 @@ class CockroachDsn(_BaseUrl):
727726
],
728727
)
729728

730-
@property
731-
def host(self) -> str:
732-
"""The required URL host."""
733-
return self._url.host # type: ignore
734-
735729

736-
class AmqpDsn(_BaseUrl):
730+
class AmqpDsn(AnyUrl):
737731
"""A type that will accept any AMQP DSN.
738732
739733
* User info required
@@ -743,8 +737,13 @@ class AmqpDsn(_BaseUrl):
743737

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

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

747-
class RedisDsn(_BaseUrl):
746+
class RedisDsn(AnyUrl):
748747
"""A type that will accept any Redis DSN.
749748
750749
* User info required
@@ -760,11 +759,6 @@ class RedisDsn(_BaseUrl):
760759
host_required=True,
761760
)
762761

763-
@property
764-
def host(self) -> str:
765-
"""The required URL host."""
766-
return self._url.host # type: ignore
767-
768762

769763
class MongoDsn(_BaseMultiHostUrl):
770764
"""A type that will accept any MongoDB DSN.
@@ -778,7 +772,7 @@ class MongoDsn(_BaseMultiHostUrl):
778772
_constraints = UrlConstraints(allowed_schemes=['mongodb', 'mongodb+srv'], default_port=27017)
779773

780774

781-
class KafkaDsn(_BaseUrl):
775+
class KafkaDsn(AnyUrl):
782776
"""A type that will accept any Kafka DSN.
783777
784778
* User info required
@@ -805,7 +799,7 @@ class NatsDsn(_BaseMultiHostUrl):
805799
)
806800

807801

808-
class MySQLDsn(_BaseUrl):
802+
class MySQLDsn(AnyUrl):
809803
"""A type that will accept any MySQL DSN.
810804
811805
* User info required
@@ -828,13 +822,8 @@ class MySQLDsn(_BaseUrl):
828822
host_required=True,
829823
)
830824

831-
@property
832-
def host(self) -> str:
833-
"""The required URL host."""
834-
return self._url.host # type: ignore
835-
836825

837-
class MariaDBDsn(_BaseUrl):
826+
class MariaDBDsn(AnyUrl):
838827
"""A type that will accept any MariaDB DSN.
839828
840829
* User info required
@@ -848,13 +837,8 @@ class MariaDBDsn(_BaseUrl):
848837
host_required=True,
849838
)
850839

851-
@property
852-
def host(self) -> str:
853-
"""The required URL host."""
854-
return self._url.host # type: ignore
855-
856840

857-
class ClickHouseDsn(_BaseUrl):
841+
class ClickHouseDsn(AnyUrl):
858842
"""A type that will accept any ClickHouse DSN.
859843
860844
* User info required
@@ -869,13 +853,8 @@ class ClickHouseDsn(_BaseUrl):
869853
host_required=True,
870854
)
871855

872-
@property
873-
def host(self) -> str:
874-
"""The required URL host."""
875-
return self._url.host # type: ignore
876-
877856

878-
class SnowflakeDsn(_BaseUrl):
857+
class SnowflakeDsn(AnyUrl):
879858
"""A type that will accept any Snowflake DSN.
880859
881860
* User info required
@@ -888,11 +867,6 @@ class SnowflakeDsn(_BaseUrl):
888867
host_required=True,
889868
)
890869

891-
@property
892-
def host(self) -> str:
893-
"""The required URL host."""
894-
return self._url.host # type: ignore
895-
896870

897871
def import_email_validator() -> None:
898872
global email_validator

tests/test_networks.py

+10
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from pydantic import (
99
AmqpDsn,
10+
AnyHttpUrl,
1011
AnyUrl,
1112
BaseModel,
1213
ClickHouseDsn,
@@ -1062,3 +1063,12 @@ def test_url_equality() -> None:
10621063
assert PostgresDsn('postgres://user:pass@localhost:5432/app') == PostgresDsn(
10631064
'postgres://user:pass@localhost:5432/app'
10641065
)
1066+
1067+
1068+
def test_url_subclasses_any_url() -> None:
1069+
http_url = AnyHttpUrl('https://localhost')
1070+
assert isinstance(http_url, AnyUrl)
1071+
assert isinstance(http_url, AnyHttpUrl)
1072+
1073+
url = TypeAdapter(AnyUrl).validate_python(http_url)
1074+
assert url is http_url

0 commit comments

Comments
 (0)