gapic-generator-python icon indicating copy to clipboard operation
gapic-generator-python copied to clipboard

samplegen maximum recursion error when recursive message in oneof

Open dizcology opened this issue 1 year ago • 1 comments

To reproduce with version 1.11.9:

  1. Add the following to tests/fragments:
# tests/fragments/test_recursive_messages_oneof.proto

syntax = "proto3";

package google.fragment;

import "google/api/client.proto";

service MyService {
  option (google.api.default_host) = "my.example.com";

  rpc MyMethod(MethodRequest) returns (MethodResponse) {}
}

message MethodRequest {
  oneof request {
    MethodRequest child = 1;
    string data = 2;
  }
}

message MethodResponse {}
  1. Run nox -s fragment-3.7 -- tests/fragments/test_recursive_messages_oneof.proto.
  2. The test fails with RecursionError: maximum recursion depth exceeded while calling a Python object. The recursion happens at this line: https://github.com/googleapis/gapic-generator-python/blob/v1.11.9/gapic/samplegen/samplegen.py#L992, around which the previous maintainer of samplegen.py included a warning and a possible solution.

The error only occurs when there is a oneof whose first field is recursive. Variants of the fragment proto that do not result in the same error:

  1. If there is no oneof.
  2. If there is no recursion.
  3. If there are mutual/multiple recursions where at least one recursion is not the first field in a oneof.
  4. If the recursion does not occur in the first field belonging to the oneof. Here "first" refers to the proto file's represenation, and not the field ID number.

dizcology avatar Oct 19 '23 18:10 dizcology

Another way the same issue occurs is when a recursive message is marked REQUIRED:

# tests/fragments/test_recursive_messages_required.proto

syntax = "proto3";

package google.fragment;

import "google/api/client.proto";
import "google/api/field_behavior.proto";

service MyService {
  option (google.api.default_host) = "my.example.com";

  rpc MyMethod(MethodRequest) returns (MethodResponse) {}
}

message MethodRequest {
  MethodRequest child = 1 [(google.api.field_behavior) = REQUIRED];
}

message MethodResponse {}

To be sure, this proto fragment is unusable since a MethodRequest message (that conforms to the field_behavior) can never be constructed.

In both cases (oneof and REQUIRED), the issue is that the same message type could recursively appear in request_fields (https://github.com/googleapis/gapic-generator-python/blob/v1.11.11/gapic/samplegen/samplegen.py#L965). The fix should be to exclude message types that have already appeared when populating the sample request message used in the generated snippets.

dizcology avatar Nov 08 '23 16:11 dizcology