Skip to content

Fix proxy methods for drivers with custom type annotations or non-literal defaults - #162

Merged
marcosfrenkel merged 4 commits into
toolsforexperiments:masterfrom
marcosfrenkel:function_type_hint
Oct 14, 2026
Merged

marcosfrenkel merged 4 commits into
toolsforexperiments:masterfrom
marcosfrenkel:function_type_hint

Conversation

@marcosfrenkel

@marcosfrenkel marcosfrenkel commented Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

Creating a proxy for some drivers crashes in ProxyInstrumentModule._getProxyMethods(). This came up while adding the QDAC2 driver:

def allocate_trace(self, name: str, size: int) -> Trace_Context: ...

_makeProxyMethod rebuilt each method by pasting str(inspect.signature(method)) into a def and running it with exec. That breaks on:

  • custom class annotations, which come out with their full module path (-> cqedtoolbox....QDAC2.Trace_Context): NameError, since the module isn't available inside exec.
  • defaults whose repr isn't Python (<StatusFlag.EAV: 4>, <object object at 0x...>): SyntaxError.

Fix

The fix is client-only (client/proxy.py). The blueprint and wire format don't change, so a new client works with an old server.

  • The generated def is built from parameter names and kinds only. Every parameter defaults to a private _MISSING marker. The / and * markers are kept, so positional-only and keyword-only parameters are still enforced on the client.
  • Only the arguments the caller actually passed are sent, and the server applies its own defaults. Positional arguments are sent by position until the first omitted one, then by keyword, so skipping a middle default (combine(0, c=5)) works.
  • inspect.signature(proxy.method) still prints the server's signature, annotations and defaults included, through a small Signature subclass. The client GUI uses this text as placeholder and tooltip. The docstring now starts with name(signature).
  • Removed the from typing import * / qcodes / collections setup the old exec needed.

Behavior change: every parameter is optional on the client, because the blueprint doesn't say which ones have defaults. Leaving out a required argument now raises an error from the server instead of a client-side TypeError.

Tests

test/pytest/test_method_annotations.py adds 18 tests using two new dummies in testing/dummy_instruments/generic.py:

  • AnnotatedMethodsInstrument uses a custom class as return and parameter annotation.
  • DefaultValuesInstrument has enum, sentinel, positional-only, keyword-only and variadic defaults.

All 18 failed before the fix and pass now. The rest of the suite has no new failures.

Not included

  • ChannelList blueprint support (the "Blueprints for instrument base type of ChannelList ... are currently not supported" warnings). The channels are still reachable as individual submodules. Possible follow-up.
  • Returned custom objects still need their class importable on the client to be rebuilt. That limitation already existed.

🤖 Generated with Claude Code

@marcosfrenkel marcosfrenkel changed the title Function type hint Fix proxy methods for drivers with custom type annotations or non-literal defaults Oct 14, 2026
marcosfrenkel and others added 2 commits October 14, 2026 12:42
…obals, apply ruff format

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@marcosfrenkel
marcosfrenkel merged commit daaa388 into toolsforexperiments:master Oct 14, 2026
7 checks passed
@marcosfrenkel
marcosfrenkel deleted the function_type_hint branch October 14, 2026 21:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant