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

yzheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/polaris.git


The following commit(s) were added to refs/heads/main by this push:
     new b41876244 feat: Make generate_clients.py windows compatible (#3084)
b41876244 is described below

commit b41876244a859ef3c2ba0bcbfc203869690eb304
Author: Yong Zheng <[email protected]>
AuthorDate: Thu Nov 20 08:28:06 2025 -0600

    feat: Make generate_clients.py windows compatible (#3084)
    
    * Make generate_clients.py windows compatible
    
    * Updated CHANGELOG.md
---
 CHANGELOG.md                      |   1 +
 client/python/generate_clients.py | 130 ++++++++++++++++++--------------------
 2 files changed, 62 insertions(+), 69 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e715a2a2c..3ad9d759b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -63,6 +63,7 @@ request adding CHANGELOG notes for breaking (!) changes and 
possibly other secti
 - `client.region` is no longer considered a "credential" property (related to 
Iceberg REST Catalog API).
 - Relaxed the requirements for S3 storage's ARN to allow Polaris to connect to 
more non-AWS S3 storage appliances. 
 - Added checksum to helm deployment so that it will restart when the configmap 
has changed.
+- Added Windows support for Python client
 
 ### Deprecations
 
diff --git a/client/python/generate_clients.py 
b/client/python/generate_clients.py
index 0c7c1b5af..c8f42cffe 100644
--- a/client/python/generate_clients.py
+++ b/client/python/generate_clients.py
@@ -36,12 +36,12 @@ import ast
 
 # Paths
 CLIENT_DIR = Path(__file__).parent
-HEADER_DIR = os.path.join(CLIENT_DIR, "templates")
-SPEC_DIR = os.path.join(CLIENT_DIR, "spec")
-POLARIS_MANAGEMENT_SPEC = os.path.join(SPEC_DIR, 
"polaris-management-service.yml")
-ICEBERG_CATALOG_SPEC = os.path.join(SPEC_DIR, 
"iceberg-rest-catalog-open-api.yaml")
-POLARIS_CATALOG_SPEC = os.path.join(SPEC_DIR, "polaris-catalog-service.yaml")
-OPEN_API_GENERATOR_IGNORE = os.path.join(CLIENT_DIR, 
".openapi-generator-ignore")
+HEADER_DIR = CLIENT_DIR / "templates"
+SPEC_DIR = CLIENT_DIR / "spec"
+POLARIS_MANAGEMENT_SPEC = SPEC_DIR / "polaris-management-service.yml"
+ICEBERG_CATALOG_SPEC = SPEC_DIR / "iceberg-rest-catalog-open-api.yaml"
+POLARIS_CATALOG_SPEC = SPEC_DIR / "polaris-catalog-service.yaml"
+OPEN_API_GENERATOR_IGNORE = CLIENT_DIR / ".openapi-generator-ignore"
 
 # Open API Generator Configs
 PACKAGE_NAME_POLARIS_MANAGEMENT = (
@@ -58,20 +58,20 @@ KEEP_TEST_FILES = [
 ]
 EXCLUDE_PATHS = [
     Path(".gitignore"),
-    Path(".openapi-generator/"),
+    Path(".openapi-generator"),
     Path(".openapi-generator-ignore"),
-    Path(".pytest_cache/"),
+    Path(".pytest_cache"),
     Path("test/test_cli_parsing.py"),
-    Path("apache_polaris/__pycache__/"),
-    Path("apache_polaris/cli/"),
-    Path("apache_polaris/sdk/__pycache__/"),
-    Path("apache_polaris/sdk/catalog/__pycache__/"),
-    Path("apache_polaris/sdk/catalog/models/__pycache__/"),
-    Path("apache_polaris/sdk/catalog/api/__pycache__/"),
-    Path("apache_polaris/sdk/management/__pycache__/"),
-    Path("apache_polaris/sdk/management/models/__pycache__/"),
-    Path("apache_polaris/sdk/management/api/__pycache__/"),
-    Path("integration_tests/"),
+    Path("apache_polaris/__pycache__"),
+    Path("apache_polaris/cli"),
+    Path("apache_polaris/sdk/__pycache__"),
+    Path("apache_polaris/sdk/catalog/__pycache__"),
+    Path("apache_polaris/sdk/catalog/models/__pycache__"),
+    Path("apache_polaris/sdk/catalog/api/__pycache__"),
+    Path("apache_polaris/sdk/management/__pycache__"),
+    Path("apache_polaris/sdk/management/models/__pycache__"),
+    Path("apache_polaris/sdk/management/api/__pycache__"),
+    Path("integration_tests"),
     Path(".github/workflows/python.yml"),
     Path(".gitlab-ci.yml"),
     Path("pyproject.toml"),
@@ -86,9 +86,9 @@ EXCLUDE_PATHS = [
     Path("README.md"),
     Path("generate_clients.py"),
     Path(".venv"),
-    Path("dist/"),
-    Path("templates/"),
-    Path("spec/"),
+    Path("dist"),
+    Path("templates"),
+    Path("spec"),
     Path("PKG-INFO"),
 ]
 EXCLUDE_EXTENSIONS = [
@@ -223,64 +223,56 @@ def _prepend_header_to_file(file_path: Path, 
header_file_path: Path) -> None:
 
 def prepend_licenses() -> None:
     logger.info("Re-applying license headers...")
+
+    # Combine all paths to exclude into one set.
+    all_excluded_paths = set(EXCLUDE_PATHS) | set(KEEP_TEST_FILES)
+
     for file_path in CLIENT_DIR.rglob("*"):
-        if file_path.is_file():
-            relative_file_path = file_path.relative_to(CLIENT_DIR)
-            file_extension = ""
-            # If it's a "dotfile" like .keep
-            if (
-                relative_file_path.name.startswith(".")
-                and "." not in relative_file_path.name[1:]
-            ):
-                # e.g., for '.keep', this is 'keep'
-                file_extension = relative_file_path.name.lstrip(".")
-            else:
-                # For standard files like generate_clients.py
-                file_extension = file_path.suffix.lstrip(".")
-
-            # Check if extension is excluded
-            if file_extension in EXCLUDE_EXTENSIONS:
-                logger.debug(f"{relative_file_path}: skipped (extension 
excluded)")
-                continue
-
-            is_excluded = False
-            # Combine EXCLUDE_PATHS and KEEP_TEST_FILES for comprehensive 
exclusion check
-            # Convert Path objects in EXCLUDE_PATHS to strings for fnmatch 
compatibility
-            # Ensure patterns ending with '/' are handled for directory 
matching
-            all_exclude_patterns = [
-                str(p) + ("/" if p.is_dir() else "") for p in EXCLUDE_PATHS
-            ] + [str(p) for p in KEEP_TEST_FILES]
-
-            for exclude_pattern_str in all_exclude_patterns:
-                # Handle direct file match or if the file is within an 
excluded directory
-                if fnmatch.fnmatch(str(relative_file_path), 
exclude_pattern_str) or (
-                    exclude_pattern_str.endswith("/")
-                    and str(relative_file_path).startswith(exclude_pattern_str)
-                ):
-                    is_excluded = True
-                    break
+        if not file_path.is_file():
+            continue
+
+        relative_file_path = file_path.relative_to(CLIENT_DIR)
+
+        # Determine file extension, handling dotfiles.
+        file_extension = ""
+        if (
+            relative_file_path.name.startswith(".")
+            and "." not in relative_file_path.name[1:]
+        ):
+            file_extension = relative_file_path.name.lstrip(".")
+        else:
+            file_extension = relative_file_path.suffix.lstrip(".")
 
-            if is_excluded:
-                logger.debug(f"{relative_file_path}: skipped (path excluded)")
-                continue
+        if file_extension in EXCLUDE_EXTENSIONS:
+            logger.debug(f"{relative_file_path}: skipped (extension excluded)")
+            continue
 
-            header_file_path = Path(
-                os.path.join(HEADER_DIR, f"header-{file_extension}.txt")
-            )
+        # Check if the path should be excluded.
+        is_excluded = False
+        for excluded_path in all_excluded_paths:
+            if relative_file_path == excluded_path or excluded_path in 
relative_file_path.parents:
+                is_excluded = True
+                break
 
-            if header_file_path.is_file():
-                _prepend_header_to_file(file_path, header_file_path)
-                logger.debug(f"{relative_file_path}: updated")
-            else:
-                logger.error(f"No header compatible with file 
{relative_file_path}")
-                sys.exit(2)
+        if is_excluded:
+            logger.debug(f"{relative_file_path}: skipped (path excluded)")
+            continue
+
+        header_file_path = HEADER_DIR / f"header-{file_extension}.txt"
+
+        if header_file_path.is_file():
+            _prepend_header_to_file(file_path, header_file_path)
+            logger.debug(f"{relative_file_path}: updated")
+        else:
+            logger.error(f"No header compatible with file 
{relative_file_path}")
+            sys.exit(2)
     logger.info("License fix complete.")
 
 
 def prepare_spec_dir():
     logger.info("Preparing spec directory...")
     spec_dir = Path(SPEC_DIR)
-    spec_source_dir = Path(os.path.join(CLIENT_DIR.parent.parent, "spec"))
+    spec_source_dir = CLIENT_DIR.parent.parent / "spec"
 
     if spec_source_dir.is_dir():
         logger.info(f"Copying spec directory from {spec_source_dir} to 
{spec_dir}")

Reply via email to