Skip to content

FYI: Multi-read optimization integrated into upstream python-snap7#1

Open
gijzelaerr wants to merge 18 commits intoQuakeString:masterfrom
gijzelaerr:master
Open

FYI: Multi-read optimization integrated into upstream python-snap7#1
gijzelaerr wants to merge 18 commits intoQuakeString:masterfrom
gijzelaerr:master

Conversation

@gijzelaerr
Copy link

Hi! This is not a code PR — just a heads-up since your repo has issues disabled.

We noticed your multi-variable read optimizer (sort-merge-packetize pipeline) and were impressed by the approach. We've implemented a similar optimization in upstream python-snap7, inspired by your work:

Our PR: gijzelaerr#641

Our implementation follows the same core idea (sort → merge → packetize → extract), with some differences:

  • No parallel dispatch — sequential for now to avoid PLC compatibility concerns with pipelined PDUs
  • No auto-tuning via SZLMaxConnections describes TCP slots, not in-flight PDU tolerance
  • Configurable max_gap via client.multi_read_max_gap
  • Clean cache class (_OptimizationPlan) instead of fields on Client
  • Server-side multi-item support for end-to-end testing

If you're interested, we'd welcome your review or any contributions back to upstream. If there are features from your fork we missed, feel free to open issues or PRs on gijzelaerr/python-snap7.

Thanks for the inspiration!

dependabot bot and others added 18 commits March 18, 2026 09:16
Bumps the all-dependencies group with 4 updates: [ruff](https://github.com/astral-sh/ruff), [tox](https://github.com/tox-dev/tox), [tox-uv](https://github.com/tox-dev/tox-uv) and [uv](https://github.com/astral-sh/uv).


Updates `ruff` from 0.15.5 to 0.15.6
- [Release notes](https://github.com/astral-sh/ruff/releases)
- [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md)
- [Commits](astral-sh/ruff@0.15.5...0.15.6)

Updates `tox` from 4.49.0 to 4.49.1
- [Release notes](https://github.com/tox-dev/tox/releases)
- [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst)
- [Commits](tox-dev/tox@4.49.0...4.49.1)

Updates `tox-uv` from 1.33.1 to 1.33.4
- [Release notes](https://github.com/tox-dev/tox-uv/releases)
- [Commits](tox-dev/tox-uv@1.33.1...1.33.4)

Updates `uv` from 0.10.9 to 0.10.10
- [Release notes](https://github.com/astral-sh/uv/releases)
- [Changelog](https://github.com/astral-sh/uv/blob/main/CHANGELOG.md)
- [Commits](astral-sh/uv@0.10.9...0.10.10)

---
updated-dependencies:
- dependency-name: ruff
  dependency-version: 0.15.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-dependencies
- dependency-name: tox
  dependency-version: 4.49.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-dependencies
- dependency-name: tox-uv
  dependency-version: 1.33.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-dependencies
- dependency-name: uv
  dependency-version: 0.10.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Strengthen messaging around the pure Python rewrite: emphasize that 3.0
completely breaks with the C snap7 shared library wrapper approach, highlight
improved portability and easier installation, and add clear guidance for
reporting issues and falling back to pre-3.0 releases.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The library no longer wraps the C snap7 library as of 3.0, but the name
is kept for backwards compatibility.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add coverage badge and coverage threshold

- Replace artifact upload with codecov/codecov-action@v5 in test workflow
- Add codecov badge to README.rst
- Add coverage threshold of 80% in pyproject.toml and codecov.yml

Closes #619

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Lower coverage threshold to 75% to match current coverage

Current coverage is 78% — set threshold to 75% to provide a safety net
without failing CI. Can be raised as coverage improves.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add project badges to README

Add PyPI version, Python versions, license, CI status, and Read the
Docs badges alongside the existing Codecov badge for a professional
landing page.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Don't fail CI when Codecov upload fails

Codecov v5 requires a token for protected branches, causing
"Token required because branch is protected" errors that block CI.
Coverage upload is best-effort, not a gate.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Add 55 tests covering PDU building/parsing, lifecycle management,
send/recv buffers, parameters, and dual-partner data exchange via
socket pairs.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
The setter functions already worked with memoryview at runtime (using
direct struct.pack() slice assignment), but the type annotations only
accepted bytearray. This caused mypy errors when passing memoryview
objects from ctypes buffers.

- Add Buffer type alias (Union[bytearray, memoryview]) to setters and getters
- Update all function signatures to accept Buffer
- Fix .decode() calls in getters to use bytes() for memoryview compat
- Add 17 memoryview compatibility tests

Credit: LuTiFlekSSer for identifying the memoryview compatibility issue.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
…rs (#644)

Adds 32 new tests exercising server-side protocol handlers through
real client-server communication: block list/info/upload/download,
SZL reads, clock get/set, PLC control (stop/start/compress), and
error scenarios for unregistered areas and nonexistent blocks.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Tests parse_address() for all address types (V, VW, VD, V.bit) and
invalid inputs, plus integration tests for Logo read/write against
the built-in server covering byte, word, dword, and bit operations
including boundary values and read-modify-write bit logic.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Add tests to improve coverage from 78% to ~85%

Add test suites for untested code paths that don't require a real PLC:
- error.py: error routing, check_error(), error_wrap() decorator
- connection.py: socket mocking, COTP parsing, exception paths
- server/__main__.py: CLI entrypoint test
- s7protocol.py: response parser tests with crafted PDUs
- util/db.py: DB/Row type conversions, dict-like interface

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix test_server_cli.py when click is not installed

Use pytest.importorskip to gracefully skip CLI tests when click
(an optional dependency) is not available in the test environment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Revert "Revert async client commits from master"

This reverts commit 746a9b2.

* Fix pre-commit issues: unused imports, hex casing, mypy types

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix CI: add pytest-asyncio dep, ruff formatting, async docs

- Add pytest-asyncio to test dependencies in pyproject.toml
- Apply ruff format to async_client.py (struct.pack arg formatting)
- Add AsyncClient documentation (doc/API/async_client.rst)
- Add async_client to Sphinx toctree
- Add async example to README.rst

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix AsyncClient.get_connected to also check connection.connected

Address PR review comment: verify the underlying Connection object
reports as connected, not just the client-level flag.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Add S7CommPlus protocol scaffolding for S7-1200/1500 support

Adds the snap7.s7commplus package as a foundation for future S7CommPlus
protocol support, targeting all S7-1200/1500 PLCs (V1/V2/V3/TLS).

Includes:
- Protocol constants (opcodes, function codes, data types, element IDs)
- VLQ encoding/decoding (Variable-Length Quantity, the S7CommPlus wire format)
- Codec for frame headers, request/response headers, and typed values
- Connection skeleton with multi-version support (V1/V2/V3/TLS)
- Client stub with symbolic variable access API
- 86 passing tests for VLQ and codec modules

The wire protocol (VLQ, data types, object model) is the same across all
protocol versions -- only the session authentication layer differs. The
protocol version is auto-detected from the PLC's CreateObject response.

Reference: thomas-v2/S7CommPlusDriver (C#, LGPL-3.0)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add S7CommPlus server emulator, async client, and integration tests

Server emulator (snap7/s7commplus/server.py):
- Full PLC memory model with thread-safe data blocks
- Named variable registration with type metadata
- Handles CreateObject/DeleteObject session management
- Handles Explore (browse registered DBs and variables)
- Handles GetMultiVariables/SetMultiVariables (read/write)
- Multi-client support (threaded)
- CPU state management

Async client (snap7/s7commplus/async_client.py):
- asyncio-based S7CommPlus client with Lock for concurrent safety
- Same API as sync client: db_read, db_write, db_read_multi, explore
- Native COTP/TPKT transport using asyncio streams

Updated sync client and connection to be functional for V1:
- CreateObject/DeleteObject session lifecycle
- Send/receive with S7CommPlus framing over COTP/TPKT
- db_read, db_write, db_read_multi operations

Integration tests (25 new tests):
- Server unit tests (data blocks, variables, CPU state)
- Sync client <-> server: connect, read, write, multi-read, explore
- Async client <-> server: connect, read, write, concurrent reads
- Data persistence across client sessions
- Multiple concurrent clients with unique sessions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Clean up security-focused wording in S7CommPlus docstrings

Reframe protocol version descriptions around interoperability rather
than security vulnerabilities. Remove CVE references and replace
implementation-specific language with neutral terminology.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix CI: remove pytest-asyncio dependency, fix formatting

Rewrite async tests to use asyncio.run() instead of @pytest.mark.asyncio
since pytest-asyncio is not a project dependency. Also apply ruff
formatting fixes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add pytest-asyncio dependency and use native async tests

Add pytest-asyncio to test dependencies and set asyncio_mode=auto.
Restore async test methods with @pytest.mark.asyncio instead of
asyncio.run() wrappers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix CI and add S7CommPlus end-to-end tests

Fix ruff formatting violations and mypy type errors in S7CommPlus code
that caused pre-commit CI to fail. Add end-to-end test suite for
validating S7CommPlus against a real S7-1200/1500 PLC.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Enhance S7CommPlus connection with variable-length TSAP support and async client improvements

Support bytes-type remote TSAP (e.g. "SIMATIC-ROOT-HMI") in ISOTCPConnection,
extend S7CommPlus protocol handling, and improve async client and server emulator.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add extensive debug logging to S7CommPlus protocol stack for real PLC diagnostics

Adds hex dumps and detailed parsing at every protocol layer (ISO-TCP,
S7CommPlus connection, client) plus 6 new diagnostic e2e tests that
probe different payload formats and function codes against real hardware.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix S7CommPlus wire format for real PLC compatibility

Rewrite client payload encoding/decoding to use the correct S7CommPlus
protocol format with ItemAddress structures (SymbolCrc, AccessArea,
AccessSubArea, LIDs), ObjectQualifier, and proper PValue response
parsing. Previously the client used a simplified custom format that
only worked with the emulated server, causing ERROR2 responses from
real S7-1200/1500 PLCs.

- client.py: Correct GetMultiVariables/SetMultiVariables request format
- async_client.py: Reuse corrected payload builders from client.py
- codec.py: Add ItemAddress, ObjectQualifier, PValue encode/decode
- protocol.py: Add Ids constants (DB_ACCESS_AREA_BASE, etc.)
- server.py: Update to parse/generate the corrected wire format

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix S7CommPlus LID byte offsets to use 1-based addressing

S7CommPlus protocol uses 1-based LID byte offsets, but the client was
sending 0-based offsets. This caused real PLCs to reject all db_read
and db_write requests. Also fixes lint issues in e2e test file.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add S7CommPlus session setup and legacy S7 fallback for data operations

Implement the missing SetMultiVariables session handshake step that echoes
ServerSessionVersion (attr 306) back to the PLC after CreateObject. Without
this, PLCs reject data operations with ERROR2 (0x05A9).

For PLCs that don't provide ServerSessionVersion or don't support S7CommPlus
data operations, the client transparently falls back to the legacy S7 protocol.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Potential fix for code scanning alert no. 9: Binding a socket to all network interfaces

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
* Add CLI tools for direct PLC interaction (read, write, dump, info)

Expand the snap7 CLI beyond snap7-server with subcommands for reading,
writing, dumping, and inspecting PLCs directly from the terminal.

Closes #621

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix CI: skip CLI tests when click is not installed

Uses pytest.importorskip("click") so test collection doesn't fail
in CI environments that only install the test dependencies.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Rename CLI entry point from snap7 to s7, add discover hook

Renames the CLI command from `snap7` to `s7` for a cleaner interface.
Adds a hook to auto-register `s7 discover` when the discovery module
is available, so both PRs compose cleanly when merged.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Adds db_read_*/db_write_* convenience methods for bool, byte, int, uint,
word, dint, udint, dword, real, lreal, string, and wstring types.
Closes #617

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Adds 70 tests validating TPKT, COTP, and S7 protocol encoding against
the specification at byte level.
Closes #620

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Add examples cookbook and troubleshooting documentation

Based on analysis of 312 issues and discussions, the two biggest
sources of user confusion are S7-1200/1500 configuration and data
type handling. These new pages address the most common questions.

examples.rst: rack/slot reference table, PLC address mapping guide,
complete data types cookbook (BOOL through DATE_AND_TIME), memory
areas, analog I/O, multi-variable read, and server setup for testing.

troubleshooting.rst: error message reference table, S7-1200/1500
TIA Portal configuration steps, connection recovery patterns, timeout
configuration, thread safety, and protocol limitations FAQ.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add PLC support matrix documentation

Add a new page documenting which Siemens PLC families are supported,
their protocol capabilities, PUT/GET configuration requirements, and
alternatives for unsupported PLCs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Cleanup: consolidate tests, fix docs, remove README async section

README:
- Remove async support section (unnecessary on landing page)

Documentation:
- Add S7CommPlus API docs with experimental warning
- Add experimental warning to AsyncClient docs
- Update PLC support matrix for S7CommPlus V1/V2 status

Test consolidation (no test logic changed):
- Merge test_server_coverage.py into test_server.py
- Merge test_partner_coverage.py into test_partner.py
- Merge test_logo_coverage.py into test_logo_client.py
- Merge test_db_coverage.py into test_util.py
- Rename test_s7protocol_coverage.py to test_s7protocol.py

Mypy fixes:
- Widen Row.set_value type to accept date/datetime/timedelta
- Add type annotations in test_s7protocol.py, test_partner.py,
  test_connection.py, test_async_client.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix ruff formatting in test_util.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Improve S7CommPlus test coverage and fix Codecov upload

Add 154 new unit tests covering codec decoders, PValue parsing for all
data types, payload builders/parsers, connection response parsing, and
client error paths. S7CommPlus coverage rises from 77% to 87%, with
codec.py reaching 100%. Also add CODECOV_TOKEN to the workflow to fix
silent upload failures on protected branches.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Cleanup: consolidate tests, fix docs, remove README async section

README:
- Remove async support section (unnecessary on landing page)

Documentation:
- Add S7CommPlus API docs with experimental warning
- Add experimental warning to AsyncClient docs
- Update PLC support matrix for S7CommPlus V1/V2 status

Test consolidation (no test logic changed):
- Merge test_server_coverage.py into test_server.py
- Merge test_partner_coverage.py into test_partner.py
- Merge test_logo_coverage.py into test_logo_client.py
- Merge test_db_coverage.py into test_util.py
- Rename test_s7protocol_coverage.py to test_s7protocol.py

Mypy fixes:
- Widen Row.set_value type to accept date/datetime/timedelta
- Add type annotations in test_s7protocol.py, test_partner.py,
  test_connection.py, test_async_client.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix ruff formatting in test_util.py

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Improve S7CommPlus test coverage and fix Codecov upload

Add 154 new unit tests covering codec decoders, PValue parsing for all
data types, payload builders/parsers, connection response parsing, and
client error paths. S7CommPlus coverage rises from 77% to 87%, with
codec.py reaching 100%. Also add CODECOV_TOKEN to the workflow to fix
silent upload failures on protected branches.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Restructure docs into logical sections

Split the monolithic examples.rst and troubleshooting.rst into focused,
topic-based pages and group them under clear sections in the toctree:
Getting Started, User Guide, Troubleshooting, Development, API Reference.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant