Repository navigation
feat(python): add @nexus.poll - #325
Merged
Merged
Conversation
This was referenced Oct 28, 2026
1 of 3 tasks
tconley1428
force-pushed
the
refactor/api-generation-targets
branch
from
November 2, 2026 17:06
cafb166 to
70ac708
Compare
moetemp
force-pushed
the
moe/up/python-poll
branch
from
November 2, 2026 19:49
0bf4004 to
8fde033
Compare
moetemp
force-pushed
the
moe/up/python-poll
branch
from
November 4, 2026 00:51
8fde033 to
f260389
Compare
tconley1428
reviewed
Nov 4, 2026
|
|
||
| /// Refuses a client poll that can't get an iterator, and an iterator with the name of another | ||
| /// operation, method or field, since one of the two would hide the other. | ||
| fn validate_client_polls(services: &[RenderedService<'_>], source_path: &Path) -> Result<()> { |
Collaborator
There was a problem hiding this comment.
Seems like this should happen a lot earlier, outside of python. That would also mean you don't need to thread source path here.
Contributor
Author
There was a problem hiding this comment.
Agreed. I moved the checks into a new compiler pass, PollValidationPass, in 6a421d5. It runs on the planned spec, right after reachability and before any generator.
tconley1428
reviewed
Nov 4, 2026
| let planned_tree = type_planning.apply(operation_lowered_tree)?; | ||
| let planned_tree = planning::ReachabilityPass::new().apply(planned_tree)?; | ||
| let planned_tree = | ||
| planning::PollValidationPass::new(nexgen_config::current().targets).apply(planned_tree)?; |
Collaborator
There was a problem hiding this comment.
No real reason to pass in the targets, since it can also check the config.
Contributor
Author
There was a problem hiding this comment.
Agreed. The pass reads the targets from the config now, in 07f142b.
tconley1428
approved these changes
Nov 4, 2026
moetemp
commented
Nov 4, 2026
| let planned_tree = type_planning.apply(operation_lowered_tree)?; | ||
| let planned_tree = planning::ReachabilityPass::new().apply(planned_tree)?; | ||
| let planned_tree = | ||
| planning::PollValidationPass::new(nexgen_config::current().targets).apply(planned_tree)?; |
Contributor
Author
There was a problem hiding this comment.
Agreed. The pass reads the targets from the config now, in 07f142b.
The poll helper names the support function that builds the next request. Only the client target gets an iterator, so the parser refuses a poll without `target="client"` and a poll on a resource method.
A WIT stream can't cross a Nexus call, so a read is a request and a response. The iterator reads every page. The extension creates one Nexus client for the iteration, and a handle uses its own client. Co-authored-by: Tim Conley <timothy.conley@temporal.io>
The Stream service sets `next_offset` on an empty page too, so the helper reads it as the Stream reader does. Each answer's next offset sets the later reads, also after `start` or `last`.
A caller that changes a yielded page then can't change the next read. `AsyncGenerator` lets a caller close the iterator with `aclose()` or `contextlib.aclosing`. A handle's iterator also gets the method doc and `Args:`.
The poll directive then reads its targets as the other directives do. A refused poll gives the service, the operation and the input path.
Also rewrite poll comments that read two ways or use `name` as a verb.
A generic directive check is planned for syntax and placement. The kept fixtures cover the client-only rule, an operation shape the iterator can't take, and an iterator name clash.
The full stream helper is in the sample, so the guide keeps a short version and links to it.
Co-authored-by: Tim Conley <timothy.conley@temporal.io>
moetemp
force-pushed
the
moe/up/python-poll
branch
from
November 4, 2026 01:47
07f142b to
10f9a37
Compare
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.
What changed?
This PR adds
@nexus.pollto the Python client target. An operation with@nexus.poll python="<helper>" targets="client"gets aniter_<name>async iterator. It yields each response and asks the support package's<helper>(request, response)for the next request, which returnsNoneto stop.iter_<name>, and so does the client extension for an operation that no resource method binds. The stream sample getsStream.iter_read. Its helper ends a read that doesn't wait at the newest Record, and starts each later read at the last page'snext_offset.targets, withtargetsthat includeworkflow, on a resource method, and on an operation it can't poll. The Workflow package doesn't change.Why?
A WIT stream can't cross a Nexus call, so the Stream read is a request and a response. The iterator reads every page, and the support package decides the next request.
Not in this PR: an iterator that yields one Record at a time and gives the offset to resume from. It can be a generated transform on top of this page iterator.
How did you test it?
Link to a test plan if any -
test_sdk_client.pyreads every page throughStream.iter_read, ends a read that doesn't wait at the newest Record, and starts the read afterstart=ENDat the first page's offset. Rejection fixtures cover the client-only rule, an operation that returns a resource, and an iterator name clash.