Skip to content
Open
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
157 changes: 157 additions & 0 deletions .github/workflows/Python_SDK.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
name: Python SDK

on:
push:
paths:
- 'SDK/python-package/**'
tags:
- 'python-sdk-v*'
pull_request:
paths:
- 'SDK/python-package/**'
workflow_dispatch:

defaults:
run:
working-directory: SDK/python-package

jobs:
# -------------------------------------------------------------------------
# Lint
# -------------------------------------------------------------------------
lint:
name: Lint (ruff)
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install ruff
run: pip install ruff

- name: Run ruff
run: ruff check src/ tests/ examples/

# -------------------------------------------------------------------------
# Test matrix
# -------------------------------------------------------------------------
test:
name: Test (Python ${{ matrix.python-version }})
runs-on: ubuntu-latest
needs: lint
permissions:
contents: read
strategy:
fail-fast: false
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install package and test dependencies
run: |
python -m pip install --upgrade pip
pip install -e .
pip install pytest pytest-cov pytest-asyncio

- name: Run tests
run: pytest --cov=csm_tcp_router --cov-report=term-missing

# -------------------------------------------------------------------------
# Build (wheel + sdist)
# -------------------------------------------------------------------------
build:
name: Build distribution
runs-on: ubuntu-latest
needs: test
permissions:
contents: read

steps:
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Install build tools
run: pip install build

- name: Build wheel and sdist
run: python -m build

- name: Verify wheel is importable
run: |
pip install dist/*.whl
python -c "import csm_tcp_router; print('Version:', csm_tcp_router.__version__)"

- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: python-sdk-dist
path: SDK/python-package/dist/

# -------------------------------------------------------------------------
# Publish to TestPyPI (on tag push only – gates production publish)
# -------------------------------------------------------------------------
publish-testpypi:
name: Publish to TestPyPI
runs-on: ubuntu-latest
needs: build
if: startsWith(github.ref, 'refs/tags/python-sdk-v')
environment:
name: testpypi
url: https://test.pypi.org/project/csm-tcp-router-client/

permissions:
id-token: write # required for OIDC trusted publishing

steps:
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: python-sdk-dist
path: dist/

- name: Publish to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/

# -------------------------------------------------------------------------
# Publish to PyPI (on tag push only – after TestPyPI succeeds)
# -------------------------------------------------------------------------
publish:
name: Publish to PyPI
runs-on: ubuntu-latest
needs: publish-testpypi
if: startsWith(github.ref, 'refs/tags/python-sdk-v')
environment:
name: pypi
url: https://pypi.org/project/csm-tcp-router-client/

permissions:
id-token: write # required for trusted publishing (OIDC)

steps:
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: python-sdk-dist
path: dist/

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
84 changes: 84 additions & 0 deletions SDK/python-package/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Changelog

All notable changes to `csm-tcp-router-client` are documented here.

The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

---

## [Unreleased]

---

## [0.2.0] – 2026-04-22

### Added

- `AsyncTcpRouterClient` class: full asyncio API mirroring every method of
`TcpRouterClient`, using `asyncio.StreamReader`/`StreamWriter` and
`asyncio.Queue` for non-blocking I/O.
- Async context-manager support: `async with AsyncTcpRouterClient() as client:`.
- Both sync and `async def` callbacks supported for `subscribe_status()` and
`register_async_callback()` on the async client.
- `AsyncTcpRouterClient` exported from the top-level `csm_tcp_router` package.
- `examples/async_usage.py` – asyncio quickstart demonstrating all features.
- Test suite extended with `tests/test_async_client.py` (48 tests: unit +
integration via `MockServer`); test runner now uses `asyncio_mode = "auto"`.
- `pytest-asyncio` added to CI test dependencies.
- Chinese documentation: `README.zh-cn.md` (full translation of `README.md`).
- `README.md` updated with asyncio quickstart, async API reference table, link
to Chinese docs, and `async_usage.py` in the examples list.
- CI: added `publish-testpypi` job that publishes to TestPyPI *before*
`publish` (production PyPI); production publish now depends on TestPyPI
success; both use OIDC trusted publishing.
- `Framework :: AsyncIO` classifier added to package metadata.

### Changed

- Package version bumped to `0.2.0`.
- `asyncio_mode = "auto"` added to `pyproject.toml` pytest options; all async
tests run automatically without explicit `@pytest.mark.asyncio` decorators.

---

## [0.1.0] – 2026-04-22

### Added

- Initial release of the `csm-tcp-router-client` Python SDK.
- `TcpRouterClient` class with full thread-safe implementation of the
CSM-TCP-Router protocol v0.
- Connection lifecycle: `connect()`, `disconnect()`, `wait_for_server()`,
`connected` property, context-manager support.
- Synchronous command: `send_and_wait()`.
- Asynchronous command: `post()` with `CMD_RESP` handshake.
- No-reply async command: `post_no_reply()` with `CMD_RESP` handshake.
- Round-trip ping: `ping()`.
- Router management helpers: `list_modules()`, `list_api()`, `list_states()`,
`help()`.
- Status / interrupt subscriptions: `subscribe_status()`,
`unsubscribe_status()`, `register_async_callback()`,
`unregister_async_callback()`.
- Polling queues: `status_queue`, `async_response_queue`.
- Typed exception hierarchy: `TcpRouterError`, `ConnectionError`,
`TimeoutError`, `ProtocolError`, `ServerError` (with `.code` and `.message`).
- Public data models: `PacketType`, `Packet`, `CommandResponse`,
`AsyncResponse`, `StatusNotification`.
- Internal protocol v0 codec (`_protocol.py`) with `encode_packet()`,
`decode_header()`, `parse_packet()`; unknown packet types mapped to
`INFO` for forward compatibility.
- Internal TCP transport layer (`_transport.py`) with background daemon
receive thread, `memoryview`-based zero-copy reads, and clean shutdown.
- Comprehensive test suite: unit tests for protocol codec, unit tests for
client dispatch logic (mock transport), and integration tests against a
`MockServer` fixture.
- Examples: `basic_usage.py`, `subscribe_status.py`.
- `pyproject.toml` with `hatchling` build backend; ready for `pip install`
and upload to PyPI.
- GitHub Actions workflow `Python_SDK.yml`: lint (ruff), test (pytest) on
Python 3.8–3.12, build, and optional publish to PyPI on tag.

[Unreleased]: https://github.com/NEVSTOP-LAB/CSM-TCP-Router-App/compare/python-sdk-v0.2.0...HEAD
[0.2.0]: https://github.com/NEVSTOP-LAB/CSM-TCP-Router-App/compare/python-sdk-v0.1.0...python-sdk-v0.2.0
[0.1.0]: https://github.com/NEVSTOP-LAB/CSM-TCP-Router-App/releases/tag/python-sdk-v0.1.0
21 changes: 21 additions & 0 deletions SDK/python-package/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 NEVSTOP-LAB

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Loading
Loading