From 46dffc34b472c41fb7fe1bad10e78ac5dff5031e Mon Sep 17 00:00:00 2001 From: chalmer lowe Date: Tue, 14 Oct 2025 09:55:50 -0400 Subject: [PATCH 1/3] updates docstrings for functions that handle filtering --- scripts/microgenerator/generate.py | 52 ++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/scripts/microgenerator/generate.py b/scripts/microgenerator/generate.py index d9f27c85b..2aa4f921c 100644 --- a/scripts/microgenerator/generate.py +++ b/scripts/microgenerator/generate.py @@ -381,7 +381,31 @@ def process_structure( def _should_include_class(class_name: str, class_filters: Dict[str, Any]) -> bool: - """Checks if a class should be included based on filter criteria.""" + """Determines if a class should be included based on name filters. + + Filters are defined in the configuration file and passed in the + `class_filters` dictionary. + + Args: + class_name: The name of the class to check. + class_filters: A dictionary containing filter rules: + "include_suffixes": List of suffixes. If provided, the class name + MUST end with one of these suffixes. + "exclude_suffixes": List of suffixes. If provided, the class name + MUST NOT end with any of these suffixes. + + Returns: + bool: True if the class should be included, False otherwise. + + Example: + >>> filters = {"include_suffixes": ["Client"], "exclude_suffixes": ["BaseClient"]} + >>> _should_include_class("DatasetClient", filters) + True + >>> _should_include_class("BaseClient", filters) + False + >>> _should_include_class("SomeOtherClass", filters) + False + """ if class_filters.get("include_suffixes"): if not class_name.endswith(tuple(class_filters["include_suffixes"])): return False @@ -392,7 +416,31 @@ def _should_include_class(class_name: str, class_filters: Dict[str, Any]) -> boo def _should_include_method(method_name: str, method_filters: Dict[str, Any]) -> bool: - """Checks if a method should be included based on filter criteria.""" + """Determines if a method should be included based on name filters. + + Filters are defined in the configuration file and passed in the + `method_filters` dictionary. + + Args: + method_name: The name of the method to check. + method_filters: A dictionary containing filter rules: + "include_prefixes": List of prefixes. If provided, the method name + MUST start with one of these prefixes. + "exclude_prefixes": List of prefixes. If provided, the method name + MUST NOT start with any of these prefixes. + + Returns: + bool: True if the method should be included, False otherwise. + + Example: + >>> filters = {"include_prefixes": ["get_", "list_"], "exclude_prefixes": ["_internal_"]} + >>> _should_include_method("get_dataset", filters) + True + >>> _should_include_method("create_dataset", filters) + False + >>> _should_include_method("_internal_get_dataset", filters) + False + """ if method_filters.get("include_prefixes"): if not any( method_name.startswith(p) for p in method_filters["include_prefixes"] From 7d6e28d2c912f762a63c47465ecd163120999880 Mon Sep 17 00:00:00 2001 From: chalmer lowe Date: Tue, 14 Oct 2025 09:56:38 -0400 Subject: [PATCH 2/3] adds tests for functions that handle filtering --- .../tests/unit/test_generate_filters.py | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 scripts/microgenerator/tests/unit/test_generate_filters.py diff --git a/scripts/microgenerator/tests/unit/test_generate_filters.py b/scripts/microgenerator/tests/unit/test_generate_filters.py new file mode 100644 index 000000000..073fe8048 --- /dev/null +++ b/scripts/microgenerator/tests/unit/test_generate_filters.py @@ -0,0 +1,112 @@ +import pytest +from scripts.microgenerator.generate import ( + _should_include_class, + _should_include_method, +) + + +# Tests for _should_include_class +@pytest.mark.parametrize( + "class_name, filters, expected", + [ + ("MyClass", {}, True), # No filters + ( + "DatasetClient", + {"include_suffixes": ["Client", "Service"]}, + True, + ), # Include suffix match + ( + "MyClass", + {"include_suffixes": ["Client", "Service"]}, + False, + ), # Include suffix no match + ( + "MyBase", + {"exclude_suffixes": ["Base", "Util"]}, + False, + ), # Exclude suffix match + ( + "MyClass", + {"exclude_suffixes": ["Base", "Util"]}, + True, + ), # Exclude suffix no match + ( + "DatasetClient", + {"include_suffixes": ["Client"], "exclude_suffixes": ["BaseClient"]}, + True, + ), # Mix include/exclude + ( + "BaseClient", + {"include_suffixes": ["Client"], "exclude_suffixes": ["BaseClient"]}, + False, + ), # Mix include/exclude + ( + "MyClass", + {"include_suffixes": [], "exclude_suffixes": []}, + True, + ), # Empty filters + ], +) +def test_should_include_class(class_name, filters, expected): + assert _should_include_class(class_name, filters) is expected + + +# Tests for _should_include_method +@pytest.mark.parametrize( + "method_name, filters, expected", + [ + ("my_method", {}, True), # No filters + ( + "get_dataset", + {"include_prefixes": ["get_", "list_"]}, + True, + ), # Include prefix match + ( + "list_jobs", + {"include_prefixes": ["get_", "list_"]}, + True, + ), # Include prefix match + ( + "create_dataset", + {"include_prefixes": ["get_", "list_"]}, + False, + ), # Include prefix no match + ( + "_private_method", + {"exclude_prefixes": ["_", "internal_"]}, + False, + ), # Exclude prefix match + ( + "internal_helper", + {"exclude_prefixes": ["_", "internal_"]}, + False, + ), # Exclude prefix match + ( + "get_dataset", + {"exclude_prefixes": ["_", "internal_"]}, + True, + ), # Exclude prefix no match + ( + "get_dataset", + {"include_prefixes": ["get_"], "exclude_prefixes": ["get_internal_"]}, + True, + ), # Mix include/exclude + ( + "get_internal_status", + {"include_prefixes": ["get_"], "exclude_prefixes": ["get_internal_"]}, + False, + ), # Mix include/exclude + ( + "list_datasets", + {"include_prefixes": ["get_"], "exclude_prefixes": ["get_internal_"]}, + False, + ), # Mix include/exclude + ( + "my_method", + {"include_prefixes": [], "exclude_prefixes": []}, + True, + ), # Empty filters + ], +) +def test_should_include_method(method_name, filters, expected): + assert _should_include_method(method_name, filters) is expected From ca8c83281502d9017b6346cc24a63b5d1e4a1ce8 Mon Sep 17 00:00:00 2001 From: chalmer lowe Date: Tue, 14 Oct 2025 10:13:17 -0400 Subject: [PATCH 3/3] refactors tests to use pytest.param() --- .../tests/unit/test_generate_filters.py | 89 +++++++++++-------- 1 file changed, 53 insertions(+), 36 deletions(-) diff --git a/scripts/microgenerator/tests/unit/test_generate_filters.py b/scripts/microgenerator/tests/unit/test_generate_filters.py index 073fe8048..3882237bf 100644 --- a/scripts/microgenerator/tests/unit/test_generate_filters.py +++ b/scripts/microgenerator/tests/unit/test_generate_filters.py @@ -9,42 +9,49 @@ @pytest.mark.parametrize( "class_name, filters, expected", [ - ("MyClass", {}, True), # No filters - ( + pytest.param("MyClass", {}, True, id="No filters"), + pytest.param( "DatasetClient", {"include_suffixes": ["Client", "Service"]}, True, - ), # Include suffix match - ( + id="Include suffix match", + ), + pytest.param( "MyClass", {"include_suffixes": ["Client", "Service"]}, False, - ), # Include suffix no match - ( + id="Include suffix no match", + ), + pytest.param( "MyBase", {"exclude_suffixes": ["Base", "Util"]}, False, - ), # Exclude suffix match - ( + id="Exclude suffix match", + ), + pytest.param( "MyClass", {"exclude_suffixes": ["Base", "Util"]}, True, - ), # Exclude suffix no match - ( + id="Exclude suffix no match", + ), + pytest.param( "DatasetClient", {"include_suffixes": ["Client"], "exclude_suffixes": ["BaseClient"]}, True, - ), # Mix include/exclude - ( + id="Mix include/exclude match", + ), + pytest.param( "BaseClient", {"include_suffixes": ["Client"], "exclude_suffixes": ["BaseClient"]}, False, - ), # Mix include/exclude - ( + id="Mix include/exclude no match", + ), + pytest.param( "MyClass", {"include_suffixes": [], "exclude_suffixes": []}, True, - ), # Empty filters + id="Empty filters", + ), ], ) def test_should_include_class(class_name, filters, expected): @@ -55,57 +62,67 @@ def test_should_include_class(class_name, filters, expected): @pytest.mark.parametrize( "method_name, filters, expected", [ - ("my_method", {}, True), # No filters - ( + pytest.param("my_method", {}, True, id="No filters"), + pytest.param( "get_dataset", {"include_prefixes": ["get_", "list_"]}, True, - ), # Include prefix match - ( + id="Include prefix match (get)", + ), + pytest.param( "list_jobs", {"include_prefixes": ["get_", "list_"]}, True, - ), # Include prefix match - ( + id="Include prefix match (list)", + ), + pytest.param( "create_dataset", {"include_prefixes": ["get_", "list_"]}, False, - ), # Include prefix no match - ( + id="Include prefix no match", + ), + pytest.param( "_private_method", {"exclude_prefixes": ["_", "internal_"]}, False, - ), # Exclude prefix match - ( + id="Exclude prefix match (private)", + ), + pytest.param( "internal_helper", {"exclude_prefixes": ["_", "internal_"]}, False, - ), # Exclude prefix match - ( + id="Exclude prefix match (internal)", + ), + pytest.param( "get_dataset", {"exclude_prefixes": ["_", "internal_"]}, True, - ), # Exclude prefix no match - ( + id="Exclude prefix no match", + ), + pytest.param( "get_dataset", {"include_prefixes": ["get_"], "exclude_prefixes": ["get_internal_"]}, True, - ), # Mix include/exclude - ( + id="Mix include/exclude match", + ), + pytest.param( "get_internal_status", {"include_prefixes": ["get_"], "exclude_prefixes": ["get_internal_"]}, False, - ), # Mix include/exclude - ( + id="Mix include/exclude no match (exclude wins)", + ), + pytest.param( "list_datasets", {"include_prefixes": ["get_"], "exclude_prefixes": ["get_internal_"]}, False, - ), # Mix include/exclude - ( + id="Mix include/exclude no match (include fails)", + ), + pytest.param( "my_method", {"include_prefixes": [], "exclude_prefixes": []}, True, - ), # Empty filters + id="Empty filters", + ), ], ) def test_should_include_method(method_name, filters, expected):