Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
374de81
CI: Bump pinned BlazingMQ tag to v0.95.20
pniedzielski Sep 4, 2026
09317c3
provide a way to create credential
emelialei88 Dec 8, 2025
d437981
picking this up again
pniedzielski Jun 22, 2026
7d9303d
Provide `DefaultAuthnCredentialCb`
pniedzielski Jun 23, 2026
721b762
Fix: Compile error from AuthnCredential API
pniedzielski Jun 26, 2026
5c9a43a
Fix: Fully qualify `AuthnCredentialCb`
pniedzielski Jun 26, 2026
c53d028
Fix: Add `fake_authn_credential_cb` value to failing tests
pniedzielski Jun 26, 2026
c98534a
Fix: Test `authn_credential_provider` in `SessionOptions`
pniedzielski Jun 26, 2026
04e7876
Test: Add tests for `ExtSession` construction
pniedzielski Jun 26, 2026
38e111f
Fix: Format `DefaultAuthnCredentialProvider`
pniedzielski Jun 26, 2026
c58f5f5
Fix: Update `AuthnCredential` in bmqt.pxd
pniedzielski Jun 26, 2026
c1b400e
Rename `FakeAuthnCredentialCb`
pniedzielski Jun 30, 2026
46585dc
Fix `isort` order
pniedzielski Jun 30, 2026
c9d0f74
Add `AuthnCredentialProvider` type alias
pniedzielski Jun 30, 2026
97926e9
clang-format C++ code
pniedzielski Jun 30, 2026
ed28229
Add documentation for `AuthnCredentialProvider`
pniedzielski Jul 1, 2026
306436a
Fix: Remove `ostream& error` from authn callback
pniedzielski Aug 31, 2026
f772b39
Remove lambda for C++03 compat
pniedzielski Sep 1, 2026
4364768
Move `authn_credential_provider` argument to avoid API break
pniedzielski Sep 1, 2026
8c92a6f
Fix: clarify docstring for `authn_credential_provider`
pniedzielski Sep 1, 2026
9327cc7
Style: Move UTF-8 encoding logic into Cython from C++
pniedzielski Sep 1, 2026
9573080
Fix: Simplify authn callback guard and bump copyright years
pniedzielski Sep 1, 2026
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
2 changes: 1 addition & 1 deletion bin/clone-dependencies.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ set -u
BDE_TOOLS_TAG=4.38.0.0
BDE_TAG=4.38.0.0
NTF_CORE_TAG=2.6.12
BLAZINGMQ_TAG=v0.95.14
BLAZINGMQ_TAG=v0.95.20


if [ ! -d "${DIR_THIRDPARTY}/bde-tools" ]; then
Expand Down
2 changes: 2 additions & 0 deletions docs/api_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ Testing Utilities
Helper Types
============

.. autoclass:: blazingmq.AuthnCredentialProvider

.. autoclass:: blazingmq.PropertyTypeDict

.. autoclass:: blazingmq.PropertyValueDict
4 changes: 3 additions & 1 deletion src/blazingmq/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2019-2023 Bloomberg Finance L.P.
# Copyright 2019-2026 Bloomberg Finance L.P.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -27,13 +27,15 @@
from ._session import Session
from ._session import SessionOptions
from ._timeouts import Timeouts
from ._typing import AuthnCredentialProvider
from ._typing import PropertyTypeDict
from ._typing import PropertyValueDict
from .exceptions import Error

__all__ = [
"Ack",
"AckStatus",
"AuthnCredentialProvider",
"BasicHealthMonitor",
"CompressionAlgorithmType",
"Error",
Expand Down
6 changes: 5 additions & 1 deletion src/blazingmq/_ext.pyi
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2019-2023 Bloomberg Finance L.P.
# Copyright 2019-2026 Bloomberg Finance L.P.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -37,6 +37,9 @@ class FakeHostHealthMonitor:
def set_healthy(self) -> None: ...
def set_unhealthy(self) -> None: ...

class AuthnCredentialCbAdapter:
def __init__(self, callback: Callable[[], Optional[tuple[str, bytes]]]) -> None: ...

class Session:
def __init__(
self,
Expand All @@ -53,6 +56,7 @@ class Session:
timeouts: Timeouts = Timeouts(),
monitor_host_health: bool = False,
fake_host_health_monitor: Optional[FakeHostHealthMonitor] = None,
authn_credential_cb: Optional[AuthnCredentialCbAdapter] = None,
user_agent_prefix: bytes = b"",
) -> None: ...
def stop(self) -> None: ...
Expand Down
35 changes: 34 additions & 1 deletion src/blazingmq/_ext.pyx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2019-2023 Bloomberg Finance L.P.
# Copyright 2019-2026 Bloomberg Finance L.P.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -154,6 +154,37 @@ cdef class FakeHostHealthMonitor:
self._monitor.get().setState(HostHealthState.e_UNHEALTHY)


cdef class AuthnCredentialCbAdapter:
cdef object _callback

def __cinit__(self, callback):
self._callback = callback

def get_credential_data(self):
"""Call the provider and marshal its result for the C++ session.

Called by ``pybmq::AuthnCredentialCbFunctor``. Returns the mechanism
and data as a tuple of ``bytes``, or `None` if credentials could not
be obtained, in which case authentication fails.
"""
try:
result = self._callback()
if result is None:
return None

mechanism, data = result
if not isinstance(mechanism, str) or not isinstance(data, bytes):
raise TypeError(
"authn_credential_provider must return (str, bytes) or None"
)

return mechanism.encode('utf-8'), data

except Exception:
LOGGER.exception("Error in authentication credential callback")
return None


cdef class Session:
cdef object __weakref__
cdef NativeSession* _session
Expand All @@ -175,6 +206,7 @@ cdef class Session:
timeouts: _timeouts.Timeouts = _timeouts.Timeouts(),
monitor_host_health: bool = False,
fake_host_health_monitor: FakeHostHealthMonitor = None,
authn_credential_cb: AuthnCredentialCbAdapter = None,
_mock: Optional[object] = None,
user_agent_prefix: bytes = b"",
) -> None:
Expand Down Expand Up @@ -249,6 +281,7 @@ cdef class Session:
session_cb,
message_cb,
ack_cb,
authn_credential_cb,
config,
fake_host_health_monitor_sp,
Error,
Expand Down
42 changes: 41 additions & 1 deletion src/blazingmq/_session.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2019-2023 Bloomberg Finance L.P.
# Copyright 2019-2026 Bloomberg Finance L.P.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
Expand Down Expand Up @@ -27,6 +27,7 @@
from ._about import __version__
from ._enums import CompressionAlgorithmType
from ._enums import PropertyType
from ._ext import AuthnCredentialCbAdapter
from ._ext import DEFAULT_CONSUMER_PRIORITY
from ._ext import DEFAULT_MAX_UNCONFIRMED_BYTES
from ._ext import DEFAULT_MAX_UNCONFIRMED_MESSAGES
Expand All @@ -38,6 +39,7 @@
from ._messages import MessageHandle
from ._monitors import BasicHealthMonitor
from ._timeouts import Timeouts
from ._typing import AuthnCredentialProvider
from ._typing import PropertyTypeDict
from ._typing import PropertyValueDict
from ._typing import PropertyValueType
Expand All @@ -54,6 +56,10 @@ def DefaultMonitor() -> Union[BasicHealthMonitor, None]:
return None


def DefaultAuthnCredentialProvider() -> Optional[AuthnCredentialProvider]:
return None


DEFAULT_TIMEOUT = DefaultTimeoutType()
KNOWN_MONITORS = ("blazingmq.BasicHealthMonitor",)

Expand Down Expand Up @@ -331,6 +337,15 @@ class SessionOptions:
96 bytes long. This is provided for libraries that are wrapping
this SDK. Applications directly using the SDK are encouraged *NOT*
to set this value.
authn_credential_provider (Optional[`~blazingmq.AuthnCredentialProvider`]):
An optional callable that returns authentication credentials as a
``(mechanism, data)`` tuple of ``(str, bytes)``. It is called
each time the session authenticates with the broker, including on
reauthentication. If it returns ``None`` or raises, the
connection is closed: starting a session fails, while an
already-started session sees `.ConnectionLost` and then
reconnects, calling this callable again. If not provided, no
authentication credentials are sent to the broker.
"""

def __init__(
Expand All @@ -344,6 +359,9 @@ def __init__(
event_queue_watermarks: Optional[tuple[int, int]] = None,
stats_dump_interval: Optional[float] = None,
user_agent_prefix: Optional[bytes] = None,
authn_credential_provider: Optional[AuthnCredentialProvider] = (
DefaultAuthnCredentialProvider()
),
) -> None:
self.message_compression_algorithm = message_compression_algorithm
self.timeouts = timeouts
Expand All @@ -354,6 +372,7 @@ def __init__(
self.event_queue_watermarks = event_queue_watermarks
self.stats_dump_interval = stats_dump_interval
self.user_agent_prefix = user_agent_prefix
self.authn_credential_provider = authn_credential_provider

def __eq__(self, other: object) -> bool:
if not isinstance(other, SessionOptions):
Expand All @@ -368,6 +387,7 @@ def __eq__(self, other: object) -> bool:
and self.event_queue_watermarks == other.event_queue_watermarks
and self.stats_dump_interval == other.stats_dump_interval
and self.user_agent_prefix == other.user_agent_prefix
and self.authn_credential_provider == other.authn_credential_provider
)

def __ne__(self, other: object) -> bool:
Expand All @@ -384,6 +404,7 @@ def __repr__(self) -> str:
"event_queue_watermarks",
"stats_dump_interval",
"user_agent_prefix",
"authn_credential_provider",
)

params = []
Expand Down Expand Up @@ -451,6 +472,15 @@ class Session:
must be at most 96 bytes long. This is provided for libraries
that are wrapping this SDK. Applications directly using the SDK
are encouraged *NOT* to set this value.
authn_credential_provider (Optional[`~blazingmq.AuthnCredentialProvider`]):
an optional callable that returns authentication credentials as a
``(mechanism, data)`` tuple of ``(str, bytes)``. It is called
each time the session authenticates with the broker, including on
reauthentication. If it returns ``None`` or raises, the
connection is closed: starting a session fails, while an
already-started session sees `.ConnectionLost` and then
reconnects, calling this callable again. If not provided, no
authentication credentials are sent to the broker.

Raises:
`~blazingmq.Error`: If the session start request was not successful.
Expand All @@ -476,6 +506,9 @@ def __init__(
event_queue_watermarks: Optional[tuple[int, int]] = None,
stats_dump_interval: Optional[float] = None,
user_agent_prefix: Optional[bytes] = None,
authn_credential_provider: Optional[AuthnCredentialProvider] = (
DefaultAuthnCredentialProvider()
),
) -> None:
if host_health_monitor is not None:
if not isinstance(host_health_monitor, BasicHealthMonitor):
Expand All @@ -486,6 +519,11 @@ def __init__(

monitor_host_health = host_health_monitor is not None
fake_host_health_monitor = getattr(host_health_monitor, "_monitor", None)
authn_credential_cb = (
AuthnCredentialCbAdapter(authn_credential_provider)
if authn_credential_provider is not None
else None
)

self._has_no_on_message = on_message is None

Expand Down Expand Up @@ -515,6 +553,7 @@ def __init__(
timeouts=_validate_timeouts(timeout),
monitor_host_health=monitor_host_health,
fake_host_health_monitor=fake_host_health_monitor,
authn_credential_cb=authn_credential_cb,
user_agent_prefix=_make_user_agent_prefix(user_agent_prefix),
)
self._ext.set_owned_by_session()
Expand Down Expand Up @@ -577,6 +616,7 @@ def with_options(
event_queue_watermarks=session_options.event_queue_watermarks,
stats_dump_interval=session_options.stats_dump_interval,
user_agent_prefix=session_options.user_agent_prefix,
authn_credential_provider=session_options.authn_credential_provider,
)

def open_queue(
Expand Down
11 changes: 10 additions & 1 deletion src/blazingmq/_typing.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2019-2023 Bloomberg Finance L.P.
# Copyright 2019-2026 Bloomberg Finance L.P.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -13,7 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import Callable
from typing import Mapping
from typing import Optional
from typing import Union

from ._enums import PropertyType
Expand All @@ -23,3 +25,10 @@
PropertyValueDict = Mapping[str, PropertyValueType]

PropertyTypeDict = Mapping[str, PropertyType]

AuthnCredentialProvider = Callable[[], Optional[tuple[str, bytes]]]
"""A callable that returns authentication credentials as a ``(mechanism,
data)`` tuple of ``(str, bytes)``, or ``None`` if an error occurs while
obtaining them, in which case authentication fails and the connection is
closed.
"""
Loading
Loading