Repository navigation
Conversation
Several hints describe values the API never sends or returns:
- CryptoQuote.bid_price, ask_price and ask_size were int; XQ sends
fractional numbers ("bp": 33052.79, "as": 0.601). They are float.
- IndexValue.timestamp was str; V sends integer Unix milliseconds.
- list_tickers(cusip=...) accepted only int. The API treats a CUSIP as a
string: it is alphanumeric, and leading zeros matter ("037833100").
The hint now accepts str as well as int, so existing callers keep
type-checking, and the docstring recommends a string.
- get_previous_close_agg() was annotated as returning one
PreviousCloseAgg, but it returns the "results" array as a list.
- list_treasury_yields(), list_short_interest(), list_short_volume() and
list_ipos() were annotated as returning a List, but like every other
list_* method they return a paginating iterator.
Hints that are imprecise but still admit every value the API sends, such
as integer timestamps hinted as float, are left alone: narrowing them
would gain little and could break callers that type-check today.
Only annotations and docstrings change; runtime behaviour is the same.
The tests pin the runtime types the corrected REST hints now describe.
@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.
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
Comment |
This was referenced Oct 24, 2026
chipkent
force-pushed
the
feat/raw-overloads
branch
from
October 24, 2026 14:55
5b3307f to
04ec7be
Compare
chipkent
changed the base branch from
feat/py-typed
to
feat/rest-missing-endpoints
October 24, 2026 14:55
Every REST method is annotated as returning Union[Model, HTTPResponse]
(or Union[Iterator[Model], HTTPResponse]), because raw=True returns the
undecoded urllib3 response. A type checker cannot rule out HTTPResponse,
so ordinary code fails:
for t in client.list_tickers():
t.ticker # "HTTPResponse" has no attribute "ticker"
client.get_aggs("AAPL", 1, "day", "2024-01-02", "2024-01-31")[0] # not indexable
Add three overloads to each of the 94 methods that take raw:
- raw: Literal[False] = False returns the model type, so the default
call is precise.
- *, raw: Literal[True] returns HTTPResponse. raw is keyword-only here
because it follows parameters with defaults; giving it a default would
overlap the first overload, which pyright reports.
- raw: bool = False returns the full union, so a non-literal flag, or
raw passed positionally, type-checks exactly as before.
This commit is mechanical: the overloads were generated from each
method's own signature and then formatted with black. They differ from
the signature only in raw and the return type. The implementations, and
the signatures that _get_params inspects at runtime, are unchanged.
Add test_rest/typing_consumer.py, ordinary user code that is checked with mypy --strict but never run. It iterates list_* results, indexes get_* lists, checks that raw=True gives HTTPResponse and a plain bool gives the union, builds models with keyword arguments, pins each corrected hint with assert_type, and keeps code written for earlier releases (isinstance narrowing, an int CUSIP) to show it still checks. make static checks it from inside test_rest/, so massive resolves to the installed package as it does for users. assert_type comes from typing on 3.11+ and from typing_extensions on 3.10 only under mypy, which bundles its stubs; the file is never imported, so nothing new is needed at runtime. AGENTS.md now describes the overload pattern for new endpoints.
chipkent
force-pushed
the
feat/raw-overloads
branch
from
October 24, 2026 15:01
04ec7be to
b037345
Compare
This was referenced Oct 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #<issue: F4d>
Builds on #16 and #7. This branch is based on #7 and merges #16's branch (+56/-18 in 11 files from #14, #15 and #16), so that the overloads also cover #7's two new methods. The change this PR adds is the last two commits.
Every REST method returns
Union[Model, HTTPResponse], whateverrawis, so once the package is typed, ordinary code needsisinstancechecks. This PR gives each of the 94 methods that takerawthree overloads:raw=True:HTTPResponse(keyword-only, so it does not overlap the first overload). Any otherbool: the oldUnion, so existing code still checks.reference.py+2,119/-278,benzinga.py+1,235/-73,financials.py+885/-61,futures.py+644/-17,etf_global.py+637/-14,economy.py+428/-1, and under +300 each in the other eight). It is 94 × 3 declarations plus two names on eachtypingimport line, generated by the script below and then formatted withblack massive. To review, regenerate rather than read: running it on the commit's parent reproduces the commit exactly, and running it again changes nothing. #<issue: F4d> asks whether a generated set like this is acceptable; if it is, I can commit the generator with a check that fails when running it would change anything.test_rest/typing_consumer.py, user code checked bymypy --strictinmake static(never run), and documents the pattern inAGENTS.md. Without the overloads the consumer reports 20 errors; with them, none.typing_consumer.pytypes the WebSocket callback asList[WebSocketMessage], which is today's annotation and the one Fix WebSocketMessage type contract massive-com/client-python#1032 keeps, so the two do not conflict.Literalandoverloadcome fromtyping; the consumer getsassert_typefromtyping_extensionsonly on 3.10 and only under mypy, which depends on it.typingimport line.Generator:
python gen_raw_overloads.py massive/rest/*.py && black massive