Repository: googleapis/gapic-generator-python

Issue:
Naming conflict with generated client for google/cloud/dataplex/v1
There is a naming conflict with `metadata` the generated client for [dataplex v1](https://github.com/googleapis/googleapis-gen/tree/master/google/cloud/dataplex/v1/dataplex-v1-py) and as a result all of the `metadata` unit tests fail.

`metadata` exists [here](https://github.com/googleapis/googleapis-gen/blob/master/google/cloud/dataplex/v1/dataplex-v1-py/google/cloud/dataplex_v1/types/metadata.py) in the generated client for dataplex and also exists [here](https://github.com/googleapis/gapic-generator-python/blob/main/gapic/schema/metadata.py#L324) in the gapic generator.

Steps to reproduce:
1) Clone googleapis-gen
2) `cd` to google/cloud/dataplex/v1/dataplex-v1-py
3) run `nox -s unit-3.8`

See stack trace for 1 test failure:
```
__________________________________________________________________________________________________________________ test_list_partitions_async_pages __________________________________________________________________________________________________________________

    @pytest.mark.asyncio
    async def test_list_partitions_async_pages():
        client = MetadataServiceAsyncClient(
            credentials=ga_credentials.AnonymousCredentials,
        )
    
        # Mock the actual call within the gRPC stub, and fake the request.
        with mock.patch.object(
                type(client.transport.list_partitions),
                '__call__', new_callable=mock.AsyncMock) as call:
            # Set the response to a series of pages.
            call.side_effect = (
                metadata.ListPartitionsResponse(
                    partitions=[
                        metadata.Partition(),
                        metadata.Partition(),
                        metadata.Partition(),
                    ],
                    next_page_token='abc',
                ),
                metadata.ListPartitionsResponse(
                    partitions=[],
                    next_page_token='def',
                ),
                metadata.ListPartitionsResponse(
                    partitions=[
                        metadata.Partition(),
                    ],
                    next_page_token='ghi',
                ),
                metadata.ListPartitionsResponse(
                    partitions=[
                        metadata.Partition(),
                        metadata.Partition(),
                    ],
                ),
                RuntimeError,
            )
            pages = []
>           async for page_ in (await client.list_partitions(request={})).pages:

tests/unit/gapic/dataplex_v1/test_metadata_service.py:1792: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <google.cloud.dataplex_v1.services.metadata_service.async_client.MetadataServiceAsyncClient object at 0x7f8c64eab1f0>, request = {}

    async def list_partitions(self,
            request: Union[metadata.ListPartitionsRequest, dict] = None,
            *,
            parent: str = None,
            retry: OptionalRetry = gapic_v1.method.DEFAULT,
            timeout: float = None,
            metadata: Sequence[Tuple[str, str]] = (),
            ) -> pagers.ListPartitionsAsyncPager:
        r"""List metadata partitions of an entity.
    
        Args:
            request (Union[google.cloud.dataplex_v1.types.ListPartitionsRequest, dict]):
                The request object. List metadata partitions request.
            parent (:class:`str`):
                Required. The resource name of the parent entity:
                ``projects/{project_number}/locations/{location_id}/lakes/{lake_id}/zones/{zone_id}/entities/{entity_id}``.
    
                This corresponds to the ``parent`` field
                on the ``request`` instance; if ``request`` is provided, this
                should not be set.
            retry (google.api_core.retry.Retry): Designation of what errors, if any,
                should be retried.
            timeout (float): The timeout for this request.
            metadata (Sequence[Tuple[str, str]]): Strings which should be
                sent along with the request as metadata.
    
        Returns:
            google.cloud.dataplex_v1.services.metadata_service.pagers.ListPartitionsAsyncPager:
                List metadata partitions response.
                Iterating over this object will yield
                results and resolve additional pages
                automatically.
    
        """
        # Create or coerce a protobuf request object.
        # Sanity check: If we got a request object, we should *not* have
        # gotten any keyword arguments that map to the request.
        has_flattened_params = any([parent])
        if request is not None and has_flattened_params:
            raise ValueError("If the `request` argument is set, then none of "
                             "the individual field arguments should be set.")
    
>       request = metadata.ListPartitionsRequest(request)
E       AttributeError: 'tuple' object has no attribute 'ListPartitionsRequest'

google/cloud/dataplex_v1/services/metadata_service/async_client.py:486: AttributeError
```
See output below:
```
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_get_entity[GetEntityRequest] - AttributeError: 'tuple' object has no attribute 'GetEntityRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_get_entity[dict] - AttributeError: 'tuple' object has no attribute 'GetEntityRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_get_entity_empty_call - AttributeError: 'tuple' object has no attribute 'GetEntityRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_get_entity_async - AttributeError: 'tuple' object has no attribute 'GetEntityRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_get_entity_async_from_dict - AttributeError: 'tuple' object has no attribute 'GetEntityRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_get_entity_field_headers - AttributeError: 'tuple' object has no attribute 'GetEntityRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_get_entity_field_headers_async - AttributeError: 'tuple' object has no attribute 'GetEntityRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_get_entity_flattened - AttributeError: 'tuple' object has no attribute 'GetEntityRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_get_entity_flattened_async - AttributeError: 'tuple' object has no attribute 'GetEntityRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_list_entities[ListEntitiesRequest] - AttributeError: 'tuple' object has no attribute 'ListEntitiesRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_list_entities[dict] - AttributeError: 'tuple' object has no attribute 'ListEntitiesRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_list_entities_empty_call - AttributeError: 'tuple' object has no attribute 'ListEntitiesRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_list_entities_async - AttributeError: 'tuple' object has no attribute 'ListEntitiesRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_list_entities_async_from_dict - AttributeError: 'tuple' object has no attribute 'ListEntitiesRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_list_entities_field_headers - AttributeError: 'tuple' object has no attribute 'ListEntitiesRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_list_entities_field_headers_async - AttributeError: 'tuple' object has no attribute 'ListEntitiesRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_list_entities_flattened - AttributeError: 'tuple' object has no attribute 'ListEntitiesRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_list_entities_flattened_async - AttributeError: 'tuple' object has no attribute 'ListEntitiesRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_list_entities_pager - UnboundLocalError: local variable 'metadata' referenced before assignment
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_list_entities_pages - AttributeError: 'tuple' object has no attribute 'ListEntitiesRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_list_entities_async_pager - AttributeError: 'tuple' object has no attribute 'ListEntitiesRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_list_entities_async_pages - AttributeError: 'tuple' object has no attribute 'ListEntitiesRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_get_partition[GetPartitionRequest] - AttributeError: 'tuple' object has no attribute 'GetPartitionRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_get_partition[dict] - AttributeError: 'tuple' object has no attribute 'GetPartitionRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_get_partition_empty_call - AttributeError: 'tuple' object has no attribute 'GetPartitionRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_get_partition_async - AttributeError: 'tuple' object has no attribute 'GetPartitionRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_get_partition_async_from_dict - AttributeError: 'tuple' object has no attribute 'GetPartitionRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_get_partition_field_headers - AttributeError: 'tuple' object has no attribute 'GetPartitionRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_get_partition_field_headers_async - AttributeError: 'tuple' object has no attribute 'GetPartitionRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_get_partition_flattened - AttributeError: 'tuple' object has no attribute 'GetPartitionRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_get_partition_flattened_async - AttributeError: 'tuple' object has no attribute 'GetPartitionRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_list_partitions[ListPartitionsRequest] - AttributeError: 'tuple' object has no attribute 'ListPartitionsRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_list_partitions[dict] - AttributeError: 'tuple' object has no attribute 'ListPartitionsRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_list_partitions_empty_call - AttributeError: 'tuple' object has no attribute 'ListPartitionsRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_list_partitions_async - AttributeError: 'tuple' object has no attribute 'ListPartitionsRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_list_partitions_async_from_dict - AttributeError: 'tuple' object has no attribute 'ListPartitionsRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_list_partitions_field_headers - AttributeError: 'tuple' object has no attribute 'ListPartitionsRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_list_partitions_field_headers_async - AttributeError: 'tuple' object has no attribute 'ListPartitionsRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_list_partitions_flattened - AttributeError: 'tuple' object has no attribute 'ListPartitionsRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_list_partitions_flattened_async - AttributeError: 'tuple' object has no attribute 'ListPartitionsRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_list_partitions_pager - UnboundLocalError: local variable 'metadata' referenced before assignment
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_list_partitions_pages - AttributeError: 'tuple' object has no attribute 'ListPartitionsRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_list_partitions_async_pager - AttributeError: 'tuple' object has no attribute 'ListPartitionsRequest'
FAILED tests/unit/gapic/dataplex_v1/test_metadata_service.py::test_list_partitions_async_pages - AttributeError: 'tuple' object has no attribute 'ListPartitionsRequest'
44 failed, 470 passed, 1 warning in 11.87s
```

Discussion:

Patch:
diff --git a/gapic/schema/api.py b/gapic/schema/api.py
index 11cb77f2..c6b69cf3 100644
--- a/gapic/schema/api.py
+++ b/gapic/schema/api.py
@@ -262,12 +262,16 @@ class API:
             file_descriptors,
         ), opts=opts)
 
+        # "metadata", "retry", "timeout", and "request" are reserved words in client methods.
+        invalid_module_names = set(keyword.kwlist) | {
+            "metadata", "retry", "timeout", "request"}
+
         def disambiguate_keyword_fname(
                 full_path: str,
                 visited_names: Container[str]) -> str:
             path, fname = os.path.split(full_path)
             name, ext = os.path.splitext(fname)
-            if name in keyword.kwlist or full_path in visited_names:
+            if name in invalid_module_names or full_path in visited_names:
                 name += "_"
                 full_path = os.path.join(path, name + ext)
                 if full_path in visited_names:


Test patch:
diff --git a/tests/unit/schema/test_api.py b/tests/unit/schema/test_api.py
index eae44b84..212c86da 100644
--- a/tests/unit/schema/test_api.py
+++ b/tests/unit/schema/test_api.py
@@ -233,7 +233,27 @@ def test_proto_keyword_fname():
             name='class.proto',
             package='google.keywords.v1',
             messages=(make_message_pb2(name='ClassRequest', fields=()),),
-        )
+        ),
+        make_file_pb2(
+            name='metadata.proto',
+            package='google.keywords.v1',
+            messages=(make_message_pb2(name='MetadataRequest', fields=()),),
+        ),
+        make_file_pb2(
+            name='retry.proto',
+            package='google.keywords.v1',
+            messages=(make_message_pb2(name='RetryRequest', fields=()),),
+        ),
+        make_file_pb2(
+            name='timeout.proto',
+            package='google.keywords.v1',
+            messages=(make_message_pb2(name='TimeoutRequest', fields=()),),
+        ),
+        make_file_pb2(
+            name='request.proto',
+            package='google.keywords.v1',
+            messages=(make_message_pb2(name='RequestRequest', fields=()),),
+        ),
     )
 
     # We can't create new collisions, so check that renames cascade.
@@ -243,6 +263,10 @@ def test_proto_keyword_fname():
         'import__.proto',
         'class_.proto',
         'class__.proto',
+        'metadata_.proto',
+        'retry_.proto',
+        'timeout_.proto',
+        'request_.proto',
     }