Skip to content

feat: ship py.typed so the package is checked as typed - #16

Draft
chipkent wants to merge 2 commits into
fix/type-hintsfrom
feat/py-typed
Draft

chipkent wants to merge 2 commits into
fix/type-hintsfrom
feat/py-typed

Conversation

@chipkent

@chipkent chipkent commented Oct 24, 2026 •

Copy link
Copy Markdown
Owner

Preview only. This is a draft in the fork showing the exact text of the PR planned for massive-com/client-python. It will be closed, not merged, once the upstream PR is opened.

Fixes #<issue: F4c>

Builds on #14. Merge #15 first: shipping the marker makes checkers see model constructors, so #15 should be in the same release. This branch includes a copy of #15's commit so that #17 can build on it; once #15 merges I rebase and the copy drops out. The change this PR adds is the last commit.

The package has no PEP 561 marker, so mypy skips it (see massive-com#876 and massive-com#379):

$ mypy --strict -c 'from massive import RESTClient'
error: Skipping analyzing "massive": module is installed, but missing library stubs or py.typed marker  [import-untyped]
  • Adds an empty massive/py.typed. The existing packages entry in pyproject.toml already ships it in the wheel and sdist.
  • Re-exports RESTClient and WebSocketClient with import X as X, so from massive import RESTClient passes --strict. No __all__ is added, so from massive import * exports the same names as before; a new test checks the main ones.
  • Runtime behavior is unchanged.

Known limitation. Every REST method is still annotated Union[Model, HTTPResponse], because raw=True returns the undecoded response. Once checkers read the hints, strict users must narrow results:

for t in client.list_tickers():
    t.ticker   # error: Item "bytes" of "Ticker | bytes" has no attribute "ticker"

#17 adds @overloads on raw so the default call returns the model type. It should merge together with this PR, or soon after.

The callback type of WebSocketClient.run() is a second known limitation, massive-com#1022, with a fix in massive-com#1032; this PR does not touch it.

@modelclass builds an __init__ that accepts every field positionally or
by keyword, but type checkers could not see it: a call such as
CryptoQuote(pair="BTC-USD", bid_price=1.5) was reported as
"Unexpected keyword argument" for every field.

Mark modelclass with dataclass_transform (PEP 681) so checkers derive
the constructor from the field annotations. The decorator is imported
only under TYPE_CHECKING, from typing on 3.11+ and from
typing_extensions on 3.10, and is a no-op at runtime, so there is no new
runtime dependency and Python 3.10 keeps working.
Without the PEP 561 marker, mypy skips the installed package and reports
import-untyped, and users who silence that get Any for every massive type.

- Add massive/py.typed. The existing packages entry in pyproject.toml
  already includes it in the wheel and sdist.
- Re-export RESTClient and WebSocketClient explicitly ("X as X"), so
  "from massive import RESTClient" passes mypy --strict, which does not
  treat plain imports in a typed package as re-exports. No __all__ is
  added, so "from massive import *" exports the same names as before.
- Add a test that "from massive import *" still provides the main names.
@coderabbitai

coderabbitai Bot commented Oct 24, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

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