Skip to content

fix: avoid non-string key in Outputs class namespace - #763

Closed
thea-ishida wants to merge 1 commit into
Point72:mainfrom
thea-ishida:fix/outputs-nonstring-key-warning
Closed

thea-ishida wants to merge 1 commit into
Point72:mainfrom
thea-ishida:fix/outputs-nonstring-key-warning

Conversation

@thea-ishida

Copy link
Copy Markdown
Contributor

Fixes #665.

Outputs.__new__ keys a single unnamed output as kwargs[None] and then passes kwargs straight to type("Outputs", (Outputs,), kwargs). Since a non-string key in a class namespace triggers a warning on Python 3.13+:

RuntimeWarning: non-string key in the __dict__ of class Outputs

which fires on plain import csp. The None key is only needed in __annotations__ (that copy is taken earlier, and _extract_outputs_from_return_annotation reads the unnamed output back from there) and by _make_pydantic_outputs, which runs before this point. So it's safe to pop it off kwargs right before the type() call — the annotations dict keeps it, the class namespace doesn't.

Added a regression test (test_outputs_no_non_string_key_warning in csp/tests/test_parsing.py) covering unnamed outputs, named outputs, and a full node/graph run, with RuntimeWarning promoted to an error.

Context on prior attempts

This same root cause and fix were independently found three times (#741, #752, and my own earlier #762 — all now closed). #741 was closed with the request to "try running all tests before posting," and neither of the follow-up attempts, including mine, provided that. #762 could not be reopened via the API/CLI (Could not open the pull request), so I'm opening this fresh PR from the same branch with the verification actually done.

Verification

Built csp from source on macOS (vcpkg + the compiled C++ engine) rather than testing against a pip-installed wheel, then ran the full suite against that build:

$ python -m pytest -v csp/tests --junitxml=junit.xml
1622 passed, 25 skipped, 2 xfailed, 614 warnings, 14 subtests passed in 93.18s

Zero failures, zero errors. This exercises Outputs end-to-end: named/unnamed/basket outputs, dynamic outputs, structs, memoization, and the pydantic-model path.

I also checked whether the C++ engine reads the Outputs class's raw namespace/__dict__ anywhere (which would make popping the None key insufficient) — it doesn't; the C++ side only ever receives already-resolved output definitions built from __annotations__.

Also ran, on the changed files:

  • ruff check
  • ruff format --check

Happy to make any changes needed.

csp.Outputs(...) builds a class via type(name, bases, namespace). For a
single unnamed output, that namespace carried a literal None key (used
internally to mark 'no name'), which becomes a non-string entry in the
resulting class's __dict__. CPython 3.13 warns on this:

  RuntimeWarning: non-string key in the __dict__ of class Outputs

The None entry is only ever read back via __annotations__[None], so it's
safe to drop the top-level entry right before constructing the class.
Added a regression test that fails (via warnings-as-errors) without the
fix, covering both named and unnamed outputs, and a full node/graph run.

Reproduced the warning under Python 3.13.15 and verified the fix
resolves it while preserving existing named/unnamed/pydantic-model
output behavior.

Fixes Point72#665

Signed-off-by: Thea <thea.ishida@gmail.com>
@robambalu

Copy link
Copy Markdown
Collaborator

On deeper review of this this PR does look correct, popping None should suffice
Please remove the test as its not necessary

@robambalu

Copy link
Copy Markdown
Collaborator

was hoping to get this in soon but dont want to wait on a response so will close and open a new one

@robambalu robambalu closed this Sep 24, 2026
@robambalu

Copy link
Copy Markdown
Collaborator

#767

@thea-ishida

Copy link
Copy Markdown
Contributor Author

@robambalu Thanks for merging this, and sorry I missed the test-removal request. Happy to keep contributing.

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.

RuntimeWarning: non-string key in the __dict__ of class Outputs

3 participants