Skip to content

fix(python): import the resource that an endpoint-less client builds - #320

Closed
moetemp wants to merge 17 commits into
moe/up/python-caller-docstring-indentfrom
moe/up/python-endpointless-resource-import
Closed

moetemp wants to merge 17 commits into
moe/up/python-caller-docstring-indentfrom
moe/up/python-endpointless-resource-import

Conversation

@moetemp

@moetemp moetemp commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

What changed?

This PR imports the resource class that a Python <Service>Client builds from an answer. A service with no @nexus.endpoint gets that class. services.py imports the resource class at run time, and one that only annotations name stays under TYPE_CHECKING.

The first commit generates Brook's writer for Python with today's output, and the second is the fix.

The writer's caller doesn't import the types of its parameters yet. So ruff ignores its undefined names, and basedpyright skips its services.py. #324 adds the imports and removes both exceptions.

It replaces moetemp#75, without the client target.
It's based on #319.

Why?

The method calls the class to build the resource, but services.py imported it only under TYPE_CHECKING, so the call raised NameError. An operation that returns the resource type itself, as in user-service, already imported it. Only a holder answer, such as the writer's writer-result, showed the bug.

How did you test it?

Link to a test plan if any -

  • Unit Tests
  • Staging
  • End to End Tests

cargo validate passes, and the new writer test opens and closes Brook's writer from a Workflow through a real Nexus endpoint.

Brook's writer uses records of the stream service across packages. The match takes the package
and the interface, since several samples share `temporal:nexus`.
The samples now generate what Brook generates, byte for byte from its WIT. No SDK has the Stream
protos yet, so the sample projects ship their bindings. Today only part of the output builds, and
each fix turns its part on.
This is today's output. Python and TypeScript panic on the record cases, so their outputs keep
their previous content. .NET types each case as the raw proto message, not as its record or enum.
No Temporal API message has a oneof with an enum case, so the sample gets its own proto.
Variant planning kept such a case as the raw proto type. Python and TypeScript panicked on a
record case, and named an enum case's type without defining it. .NET typed the case as the proto
class or enum. The case now plans as the record or enum, as a record field already does. Brook's
stream service now generates for TypeScript, and its .NET read item holds the records.
A proto in temporal/... always became a temporalio module, so a proto that the SDK doesn't have
yet couldn't be imported. --python-proto-module, and python_proto_modules in examples.toml, map a
file path prefix to the module that replaces it. A prefix matches whole path segments, and the
module must be a dotted Python name. Brook's stream service now generates for Python, with the
Stream protos from the sample's own package.
A proto `uint64` field was planned as a signed `s64`, so .NET typed it as `long` against the
`ulong` of its proto class and the model didn't compile. The `writer-id` of Brook's writer sample
shows the fix, and the writer now builds for .NET.
A proto-backed request holds a 64-bit integer as a `Long`, but a resource method took a number.
A Brook offset doesn't fit in a number. A resource field, method param or result is now a `Long`
when the method's request is proto-backed, so it goes into the request as it is. The client of a
service with no endpoint also builds a returned resource from a value import, with the
`requiredField` helper. Brook's stream service and writer now type-check.
The setup of a @nexus.flatten-in-api record always took the indent of a module
function. In a method of the client of a service with no endpoint, the code
then left the method body, and services.py didn't parse.
Before planning, a proto-backed record is named by its proto type, so the walk
for a resource's type parameters stopped there. nexgen then wrote generic models
for a resource class that isn't generic, and the code didn't compile.
…output.

The request's email field has the name of the user's own email, with another type. Today the
user's email fills it.
A resource method filled each request field from the resource field or param of the same
name, whatever its type. So the user's own email went into the notify request's email flag.
A source now binds only when its type is the field's type.
Python's caller method renders a bare `*` with no keyword parameter after it, so its services.py
doesn't parse and the Python output keeps its previous content.
Python refuses a bare `*` with no parameter after it. An unpacked operation with an empty request
rendered one, in the caller of a service with no endpoint and in an operation function. The
user-service caller now generates its `notify_all` method.
Python's caller method takes the docstring indent of a module function, so its services.py
doesn't parse and the Python output keeps its previous content.
The caller of a service with no endpoint took the docstring indent of a module function, so a
documented operation broke the module. The user-service caller now generates with the doc of
`notify_all`.
The caller of a service with no endpoint builds the writer from the `writer-result` answer, but
services.py imports the `Writer` class only under `TYPE_CHECKING`. The caller doesn't import the
types of its params either, so the Python checks skip its services.py.
The caller of a service with no endpoint builds a returned resource from its class, but
services.py imported the class only under `TYPE_CHECKING`. So the call failed with `NameError`. A
test opens and closes Brook's writer from a Workflow through a real endpoint.
This was referenced Oct 28, 2026
@moetemp
moetemp force-pushed the moe/up/python-caller-docstring-indent branch from 451fef4 to 78851a5 Compare October 29, 2026 04:00
@moetemp

moetemp commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

Closing this. The endpoint-less resource import has no case left without the stream writer.

@moetemp moetemp closed this Oct 29, 2026
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