Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Bug Fixes

- `DisciplineServer` inherited `DisciplineService`, the generated experimental
static-call API, rather than `DisciplineServiceServicer`, the servicer base
that `add_DisciplineServiceServicer_to_server` is written against. No
shipping code path reached the difference, because `DisciplineServer`
overrides all eight RPCs itself, but the class did not inherit the
`UNIMPLEMENTED` defaults a servicer base provides. An RPC left undefined --
for instance one added to the `.proto` and not yet implemented -- resolved
instead to a static client stub, which registers without complaint and then
fails at call time by treating the `ServicerContext` as a target address,
raising out of the handler without setting a status, so the caller saw
`UNKNOWN` rather than `UNIMPLEMENTED`. The base is now
`DisciplineServiceServicer`, matching `ExplicitServer` and `ImplicitServer`
(#70).

- Fixed `RemoteExplicitComponent` and `RemoteImplicitComponent` sending the raw
constructor keyword arguments to the server rather than the component's
resolved options. An option that reached the component by any other route --
Expand Down
2 changes: 1 addition & 1 deletion philote_mdo/general/discipline_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
from philote_mdo.utils.validation import PhiloteValidationError, validate_shape


class DisciplineServer(disc.DisciplineService):
class DisciplineServer(disc.DisciplineServiceServicer):
"""
Base class for all server classes.
"""
Expand Down
26 changes: 26 additions & 0 deletions tests/test_discipline_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
from philote_mdo.general import Discipline, DisciplineServer
from philote_mdo.utils.validation import PhiloteValidationError
import philote_mdo.generated.data_pb2 as data
import philote_mdo.generated.disciplines_pb2_grpc as disc


class TestDisciplineServer(unittest.TestCase):
Expand Down Expand Up @@ -610,6 +611,31 @@ def test_setup_general_exception_aborts(self):
self.assertEqual(args[0][0], grpc.StatusCode.INTERNAL)
self.assertIn("Setup failed", args[0][1])

def test_servicer_base_class(self):
"""
Tests that the server derives from the servicer, not the static API.
"""
self.assertTrue(issubclass(DisciplineServer, disc.DisciplineServiceServicer))
self.assertNotIn(disc.DisciplineService, DisciplineServer.__mro__)

def test_missing_rpc_falls_back_to_servicer_default(self):
"""
Tests that an RPC a subclass fails to define resolves to the servicer's
UNIMPLEMENTED default rather than to the static-call API.
"""
skip = ("__dict__", "__weakref__", "GetInfo")
attrs = {k: v for k, v in vars(DisciplineServer).items() if k not in skip}
partial_cls = type("PartialServer", DisciplineServer.__bases__, attrs)

# the inherited GetInfo must come from the servicer, not the static API
self.assertIs(partial_cls.GetInfo, disc.DisciplineServiceServicer.GetInfo)

context = Mock()
with self.assertRaises(NotImplementedError):
partial_cls().GetInfo(Empty(), context)

context.set_code.assert_called_once_with(grpc.StatusCode.UNIMPLEMENTED)


if __name__ == "__main__":
unittest.main(verbosity=2)
Loading