Just-makeit

Latest version: v0.75.5

Safety actively analyzes 1006753 Python packages for vulnerabilities to keep your Python projects secure.

Scan your dependencies

Page 1 of 47

0.75.5

Added

- **Two `single = true` records sharing a public name now WARN when their
docs disagree, instead of silently dropping one (gh-1270).** gh-1268
made two same-shape records under one name safe to alias
(`B_sum_type = A_sum_type;`) rather than a segfault, but left open what
happens to the aliased record's own doc: its descriptor (with its own
`record_doc` or `--result-field` doc) is still compiled, and then never
used -- nothing ever calls `PyStructSequence_NewType` on it, so a reader
of the aliased method sees the first method's prose on both faces. Two
methods sharing one record almost always share its documentation, so
this is advisory (`_report.warn`, `~`), not the refusal a shape mismatch
earns -- naming both methods, both doc strings, and the two ways out
(`--record-name`, or dropping `--record-doc` from whichever should
inherit the other's). Surfaced wherever a record's runtime type is
assembled: the module aggregator (a parent+view or two sibling objects
sharing a name) and a standalone object's own `PyInit_` (two methods on
one object). `jm status --check` has nothing to fail on.

Fixed

- **A view may now use `<obj>_create` when the parent's `create_fn` points
elsewhere (gh-1277).** The check refusing a view whose constructor matched
its parent's compared against the *default-derived* `<obj>_create`, while
its own message claimed to be naming the parent's. gh-509 lets an object
back its `tp_init` with a differently named C function, and this check
never learned about it -- so under an override, `<obj>_create` stayed the
one name a view could not use even though it was no longer the parent's.
That blocked the arrangement where the **general** constructor is the base
and the specialised one is the flavor: `exclude_properties`/
`exclude_methods` trim a view and never a parent, so a surface belonging to
only one of two front doors must sit on the base, with the trimmed one as
the view -- unreachable when the trimmed one is the historical
`<obj>_create`. Found in doppler, whose `frame` object has a deferred
builder (`frame_create_desc`) and a materialising four-field constructor
(`frame_create`) whose nine builder verbs all refuse. Now compared against
`C.object_create_fn`, the accessor `_apply` already used; byte-identical
for any project that never set `create_fn`.

0.75.4

Fixed

- **A record's docs now reach the runtime type on `apply`, not only the
`.pyi` (gh-1267).** gh-646 made `_record.descriptor_c` emit the record
type's own doc and each field's doc into the two `PyStructSequence`
tables, from the same `_record.fields` / `_record.type_doc` calls the
`.pyi` writer uses -- but nothing refreshed an *existing* fragment. A
module object's `native/src/<mod>/<mod>_ext_<obj>.c` is sacred: it is
spliced, never re-rendered, so hand-written bindings survive, and
`_docsync` carries derived prose into it instead -- for `PyMethodDef`,
`PyGetSetDef` and `tp_doc`, but not for the structseq descriptor. That
descriptor was therefore frozen at the moment the method was first
declared, so documenting the record afterwards (`/**< … */` on the struct
members in the sacred header, or `record_doc` / a `--result-field` doc in
the manifest) reached the `.pyi` and stopped: the stub grew a full
`Attributes` table while `help()` on the built type showed
`{"n", NULL}` and the `Sum(n, mean)` synopsis. Measured in doppler on
0.75.3, where gh-1264 made these types real module attributes and so made
the gap visible. The standalone path never had it -- it re-renders
`<comp>_ext.c` wholesale. Fixed by teaching the transplant the two
tables; those slots have no hand-written variant to protect, since the
whole descriptor is generated from the manifest and the header.

- **One extension module now publishes exactly one type object per record
name, so a view and its parent sharing a `single = true` method no
longer segfault (gh-1268).** gh-1264 deduplicated the list of records to
register in `_record.registrations`, which sees one *component*, while
the name being deduplicated is an attribute of the whole *module*. A view
and its parent each passed their own "first occurrence" test, so the
aggregator emitted `PyModule_AddObject(m, "FrameLayout", ...)` twice with
two different type objects. That call *steals* the reference it is given:
the second dropped the module's only reference to the first type, which
survived on its own `tp_mro`/descriptor self-references as an unreachable
cycle and was freed at the next GC pass -- while the first wrapper's
`static PyTypeObject *` still pointed at it. The next call through that
wrapper read freed memory. Nothing failed at import, and on a fresh
interpreter the first call still returned a correct-looking record, which
is why it shipped; doppler caught it as `make test-stubs` exiting 139.
The rule now lives in `_record.resolve`, over a namespace the module
aggregator keeps for all of its components: a repeat of a name already
claimed by an identical shape **aliases** the first type object
(`SeededAcc_summary_type = Acc_summary_type;`) instead of registering a
second, so both classes return instances of the one class the `.pyi`
declares and `isinstance` holds for both. A repeat claiming one name with
a **different** shape is refused instead -- aliasing would fill a
descriptor of one arity from a kernel of another -- and `jm method`
refuses it before writing anything, naming both claimants, both field
lists, and `--record-name` as the way out.

0.75.3

Fixed

- **A `single = true` method's record type is now created at module init and
registered under its public name, so it is reachable the way the `.pyi`
already advertises it (gh-1264).** The `.pyi` declares `class X(tuple [...])` at module level, so a reader and a type checker both expect
`from pkg.mod import X` -- but the C binding built the `PyStructSequence`
type lazily, inside the method wrapper, and never handed it to
`PyModule_AddObject`. `hasattr(pkg.mod, "X")` stayed `False` even after
the method had run and returned an instance: `type(r).__name__` read
`"X"` while `pkg.mod.X` did not exist, so `isinstance(r, X)` and a docs
directive naming `pkg.mod.X` both had nothing to bind to. Measured in
doppler on jm 0.73.1 for every record it has (`doppler.ber.BerInterval`,
`doppler.measure.ToneMetrics`, `doppler.dsss.ReceiverStatus`). Fixed by
creating the type eagerly at module init -- the same
`PyType_Ready`/`PyModule_AddObject` split every other jm-owned type
already goes through -- in the standalone object's own `PyInit_`, the
module aggregator's `PyInit_<module>`, and the module subpackage's
`__init__.py` re-export list, which previously advertised only the
Component classes.

0.75.2

Fixed

- **`reconcile_param_docs` no longer detaches a wrapped `param` description's
continuation line, and no longer deletes the block's own closing `*/`
when it does (gh-1261).** The parser was purely line-based -- one dict
entry per matched `param NAME ...` line -- so a description that wraps
onto a following physical line (common under a 79-column style) fell
through into "everything else, left in place" at its OLD position. Once
the `param` group was re-emitted elsewhere the orphaned continuation was
left behind, silently attaching one parameter's prose to a different one
or leaving it dangling after the whole group -- and since the block's
closing `*/` satisfies the same "non-blank comment content" test as a
real continuation, a `param` with no following `return` could swallow
the terminator itself; if that param's signature then dropped an
argument, its whole entry -- closing marker included -- vanished with it,
leaving an unterminated `/**` comment that ate the next declaration.
Continuation lines now travel with their owning `param` as one unit,
whether kept, dropped, or reordered, and the closing marker is excluded
from being claimed by any of them. Found re-verifying gh-1257 against
real headers in doppler: multiple sacred headers had a wrapped `param`
description and the type-only `complex` -> `_Complex` respelling was
enough to trigger it even though no `param` set actually changed.

0.75.1

Fixed

- gh-1256's `init_kwargs_drift` fourth axis: covered the `except ValueError`
fallback for a non-numeric C default (a symbolic constant, e.g. an enum
member) that `codecov/patch` flagged after 1258 merged. No behavior
change -- test coverage only.

- **`jm status --check` now catches a retuned `init_params` default that
never reached the sacred binding fragment (gh-1256).** `init_kwargs_drift`
already compared the constructor's keyword names, their order, and (gh-823)
which side of the `PyArg_ParseTupleAndKeywords` `|` each sits on -- none of
those three axes reads the *value* the local variable is seeded with. A
parameter that stays optional at the same position, with only its manifest
`default` changed (`50.0` -> `0.0`), produced identical names in identical
order and an unmoved `|`: no drift on any existing axis, while
`double cn0 = 50.0;` in `native/src/<mod>/<mod>_ext_<obj>.c` kept the stale
value forever -- `apply` regenerated the `.pyi` and even the runtime
docstring's own "default 0.0" text, so every other signal read as current
while the compiled constructor still built with the old default whenever
the caller omitted the keyword. Adds the fourth axis to the one
`init_kwargs_drift` implementation, gated the same way the third one
(gh-823) is: unsuppressible except through `status_allow`, since jm cannot
regenerate a hand-owned constructor body on its own and reconciling the
manifest with the binding (or moving the constructor to an `_extra.c`) is
the author's call to make.

- **`jm apply` no longer swaps `param` docs between two functions in the
same sacred header when only the later one needs its declaration
replaced (gh-1257).** `_reconcile_decl_doc` located the Doxygen block
above a refreshed declaration with `/\*\*.*?\*/` anchored at the end of
the preceding text. A lazy `.*?` is not leftmost-shortest across the
whole match: when the *nearest* `/**` couldn't reach the anchor without
hopping over an intervening `*/`, `re.search` kept stretching that same
candidate past it -- through an unrelated, unchanged function's
prototype and into the target's own comment -- rather than trying the
next, closer `/**` first. The reconciler then rewrote the resulting
span's first `param` group, which belonged to the earlier, unchanged
function, with the later function's parameter names -- corrupting real,
published API documentation for a function nothing about the apply run
touched. Found adopting 0.75.0 in doppler, where gh-1246's
`complex` -> `_Complex` respelling forced nearly every hand-written
prototype with a complex parameter through the replace-by-name path at
once, hitting this in 9 sacred headers.

0.75.0

Added

- **`jm upgrade` migrates a project to the `_Complex` spelling (gh-1248).**
gh-1246 changed what jm *emits*; it could not change what an existing
project already contains, and nothing jm shipped could either. `apply`
cannot rewrite the inline `step()` because it lives in the sacred
`<comp>_core.h`, and `regenerate` leaves it for the same reason -- measured
on 0.74.0, all three of `apply`, `regenerate` and `upgrade` left 8
occurrences untouched. The answer in `docs/upgrading.md` was a
`perl -pi -e` block the author ran by hand.

It lives in `upgrade` because gh-887 already made that the home for "your
project is current by schema number and still needs work" -- it reports
stale manifest keys there rather than claiming "already up to date". This
needs no schema bump either, so it runs on **every** exit of `upgrade`,
including the already-current one; wiring it only into the migration loop
would have meant it never ran for anybody it is for.

It **rewrites** where gh-887 deliberately only reports, and the line
between them is gh-887's own: *"the replacement can alter what your API
does, so it is yours to make, not a migration's."* `check_return` ->
`status_return` changes whether a non-zero return raises. `complex` ->
`_Complex` changes nothing -- `complex` is a `<complex.h>` macro for
`_Complex`, so they are identical tokens after preprocessing. A migration
may rewrite when the rewrite is provably behaviour-neutral, and must only
report when it encodes a decision.

Idempotent: a second run is silent, and so is every run on a project
scaffolded by 0.74.0 or later. `clib_common.h` is skipped -- its comment
quotes the old spelling while explaining why that spelling was a problem,
so a blanket pass would rewrite that prose into a false statement.

Gated end-to-end: a pre-gh-1246 project, migrated, compiles from C++11
with `std::complex` in **both** include orders.

Fixed

- **`make ship` no longer watches the wrong release run.** Re-vendored from
canonical (just-buildit.github.io36). `release-watch` selected the run on
`headBranch == <tag>` alone, which is not enough once a tag has been
deleted and re-pushed: a run from the previous push still carries that
name, and a tag push returns *before* GitHub creates the new run -- so the
"wait for the run to appear" loop was satisfied on its first iteration by a
run that had finished hours earlier.

Measured here on v0.74.0: the first attempt failed its pre-publish smoke,
the tag was deleted, the defect fixed and the tag re-pushed;
`release-watch` then reported the **first** run's failure -- twelve red
smoke jobs, `publish: skipped` -- while the real run was still in progress
and went on to publish cleanly. The ordering was never wrong (`gh run list`
is newest-first and `[0]` is correct); the existence check was.

It matches on the tag's commit now, resolved from the remote with `^{}`
peeling, falling back to name-only with a warning when a tag resolves to
nothing.

Page 1 of 47

Links

Releases

Has known vulnerabilities

© 2026 Safety CLI Cybersecurity Inc. All Rights Reserved.