chore(release): 0.7.1, with the review follow-ups to #266 - #267
Merged
Conversation
Version bump across all six distributions (`tests/test_packaging.py` fails the build if a sibling pin is left behind), plus the findings from reviewing #266 after it merged. Shipping a release with those still open would have baked a known-weak guard into a live-trading build. Follow-ups to #266: * `test_broker_strict_flags_match_mypy_strict` died with a bare `StopIteration` in exactly the case it exists to catch -- someone re-collapsing the broker block to `strict = true`. It now asserts, and the message points at the pyproject comment explaining why the block is expanded. * That test's `warn_redundant_casts` exclusion was implicit: the flag is absent from the default-vs-strict diff only because it is currently mypy's default. Were that default to flip, the test would demand the flag in a per-module section where mypy refuses to accept it -- unsatisfiable. Excluded by name. * `TradeOutcome` was spelled with the PEP 695 `type` statement. `get_type_hints` leaves such an alias as a `TypeAliasType` whose `get_origin()` is `None`, while the assignment form resolves to `Literal` -- and `commands.rules._declared_choices` validates operator-supplied `rules add --params` by testing precisely `get_origin(hint) is Literal`. No effect today (`TradeOutcome` is in no rule constructor), but it planted the spelling that silently disables that validation beside the modules whose `--params` safety depends on it. Reverted to the assignment form used by `StopMethod`/ `TargetMethod`. Three new user-visible behaviours from #266 shipped untested; all three mutations survived the whole suite. Now covered, and each verified to fail when its guard is removed: * `summarize()` rejecting a closed trade with no P&L, asserting the outcome appears in the message -- the named diagnostic is the entire point of raising. * Both `_human_dt(None)` guards in the TUI. These never raised: `time.localtime(None)` means "now", so a missing timestamp rendered the current instant as fact. Each test asserts the text says "unknown" AND does not contain the rendered current time. Not added: a test pinning `cli.avg_hold_hours`'s `exit_ts` filter. Both `SimTrade` producers set `outcome`/`exit_ts` together, so `outcome != "open"` implies `exit_ts is not None` and the filtered denominator provably cannot differ. Covering it means fabricating a state the code cannot reach, which would pin an arbitrary choice rather than a behaviour. 2723 -> 2726 tests. mypy, ruff clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Bumps all six distributions to 0.7.1 and closes the findings from reviewing #266 after it merged. Per
docs/RELEASING.md, the release workflow refuses to set the version itself — that decision belongs in a reviewed PR.Why the fixes ride along
#266 merged before its review findings were applied. Releasing 0.7.1 without them would bake a known-weak guard into a live-trading build, so they are here rather than in a follow-up.
Follow-ups to #266
test_broker_strict_flags_match_mypy_strictfailed badly in its own headline case. Re-collapsing the broker block tostrict = true— the exact regression the test exists to catch — made it die with a bareStopIterationfrom an exhausted generator, the least legible failure pytest renders. It now asserts, and the message points at the pyproject comment explaining why the block is expanded.warn_redundant_castsexclusion was implicit. The flag is missing from the default-vs-strict diff only because it is currently mypy's default. If that default ever flips, the flag entersexpected, the test demands it in a per-module section, and mypy rejects it there as global-only — leaving the config unsatisfiable. Now excluded by name.TradeOutcomeused the PEP 695typestatement.get_type_hints()leaves such an alias as aTypeAliasTypewhoseget_origin()isNone, whereas the assignment form resolves through toLiteral.commands.rules._declared_choicesvalidates operator-suppliedrules add --paramsby testing preciselyget_origin(hint) is Literal. No impact today —TradeOutcomeappears in no rule constructor — but it introduced the spelling that silently disables that validation, right beside the modules whose--paramssafety depends on the other one. Reverted to the formStopMethod/TargetMethodalready use.Tests for behaviours #266 shipped uncovered
Three new user-visible behaviours had zero coverage — all three mutations survived the entire suite. Each new test was verified to fail when its guard is removed:
summarize()rejecting a closed trade with no realised P&L, asserting the outcome appears in the message. The named diagnostic is the whole reason for raising: unguarded this surfaced asTypeError: unsupported operand type(s) for +: 'Decimal' and 'NoneType'from inside a generator, with no way to tell which trade caused it._human_dt(None)guards in the TUI. These never raised —time.localtime(None)means now — so a missing timestamp rendered the current instant as fact. Each test asserts the text saysunknownand does not contain the rendered current time.Deliberately not added: a test pinning
cli.avg_hold_hours'sexit_tsfilter. BothSimTradeproducers setoutcomeandexit_tstogether, sooutcome != "open"impliesexit_ts is not Noneand the filtered denominator provably cannot differ from the old one. Covering it would mean fabricating a state the code cannot reach, pinning an arbitrary choice rather than a behaviour.Version bump
All 12 version strings across 6
pyproject.tomlfiles, plusuv.lock. The==sibling pins move too —tests/test_packaging.pyfails the build if one is left behind.Verification
mypy— clean, 224 source filesruff check keel tests packages— cleanpytest— 2726 passed, 1 skipped (2723 + the 3 new tests)After merge
Actions → Release → Run workflow, input
0.7.1.🤖 Generated with Claude Code