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
4 changes: 3 additions & 1 deletion doc/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ PyMongo 4.19 brings a number of changes including:
:class:`~pymongo.asynchronous.mongo_client.AsyncMongoClient`, an alternative to
``srvAllowedHostsSuffix`` for deployments whose acceptable SRV hosts cannot be
expressed as a single suffix. The callback is invoked once per SRV-returned
host and returns ``True`` to accept it. It is mutually exclusive with
host and must return a boolean: ``True`` to accept the host and ``False`` to
reject it. During initial DNS discovery a non-boolean return raises an error;
during SRV polling it is treated as a rejection. It is mutually exclusive with
``srvAllowedHostsSuffix`` and, because it takes a callable, cannot be set in a
connection string. See the
:class:`~pymongo.synchronous.mongo_client.MongoClient` and
Expand Down
4 changes: 3 additions & 1 deletion pymongo/asynchronous/mongo_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,9 @@ def __init__(
argument, and must return ``True`` to accept the host or ``False`` to
reject it. During initial DNS discovery, rejecting a host raises
:exc:`~pymongo.errors.ConfigurationError`, as does an exception raised by
the callback itself. Use this when the set of acceptable hosts cannot be
the callback itself or a return value that is not a boolean. During SRV
polling, a rejection or a non-boolean return is treated as ``False`` and no
error is raised. Use this when the set of acceptable hosts cannot be
expressed as a single suffix::

def validator(host: str) -> bool:
Expand Down
5 changes: 5 additions & 0 deletions pymongo/asynchronous/srv_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,11 @@ def _validate_host(self, srv_host: str) -> None:
raise ConfigurationError(
f"srv_host_validator raised an exception for SRV host {srv_host}: {exc}"
) from exc
if not isinstance(allowed, bool):
raise ConfigurationError(
f"srv_host_validator must return a bool for SRV host {srv_host}, "
f"not {type(allowed).__name__}"
)
if not allowed:
raise ConfigurationError(
f"Invalid SRV host: {srv_host} was rejected by srv_host_validator"
Expand Down
4 changes: 3 additions & 1 deletion pymongo/synchronous/mongo_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,9 @@ def __init__(
argument, and must return ``True`` to accept the host or ``False`` to
reject it. During initial DNS discovery, rejecting a host raises
:exc:`~pymongo.errors.ConfigurationError`, as does an exception raised by
the callback itself. Use this when the set of acceptable hosts cannot be
the callback itself or a return value that is not a boolean. During SRV
polling, a rejection or a non-boolean return is treated as ``False`` and no
error is raised. Use this when the set of acceptable hosts cannot be
expressed as a single suffix::

def validator(host: str) -> bool:
Expand Down
5 changes: 5 additions & 0 deletions pymongo/synchronous/srv_resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,11 @@ def _validate_host(self, srv_host: str) -> None:
raise ConfigurationError(
f"srv_host_validator raised an exception for SRV host {srv_host}: {exc}"
) from exc
if not isinstance(allowed, bool):
raise ConfigurationError(
f"srv_host_validator must return a bool for SRV host {srv_host}, "
f"not {type(allowed).__name__}"
)
if not allowed:
raise ConfigurationError(
f"Invalid SRV host: {srv_host} was rejected by srv_host_validator"
Expand Down
14 changes: 14 additions & 0 deletions test/asynchronous/test_dns.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,20 @@ async def test_13_throw_when_srv_host_validator_is_used_with_a_non_srv_uri(self)
connect=False,
)

async def test_14_throw_when_srv_host_validator_returns_a_non_boolean_value(self):
with self.assertRaisesRegex(ConfigurationError, "srv_host_validator must return a bool"):
await self._parse(
"blogs.mongodb.com", "cluster.mongodb.com", srv_host_validator=lambda host: "true"
)

async def test_15_accept_an_underscore_in_srv_allowed_hosts_suffix(self):
res = await self._parse(
"blogs.my_domain.net",
"cluster.my_domain.net",
srv_allowed_hosts_suffix=".my_domain.net",
)
self.assertEqual(["cluster.my_domain.net"], [node[0] for node in res["nodelist"]])

async def test_srv_hostname_with_three_or_more_parts_may_equal_the_returned_hostname(
self,
):
Expand Down
14 changes: 14 additions & 0 deletions test/test_dns.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,20 @@ def test_13_throw_when_srv_host_validator_is_used_with_a_non_srv_uri(self):
connect=False,
)

def test_14_throw_when_srv_host_validator_returns_a_non_boolean_value(self):
with self.assertRaisesRegex(ConfigurationError, "srv_host_validator must return a bool"):
self._parse(
"blogs.mongodb.com", "cluster.mongodb.com", srv_host_validator=lambda host: "true"
)

def test_15_accept_an_underscore_in_srv_allowed_hosts_suffix(self):
res = self._parse(
"blogs.my_domain.net",
"cluster.my_domain.net",
srv_allowed_hosts_suffix=".my_domain.net",
)
self.assertEqual(["cluster.my_domain.net"], [node[0] for node in res["nodelist"]])

def test_srv_hostname_with_three_or_more_parts_may_equal_the_returned_hostname(
self,
):
Expand Down
Loading