fix: registration fence for script-defined tasks under Python 3.14#37
Merged
Conversation
…robust to PEP 649 Registering a bodyless __main__ task whose signature names something only the script defines is supposed to refuse early. The check relied on the def statement eager-evaluating annotations, which two cases defeat: Python 3.14 (PEP 649/749) defers evaluation so the def no longer raises, and a string forward-ref is never evaluated by the def on any version. Force evaluation explicitly with get_type_hints against the generated artifact's own globals, so an undefined name raises NameError (and the teachable refusal) on every supported Python. No change for valid tasks. Adds a string-forward-ref regression test that fails against the old check on every Python. Verified on 3.12 and 3.14. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
Fix: registration fence for script-defined tasks under Python 3.14
ws.tasks.register(fn)for a bodyless task defined in a run-as-script file (__main__) captures the function's definition as its artifact. If the signature annotates with a name only the script defines — a local class, or any non-shepherd import — that name won't travel with the artifact, so registration is supposed to refuse early with a "move it to an importable module" message.That check relied on the
defstatement evaluating its annotations eagerly, which two things defeat:defno longer raises for an unresolved name and the check silently passed — surfacing the failure later, more confusingly, instead of at registration.thing: "LocalThing") is never evaluated by thedefon any Python version, so it slipped through everywhere.The fix
Force annotation evaluation explicitly (
get_type_hintsagainst the generated artifact's own globals, which carry onlyimport shepherd as sp) instead of relying on thedefto do it. An undefined name now raisesNameError— and the teachable refusal — on every supported Python (3.11–3.14). No change for valid tasks: normalMay[GitRepo, ...]/str/Noneannotations resolve cleanly.Tests
Adds a string-forward-ref regression test that fails against the old check on every Python (not just 3.14), so the behavior is guarded on the interpreters CI already runs. Verified: the registration ergonomics suites pass on both 3.12 and 3.14.
🤖 Generated with Claude Code