Skip to content

fix: reject *args/**kwargs in @inject to prevent silent arg corruption#1

Merged
lesnik512 merged 1 commit into
mainfrom
fix/inject-variadic-guard
Jul 11, 2026
Merged

fix: reject *args/**kwargs in @inject to prevent silent arg corruption#1
lesnik512 merged 1 commit into
mainfrom
fix/inject-variadic-guard

Conversation

@lesnik512

Copy link
Copy Markdown
Member

The bug

Since 2.0.0, @inject binds a task's arguments to its visible signature by name (bound.arguments) — that's what makes injection parameter-order-insensitive. But a by-name call can't faithfully forward *args/**kwargs: inspect.Signature.bind stores their values under the literal names "args"/"kwargs", so a task declaring *args/**kwargs alongside a FromDI parameter had its real payload silently misrouted.

Reproduced (pre-fix): sample.delay(1, 2, foo="bar") on def sample(svc: Annotated[Svc, FromDI(Svc)], *args, **kwargs) arrives inside the task as args=(), kwargs={"args": (1, 2), "kwargs": {"foo": "bar"}} — silent corruption, no error.

(Same latent bug was found and fixed in modern-di-arq during its build; this ports the guard back.)

The fix

@inject now raises a clear TypeError at decoration time when a task declares a VAR_POSITIONAL/VAR_KEYWORD parameter together with a FromDI parameter, naming the offending parameter. A task with no FromDI parameter is returned unchanged and may use *args/**kwargs freely. The DITask path inherits the guard (fails at class instantiation).

TDD: two failing tests first (test_inject_rejects_var_{positional,keyword}_with_fromdi), then the guard. Bug reproduced and fix verified by an independent reviewer. Documented in architecture/dependency-injection.md; notes at planning/releases/2.0.1.md.

100% coverage; ruff/ty/eof-fixer clean.

🤖 Generated with Claude Code

Since 2.0.0, @Inject binds a task's arguments to its visible signature by name
(bound.arguments), which makes injection parameter-order-insensitive but cannot
faithfully forward *args/**kwargs: Signature.bind stores their values under the
literal names "args"/"kwargs", so a task declaring *args/**kwargs alongside a
FromDI parameter had its real payload silently misrouted into a keyword argument
instead of raising.

Reject such signatures at decoration time with a clear TypeError naming the
offending parameter. A task with no FromDI parameter is returned unchanged and
may still use *args/**kwargs. Documented in architecture/dependency-injection.md;
notes at planning/releases/2.0.1.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@lesnik512 lesnik512 merged commit 92d96bb into main Jul 11, 2026
6 checks passed
@lesnik512 lesnik512 deleted the fix/inject-variadic-guard branch July 11, 2026 10:05
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