Skip to content

RequestRouter doesn't recognise parameters annotated with the CompositeMetadata type when named other than composite_metadata #333

@km-64

Description

@km-64

According to the tutorial docs for rsocket-py, an argument type named composite_metadata or an argument (with any name) annotated with the CompositeMetadata type should be passed an instance of CompositeMetadata, however the actual behaviour of the router is to pass a Payload instance in the case of the latter. The only effective way to acquire the metadata is to name the argument composite_metadata. The annotation is effectively ignored by the router.

All methods decorated by the request router accept two optional arguments:

Any argument named composite_metadata (regardless of type-hint), or type-hinted with CompositeMetadata will receive a CompositeMetadata instance containing parsed composite metadata
Any other argument without a type-hint, or type-hinted with Payload will receive the request's payload
https://rsocket.io/guides/rsocket-py/tutorial/request_routing#routing-request-handler

Behaviour

Name and type (works)

router = RequestRouter()
@router.response("example") 
async def example_response(payload: Payload, composite_metadata: CompositeMetadata) -> Awaitable[Payload]:
    assert isinstance(composite_metadata, CompositeMetadata)  # passes
    return create_response(ensure_bytes("test"))

Name without type (works)

router = RequestRouter()
@router.response("example") 
async def example_response(payload: Payload, composite_metadata) -> Awaitable[Payload]:
    assert isinstance(composite_metadata, CompositeMetadata)  # passes
    return create_response(ensure_bytes("test"))

Arbitrary name with type (should work but doesn't)

router = RequestRouter()
@router.response("example") 
async def example_response(payload: Payload, arbitrary_name: CompositeMetadata) -> Awaitable[Payload]:
    assert isinstance(arbitrary_metadata, CompositeMetadata)  # fails
    print(type(arbitrary_metadata))  # parameter_type.annotation is CompositeMetadata
    return create_response(ensure_bytes("test"))

Cause and solution

This is due to the implementation of _collect_route_arguments:

def _collect_route_arguments(self,
route_info: RouteInfo,
payload: Payload,
composite_metadata: CompositeMetadata):
route_signature = route_info.signature
route_kwargs = {}
for parameter in route_signature.parameters:
parameter_type = route_signature.parameters[parameter]
if 'composite_metadata' == parameter or parameter_type.annotation is CompositeMetadata:
route_kwargs[parameter] = composite_metadata
else:
payload_data = payload
if parameter_type.annotation not in (Payload, parameter_type.empty):
payload_data = self._payload_deserializer(parameter_type.annotation, payload)
route_kwargs[parameter] = payload_data
return route_kwargs

On line 177, the check should access the annotation property of the inspect.Paramter class, but it doesn't do that. So the right hand side of the or operator. Since it checks that the Parameter instance is CompositeMetadata, it will always evaluate to false.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions