ADR-029: Free functions¶
Status¶
Accepted; amended by ADR-053.
Decision¶
D1. The unit is one call, and a refusal is local to it¶
A free function holds no state, so rewrite_call has no group: each call it
claims is decided alone, and the other calls in the file are still rewritten
around one that refuses. ADR-010 F-1 withholds a file whose usages are not all
auto, so the mixed output never reaches a user's disk.
D2. positional_to_kw is the legacy signature's parameters, and therefore the new call's arguments¶
Its order is the legacy positional order, so a positional argument is lifted
onto the name at its index; its membership is the set of arguments the new call
takes. config_kwargs is the other destination, and a keyword (or splat) in
neither is unsupported_kwarg: passing an unknown keyword through would be a
TypeError at the call under an auto report. A pack that omits a parameter
therefore refuses the calls that pass it. keywords is a third destination, for parameters carried
only when written as keywords: a positional argument beyond positional_to_kw stays
positional_arg_ambiguous (ADR-053 D4).
D3. A configuration object arrives under a name the pack gives it¶
config_kwarg is the keyword the new call takes the configuration object
under. The schema requires config_class, config_kwargs and config_kwarg
together or not at all, since none can be emitted without the others. A call
that carries no configuration field gets no configuration object, which
nobody wrote.
D4. Four coherence rules over the parameter lists, because a pack with no field evidence is checked by its schema¶
| Rule | What it would otherwise produce |
|---|---|
A parameter is in one of positional_to_kw, keywords and config_kwargs, never two |
A destination decided by the order of the rule's own tests |
Every arg_map key is in one of those lists |
A rename that never fires: the call is refused as an unsupported keyword first |
No two parameters land on one keyword once arg_map is applied |
A call naming one keyword twice, which Python refuses at compile time |
Every dispatch_prefixes key is in one of those lists, and no list is empty |
A guard nothing consults, or one that refuses every call |
D5. A call whose answer is not the new call's answer is refused, and the pack states how it knows¶
response_shape_changed, stated three ways. Under result_access_flags
(embed_content returned a mapping; the new object's right reading depends on
the runtime type of content=) a call is written only where its result is
discarded. Under dispatch_prefixes (legacy get_model returned another class
for tunedModels/...; the new call returns types.Model for both) an argument
that is not a literal under a listed prefix is refused. Under result_paths
(a 0.28.1 result was a dictionary and a new one is not) a call is written only
where every read of its result is a listed attribute path (ADR-053 D5). A field
in result_attribute_flags, which the new result lacks, read off the result,
off the for or comprehension target or one name it feeds, or by getattr, is
attribute_removed. Whatever the pack states, a call in an f-string field written with =, which
prints the source text the rewrite changes, is response_shape_changed as well, here and in
generative_model_calls.
D6. The client bail is borrowed, and the scan's blind spot is written down rather than fixed here¶
A module with no client has nothing to make these calls on, and this rule
re-raises the client rule's code or the planner's client_source_unresolved
rather than name one defect twice. The client's placement counts every free
function a rule writes on the client (client_readers). The scan's
needs_client does not, so obelize scan over-states how migratable a
client-less module is; fixing it changes spec_digest and so waits for the
next re-run of the ADR-024 measurement (COVERAGE.md gap 21). A rule rooted on
the module has no client to place and raises neither (ADR-053 D3).
D7. positional_arg_ambiguous covers both shapes of "which parameter is this"¶
More positional arguments than positional_to_kw names, and one parameter
given twice, are both positional_arg_ambiguous. Both are already a
TypeError, and mapping an argument onto a parameter the pack never named
would turn a loud failure into a silent one (ADR-027 D3).
D8. unsupported_kwarg covers resumable= too, and C-47's proposed code is declined¶
upload_file(..., resumable=True) is unsupported_kwarg, like any keyword
with no counterpart; C-47's upload_resumable_unsupported is declined, because
the report already names the line and the keyword.
D9. The layout algorithm is unchanged, and the configuration object has no source¶
The configuration object this rule emits has source None, since nothing
earlier laid its fields out (ADR-026
D7). Its indent is the one the outer call wraps to, and the outer call wraps
whenever the object does (width_trigger.before.py).
D10. The pack's own answer keys are compared, not merely counted¶
tests/packs/test_all_packs.py runs the rules over every positive pack
fixture and compares each <name>.after.py byte for byte, which is
PACK_SPEC's fixture contract; a key exists for exactly the
fixtures a run migrates whole.
Consequences¶
- The development corpus used these four surfaces zero times (ADR-008 D6):
their mappings are verified against the two installed SDKs only, and the
pack's
limitationssays so. get_fileanddelete_filecarry atypes.Filehandle as well as a name; a handle of any other shape raisesValueError, whichlimitationsrecords.- The scan reports a subscript of a legacy name
(
genai.embed_content(...)["embedding"]) once, at the node inside it. - Open: gap 21 (D6); whether
output_dimensionalitytruncates as the legacy SDK did (the pack carries it as an ordinary configuration key); and the call thefiles_surface.before.pyanswer key wraps inside a comprehension, whereruff formatwould break the brackets.