Fix executor return-type detection under from __future__ import annotations (closes #3061)#3062
Open
ayush268 wants to merge 4 commits into
Open
Fix executor return-type detection under from __future__ import annotations (closes #3061)#3062ayush268 wants to merge 4 commits into
from __future__ import annotations (closes #3061)#3062ayush268 wants to merge 4 commits into
Conversation
from __future__ import annotations (closes #3061from __future__ import annotations (closes #3061)
natestemen
reviewed
Jun 20, 2026
| # an unresolvable forward reference). | ||
| try: | ||
| executor_annotation = typing.get_type_hints(executor) | ||
| except Exception: |
Member
There was a problem hiding this comment.
What exception gets raised by line 84?
| (e.g. ``"float"`` -> ``float``) so that float / measurement / | ||
| density-matrix executors are detected correctly. | ||
|
|
||
| See https://github.com/unitaryfoundation/mitiq for the originating issue. |
Member
There was a problem hiding this comment.
Add the issue # or remove this line.
`Executor` read the executor's return type via `inspect.getfullargspec(executor).annotations`, which returns the raw `__annotations__` dict. When the executor is defined in a module using `from __future__ import annotations`, annotations are strings, so the return type became the string `"float"` instead of the `float` type. The membership checks in `evaluate()` then matched nothing and raised `ValueError: Could not parse executed results from executor with type float`, even though the executor returned a valid float. This also affected measurement/density-matrix executors and any technique that builds an `Executor` internally (PEC, ZNE, ...). Resolve the annotation with `typing.get_type_hints()`, which evaluates stringized (PEP 563) annotations back into concrete types, with a fallback to the raw annotations when a hint cannot be resolved. Add regression tests in a module that uses `from __future__ import annotations`.
851de8f to
3e38cbd
Compare
Author
|
The exception can be a Example code to reproduce: from __future__ import annotations
from typing import TYPE_CHECKING, get_type_hints
# ---- Case 1: NameError ----
# A name imported ONLY for type-checkers does not exist at runtime, so
# get_type_hints() cannot evaluate the (stringized) annotation.
if TYPE_CHECKING:
from some_heavy_pkg import CircuitLike # never imported at runtime
def executor_typecheck_only(circuit: CircuitLike) -> float:
return 1.0
# ---- Case 2: TypeError ----
# get_type_hints() expects a module/class/method/function. A plain callable
# *object* (an instance with __call__) is not a valid argument.
class CallableExecutor:
def __call__(self, circuit) -> float:
return 1.0
callable_obj = CallableExecutor()
get_type_hints(executor_typecheck_only) # raises NameError
get_type_hints(callable_obj) # raises TypeError |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #3061
Executorread the executor's return type viainspect.getfullargspec(executor).annotations, which returns the raw__annotations__dict. When the executor is defined in a module usingfrom __future__ import annotations, annotations are strings, so the return type became the string"float"instead of thefloattype. The membership checks inevaluate()then matched nothing and raisedValueError: Could not parse executed results from executor with type float, even though the executor returned a valid float. This also affected measurement/density-matrix executors and any technique that builds anExecutorinternally (PEC, ZNE, ...).Resolve the annotation with
typing.get_type_hints(), which evaluates stringized (PEP 563) annotations back into concrete types, with a fallback to the raw annotations when a hint cannot be resolved.Add regression tests in a module that uses
from __future__ import annotations.Description
Executor.__init__reads the executor's return type viainspect.getfullargspec(executor).annotations, which returns the raw__annotations__dict. When the executor is defined in a module usingfrom __future__ import annotations(PEP 563), all annotations are strings, so_executor_return_typebecomes the string"float"instead of thefloattype. The membership checks inevaluate()(... in FloatLike, etc.) compare against concrete type objects, match nothing, and the method raises:…even though the executor returns a valid float. This also affects measurement/density-matrix executors and any technique that builds an
Executorinternally (PEC, ZNE, …).Reproducer
Fix
Resolve the annotation with
typing.get_type_hints(), which evaluates stringized annotations back into concrete types using the executor's__globals__, with a fallback to the raw annotations if a hint can't be resolved (e.g. an unresolvable forward reference).Tests
Adds
mitiq/executor/tests/test_executor_pep563.pywith regression tests in a module that usesfrom __future__ import annotations. They fail onmainand pass with this change.License
Before opening the PR, please ensure you have completed the following where appropriate.