This is an automated email from the ASF dual-hosted git repository.

robertwb pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/beam.git


The following commit(s) were added to refs/heads/master by this push:
     new 9e3aeca6123 Add doc string and signature to generated Python wrappers 
(#32337)
9e3aeca6123 is described below

commit 9e3aeca6123463146f7ec8cd4fec855fb912d25c
Author: Ahmed Abualsaud <[email protected]>
AuthorDate: Tue Aug 27 19:05:44 2024 -0400

    Add doc string and signature to generated Python wrappers (#32337)
---
 .../transforms/external_transform_provider.py      | 50 ++++++++++++++++------
 .../external_transform_provider_it_test.py         | 11 ++---
 sdks/python/gen_xlang_wrappers.py                  |  3 +-
 3 files changed, 45 insertions(+), 19 deletions(-)

diff --git a/sdks/python/apache_beam/transforms/external_transform_provider.py 
b/sdks/python/apache_beam/transforms/external_transform_provider.py
index 67adda5aec0..117c7f7c9b9 100644
--- a/sdks/python/apache_beam/transforms/external_transform_provider.py
+++ b/sdks/python/apache_beam/transforms/external_transform_provider.py
@@ -18,6 +18,8 @@
 import logging
 import re
 from collections import namedtuple
+from inspect import Parameter
+from inspect import Signature
 from typing import Dict
 from typing import List
 from typing import Tuple
@@ -58,6 +60,22 @@ def get_config_with_descriptions(
   return fields_with_descriptions
 
 
+def _generate_signature(schematransform: SchemaTransformsConfig) -> Signature:
+  schema = named_tuple_to_schema(schematransform.configuration_schema)
+  descriptions = schematransform.configuration_schema._field_descriptions
+  params: List[Parameter] = []
+  for field in schema.fields:
+    annotation = str(typing_from_runner_api(field.type))
+    description = descriptions[field.name]
+    if description:
+      annotation = annotation + f": {description}"
+    params.append(
+        Parameter(
+            field.name, Parameter.POSITIONAL_OR_KEYWORD, 
annotation=annotation))
+
+  return Signature(params)
+
+
 class ExternalTransform(PTransform):
   """Template for a wrapper class of an external SchemaTransform
 
@@ -69,7 +87,6 @@ class ExternalTransform(PTransform):
   # These attributes need to be set when
   # creating an ExternalTransform type
   default_expansion_service = None
-  description: str = ""
   identifier: str = ""
   configuration_schema: Dict[str, ParamInfo] = {}
 
@@ -138,18 +155,20 @@ class ExternalTransformProvider:
   ...       'beam:schematransform:org.apache.beam:bigquery_storage_read:v1')
   >>> provider.BigqueryStorageRead
 
-  To know more about the usage of a given transform, take a look at the
-  `description` attribute. This returns some documentation IF the underlying
-  SchemaTransform provides any.
-  >>> provider.BigqueryStorageRead.description
+  You can inspect the transform's documentation to know more about it. This
+  returns some documentation only IF the underlying SchemaTransform
+  implementation provides any.
+  >>> import inspect
+  >>> inspect.getdoc(provider.BigqueryStorageRead)
 
-  Similarly, the `configuration_schema` attribute returns information about the
+  Similarly, you can inspect the transform's signature to know more about its
   parameters, including their names, types, and any documentation that the
   underlying SchemaTransform may provide:
-  >>> provider.BigqueryStorageRead.configuration_schema
-  {'query': ParamInfo(type=typing.Optional[str], description='The SQL query to
-  be executed to read from the BigQuery table.', original_name='query'),
-  'row_restriction': ParamInfo(type=typing.Optional[str]...}
+  >>> inspect.signature(provider.BigqueryStorageRead)
+  (query: 'typing.Union[str, NoneType]: The SQL query to be executed to...',
+  row_restriction: 'typing.Union[str, NoneType]: Read only rows that match...',
+  selected_fields: 'typing.Union[typing.Sequence[str], NoneType]: Read ...',
+  table_spec: 'typing.Union[str, NoneType]: The fully-qualified name of ...')
 
   The retrieved external transform can be used as a normal PTransform like so::
 
@@ -213,14 +232,19 @@ class ExternalTransformProvider:
             skipped_urns.append(identifier)
             continue
 
-          self._transforms[identifier] = type(
-              name, (ExternalTransform, ),
+          transform = type(
+              name,
+              (ExternalTransform, ),
               dict(
                   identifier=identifier,
                   default_expansion_service=service,
                   schematransform=config,
-                  description=config.description,
+                  # configuration_schema is used by the auto-wrapper generator
                   configuration_schema=get_config_with_descriptions(config)))
+          transform.__doc__ = config.description
+          transform.__signature__ = _generate_signature(config)
+
+          self._transforms[identifier] = transform
           self._name_to_urn[name] = identifier
 
       if skipped_urns:
diff --git 
a/sdks/python/apache_beam/transforms/external_transform_provider_it_test.py 
b/sdks/python/apache_beam/transforms/external_transform_provider_it_test.py
index 95720cee7ee..df30611598f 100644
--- a/sdks/python/apache_beam/transforms/external_transform_provider_it_test.py
+++ b/sdks/python/apache_beam/transforms/external_transform_provider_it_test.py
@@ -15,6 +15,7 @@
 # limitations under the License.
 #
 import importlib
+import inspect
 import logging
 import os
 import secrets
@@ -92,7 +93,7 @@ class NameAndTypeUtilsTest(unittest.TestCase):
     "EXPANSION_JARS environment var is not provided, "
     "indicating that jars have not been built")
 class ExternalTransformProviderIT(unittest.TestCase):
-  def test_generate_sequence_config_schema_and_description(self):
+  def test_generate_sequence_signature_and_doc(self):
     provider = ExternalTransformProvider(
         BeamJarExpansionService(":sdks:java:io:expansion-service:shadowJar"))
 
@@ -102,14 +103,14 @@ class ExternalTransformProviderIT(unittest.TestCase):
     ) in provider.get_available())
 
     GenerateSequence = provider.get('GenerateSequence')
-    config_schema = GenerateSequence.configuration_schema
+    signature = inspect.signature(GenerateSequence)
     for param in ['start', 'end', 'rate']:
-      self.assertTrue(param in config_schema)
+      self.assertTrue(param in signature.parameters.keys())
 
-    description_substring = (
+    doc_substring = (
         "Outputs a PCollection of Beam Rows, each "
         "containing a single INT64")
-    self.assertTrue(description_substring in GenerateSequence.description)
+    self.assertTrue(doc_substring in inspect.getdoc(GenerateSequence))
 
   def test_run_generate_sequence(self):
     provider = ExternalTransformProvider(
diff --git a/sdks/python/gen_xlang_wrappers.py 
b/sdks/python/gen_xlang_wrappers.py
index ea4f496c2d0..22adf0cd354 100644
--- a/sdks/python/gen_xlang_wrappers.py
+++ b/sdks/python/gen_xlang_wrappers.py
@@ -22,6 +22,7 @@ SchemaTransforms)
 
 import argparse
 import datetime
+import inspect
 import logging
 import os
 import shutil
@@ -168,7 +169,7 @@ def generate_transforms_config(input_services, output_file):
           'destinations': transform_destinations,
           'default_service': target,
           'fields': fields,
-          'description': wrapper.description
+          'description': inspect.getdoc(wrapper)
       }
       transform_list.append(transform)
 

Reply via email to