This repository contains a PoC for dynamically checking the types of ariadne resolvers.
The current version is able to dynamically detect the return types of parent resolvers and ensures that they match the expected parent type of the field resolvers.
The key is to create an artificial function for every ObjectType
, that takes all resolvers related to the object and
tries to coerce the return type of every parent resolver to the same TypeVar
T
, which must also be the same as the
parent object (first argument) expected by all field resolvers of the object.
The function doesn't require a body, but only a typed signature. It can then be called with the actual resolver
functions that are imported from a custom module. If this function call is wrapped inside a if TYPE_CHECKING:
clause,
it will never be executed but only evaluated by a type checker (hence no need for a body).
This PoC contains two approaches to define the signature of this artificial function:
generated/resolver_type_check_protocols:validate_issue
defines the signature asProtocol
's (inspired by graphql-codegen-ariadne. This has the advantage, that it can be reused when assigning the resolvers to the objects fields. This approach is recognized by bothmypy
andpyright
.- Unfortunately not all type checkers support this
Protocol
-based approach. E.g. pyCharms integrated type check doesn't recognize the errors. To provide type-checking support inside pyCharm (without running a separate type checker) the artificial function signature can be typed with completeCallable
's ( seegenerated/resolver_type_check_protocols:validate_issue
).
Example schema:
type Query {
issue(id: ID!): Issue
}
type Issue {
id: ID!
text: String!
parent: Issue
}
Test cases:
- Resolver returns object type (
Query.issue
,Issue.parent
) - Resolver returns scalar type (
Issue.id
,Issue.text
) - Multiple resolvers return the same return type (
Query.issue
,Issue.parent
) - Multiple resolvers require the same parent type (
Issue.*
)
- Base types for resolvers (sync, async, callable, iterable).
- Example schema (based on this issue).
- Basic resolver implementation with at least one non-default resolver.
- Create typed resolver signatures.
- Validate that return type of parent resolvers match expected parent type of child resolver.
- Confirm validation with mypy and pyright.
- Create bindables based on schema and assign custom resolvers to fields.
- Create schema from generated bindables.
- Create basic tests for generated schema.
Install dependencies via poetry poetry install
or use provided Makefile make install
.
Run the type checks with make -k check
(-k
to continue after mypy fails at the expected positions).
In the current version, this should flag the validation functions
in tests/generated/check_wrong_resolver_type_check_*.py
.