Skip to content

Commit 6126b03

Browse files
authored
add more doc strings for integration tests (#35171)
1 parent 9365436 commit 6126b03

1 file changed

Lines changed: 109 additions & 7 deletions

File tree

sdks/python/apache_beam/yaml/integration_tests.py

Lines changed: 109 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -361,11 +361,11 @@ def temp_sqlserver_database():
361361

362362
class OracleTestContainer(DockerContainer):
363363
"""
364-
OracleTestContainer is an updated version of OracleDBContainer that goes
365-
ahead and sets the oracle password, waits for logs to establish that the
366-
container is ready before calling get_exposed_port, and uses a more modern
367-
oracle driver.
368-
"""
364+
OracleTestContainer is an updated version of OracleDBContainer that goes
365+
ahead and sets the oracle password, waits for logs to establish that the
366+
container is ready before calling get_exposed_port, and uses a more modern
367+
oracle driver.
368+
"""
369369
def __init__(self):
370370
super().__init__("gvenzl/oracle-xe:21-slim")
371371
self.with_env("ORACLE_PASSWORD", "oracle")
@@ -483,6 +483,22 @@ def temp_pubsub_emulator(project_id="apache-beam-testing"):
483483

484484

485485
def replace_recursive(spec, vars):
486+
"""Recursively replaces string placeholders in a spec with values from vars.
487+
488+
Traverses a nested structure (dicts, lists, or other types). If a string
489+
is encountered and contains placeholders in the format '{key}', it attempts
490+
to replace them using the `vars` dictionary.
491+
492+
Args:
493+
spec: The (potentially nested) structure to process.
494+
vars: A dictionary of variable names to their replacement values.
495+
496+
Returns:
497+
The spec with placeholders replaced.
498+
499+
Raises:
500+
ValueError: If a string formatting error occurs.
501+
"""
486502
if isinstance(spec, dict):
487503
return {
488504
key: replace_recursive(value, vars)
@@ -503,6 +519,20 @@ def replace_recursive(spec, vars):
503519

504520

505521
def transform_types(spec):
522+
"""Recursively extracts all transform types from a pipeline specification.
523+
524+
This generator function traverses a nested pipeline specification (likely
525+
parsed from YAML). It identifies and yields the 'type' string for each
526+
transform defined within the specification, including those within
527+
'composite' or 'chain' structures.
528+
529+
Args:
530+
spec (dict): A dictionary representing a pipeline or transform
531+
specification.
532+
533+
Yields:
534+
str: The 'type' of each transform found in the specification.
535+
"""
506536
if spec.get('type', None) in (None, 'composite', 'chain'):
507537
if 'source' in spec:
508538
yield from transform_types(spec['source'])
@@ -515,8 +545,30 @@ def transform_types(spec):
515545

516546

517547
def provider_sets(spec, require_available=False):
518-
"""For transforms that are vended by multiple providers, yields all possible
519-
combinations of providers to use.
548+
"""
549+
Generates all relevant combinations of providers for a given pipeline spec.
550+
551+
This function analyzes a pipeline specification to identify transforms that
552+
can be implemented by multiple underlying providers (e.g., a generic
553+
transform vs. a SQL-backed one). It then yields different "provider sets,"
554+
each representing a unique combination of choices for these multi-provider
555+
transforms.
556+
557+
If no transforms have multiple available providers, it yields a single
558+
provider set using the standard defaults.
559+
560+
Args:
561+
spec (dict): The pipeline specification, typically loaded from YAML.
562+
require_available (bool): If True, raises an error if a provider
563+
needed for a transform is not available. If False (default),
564+
unavailable providers are skipped, potentially reducing the number
565+
of yielded combinations.
566+
567+
Yields:
568+
tuple: A tuple where the first element is a string suffix uniquely
569+
identifying the provider combination (e.g., "MyTransform_SqlProvider_0"),
570+
and the second element is a dictionary mapping transform types to a list
571+
containing the selected provider(s) for that combination.
520572
"""
521573
try:
522574
for p in spec['pipelines']:
@@ -566,6 +618,39 @@ def filter_to_available(t, providers):
566618

567619

568620
def create_test_methods(spec):
621+
"""Dynamically creates test methods based on a YAML specification.
622+
623+
This function takes a YAML specification (`spec`) which defines pipelines,
624+
fixtures, and potentially options. It iterates through different
625+
combinations of "providers" (which determine how YAML transforms are
626+
implemented, e.g., using Python or SQL).
627+
628+
For each combination of providers:
629+
1. It constructs a unique test method name (e.g., `test_only`).
630+
2. It defines a test method that:
631+
a. Sets up any specified fixtures, making their values available as
632+
variables.
633+
b. Mocks the standard YAML providers to use the current combination
634+
of providers for this test run.
635+
c. For each pipeline defined in the `spec`:
636+
i. Creates a `beam.Pipeline` instance with specified options.
637+
ii. Expands the YAML pipeline definition using
638+
`yaml_transform.expand_pipeline`, substituting any fixture
639+
variables.
640+
iii. Runs the Beam pipeline.
641+
642+
The function yields tuples of (test_method_name, test_method_function),
643+
which can then be used to populate a `unittest.TestCase` class.
644+
645+
Args:
646+
spec (dict): A dictionary parsed from a YAML test specification file.
647+
It's expected to have keys like 'fixtures' (optional) and 'pipelines'.
648+
649+
Yields:
650+
tuple: A tuple containing:
651+
- str: The generated name for the test method (e.g., "test_only").
652+
- function: The dynamically generated test method.
653+
"""
569654
for suffix, providers in provider_sets(spec):
570655

571656
def test(self, providers=providers): # default arg to capture loop value
@@ -593,6 +678,23 @@ def test(self, providers=providers): # default arg to capture loop value
593678

594679

595680
def parse_test_files(filepattern):
681+
"""Parses YAML test files and dynamically creates test cases.
682+
683+
This function iterates through all files matching the given glob pattern.
684+
For each YAML file found, it:
685+
1. Reads the file content.
686+
2. Determines a test suite name based on the file name.
687+
3. Calls `create_test_methods` to generate test methods from the
688+
YAML specification.
689+
4. Dynamically creates a new TestCase class (inheriting from
690+
`unittest.TestCase`) and populates it with the generated test methods.
691+
5. Adds this newly created TestCase class to the global scope, making it
692+
discoverable by the unittest framework.
693+
694+
Args:
695+
filepattern (str): A glob pattern specifying the YAML test files to parse.
696+
For example, 'path/to/tests/*.yaml'.
697+
"""
596698
for path in glob.glob(filepattern):
597699
with open(path) as fin:
598700
suite_name = os.path.splitext(os.path.basename(path))[0].title().replace(

0 commit comments

Comments
 (0)