ROLLOUT 0 · mirumee/ariadne
mirumee__ariadne-24
Solved
453 stotal time
15tool calls
15model calls
7,920tokens generated
123,585prompt tokens read
Agent exceeded session timeout (7.5 min)
Turn by turn
Token shading: p ≥ 0.9 0.5 – 0.9 0.2 – 0.5 below 0.2. Hover a token for its id and probability.
System instructions 4,822 characters
You are a principal Python software engineer resolving a repository issue in `/workspace`. Your sole objective is to produce a minimal, root-cause source patch that passes every hidden fail-to-pass and pass-to-pass regression test on the first grading run. ## Critical Environment & Grader Rules 1. **Air-Gapped Sandbox & Available CLI Tools**: No network access. Never run `pip`, `uv`, `conda`, or `git fetch`. Note: `rg` (`ripgrep`) is NOT installed in the container — always use `grep -rn --include="*.py"` for fast exact searches so you never waste a tool call on `command not found`. 2. **Protected Grading Paths**: Before running official verification, the grading harness **strips and reverts all changes** to `tests/`, `test/`, `testing/`, `test_*.py`, `*_test.py`, `conftest.py`, `pyproject.toml`, `setup.cfg`, `pytest.ini`, and `tox.ini`. Every part of your fix MUST reside in non-test production source files. 3. **Untracked File Trap (`git add -N .`)**: `submit_patch` stages ALL untracked files inside `/workspace`! Never leave reproduction or scratch files inside `/workspace` — run reproductions inline via `python3 - <<'PY'` or inside `/tmp/repro.py` in a single `run_command`. 4. **Stdout Truncation Rule**: `run_command` truncates stdout after the first 5,000 characters from the TOP. Always run pytest quietly with short tracebacks: `pytest path/to/test_file.py -k pattern -q --tb=short --no-header` If a test file is large, redirect output to `/tmp/pytest.log` and inspect `tail -n 80 /tmp/pytest.log` so failure tracebacks at the bottom are never cut off. 5. **Strict Pacing Discipline**: `get_status` and `submit_patch` are free and do NOT count toward `max_tool_calls`. Target **16-22 counted tool calls total**, and **begin your first `edit_file` change no later than tool call 15**. Reserve at least 4 calls for verification, `git status --short && git diff`, and submission. ## Execution Protocol ### Phase 1 — Pinpoint Code & Tests Immediately (1-2 tool calls) - The user message already contains `Workspace Layout` — do NOT waste a turn running `ls` on top-level directories. - Run a single compound `run_command` with `grep -rn --include="*.py"` to locate: (a) the exact symbols, error strings, or functions from the problem description, (b) **all parallel or sibling implementations** (for example sync + async variants, `Client` + `AsyncClient`, serializer + deserializer, multiple backends/dialects, base + subclass methods, `__init__.py` / `__all__` exports), and (c) the closest existing unit test files under `tests/`. ### Phase 2 — Deep Read & Spec Anchor (2-5 tool calls) - Use `read_file` with generous `start_line` and `end_line` windows (180-300 lines per call so you read whole modules in fewer calls) on the target function/class, callers, type annotations, and neighboring tests. - Before editing, verify every explicit and implicit requirement from the issue: exact parameter names, defaults, boundary/domain edge cases, exception types/messages, `None` vs empty collection vs falsy (`0`, `False`, `""`) semantics, mutability/copying, and return types. ### Phase 3 — Reproduce Bug in One Call (1 tool call) - Run ONE concise command (`python3 - <<'PY' ... PY` or targeted `pytest ... -q --tb=short --no-header`) via `run_command` to observe the exact pre-fix failure. Do NOT spend multiple tool calls writing separate reproduction scripts. ### Phase 4 — Surgical Implementation Across All Parallel Paths (1-4 tool calls) - Begin editing by tool call 15! Use `edit_file` with 4-6 lines of exact surrounding context copied from `read_file`. - Update ALL parallel codepaths (sync/async, multiple classes/dialects, type hints) consistently so partial fixes do not fail sibling tests. - **2-Strike Edit Rule**: If `edit_file` fails twice on the same block due to whitespace or escaping, do not retry `edit_file` a third time — apply an atomic Python transformation via `run_command` and immediately verify `python3 -m py_compile path/to/file.py`. ### Phase 5 — Edge-Case Verification, Diff Hygiene & Clean Exit (2-4 tool calls) - Run a single compound `run_command` testing both your reproduction + boundary edge cases (`None`, empty input, leading/trailing delimiters, subdomain/prefix boundaries, subclass/async parity) AND the relevant existing `pytest` module(s) with `-q --tb=short --no-header`. - Run `git status --short && git diff` via `run_command`. Confirm: - ZERO untracked (`??`) files in `/workspace` (`rm -f` any untracked workspace files immediately), - ZERO modifications to `tests/` or protected config files (`git checkout -- tests/` if touched), - Minimal, clean diff with valid imports and indentation. - Call `submit_patch` once verification passes, then respond with a brief 2-sentence plain-text summary and make NO further tool calls.
Task prompt 2,367 characters
You are evaluating a software engineering task for repository mirumee/ariadne.
Problem Statement:
`add_resolve_functions_to_schema` should support Scalars parse_value and parse_literal
Currently Ariadne Scalar support is limited to serializing python types to JSON before returning them to client, but we also want to support using custom scalars for input.
Our `add_resolve_functions_to_scalar` utility could support following use-cases:
Code below results in one-way only scalar:
- `type_defs = {'Scalar': {'serialize': callable}}`
And this code results in two-way scalar:
- `type_defs = {'Scalar': {'serialize': callable, 'parse_value': callable, 'parse_literal': callable}}` - explicit syntax for two-directional scalar.
## Task Budget (Session terminates when any budget is exhausted)
- Time allowance: 7.5 minutes
- Tool calls allowance: 30 calls
- Max loop iterations: 40 turns
## Execution Environment Rules
- Single command timeout: 90 seconds (commands exceeding this fail without ending the session)
- Command output limit: 5000 characters
- File view limit: 150 lines per read_file call
- File character limit: 10000 characters per read_file call
- Environment is offline (no network/PyPI access). All repository and test dependencies are ALREADY pre-installed. Do NOT attempt to run pip install or download packages.
## Instructions:
0. All source code is under `/workspace`. Do NOT search outside `/workspace` (e.g. `/usr/`, `/wheels/`, system site-packages). If imports fail, the issue is in the source code under `/workspace`, not in missing system packages.
1. Analyze the problem statement and any provided hints carefully to identify all requested script paths, CLI subcommands, or Python modules.
2. Inspect existing codebase conventions and test files before making edits.
3. Verify your implementation using targeted tests or inline assertions before submitting.
4. Call submit_patch only after your implementation is complete and verified.
5. As your final action, you must return a text-only response reporting your completion to terminate the session.
## Workspace Layout
The repository is located at `/workspace`. Here is the directory tree (up to 3 levels):
```
./LICENSE
./README.md
./ariadne
./ariadne/__init__.py
./ariadne/build_schema.py
./ariadne/executable_schema.py
./ariadne/resolvers.py
./setup.py
./tests
```
Turn 1at 21 s234 tokens out2,614 prompt tokens
The user wants to enhance `add_resolve_functions_to_schema` (or potentially `add_resolve_functions_to_scalar`, although the problem statement mentions both) to support `parse_value` and `parse_literal` for Scalars in Ariadne. Currently, it seems Ariadne scalars only support `serialize`. The goal is to support `parse_value` and `parse_literal` as well, allowing two-way data flow. Looking at the Workspace Layout: - `ariadne/build_schema.py` - `ariadne/executable_schema.py` - `ariadne/resolvers.py` The problem statement mentions `add_resolve_functions_to_schema` and `add_resolve_functions_to_scalar`. I should find where these are defined. Let's start by searching for these functions.
TOOL CALL · run_command
{
"command": "grep -rn --include=\"*.py\" -E \"add_resolve_functions_to_schema|add_resolve_functions_to_scalar\" ."
}Result of run_command 519 characters
{
"status": "ok",
"stdout": "./ariadne/executable_schema.py:6:from .resolvers import add_resolve_functions_to_schema\n./ariadne/executable_schema.py:13: add_resolve_functions_to_schema(schema, resolvers)\n./ariadne/__init__.py:3:from .resolvers import add_resolve_functions_to_schema, default_resolver, resolve_to\n./ariadne/__init__.py:6: \"add_resolve_functions_to_schema\",\n./ariadne/resolvers.py:22:def add_resolve_functions_to_schema(schema: GraphQLSchema, resolvers: dict):\n",
"stderr": "",
"exit_code": 0
}Generated tokens 234 tokens · stopped: STOP
<|channel>thought
The user wants to enhance `add_resolve_functions_to_schema` (or potentially `add_resolve_functions_to_scalar`, although the problem statement mentions both) to support `parse_value` and `parse_literal` for Scalars in Ariadne.
Currently, it seems Ariadne scalars only support `serialize`. The goal is to support `parse_value` and `parse_literal` as well, allowing two-way data flow.
Looking at the Workspace Layout:
- `ariadne/build_schema.py`
- `ariadne/executable_schema.py`
- `ariadne/resolvers.py`
The problem statement mentions `add_resolve_functions_to_schema` and `add_resolve_functions_to_scalar`. I should find where these are defined.
Let's start by searching for these functions.<channel|><|tool_call>call:run_command{command:<|"|>grep -rn --include="*.py" -E "add_resolve_functions_to_schema|add_resolve_functions_to_scalar" .<|"|>}<tool_call|><|tool_response>
Turn 2at 34 s169 tokens out3,031 prompt tokens
It seems `add_resolve_functions_to_schema` is defined in `ariadne/resolvers.py`. I didn't see `add_resolve_functions_to_scalar` in the grep results, maybe the user meant `add_resolve_functions_to_schema` when referring to scalars or it's a helper inside `add_resolve_functions_to_schema`. Wait, the problem statement says: "Our `add_resolve_functions_to_scalar` utility could support following use-cases:" But grep didn't find it. Maybe it doesn't exist yet or it's named differently. Let's examine `ariadne/resolvers.py`.
TOOL CALL · read_file
{
"filepath": "ariadne/resolvers.py"
}Result of read_file 1,582 characters
{
"status": "ok",
"filepath": "ariadne/resolvers.py",
"content": "from graphql import GraphQLObjectType, GraphQLScalarType, GraphQLSchema\nfrom graphql.execution.base import ResolveInfo\n\n\ndef resolve_parent_field(parent, name: str):\n if isinstance(parent, dict):\n return parent.get(name)\n return getattr(parent, name, None)\n\n\ndef default_resolver(parent, info: ResolveInfo):\n return resolve_parent_field(parent, info.field_name)\n\n\ndef resolve_to(name: str):\n def resolver(parent, *_):\n return resolve_parent_field(parent, name)\n\n return resolver\n\n\ndef add_resolve_functions_to_schema(schema: GraphQLSchema, resolvers: dict):\n for type_name, type_object in schema.get_type_map().items():\n if isinstance(type_object, GraphQLObjectType):\n add_resolve_functions_to_object(type_name, type_object, resolvers)\n if isinstance(type_object, GraphQLScalarType):\n add_resolve_function_to_scalar(type_name, type_object, resolvers)\n\n\ndef add_resolve_functions_to_object(name: str, obj: GraphQLObjectType, resolvers: dict):\n type_resolver = resolvers.get(name, {})\n for field_name, field_object in obj.fields.items():\n field_resolver = type_resolver.get(field_name, default_resolver)\n field_object.resolver = field_resolver\n\n\ndef add_resolve_function_to_scalar(name: str, obj: GraphQLObjectType, resolvers: dict):\n serializer = resolvers.get(name, obj.serialize)\n obj.serialize = serializer",
"start_line": 1,
"end_line": 39,
"total_lines": 39,
"is_truncated": false
}Generated tokens 169 tokens · stopped: STOP
It seems `add_resolve_functions_to_schema` is defined in `ariadne/resolvers.py`. I didn't see `add_resolve_functions_to_scalar` in the grep results, maybe the user meant `add_resolve_functions_to_schema` when referring to scalars or it's a helper inside `add_resolve_functions_to_schema`.
Wait, the problem statement says:
"Our `add_resolve_functions_to_scalar` utility could support following use-cases:"
But grep didn't find it. Maybe it doesn't exist yet or it's named differently.
Let's examine `ariadne/resolvers.py`.<channel|><|tool_call>call:read_file{filepath:<|"|>ariadne/resolvers.py<|"|>}<tool_call|><|tool_response>
Turn 3at 102 s1,132 tokens out3,672 prompt tokens
I found `add_resolve_function_to_scalar` (singular 'function') at the end of `ariadne/resolvers.py`.
Wait, look closely at the definition of `add_resolve_function_to_scalar`:
```python
def add_resolve_function_to_scalar(name: str, obj: GraphQLObjectType, resolvers: dict):
serializer = resolvers.get(name, obj.serialize)
obj.serialize = serializer
```
Wait, `obj` is typed as `GraphQLObjectType` but it's called when `type_object` is `GraphQLScalarType` in `add_resolve_functions_to_schema`. That looks like a bug in type hinting, but more importantly, it only handles `serialize`.
And it's taking `serializer` from `resolvers.get(name, obj.serialize)`.
If `resolvers[name]` is a callable, it sets `obj.serialize = resolvers[name]`.
But the problem statement says:
- `type_defs = {'Scalar': {'serialize': callable}}`
- `type_defs = {'Scalar': {'serialize': callable, 'parse_value': callable, 'parse_literal': callable}}`
If `resolvers[name]` is a dictionary, the current implementation does:
`serializer = resolvers.get(name, obj.serialize)`
If `resolvers = {'Scalar': {'serialize': some_callable}}`, then `resolvers.get('Scalar', obj.serialize)` returns `{'serialize': some_callable}`.
And then `obj.serialize = {'serialize': some_callable}`.
This is definitely wrong if the user provides a dictionary of resolvers for the scalar.
Wait, let's re-read the current implementation of `add_resolve_function_to_scalar`:
```python
def add_resolve_function_to_scalar(name: str, obj: GraphQLObjectType, resolvers: dict):
serializer = resolvers.get(name, obj.serialize)
obj.serialize = serializer
```
In `add_resolve_functions_to_schema`:
```python
if isinstance(type_object, GraphQLScalarType):
add_resolve_function_to_scalar(type_name, type_object, resolvers)
```
If the user does:
`resolvers = {'MyScalar': my_serialize_func}`
Then `serializer` becomes `my_serialize_func`, and `obj.serialize` is set to `my_serialize_func`. This works for basic one-way scalars.
But if the user does:
`resolvers = {'MyScalar': {'serialize': serialize_func, 'parse_value': parse_val_func, 'parse_literal': parse_lit_func}}`
Then `serializer` becomes `{'serialize': serialize_func, 'parse_value': parse_val_func, 'parse_literal': parse_lit_func}`.
And `obj.serialize` becomes that dictionary. This is wrong.
So I need to change `add_resolve_function_to_scalar` to handle both a single callable and a dictionary of callables.
Let's see how `add_resolve_functions_to_object` handles it:
```python
def add_resolve_functions_to_object(name: str, obj: GraphQLObjectType, resolvers: dict):
type_resolver = resolvers.get(name, {})
for field_name, field_object in obj.fields.items():
field_resolver = type_resolver.get(field_name, default_resolver)
field_object.resolver = field_resolver
```
This one assumes `resolvers.get(name)` is a dictionary.
For scalars, the user might provide a single function (which should be the serializer) or a dictionary of functions.
According to the problem statement:
- `type_defs = {'Scalar': {'serialize': callable}}`
- `type_defs = {'Scalar': {'serialize': callable, 'parse_value': callable, 'parse_literal': callable}}`
Wait, the problem statement uses `type_defs` but it probably means the `resolvers` argument to `add_resolve_functions_to_schema`.
Let's try to reproduce this. I'll create a small script.
I need to know what `GraphQLScalarType` expects for `serialize`, `parse_value`, and `parse_literal`.
Usually in `graphql-core`, `GraphQLScalarType` is defined with these.
Let's check `graphql-core` versions or how it's used in the codebase.
Actually, I can just write a test case.
Reproduction script:
```python
import pytest
from graphql import GraphQLSchema, GraphQLScalarType, make_executable_schema
from ariadne import make_executable_schema, add_resolve_functions_to_schema, gql
# ariadne's make_executable_schema actually uses add_resolve_functions_to_schema internally?
# Let's check executable_schema.py
```
Let's check `ariadne/executable_schema.py`.
TOOL CALL · read_file
{
"filepath": "ariadne/executable_schema.py"
}Result of read_file 581 characters
{
"status": "ok",
"filepath": "ariadne/executable_schema.py",
"content": "from typing import List, Union\n\nfrom graphql import GraphQLSchema\n\nfrom .build_schema import build_schema_from_type_definitions\nfrom .resolvers import add_resolve_functions_to_schema\n\n\ndef make_executable_schema(\n type_defs: Union[str, List[str]], resolvers: dict\n) -> GraphQLSchema:\n schema = build_schema_from_type_definitions(type_defs)\n add_resolve_functions_to_schema(schema, resolvers)\n return schema",
"start_line": 1,
"end_line": 14,
"total_lines": 14,
"is_truncated": false
}Generated tokens 1,132 tokens · stopped: STOP
I found `add_resolve_function_to_scalar` (singular 'function') at the end of `ariadne/resolvers.py`.
Wait, look closely at the definition of `add_resolve_function_to_scalar`:
```python
def add_resolve_function_to_scalar(name: str, obj: GraphQLObjectType, resolvers: dict):
serializer = resolvers.get(name, obj.serialize)
obj.serialize = serializer
```
Wait, `obj` is typed as `GraphQLObjectType` but it's called when `type_object` is `GraphQLScalarType` in `add_resolve_functions_to_schema`. That looks like a bug in type hinting, but more importantly, it only handles `serialize`.
And it's taking `serializer` from `resolvers.get(name, obj.serialize)`.
If `resolvers[name]` is a callable, it sets `obj.serialize = resolvers[name]`.
But the problem statement says:
- `type_defs = {'Scalar': {'serialize': callable}}`
- `type_defs = {'Scalar': {'serialize': callable, 'parse_value': callable, 'parse_literal': callable}}`
If `resolvers[name]` is a dictionary, the current implementation does:
`serializer = resolvers.get(name, obj.serialize)`
If `resolvers = {'Scalar': {'serialize': some_callable}}`, then `resolvers.get('Scalar', obj.serialize)` returns `{'serialize': some_callable}`.
And then `obj.serialize = {'serialize': some_callable}`.
This is definitely wrong if the user provides a dictionary of resolvers for the scalar.
Wait, let's re-read the current implementation of `add_resolve_function_to_scalar`:
```python
def add_resolve_function_to_scalar(name: str, obj: GraphQLObjectType, resolvers: dict):
serializer = resolvers.get(name, obj.serialize)
obj.serialize = serializer
```
In `add_resolve_functions_to_schema`:
```python
if isinstance(type_object, GraphQLScalarType):
add_resolve_function_to_scalar(type_name, type_object, resolvers)
```
If the user does:
`resolvers = {'MyScalar': my_serialize_func}`
Then `serializer` becomes `my_serialize_func`, and `obj.serialize` is set to `my_serialize_func`. This works for basic one-way scalars.
But if the user does:
`resolvers = {'MyScalar': {'serialize': serialize_func, 'parse_value': parse_val_func, 'parse_literal': parse_lit_func}}`
Then `serializer` becomes `{'serialize': serialize_func, 'parse_value': parse_val_func, 'parse_literal': parse_lit_func}`.
And `obj.serialize` becomes that dictionary. This is wrong.
So I need to change `add_resolve_function_to_scalar` to handle both a single callable and a dictionary of callables.
Let's see how `add_resolve_functions_to_object` handles it:
```python
def add_resolve_functions_to_object(name: str, obj: GraphQLObjectType, resolvers: dict):
type_resolver = resolvers.get(name, {})
for field_name, field_object in obj.fields.items():
field_resolver = type_resolver.get(field_name, default_resolver)
field_object.resolver = field_resolver
```
This one assumes `resolvers.get(name)` is a dictionary.
For scalars, the user might provide a single function (which should be the serializer) or a dictionary of functions.
According to the problem statement:
- `type_defs = {'Scalar': {'serialize': callable}}`
- `type_defs = {'Scalar': {'serialize': callable, 'parse_value': callable, 'parse_literal': callable}}`
Wait, the problem statement uses `type_defs` but it probably means the `resolvers` argument to `add_resolve_functions_to_schema`.
Let's try to reproduce this. I'll create a small script.
I need to know what `GraphQLScalarType` expects for `serialize`, `parse_value`, and `parse_literal`.
Usually in `graphql-core`, `GraphQLScalarType` is defined with these.
Let's check `graphql-core` versions or how it's used in the codebase.
Actually, I can just write a test case.
Reproduction script:
```python
import pytest
from graphql import GraphQLSchema, GraphQLScalarType, make_executable_schema
from ariadne import make_executable_schema, add_resolve_functions_to_schema, gql
# ariadne's make_executable_schema actually uses add_resolve_functions_to_schema internally?
# Let's check executable_schema.py
```
Let's check `ariadne/executable_schema.py`.
<channel|><|tool_call>call:read_file{filepath:<|"|>ariadne/executable_schema.py<|"|>}<tool_call|><|tool_response>
Turn 4at 152 s939 tokens out4,999 prompt tokens
Yes, `make_executable_schema` calls `add_resolve_functions_to_schema`.
Let's create a reproduction script in `/tmp/repro.py`.
I want to verify that passing a dictionary for a scalar resolver currently fails or doesn't work as expected.
```python
from graphql import gql, GraphQLSchema, GraphQLScalarType
from ariadne import make_executable_schema
def serialize(value):
return f"Serialized: {value}"
def parse_value(value):
return f"ParsedValue: {value}"
def parse_literal(node):
return f"ParsedLiteral: {node.value}"
type_defs = gql("scalar MyScalar")
resolvers = {
'MyScalar': {
'serialize': serialize,
'parse_value': parse_value,
'parse_literal': parse_literal,
}
}
schema = make_executable_schema(type_defs, resolvers)
# In graphql-core, GraphQLScalarType has serialize, parse_value, and parse_literal attributes.
scalar_type = schema.get_type_map()['MyScalar']
print(f"Scalar Type: {scalar_type}")
print(f"Serialize: {scalar_type.serialize}")
print(f"Parse Value: {scalar_type.parse_value}")
print(f"Parse Literal: {scalar_type.parse_literal}")
# Check if serialize is the function or the dict
if scalar_type.serialize == resolvers['MyScalar']:
print("FAILED: serialize is set to the dict, not the serialize function")
elif scalar_type.serialize == serialize:
print("SUCCESS: serialize is set correctly")
else:
print(f"UNEXPECTED: serialize is {scalar_type.serialize}")
if scalar_type.parse_value == parse_value:
print("SUCCESS: parse_value is set correctly")
else:
print(f"FAILED: parse_value is {scalar_type.parse_value}")
if scalar_type.parse_literal == parse_literal:
print("SUCCESS: parse_literal is set correctly")
else:
print(f"FAILED: parse_literal is {scalar_type.parse_literal}")
```
Let's run this.
TOOL CALL · run_command
{
"command": "python3 - <<'PY'\nfrom graphql import gql, GraphQLSchema, GraphQLScalarType\nfrom ariadne import make_executable_schema\n\ndef serialize(value):\n return f\"Serialized: {value}\"\n\ndef parse_value(value):\n return f\"ParsedValue: {value}\"\n\ndef parse_literal(node):\n return f\"ParsedLiteral: {node.value}\"\n\ntype_defs = gql(\"scalar MyScalar\")\nresolvers = {\n 'MyScalar': {\n 'serialize': serialize,\n 'parse_value': parse_value,\n 'parse_literal': parse_literal,\n }\n}\n\nschema = make_executable_schema(type_defs, resolvers)\n\nscalar_type = schema.get_type_map()['MyScalar']\n\nprint(f\"Scalar Type: {scalar_type}\")\nprint(f\"Serialize: {scalar_type.serialize}\")\nprint(f\"Parse Value: {scalar_type.parse_value}\")\nprint(f\"Parse Literal: {scalar_type.parse_literal}\")\n\nif scalar_type.serialize == resolvers['MyScalar']:\n print(\"FAILED: serialize is set to the dict, not the serialize function\")\nelif scalar_type.serialize == serialize:\n print(\"SUCCESS: serialize is set correctly\")\nelse:\n print(f\"UNEXPECTED: serialize is {scalar_type.serialize}\")\n\nif scalar_type.parse_value == parse_value:\n print(\"SUCCESS: parse_value is set correctly\")\nelse:\n print(f\"FAILED: parse_value is {scalar_type.parse_value}\")\n\nif scalar_type.parse_literal == parse_literal:\n print(\"SUCCESS: parse_literal is set correctly\")\nelse:\n print(f\"FAILED: parse_literal is {scalar_type.parse_literal}\")\nPY\n"
}Result of run_command 509 characters
{
"status": "error",
"error_type": "CommandError",
"error_message": "Traceback (most recent call last):\n File \"<stdin>\", line 1, in <module>\nImportError: cannot import name 'gql' from 'graphql' (/opt/py37/lib/python3.7/site-packages/graphql/__init__.py)\n",
"details": {
"stdout": "",
"stderr": "Traceback (most recent call last):\n File \"<stdin>\", line 1, in <module>\nImportError: cannot import name 'gql' from 'graphql' (/opt/py37/lib/python3.7/site-packages/graphql/__init__.py)\n",
"exit_code": 1
}
}Generated tokens 939 tokens · stopped: STOP
Yes, `make_executable_schema` calls `add_resolve_functions_to_schema`.
Let's create a reproduction script in `/tmp/repro.py`.
I want to verify that passing a dictionary for a scalar resolver currently fails or doesn't work as expected.
```python
from graphql import gql, GraphQLSchema, GraphQLScalarType
from ariadne import make_executable_schema
def serialize(value):
return f"Serialized: {value}"
def parse_value(value):
return f"ParsedValue: {value}"
def parse_literal(node):
return f"ParsedLiteral: {node.value}"
type_defs = gql("scalar MyScalar")
resolvers = {
'MyScalar': {
'serialize': serialize,
'parse_value': parse_value,
'parse_literal': parse_literal,
}
}
schema = make_executable_schema(type_defs, resolvers)
# In graphql-core, GraphQLScalarType has serialize, parse_value, and parse_literal attributes.
scalar_type = schema.get_type_map()['MyScalar']
print(f"Scalar Type: {scalar_type}")
print(f"Serialize: {scalar_type.serialize}")
print(f"Parse Value: {scalar_type.parse_value}")
print(f"Parse Literal: {scalar_type.parse_literal}")
# Check if serialize is the function or the dict
if scalar_type.serialize == resolvers['MyScalar']:
print("FAILED: serialize is set to the dict, not the serialize function")
elif scalar_type.serialize == serialize:
print("SUCCESS: serialize is set correctly")
else:
print(f"UNEXPECTED: serialize is {scalar_type.serialize}")
if scalar_type.parse_value == parse_value:
print("SUCCESS: parse_value is set correctly")
else:
print(f"FAILED: parse_value is {scalar_type.parse_value}")
if scalar_type.parse_literal == parse_literal:
print("SUCCESS: parse_literal is set correctly")
else:
print(f"FAILED: parse_literal is {scalar_type.parse_literal}")
```
Let's run this.
<channel|><|tool_call>call:run_command{command:<|"|>python3 - <<'PY'
from graphql import gql, GraphQLSchema, GraphQLScalarType
from ariadne import make_executable_schema
def serialize(value):
return f"Serialized: {value}"
def parse_value(value):
return f"ParsedValue: {value}"
def parse_literal(node):
return f"ParsedLiteral: {node.value}"
type_defs = gql("scalar MyScalar")
resolvers = {
'MyScalar': {
'serialize': serialize,
'parse_value': parse_value,
'parse_literal': parse_literal,
}
}
schema = make_executable_schema(type_defs, resolvers)
scalar_type = schema.get_type_map()['MyScalar']
print(f"Scalar Type: {scalar_type}")
print(f"Serialize: {scalar_type.serialize}")
print(f"Parse Value: {scalar_type.parse_value}")
print(f"Parse Literal: {scalar_type.parse_literal}")
if scalar_type.serialize == resolvers['MyScalar']:
print("FAILED: serialize is set to the dict, not the serialize function")
elif scalar_type.serialize == serialize:
print("SUCCESS: serialize is set correctly")
else:
print(f"UNEXPECTED: serialize is {scalar_type.serialize}")
if scalar_type.parse_value == parse_value:
print("SUCCESS: parse_value is set correctly")
else:
print(f"FAILED: parse_value is {scalar_type.parse_value}")
if scalar_type.parse_literal == parse_literal:
print("SUCCESS: parse_literal is set correctly")
else:
print(f"FAILED: parse_literal is {scalar_type.parse_literal}")
PY
<|"|>}<tool_call|><|tool_response>
Turn 5at 184 s896 tokens out6,121 prompt tokens
Ah, `gql` is from `ariadne`, not `graphql`. Correct.
Also, `make_executable_schema` was imported from both `graphql` and `ariadne` in my script (accidentally).
Corrected reproduction script:
```python
from graphql import GraphQLSchema, GraphQLScalarType
from ariadne import gql, make_executable_schema
def serialize(value):
return f"Serialized: {value}"
def parse_value(value):
return f"ParsedValue: {value}"
def parse_literal(node):
return f"ParsedLiteral: {node.value}"
type_defs = gql("scalar MyScalar")
resolvers = {
'MyScalar': {
'serialize': serialize,
'parse_value': parse_value,
'parse_literal': parse_literal,
}
}
schema = make_executable_schema(type_defs, resolvers)
scalar_type = schema.get_type_map()['MyScalar']
print(f"Scalar Type: {scalar_type}")
print(f"Serialize: {scalar_type.serialize}")
print(f"Parse Value: {scalar_type.parse_value}")
print(f"Parse Literal: {scalar_type.parse_literal}")
if scalar_type.serialize == resolvers['MyScalar']:
print("FAILED: serialize is set to the dict, not the serialize function")
elif scalar_type.serialize == serialize:
print("SUCCESS: serialize is set correctly")
else:
print(f"UNEXPECTED: serialize is {scalar_type.serialize}")
if scalar_type.parse_value == parse_value:
print("SUCCESS: parse_value is set correctly")
else:
print(f"FAILED: parse_value is {scalar_type.parse_value}")
if scalar_type.parse_literal == parse_literal:
print("SUCCESS: parse_literal is set correctly")
else:
print(f"FAILED: parse_literal is {scalar_type.parse_literal}")
```
Let's run it.
TOOL CALL · run_command
{
"command": "python3 - <<'PY'\nfrom graphql import GraphQLSchema, GraphQLScalarType\nfrom ariadne import gql, make_executable_schema\n\ndef serialize(value):\n return f\"Serialized: {value}\"\n\ndef parse_value(value):\n return f\"ParsedValue: {value}\"\n\ndef parse_literal(node):\n return f\"ParsedLiteral: {node.value}\"\n\ntype_defs = gql(\"scalar MyScalar\")\nresolvers = {\n 'MyScalar': {\n 'serialize': serialize,\n 'parse_value': parse_value,\n 'parse_literal': parse_literal,\n }\n}\n\nschema = make_executable_schema(type_defs, resolvers)\n\nscalar_type = schema.get_type_map()['MyScalar']\n\nprint(f\"Scalar Type: {scalar_type}\")\nprint(f\"Serialize: {scalar_type.serialize}\")\nprint(f\"Parse Value: {scalar_type.parse_value}\")\nprint(f\"Parse Literal: {scalar_type.parse_literal}\")\n\nif scalar_type.serialize == resolvers['MyScalar']:\n print(\"FAILED: serialize is set to the dict, not the serialize function\")\nelif scalar_type.serialize == serialize:\n print(\"SUCCESS: serialize is set correctly\")\nelse:\n print(f\"UNEXPECTED: serialize is {scalar_type.serialize}\")\n\nif scalar_type.parse_value == parse_value:\n print(\"SUCCESS: parse_value is set correctly\")\nelse:\n print(f\"FAILED: parse_value is {scalar_type.parse_value}\")\n\nif scalar_type.parse_literal == parse_literal:\n print(\"SUCCESS: parse_literal is set correctly\")\nelse:\n print(f\"FAILED: parse_literal is {scalar_type.parse_literal}\")\nPY\n"
}Result of run_command 455 characters
{
"status": "error",
"error_type": "CommandError",
"error_message": "Traceback (most recent call last):\n File \"<stdin>\", line 2, in <module>\nImportError: cannot import name 'gql' from 'ariadne' (/workspace/ariadne/__init__.py)\n",
"details": {
"stdout": "",
"stderr": "Traceback (most recent call last):\n File \"<stdin>\", line 2, in <module>\nImportError: cannot import name 'gql' from 'ariadne' (/workspace/ariadne/__init__.py)\n",
"exit_code": 1
}
}Generated tokens 896 tokens · stopped: STOP
Ah, `gql` is from `ariadne`, not `graphql`. Correct.
Also, `make_executable_schema` was imported from both `graphql` and `ariadne` in my script (accidentally).
Corrected reproduction script:
```python
from graphql import GraphQLSchema, GraphQLScalarType
from ariadne import gql, make_executable_schema
def serialize(value):
return f"Serialized: {value}"
def parse_value(value):
return f"ParsedValue: {value}"
def parse_literal(node):
return f"ParsedLiteral: {node.value}"
type_defs = gql("scalar MyScalar")
resolvers = {
'MyScalar': {
'serialize': serialize,
'parse_value': parse_value,
'parse_literal': parse_literal,
}
}
schema = make_executable_schema(type_defs, resolvers)
scalar_type = schema.get_type_map()['MyScalar']
print(f"Scalar Type: {scalar_type}")
print(f"Serialize: {scalar_type.serialize}")
print(f"Parse Value: {scalar_type.parse_value}")
print(f"Parse Literal: {scalar_type.parse_literal}")
if scalar_type.serialize == resolvers['MyScalar']:
print("FAILED: serialize is set to the dict, not the serialize function")
elif scalar_type.serialize == serialize:
print("SUCCESS: serialize is set correctly")
else:
print(f"UNEXPECTED: serialize is {scalar_type.serialize}")
if scalar_type.parse_value == parse_value:
print("SUCCESS: parse_value is set correctly")
else:
print(f"FAILED: parse_value is {scalar_type.parse_value}")
if scalar_type.parse_literal == parse_literal:
print("SUCCESS: parse_literal is set correctly")
else:
print(f"FAILED: parse_literal is {scalar_type.parse_literal}")
```
Let's run it.
<channel|><|tool_call>call:run_command{command:<|"|>python3 - <<'PY'
from graphql import GraphQLSchema, GraphQLScalarType
from ariadne import gql, make_executable_schema
def serialize(value):
return f"Serialized: {value}"
def parse_value(value):
return f"ParsedValue: {value}"
def parse_literal(node):
return f"ParsedLiteral: {node.value}"
type_defs = gql("scalar MyScalar")
resolvers = {
'MyScalar': {
'serialize': serialize,
'parse_value': parse_value,
'parse_literal': parse_literal,
}
}
schema = make_executable_schema(type_defs, resolvers)
scalar_type = schema.get_type_map()['MyScalar']
print(f"Scalar Type: {scalar_type}")
print(f"Serialize: {scalar_type.serialize}")
print(f"Parse Value: {scalar_type.parse_value}")
print(f"Parse Literal: {scalar_type.parse_literal}")
if scalar_type.serialize == resolvers['MyScalar']:
print("FAILED: serialize is set to the dict, not the serialize function")
elif scalar_type.serialize == serialize:
print("SUCCESS: serialize is set correctly")
else:
print(f"UNEXPECTED: serialize is {scalar_type.serialize}")
if scalar_type.parse_value == parse_value:
print("SUCCESS: parse_value is set correctly")
else:
print(f"FAILED: parse_value is {scalar_type.parse_value}")
if scalar_type.parse_literal == parse_literal:
print("SUCCESS: parse_literal is set correctly")
else:
print(f"FAILED: parse_literal is {scalar_type.parse_literal}")
PY
<|"|>}<tool_call|><|tool_response>
Turn 6at 187 s54 tokens out7,174 prompt tokens
Wait, `gql` is not in `ariadne/__init__.py`? Let's check `ariadne/__init__.py` again.
TOOL CALL · read_file
{
"filepath": "ariadne/__init__.py"
}Result of read_file 528 characters
{
"status": "ok",
"filepath": "ariadne/__init__.py",
"content": "from .build_schema import build_schema_from_type_definitions\nfrom .executable_schema import make_executable_schema\nfrom .resolvers import add_resolve_functions_to_schema, default_resolver, resolve_to\n\n__all__ = [\n \"add_resolve_functions_to_schema\",\n \"build_schema_from_type_definitions\",\n \"default_resolver\",\n \"make_executable_schema\",\n \"resolve_to\",\n]",
"start_line": 1,
"end_line": 11,
"total_lines": 11,
"is_truncated": false
}Generated tokens 54 tokens · stopped: STOP
Wait, `gql` is not in `ariadne/__init__.py`?
Let's check `ariadne/__init__.py` again.
<channel|><|tool_call>call:read_file{filepath:<|"|>ariadne/__init__.py<|"|>}<tool_call|><|tool_response>
Turn 7at 191 s58 tokens out7,413 prompt tokens
Indeed, `gql` is not there. I must have assumed it was. Let's see where `gql` is defined. Search for `def gql`.
TOOL CALL · run_command
{
"command": "grep -rn \"def gql\" ."
}Result of run_command 127 characters
{
"status": "error",
"error_type": "CommandError",
"error_message": "",
"details": {
"stdout": "",
"stderr": "",
"exit_code": 1
}
}Generated tokens 58 tokens · stopped: STOP
Indeed, `gql` is not there. I must have assumed it was. Let's see where `gql` is defined.
Search for `def gql`.
<channel|><|tool_call>call:run_command{command:<|"|>grep -rn "def gql" .<|"|>}<tool_call|><|tool_response>
Turn 8at 198 s104 tokens out7,526 prompt tokens
That's strange. Ariadne usually has `gql`. Let me check the file list again. `ariadne/build_schema.py`, `ariadne/executable_schema.py`, `ariadne/resolvers.py`. Maybe it's in `ariadne/build_schema.py`?
TOOL CALL · read_file
{
"end_line": 150,
"filepath": "ariadne/build_schema.py",
"start_line": 1
}Result of read_file 2,086 characters
{
"status": "ok",
"filepath": "ariadne/build_schema.py",
"content": "from typing import List, Union\n\nfrom graphql import GraphQLSchema, parse\nfrom graphql.language.ast import (\n Document,\n ObjectTypeDefinition,\n OperationTypeDefinition,\n Name,\n NamedType,\n SchemaDefinition,\n)\nfrom graphql.utils.build_ast_schema import build_ast_schema\n\n\ndef build_schema_from_type_definitions(\n type_defs: Union[str, List[str]]\n) -> GraphQLSchema:\n if isinstance(type_defs, list):\n type_defs = concatenate_type_defs(type_defs)\n\n document = parse(type_defs)\n\n if not document_has_schema(document):\n schema_definition = build_default_schema(document)\n document.definitions.append(schema_definition)\n\n return build_ast_schema(document)\n\n\ndef concatenate_type_defs(type_defs: List[str]) -> str:\n resolved_type_defs = []\n for type_def in type_defs:\n resolved_type_defs.append(type_def.strip())\n return \"\\n\\n\".join(resolved_type_defs)\n\n\ndef build_default_schema(document: Document) -> SchemaDefinition:\n defined_types = [\n td.name.value\n for td in document.definitions\n if isinstance(td, ObjectTypeDefinition)\n ]\n operations = []\n if \"Query\" in defined_types:\n operations.append(\n OperationTypeDefinition(\"query\", type=NamedType(name=Name(\"Query\")))\n )\n if \"Mutation\" in defined_types:\n operations.append(\n OperationTypeDefinition(\"mutation\", type=NamedType(name=Name(\"Mutation\")))\n )\n if \"Subscription\" in defined_types:\n operations.append(\n OperationTypeDefinition(\n \"subscription\", type=NamedType(name=Name(\"Subscription\"))\n )\n )\n return SchemaDefinition(operation_types=operations, directives=[])\n\n\ndef document_has_schema(document: Document) -> bool:\n return any(isinstance(td, SchemaDefinition) for td in document.definitions)",
"start_line": 1,
"end_line": 62,
"total_lines": 62,
"is_truncated": false
}Generated tokens 104 tokens · stopped: STOP
That's strange. Ariadne usually has `gql`. Let me check the file list again.
`ariadne/build_schema.py`, `ariadne/executable_schema.py`, `ariadne/resolvers.py`.
Maybe it's in `ariadne/build_schema.py`?
<channel|><|tool_call>call:read_file{filepath:<|"|>ariadne/build_schema.py<|"|>,start_line:1,end_line:150}<tool_call|><|tool_response>
Turn 9at 218 s492 tokens out8,233 prompt tokens
It seems `gql` is not present in this version of Ariadne. I should use a string for `type_defs` since `build_schema_from_type_definitions` accepts `Union[str, List[str]]`. Let's try the reproduction again with a string for `type_defs`.
TOOL CALL · run_command
{
"command": "python3 - <<'PY'\nfrom graphql import GraphQLSchema, GraphQLScalarType\nfrom ariadne import make_executable_schema\n\ndef serialize(value):\n return f\"Serialized: {value}\"\n\ndef parse_value(value):\n return f\"ParsedValue: {value}\"\n\ndef parse_literal(node):\n return f\"ParsedLiteral: {node.value}\"\n\ntype_defs = \"scalar MyScalar\"\nresolvers = {\n 'MyScalar': {\n 'serialize': serialize,\n 'parse_value': parse_value,\n 'parse_literal': parse_literal,\n }\n}\n\nschema = make_executable_schema(type_defs, resolvers)\n\nscalar_type = schema.get_type_map()['MyScalar']\n\nprint(f\"Scalar Type: {scalar_type}\")\nprint(f\"Serialize: {scalar_type.serialize}\")\nprint(f\"Parse Value: {scalar_type.parse_value}\")\nprint(f\"Parse Literal: {scalar_type.parse_literal}\")\n\nif scalar_type.serialize == resolvers['MyScalar']:\n print(\"FAILED: serialize is set to the dict, not the serialize function\")\nelif scalar_type.serialize == serialize:\n print(\"SUCCESS: serialize is set correctly\")\nelse:\n print(f\"UNEXPECTED: serialize is {scalar_type.serialize}\")\n\nif scalar_type.parse_value == parse_value:\n print(\"SUCCESS: parse_value is set correctly\")\nelse:\n print(f\"FAILED: parse_value is {scalar_type.parse_value}\")\n\nif scalar_type.parse_literal == parse_literal:\n print(\"SUCCESS: parse_literal is set correctly\")\nelse:\n print(f\"FAILED: parse_literal is {scalar_type.parse_literal}\")\nPY\n"
}Result of run_command 1,345 characters
{
"status": "error",
"error_type": "CommandError",
"error_message": "Traceback (most recent call last):\n File \"<stdin>\", line 22, in <module>\n File \"/workspace/ariadne/executable_schema.py\", line 12, in make_executable_schema\n schema = build_schema_from_type_definitions(type_defs)\n File \"/workspace/ariadne/build_schema.py\", line 27, in build_schema_from_type_definitions\n return build_ast_schema(document)\n File \"/opt/py37/lib/python3.7/site-packages/graphql/utils/build_ast_schema.py\", line 124, in build_ast_schema\n raise Exception(\"Must provide schema definition with query type.\")\nException: Must provide schema definition with query type.\n",
"details": {
"stdout": "",
"stderr": "Traceback (most recent call last):\n File \"<stdin>\", line 22, in <module>\n File \"/workspace/ariadne/executable_schema.py\", line 12, in make_executable_schema\n schema = build_schema_from_type_definitions(type_defs)\n File \"/workspace/ariadne/build_schema.py\", line 27, in build_schema_from_type_definitions\n return build_ast_schema(document)\n File \"/opt/py37/lib/python3.7/site-packages/graphql/utils/build_ast_schema.py\", line 124, in build_ast_schema\n raise Exception(\"Must provide schema definition with query type.\")\nException: Must provide schema definition with query type.\n",
"exit_code": 1
}
}Generated tokens 492 tokens · stopped: STOP
It seems `gql` is not present in this version of Ariadne. I should use a string for `type_defs` since `build_schema_from_type_definitions` accepts `Union[str, List[str]]`.
Let's try the reproduction again with a string for `type_defs`.
<channel|><|tool_call>call:run_command{command:<|"|>python3 - <<'PY'
from graphql import GraphQLSchema, GraphQLScalarType
from ariadne import make_executable_schema
def serialize(value):
return f"Serialized: {value}"
def parse_value(value):
return f"ParsedValue: {value}"
def parse_literal(node):
return f"ParsedLiteral: {node.value}"
type_defs = "scalar MyScalar"
resolvers = {
'MyScalar': {
'serialize': serialize,
'parse_value': parse_value,
'parse_literal': parse_literal,
}
}
schema = make_executable_schema(type_defs, resolvers)
scalar_type = schema.get_type_map()['MyScalar']
print(f"Scalar Type: {scalar_type}")
print(f"Serialize: {scalar_type.serialize}")
print(f"Parse Value: {scalar_type.parse_value}")
print(f"Parse Literal: {scalar_type.parse_literal}")
if scalar_type.serialize == resolvers['MyScalar']:
print("FAILED: serialize is set to the dict, not the serialize function")
elif scalar_type.serialize == serialize:
print("SUCCESS: serialize is set correctly")
else:
print(f"UNEXPECTED: serialize is {scalar_type.serialize}")
if scalar_type.parse_value == parse_value:
print("SUCCESS: parse_value is set correctly")
else:
print(f"FAILED: parse_value is {scalar_type.parse_value}")
if scalar_type.parse_literal == parse_literal:
print("SUCCESS: parse_literal is set correctly")
else:
print(f"FAILED: parse_literal is {scalar_type.parse_literal}")
PY
<|"|>}<tool_call|><|tool_response>
Turn 10at 250 s729 tokens out9,160 prompt tokens
The error is "Must provide schema definition with query type.".
In `ariadne/build_schema.py`, `build_schema_from_type_definitions` tries to add a default schema if one isn't provided:
```python
if not document_has_schema(document):
schema_definition = build_default_schema(document)
document.definitions.append(schema_definition)
```
But `build_default_schema` only adds `Query` if `Query` is defined in the document.
```python
def build_default_schema(document: Document) -> SchemaDefinition:
defined_types = [
td.name.value
for td in document.definitions
if isinstance(td, ObjectTypeDefinition)
]
operations = []
if "Query" in defined_types:
# ...
```
Since I only have `scalar MyScalar`, `defined_types` is empty, and `operations` is empty. `build_ast_schema` then fails because it needs at least a query type.
I should add a dummy query type to my reproduction script.
```python
type_defs = """
type Query {
query: String
}
scalar MyScalar
"""
```
Let's try again.
TOOL CALL · run_command
{
"command": "python3 - <<'PY'\nfrom graphql import GraphQLSchema, GraphQLScalarType\nfrom ariadne import make_executable_schema\n\ndef serialize(value):\n return f\"Serialized: {value}\"\n\ndef parse_value(value):\n return f\"ParsedValue: {value}\"\n\ndef parse_literal(node):\n return f\"ParsedLiteral: {node.value}\"\n\ntype_defs = \"\"\"\n type Query {\n query: String\n }\n scalar MyScalar\n\"\"\"\nresolvers = {\n 'MyScalar': {\n 'serialize': serialize,\n 'parse_value': parse_value,\n 'parse_literal': parse_literal,\n }\n}\n\nschema = make_executable_schema(type_defs, resolvers)\n\nscalar_type = schema.get_type_map()['MyScalar']\n\nprint(f\"Scalar Type: {scalar_type}\")\nprint(f\"Serialize: {scalar_type.serialize}\")\nprint(f\"Parse Value: {scalar_type.parse_value}\")\nprint(f\"Parse Literal: {scalar_type.parse_literal}\")\n\nif scalar_type.serialize == resolvers['MyScalar']:\n print(\"FAILED: serialize is set to the dict, not the serialize function\")\nelif scalar_type.serialize == serialize:\n print(\"SUCCESS: serialize is set correctly\")\nelse:\n print(f\"UNEXPECTED: serialize is {scalar_type.serialize}\")\n\nif scalar_type.parse_value == parse_value:\n print(\"SUCCESS: parse_value is set correctly\")\nelse:\n print(f\"FAILED: parse_value is {scalar_type.parse_value}\")\n\nif scalar_type.parse_literal == parse_literal:\n print(\"SUCCESS: parse_literal is set correctly\")\nelse:\n print(f\"FAILED: parse_literal is {scalar_type.parse_literal}\")\nPY\n"
}Result of run_command 557 characters
{
"status": "ok",
"stdout": "Scalar Type: MyScalar\nSerialize: {'serialize': <function serialize at 0x753629085b90>, 'parse_value': <function parse_value at 0x75362a0df560>, 'parse_literal': <function parse_literal at 0x75362a0df3b0>}\nParse Value: <function _false at 0x753628ff00e0>\nParse Literal: <function _false at 0x753628ff00e0>\nFAILED: serialize is set to the dict, not the serialize function\nFAILED: parse_value is <function _false at 0x753628ff00e0>\nFAILED: parse_literal is <function _false at 0x753628ff00e0>\n",
"stderr": "",
"exit_code": 0
}Generated tokens 729 tokens · stopped: STOP
The error is "Must provide schema definition with query type.".
In `ariadne/build_schema.py`, `build_schema_from_type_definitions` tries to add a default schema if one isn't provided:
```python
if not document_has_schema(document):
schema_definition = build_default_schema(document)
document.definitions.append(schema_definition)
```
But `build_default_schema` only adds `Query` if `Query` is defined in the document.
```python
def build_default_schema(document: Document) -> SchemaDefinition:
defined_types = [
td.name.value
for td in document.definitions
if isinstance(td, ObjectTypeDefinition)
]
operations = []
if "Query" in defined_types:
# ...
```
Since I only have `scalar MyScalar`, `defined_types` is empty, and `operations` is empty. `build_ast_schema` then fails because it needs at least a query type.
I should add a dummy query type to my reproduction script.
```python
type_defs = """
type Query {
query: String
}
scalar MyScalar
"""
```
Let's try again.
<channel|><|tool_call>call:run_command{command:<|"|>python3 - <<'PY'
from graphql import GraphQLSchema, GraphQLScalarType
from ariadne import make_executable_schema
def serialize(value):
return f"Serialized: {value}"
def parse_value(value):
return f"ParsedValue: {value}"
def parse_literal(node):
return f"ParsedLiteral: {node.value}"
type_defs = """
type Query {
query: String
}
scalar MyScalar
"""
resolvers = {
'MyScalar': {
'serialize': serialize,
'parse_value': parse_value,
'parse_literal': parse_literal,
}
}
schema = make_executable_schema(type_defs, resolvers)
scalar_type = schema.get_type_map()['MyScalar']
print(f"Scalar Type: {scalar_type}")
print(f"Serialize: {scalar_type.serialize}")
print(f"Parse Value: {scalar_type.parse_value}")
print(f"Parse Literal: {scalar_type.parse_literal}")
if scalar_type.serialize == resolvers['MyScalar']:
print("FAILED: serialize is set to the dict, not the serialize function")
elif scalar_type.serialize == serialize:
print("SUCCESS: serialize is set correctly")
else:
print(f"UNEXPECTED: serialize is {scalar_type.serialize}")
if scalar_type.parse_value == parse_value:
print("SUCCESS: parse_value is set correctly")
else:
print(f"FAILED: parse_value is {scalar_type.parse_value}")
if scalar_type.parse_literal == parse_literal:
print("SUCCESS: parse_literal is set correctly")
else:
print(f"FAILED: parse_literal is {scalar_type.parse_literal}")
PY
<|"|>}<tool_call|><|tool_response>
Turn 11at 356 s1,913 tokens out10,129 prompt tokens
Confirmed. The current implementation of `add_resolve_function_to_scalar` just takes whatever is at `resolvers[name]` and sets it as `obj.serialize`.
```python
def add_resolve_function_to_scalar(name: str, obj: GraphQLObjectType, resolvers: dict):
serializer = resolvers.get(name, obj.serialize)
obj.serialize = serializer
```
If `resolvers[name]` is a dict, `obj.serialize` becomes that dict. And `parse_value` and `parse_literal` remain whatever they were (likely default `_false` from `graphql-core`).
Now I need to implement it correctly.
I should modify `add_resolve_function_to_scalar` to handle both a callable and a dictionary.
If it's a callable, it should be assigned to `serialize`.
If it's a dictionary, it should look for `serialize`, `parse_value`, and `parse_literal`.
Wait, what if only some of them are provided?
The problem statement says:
- `type_defs = {'Scalar': {'serialize': callable}}`
- `type_defs = {'Scalar': {'serialize': callable, 'parse_value': callable, 'parse_literal': callable}}`
So I should handle both.
Proposed implementation for `add_resolve_function_to_scalar`:
```python
def add_resolve_function_to_scalar(name: str, obj: GraphQLScalarType, resolvers: dict):
scalar_resolvers = resolvers.get(name)
if scalar_resolvers is None:
return
if callable(scalar_resolvers):
obj.serialize = scalar_resolvers
elif isinstance(scalar_resolvers, dict):
if 'serialize' in scalar_resolvers:
obj.serialize = scalar_resolvers['serialize']
if 'parse_value' in scalar_resolvers:
obj.parse_value = scalar_resolvers['parse_value']
if 'parse_literal' in scalar_resolvers:
obj.parse_literal = scalar_resolvers['parse_literal']
```
Wait, the current implementation is:
```python
def add_resolve_function_to_scalar(name: str, obj: GraphQLObjectType, resolvers: dict):
serializer = resolvers.get(name, obj.serialize)
obj.serialize = serializer
```
It does not check if `name` is in `resolvers`. If it's not, it just sets `obj.serialize = obj.serialize`, which is a no-op.
One detail: the type hint for `obj` is `GraphQLObjectType`, but it should be `GraphQLScalarType`.
Let's check the imports in `ariadne/resolvers.py`.
```python
from graphql import GraphQLObjectType, GraphQLScalarType, GraphQLSchema
```
Yes, `GraphQLScalarType` is available.
Let's implement this fix.
Wait, I should also test the case where only a callable is provided.
```python
type_defs = """
type Query {
query: String
}
scalar MyScalar
"""
resolvers = {
'MyScalar': serialize,
}
```
In this case, `obj.serialize` should be `serialize`.
Let's refine the implementation to be concise.
```python
def add_resolve_function_to_scalar(name: str, obj: GraphQLScalarType, resolvers: dict):
resolver = resolvers.get(name)
if callable(resolver):
obj.serialize = resolver
elif isinstance(resolver, dict):
obj.serialize = resolver.get('serialize', obj.serialize)
obj.parse_value = resolver.get('parse_value', obj.parse_value)
obj.parse_literal = resolver.get('parse_literal', obj.parse_literal)
```
Actually, if I use `.get(..., obj.serialize)`, it won't overwrite with `None` if the user explicitly put `None` in the dict, but that's probably fine.
Wait, if `resolver` is `None` (meaning `name` is not in `resolvers`), then `callable(None)` is False and `isinstance(None, dict)` is False, so it does nothing. That's correct.
Let's check if `GraphQLScalarType` in `graphql-core` actually allows setting `serialize`, `parse_value`, and `parse_literal` attributes after instantiation.
In `graphql-core` (depending on version), these are often passed in constructor. However, many versions allow overwriting them on the instance.
Looking at the reproduction output:
`Parse Value: <function _false at 0x753628ff00e0>`
`Parse Literal: <function _false at 0x753628ff00e0>`
These are the default attributes.
Wait, I should check if `GraphQLScalarType` attributes are actually writable.
In my reproduction, I set `obj.serialize = serializer` and it worked (it set it to the dict). So they are writable.
Let's apply the fix to `ariadne/resolvers.py`.
Wait, I noticed something in `ariadne/resolvers.py`:
```python
def add_resolve_functions_to_schema(schema: GraphQLSchema, resolvers: dict):
for type_name, type_object in schema.get_type_map().items():
if isinstance(type_object, GraphQLObjectType):
add_resolve_functions_to_object(type_name, type_object, resolvers)
if isinstance(type_object, GraphQLScalarType):
add_resolve_function_to_scalar(type_name, type_object, resolvers)
```
And `add_resolve_function_to_scalar` is:
```python
def add_resolve_function_to_scalar(name: str, obj: GraphQLObjectType, resolvers: dict):
serializer = resolvers.get(name, obj.serialize)
obj.serialize = serializer
```
I will change `GraphQLObjectType` to `GraphQLScalarType` for the type hint and update the logic.
Wait, if `resolver` is a callable, I only set `serialize`. This matches the current behavior (where `resolvers[name]` was used as `serialize`).
Let's review the requested use cases again:
- `type_defs = {'Scalar': {'serialize': callable}}` -> `scalar_type.serialize` set to `callable`.
- `type_defs = {'Scalar': {'serialize': callable, 'parse_value': callable, 'parse_literal': callable}}` -> all three set.
What if the user only provides `parse_value`?
`type_defs = {'Scalar': {'parse_value': callable}}`
The current implementation of `add_resolve_function_to_scalar` (broken) would set `obj.serialize` to the dict.
My proposed implementation:
```python
elif isinstance(resolver, dict):
obj.serialize = resolver.get('serialize', obj.serialize)
obj.parse_value = resolver.get('parse_value', obj.parse_value)
obj.parse_literal = resolver.get('parse_literal', obj.parse_literal)
```
This would set `obj.parse_value` to `callable` and keep `obj.serialize` as is. This seems correct.
One more thing: does `graphql-core` `GraphQLScalarType` have these as properties or just attributes?
Usually they are attributes.
Let's double check the types used in `ariadne/resolvers.py`.
`GraphQLObjectType`, `GraphQLScalarType`, `GraphQLSchema` are imported from `graphql`.
Let's apply the change.
TOOL CALL · edit_file
{
"filepath": "ariadne/resolvers.py",
"new_string": "def add_resolve_function_to_scalar(name: str, obj: GraphQLScalarType, resolvers: dict):\n resolver = resolvers.get(name)\n if callable(resolver):\n obj.serialize = resolver\n elif isinstance(resolver, dict):\n obj.serialize = resolver.get(\"serialize\", obj.serialize)\n obj.parse_value = resolver.get(\"parse_value\", obj.parse_value)\n obj.parse_literal = resolver.get(\"parse_literal\", obj.parse_literal)\n",
"old_string": "def add_resolve_function_to_scalar(name: str, obj: GraphQLObjectType, resolvers: dict):\n serializer = resolvers.get(name, obj.serialize)\n obj.serialize = serializer"
}Result of edit_file 892 characters
{
"status": "ok",
"filepath": "ariadne/resolvers.py",
"occurrences": 1,
"strategy": "exact",
"diff": "--- a/ariadne/resolvers.py\n+++ b/ariadne/resolvers.py\n@@ -34,6 +34,12 @@\n field_object.resolver = field_resolver\n \n \n-def add_resolve_function_to_scalar(name: str, obj: GraphQLObjectType, resolvers: dict):\n- serializer = resolvers.get(name, obj.serialize)\n- obj.serialize = serializer\n+def add_resolve_function_to_scalar(name: str, obj: GraphQLScalarType, resolvers: dict):\n+ resolver = resolvers.get(name)\n+ if callable(resolver):\n+ obj.serialize = resolver\n+ elif isinstance(resolver, dict):\n+ obj.serialize = resolver.get(\"serialize\", obj.serialize)\n+ obj.parse_value = resolver.get(\"parse_value\", obj.parse_value)\n+ obj.parse_literal = resolver.get(\"parse_literal\", obj.parse_literal)\n+\n",
"is_truncated": false
}Generated tokens 1,913 tokens · stopped: STOP
Confirmed. The current implementation of `add_resolve_function_to_scalar` just takes whatever is at `resolvers[name]` and sets it as `obj.serialize`.
```python
def add_resolve_function_to_scalar(name: str, obj: GraphQLObjectType, resolvers: dict):
serializer = resolvers.get(name, obj.serialize)
obj.serialize = serializer
```
If `resolvers[name]` is a dict, `obj.serialize` becomes that dict. And `parse_value` and `parse_literal` remain whatever they were (likely default `_false` from `graphql-core`).
Now I need to implement it correctly.
I should modify `add_resolve_function_to_scalar` to handle both a callable and a dictionary.
If it's a callable, it should be assigned to `serialize`.
If it's a dictionary, it should look for `serialize`, `parse_value`, and `parse_literal`.
Wait, what if only some of them are provided?
The problem statement says:
- `type_defs = {'Scalar': {'serialize': callable}}`
- `type_defs = {'Scalar': {'serialize': callable, 'parse_value': callable, 'parse_literal': callable}}`
So I should handle both.
Proposed implementation for `add_resolve_function_to_scalar`:
```python
def add_resolve_function_to_scalar(name: str, obj: GraphQLScalarType, resolvers: dict):
scalar_resolvers = resolvers.get(name)
if scalar_resolvers is None:
return
if callable(scalar_resolvers):
obj.serialize = scalar_resolvers
elif isinstance(scalar_resolvers, dict):
if 'serialize' in scalar_resolvers:
obj.serialize = scalar_resolvers['serialize']
if 'parse_value' in scalar_resolvers:
obj.parse_value = scalar_resolvers['parse_value']
if 'parse_literal' in scalar_resolvers:
obj.parse_literal = scalar_resolvers['parse_literal']
```
Wait, the current implementation is:
```python
def add_resolve_function_to_scalar(name: str, obj: GraphQLObjectType, resolvers: dict):
serializer = resolvers.get(name, obj.serialize)
obj.serialize = serializer
```
It does not check if `name` is in `resolvers`. If it's not, it just sets `obj.serialize = obj.serialize`, which is a no-op.
One detail: the type hint for `obj` is `GraphQLObjectType`, but it should be `GraphQLScalarType`.
Let's check the imports in `ariadne/resolvers.py`.
```python
from graphql import GraphQLObjectType, GraphQLScalarType, GraphQLSchema
```
Yes, `GraphQLScalarType` is available.
Let's implement this fix.
Wait, I should also test the case where only a callable is provided.
```python
type_defs = """
type Query {
query: String
}
scalar MyScalar
"""
resolvers = {
'MyScalar': serialize,
}
```
In this case, `obj.serialize` should be `serialize`.
Let's refine the implementation to be concise.
```python
def add_resolve_function_to_scalar(name: str, obj: GraphQLScalarType, resolvers: dict):
resolver = resolvers.get(name)
if callable(resolver):
obj.serialize = resolver
elif isinstance(resolver, dict):
obj.serialize = resolver.get('serialize', obj.serialize)
obj.parse_value = resolver.get('parse_value', obj.parse_value)
obj.parse_literal = resolver.get('parse_literal', obj.parse_literal)
```
Actually, if I use `.get(..., obj.serialize)`, it won't overwrite with `None` if the user explicitly put `None` in the dict, but that's probably fine.
Wait, if `resolver` is `None` (meaning `name` is not in `resolvers`), then `callable(None)` is False and `isinstance(None, dict)` is False, so it does nothing. That's correct.
Let's check if `GraphQLScalarType` in `graphql-core` actually allows setting `serialize`, `parse_value`, and `parse_literal` attributes after instantiation.
In `graphql-core` (depending on version), these are often passed in constructor. However, many versions allow overwriting them on the instance.
Looking at the reproduction output:
`Parse Value: <function _false at 0x753628ff00e0>`
`Parse Literal: <function _false at 0x753628ff00e0>`
These are the default attributes.
Wait, I should check if `GraphQLScalarType` attributes are actually writable.
In my reproduction, I set `obj.serialize = serializer` and it worked (it set it to the dict). So they are writable.
Let's apply the fix to `ariadne/resolvers.py`.
Wait, I noticed something in `ariadne/resolvers.py`:
```python
def add_resolve_functions_to_schema(schema: GraphQLSchema, resolvers: dict):
for type_name, type_object in schema.get_type_map().items():
if isinstance(type_object, GraphQLObjectType):
add_resolve_functions_to_object(type_name, type_object, resolvers)
if isinstance(type_object, GraphQLScalarType):
add_resolve_function_to_scalar(type_name, type_object, resolvers)
```
And `add_resolve_function_to_scalar` is:
```python
def add_resolve_function_to_scalar(name: str, obj: GraphQLObjectType, resolvers: dict):
serializer = resolvers.get(name, obj.serialize)
obj.serialize = serializer
```
I will change `GraphQLObjectType` to `GraphQLScalarType` for the type hint and update the logic.
Wait, if `resolver` is a callable, I only set `serialize`. This matches the current behavior (where `resolvers[name]` was used as `serialize`).
Let's review the requested use cases again:
- `type_defs = {'Scalar': {'serialize': callable}}` -> `scalar_type.serialize` set to `callable`.
- `type_defs = {'Scalar': {'serialize': callable, 'parse_value': callable, 'parse_literal': callable}}` -> all three set.
What if the user only provides `parse_value`?
`type_defs = {'Scalar': {'parse_value': callable}}`
The current implementation of `add_resolve_function_to_scalar` (broken) would set `obj.serialize` to the dict.
My proposed implementation:
```python
elif isinstance(resolver, dict):
obj.serialize = resolver.get('serialize', obj.serialize)
obj.parse_value = resolver.get('parse_value', obj.parse_value)
obj.parse_literal = resolver.get('parse_literal', obj.parse_literal)
```
This would set `obj.parse_value` to `callable` and keep `obj.serialize` as is. This seems correct.
One more thing: does `graphql-core` `GraphQLScalarType` have these as properties or just attributes?
Usually they are attributes.
Let's double check the types used in `ariadne/resolvers.py`.
`GraphQLObjectType`, `GraphQLScalarType`, `GraphQLSchema` are imported from `graphql`.
Let's apply the change.
<channel|><|tool_call>call:edit_file{filepath:<|"|>ariadne/resolvers.py<|"|>,new_string:<|"|>def add_resolve_function_to_scalar(name: str, obj: GraphQLScalarType, resolvers: dict):
resolver = resolvers.get(name)
if callable(resolver):
obj.serialize = resolver
elif isinstance(resolver, dict):
obj.serialize = resolver.get("serialize", obj.serialize)
obj.parse_value = resolver.get("parse_value", obj.parse_value)
obj.parse_literal = resolver.get("parse_literal", obj.parse_literal)
<|"|>,old_string:<|"|>def add_resolve_function_to_scalar(name: str, obj: GraphQLObjectType, resolvers: dict):
serializer = resolvers.get(name, obj.serialize)
obj.serialize = serializer<|"|>}<tool_call|><|tool_response>
Turn 12at 388 s688 tokens out12,335 prompt tokens
Now let's verify the fix with the reproduction script and also test the single callable case.
TOOL CALL · run_command
{
"command": "python3 - <<'PY'\nfrom graphql import GraphQLSchema, GraphQLScalarType\nfrom ariadne import make_executable_schema\n\ndef serialize(value):\n return f\"Serialized: {value}\"\n\ndef parse_value(value):\n return f\"ParsedValue: {value}\"\n\ndef parse_literal(node):\n return f\"ParsedLiteral: {node.value}\"\n\ntype_defs = \"\"\"\n type Query {\n query: String\n }\n scalar MyScalar\n\"\"\"\n\n# Case 1: Full dict\nresolvers_full = {\n 'MyScalar': {\n 'serialize': serialize,\n 'parse_value': parse_value,\n 'parse_literal': parse_literal,\n }\n}\n\nschema_full = make_executable_schema(type_defs, resolvers_full)\nscalar_full = schema_full.get_type_map()['MyScalar']\nprint(f\"Full Dict - Serialize: {scalar_full.serialize == serialize}\")\nprint(f\"Full Dict - Parse Value: {scalar_full.parse_value == parse_value}\")\nprint(f\"Full Dict - Parse Literal: {scalar_full.parse_literal == parse_literal}\")\n\n# Case 2: Only serialize in dict\nresolvers_ser = {\n 'MyScalar': {\n 'serialize': serialize,\n }\n}\nschema_ser = make_executable_schema(type_defs, resolvers_ser)\nscalar_ser = schema_ser.get_type_map()['MyScalar']\nprint(f\"Serialize Only Dict - Serialize: {scalar_ser.serialize == serialize}\")\n\n# Case 3: Single callable\nresolvers_call = {\n 'MyScalar': serialize,\n}\nschema_call = make_executable_schema(type_defs, resolvers_call)\nscalar_call = schema_call.get_type_map()['MyScalar']\nprint(f\"Single Callable - Serialize: {scalar_call.serialize == serialize}\")\n\n# Case 4: Only parse_value in dict\nresolvers_pv = {\n 'MyScalar': {\n 'parse_value': parse_value,\n }\n}\nschema_pv = make_executable_schema(type_defs, resolvers_pv)\nscalar_pv = schema_pv.get_type_map()['MyScalar']\nprint(f\"Parse Value Only Dict - Parse Value: {scalar_pv.parse_value == parse_value}\")\n\n# Case 5: Only parse_literal in dict\nresolvers_pl = {\n 'MyScalar': {\n 'parse_literal': parse_literal,\n }\n}\nschema_pl = make_executable_schema(type_defs, resolvers_pl)\nscalar_pl = schema_pl.get_type_map()['MyScalar']\nprint(f\"Parse Literal Only Dict - Parse Literal: {scalar_pl.parse_literal == parse_literal}\")\nPY\n"
}Result of run_command 317 characters
{
"status": "ok",
"stdout": "Full Dict - Serialize: True\nFull Dict - Parse Value: True\nFull Dict - Parse Literal: True\nSerialize Only Dict - Serialize: True\nSingle Callable - Serialize: True\nParse Value Only Dict - Parse Value: True\nParse Literal Only Dict - Parse Literal: True\n",
"stderr": "",
"exit_code": 0
}Generated tokens 688 tokens · stopped: STOP
Now let's verify the fix with the reproduction script and also test the single callable case.
<channel|><|tool_call>call:run_command{command:<|"|>python3 - <<'PY'
from graphql import GraphQLSchema, GraphQLScalarType
from ariadne import make_executable_schema
def serialize(value):
return f"Serialized: {value}"
def parse_value(value):
return f"ParsedValue: {value}"
def parse_literal(node):
return f"ParsedLiteral: {node.value}"
type_defs = """
type Query {
query: String
}
scalar MyScalar
"""
# Case 1: Full dict
resolvers_full = {
'MyScalar': {
'serialize': serialize,
'parse_value': parse_value,
'parse_literal': parse_literal,
}
}
schema_full = make_executable_schema(type_defs, resolvers_full)
scalar_full = schema_full.get_type_map()['MyScalar']
print(f"Full Dict - Serialize: {scalar_full.serialize == serialize}")
print(f"Full Dict - Parse Value: {scalar_full.parse_value == parse_value}")
print(f"Full Dict - Parse Literal: {scalar_full.parse_literal == parse_literal}")
# Case 2: Only serialize in dict
resolvers_ser = {
'MyScalar': {
'serialize': serialize,
}
}
schema_ser = make_executable_schema(type_defs, resolvers_ser)
scalar_ser = schema_ser.get_type_map()['MyScalar']
print(f"Serialize Only Dict - Serialize: {scalar_ser.serialize == serialize}")
# Case 3: Single callable
resolvers_call = {
'MyScalar': serialize,
}
schema_call = make_executable_schema(type_defs, resolvers_call)
scalar_call = schema_call.get_type_map()['MyScalar']
print(f"Single Callable - Serialize: {scalar_call.serialize == serialize}")
# Case 4: Only parse_value in dict
resolvers_pv = {
'MyScalar': {
'parse_value': parse_value,
}
}
schema_pv = make_executable_schema(type_defs, resolvers_pv)
scalar_pv = schema_pv.get_type_map()['MyScalar']
print(f"Parse Value Only Dict - Parse Value: {scalar_pv.parse_value == parse_value}")
# Case 5: Only parse_literal in dict
resolvers_pl = {
'MyScalar': {
'parse_literal': parse_literal,
}
}
schema_pl = make_executable_schema(type_defs, resolvers_pl)
scalar_pl = schema_pl.get_type_map()['MyScalar']
print(f"Parse Literal Only Dict - Parse Literal: {scalar_pl.parse_literal == parse_literal}")
PY
<|"|>}<tool_call|>('').<|tool_response>
Turn 13at 396 s149 tokens out13,125 prompt tokens
All test cases passed. Now I should check if there are any other places where scalars are handled. I already checked `ariadne/resolvers.py`, `ariadne/executable_schema.py`, and `ariadne/build_schema.py`. The `add_resolve_functions_to_schema` function in `ariadne/resolvers.py` is the entry point for this functionality and it calls `add_resolve_function_to_scalar`. I should check if there are any existing tests for scalars in the `tests/` directory.
TOOL CALL · run_command
{
"command": "grep -rn \"scalar\" tests/ | head -n 20"
}Result of run_command 292 characters
{
"status": "ok",
"stdout": "tests/test_queries.py:9:def test_query_default_scalar():\ntests/test_queries.py:25:def test_query_custom_scalar():\ntests/test_queries.py:27: scalar Date\ntests/test_mutations.py:6:def test_mutation_return_default_scalar():\n",
"stderr": "",
"exit_code": 0
}Generated tokens 149 tokens · stopped: STOP
All test cases passed.
Now I should check if there are any other places where scalars are handled.
I already checked `ariadne/resolvers.py`, `ariadne/executable_schema.py`, and `ariadne/build_schema.py`.
The `add_resolve_functions_to_schema` function in `ariadne/resolvers.py` is the entry point for this functionality and it calls `add_resolve_function_to_scalar`.
I should check if there are any existing tests for scalars in the `tests/` directory.
<channel|><|tool_call>call:run_command{command:<|"|>grep -rn "scalar" tests/ | head -n 20<|"|>}<tool_call|><|tool_response>
Turn 14at 398 s35 tokens out13,395 prompt tokens
Let's check `tests/test_queries.py`.
TOOL CALL · read_file
{
"filepath": "tests/test_queries.py"
}Result of read_file 3,932 characters
{
"status": "ok",
"filepath": "tests/test_queries.py",
"content": "from datetime import date\nfrom unittest.mock import Mock\n\nfrom graphql import graphql\n\nfrom ariadne import make_executable_schema, resolve_to\n\n\ndef test_query_default_scalar():\n type_defs = \"\"\"\n type Query {\n test: String\n }\n \"\"\"\n\n resolvers = {\"Query\": {\"test\": lambda *_: \"success\"}}\n\n schema = make_executable_schema(type_defs, resolvers)\n\n result = graphql(schema, \"{ test }\")\n assert result.errors is None\n assert result.data == {\"test\": \"success\"}\n\n\ndef test_query_custom_scalar():\n type_defs = \"\"\"\n scalar Date\n\n type Query {\n test: Date\n }\n \"\"\"\n\n resolvers = {\n \"Query\": {\"test\": lambda *_: date.today()},\n \"Date\": lambda date: date.strftime(\"%Y-%m-%d\"),\n }\n\n schema = make_executable_schema(type_defs, resolvers)\n\n result = graphql(schema, \"{ test }\")\n assert result.errors is None\n assert result.data == {\"test\": date.today().strftime(\"%Y-%m-%d\")}\n\n\ndef test_query_custom_type_default_resolver():\n type_defs = \"\"\"\n type Query {\n test: Custom\n }\n\n type Custom {\n node: String\n }\n \"\"\"\n\n resolvers = {\"Query\": {\"test\": lambda *_: {\"node\": \"custom\"}}}\n\n schema = make_executable_schema(type_defs, resolvers)\n\n result = graphql(schema, \"{ test { node } }\")\n assert result.errors is None\n assert result.data == {\"test\": {\"node\": \"custom\"}}\n\n\ndef test_query_custom_type_object_default_resolver():\n type_defs = \"\"\"\n type Query {\n test: Custom\n }\n\n type Custom {\n node: String\n }\n \"\"\"\n\n resolvers = {\"Query\": {\"test\": lambda *_: Mock(node=\"custom\")}}\n\n schema = make_executable_schema(type_defs, resolvers)\n\n result = graphql(schema, \"{ test { node } }\")\n assert result.errors is None\n assert result.data == {\"test\": {\"node\": \"custom\"}}\n\n\ndef test_query_custom_type_custom_resolver():\n type_defs = \"\"\"\n type Query {\n test: Custom\n }\n\n type Custom {\n node: String\n }\n \"\"\"\n\n resolvers = {\n \"Query\": {\"test\": lambda *_: {\"node\": \"custom\"}},\n \"Custom\": {\"node\": lambda *_: \"deep\"},\n }\n\n schema = make_executable_schema(type_defs, resolvers)\n\n result = graphql(schema, \"{ test { node } }\")\n assert result.errors is None\n assert result.data == {\"test\": {\"node\": \"deep\"}}\n\n\ndef test_query_custom_type_merged_custom_default_resolvers():\n type_defs = \"\"\"\n type Query {\n test: Custom\n }\n\n type Custom {\n node: String\n default: String\n }\n \"\"\"\n\n resolvers = {\n \"Query\": {\"test\": lambda *_: {\"node\": \"custom\", \"default\": \"ok\"}},\n \"Custom\": {\"node\": lambda *_: \"deep\"},\n }\n\n schema = make_executable_schema(type_defs, resolvers)\n\n result = graphql(schema, \"{ test { node default } }\")\n assert result.errors is None\n assert result.data == {\"test\": {\"node\": \"deep\", \"default\": \"ok\"}}\n\n\ndef test_query_with_argument():\n type_defs = \"\"\"\n type Query {\n test(returnValue: Int!): Int\n }\n \"\"\"\n\n def resolve_test(*_, returnValue):\n assert returnValue == 4\n return \"42\"\n\n resolvers = {\"Query\": {\"test\": resolve_test}}\n\n schema = make_executable_schema(type_defs, resolvers)\n\n result = graphql(schema, \"{ test(returnValue: 4) }\")\n assert result.errors is None\n assert result.data == {\"test\": 42}",
"start_line": 1,
"end_line": 150,
"total_lines": 220,
"is_truncated": true
}Generated tokens 35 tokens · stopped: STOP
Let's check `tests/test_queries.py`.<channel|><|tool_call>call:read_file{filepath:<|"|>tests/test_queries.py<|"|>}<tool_call|><|tool_response>
Turn 15at 414 s328 tokens out14,658 prompt tokens
`test_query_custom_scalar` uses a single callable for the scalar resolver, which is the case I already tested.
I've covered the requirements:
1. Support `parse_value` and `parse_literal` when provided in a dictionary for a scalar.
2. Maintain support for the single callable (which is used as `serialize`).
3. Corrected the type hint for `obj` from `GraphQLObjectType` to `GraphQLScalarType`.
I'll do one last check of the code to make sure everything is correct.
`ariadne/resolvers.py`:
```python
def add_resolve_function_to_scalar(name: str, obj: GraphQLScalarType, resolvers: dict):
resolver = resolvers.get(name)
if callable(resolver):
obj.serialize = resolver
elif isinstance(resolver, dict):
obj.serialize = resolver.get("serialize", obj.serialize)
obj.parse_value = resolver.get("parse_value", obj.parse_value)
obj.parse_literal = resolver.get("parse_literal", obj.parse_literal)
```
This looks solid. If `resolvers.get(name)` is `None`, neither condition is met, and the scalar remains unchanged.
I'll run the tests one more time including existing ones to be sure.
TOOL CALL · run_command
{
"command": "pytest tests/test_queries.py -q --tb=short --no-header"
}Result of run_command 161 characters
{
"status": "ok",
"stdout": ".......... [100%]\n10 passed in 0.06s\n",
"stderr": "",
"exit_code": 0
}Generated tokens 328 tokens · stopped: STOP
`test_query_custom_scalar` uses a single callable for the scalar resolver, which is the case I already tested.
I've covered the requirements:
1. Support `parse_value` and `parse_literal` when provided in a dictionary for a scalar.
2. Maintain support for the single callable (which is used as `serialize`).
3. Corrected the type hint for `obj` from `GraphQLObjectType` to `GraphQLScalarType`.
I'll do one last check of the code to make sure everything is correct.
`ariadne/resolvers.py`:
```python
def add_resolve_function_to_scalar(name: str, obj: GraphQLScalarType, resolvers: dict):
resolver = resolvers.get(name)
if callable(resolver):
obj.serialize = resolver
elif isinstance(resolver, dict):
obj.serialize = resolver.get("serialize", obj.serialize)
obj.parse_value = resolver.get("parse_value", obj.parse_value)
obj.parse_literal = resolver.get("parse_literal", obj.parse_literal)
```
This looks solid. If `resolvers.get(name)` is `None`, neither condition is met, and the scalar remains unchanged.
I'll run the tests one more time including existing ones to be sure.
<channel|><|tool_call>call:run_command{command:<|"|>pytest tests/test_queries.py -q --tb=short --no-header<|"|>}<tool_call|><|tool_response>
Patch, tests and logs
Final code change Included
diff --git a/ariadne/resolvers.py b/ariadne/resolvers.py
index 388e3ad..46201f3 100644
--- a/ariadne/resolvers.py
+++ b/ariadne/resolvers.py
@@ -34,6 +34,12 @@ def add_resolve_functions_to_object(name: str, obj: GraphQLObjectType, resolvers
field_object.resolver = field_resolver
-def add_resolve_function_to_scalar(name: str, obj: GraphQLObjectType, resolvers: dict):
- serializer = resolvers.get(name, obj.serialize)
- obj.serialize = serializer
+def add_resolve_function_to_scalar(name: str, obj: GraphQLScalarType, resolvers: dict):
+ resolver = resolvers.get(name)
+ if callable(resolver):
+ obj.serialize = resolver
+ elif isinstance(resolver, dict):
+ obj.serialize = resolver.get("serialize", obj.serialize)
+ obj.parse_value = resolver.get("parse_value", obj.parse_value)
+ obj.parse_literal = resolver.get("parse_literal", obj.parse_literal)
+
Test output Included
============================= test session starts ============================== collected 16 items tests/test_custom_scalars.py ....... [ 43%] tests/test_queries.py ......... [100%] ==================================== PASSES ==================================== - generated xml file: /tmp/rebench-grade-c17beea275fa4bd5a4d2731896ffe73e.xml -- =========================== short test summary info ============================ PASSED tests/test_custom_scalars.py::test_serialize_date_obj_to_date_str PASSED tests/test_custom_scalars.py::test_parse_literal_valid_str_ast_to_date_instance PASSED tests/test_custom_scalars.py::test_parse_literal_invalid_str_ast_to_date_instance PASSED tests/test_custom_scalars.py::test_parse_literal_invalid_int_ast_errors PASSED tests/test_custom_scalars.py::test_parse_value_valid_date_str_returns_date_instance PASSED tests/test_custom_scalars.py::test_parse_value_invalid_str_errors PASSED tests/test_custom_scalars.py::test_parse_value_invalid_value_type_int_errors PASSED tests/test_queries.py::test_query_root_type_default_resolver PASSED tests/test_queries.py::test_query_custom_type_default_resolver PASSED tests/test_queries.py::test_query_custom_type_object_default_resolver PASSED tests/test_queries.py::test_query_custom_type_custom_resolver PASSED tests/test_queries.py::test_query_custom_type_merged_custom_default_resolvers PASSED tests/test_queries.py::test_query_with_argument PASSED tests/test_queries.py::test_query_with_input PASSED tests/test_queries.py::test_mapping_resolver PASSED tests/test_queries.py::test_mapping_resolver_to_object_attribute ============================== 16 passed in 0.08s ==============================
Test errors Included
Test report (JUnit) Included
<?xml version="1.0" encoding="utf-8"?><testsuites><testsuite errors="0" failures="0" hostname="c3530c8d4315" name="pytest" skipped="0" tests="16" time="0.083" timestamp="2026-11-14T04:59:37.866082"><testcase classname="tests.test_custom_scalars" name="test_serialize_date_obj_to_date_str" time="0.001" /><testcase classname="tests.test_custom_scalars" name="test_parse_literal_valid_str_ast_to_date_instance" time="0.001" /><testcase classname="tests.test_custom_scalars" name="test_parse_literal_invalid_str_ast_to_date_instance" time="0.000" /><testcase classname="tests.test_custom_scalars" name="test_parse_literal_invalid_int_ast_errors" time="0.000" /><testcase classname="tests.test_custom_scalars" name="test_parse_value_valid_date_str_returns_date_instance" time="0.001" /><testcase classname="tests.test_custom_scalars" name="test_parse_value_invalid_str_errors" time="0.001" /><testcase classname="tests.test_custom_scalars" name="test_parse_value_invalid_value_type_int_errors" time="0.001" /><testcase classname="tests.test_queries" name="test_query_root_type_default_resolver" time="0.001" /><testcase classname="tests.test_queries" name="test_query_custom_type_default_resolver" time="0.001" /><testcase classname="tests.test_queries" name="test_query_custom_type_object_default_resolver" time="0.001" /><testcase classname="tests.test_queries" name="test_query_custom_type_custom_resolver" time="0.001" /><testcase classname="tests.test_queries" name="test_query_custom_type_merged_custom_default_resolvers" time="0.001" /><testcase classname="tests.test_queries" name="test_query_with_argument" time="0.001" /><testcase classname="tests.test_queries" name="test_query_with_input" time="0.001" /><testcase classname="tests.test_queries" name="test_mapping_resolver" time="0.001" /><testcase classname="tests.test_queries" name="test_mapping_resolver_to_object_attribute" time="0.001" /></testsuite></testsuites>
Agent log Included
[ 0.0s] 📋 System Prompt: You are a principal Python software engineer resolving a repository issue in `/workspace`.
[ 0.0s] 🎯 Task Prompt: You are evaluating a software engineering task for repository mirumee/ariadne.
[ 20.3s] 💭 (692 chars) "The user wants to enhance `add_resolve_functions_to_schema` (or potentially
`add_resolve_functions_to_scalar`, although ..."
[ 20.3s] 🔧 run_command grep -rn --include="*.py" -E "add_resolve_functions_to_schema|add_resolve_functi
[ 20.3s] → ./ariadne/executable_schema.py:6:from .resolvers import add_resolve_functions_to_schema
[ 33.4s] 💭 (524 chars) "It seems `add_resolve_functions_to_schema` is defined in `ariadne/resolvers.py`. I didn't
see `add_resolve_functions_to_..."
[ 33.4s] 📖 read_file (ariadne/resolvers.py)
[ 33.4s] ✓ {'status': 'ok', 'filepath': 'ariadne/resolvers.py', 'content': 'from graphql import GraphQLObjectType,
GraphQLScalarTyp…
[ 101.7s] 💭 (4050 chars) "I found `add_resolve_function_to_scalar` (singular 'function') at the end of
`ariadne/resolvers.py`.
Wait, look closely ..."
[ 101.7s] 📖 read_file (ariadne/executable_schema.py)
[ 101.8s] ✓ {'status': 'ok', 'filepath': 'ariadne/executable_schema.py', 'content': 'from typing import List,
Union\n\nfrom graphql …
[ 152.1s] 💭 (1798 chars) "Yes, `make_executable_schema` calls `add_resolve_functions_to_schema`.
Let's create a reproduction script in `/tmp/repr..."
[ 152.1s] 🔧 run_command (48 lines) python3 - <<'PY'
[ 152.2s] → error (exit 0):
[ 184.0s] 💭 (1606 chars) "Ah, `gql` is from `ariadne`, not `graphql`. Correct.
Also, `make_executable_schema` was imported from both `graphql` and..."
[ 184.0s] 🔧 run_command (48 lines) python3 - <<'PY'
[ 184.2s] → error (exit 0):
[ 186.6s] 💭 (85 chars) "Wait, `gql` is not in `ariadne/__init__.py`?
Let's check `ariadne/__init__.py` again."
[ 186.6s] 📖 read_file (ariadne/__init__.py)
[ 186.7s] ✓ {'status': 'ok', 'filepath': 'ariadne/__init__.py', 'content': 'from .build_schema import
build_schema_from_type_definit…
[ 191.0s] 💭 (111 chars) "Indeed, `gql` is not there. I must have assumed it was. Let's see where `gql` is defined.
Search for `def gql`."
[ 191.0s] 🔧 run_command grep -rn "def gql" .
[ 191.0s] → error (exit 0):
[ 197.8s] 💭 (200 chars) "That's strange. Ariadne usually has `gql`. Let me check the file list again.
`ariadne/build_schema.py`, `ariadne/executa..."
[ 197.8s] 📖 read_file (ariadne/build_schema.py)
[ 197.8s] ✓ {'status': 'ok', 'filepath': 'ariadne/build_schema.py', 'content': 'from typing import List,
Union\n\nfrom graphql impor…
[ 217.6s] 💭 (236 chars) "It seems `gql` is not present in this version of Ariadne. I should use a string for
`type_defs` since `build_schema_from..."
[ 217.6s] 🔧 run_command (48 lines) python3 - <<'PY'
[ 217.7s] → error (exit 0):
[ 249.4s] 💭 (1064 chars) "The error is "Must provide schema definition with query type.".
In `ariadne/build_schema.py`, `build_schema_from_type_de..."
[ 249.4s] 🔧 run_command (53 lines) python3 - <<'PY'
[ 249.5s] → Scalar Type: MyScalar
[ 355.8s] 💭 (6335 chars) "Confirmed. The current implementation of `add_resolve_function_to_scalar` just takes
whatever is at `resolvers[name]` an..."
[ 355.8s] ✏️ edit_file (ariadne/resolvers.py)
[ 355.9s] ✓ {'status': 'ok', 'filepath': 'ariadne/resolvers.py', 'occurrences': 1, 'strategy': 'exact', 'diff': '---
a/ariadne/resol…
[ 388.1s] 💭 (93 chars) "Now let's verify the fix with the reproduction script and also test the single callable
case."
[ 388.1s] 🔧 run_command (73 lines) python3 - <<'PY'
[ 388.2s] → Full Dict - Serialize: True
[ 395.8s] 💭 (453 chars) "All test cases passed.
Now I should check if there are any other places where scalars are handled.
I already checked `a..."
[ 395.8s] 🔧 run_command grep -rn "scalar" tests/ | head -n 20
[ 395.8s] → tests/test_queries.py:9:def test_query_default_scalar():
[ 397.2s] 💭 (36 chars) "Let's check `tests/test_queries.py`."
[ 397.2s] 📖 read_file (tests/test_queries.py)
[ 397.3s] ✓ {'status': 'ok', 'filepath': 'tests/test_queries.py', 'content': 'from datetime import date\nfrom
unittest.mock import M…
[ 413.6s] 💭 (1122 chars) "`test_query_custom_scalar` uses a single callable for the scalar resolver, which is the
case I already tested.
I've cov..."
[ 413.6s] 🔧 run_command pytest tests/test_queries.py -q --tb=short --no-header
[ 413.8s] → .......... [100%]
Native result row
{
"agent_error": "Agent exceeded session timeout (7.5 min)",
"duration_seconds": 453.35,
"error": "Agent exceeded session timeout (7.5 min)",
"grade": {
"collected_tests": 16,
"competition_junit_ok": true,
"error": null,
"required_issues_count": 0,
"resolved": true,
"status_counts": {
"PASSED": 16
},
"strict_clean_patch": true,
"test_exit_code": 0
},
"instance_id": "mirumee__ariadne-24",
"pass_idx": 0,
"patch_chars": 910,
"phase1_seconds": 452.67,
"repo": "mirumee/ariadne",
"request_log_path": "/kaggle-root/working/r31/rollouts_X/requests/mirumee__ariadne-24__p0__r0.jsonl",
"resolved": true,
"rollout_idx": 0,
"run_key": "mirumee__ariadne-24__p0__r0",
"strict_clean_patch": true,
"tool_calls": 15,
"total_llm_calls": 15,
"trace_path": "/kaggle-root/working/r31/rollouts_X/traces/trace_mirumee__ariadne-24__p0__r0.json"
}