Skip to content

GetInfo is implemented as a generator but declared unary, so the RPC always fails #67

Description

@chrislupp

Summary

DisciplineServer.GetInfo is written as a generator (yield), but the proto declares GetInfo as a unary RPC. gRPC cannot serialize a generator into a DisciplineProperties, so the call fails for every caller — including Philote-Python's own client.

Reproduction

from concurrent import futures
import grpc, philote_mdo.general as pmdo
from philote_mdo.examples import Paraboloid

server = grpc.server(futures.ThreadPoolExecutor(max_workers=4))
pmdo.ExplicitServer(discipline=Paraboloid()).attach_to_server(server)
server.add_insecure_port("[::]:50079"); server.start()

client = pmdo.ExplicitClient(channel=grpc.insecure_channel("localhost:50079"))
client.get_discipline_info()

Actual:

StatusCode.INTERNAL | Failed to serialize response!

Expected: the discipline's properties, with _is_continuous / _is_differentiable / _provides_gradients populated on the client.

Cause

The proto declares a unary response:

// proto/disciplines.proto:36
rpc GetInfo(google.protobuf.Empty) returns (philote.DisciplineProperties) {}

The servicer yields instead of returning, so gRPC receives a generator object where it expects a message:

# philote_mdo/general/discipline_server.py:67
def GetInfo(self, request, context):
    yield data.DisciplineProperties(     # <-- should be `return`
        continuous=self._discipline._is_continuous,
        ...
    )

The client mirrors the same misunderstanding and indexes the response as if it were a stream:

# philote_mdo/general/discipline_client.py:79
response = self._disc_stub.GetInfo(empty.Empty())
self._is_continuous = response[0].continuous     # <-- should be response.continuous

Both sides need changing together: with the server fixed, response[0] raises TypeError: 'DisciplineProperties' object is not subscriptable.

Why CI doesn't catch this

tests/test_discipline_client.py:67 patches the stub and hands back a list, so the test asserts against the mock's shape rather than the server's:

mock_stub.GetInfo.return_value = [
    data.DisciplineProperties(continuous=True, differentiable=True, provides_gradients=True)
]

Nothing exercises GetInfo against a real server. examples/parabaloid_client.py and examples/quadratic_client.py never call get_discipline_info(), which is why the examples all pass.

Suggested fix

  1. discipline_server.py:67yieldreturn.
  2. discipline_client.py:79-81 — drop the [0] indexing.
  3. Update the mock in tests/test_discipline_client.py:72 to return the message rather than a list, and ideally add an end-to-end check against a real server so the two sides can't drift again.

Related

Also worth noting: DisciplineProperties.name and .version are never populated by the server, so even once this is fixed a client gets empty strings for both.

Found while implementing Philote-Rust against the same proto; a spec-conforming unary client hits this immediately on connect.

Environment: Philote-Python 0.8.0 @ 1f3baf6, proto v0.8.0.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions