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

xiaokang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-graphar.git


The following commit(s) were added to refs/heads/main by this push:
     new 31bab218 feat(python): build python wheel and public (#813)
31bab218 is described below

commit 31bab2188391e4dc5272cf7601bc838978959aa1
Author: Xiaokang Yang <[email protected]>
AuthorDate: Thu Dec 25 20:09:44 2025 +0800

    feat(python): build python wheel and public (#813)
    
    * add python wheel workflow
    
    * update workflow trigger and test
    
    * update
    
    * updae
    
    * update readme
    
    * fix docs
    
    * fix poetry lock
    
    * fix copilot comment
    
    * update
---
 .github/scripts/update_version.py           |  88 +++++++++
 .github/workflows/python-wheel-workflow.yml | 286 ++++++++++++++++++++++++++++
 cpp/CMakeLists.txt                          |  32 +++-
 python/CMakeLists.txt                       |  69 ++++++-
 python/README.md                            |   7 +
 python/poetry.lock                          |  13 +-
 python/pyproject.toml                       |  31 ++-
 python/src/cli/README.md                    |   9 +
 8 files changed, 513 insertions(+), 22 deletions(-)

diff --git a/.github/scripts/update_version.py 
b/.github/scripts/update_version.py
new file mode 100644
index 00000000..a677b862
--- /dev/null
+++ b/.github/scripts/update_version.py
@@ -0,0 +1,88 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+import json
+import os
+import re
+import sys
+import urllib.request
+from packaging.version import Version
+
+PACKAGE_NAME = "graphar"
+FILE_PATH = "python/pyproject.toml"
+URL_TIMEOUT_SECONDS = int(os.getenv("GRAPHAR_VERSION_FETCH_TIMEOUT", "10"))
+
+def get_next_version():
+    versions = []
+    urls = [
+        f"https://pypi.org/pypi/{PACKAGE_NAME}/json";,
+        f"https://test.pypi.org/pypi/{PACKAGE_NAME}/json";
+    ]
+    
+    print(f"Fetching versions for {PACKAGE_NAME}...")
+    for url in urls:
+        try:
+            with urllib.request.urlopen(url, timeout=URL_TIMEOUT_SECONDS) as r:
+                data = json.load(r)
+                versions.extend(data.get("releases", {}).keys())
+        except Exception as e:
+            print(
+                f"Warning: Failed to fetch versions from {url}: 
{type(e).__name__}: {e}",
+                file=sys.stderr,
+            )
+
+    if not versions:
+        return "0.0.1.dev1"
+
+    latest = max([Version(v) for v in versions])
+    print(f"Latest version found: {latest}")
+
+    if latest.is_devrelease:
+        dev_number = latest.dev if latest.dev is not None else 0
+        return f"{latest.major}.{latest.minor}.{latest.micro}.dev{dev_number + 
1}"
+    else:
+        return f"{latest.major}.{latest.minor}.{latest.micro + 1}.dev1"
+
+def main():
+    new_ver = get_next_version()
+    print(f"Target version: {new_ver}")
+
+    try:
+        with open(FILE_PATH, "r", encoding="utf-8") as f:
+            content = f.read()
+
+        new_content, count = re.subn(
+            r'(version\s*=\s*")([^"]+)(")', 
+            rf'\g<1>{new_ver}\g<3>', 
+            content
+        )
+        
+        if count == 0:
+            print(f"Error: Could not find 'version' key in {FILE_PATH}")
+            sys.exit(1)
+
+        with open(FILE_PATH, "w", encoding="utf-8") as f:
+            f.write(new_content)
+            
+        print(f"Successfully updated {FILE_PATH} to {new_ver}")
+        
+    except FileNotFoundError:
+        print(f"Error: File {FILE_PATH} not found.")
+        sys.exit(1)
+
+if __name__ == "__main__":
+    main()
diff --git a/.github/workflows/python-wheel-workflow.yml 
b/.github/workflows/python-wheel-workflow.yml
new file mode 100644
index 00000000..cdbe0702
--- /dev/null
+++ b/.github/workflows/python-wheel-workflow.yml
@@ -0,0 +1,286 @@
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+name: Build Python Wheels
+
+concurrency:
+  group: python-wheel-${{ github.ref }}
+  cancel-in-progress: false
+
+on:
+  # Trigger the workflow on push or pull request,
+  # but only for the main branch
+  push:
+    branches:
+      - "main"
+    paths:
+      - 'cpp/**'
+      - 'python/**'
+      - '.github/workflows/python-wheel-workflow.yml'
+      - '.github/scripts/update_version.py'
+  pull_request:
+    branches:
+      - "main"
+    paths:
+      - 'cpp/**'
+      - 'python/**'
+      - '.github/workflows/python-wheel-workflow.yml'
+      - '.github/scripts/update_version.py'
+  workflow_dispatch:
+    inputs:
+      publish_pypi:
+        description: "Publish to PyPI (manual runs only)"
+        required: true
+        default: false
+        type: boolean
+
+jobs:
+  build_sdist:
+    name: Build source distribution
+    runs-on: ubuntu-22.04
+    steps:
+      - uses: actions/checkout@v4
+
+      - name: Set up Python
+        uses: actions/setup-python@v5
+        with:
+          python-version: "3.9"
+
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip
+          pip install build twine
+      - name: update pyproject version
+        if: github.event_name != 'workflow_dispatch' || 
github.event.inputs.publish_pypi != 'true'
+        run: |
+          python .github/scripts/update_version.py
+      - name: Build sdist
+        run: |
+          # Bundle C++ sources into python/ so the sdist contains them.
+          rm -rf python/_bundled_cpp
+          cp -a cpp python/_bundled_cpp
+          cd python
+          python -m build --sdist
+      - name: Store artifacts
+        uses: actions/upload-artifact@v4
+        with:
+          name: sdist
+          path: python/dist/*
+
+  build_wheels:
+    name: Build wheels on ${{ matrix.runner }}
+    runs-on: ${{ matrix.runner }}
+    needs: build_sdist
+    strategy:
+      matrix:
+        include:
+          # Job 1: Native x86_64 build
+          - platform: x86_64
+            runner: ubuntu-latest # This is the standard x86_64 runner
+            os: linux
+            manylinux: _2_28
+            deployment-target: ''
+
+          # Job 2: Native aarch64 build
+          - platform: aarch64
+            runner: ubuntu-22.04-arm # This is a native ARM64 runner
+            os: linux
+            manylinux: _2_28
+            deployment-target: ''
+
+          # Job 3: macOS arm64 build
+          - platform: arm64
+            runner: macos-latest
+            os: macos
+            deployment-target: '11.0'
+
+
+    env:
+      CIBW_PLATFORM: ${{ matrix.os }}
+      CIBW_BUILD: "cp39-* cp310-* cp311-* cp312-* cp313-*"
+      CIBW_SKIP: "*-musllinux_*"
+      # Pin arch to the matrix platform
+      CIBW_ARCHS: ${{ matrix.platform }}
+      CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.os == 'linux' && 
format('manylinux{0}', matrix.manylinux) || '' }}
+      CIBW_MANYLINUX_AARCH64_IMAGE: ${{ matrix.os == 'linux' && 
format('manylinux{0}', matrix.manylinux) || '' }}
+      CIBW_ENVIRONMENT_WINDOWS: DISTUTILS_USE_SDK=1 MSSdk=1
+      CIBW_ENVIRONMENT_MACOS: ${{ matrix.os == 'macos' && 
format('MACOSX_DEPLOYMENT_TARGET={0} CMAKE_OSX_DEPLOYMENT_TARGET={0} 
CFLAGS=-mmacosx-version-min={0} CXXFLAGS=-mmacosx-version-min={0} 
LDFLAGS=-mmacosx-version-min={0}', matrix.deployment-target) || '' }}
+      CIBW_BEFORE_BUILD_LINUX: |
+        set -eux
+        if [ -f /etc/system-release-cpe ]; then
+          ALMA_MAJOR="$(cut -d: -f5 /etc/system-release-cpe | cut -d. -f1)"
+        else
+          . /etc/os-release
+          ALMA_MAJOR="${VERSION_ID%%.*}"
+        fi
+        dnf install -y 'dnf-command(config-manager)' || dnf install -y 
dnf-plugins-core || true
+        # Follow official Apache Arrow install instructions for 
AlmaLinux/RHEL-family
+        dnf install -y epel-release || \
+          dnf install -y oracle-epel-release-el${ALMA_MAJOR} || \
+          dnf install -y 
https://dl.fedoraproject.org/pub/epel/epel-release-latest-${ALMA_MAJOR}.noarch.rpm
+        dnf install -y 
https://packages.apache.org/artifactory/arrow/almalinux/${ALMA_MAJOR}/apache-arrow-release-latest.rpm
+        dnf config-manager --set-enabled epel || :
+        dnf config-manager --set-enabled powertools || :
+        dnf config-manager --set-enabled crb || :
+        dnf config-manager --set-enabled ol${ALMA_MAJOR}_codeready_builder || :
+        dnf config-manager --set-enabled 
codeready-builder-for-rhel-${ALMA_MAJOR}-rhui-rpms || :
+        subscription-manager repos --enable 
codeready-builder-for-rhel-${ALMA_MAJOR}-$(arch)-rpms || :
+        # manylinux images may carry older Arrow packages (e.g. arrow1700-*) 
which
+        # conflict with the newer packages from the Apache Arrow repo (e.g. 
arrow2200-*).
+        # Remove any preinstalled Arrow/Parquet RPMs so we install a 
consistent set.
+        dnf remove -y 'arrow*' 'parquet*' || true
+        # Required for GraphAr C++ build via Arrow CMake packages
+        dnf install -y --allowerasing \
+          arrow-devel \
+          arrow-dataset-devel \
+          arrow-acero-devel \
+          parquet-devel \
+          libcurl-devel re2-devel ccache
+    steps:
+      - name: Checkout (needed for some tooling)
+        uses: actions/checkout@v4
+
+      - name: Set up Python
+        uses: actions/setup-python@v5
+        with:
+          python-version: "3.9"
+
+      - name: Set up Miniconda (macOS/Windows)
+        if: matrix.os == 'windows' || matrix.os == 'macos'
+        uses: conda-incubator/setup-miniconda@v3
+        with:
+          auto-activate-base: true
+          miniforge-version: latest
+          use-mamba: true
+
+      - name: Install Arrow (macOS)
+        if: matrix.os == 'macos'
+        shell: bash
+        run: |
+          set -euxo pipefail
+          mamba install -y -c conda-forge arrow-cpp
+          # Note: CONDA_PREFIX may be unset unless conda is activated in this 
shell.
+          # setup-miniconda exports CONDA (base install prefix), which is 
sufficient here.
+          echo "CMAKE_PREFIX_PATH=$CONDA" >> "$GITHUB_ENV"
+          # Optional sanity check: ensure Arrow dylib isn't built for a newer 
macOS than deployment target.
+          if command -v otool >/dev/null 2>&1; then
+            ls -lah "$CONDA/lib" || true
+            if [ -f "$CONDA/lib/libarrow.dylib" ]; then
+              otool -l "$CONDA/lib/libarrow.dylib" | (grep -A3 -E 
'LC_BUILD_VERSION|LC_VERSION_MIN_MACOSX' || true)
+            fi
+          fi
+      - name: Install Arrow (Windows)
+        if: matrix.os == 'windows'
+        shell: pwsh
+        run: |
+          mamba install -y -c conda-forge arrow-cpp
+          Add-Content $env:GITHUB_ENV 
"CMAKE_PREFIX_PATH=$env:CONDA_PREFIX\\Library"
+          Add-Content $env:GITHUB_ENV 
"PATH=$env:CONDA_PREFIX\\Library\\bin;$env:PATH"
+      - name: Download sdist artifact
+        uses: actions/download-artifact@v4
+        with:
+          name: sdist
+          path: sdist
+
+      - name: Extract sdist
+        shell: bash
+        run: |
+          set -euxo pipefail
+          ls -lah sdist
+          SDIST_FILE=""
+          for f in sdist/*.tar.gz sdist/*.zip; do
+            if [ -f "$f" ]; then
+              SDIST_FILE="$f"
+              break
+            fi
+          done
+          if [ -z "$SDIST_FILE" ]; then
+            echo "No sdist file found in sdist/" >&2
+            exit 1
+          fi
+          mkdir -p sdist_pkg
+          case "$SDIST_FILE" in
+            *.tar.gz) tar -xzf "$SDIST_FILE" -C sdist_pkg ;;
+            *.zip) unzip -q "$SDIST_FILE" -d sdist_pkg ;;
+          esac
+          PKGDIR="$(find sdist_pkg -mindepth 1 -maxdepth 1 -type d | head -n 
1)"
+          if [ -z "$PKGDIR" ]; then
+            echo "Failed to locate extracted sdist directory" >&2
+            exit 1
+          fi
+          echo "PKGDIR=$PKGDIR" >> "$GITHUB_ENV"
+      - name: Build wheels
+        shell: bash
+        run: |
+          set -euxo pipefail
+          python -m pip install --upgrade pip
+          python -m pip install packaging cibuildwheel
+          mkdir -p python/dist
+          python -m cibuildwheel --output-dir python/dist "$PKGDIR"
+      - name: Store artifacts
+        uses: actions/upload-artifact@v4
+        with:
+          name: wheels-${{ matrix.os }}-${{ matrix.platform }}
+          path: python/dist/*
+
+  upload_test_pypi:
+    name: Publish to TestPyPI (auto)
+    needs: [build_wheels, build_sdist]
+    runs-on: ubuntu-22.04
+    if: github.event_name == 'push'
+    permissions:
+      contents: read
+      id-token: write
+    steps:
+      - name: Download artifacts
+        uses: actions/download-artifact@v4
+        with:
+          path: dist
+
+      - name: Move artifacts to correct location
+        run: |
+          mkdir -p python/dist
+          find dist -name "*" -type f -exec mv {} python/dist/ \;
+      - name: Publish to Test PyPI
+        uses: pypa/gh-action-pypi-publish@release/v1
+        with:
+          repository-url: https://test.pypi.org/legacy/
+          packages-dir: python/dist/
+
+  upload_pypi:
+    name: Publish (manual)
+    needs: [build_wheels, build_sdist]
+    runs-on: ubuntu-22.04
+    if: github.event_name == 'workflow_dispatch' && inputs.publish_pypi
+    permissions:
+      contents: read
+      id-token: write
+    steps:
+      - name: Download artifacts
+        uses: actions/download-artifact@v4
+        with:
+          path: dist
+
+      - name: Move artifacts to correct location
+        run: |
+          mkdir -p python/dist
+          find dist -name "*" -type f -exec mv {} python/dist/ \;
+      - name: Publish to PyPI
+        uses: pypa/gh-action-pypi-publish@release/v1
+        with:
+          packages-dir: python/dist/
\ No newline at end of file
diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt
index be080cd8..2d4e477b 100644
--- a/cpp/CMakeLists.txt
+++ b/cpp/CMakeLists.txt
@@ -102,12 +102,17 @@ if(NOT (CMAKE_CXX_COMPILER_LAUNCHER MATCHES "ccache") AND 
NOT (CMAKE_C_COMPILER_
     endif(ccache_EXECUTABLE)
 endif()
 
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -Wall")
+if(MSVC)
+    # Avoid GCC/Clang-specific flags on MSVC.
+    # C++17 is already enforced via CMAKE_CXX_STANDARD/target features.
+else()
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -Wall")
+endif()
 
 if (APPLE)
-  set(CMAKE_MACOSX_RPATH ON)
-else ()
-  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath,$ORIGIN")
+    set(CMAKE_MACOSX_RPATH ON)
+elseif(UNIX)
+    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath,$ORIGIN")
 endif ()
 
 set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g 
-fno-omit-frame-pointer -fsanitize=address")
@@ -290,6 +295,22 @@ macro(build_graphar)
                 ArrowDataset::arrow_dataset_shared
                 ArrowAcero::arrow_acero_shared)
         endif()
+    elseif(MSVC)
+        # MSVC doesn't support GNU ld-style -Wl flags. For shared libraries we
+        # don't need whole-archive semantics.
+        if(USE_STATIC_ARROW)
+            target_link_libraries(graphar PRIVATE
+                Arrow::arrow_static
+                Parquet::parquet_static
+                ArrowDataset::arrow_dataset_static
+                ArrowAcero::arrow_acero_static)
+        else()
+            target_link_libraries(graphar PRIVATE
+                Arrow::arrow_shared
+                Parquet::parquet_shared
+                ArrowDataset::arrow_dataset_shared
+                ArrowAcero::arrow_acero_shared)
+        endif()
     else()
         if(USE_STATIC_ARROW)
             target_link_libraries(graphar PRIVATE -Wl,--exclude-libs,ALL 
-Wl,--whole-archive
@@ -350,6 +371,9 @@ macro(build_graphar_with_arrow_bundled)
                 graphar_bundled_dependencies
                 "-framework CoreFoundation"
                 "-framework Security")
+    elseif(MSVC)
+        # MSVC doesn't support GNU ld-style -Wl flags.
+        target_link_libraries(graphar PRIVATE graphar_bundled_dependencies)
     else()
         target_link_libraries(graphar PRIVATE -Wl,--exclude-libs,ALL
                 graphar_bundled_dependencies)
diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt
index 7938fea0..e4bd63b6 100644
--- a/python/CMakeLists.txt
+++ b/python/CMakeLists.txt
@@ -27,7 +27,27 @@ project(
   LANGUAGES CXX)
 
 set(CMAKE_CXX_STANDARD 17)
-add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../cpp 
${CMAKE_BINARY_DIR}/graphar)
+
+# The Python wheel build (e.g. via cibuildwheel) may run from an isolated copy 
of
+# the `python/` directory, where `../cpp` doesn't exist. In CI we bundle `cpp/`
+# into the project directory as `_bundled_cpp/`.
+if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/_bundled_cpp/CMakeLists.txt")
+  set(GRAPHAR_CPP_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/_bundled_cpp")
+elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../cpp/CMakeLists.txt")
+  set(GRAPHAR_CPP_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../cpp")
+else()
+  message(FATAL_ERROR "GraphAr C++ sources not found. Expected either 
'_bundled_cpp/' or '../cpp/'.")
+endif()
+
+# Verify that the expected subdirectories exist before adding the C++ sources.
+if(NOT EXISTS "${GRAPHAR_CPP_SOURCE_DIR}/src")
+  message(FATAL_ERROR "GraphAr C++ sources are missing the 'src' directory 
under '${GRAPHAR_CPP_SOURCE_DIR}'.")
+endif()
+if(NOT EXISTS "${GRAPHAR_CPP_SOURCE_DIR}/thirdparty")
+  message(FATAL_ERROR "GraphAr C++ sources are missing the 'thirdparty' 
directory under '${GRAPHAR_CPP_SOURCE_DIR}'.")
+endif()
+
+add_subdirectory(${GRAPHAR_CPP_SOURCE_DIR} ${CMAKE_BINARY_DIR}/graphar)
 
 # Find the module development requirements (requires FindPython from 3.17 or
 # scikit-build-core's built-in backport)
@@ -37,6 +57,29 @@ find_package(Arrow REQUIRED)
 find_package(ArrowDataset REQUIRED)
 find_package(ArrowAcero REQUIRED)
 find_package(Parquet REQUIRED)
+
+if(APPLE)
+  # cibuildwheel uses delocate to bundle external dylibs into the wheel.
+  # delocate needs to be able to resolve @rpath dependencies (e.g.
+  # @rpath/libarrow.1300.dylib). We add the imported Arrow/Parquet library
+  # directories to LC_RPATH so delocate can find and copy them.
+  set(_graphar_macos_rpaths "")
+
+  foreach(_tgt IN ITEMS Arrow::arrow_shared ArrowDataset::arrow_dataset_shared 
ArrowAcero::arrow_acero_shared Parquet::parquet_shared)
+    if(TARGET ${_tgt})
+      get_target_property(_loc ${_tgt} IMPORTED_LOCATION_RELEASE)
+      if(NOT _loc)
+        get_target_property(_loc ${_tgt} IMPORTED_LOCATION)
+      endif()
+      if(_loc)
+        get_filename_component(_libdir "${_loc}" DIRECTORY)
+        list(APPEND _graphar_macos_rpaths "${_libdir}")
+      endif()
+    endif()
+  endforeach()
+
+  list(REMOVE_DUPLICATES _graphar_macos_rpaths)
+endif()
 # Check if ORC is enabled.
 if (NOT ${ARROW_ORC})
     message(WARNING "apache-arrow is built without ORC extension, ORC related 
functionalities will be disabled.")
@@ -57,18 +100,32 @@ target_link_libraries(_core PRIVATE pybind11::headers 
graphar Arrow::arrow_share
                                     ArrowAcero::arrow_acero_shared
                                     )
 target_include_directories(_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
-target_include_directories(_core PRIVATE 
${CMAKE_CURRENT_SOURCE_DIR}/../cpp/src)
-target_include_directories(_core PRIVATE 
${CMAKE_CURRENT_SOURCE_DIR}/../cpp/thirdparty)
+target_include_directories(_core PRIVATE ${GRAPHAR_CPP_SOURCE_DIR}/src)
+target_include_directories(_core PRIVATE ${GRAPHAR_CPP_SOURCE_DIR}/thirdparty)
 
 # This is passing in the version as a define just as an example
 target_compile_definitions(_core PRIVATE VERSION_INFO=${PROJECT_VERSION})
 
-# The install directory is the output (wheel) directory
+# The install directory is the output (wheel) directory.
 # Use platform-appropriate rpath so the Python extension can find the
 # packaged libgraphar at runtime inside the wheel.
 if(APPLE)
-  # macOS uses @loader_path to find libraries relative to the module
-  set_target_properties(_core PROPERTIES INSTALL_RPATH "@loader_path")
+  # Keep @loader_path for runtime inside the wheel, and add Arrow/Parquet
+  # library dirs so delocate can resolve @rpath dependencies during repair.
+  set(_core_rpath "@loader_path")
+  if(_graphar_macos_rpaths)
+    list(JOIN _graphar_macos_rpaths ";" _graphar_macos_rpaths_joined)
+    set(_core_rpath "${_core_rpath};${_graphar_macos_rpaths_joined}")
+  endif()
+  set_target_properties(_core PROPERTIES BUILD_WITH_INSTALL_RPATH ON)
+  set_target_properties(_core PROPERTIES INSTALL_RPATH "${_core_rpath}")
+
+  if(TARGET graphar)
+    set_target_properties(graphar PROPERTIES BUILD_WITH_INSTALL_RPATH ON)
+    if(_graphar_macos_rpaths)
+      set_target_properties(graphar PROPERTIES INSTALL_RPATH 
"${_graphar_macos_rpaths_joined}")
+    endif()
+  endif()
 else()
   # On Linux and other Unix, use $ORIGIN (escaped so CMake preserves the $)
   set_target_properties(_core PROPERTIES INSTALL_RPATH "\$ORIGIN")
diff --git a/python/README.md b/python/README.md
index 227d5a47..c6a5e8ff 100644
--- a/python/README.md
+++ b/python/README.md
@@ -11,6 +11,13 @@ GraphAr Python SDK provides Python bindings for the GraphAr 
C++ library, allowin
 - CMake >= 3.15 (for building from source)
 - Apache Arrow >= 12.0 (for building from source)
 
+### Install from Pypi
+Install the latest released version from PyPI:
+
+```bash
+pip install -U graphar
+```
+
 ### Install from Source
 
 Clone the repository and install the Python package:
diff --git a/python/poetry.lock b/python/poetry.lock
index f040da77..2a24329d 100644
--- a/python/poetry.lock
+++ b/python/poetry.lock
@@ -108,7 +108,7 @@ version = "3.1.6"
 description = "A very fast and expressive template engine."
 optional = false
 python-versions = ">=3.7"
-groups = ["docs"]
+groups = ["main", "docs"]
 files = [
     {file = "jinja2-3.1.6-py3-none-any.whl", hash = 
"sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"},
     {file = "jinja2-3.1.6.tar.gz", hash = 
"sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"},
@@ -177,7 +177,7 @@ version = "2.5.4"
 description = "A fast and complete Python implementation of Markdown"
 optional = false
 python-versions = "<4,>=3.9"
-groups = ["docs"]
+groups = ["main", "docs"]
 markers = "python_version == \"3.11\""
 files = [
     {file = "markdown2-2.5.4-py3-none-any.whl", hash = 
"sha256:3c4b2934e677be7fec0e6f2de4410e116681f4ad50ec8e5ba7557be506d3f439"},
@@ -196,7 +196,7 @@ version = "3.0.3"
 description = "Safely add untrusted strings to HTML/XML markup."
 optional = false
 python-versions = ">=3.9"
-groups = ["docs"]
+groups = ["main", "docs"]
 files = [
     {file = "markupsafe-3.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = 
"sha256:2f981d352f04553a7171b8e44369f2af4055f888dfb147d55e42d29e29e74559"},
     {file = "markupsafe-3.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = 
"sha256:e1c1493fb6e50ab01d20a22826e57520f1284df32f2d8601fdd90b6304601419"},
@@ -561,7 +561,7 @@ version = "15.0.4"
 description = "API Documentation for Python Projects"
 optional = false
 python-versions = ">=3.9"
-groups = ["docs"]
+groups = ["main", "docs"]
 markers = "python_version < \"3.11\" or python_version >= \"3.12\""
 files = [
     {file = "pdoc-15.0.4-py3-none-any.whl", hash = 
"sha256:f9028e85e7bb8475b054e69bde1f6d26fc4693d25d9fa1b1ce9009bec7f7a5c4"},
@@ -579,7 +579,7 @@ version = "16.0.0"
 description = "API Documentation for Python Projects"
 optional = false
 python-versions = ">=3.10"
-groups = ["docs"]
+groups = ["main", "docs"]
 markers = "python_version == \"3.11\""
 files = [
     {file = "pdoc-16.0.0-py3-none-any.whl", hash = 
"sha256:070b51de2743b9b1a4e0ab193a06c9e6c12cf4151cf9137656eebb16e8556628"},
@@ -1048,9 +1048,10 @@ files = [
 ]
 
 [extras]
+docs = ["pdoc"]
 test = ["pandas", "typing_extensions"]
 
 [metadata]
 lock-version = "2.1"
 python-versions = ">=3.9"
-content-hash = 
"7c0d5b634b473bab1d16f5587ef80a0db3ff4fcc969ad15bd585b5c5c854d134"
+content-hash = 
"6cc0ac1c9c1fc1c375be6f187e8e20a932a0a6daf0a30a2fc6574567feb30948"
diff --git a/python/pyproject.toml b/python/pyproject.toml
index a09e68d4..5f3c6e96 100644
--- a/python/pyproject.toml
+++ b/python/pyproject.toml
@@ -16,18 +16,28 @@
 # under the License.
 
 [build-system]
-requires = ["scikit-build-core>=0.3.3", "pybind11", "ninja ~= 1.11"]
+requires = ["scikit-build-core>=0.8.3", "pybind11", "ninja ~= 1.11"]
 build-backend = "scikit_build_core.build"
 
 
 [project]
 name = "graphar"
-version = "0.13.0"
-description = "GraphAr command line tool"
+version = "0.13.0-dev"
+description = "An open source, standard data file format for graph data 
storage and retrieval."
 readme = "README.md"
 authors = [{ name = "GraphAr community", email = "[email protected]" }]
 requires-python = ">=3.9"
 dependencies = ["typer ~= 0.1", "pydantic ~= 2.0, < 2.12", "pyyaml ~= 6.0", 
"pytest ~= 7.2"]
+license = {text = "Apache-2.0"}
+classifiers = [
+    "License :: OSI Approved :: Apache Software License",
+    "Programming Language :: Python :: 3",
+    "Programming Language :: Python :: 3.9",
+    "Programming Language :: Python :: 3.10",
+    "Programming Language :: Python :: 3.11",
+    "Programming Language :: Python :: 3.12",
+    "Programming Language :: Python :: 3.13",
+]
 
 [tool.poetry.group.docs]
 optional = true
@@ -35,12 +45,21 @@ optional = true
 [tool.poetry.group.docs.dependencies]
 pdoc = "*"
 
-[project.optional-dependencies]
-test = ["pandas ~= 2.0", "typing_extensions ~= 4.0"]
-
 [tool.scikit-build]
 build-dir = "build"
 
+[tool.scikit-build.sdist]
+include = [
+  "../cpp/**",
+  "../CMakeLists.txt",
+  "../LICENSE",
+  "../NOTICE",
+]
+
+[project.optional-dependencies]
+test = ["pandas ~= 2.0", "typing_extensions ~= 4.0"]
+docs = ["pdoc"]
+
 [project.scripts]
 graphar = "cli.graphar_cli:main"
 
diff --git a/python/src/cli/README.md b/python/src/cli/README.md
index 22e68b1d..e5f78426 100644
--- a/python/src/cli/README.md
+++ b/python/src/cli/README.md
@@ -21,6 +21,15 @@ And using Python in conda or venv is a good choice.
 
 ## Installation
 
+### Install from Pypi
+Install the latest released version from PyPI:
+
+```bash
+pip install -U graphar
+```
+
+### Install from Source
+
 - Clone this repository
 - `pip install ./python` or set verbose level `pip install -v ./python`
 


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

Reply via email to