stompman


Namestompman JSON
Version 1.4.0 PyPI version JSON
download
home_pageNone
SummaryPython STOMP client with pleasant API
upload_time2024-08-29 13:17:27
maintainerNone
docs_urlNone
authorNone
requires_python>=3.11
licenseMIT
keywords activemq artemis jms messaging stomp
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # stompman

A Python client for STOMP asynchronous messaging protocol that is:

- asynchronous,
- not abandoned,
- has typed, modern, comprehensible API.

## How To Use

Before you start using stompman, make sure you have it installed:

```sh
pip install stompman
poetry add stompman
uv add stompman
```

Initialize a client:

```python
async with stompman.Client(
    servers=[
        stompman.ConnectionParameters(host="171.0.0.1", port=61616, login="user1", passcode="passcode1"),
        stompman.ConnectionParameters(host="172.0.0.1", port=61616, login="user2", passcode="passcode2"),
    ],

    # Handlers:
    on_error_frame=lambda error_frame: print(error_frame.body),
    on_heartbeat=lambda: print("Server sent a heartbeat"),

    # SSL — can be either `None` (default), `True`, or `ssl.SSLContext'
    ssl=None,

    # Optional parameters with sensible defaults:
    heartbeat=stompman.Heartbeat(will_send_interval_ms=1000, want_to_receive_interval_ms=1000),
    connect_retry_attempts=3,
    connect_retry_interval=1,
    connect_timeout=2,
    connection_confirmation_timeout=2,
    disconnect_confirmation_timeout=2,
    read_timeout=2,
    write_retry_attempts=3,
) as client:
    ...
```

### Sending Messages

To send a message, use the following code:

```python
await client.send(b"hi there!", destination="DLQ", headers={"persistent": "true"})
```

Or, to send messages in a transaction:

```python
async with client.begin() as transaction:
    for _ in range(10):
        await transaction.send(body=b"hi there!", destination="DLQ", headers={"persistent": "true"})
        await asyncio.sleep(0.1)
```

### Listening for Messages

Now, let's subscribe to a destination and listen for messages:

```python
async def handle_message_from_dlq(message_frame: stompman.MessageFrame) -> None:
    print(message_frame.body)


await client.subscribe("DLQ", handle_message_from_dlq, on_suppressed_exception=print)
```

Entered `stompman.Client` will block forever waiting for messages if there are any active subscriptions.

Sometimes it's useful to avoid that:

```python
dlq_subscription = await client.subscribe("DLQ", handle_message_from_dlq, on_suppressed_exception=print)
await dlq_subscription.unsubscribe()
```

By default, subscription have ACK mode "client-individual". If handler successfully processes the message, an `ACK` frame will be sent. If handler raises an exception, a `NACK` frame will be sent. You can catch (and log) exceptions using `on_suppressed_exception` parameter:

```python
await client.subscribe(
    "DLQ",
    handle_message_from_dlq,
    on_suppressed_exception=lambda exception, message_frame: print(exception, message_frame),
)
```

You can change the ack mode used by specifying the `ack` parameter:

```python
# Server will assume that all messages sent to the subscription before the ACK'ed message are received and processed:
await client.subscribe("DLQ", handle_message_from_dlq, ack="client", on_suppressed_exception=print)

# Server will assume that messages are received as soon as it send them to client:
await client.subscribe("DLQ", handle_message_from_dlq, ack="auto", on_suppressed_exception=print)
```

You can pass custom headers to `client.subscribe()`:

```python
await client.subscribe("DLQ", handle_message_from_dlq, ack="client", headers={"selector": "location = 'Europe'"}, on_suppressed_exception=print)
```

### Cleaning Up

stompman takes care of cleaning up resources automatically. When you leave the context of async context managers `stompman.Client()`, or `client.begin()`, the necessary frames will be sent to the server.

### Handling Connectivity Issues

- If multiple servers were provided, stompman will attempt to connect to each one simultaneously and will use the first that succeeds. If all servers fail to connect, an `stompman.FailedAllConnectAttemptsError` will be raised. In normal situation it doesn't need to be handled: tune retry and timeout parameters in `stompman.Client()` to your needs.

- When connection is lost, stompman will attempt to handle it automatically. `stompman.FailedAllConnectAttemptsError` will be raised if all connection attempts fail. `stompman.FailedAllWriteAttemptsError` will be raised if connection succeeds but sending a frame or heartbeat lead to losing connection.

### ...and caveats

- stompman supports Python 3.11 and newer.
- It implements [STOMP 1.2](https://stomp.github.io/stomp-specification-1.2.html) — the latest version of the protocol.
- Heartbeats are required, and sent automatically in background (defaults to 1 second).

Also, I want to pointed out that:

- Protocol parsing is inspired by [aiostomp](https://github.com/pedrokiefer/aiostomp/blob/3449dcb53f43e5956ccc7662bb5b7d76bc6ef36b/aiostomp/protocol.py) (meaning: consumed by me and refactored from).
- stompman is tested and used with [Artemis ActiveMQ](https://activemq.apache.org/components/artemis/).
- Specification says that headers in CONNECT and CONNECTED frames shouldn't be escaped for backwards compatibility. stompman escapes headers in CONNECT frame (outcoming), but does not unescape headers in CONNECTED (outcoming).

### Examples

See producer and consumer examples in [testing/](./testing/).

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "stompman",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.11",
    "maintainer_email": null,
    "keywords": "activemq, artemis, jms, messaging, stomp",
    "author": null,
    "author_email": "Lev Vereshchagin <mail@vrslev.com>",
    "download_url": "https://files.pythonhosted.org/packages/7f/67/e8a4c3826baf1ea7ab6c5d1b4e8efd25e2340445d269f605340fffbae6e2/stompman-1.4.0.tar.gz",
    "platform": null,
    "description": "# stompman\n\nA Python client for STOMP asynchronous messaging protocol that is:\n\n- asynchronous,\n- not abandoned,\n- has typed, modern, comprehensible API.\n\n## How To Use\n\nBefore you start using stompman, make sure you have it installed:\n\n```sh\npip install stompman\npoetry add stompman\nuv add stompman\n```\n\nInitialize a client:\n\n```python\nasync with stompman.Client(\n    servers=[\n        stompman.ConnectionParameters(host=\"171.0.0.1\", port=61616, login=\"user1\", passcode=\"passcode1\"),\n        stompman.ConnectionParameters(host=\"172.0.0.1\", port=61616, login=\"user2\", passcode=\"passcode2\"),\n    ],\n\n    # Handlers:\n    on_error_frame=lambda error_frame: print(error_frame.body),\n    on_heartbeat=lambda: print(\"Server sent a heartbeat\"),\n\n    # SSL \u2014 can be either `None` (default), `True`, or `ssl.SSLContext'\n    ssl=None,\n\n    # Optional parameters with sensible defaults:\n    heartbeat=stompman.Heartbeat(will_send_interval_ms=1000, want_to_receive_interval_ms=1000),\n    connect_retry_attempts=3,\n    connect_retry_interval=1,\n    connect_timeout=2,\n    connection_confirmation_timeout=2,\n    disconnect_confirmation_timeout=2,\n    read_timeout=2,\n    write_retry_attempts=3,\n) as client:\n    ...\n```\n\n### Sending Messages\n\nTo send a message, use the following code:\n\n```python\nawait client.send(b\"hi there!\", destination=\"DLQ\", headers={\"persistent\": \"true\"})\n```\n\nOr, to send messages in a transaction:\n\n```python\nasync with client.begin() as transaction:\n    for _ in range(10):\n        await transaction.send(body=b\"hi there!\", destination=\"DLQ\", headers={\"persistent\": \"true\"})\n        await asyncio.sleep(0.1)\n```\n\n### Listening for Messages\n\nNow, let's subscribe to a destination and listen for messages:\n\n```python\nasync def handle_message_from_dlq(message_frame: stompman.MessageFrame) -> None:\n    print(message_frame.body)\n\n\nawait client.subscribe(\"DLQ\", handle_message_from_dlq, on_suppressed_exception=print)\n```\n\nEntered `stompman.Client` will block forever waiting for messages if there are any active subscriptions.\n\nSometimes it's useful to avoid that:\n\n```python\ndlq_subscription = await client.subscribe(\"DLQ\", handle_message_from_dlq, on_suppressed_exception=print)\nawait dlq_subscription.unsubscribe()\n```\n\nBy default, subscription have ACK mode \"client-individual\". If handler successfully processes the message, an `ACK` frame will be sent. If handler raises an exception, a `NACK` frame will be sent. You can catch (and log) exceptions using `on_suppressed_exception` parameter:\n\n```python\nawait client.subscribe(\n    \"DLQ\",\n    handle_message_from_dlq,\n    on_suppressed_exception=lambda exception, message_frame: print(exception, message_frame),\n)\n```\n\nYou can change the ack mode used by specifying the `ack` parameter:\n\n```python\n# Server will assume that all messages sent to the subscription before the ACK'ed message are received and processed:\nawait client.subscribe(\"DLQ\", handle_message_from_dlq, ack=\"client\", on_suppressed_exception=print)\n\n# Server will assume that messages are received as soon as it send them to client:\nawait client.subscribe(\"DLQ\", handle_message_from_dlq, ack=\"auto\", on_suppressed_exception=print)\n```\n\nYou can pass custom headers to `client.subscribe()`:\n\n```python\nawait client.subscribe(\"DLQ\", handle_message_from_dlq, ack=\"client\", headers={\"selector\": \"location = 'Europe'\"}, on_suppressed_exception=print)\n```\n\n### Cleaning Up\n\nstompman takes care of cleaning up resources automatically. When you leave the context of async context managers `stompman.Client()`, or `client.begin()`, the necessary frames will be sent to the server.\n\n### Handling Connectivity Issues\n\n- If multiple servers were provided, stompman will attempt to connect to each one simultaneously and will use the first that succeeds. If all servers fail to connect, an `stompman.FailedAllConnectAttemptsError` will be raised. In normal situation it doesn't need to be handled: tune retry and timeout parameters in `stompman.Client()` to your needs.\n\n- When connection is lost, stompman will attempt to handle it automatically. `stompman.FailedAllConnectAttemptsError` will be raised if all connection attempts fail. `stompman.FailedAllWriteAttemptsError` will be raised if connection succeeds but sending a frame or heartbeat lead to losing connection.\n\n### ...and caveats\n\n- stompman supports Python 3.11 and newer.\n- It implements [STOMP 1.2](https://stomp.github.io/stomp-specification-1.2.html) \u2014 the latest version of the protocol.\n- Heartbeats are required, and sent automatically in background (defaults to 1 second).\n\nAlso, I want to pointed out that:\n\n- Protocol parsing is inspired by [aiostomp](https://github.com/pedrokiefer/aiostomp/blob/3449dcb53f43e5956ccc7662bb5b7d76bc6ef36b/aiostomp/protocol.py) (meaning: consumed by me and refactored from).\n- stompman is tested and used with [Artemis ActiveMQ](https://activemq.apache.org/components/artemis/).\n- Specification says that headers in CONNECT and CONNECTED frames shouldn't be escaped for backwards compatibility. stompman escapes headers in CONNECT frame (outcoming), but does not unescape headers in CONNECTED (outcoming).\n\n### Examples\n\nSee producer and consumer examples in [testing/](./testing/).\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Python STOMP client with pleasant API",
    "version": "1.4.0",
    "project_urls": {
        "repository": "https://github.com/vrslev/stompman"
    },
    "split_keywords": [
        "activemq",
        " artemis",
        " jms",
        " messaging",
        " stomp"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "12ce743e1446b984fb76e6783117ba2169d4e912feeb6f17b24235cf95051d19",
                "md5": "111116170c3654b492f3eb312027d507",
                "sha256": "16451bc3f4e1edc4c17c425a04670abe8896e458df5be5b5c6e353503ddfd3b4"
            },
            "downloads": -1,
            "filename": "stompman-1.4.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "111116170c3654b492f3eb312027d507",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.11",
            "size": 17813,
            "upload_time": "2024-08-29T13:17:26",
            "upload_time_iso_8601": "2024-08-29T13:17:26.813586Z",
            "url": "https://files.pythonhosted.org/packages/12/ce/743e1446b984fb76e6783117ba2169d4e912feeb6f17b24235cf95051d19/stompman-1.4.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "7f67e8a4c3826baf1ea7ab6c5d1b4e8efd25e2340445d269f605340fffbae6e2",
                "md5": "e640d8831a6f3d7277d00268ab732b0b",
                "sha256": "cbe00c7fb6b02cf694e0288a60a7f459e3bac61b0d8736eaaf2d2487ce38414b"
            },
            "downloads": -1,
            "filename": "stompman-1.4.0.tar.gz",
            "has_sig": false,
            "md5_digest": "e640d8831a6f3d7277d00268ab732b0b",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.11",
            "size": 24882,
            "upload_time": "2024-08-29T13:17:27",
            "upload_time_iso_8601": "2024-08-29T13:17:27.740429Z",
            "url": "https://files.pythonhosted.org/packages/7f/67/e8a4c3826baf1ea7ab6c5d1b4e8efd25e2340445d269f605340fffbae6e2/stompman-1.4.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-08-29 13:17:27",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "vrslev",
    "github_project": "stompman",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "stompman"
}
        
Elapsed time: 0.57185s