You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
ClassMechanics and weapon_attack are now part of the public API (dndwright.__all__,
72 → 74 names). Both were already reachable — ClassMechanics only via the private dndwright.rules.components, and weapon_attack via the published dndwright.combat
sub-API — and both are now importable directly from dndwright. Additive: no existing
import path was removed, so nothing breaks.
The API contract test pins all three of its surfaces accordingly: the exported name set, weapon_attack's signature (every public free function must be pinned), and ClassMechanics's field list (every public pydantic model must be pinned).
A minor version bump is owed at the next release under SemVer, since this widens the
public surface. Deliberately not bumped here: bumping without releasing is what left
the sibling package mapwright with a 0.28.0 that looks shipped and never was.
Changed
Examples and the module docstring now teach published import paths only. examples/multiclass.py and the __init__ docstring imported ClassMechanics from dndwright.rules.components; examples/stat_diff.py imported compute_stat_diff from dndwright.rules.character_evaluator even though that name was already public. Since tests/test_examples.py executes every example, the package was effectively guaranteeing
that private import paths keep working.
Added (2)
dndwright.combat now re-exports both submodules' full declared surfaces — the 20 names
in conditions.__all__ and the 7 in initiative.__all__. combat.__all__ goes 22 → 49, so from dndwright.combat import attempt_save, order_initiative works instead of needing a
second import from the submodule. Additive: dndwright.combat.conditions and .initiative remain importable exactly as before.
Both submodule surfaces were ALREADY contract-pinned (EXPECTED_CONDITIONS_PUBLIC, EXPECTED_INITIATIVE_PUBLIC); this publishes them one level up and pins the wider EXPECTED_COMBAT_PUBLIC to match.
The full submodule surfaces were taken rather than only the nine names the examples
happened to use, because four of those nine return types (SaveResult, ConditionChange, TurnAdvance, InitiativeRoll) would otherwise have stayed
unpublished — leaving callers able to invoke a function but unable to name its result.
examples/conditions.py and examples/initiative.py now import from dndwright.combat. No example imports a private path any more.
Known
examples/conditions.py and examples/initiative.py still import names that are on no
published surface — attempt_save, tick_conditions, ActiveCondition, ROUND_END, SAVE_ENDS, order_initiative, advance_turn, roll_initiative, InitiativeEntry. dndwright.combat.__all__ exports the conditions and initiativesubmodules but none
of the functions within them. Widening the surface further is a separate decision and has
not been taken.
Changed
Adopted the wider lint families (I, SIM, UP, RUF) on top of the existing
explicit rule set, in a deliberate pass rather than as a dependency side effect:
import sorting, f-string conversions, __all__ sorting, and a deprecated import. PL is not adopted (262 findings, magic values that are the ruleset itself); RUF001-003, RUF005 and RUF012 are declined with reasons stated in pyproject.toml.
Added
RUF046 is enabled here, and the reason is now checked.RUF046 rewrites int(round(x)) into round(x), which is only safe while no value is a numpy
scalar — round(np.float64) returns a numpy float, not an int. dndwright has no
numpy by rule, so the rewrite is safe; the sibling package mapwright is numpy-based
and declines the same rule. Those two decisions are coupled and nothing said so, so tests/test_packaging_contract.py now fails if numpy is ever added while RUF046
is still enforced, naming the reason.
Notes
No behavioural change and no version bump. The public API is the same set of 72
names (__all__ declaration order moved when it was sorted; the set did not), and
seeded dice rolls and a computed character sheet hash identically against a detached
worktree of the previous commit.