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

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


The following commit(s) were added to refs/heads/main by this push:
     new 984dfc89f feat(python): parallel python wheel build (#2989)
984dfc89f is described below

commit 984dfc89fda7276c240ddc4d5bc5828c1b3fd084
Author: Shawn Yang <[email protected]>
AuthorDate: Thu Dec 4 16:16:51 2025 +0800

    feat(python): parallel python wheel build (#2989)
    
    ## Why?
    
    
    
    ## What does this PR do?
    
    
    
    ## Related issues
    
    
    
    ## Does this PR introduce any user-facing change?
    
    
    
    - [ ] Does this PR introduce any public API change?
    - [ ] Does this PR introduce any binary protocol compatibility change?
    
    ## Benchmark
---
 .github/workflows/build-containerized-pr.yml      |  5 +-
 .github/workflows/build-containerized-release.yml | 25 +++---
 .github/workflows/build-native-pr.yml             |  2 +-
 .gitignore                                        |  4 +-
 ci/build_linux_wheels.py                          | 98 ++++++++---------------
 python/pyfory/tests/test_buffer.py                |  1 +
 6 files changed, 51 insertions(+), 84 deletions(-)

diff --git a/.github/workflows/build-containerized-pr.yml 
b/.github/workflows/build-containerized-pr.yml
index 3f1240f1d..837a50811 100644
--- a/.github/workflows/build-containerized-pr.yml
+++ b/.github/workflows/build-containerized-pr.yml
@@ -29,12 +29,13 @@ jobs:
     strategy:
       matrix:
         os: [ubuntu-latest, ubuntu-24.04-arm]
+        python: [cp313-cp313]
     steps:
       - uses: actions/checkout@v5
       - name: Build and test wheels
-        run: ./ci/build_linux_wheels.py --arch ${{ runner.arch }}
+        run: ./ci/build_linux_wheels.py --arch ${{ runner.arch }} --python ${{ 
matrix.python }}
       - name: Upload wheels as artifacts
         uses: actions/upload-artifact@v4
         with:
-          name: pyfory-wheels-containerized-${{ matrix.os }}
+          name: pyfory-wheels-${{ matrix.os }}-${{ matrix.python }}
           path: dist/*.whl
diff --git a/.github/workflows/build-containerized-release.yml 
b/.github/workflows/build-containerized-release.yml
index 7f9a78b23..513a1cd99 100644
--- a/.github/workflows/build-containerized-release.yml
+++ b/.github/workflows/build-containerized-release.yml
@@ -26,29 +26,22 @@ jobs:
     strategy:
       matrix:
         os: [ubuntu-latest, ubuntu-24.04-arm]
+        python:
+          - cp38-cp38
+          - cp39-cp39
+          - cp310-cp310
+          - cp311-cp311
+          - cp312-cp312
+          - cp313-cp313
     steps:
       - uses: actions/checkout@v5
       - name: Bump version
         # Pass the tag name from the push (e.g. "v0.12.1b1"); deploy.sh will 
strip leading "v".
         run: ./ci/deploy.sh bump_py_version "${{ github.ref_name }}"
-      - name: Set up Python 3.8
-        uses: actions/setup-python@v5
-        with:
-          python-version: 3.8
-          cache: 'pip'
-      - name: Cache Bazel binary
-        uses: actions/cache@v4
-        with:
-          path: |
-            ~/bin/bazel
-            ~/.local/bin/bazel
-          key: bazel-binary-${{ runner.os }}-${{ runner.arch }}-${{ 
hashFiles('.bazelversion') }}
-      - name: Install bazel
-        run: ./ci/run_ci.sh install_bazel
       - name: Build and test wheels
-        run: ./ci/build_linux_wheels.py --arch ${{ runner.arch }} --release
+        run: ./ci/build_linux_wheels.py --arch ${{ runner.arch }} --python ${{ 
matrix.python }} --release
       - name: Upload wheels as artifacts
         uses: actions/upload-artifact@v4
         with:
-          name: pyfory-wheels-${{ matrix.os }}-${{ runner.arch }}-${{ 
github.ref_name }}
+          name: pyfory-wheels-${{ matrix.os }}-${{ matrix.python }}-${{ 
github.ref_name }}
           path: dist/*.whl
diff --git a/.github/workflows/build-native-pr.yml 
b/.github/workflows/build-native-pr.yml
index e7f4131a9..044c8f077 100644
--- a/.github/workflows/build-native-pr.yml
+++ b/.github/workflows/build-native-pr.yml
@@ -29,7 +29,7 @@ jobs:
     strategy:
       matrix:
         os: [macos-latest, windows-latest]
-        python-version: ['3.8', '3.13']
+        python-version: ['3.13']
     steps:
       - uses: actions/checkout@v5
       - uses: actions/setup-python@v5
diff --git a/.gitignore b/.gitignore
index 558badab6..6e70e0eb9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,8 @@
 **/*.dylib
 **/*.pyd
 bazel-*
+.bazel_user_root/
+**/.bazel_user_root/
 .whl
 python/.cache
 python/pyfory/__pycache__/
@@ -80,4 +82,4 @@ dist/
 **/.pytest_cache
 .venv
 **/.idea
-examples/cpp/cmake_example/build
\ No newline at end of file
+examples/cpp/cmake_example/build
diff --git a/ci/build_linux_wheels.py b/ci/build_linux_wheels.py
index d1ba2cb5d..54fdc2ed3 100755
--- a/ci/build_linux_wheels.py
+++ b/ci/build_linux_wheels.py
@@ -18,14 +18,18 @@
 # under the License.
 
 """
-Host-side wrapper: workflow provides only --arch.
-Images are defined as regular Python lists (no env vars).
+Host-side wrapper for building Python wheels in manylinux containers.
+
+Usage:
+  ./build_linux_wheels.py --arch X86 --python cp38-cp38
+  ./build_linux_wheels.py --arch AARCH64 --python cp313-cp313 --release
 
 Environment:
   - GITHUB_WORKSPACE (optional; defaults to cwd)
 """
 
 from __future__ import annotations
+
 import argparse
 import os
 import shlex
@@ -33,27 +37,15 @@ import subprocess
 import sys
 from typing import List
 
-# Define Python version sets directly in the Python script
-RELEASE_PYTHON_VERSIONS = (
-    "cp38-cp38 cp39-cp39 cp310-cp310 cp311-cp311 cp312-cp312 cp313-cp313"
-)
-DEFAULT_PYTHON_VERSIONS = "cp38-cp38 cp313-cp313"
-
 # Path to the container build script
 CONTAINER_SCRIPT_PATH = "ci/tasks/python_container_build_script.sh"
 
 DEFAULT_X86_IMAGES = [
     "quay.io/pypa/manylinux2014_x86_64:latest",
-    # "quay.io/pypa/manylinux_2_28_x86_64:latest",
-    # bazel binaries do not work with musl
-    # "quay.io/pypa/musllinux_1_2_x86_64:latest",
 ]
 
 DEFAULT_AARCH64_IMAGES = [
     "quay.io/pypa/manylinux2014_aarch64:latest",
-    # "quay.io/pypa/manylinux_2_28_aarch64:latest",
-    # bazel binaries do not work with musl
-    # "quay.io/pypa/musllinux_1_2_aarch64:latest",
 ]
 
 ARCH_ALIASES = {
@@ -73,10 +65,11 @@ def parse_args():
         "--arch", required=True, help="Architecture (e.g. X86, X64, AARCH64)"
     )
     p.add_argument(
-        "--release", action="store_true", help="Run full test suite for 
release"
+        "--python", required=True, help="Python version (e.g. cp38-cp38, 
cp313-cp313)"
     )
+    p.add_argument("--release", action="store_true", help="Run in release 
mode")
     p.add_argument(
-        "--dry-run", action="store_true", help="Print docker commands without 
running"
+        "--dry-run", action="store_true", help="Print docker command without 
running"
     )
     return p.parse_args()
 
@@ -86,21 +79,19 @@ def normalize_arch(raw: str) -> str:
     return ARCH_ALIASES.get(key, raw.strip().lower())
 
 
-def collect_images_for_arch(arch_normalized: str) -> List[str]:
+def get_image_for_arch(arch_normalized: str) -> str:
     if arch_normalized == "x86":
-        imgs = DEFAULT_X86_IMAGES  # dedupe preserving order
+        return DEFAULT_X86_IMAGES[0]
     elif arch_normalized == "arm64":
-        imgs = DEFAULT_AARCH64_IMAGES
+        return DEFAULT_AARCH64_IMAGES[0]
     else:
         raise SystemExit(f"Unsupported arch: {arch_normalized!r}")
-    return imgs
 
 
-def build_docker_cmd(workspace: str, image: str, release: bool = False) -> 
List[str]:
+def build_docker_cmd(
+    workspace: str, image: str, python_version: str, release: bool = False
+) -> List[str]:
     workspace = os.path.abspath(workspace)
-    python_versions = RELEASE_PYTHON_VERSIONS if release else 
DEFAULT_PYTHON_VERSIONS
-
-    # Get GitHub reference name from environment
     github_ref_name = os.environ.get("GITHUB_REF_NAME", "")
 
     cmd = [
@@ -109,16 +100,15 @@ def build_docker_cmd(workspace: str, image: str, release: 
bool = False) -> List[
         "-i",
         "--rm",
         "-v",
-        f"{workspace}:/work",  # (v)olume
+        f"{workspace}:/work",
         "-w",
-        "/work",  # (w)orking directory
+        "/work",
         "-e",
-        f"PYTHON_VERSIONS={python_versions}",  # (e)nvironment variables
+        f"PYTHON_VERSIONS={python_version}",
         "-e",
         f"RELEASE_BUILD={'1' if release else '0'}",
     ]
 
-    # Pass GitHub reference name if available
     if github_ref_name:
         cmd.extend(["-e", f"GITHUB_REF_NAME={github_ref_name}"])
 
@@ -126,52 +116,32 @@ def build_docker_cmd(workspace: str, image: str, release: 
bool = False) -> List[
     return cmd
 
 
-def run_for_images(
-    images: List[str], workspace: str, dry_run: bool, release: bool = False
-) -> int:
-    rc_overall = 0
-    for image in images:
-        docker_cmd = build_docker_cmd(workspace, image, release=release)
-        printable = " ".join(shlex.quote(c) for c in docker_cmd)
-        print(f"+ {printable}")
-        if dry_run:
-            continue
-        try:
-            completed = subprocess.run(docker_cmd)
-            if completed.returncode != 0:
-                print(
-                    f"Container {image} exited with {completed.returncode}",
-                    file=sys.stderr,
-                )
-                rc_overall = completed.returncode if rc_overall == 0 else 
rc_overall
-            else:
-                print(f"Container {image} completed successfully.")
-        except KeyboardInterrupt:
-            print("Interrupted by user", file=sys.stderr)
-            return 130
-        except FileNotFoundError as e:
-            print(f"Error running docker: {e}", file=sys.stderr)
-            return 2
-    return rc_overall
-
-
 def main() -> int:
     args = parse_args()
     arch = normalize_arch(args.arch)
-    images = collect_images_for_arch(arch)
-    if not images:
-        print(f"No images configured for arch {arch}", file=sys.stderr)
-        return 2
+    image = get_image_for_arch(arch)
     workspace = os.environ.get("GITHUB_WORKSPACE", os.getcwd())
 
-    # Check if the container script exists
     script_path = os.path.join(workspace, CONTAINER_SCRIPT_PATH)
     if not os.path.exists(script_path):
         print(f"Container script not found at {script_path}", file=sys.stderr)
         return 2
 
-    print(f"Selected images for arch {args.arch}: {images}")
-    return run_for_images(images, workspace, args.dry_run, 
release=args.release)
+    docker_cmd = build_docker_cmd(workspace, image, args.python, 
release=args.release)
+    printable = " ".join(shlex.quote(c) for c in docker_cmd)
+    print(f"+ {printable}")
+
+    if args.dry_run:
+        return 0
+
+    try:
+        completed = subprocess.run(docker_cmd)
+        if completed.returncode != 0:
+            print(f"Container exited with {completed.returncode}", 
file=sys.stderr)
+        return completed.returncode
+    except FileNotFoundError as e:
+        print(f"Error running docker: {e}", file=sys.stderr)
+        return 2
 
 
 if __name__ == "__main__":
diff --git a/python/pyfory/tests/test_buffer.py 
b/python/pyfory/tests/test_buffer.py
index cefd6abf5..f034c2c53 100644
--- a/python/pyfory/tests/test_buffer.py
+++ b/python/pyfory/tests/test_buffer.py
@@ -138,6 +138,7 @@ def check_varint32(buf: Buffer, value: int):
 
 @require_pyarrow
 def test_buffer_protocol():
+    # test buffer protocol compatibility with pyarrow
     buffer = Buffer.allocate(32)
     binary = b"b" * 100
     buffer.write_bytes_and_size(binary)


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to