Skip to content

Centralize action-argument validation in modelapi#86

Merged
jrfaeder merged 3 commits into
RuleWorld:mainfrom
wshlavacek:modelapi-centralize-action-validation
May 11, 2026
Merged

Centralize action-argument validation in modelapi#86
jrfaeder merged 3 commits into
RuleWorld:mainfrom
wshlavacek:modelapi-centralize-action-validation

Conversation

@wshlavacek
Copy link
Copy Markdown
Contributor

Summary

Route `Action.init` (in `structs.py`) through a single `ActionList.validate_action` method (added to `utils.py`) so that direct construction of `Action` instances applies the same schema check as the parser.

The previous inline validation in `Action.init` had two bugs:

  • For positional actions (`no_setter_syntax` / `square_braces`, e.g. `setConcentration`, `quit`, `saveConcentrations`) the `arg_dict[type]` lookup returns an empty list `[]`, so the `len(valid_arg_list) > 0` guard skipped validation entirely — any args were accepted.
  • For unknown action types the error message was good, but it ran AFTER setting `self.type`, leaving a half-constructed object alive in the rare case someone catches the exception.

What the new validation enforces

  • Positional actions store args as a dict with `None` values (the canonical shape the parser emits). Keyword-shape dicts passed by hand are rejected with a clear error pointing at the canonical form.
  • Per-action positional arity (`positional_arity` table) catches `Action("quit", {'"x"': None})` (too many) and `Action("setConcentration", {'"A()"': None})` (too few).
  • Normal-type validation: arg names must be in `arg_dict[type]` (existing behavior, just centralized).

No behavior change at parse time

The parser already emits the canonical positional-args-as-`{value: None}` shape, so the stricter direct-construction path doesn't reject anything the parser produces. Only direct `Action(...)` calls with malformed args see a new error (which is the point).

Test plan

  • Existing CI passes
  • `Action("simulate", {"method": '"ode"'})` constructs as before
  • `Action("setConcentration", {'"A()"': None, "100": None})` constructs and gen_strings to `setConcentration("A()",100)`
  • `Action("setConcentration", {"A()": "100"})` raises `BNGParseError` (the bug fix)

wshlavacek and others added 3 commits May 10, 2026 17:28
Route `Action.__init__` (in `structs.py`) through a single
`ActionList.validate_action` method (added to `utils.py`) so that direct
construction of `Action` instances applies the same schema check as the
parser. Previously the inline validation in `Action.__init__` had two
bugs:

- For positional actions (`no_setter_syntax` / `square_braces`,
  e.g. `setConcentration`, `quit`, `saveConcentrations`) the
  `arg_dict[type]` lookup returns an empty list `[]`, so the
  `len(valid_arg_list) > 0` guard skipped validation entirely —
  any args were accepted.
- For unknown action types the error message was good, but it ran
  AFTER setting `self.type`, leaving a half-constructed object alive
  in the rare case someone catches the exception.

The new `validate_action` adds:

- positional actions store args as a dict with `None` values
  (the canonical shape the parser emits) — keyword-shape dicts
  passed by hand are rejected with a clear error.
- per-action positional arity (`positional_arity` table) catches
  `Action("quit", {'"x"': None})` (too many) and
  `Action("setConcentration", {'"A()"': None})` (too few).
- normal-type validation: arg names must be in `arg_dict[type]`
  (existing behavior, just centralized).

`Action.__init__` is simplified to call `validate_action` and then
just track duplicate-arg warnings in a small loop.

No behavior change for parse-time construction: the parser already
emits the canonical positional-args-as-`{value: None}` shape, so the
stricter direct-construction path doesn't reject anything the parser
produces.
Resolved conflict by combining:
- Updated import style (separate lines, shutil instead of distutils)
- Added BNGParseError import needed for validate_action method
@jrfaeder jrfaeder merged commit 014fe1d into RuleWorld:main May 11, 2026
17 checks passed
@wshlavacek wshlavacek deleted the modelapi-centralize-action-validation branch May 11, 2026 17:07
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.

2 participants