Skip to content

fix: treat i2's NotSet signature default as required / no default - #34

Merged
thorwhalen merged 2 commits into
masterfrom
not-set-defaults-are-required
Sep 22, 2026
Merged

thorwhalen merged 2 commits into
masterfrom
not-set-defaults-are-required

Conversation

@thorwhalen

Copy link
Copy Markdown
Member

What

Step 2 of the re-land plan in i2mint/i2#48, for front: treat i2's NotSet sentinel as a signature default the same way as inspect.Parameter.empty ("required, no default").

  • New front.util.param_default(param): returns param.default, or Parameter.empty when it is NotSet.
  • Used everywhere front reads a signature default: InputBase.__post_init__ (value prefill), mk_input_element_specs (type inference, is_noneable), func_to_pyd_model_specs (pydantic default), _annotate_func_arguments (annotation from default type).

Why

i2mint/i2#88 gave FuncFactory signatures NotSet defaults and was reverted in i2mint/i2#89, because IntInputBase/FloatInputBase crashed on int(NotSet) and TextInputBase was prefilled with the text "NotSet". With this PR, front already behaves when #88 is re-applied.

No-op against the current i2: no current signature carries NotSet, so param_default(p) is p.default for every existing parameter.

i2 compatibility (decision)

Uses i2.is_not_set (added in i2mint/i2#91) when available, otherwise falls back on i2.deco.NotSet (present since 2023). So the i2 floor is not raised. Chosen over declaring a minimum i2 version because the consumer change must work before and after the i2 release, and raising a floor forces every front user to upgrade i2 for a no-op.

Tests

tests/test_not_set_defaults.py builds _foo's signature with NotSet defaults (using i2.deco.NotSet, so it works with any i2) and checks: no crash, no prefill, NotSet never reaches state; real defaults still prefill; input specs are identical to the plain signature's; pydantic specs mark the params required. 4 of these fail without the fix.

Gate

  • pytest --doctest-modules front tests (scrap/examples ignored), py3.12: 71 passed, with i2 master (has is_not_set) and with i2 0.1.73 from PyPI (fallback path). Master: 63 passed.
  • ruff check / ruff format --check on changed files: clean.
  • Dependents (with this branch + i2 master), same as baseline: streamlitfront 16 passed (selenium-only modules skipped, not installed), opyratorfront 2 passed, extrude 3 passed.
  • Hosted CI: see checks.

🤖 Generated with Claude Code

thorwhalen and others added 2 commits September 22, 2026 15:19
Input elements, input specs, pydantic model specs and argument annotation
now read defaults through `front.util.param_default`, which maps i2's
`NotSet` sentinel to `Parameter.empty`. So a signature with NotSet
defaults (as a re-landed i2mint/i2#88 FuncFactory would show) no longer
crashes numeric inputs (`int(NotSet)`), prefills text inputs with
"NotSet", or becomes a pydantic default. No-op for current signatures.

Uses `i2.is_not_set` when available, else falls back on `i2.deco.NotSet`,
so the i2 floor is unchanged.

Refs i2mint/i2#48

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ly (review follow-up)

The fixture no longer mutates _foo in place (Sig.__call__ sets __signature__
on its argument), so comparisons against the plain signature are meaningful;
6 of 8 tests now fail without the fix. The i2 fallback imports i2.deco.NotSet
at import time instead of on every call.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@thorwhalen

Copy link
Copy Markdown
Member Author

Independent refute-review (sub-agent) could not refute the change: pydantic (incl. the field-rename path), crude dispatch and input specs all treat NotSet as required, with both i2 master and i2 0.1.73. Follow-ups applied in the latest commit:

  • One test proved nothing. Worse, the fixture used Sig(_foo).ch_defaults(...)(_foo), which sets __signature__ on _foo itself, so the "plain" comparisons compared NotSet against NotSet. The fixture is now a separate function, input specs are keyed on the sentinel's type, and _annotate_func_arguments has a test. 6 of 8 tests now fail against master front.
  • The fallback now imports i2.deco.NotSet at import time rather than on each call.

Accepted as-is (low): a crude-wrapped function called directly (not via the UI) with a missing arg now receives NotSet instead of raising TypeError. Python applies the signature default here, not front, and a real FuncFactory filters NotSet itself.
Out of scope here: streamlitfront's deprecated dispatch_funcs/SimplePageFunc path reads sig.defaults directly. It will be handled in a separate streamlitfront PR.

Gate re-run: 72 passed with i2 master and with i2 0.1.73.

@thorwhalen
thorwhalen merged commit ba74100 into master Sep 22, 2026
12 checks passed
@thorwhalen
thorwhalen deleted the not-set-defaults-are-required branch September 22, 2026 15:27
thorwhalen added a commit to i2mint/streamlitfront that referenced this pull request Sep 22, 2026
…#13)

The legacy spec builders (`infer_type`, `get_func_args_specs`,
`page_funcs.*get_func_args_specs`, `get_func_elements_commands`) read
defaults through the new `util.signature_defaults(sig)`, which drops
params whose default is i2's `NotSet` sentinel. So a signature with NotSet
defaults (as a re-landed i2mint/i2#88 FuncFactory would show) gives the
same widgets as the plain one. No-op for current signatures. The
`mk_app` path goes through front, fixed separately (i2mint/front#34).

Uses `i2.is_not_set` when available, else falls back on `i2.deco.NotSet`.

Refs i2mint/i2#48

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant