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}")