This is an automated email from the ASF dual-hosted git repository.
xqhu 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 624b2fcd3fd Avoid unreasonably long stage names for @ptransform_fn.
(#35660)
624b2fcd3fd is described below
commit 624b2fcd3fdd249d3a13b6faba6e71521e9fd0c1
Author: Robert Bradshaw <[email protected]>
AuthorDate: Wed Aug 6 08:42:23 2025 -0400
Avoid unreasonably long stage names for @ptransform_fn. (#35660)
* Avoid unreasonably long stage names for @ptransform_fn.
This respects the update compatibility flag and truncates rather than elides
long first arguments to avoid issues with the prior attempt.
* yapf
* Update sdks/python/apache_beam/transforms/ptransform.py
Co-authored-by: Danny McCormick <[email protected]>
* add type hint
* Slightly safer ane more unique.
Includes the tail as well as the prefix (useful for file paths) and ensures
the result is a string (mock objects behave strangely).
* Bump compat version.
* Add a pair of tests.
---------
Co-authored-by: Danny McCormick <[email protected]>
---
sdks/python/apache_beam/pipeline.py | 10 +++++--
sdks/python/apache_beam/transforms/ptransform.py | 28 +++++++++++++++---
.../apache_beam/transforms/ptransform_test.py | 33 ++++++++++++++++++++++
3 files changed, 64 insertions(+), 7 deletions(-)
diff --git a/sdks/python/apache_beam/pipeline.py
b/sdks/python/apache_beam/pipeline.py
index 269b4acdc21..83a0bee8145 100644
--- a/sdks/python/apache_beam/pipeline.py
+++ b/sdks/python/apache_beam/pipeline.py
@@ -115,11 +115,11 @@ __all__ = ['Pipeline', 'transform_annotations']
class Pipeline(HasDisplayData):
- """A pipeline object that manages a DAG of
- :class:`~apache_beam.transforms.ptransform.PTransform` s
+ """A pipeline object that manages a DAG of
+ :class:`~apache_beam.transforms.ptransform.PTransform` s
and their :class:`~apache_beam.pvalue.PValue` s.
- Conceptually the :class:`~apache_beam.transforms.ptransform.PTransform` s
are
+ Conceptually the :class:`~apache_beam.transforms.ptransform.PTransform` s are
the DAG's nodes and the :class:`~apache_beam.pvalue.PValue` s are the edges.
All the transforms applied to the pipeline must have distinct full labels.
@@ -722,6 +722,10 @@ class Pipeline(HasDisplayData):
return self.apply(
transform.transform, pvalueish, label or transform.label)
+ if not label and isinstance(transform, ptransform._PTransformFnPTransform):
+ # This must be set before label is inspected.
+ transform.set_options(self._options)
+
if not isinstance(transform, ptransform.PTransform):
raise TypeError("Expected a PTransform object, got %s" % transform)
diff --git a/sdks/python/apache_beam/transforms/ptransform.py
b/sdks/python/apache_beam/transforms/ptransform.py
index c4f0e3455d4..d2cf836713f 100644
--- a/sdks/python/apache_beam/transforms/ptransform.py
+++ b/sdks/python/apache_beam/transforms/ptransform.py
@@ -1002,6 +1002,7 @@ class _PTransformFnPTransform(PTransform):
self._fn = fn
self._args = args
self._kwargs = kwargs
+ self._use_backwards_compatible_label = True
def display_data(self):
res = {
@@ -1030,11 +1031,30 @@ class _PTransformFnPTransform(PTransform):
pass
return self._fn(pcoll, *args, **kwargs)
- def default_label(self):
+ def set_options(self, options):
+ # Avoid circular import.
+ from apache_beam.transforms.util import is_compat_version_prior_to
+ self._use_backwards_compatible_label = is_compat_version_prior_to(
+ options, '2.68.0')
+
+ def default_label(self) -> str:
+ # Attempt to give a reasonable name to this transform.
+ # We want it to be reasonably unique, but also not sensitive to
+ # irrelevent parameters to minimize pipeline-to-pipeline variance.
+ # For now, use only the first argument (if any), iff it would not make
+ # the name unwieldy.
if self._args:
- return '%s(%s)' % (
- label_from_callable(self._fn), label_from_callable(self._args[0]))
- return label_from_callable(self._fn)
+ first_arg_string = label_from_callable(self._args[0])
+ if (self._use_backwards_compatible_label or
+ not isinstance(first_arg_string, str) or len(first_arg_string) <=
19):
+ suffix = '(%s)' % first_arg_string
+ else:
+ suffix = ('(%s...%s)' %
+ (first_arg_string[:10], first_arg_string[-6:])).replace(
+ '\n', ' ')
+ else:
+ suffix = ''
+ return label_from_callable(self._fn) + suffix
def ptransform_fn(fn):
diff --git a/sdks/python/apache_beam/transforms/ptransform_test.py
b/sdks/python/apache_beam/transforms/ptransform_test.py
index 78d5c3ef38b..e1c84c7dc9a 100644
--- a/sdks/python/apache_beam/transforms/ptransform_test.py
+++ b/sdks/python/apache_beam/transforms/ptransform_test.py
@@ -1157,6 +1157,39 @@ class PTransformLabelsTest(unittest.TestCase):
self.assertTrue('*Sample*/Group' in pipeline.applied_labels)
self.assertTrue('*Sample*/Distinct' in pipeline.applied_labels)
+ def test_ptransformfn_default_label(self):
+ @beam.ptransform_fn
+ def MyTransform(self, suffix="xyz"):
+ return pcoll | beam.Map(lambda s: s + suffix)
+
+ pipeline = TestPipeline()
+ pcoll = pipeline | beam.Create(['a', 'b', 'c'])
+
+ _ = pcoll | MyTransform()
+ self.assertIn('MyTransform', pipeline.applied_labels)
+ _ = pcoll | MyTransform("suffix")
+ self.assertIn('MyTransform(suffix)', pipeline.applied_labels)
+ _ = pcoll | MyTransform("looooooooooooooooooooooooooooooooooooooooong")
+ self.assertIn('MyTransform(looooooooo...oooong)', pipeline.applied_labels)
+
+ def test_ptransformfn_legacy_default_label(self):
+ @beam.ptransform_fn
+ def MyTransform(self, suffix="xyz"):
+ return pcoll | beam.Map(lambda s: s + suffix)
+
+ pipeline = TestPipeline(
+ options=PipelineOptions(update_compatibility_version='2.67.0'))
+ pcoll = pipeline | beam.Create(['a', 'b', 'c'])
+
+ _ = pcoll | MyTransform()
+ self.assertIn('MyTransform', pipeline.applied_labels)
+ _ = pcoll | MyTransform("suffix")
+ self.assertIn('MyTransform(suffix)', pipeline.applied_labels)
+ _ = pcoll | MyTransform("looooooooooooooooooooooooooooooooooooooooong")
+ self.assertIn(
+ 'MyTransform(looooooooooooooooooooooooooooooooooooooooong)',
+ pipeline.applied_labels)
+
def test_combine_with_label(self):
vals = [1, 2, 3, 4, 5, 6, 7]
with TestPipeline() as pipeline: