DefStream-RabbitMQ 0.1.1__tar.gz → 0.1.1.2__tar.gz

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.
Files changed (33) hide show
  1. defstream_rabbitmq-0.1.1.2/.github/workflows/ci.yml +403 -0
  2. defstream_rabbitmq-0.1.1.2/.gitignore +58 -0
  3. {defstream_rabbitmq-0.1.1 → defstream_rabbitmq-0.1.1.2}/DefStream_RabbitMQ.egg-info/PKG-INFO +54 -6
  4. {defstream_rabbitmq-0.1.1 → defstream_rabbitmq-0.1.1.2}/DefStream_RabbitMQ.egg-info/SOURCES.txt +5 -2
  5. {defstream_rabbitmq-0.1.1 → defstream_rabbitmq-0.1.1.2}/PKG-INFO +54 -6
  6. {defstream_rabbitmq-0.1.1 → defstream_rabbitmq-0.1.1.2}/README.md +52 -5
  7. defstream_rabbitmq-0.1.1.2/sample/Sample.RPC.py +168 -0
  8. defstream_rabbitmq-0.1.1.2/sample/Sample.Stream.py +84 -0
  9. defstream_rabbitmq-0.1.1.2/sample/Sample.py +127 -0
  10. {defstream_rabbitmq-0.1.1 → defstream_rabbitmq-0.1.1.2}/setup.py +7 -1
  11. defstream_rabbitmq-0.1.1/MANIFEST.in +0 -1
  12. defstream_rabbitmq-0.1.1/VERSION +0 -1
  13. {defstream_rabbitmq-0.1.1 → defstream_rabbitmq-0.1.1.2}/DefStream_RabbitMQ.egg-info/dependency_links.txt +0 -0
  14. {defstream_rabbitmq-0.1.1 → defstream_rabbitmq-0.1.1.2}/DefStream_RabbitMQ.egg-info/requires.txt +0 -0
  15. {defstream_rabbitmq-0.1.1 → defstream_rabbitmq-0.1.1.2}/DefStream_RabbitMQ.egg-info/top_level.txt +0 -0
  16. {defstream_rabbitmq-0.1.1 → defstream_rabbitmq-0.1.1.2}/LICENSE +0 -0
  17. {defstream_rabbitmq-0.1.1 → defstream_rabbitmq-0.1.1.2}/setup.cfg +0 -0
  18. {defstream_rabbitmq-0.1.1 → defstream_rabbitmq-0.1.1.2}/src/Configuration.py +0 -0
  19. {defstream_rabbitmq-0.1.1 → defstream_rabbitmq-0.1.1.2}/src/Connection.py +0 -0
  20. {defstream_rabbitmq-0.1.1 → defstream_rabbitmq-0.1.1.2}/src/Consumer.py +0 -0
  21. {defstream_rabbitmq-0.1.1 → defstream_rabbitmq-0.1.1.2}/src/Event.py +0 -0
  22. {defstream_rabbitmq-0.1.1 → defstream_rabbitmq-0.1.1.2}/src/Exchange.py +0 -0
  23. {defstream_rabbitmq-0.1.1 → defstream_rabbitmq-0.1.1.2}/src/Policies.py +0 -0
  24. {defstream_rabbitmq-0.1.1 → defstream_rabbitmq-0.1.1.2}/src/Queue.py +0 -0
  25. {defstream_rabbitmq-0.1.1 → defstream_rabbitmq-0.1.1.2}/src/Serializer.py +0 -0
  26. {defstream_rabbitmq-0.1.1 → defstream_rabbitmq-0.1.1.2}/src/Stream.py +0 -0
  27. {defstream_rabbitmq-0.1.1 → defstream_rabbitmq-0.1.1.2}/src/Subscription.py +0 -0
  28. {defstream_rabbitmq-0.1.1 → defstream_rabbitmq-0.1.1.2}/src/__init__.py +0 -0
  29. {defstream_rabbitmq-0.1.1 → defstream_rabbitmq-0.1.1.2}/test/test_broker_methods.py +0 -0
  30. {defstream_rabbitmq-0.1.1 → defstream_rabbitmq-0.1.1.2}/test/test_rabbitmq_integration.py +0 -0
  31. {defstream_rabbitmq-0.1.1 → defstream_rabbitmq-0.1.1.2}/test/test_rabbitmq_worker.py +0 -0
  32. {defstream_rabbitmq-0.1.1 → defstream_rabbitmq-0.1.1.2}/test/test_rpc.py +0 -0
  33. {defstream_rabbitmq-0.1.1 → defstream_rabbitmq-0.1.1.2}/test/test_rpc_integration.py +0 -0
@@ -0,0 +1,403 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [master]
6
+ push:
7
+ branches: [master]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ env:
13
+ RELEASE_SERIES: '0.1'
14
+
15
+ concurrency:
16
+ group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
17
+ cancel-in-progress: false
18
+
19
+ jobs:
20
+ changes:
21
+ name: Plan release version
22
+ runs-on: ubuntu-latest
23
+ timeout-minutes: 5
24
+ outputs:
25
+ release: ${{ steps.changes.outputs.release }}
26
+ version: ${{ steps.changes.outputs.version }}
27
+ steps:
28
+ - uses: actions/checkout@v7
29
+ with:
30
+ fetch-depth: 0
31
+ persist-credentials: false
32
+ - name: Set up release planner Python
33
+ uses: actions/setup-python@v7
34
+ with:
35
+ python-version: '3.13'
36
+ - name: Check complete push range and plan version
37
+ id: changes
38
+ env:
39
+ BEFORE: ${{ github.event.before }}
40
+ RELEASE_PUSH: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
41
+ run: |
42
+ python - <<'PY'
43
+ from os import environ
44
+ from pathlib import Path
45
+ from re import fullmatch
46
+ from subprocess import check_output
47
+
48
+ if environ['RELEASE_PUSH'] != 'true':
49
+ with Path(environ['GITHUB_OUTPUT']).open('a', encoding='utf-8') as output:
50
+ output.write('release=false\n')
51
+ raise SystemExit(0)
52
+ series = environ['RELEASE_SERIES']
53
+ if fullmatch(r'(0|[1-9]\d*)\.(0|[1-9]\d*)', series) is None:
54
+ raise ValueError(f'Invalid RELEASE_SERIES: {series!r}')
55
+ before = environ['BEFORE']
56
+ if not before:
57
+ raise ValueError('Missing push base commit')
58
+ first_push = set(before) == {'0'}
59
+ base = check_output(['git', 'hash-object', '-t', 'tree', '--stdin'], input='', text=True).strip() if first_push else before
60
+ changed = check_output(
61
+ ['git', 'diff', '--name-only', '--no-renames', base, environ['GITHUB_SHA'], '--', 'src/'],
62
+ text=True,
63
+ ).strip()
64
+ # Anchor to the pre-push history so a rerun ignores its own release tag.
65
+ tags = [] if first_push else check_output(['git', 'tag', '--merged', before], text=True).splitlines()
66
+ releases = []
67
+ for tag in tags:
68
+ match = fullmatch(r'v?(\d+)\.(\d+)\.(\d+)(?:\.(\d+))?(?:\.post\d+)?', tag)
69
+ if match and f'{int(match[1])}.{int(match[2])}' == series:
70
+ releases.append(tuple(map(int, match.groups()[:3])) + (int(match[4] or 0),))
71
+ latest = max(releases) if releases else None
72
+ number = int(environ['GITHUB_RUN_NUMBER'])
73
+ if number < 1:
74
+ raise ValueError('Build number must be positive')
75
+ if latest is not None and number <= latest[3]:
76
+ raise ValueError('Build number must exceed the previous build; do not reset the workflow counter')
77
+ if changed:
78
+ patch = latest[2] + 1 if latest is not None else 0
79
+ print(f'Source changes:\n{changed}')
80
+ else:
81
+ if latest is None:
82
+ raise ValueError(
83
+ 'No release tag in pre-push history for this series. '
84
+ 'Tag the exact source commit of the existing PyPI release before retrying.'
85
+ )
86
+ patch = latest[2]
87
+ print('No src changes: keeping MAJOR.MINOR.PATCH and increasing BUILD')
88
+ version = f'{series}.{patch}.{number}'
89
+ print(f'Planned version: {version}')
90
+ with Path(environ['GITHUB_OUTPUT']).open('a', encoding='utf-8') as output:
91
+ output.write(f'release=true\nversion={version}\n')
92
+ PY
93
+
94
+ test:
95
+ name: Test Python ${{ matrix.python }}
96
+ needs: changes
97
+ runs-on: ubuntu-latest
98
+ timeout-minutes: 25
99
+ strategy:
100
+ fail-fast: false
101
+ matrix:
102
+ python: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14']
103
+ env:
104
+ PROJECT: ${{ github.event.repository.name }}
105
+ DEFSTREAM_RABBITMQ_TESTS: '1'
106
+ DEFSTREAM_REDIS_TESTS: '1'
107
+ services:
108
+ rabbitmq:
109
+ image: rabbitmq:4.1
110
+ ports:
111
+ - 5672:5672
112
+ env:
113
+ RABBITMQ_DEFAULT_USER: guest
114
+ RABBITMQ_DEFAULT_PASS: guest
115
+ RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS: '-rabbit loopback_users []'
116
+ options: >-
117
+ --health-cmd "rabbitmq-diagnostics -q ping"
118
+ --health-interval 10s
119
+ --health-timeout 10s
120
+ --health-retries 12
121
+ redis:
122
+ image: redis:7.4
123
+ ports:
124
+ - 6379:6379
125
+ options: >-
126
+ --health-cmd "redis-cli ping"
127
+ --health-interval 5s
128
+ --health-timeout 5s
129
+ --health-retries 12
130
+ steps:
131
+ - name: Check out core
132
+ uses: actions/checkout@v7
133
+ with:
134
+ repository: yong5eon/DefStream
135
+ ref: ${{ github.event.repository.name == 'DefStream' && github.sha || 'master' }}
136
+ path: packages/DefStream
137
+ fetch-depth: 0
138
+ persist-credentials: false
139
+
140
+ - name: Check out RabbitMQ adapter
141
+ uses: actions/checkout@v7
142
+ with:
143
+ repository: yong5eon/DefStream-RabbitMQ
144
+ ref: ${{ github.event.repository.name == 'DefStream-RabbitMQ' && github.sha || 'master' }}
145
+ path: packages/DefStream-RabbitMQ
146
+ fetch-depth: 0
147
+ persist-credentials: false
148
+
149
+ - name: Check out Redis adapter
150
+ uses: actions/checkout@v7
151
+ with:
152
+ repository: yong5eon/DefStream-Redis
153
+ ref: ${{ github.event.repository.name == 'DefStream-Redis' && github.sha || 'master' }}
154
+ path: packages/DefStream-Redis
155
+ fetch-depth: 0
156
+ persist-credentials: false
157
+
158
+ - name: Set up Python
159
+ uses: actions/setup-python@v7
160
+ with:
161
+ python-version: ${{ matrix.python }}
162
+
163
+ - name: Record source commits and tag candidate locally
164
+ env:
165
+ RELEASE_BUILD: ${{ needs.changes.outputs.release }}
166
+ RELEASE_VERSION: ${{ needs.changes.outputs.version }}
167
+ run: |
168
+ for package in packages/*; do
169
+ echo "$package: $(git -C "$package" rev-parse HEAD)"
170
+ done
171
+ echo "SOURCE_DATE_EPOCH=$(git -C "packages/$PROJECT" show -s --format=%ct HEAD)" >> "$GITHUB_ENV"
172
+ python - <<'PY'
173
+ from os import environ
174
+ from pathlib import Path
175
+ from re import fullmatch
176
+ from subprocess import check_output, run
177
+
178
+ project = environ['PROJECT']
179
+ if project not in ('DefStream', 'DefStream-RabbitMQ', 'DefStream-Redis'):
180
+ raise ValueError(f'Unexpected project: {project}')
181
+ if environ['RELEASE_BUILD'] != 'true':
182
+ print('Test-only build: using the existing Git-derived development version.')
183
+ raise SystemExit(0)
184
+ version = environ['RELEASE_VERSION']
185
+ if fullmatch(r'(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)\.[1-9]\d*', version) is None:
186
+ raise ValueError(f'Invalid planned version: {version!r}')
187
+ path = Path('packages') / project
188
+ tag = version
189
+ tags = check_output(['git', '-C', str(path), 'tag', '--list', tag], text=True).strip()
190
+ if tags:
191
+ tagged = check_output(['git', '-C', str(path), 'rev-parse', f'{tag}^{{commit}}'], text=True).strip()
192
+ if tagged != environ['GITHUB_SHA']:
193
+ raise RuntimeError(f'{tag} already points at another commit')
194
+ else:
195
+ run(['git', '-C', str(path), 'tag', tag, environ['GITHUB_SHA']], check=True)
196
+ print(f'{project}: local candidate {tag}')
197
+ with Path(environ['GITHUB_STEP_SUMMARY']).open('a', encoding='utf-8') as summary:
198
+ summary.write(f'## {project} {version}\n\nSource: `{environ["GITHUB_SHA"]}`\n')
199
+ PY
200
+
201
+ - name: Install build tools
202
+ run: python -m pip install --upgrade build twine
203
+
204
+ - name: Build source distributions and wheels
205
+ run: |
206
+ mkdir -p dist dependency-wheels
207
+ for name in DefStream DefStream-RabbitMQ DefStream-Redis; do
208
+ if [ "$name" = "$PROJECT" ]; then
209
+ python -m build --outdir "$GITHUB_WORKSPACE/dist" "packages/$name"
210
+ else
211
+ python -m build --outdir "$GITHUB_WORKSPACE/dependency-wheels" "packages/$name"
212
+ fi
213
+ done
214
+ python -m twine check --strict dist/*
215
+
216
+ - name: Install built wheels into a clean environment
217
+ run: |
218
+ python -m venv "$RUNNER_TEMP/installed"
219
+ "$RUNNER_TEMP/installed/bin/python" -m pip install dist/*.whl dependency-wheels/*.whl
220
+ "$RUNNER_TEMP/installed/bin/python" -m pip check
221
+
222
+ - name: Test installed package
223
+ run: |
224
+ cd "$RUNNER_TEMP"
225
+ "$RUNNER_TEMP/installed/bin/python" -m unittest discover \
226
+ -s "$GITHUB_WORKSPACE/packages/$PROJECT/test" -p 'test_*.py' -v
227
+
228
+ - name: Set up Node for core UI tests
229
+ if: env.PROJECT == 'DefStream'
230
+ uses: actions/setup-node@v7
231
+ with:
232
+ node-version: '22'
233
+
234
+ - name: Test core UI
235
+ if: env.PROJECT == 'DefStream'
236
+ run: node --test packages/DefStream/test/test_dashboard_ui.cjs
237
+
238
+ - name: Preserve verified release distributions
239
+ if: needs.changes.outputs.release == 'true' && matrix.python == '3.13'
240
+ uses: actions/upload-artifact@v7
241
+ with:
242
+ name: distributions
243
+ path: dist/*
244
+ if-no-files-found: error
245
+ retention-days: 14
246
+
247
+ publish:
248
+ name: Publish PyPI
249
+ if: github.event_name == 'push' && github.ref == 'refs/heads/master' && needs.changes.outputs.release == 'true'
250
+ needs: [changes, test]
251
+ runs-on: ubuntu-latest
252
+ timeout-minutes: 10
253
+ environment: pypi
254
+ permissions:
255
+ contents: write
256
+ steps:
257
+ - name: Check out release source for tagging
258
+ uses: actions/checkout@v7
259
+ with:
260
+ ref: ${{ github.sha }}
261
+ path: release-source
262
+ fetch-depth: 0
263
+
264
+ - name: Download verified distributions
265
+ uses: actions/download-artifact@v8
266
+ with:
267
+ name: distributions
268
+ path: dist
269
+
270
+ - name: Set up Python
271
+ uses: actions/setup-python@v7
272
+ with:
273
+ python-version: '3.13'
274
+
275
+ - name: Check release status
276
+ id: release
277
+ env:
278
+ PROJECT: ${{ github.event.repository.name }}
279
+ RELEASE_VERSION: ${{ needs.changes.outputs.version }}
280
+ run: |
281
+ python - <<'PY'
282
+ from email.parser import BytesParser
283
+ from json import load
284
+ from os import environ
285
+ from pathlib import Path
286
+ from urllib.error import HTTPError
287
+ from urllib.request import urlopen
288
+ from zipfile import ZipFile
289
+
290
+ wheels = list(Path('dist').glob('*.whl'))
291
+ sources = list(Path('dist').glob('*.tar.gz'))
292
+ if len(wheels) != 1 or len(sources) != 1:
293
+ raise ValueError('Expected exactly one wheel and one source distribution')
294
+ with ZipFile(wheels[0]) as archive:
295
+ metadata_path, = (name for name in archive.namelist() if name.endswith('.dist-info/METADATA'))
296
+ metadata = BytesParser().parsebytes(archive.read(metadata_path))
297
+ project = environ['PROJECT']
298
+ if metadata['Name'] != project:
299
+ raise ValueError(f'Unexpected distribution: {metadata["Name"]}')
300
+ version = metadata['Version']
301
+ expected_version = environ['RELEASE_VERSION']
302
+ if version != expected_version:
303
+ raise ValueError(f'Expected {expected_version}, got {version}')
304
+ try:
305
+ with urlopen(f'https://pypi.org/pypi/{project}/{version}/json', timeout=30) as response:
306
+ release = load(response)
307
+ except HTTPError as error:
308
+ if error.code != 404:
309
+ raise
310
+ exists = False
311
+ else:
312
+ expected = {wheels[0].name, sources[0].name}
313
+ published = {file['filename'] for file in release['urls']}
314
+ if int(environ['GITHUB_RUN_ATTEMPT']) == 1 or not expected.issubset(published):
315
+ raise RuntimeError(
316
+ f'{project} {version} already exists or is partially published. '
317
+ 'Do not overwrite it; use a new run/version or repair the release manually.'
318
+ )
319
+ print(f'{project} {version}: already published; skipping this rerun')
320
+ exists = True
321
+ with Path(environ['GITHUB_OUTPUT']).open('a', encoding='utf-8') as output:
322
+ output.write(f'publish={str(not exists).lower()}\n')
323
+ output.write(f'version={version}\n')
324
+ PY
325
+
326
+ - name: Record verified release tag
327
+ env:
328
+ VERSION: ${{ steps.release.outputs.version }}
329
+ run: |
330
+ tag="$VERSION"
331
+ cd release-source
332
+ if git show-ref --verify --quiet "refs/tags/$tag"; then
333
+ if [ "$(git rev-parse "$tag^{commit}")" != "$GITHUB_SHA" ]; then
334
+ echo "::error::$tag already points at another commit."
335
+ exit 1
336
+ fi
337
+ else
338
+ git tag "$tag" "$GITHUB_SHA"
339
+ fi
340
+ git push origin "refs/tags/$tag"
341
+
342
+ - name: Install upload tool
343
+ if: steps.release.outputs.publish == 'true'
344
+ run: python -m pip install --upgrade twine
345
+
346
+ - name: Upload to PyPI
347
+ if: steps.release.outputs.publish == 'true'
348
+ env:
349
+ TWINE_USERNAME: __token__
350
+ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
351
+ run: |
352
+ if [ -z "$TWINE_PASSWORD" ]; then
353
+ echo "::error::Configure PYPI_API_TOKEN in the pypi environment or repository Actions secrets."
354
+ exit 1
355
+ fi
356
+ python -m twine upload --non-interactive dist/*
357
+
358
+ - name: Publish GitHub Release with verified distributions
359
+ env:
360
+ GH_TOKEN: ${{ github.token }}
361
+ VERSION: ${{ steps.release.outputs.version }}
362
+ run: |
363
+ python - <<'PY'
364
+ from json import load
365
+ from os import environ
366
+ from pathlib import Path
367
+ from subprocess import run
368
+ from urllib.error import HTTPError
369
+ from urllib.parse import quote
370
+ from urllib.request import Request, urlopen
371
+
372
+ repository = environ['GITHUB_REPOSITORY']
373
+ version = environ['VERSION']
374
+ request = Request(
375
+ f'{environ["GITHUB_API_URL"]}/repos/{repository}/releases/tags/{quote(version, safe="")}',
376
+ headers={
377
+ 'Authorization': f'Bearer {environ["GH_TOKEN"]}',
378
+ 'Accept': 'application/vnd.github+json',
379
+ 'X-GitHub-Api-Version': '2022-11-28',
380
+ },
381
+ )
382
+ try:
383
+ with urlopen(request, timeout=30) as response:
384
+ release = load(response)
385
+ except HTTPError as error:
386
+ if error.code != 404:
387
+ raise
388
+ run([
389
+ 'gh', 'release', 'create', version, '--repo', repository,
390
+ '--verify-tag', '--title', version, '--generate-notes', '--draft',
391
+ ], check=True)
392
+ else:
393
+ if release['tag_name'] != version:
394
+ raise ValueError('GitHub Release does not match the verified version')
395
+ artifacts = sorted(str(path) for path in Path('dist').iterdir() if path.is_file())
396
+ run([
397
+ 'gh', 'release', 'upload', version, '--repo', repository,
398
+ '--clobber', *artifacts,
399
+ ], check=True)
400
+ run([
401
+ 'gh', 'release', 'edit', version, '--repo', repository, '--draft=false',
402
+ ], check=True)
403
+ PY
@@ -0,0 +1,58 @@
1
+ # Python bytecode and caches
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+ .pytest_cache/
6
+ .mypy_cache/
7
+ .ruff_cache/
8
+ .tox/
9
+ .nox/
10
+ .coverage
11
+ .coverage.*
12
+
13
+ # Virtual environments and local environment files
14
+ .venv/
15
+ venv/
16
+ env/
17
+ .env
18
+ .env.*
19
+
20
+ # Python packaging and build artifacts
21
+ build/
22
+ dist/
23
+ *.egg-info/
24
+ *.egg
25
+ .Python
26
+
27
+ # Operating system files
28
+ .DS_Store
29
+ Thumbs.db
30
+ Desktop.ini
31
+
32
+ # Miscellaneous files
33
+ *.log
34
+ *.tmp
35
+ *.bak
36
+ *.swp
37
+ *.swo
38
+
39
+ # Temporary files
40
+ tmp/
41
+ *.tmp
42
+ *.temp
43
+
44
+ # IDE and editor specific files
45
+ .vscode/
46
+ .idea/
47
+
48
+ # Git
49
+ .git/
50
+
51
+ # GitHub specific files
52
+ .github/
53
+ !/.github/
54
+ /.github/*
55
+ !/.github/workflows/
56
+
57
+ # GitLab specific files
58
+ .gitlab/
@@ -1,11 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: DefStream-RabbitMQ
3
- Version: 0.1.1
3
+ Version: 0.1.1.2
4
4
  Summary: RabbitMQ broker adapter for DefStream
5
5
  Home-page: https://github.com/yong5eon/DefStream-RabbitMQ
6
6
  Author: Heo Yongseon
7
7
  Project-URL: Source, https://github.com/yong5eon/DefStream-RabbitMQ
8
8
  Project-URL: Issues, https://github.com/yong5eon/DefStream-RabbitMQ/issues
9
+ Project-URL: Releases, https://github.com/yong5eon/DefStream-RabbitMQ/releases
9
10
  Classifier: Programming Language :: Python :: 3.9
10
11
  Classifier: Programming Language :: Python :: 3.10
11
12
  Classifier: Programming Language :: Python :: 3.11
@@ -41,11 +42,58 @@ commit IDs recorded in the log. All three GitHub repositories must be accessible
41
42
  This bootstrap setup does not require existing PyPI releases and does not
42
43
  certify compatibility with previously published dependencies.
43
44
 
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.
45
+ There is no `VERSION` or `pyproject.toml` file. `setup.py` configures
46
+ `setuptools-scm` via `setup_requires`; `python -m build` resolves the build
47
+ dependency in isolation. This legacy declaration may emit a deprecation warning.
48
+ `setuptools-scm` derives versions from Git tags.
49
+ CI fetches full history and creates a local candidate tag
50
+ `<RELEASE_SERIES>.<PATCH>.<github.run_number>` (for example, `0.1.3.123`) on
51
+ every `master` push. Source changes increment PATCH by one; all other changes
52
+ keep MAJOR.MINOR.PATCH. BUILD is always the Actions run number:
53
+ `0.1.3.123` -> `0.1.3.124` (docs) -> `0.1.4.125` (source).
54
+ The highest matching version in pre-push history supplies the base, keeping
55
+ reruns stable. The four-part value is the full public PyPI version.
56
+ New tags have no `v` prefix; existing `v` tags remain readable.
57
+ PRs only run tests using their Git-derived development version without a tag.
58
+ The complete push range is compared, including additions, deletions, and
59
+ renames. Changes only to tests, docs, samples, workflows, or `setup.py` increase
60
+ only BUILD. PyPI files cannot be overwritten; every upload has a distinct
61
+ version identifier. Change
62
+ `RELEASE_SERIES` in the workflow to start a new major/minor series. No version
63
+ commit or version-update PR is created; run numbers can have gaps.
64
+
65
+ After all tests pass, the publish job pushes the release tag to the tested
66
+ commit before uploading the verified artifacts. A tag may exist if uploading
67
+ subsequently fails; retry the run and check PyPI for publication status.
68
+ Existing tags on other commits are never overwritten. Allow the workflow's
69
+ `GITHUB_TOKEN` to write contents and permit numeric four-part tags
70
+ in any tag rulesets.
71
+
72
+ After PyPI upload succeeds (or a rerun confirms the version is fully
73
+ published), CI creates a GitHub Release titled and tagged with the same
74
+ version, with automatically generated notes and the verified wheel and sdist
75
+ attached. New releases remain drafts until both assets are uploaded. A retry
76
+ reuses the release, replaces matching assets with the verified files, and
77
+ publishes it. PyPI upload failures prevent this step. The existing write-enabled
78
+ `GITHUB_TOKEN` is sufficient; no extra token secret is required. PyPI's project
79
+ links include the GitHub Releases page for change history.
80
+
81
+ Fetch tags with `git fetch --tags` for local builds. Tagged commits produce
82
+ release versions; later commits produce development versions, with local
83
+ suffixes for dirty trees. Build from a Git checkout or a published sdist, not
84
+ a GitHub source ZIP without Git metadata. Installed versions are available via
85
+ `importlib.metadata.version('DefStream-RabbitMQ')`. Before migration, tag the
86
+ exact source commits of existing PyPI releases in all three repositories,
87
+ using their version numbers as tags.
88
+ Untagged SCM dependencies may resolve below their required minimum version;
89
+ initial release tags also provide the starting PATCH for numeric builds.
90
+ A tag such as `0.1.3` must name the commit used to publish that version, not
91
+ an arbitrary current commit. Adding that tag does not edit sources or upload
92
+ anything to PyPI.
93
+ A new release series needs a source change or an existing matching release tag
94
+ before it can produce builds. Its first source release starts at PATCH 0.
95
+ Build counter regressions fail explicitly. Legacy `.postN` tags remain
96
+ readable; numeric builds sort above post releases of a three-part base.
49
97
 
50
98
  Only after all six Python checks pass does a separate publish job upload the
51
99
  exact Python 3.13 build artifacts. A fully published version is skipped on a
@@ -1,13 +1,16 @@
1
+ .gitignore
1
2
  LICENSE
2
- MANIFEST.in
3
3
  README.md
4
- VERSION
5
4
  setup.py
5
+ .github/workflows/ci.yml
6
6
  DefStream_RabbitMQ.egg-info/PKG-INFO
7
7
  DefStream_RabbitMQ.egg-info/SOURCES.txt
8
8
  DefStream_RabbitMQ.egg-info/dependency_links.txt
9
9
  DefStream_RabbitMQ.egg-info/requires.txt
10
10
  DefStream_RabbitMQ.egg-info/top_level.txt
11
+ sample/Sample.RPC.py
12
+ sample/Sample.Stream.py
13
+ sample/Sample.py
11
14
  src/Configuration.py
12
15
  src/Connection.py
13
16
  src/Consumer.py
@@ -1,11 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: DefStream-RabbitMQ
3
- Version: 0.1.1
3
+ Version: 0.1.1.2
4
4
  Summary: RabbitMQ broker adapter for DefStream
5
5
  Home-page: https://github.com/yong5eon/DefStream-RabbitMQ
6
6
  Author: Heo Yongseon
7
7
  Project-URL: Source, https://github.com/yong5eon/DefStream-RabbitMQ
8
8
  Project-URL: Issues, https://github.com/yong5eon/DefStream-RabbitMQ/issues
9
+ Project-URL: Releases, https://github.com/yong5eon/DefStream-RabbitMQ/releases
9
10
  Classifier: Programming Language :: Python :: 3.9
10
11
  Classifier: Programming Language :: Python :: 3.10
11
12
  Classifier: Programming Language :: Python :: 3.11
@@ -41,11 +42,58 @@ commit IDs recorded in the log. All three GitHub repositories must be accessible
41
42
  This bootstrap setup does not require existing PyPI releases and does not
42
43
  certify compatibility with previously published dependencies.
43
44
 
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.
45
+ There is no `VERSION` or `pyproject.toml` file. `setup.py` configures
46
+ `setuptools-scm` via `setup_requires`; `python -m build` resolves the build
47
+ dependency in isolation. This legacy declaration may emit a deprecation warning.
48
+ `setuptools-scm` derives versions from Git tags.
49
+ CI fetches full history and creates a local candidate tag
50
+ `<RELEASE_SERIES>.<PATCH>.<github.run_number>` (for example, `0.1.3.123`) on
51
+ every `master` push. Source changes increment PATCH by one; all other changes
52
+ keep MAJOR.MINOR.PATCH. BUILD is always the Actions run number:
53
+ `0.1.3.123` -> `0.1.3.124` (docs) -> `0.1.4.125` (source).
54
+ The highest matching version in pre-push history supplies the base, keeping
55
+ reruns stable. The four-part value is the full public PyPI version.
56
+ New tags have no `v` prefix; existing `v` tags remain readable.
57
+ PRs only run tests using their Git-derived development version without a tag.
58
+ The complete push range is compared, including additions, deletions, and
59
+ renames. Changes only to tests, docs, samples, workflows, or `setup.py` increase
60
+ only BUILD. PyPI files cannot be overwritten; every upload has a distinct
61
+ version identifier. Change
62
+ `RELEASE_SERIES` in the workflow to start a new major/minor series. No version
63
+ commit or version-update PR is created; run numbers can have gaps.
64
+
65
+ After all tests pass, the publish job pushes the release tag to the tested
66
+ commit before uploading the verified artifacts. A tag may exist if uploading
67
+ subsequently fails; retry the run and check PyPI for publication status.
68
+ Existing tags on other commits are never overwritten. Allow the workflow's
69
+ `GITHUB_TOKEN` to write contents and permit numeric four-part tags
70
+ in any tag rulesets.
71
+
72
+ After PyPI upload succeeds (or a rerun confirms the version is fully
73
+ published), CI creates a GitHub Release titled and tagged with the same
74
+ version, with automatically generated notes and the verified wheel and sdist
75
+ attached. New releases remain drafts until both assets are uploaded. A retry
76
+ reuses the release, replaces matching assets with the verified files, and
77
+ publishes it. PyPI upload failures prevent this step. The existing write-enabled
78
+ `GITHUB_TOKEN` is sufficient; no extra token secret is required. PyPI's project
79
+ links include the GitHub Releases page for change history.
80
+
81
+ Fetch tags with `git fetch --tags` for local builds. Tagged commits produce
82
+ release versions; later commits produce development versions, with local
83
+ suffixes for dirty trees. Build from a Git checkout or a published sdist, not
84
+ a GitHub source ZIP without Git metadata. Installed versions are available via
85
+ `importlib.metadata.version('DefStream-RabbitMQ')`. Before migration, tag the
86
+ exact source commits of existing PyPI releases in all three repositories,
87
+ using their version numbers as tags.
88
+ Untagged SCM dependencies may resolve below their required minimum version;
89
+ initial release tags also provide the starting PATCH for numeric builds.
90
+ A tag such as `0.1.3` must name the commit used to publish that version, not
91
+ an arbitrary current commit. Adding that tag does not edit sources or upload
92
+ anything to PyPI.
93
+ A new release series needs a source change or an existing matching release tag
94
+ before it can produce builds. Its first source release starts at PATCH 0.
95
+ Build counter regressions fail explicitly. Legacy `.postN` tags remain
96
+ readable; numeric builds sort above post releases of a three-part base.
49
97
 
50
98
  Only after all six Python checks pass does a separate publish job upload the
51
99
  exact Python 3.13 build artifacts. A fully published version is skipped on a
@@ -11,11 +11,58 @@ commit IDs recorded in the log. All three GitHub repositories must be accessible
11
11
  This bootstrap setup does not require existing PyPI releases and does not
12
12
  certify compatibility with previously published dependencies.
13
13
 
14
- On a `master` push, the build uses `major.minor.<github.run_number>` from the
15
- checked-in `VERSION` (for example, `0.1.42`). PR builds retain `VERSION`. No
16
- version commit, PR, or tag is created. Change major/minor to start a new series;
17
- run numbers can have gaps. Check for PyPI version collisions before replacing
18
- or renaming the workflow.
14
+ There is no `VERSION` or `pyproject.toml` file. `setup.py` configures
15
+ `setuptools-scm` via `setup_requires`; `python -m build` resolves the build
16
+ dependency in isolation. This legacy declaration may emit a deprecation warning.
17
+ `setuptools-scm` derives versions from Git tags.
18
+ CI fetches full history and creates a local candidate tag
19
+ `<RELEASE_SERIES>.<PATCH>.<github.run_number>` (for example, `0.1.3.123`) on
20
+ every `master` push. Source changes increment PATCH by one; all other changes
21
+ keep MAJOR.MINOR.PATCH. BUILD is always the Actions run number:
22
+ `0.1.3.123` -> `0.1.3.124` (docs) -> `0.1.4.125` (source).
23
+ The highest matching version in pre-push history supplies the base, keeping
24
+ reruns stable. The four-part value is the full public PyPI version.
25
+ New tags have no `v` prefix; existing `v` tags remain readable.
26
+ PRs only run tests using their Git-derived development version without a tag.
27
+ The complete push range is compared, including additions, deletions, and
28
+ renames. Changes only to tests, docs, samples, workflows, or `setup.py` increase
29
+ only BUILD. PyPI files cannot be overwritten; every upload has a distinct
30
+ version identifier. Change
31
+ `RELEASE_SERIES` in the workflow to start a new major/minor series. No version
32
+ commit or version-update PR is created; run numbers can have gaps.
33
+
34
+ After all tests pass, the publish job pushes the release tag to the tested
35
+ commit before uploading the verified artifacts. A tag may exist if uploading
36
+ subsequently fails; retry the run and check PyPI for publication status.
37
+ Existing tags on other commits are never overwritten. Allow the workflow's
38
+ `GITHUB_TOKEN` to write contents and permit numeric four-part tags
39
+ in any tag rulesets.
40
+
41
+ After PyPI upload succeeds (or a rerun confirms the version is fully
42
+ published), CI creates a GitHub Release titled and tagged with the same
43
+ version, with automatically generated notes and the verified wheel and sdist
44
+ attached. New releases remain drafts until both assets are uploaded. A retry
45
+ reuses the release, replaces matching assets with the verified files, and
46
+ publishes it. PyPI upload failures prevent this step. The existing write-enabled
47
+ `GITHUB_TOKEN` is sufficient; no extra token secret is required. PyPI's project
48
+ links include the GitHub Releases page for change history.
49
+
50
+ Fetch tags with `git fetch --tags` for local builds. Tagged commits produce
51
+ release versions; later commits produce development versions, with local
52
+ suffixes for dirty trees. Build from a Git checkout or a published sdist, not
53
+ a GitHub source ZIP without Git metadata. Installed versions are available via
54
+ `importlib.metadata.version('DefStream-RabbitMQ')`. Before migration, tag the
55
+ exact source commits of existing PyPI releases in all three repositories,
56
+ using their version numbers as tags.
57
+ Untagged SCM dependencies may resolve below their required minimum version;
58
+ initial release tags also provide the starting PATCH for numeric builds.
59
+ A tag such as `0.1.3` must name the commit used to publish that version, not
60
+ an arbitrary current commit. Adding that tag does not edit sources or upload
61
+ anything to PyPI.
62
+ A new release series needs a source change or an existing matching release tag
63
+ before it can produce builds. Its first source release starts at PATCH 0.
64
+ Build counter regressions fail explicitly. Legacy `.postN` tags remain
65
+ readable; numeric builds sort above post releases of a three-part base.
19
66
 
20
67
  Only after all six Python checks pass does a separate publish job upload the
21
68
  exact Python 3.13 build artifacts. A fully published version is skipped on a
@@ -0,0 +1,168 @@
1
+ from concurrent.futures import ThreadPoolExecutor
2
+ from sys import stderr
3
+ from threading import Event as PyEvent
4
+ from uuid import uuid4
5
+
6
+ from DefStream.Broker import RemoteError
7
+ from DefStream.Broker.Implements.RabbitMQ import (
8
+ Connection,
9
+ Configuration,
10
+ Consumer,
11
+ Event,
12
+ EventHandler,
13
+ ExchangeType,
14
+ Reject,
15
+ )
16
+
17
+
18
+ class Calculate(EventHandler):
19
+ def __init__(
20
+ self,
21
+ consumer: Consumer,
22
+ ):
23
+ self.consumer = consumer
24
+
25
+ def __call__(
26
+ self,
27
+ event: Event,
28
+ ) -> None:
29
+ try:
30
+ result = event.body['a'] / event.body['b']
31
+ except ZeroDivisionError:
32
+ self.consumer.fail(
33
+ event,
34
+ Reject(),
35
+ )
36
+ else:
37
+ self.consumer.complete(
38
+ event,
39
+ result,
40
+ )
41
+
42
+
43
+ def serve(
44
+ configuration: Configuration,
45
+ queue: str,
46
+ ready: PyEvent,
47
+ stopping: PyEvent,
48
+ ) -> None:
49
+ broker = Connection(configuration)
50
+ try:
51
+ broker.connect()
52
+ consumer = broker.consumer()
53
+ try:
54
+ consumer.subs(
55
+ queue,
56
+ Calculate(consumer),
57
+ )
58
+ ready.set()
59
+
60
+ def check_stop() -> None:
61
+ if stopping.is_set():
62
+ consumer.stop()
63
+ else:
64
+ broker.connection.call_later(
65
+ 0.1,
66
+ check_stop,
67
+ )
68
+
69
+ broker.connection.call_later(
70
+ 0.1,
71
+ check_stop,
72
+ )
73
+ consumer.run()
74
+ finally:
75
+ consumer.close()
76
+ finally:
77
+ broker.close()
78
+ ready.set()
79
+
80
+
81
+ if __name__ == '__main__':
82
+ configuration = Configuration(
83
+ host='127.0.0.1',
84
+ port=5672,
85
+ username='guest',
86
+ password='guest',
87
+ )
88
+ broker = Connection(configuration)
89
+ name = 'defstream-rpc-sample-' + uuid4().hex
90
+ ready = PyEvent()
91
+ stopping = PyEvent()
92
+ created_queue = False
93
+ created_exchange = False
94
+ try:
95
+ broker.connect()
96
+ broker.create_exchange(
97
+ name,
98
+ ExchangeType.Direct,
99
+ durable=False,
100
+ )
101
+ created_exchange = True
102
+ broker.create_queue(
103
+ name,
104
+ durable=False,
105
+ )
106
+ created_queue = True
107
+ broker.bind_exchange_to_queue(
108
+ name,
109
+ name,
110
+ event='divide',
111
+ )
112
+ with ThreadPoolExecutor(1) as executor:
113
+ server = executor.submit(
114
+ serve,
115
+ configuration,
116
+ name,
117
+ ready,
118
+ stopping,
119
+ )
120
+ try:
121
+ if not ready.wait(10):
122
+ raise TimeoutError('RPC server did not start')
123
+ if server.done():
124
+ server.result()
125
+ raise RuntimeError('RPC server exited before handling requests')
126
+ result = broker.queue(name).call(
127
+ {
128
+ 'a': 20,
129
+ 'b': 4,
130
+ },
131
+ timeout=5,
132
+ )
133
+ print(f'Queue RPC: 20 / 4 = {result}')
134
+ result = broker.exchange(name).call(
135
+ {
136
+ 'a': 30,
137
+ 'b': 5,
138
+ },
139
+ timeout=5,
140
+ event='divide',
141
+ )
142
+ print(f'Exchange RPC: 30 / 5 = {result}')
143
+ try:
144
+ broker.queue(name).call(
145
+ {
146
+ 'a': 10,
147
+ 'b': 0,
148
+ },
149
+ timeout=5,
150
+ )
151
+ except RemoteError as error:
152
+ print(
153
+ f'RPC rejected: {error}',
154
+ file=stderr,
155
+ )
156
+ finally:
157
+ stopping.set()
158
+ server.result(timeout=10)
159
+ finally:
160
+ try:
161
+ if created_queue:
162
+ broker.delete_queue(name)
163
+ finally:
164
+ try:
165
+ if created_exchange:
166
+ broker.delete_exchange(name)
167
+ finally:
168
+ broker.close()
@@ -0,0 +1,84 @@
1
+ # Sample for DefStream.Broker.Implements.RabbitMQ
2
+
3
+ from DefStream.Broker.Implements.RabbitMQ import (
4
+ Connection as RabbitConnection,
5
+ Configuration,
6
+ Event,
7
+ EventHandler,
8
+ )
9
+ from threading import Timer
10
+ from random import randint
11
+
12
+ if __name__ == '__main__':
13
+
14
+ con = RabbitConnection(
15
+ Configuration(
16
+ host='127.0.0.1',
17
+ port=5672,
18
+ username='guest',
19
+ password='guest',
20
+ ),
21
+ )
22
+
23
+ con.connect()
24
+ con.create_stream('stream')
25
+
26
+ stream = con.stream('stream')
27
+ for i in range(randint(
28
+ 5,
29
+ 10,
30
+ )):
31
+ stream.send(randint(
32
+ 0,
33
+ 10,
34
+ ))
35
+
36
+ class SampleEventHandler(EventHandler):
37
+ def __call__(
38
+ self,
39
+ event: Event,
40
+ ):
41
+ try:
42
+ print(event.body)
43
+ event.ack()
44
+ except RuntimeError:
45
+ event.nack() # if you want requeue message
46
+ # event.reject() # if you want drop message or move to DLQ or DLE
47
+ pass
48
+ return
49
+
50
+ for i in range(randint(
51
+ 2,
52
+ 5,
53
+ )):
54
+ print('Consuming Events from Stream Queue: {}'.format(i + 1))
55
+ print('-' * 20)
56
+ consumer = con.consumer(
57
+ handler=SampleEventHandler(),
58
+ qos=1,
59
+ offset=0,
60
+ )
61
+
62
+ def stop(timer):
63
+ consumer.stop()
64
+ return
65
+
66
+ Timer(
67
+ 1,
68
+ lambda: stop(None),
69
+ ).start()
70
+
71
+ consumer.subs('stream')
72
+ consumer.run()
73
+
74
+ print('Read Events from Stream Queue:')
75
+ print('-' * 20)
76
+ stream = con.stream('stream')
77
+ try:
78
+ for event in stream.read(timeout=1000):
79
+ print(event.body)
80
+ except KeyboardInterrupt:
81
+ pass
82
+
83
+ con.delete_stream('stream')
84
+ con.close()
@@ -0,0 +1,127 @@
1
+ # Sample for DefStream.Broker.Implements.RabbitMQ
2
+
3
+ from DefStream.Broker.Implements.RabbitMQ import (
4
+ Connection as RabbitConnection,
5
+ Configuration,
6
+ ExchangeType,
7
+ Event,
8
+ EventHandler,
9
+ )
10
+ from threading import Timer
11
+
12
+ if __name__ == '__main__':
13
+
14
+ con = RabbitConnection(
15
+ Configuration(
16
+ host='127.0.0.1',
17
+ port=5672,
18
+ username='guest',
19
+ password='guest',
20
+ ),
21
+ )
22
+
23
+ con.connect()
24
+ con.create_exchange(
25
+ 'topic',
26
+ ExchangeType.Topic,
27
+ )
28
+ con.create_queue(
29
+ 'queue',
30
+ maxPriority=255,
31
+ )
32
+ con.bind_exchange_to_queue(
33
+ 'topic',
34
+ 'queue',
35
+ )
36
+
37
+ exchange = con.exchange('topic')
38
+ exchange.send(
39
+ {
40
+ 'a': True,
41
+ 'b': 1,
42
+ 'c': 1.0,
43
+ 'd': 'abc',
44
+ },
45
+ priority=1,
46
+ )
47
+ queue = con.queue('queue')
48
+ queue.send({
49
+ 'a': False,
50
+ 'b': 2,
51
+ 'c': 2.0,
52
+ 'd': 'def',
53
+ })
54
+
55
+ reader = con.queue('queue')
56
+ e = reader.pop()
57
+ print(e)
58
+ e.ack()
59
+ e = reader.pop()
60
+ print(e)
61
+ e.ack()
62
+
63
+ exchange.send(
64
+ {
65
+ 'a': True,
66
+ 'b': 1,
67
+ 'c': 1.0,
68
+ 'd': 'abc',
69
+ },
70
+ priority=0,
71
+ )
72
+ queue.send(
73
+ {
74
+ 'a': False,
75
+ 'b': 2,
76
+ 'c': 2.0,
77
+ 'd': 'def',
78
+ },
79
+ priority=255,
80
+ )
81
+ exchange.send({
82
+ 'a': True,
83
+ 'b': 1,
84
+ 'c': 1.0,
85
+ 'd': 'abc',
86
+ })
87
+ queue.send({
88
+ 'a': False,
89
+ 'b': 1,
90
+ 'c': 3.0,
91
+ 'd': 'def',
92
+ })
93
+
94
+ class SampleEventHandler(EventHandler):
95
+ def __call__(
96
+ self,
97
+ event: Event,
98
+ ):
99
+ try:
100
+ print(event.body)
101
+ print(event.priority)
102
+ event.ack()
103
+ except RuntimeError:
104
+ event.nack() # if you want requeue message
105
+ # event.reject() # if you want drop message or move to DLQ or DLE
106
+ return
107
+
108
+ consumer = con.consumer(
109
+ handler=SampleEventHandler(),
110
+ qos=1,
111
+ )
112
+
113
+ def stop(timer):
114
+ consumer.stop()
115
+ return
116
+
117
+ Timer(
118
+ 1,
119
+ lambda: stop(None),
120
+ ).start()
121
+
122
+ consumer.subs('queue')
123
+ consumer.run()
124
+
125
+ con.delete_queue('queue')
126
+ con.delete_exchange('topic')
127
+ con.close()
@@ -38,8 +38,14 @@ setup(
38
38
  project_urls={
39
39
  'Source': 'https://github.com/yong5eon/DefStream-RabbitMQ',
40
40
  'Issues': 'https://github.com/yong5eon/DefStream-RabbitMQ/issues',
41
+ 'Releases': 'https://github.com/yong5eon/DefStream-RabbitMQ/releases',
41
42
  },
42
- version=Path('VERSION').read_text(encoding='utf-8').strip(),
43
+ use_scm_version={
44
+ 'tag_regex': r'^v?(?P<version>[0-9]+\.[0-9]+\.[0-9]+(?:\.[0-9]+)?(?:(?:rc|\.post)[0-9]+)?)$',
45
+ },
46
+ setup_requires=[
47
+ 'setuptools-scm>=8,<10',
48
+ ],
43
49
  packages=PACKAGES,
44
50
  package_dir=PACKAGE_DIR,
45
51
  include_package_data=False,
@@ -1 +0,0 @@
1
- include VERSION
@@ -1 +0,0 @@
1
- 0.1.1