Skip to content

Fork Changes

cbyte edited this page Jul 23, 2026 · 2 revisions

Fork Changes

This page summarises what's different in H47R15/steam vs. the upstream ValvePython/steam. For the definitive current diff, see the README.rst at the repo root.

Upstream is largely inactive. This fork exists to keep the library working against modern Python and current Steam wire protocols. The shape of the public API is intentionally preserved — the same SteamClient, WebAPI, WebAuth, SteamID, etc. call sites work.

Python baseline

  • Python 3.13+ only. Dropped every py2 / py<3.4 compat shim:
    • six, six.moves
    • raw_input polyfills
    • xrange
    • long
    • win_inet_pton (py2.7 Windows-only IPv4 helper)
    • backports.lzma
    • enum34

The stdlib on Python 3.13 provides all of these natively.

Older Pythons are not supported — the runtime pyproject.toml pin is ^3.13.11.

Protobuf runtime

  • Runtime protobuf dependency bumped from ==3.20.3 to >=5.26,<7.

  • Regenerated every _pb2.py with protoc 33.2 against fresh SteamDB proto sources. The old per-message _reflection.GeneratedProtocolMessageType codegen shrunk ~20× to the modern _descriptor_pool.AddSerializedFile + _builder pattern.

    Example: steammessages_base_pb2.py went from ~2 200 lines to ~96 lines. The runtime does one AddSerializedFile call at import time and lets _builder construct message classes lazily — no more dynamic attribute mutation of a top-level dict, which pyright / Pylance could never introspect.

  • Upper bound <7 reserves the next major bump for a deliberate migration, not an accidental one.

Type checking

  • Full .pyi type stubs for every _pb2 file via mypy-protobuf. msg.field accesses now type-check under Pylance / pyright.
  • py.typed marker at steam/py.typed (PEP 561) — signals that this package ships inline types.
  • types-protobuf >= 4.24 dev dep — provides the base Message typeshed stubs the domain .pyi files inherit from.
  • Local stub override at typings/google/protobuf/internal/builder.pyi — patches a typeshed bug in BuildServices. See Type Checking for details.

Poetry-first workflow

  • Removed setup.py, Makefile, dev_requirements.txt.
  • Single pyproject.toml for the runtime deps, dev deps, extras (client), and metadata.
  • Regeneration steps registered as poetry run pb-* console scripts:
    • poetry run pb-fetch — download proto sources.
    • poetry run pb-compile — protoc + post-process.
    • poetry run pb-services — regenerate steam/core/msg/unified.py service map.
    • poetry run pb-gen-enums — regenerate steam/enums/proto.py from *_pb2.
    • poetry run pb-update — all four in sequence (the usual entry point).
  • Removed Vagrantfile (the old Vagrant-based dev setup is no longer needed).

Full workflow on Regenerating Protobufs.

Async + FastAPI + MCP (new in 1.6)

Additive — no change to the sync surface. Upstream stops at the gevent-based SteamClient; this fork adds an asyncio-facing layer on top.

  • steam.aio.AsyncSteamClient — asyncio facade around SteamClient. Runs the sync client on a dedicated daemon thread with its own isolated gevent hub. The asyncio process is never monkey-patched, so httpx / uvicorn / motor keep working. See AsyncSteamClient for the design and full API.
  • Auto-reconnect + relogin on CM disconnect, with ReconnectPolicy for backoff / retry tuning.
  • Typed exception hierarchy (steam.aio.errors) — AsyncSteamError base + SteamNotStartedError, SteamClosedError, SteamLoginError (carries EResult), SteamReconnectError, SteamRPCTimeoutError. Multi-inherits from RuntimeError / TimeoutError for backwards-compat with callers catching stdlib types.
  • Event bridgeawait client.wait_event(name, timeout=...) for single-shot, async for evt in client.events(*names) for streaming subscription with a bounded internal queue.
  • Cancellation — cancelling the awaiting coroutine kills the underlying gevent greenlet (no zombie greenlets on the runner thread).
  • AsyncSteamPool — multi-account pool with concurrent bringup, round-robin selection, per-member failure isolation.
  • Status + metrics hookclient.status returns a JSON-serialisable ClientStatus dataclass suitable for a FastAPI /health endpoint. metrics_hook= callback fires on every lifecycle transition and RPC boundary; prometheus_hook() factory included.
  • Framework integrations at steam.aio.integrations.fastapi / steam.aio.integrations.taskiqlifespan + Depends for FastAPI, broker startup hook + TaskiqDepends for TaskIQ. Framework SDKs imported lazily so users don't pay for what they don't use.

Model Context Protocol tools (new in 1.6)

  • steam.mcp — exposes AsyncSteamClient as MCP tools an LLM agent can call.
    • Framework-agnostic tool definitions (Pydantic schemas + async handlers) at steam.mcp.tools.
    • Thin FastMCP adapter at steam.mcp.server — works with the official mcp SDK (mcp.server.fastmcp.FastMCP) and the standalone fastmcp package.
    • Three built-in tools: steam.status, steam.get_product_info, steam.send_um.
    • Extend or filter via the bindings= parameter to register_steam_tools.

See MCP for the full walkthrough.

New proto files (~50)

Picked up from upstream Steam since the fork was last synced. Includes:

  • Family groups.
  • Game recording.
  • Remote client.
  • SteamOS webui messages.
  • HTML messages.
  • Virtual controller.
  • Community messages.

steam/enums/proto.py grew from ~90 enums to ~247 enums as a consequence.

Bug fixes

Real latent bugs surfaced while porting. A few notable ones:

  • struct.py TypeError: unsupported operand type(s) for +: 'list' and 'map' — py2 semantics returned a list from map(...); py3 returns an iterator. The list + map(...) expression only ever worked on py2. Fixed.
  • MarketingMessage.flags tuple-vs-int mismatch — the flags field was being unpacked as a tuple in one code path and indexed as an int in another. Fixed.
  • hexlify(None) in avatar-URL fallback — accounts without an avatar returned None from the source field; hexlify doesn't accept None. Guarded.
  • Broken CookieJar iteration in WebAuthRequestsCookieJar mixes CookieJar (yields Cookie) with MutableMapping[str, str] (yields str). Direct iteration was type-ambiguous and misbehaved under py3. Fixed by wrapping in list(iter(...)).

What we intentionally kept

  • Same public API. SteamClient, WebAPI, WebAuth, SteamID, SteamAuthenticator, CDNClient, game_servers.query_master, game_servers.a2s_* — all live at their old import paths with their old signatures.
  • Same MIT license.
  • Same class-level docstrings.
  • Same event names (EVENT_LOGGED_ON, EVENT_DISCONNECTED, etc.).

Upstream ReadTheDocs steam.readthedocs.io is mostly still applicable — the library shape has not diverged, only its Python runtime and protobuf vintage baseline.

What we intentionally removed

  • Vagrantfile — no more Vagrant.
  • Py2 shims — six, xrange, raw_input, long, win_inet_pton, backports.lzma, enum34.
  • setup.py / Makefile / dev_requirements.txt — replaced by pyproject.toml.
  • lxml dependency in WebAPI — replaced with stdlib xml.etree.ElementTree. Zero runtime dep. If you need XPath 1.0 or other lxml features, pass raw=True and parse the text yourself.

Version cadence

Versioning is managed by python-semantic-release. Conventional-commit messages drive the version bumps automatically in CI. See Contributing for commit style.

Upstream links (reference only)

Clone this wiki locally