Skip to content

Support validation of subscripted collections.abc.Callable types#27

Closed
maread99 wants to merge 1 commit into
mainfrom
claude/awesome-lovelace-hjvwt
Closed

Support validation of subscripted collections.abc.Callable types#27
maread99 wants to merge 1 commit into
mainfrom
claude/awesome-lovelace-hjvwt

Conversation

@maread99

@maread99 maread99 commented Jun 2, 2026

Copy link
Copy Markdown
Owner

Summary

Previously valimp only verified that an input annotated with collections.abc.Callable was callable; any subscripted argument types and return type were ignored (this was noted as a limitation in the README and module docstring).

This PR adds validation of a callable input's signature against the subscripted types.

What's validated

For an annotation such as collections.abc.Callable[[str, int], bool]:

  • Arity — the number of positional arguments accepted by the input is validated as accommodating the number of subscripted argument types. *args, parameters with defaults and required keyword-only parameters are all accounted for. Skipped when the arguments are subscripted as ... (e.g. Callable[..., bool]).
  • Annotations — where the input annotates a parameter or its return, that annotation is validated as matching the corresponding subscripted type. Any parameter or the return that the input does not annotate is treated as typing.Any (so unannotated callables, e.g. lambdas, conform on the basis of arity alone). Per-position annotations of arguments absorbed by *args are not checked.

Validation is skipped (and passes) where the input's signature cannot be introspected, as can be the case for some built-in callables.

Implementation

  • New validates_against_callable_hint (and helper get_callable_positional_params) in src/valimp/valimp.py, dispatched to from validates_against_hint when a Callable hint is subscripted.
  • Module docstring updated to document the new behaviour.

Documentation

  • README — the main example is extended with a subscripted Callable parameter, and the corresponding return/error examples are updated. The supported-types list and limitations section are updated.
  • Tutorial — the collections.abc.Callable section is rewritten to demonstrate conforming inputs (including lambdas), subscription-validation failures (arity, parameter-annotation and return-annotation mismatches) and the not-callable error.

Tests

New focused tests cover arity (too few/too many/required keyword-only), parameter- and return-annotation mismatches, ... behaviour, unannotated callables, *args, defaults, callable instances and non-introspectable callables. The shared fixtures are updated so the callables they pass conform under the new behaviour.

All 28 tests pass; ruff check, ruff format --check and mypy are clean (mypy reports only pre-existing errors unrelated to this change).

https://claude.ai/code/session_01XP8SomvVNUYuuuv5qEGmUc


Generated by Claude Code

Previously valimp only verified that an input annotated with
`collections.abc.Callable` was callable; any subscripted argument types
and return type were ignored.

Validation now additionally checks a callable input's signature against
the subscripted types:
- the number of positional arguments accepted by the input is validated
  as accommodating the number of subscripted argument types (unless the
  arguments are subscripted as `...`);
- where the input annotates a parameter or its return, that annotation is
  validated as matching the corresponding subscripted type (unannotated
  parameters/return are treated as `typing.Any`).

Validation is skipped (and passes) where the input's signature cannot be
introspected, as can be the case for some built-in callables.

Tests, the README example and the tutorial are updated to exhibit the
new functionality.

maread99 commented Jun 2, 2026

Copy link
Copy Markdown
Owner Author

Closing this without merging.

On reflection, validating the subscripted types of a Callable annotation falls outside valimp's purpose. valimp is about validating the types of the actual runtime values passed to public functions — for list[str] it inspects the real items, for dict[str, int] the real keys and values. The subscripted-Callable check is a different kind of validation:

  • It validates declarations, not values. A callable's accepted/returned types can't be observed without calling it, so the check introspects the callable's declared annotations — metadata the author may or may not have written — rather than anything about the value itself. An unannotated lambda passes regardless of behaviour.
  • It's inherently incomplete. By design it passes whenever the signature can't be introspected, or annotations are absent, Any, or unresolvable. So it can only catch the narrow case where the author annotated and annotated differently — a soft fit for a validation library.
  • It's inconsistent with valimp's own conformance rules. Matching is by exact equality, whereas valimp elsewhere honours the PEP 484 numeric tower (an int satisfies a float annotation). A callable annotated (int) -> ... would not satisfy Callable[[float], ...].
  • It overlaps with the static type checker's job. Verifying a passed callable's signature against a Callable[...] annotation is squarely what mypy/pyright do at analysis time, and they do it more completely (variance, overloads, ParamSpec, protocols). valimp's value is checking what static checkers can't — actual runtime values, coercion and dynamic parsing.

Validating that an input simply is callable (bare collections.abc.Callable, via the existing isinstance check) remains in scope and is unaffected — accepting a subscripted Callable syntactically while only checking it's callable at runtime is the right behaviour. So the existing limitation noted in the README/module docstring is intentional and stays.

Thanks for the work here; the analysis was useful in clarifying where the package's boundary sits.


Generated by Claude Code

@maread99 maread99 closed this Jun 2, 2026
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