DefStream-RabbitMQ 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,119 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from ... import (
4
+ Sendable,
5
+ Readable,
6
+ )
7
+ from .Serializer import (
8
+ Encoder,
9
+ Decoder,
10
+ )
11
+ from .Event import Event
12
+
13
+ from pika import (
14
+ BlockingConnection,
15
+ BasicProperties,
16
+ )
17
+
18
+ from time import time
19
+ from uuid import uuid4
20
+
21
+ from typing import (
22
+ Dict,
23
+ Iterable,
24
+ )
25
+
26
+ __all__ = (
27
+ 'Queue'
28
+ )
29
+
30
+
31
+ class Stream(
32
+ Sendable,
33
+ Readable,
34
+ ):
35
+ """Stream of Event Broker for RabbitMQ"""
36
+ def __init__(
37
+ self,
38
+ connection: BlockingConnection,
39
+ encode: Encoder,
40
+ decode: Decoder,
41
+ name: str = None,
42
+ ):
43
+ self.connection = connection
44
+ self.encode = encode
45
+ self.decode = decode
46
+ self.channel = self.connection.channel()
47
+ self.channel.auto_decode = False
48
+ self.queue = name
49
+ self.event = None
50
+ return
51
+
52
+ def __del__(self):
53
+ if self.channel and self.channel.is_open:
54
+ self.channel.close()
55
+ return
56
+
57
+ def __str__(self): return self.queue
58
+
59
+ def send(
60
+ self,
61
+ body,
62
+ event: str = None,
63
+ headers: Dict = {},
64
+ timestamp: int = None,
65
+ id: str = None,
66
+ ):
67
+ if not timestamp: timestamp = int(time())
68
+ if not id: id = uuid4().hex
69
+ properties = BasicProperties(
70
+ type=event if event else '',
71
+ headers=headers,
72
+ content_type=self.encode.format,
73
+ content_encoding=self.encode.charset,
74
+ timestamp=timestamp,
75
+ message_id=id,
76
+ )
77
+ self.channel.basic_publish(
78
+ exchange='',
79
+ routing_key=self.queue,
80
+ properties=properties,
81
+ body=self.encode(body)
82
+ )
83
+ return id
84
+
85
+ def read(self, offset: int = 0, timeout: int = None) -> Iterable[Event]:
86
+ channel = self.channel
87
+ channel.basic_qos(0, 1, False)
88
+
89
+ deadline = time() + (timeout / 1000) if timeout else None
90
+
91
+ try:
92
+ for method, properties, body in channel.consume(
93
+ queue=self.queue,
94
+ auto_ack=False,
95
+ inactivity_timeout=1,
96
+ arguments={'x-stream-offset': offset},
97
+ ):
98
+ if method is None:
99
+ if deadline and time() >= deadline:
100
+ break
101
+ continue
102
+
103
+ event = Event(
104
+ channel,
105
+ self.queue,
106
+ method.consumer_tag,
107
+ method.delivery_tag,
108
+ properties,
109
+ self.decode(body, properties.content_type, properties.content_encoding),
110
+ )
111
+
112
+ try:
113
+ yield event
114
+ channel.basic_ack(delivery_tag=method.delivery_tag)
115
+ except Exception:
116
+ channel.basic_nack(delivery_tag=method.delivery_tag, requeue=True)
117
+ raise
118
+ finally:
119
+ channel.cancel()
@@ -0,0 +1,47 @@
1
+ from collections.abc import Mapping
2
+ from dataclasses import dataclass
3
+ from typing import (
4
+ Any,
5
+ Optional,
6
+ )
7
+
8
+ __all__ = (
9
+ 'Subscription',
10
+ )
11
+
12
+
13
+ @dataclass(frozen=True)
14
+ class Subscription:
15
+ queue: str
16
+ offset: Optional[int] = None
17
+ qos: Optional[int] = None
18
+ error: Optional[str] = None
19
+
20
+ def __post_init__(self):
21
+ if not isinstance(self.queue, str) or not self.queue:
22
+ raise ValueError('Subscription queue must be a non-empty string')
23
+ if self.error is not None:
24
+ if not isinstance(
25
+ self.error,
26
+ str,
27
+ ) or not self.error:
28
+ raise ValueError('Subscription error must be a non-empty string or None')
29
+ if self.offset is not None and (isinstance(self.offset, bool) or not isinstance(self.offset, int)):
30
+ raise ValueError('Subscription offset must be an integer')
31
+ if self.qos is not None:
32
+ if isinstance(
33
+ self.qos,
34
+ bool,
35
+ ) or not isinstance(
36
+ self.qos,
37
+ int,
38
+ ) or not 0 <= self.qos <= 65535:
39
+ raise ValueError('Subscription qos must be an integer between 0 and 65535')
40
+
41
+ @classmethod
42
+ def parse(cls, parameters: Any) -> 'Subscription':
43
+ if isinstance(parameters, cls):
44
+ return parameters
45
+ if isinstance(parameters, Mapping):
46
+ return cls(**dict(parameters))
47
+ raise TypeError('RabbitMQ subscriptions require a Subscription or mapping')
@@ -0,0 +1,47 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from .Configuration import Configuration
4
+ from .Connection import (
5
+ Connection,
6
+ ExchangeType,
7
+ Parameters,
8
+ All,
9
+ Any,
10
+ )
11
+ from .Exchange import Exchange
12
+ from .Queue import Queue
13
+ from .Stream import Stream
14
+ from .Consumer import Consumer
15
+ from .Subscription import Subscription
16
+ from .Policies import (
17
+ Requeue,
18
+ Reject,
19
+ )
20
+ from .Event import (
21
+ Event,
22
+ EventHandler,
23
+ )
24
+ from .Serializer import (
25
+ Encoder,
26
+ Decoder,
27
+ )
28
+
29
+ __all__ = (
30
+ 'Configuration',
31
+ 'Connection',
32
+ 'ExchangeType',
33
+ 'Parameters',
34
+ 'All',
35
+ 'Any',
36
+ 'Exchange',
37
+ 'Queue',
38
+ 'Stream',
39
+ 'Consumer',
40
+ 'Subscription',
41
+ 'Requeue',
42
+ 'Reject',
43
+ 'Event',
44
+ 'EventHandler',
45
+ 'Encoder',
46
+ 'Decoder',
47
+ )
@@ -0,0 +1,143 @@
1
+ Metadata-Version: 2.4
2
+ Name: DefStream-RabbitMQ
3
+ Version: 0.1.1
4
+ Summary: RabbitMQ broker adapter for DefStream
5
+ Home-page: https://github.com/yong5eon/DefStream-RabbitMQ
6
+ Author: Heo Yongseon
7
+ Project-URL: Source, https://github.com/yong5eon/DefStream-RabbitMQ
8
+ Project-URL: Issues, https://github.com/yong5eon/DefStream-RabbitMQ/issues
9
+ Classifier: Programming Language :: Python :: 3.9
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Requires-Python: >=3.9
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: DefStream>=0.1.0
19
+ Requires-Dist: pika>=1.3.2
20
+ Dynamic: author
21
+ Dynamic: classifier
22
+ Dynamic: description
23
+ Dynamic: description-content-type
24
+ Dynamic: home-page
25
+ Dynamic: license-file
26
+ Dynamic: project-url
27
+ Dynamic: requires-dist
28
+ Dynamic: requires-python
29
+ Dynamic: summary
30
+
31
+ # DefStream-RabbitMQ
32
+
33
+ ## CI and PyPI releases
34
+
35
+ The `CI` workflow tests PRs targeting `master` and pushes to `master` on Python
36
+ 3.9 through 3.14. It builds all three DefStream packages from source, installs
37
+ their wheels in a clean environment, and runs this repository's tests with
38
+ RabbitMQ 4.1 and Redis 7.4 service containers. The current repository uses the
39
+ event commit; sibling repositories use their `master` branches, with resolved
40
+ commit IDs recorded in the log. All three GitHub repositories must be accessible.
41
+ This bootstrap setup does not require existing PyPI releases and does not
42
+ certify compatibility with previously published dependencies.
43
+
44
+ On a `master` push, the build uses `major.minor.<github.run_number>` from the
45
+ checked-in `VERSION` (for example, `0.1.42`). PR builds retain `VERSION`. No
46
+ version commit, PR, or tag is created. Change major/minor to start a new series;
47
+ run numbers can have gaps. Check for PyPI version collisions before replacing
48
+ or renaming the workflow.
49
+
50
+ Only after all six Python checks pass does a separate publish job upload the
51
+ exact Python 3.13 build artifacts. A fully published version is skipped on a
52
+ rerun; first-attempt collisions, incomplete releases, and unexpected network
53
+ errors fail. Active runs are not cancelled; older pending runs may be superseded.
54
+
55
+ Configure `PYPI_API_TOKEN` as a repository Actions secret or a secret in the
56
+ `pypi` environment. Restrict that environment to `master`; omit approval rules
57
+ for fully automatic releases. Protect `master` with required PRs and all six
58
+ `Test Python 3.x` checks, not the push-only `Publish PyPI` job. Never commit the
59
+ token. Publish the core before this adapter so users can resolve its dependency.
60
+
61
+ RabbitMQ broker adapter for DefStream, distributed separately from the core.
62
+ Requires Python 3.9 or newer.
63
+
64
+ ## Installation
65
+
66
+ ```shell
67
+ pip install DefStream-RabbitMQ
68
+ ```
69
+
70
+ This installs `DefStream>=0.1.0` and `pika>=1.3.2`. The distribution name is
71
+ `DefStream-RabbitMQ`; the Python import path remains
72
+ `DefStream.Broker.Implements.RabbitMQ`.
73
+
74
+ ## Usage
75
+
76
+ ```python
77
+ from DefStream.Broker.Implements.RabbitMQ import (
78
+ Configuration,
79
+ Connection,
80
+ )
81
+
82
+ broker = Connection(Configuration(
83
+ host='127.0.0.1',
84
+ port=5672,
85
+ username='guest',
86
+ password='guest',
87
+ ))
88
+ broker.connect()
89
+ try:
90
+ broker.create_queue('tasks')
91
+ broker.queue('tasks').send({'message': 'hello'})
92
+ finally:
93
+ broker.close()
94
+ ```
95
+
96
+ A reachable RabbitMQ server is required. Use connections and their resources
97
+ on the thread that owns them.
98
+
99
+ The adapter provides queues, exchanges, streams, subscriptions, error policies,
100
+ and synchronous queue/exchange RPC. Use its `Connection` with
101
+ `DefStream.Worker.Worker` for worker execution.
102
+
103
+ ## Source layout
104
+
105
+ The contents of the former `DefStream/src/Broker/Implements/RabbitMQ` directory
106
+ now live directly in this project's [src](./src) directory. Packaging maps that
107
+ directory to `DefStream.Broker.Implements.RabbitMQ`; this distribution does not
108
+ own or overwrite the parent packages supplied by DefStream.
109
+
110
+ ## Development
111
+
112
+ With the core and both adapters checked out as sibling directories, run this
113
+ from their common parent using your virtual environment's Python:
114
+
115
+ ```powershell
116
+ python -m pip install --config-settings editable_mode=strict -e .\DefStream
117
+ python -m pip install -e .\DefStream-RabbitMQ -e .\DefStream-Redis
118
+ python -m unittest discover -s .\DefStream-RabbitMQ\test -p "test_*.py"
119
+ ```
120
+
121
+ The core uses strict editable mode to prevent the adapters' editable namespace
122
+ entries from masking its packages. Keep the core's generated `build` link tree;
123
+ reinstall the core after adding or removing its modules. Adapters use default
124
+ editable mode.
125
+
126
+ RabbitMQ-specific tests live in this project's `test` directory, covering
127
+ resource methods, subscriptions, consumers, RPC, and broker integration.
128
+ Broker examples live in `sample`. Shared Worker, Inspector, and cross-adapter
129
+ contract tests remain in the DefStream repository and require both adapters.
130
+
131
+ Run `python -m unittest discover -s test -p "test_*.py"` from this project
132
+ directory after installation. Integration tests are opt-in via
133
+ `DEFSTREAM_RABBITMQ_TESTS=1`; ordinary unit tests do not require a running broker
134
+ or the Redis adapter.
135
+
136
+ Do not combine editable installs with a `site-packages/DefStream` link pointing
137
+ directly at the core source: installing adapter files through that link can
138
+ write into the core checkout. Remove such a link before installing the projects.
139
+ Reinstall after changing package metadata or dependencies.
140
+
141
+ ## License
142
+
143
+ See [LICENSE](./LICENSE), copied unchanged from DefStream.
@@ -0,0 +1,16 @@
1
+ DefStream/Broker/Implements/RabbitMQ/Configuration.py,sha256=UFE3BCny4KXmLqEUuyDNXUvYcy9yleqCNio1LF1u_y8,1046
2
+ DefStream/Broker/Implements/RabbitMQ/Connection.py,sha256=cDCfzKmYHs5WMQZvmvNSq7EAuLVRtHVhSBuYHLmDNks,8926
3
+ DefStream/Broker/Implements/RabbitMQ/Consumer.py,sha256=KUBhqiDujMKzcFuyPyzsbDv-5uMmrIYFAgRJ9KdWBwk,5182
4
+ DefStream/Broker/Implements/RabbitMQ/Event.py,sha256=LmSiaSUqJF_kx7v78tt-bawy797z5Q-lM5OO0hnIQcc,1747
5
+ DefStream/Broker/Implements/RabbitMQ/Exchange.py,sha256=MjOdtn1Aeh20mzGZOsa0MxMlpePpyQkz49_VDlf_7oM,4815
6
+ DefStream/Broker/Implements/RabbitMQ/Policies.py,sha256=kT7vreCclwsu5hMAFkDldsNBktuOMB611biOePk7Lr4,85
7
+ DefStream/Broker/Implements/RabbitMQ/Queue.py,sha256=cQTO988qlL0K-ZMgW71jYFoGigcu0KVXhGnjltHMJSU,5325
8
+ DefStream/Broker/Implements/RabbitMQ/Serializer.py,sha256=ylwIBJ0blftq2W9usuYvjS3xfXovR8_9kP3O9DlXYxI,1575
9
+ DefStream/Broker/Implements/RabbitMQ/Stream.py,sha256=9E7R_bX_kDZ3VIH7hkMpP-Z7zGbJTonU8V6q_KT3YRI,2283
10
+ DefStream/Broker/Implements/RabbitMQ/Subscription.py,sha256=0ACs8skbn_s9UfricBDf59hT0F6G3ljO4aUrkpLuI9g,1284
11
+ DefStream/Broker/Implements/RabbitMQ/__init__.py,sha256=Ndt5yNXcgecN1mj0Qv7M32LUFK96WKmSi88wKMB1ZdU,673
12
+ defstream_rabbitmq-0.1.1.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
13
+ defstream_rabbitmq-0.1.1.dist-info/METADATA,sha256=aXWKLYLX7ygLs5KI_94b6fKBU5qqL3NC_0Y9ENc1pV0,5569
14
+ defstream_rabbitmq-0.1.1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
15
+ defstream_rabbitmq-0.1.1.dist-info/top_level.txt,sha256=zsnisIu5wHL4b7VM8e9AggG9KTSdUU4morFqpb8YWFs,10
16
+ defstream_rabbitmq-0.1.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+