diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 1faaf57..903dff0 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -1,116 +1,20 @@ name: Build, Test, and Publish on: - push: - branches: [main] pull_request: - branches: [main] + push: release: types: [published] workflow_dispatch: jobs: - build_wheels: - name: Build (${{ matrix.os }} / ${{ matrix.arch }} / py${{ matrix.python }}) - runs-on: ${{ matrix.runner }} - container: ${{ matrix.container || '' }} + build_linux_x86_64_wheels: + name: Build (linux / x86_64 / py${{ matrix.python }}) + runs-on: ubuntu-latest strategy: fail-fast: false matrix: - include: - # Linux x86_64 (manylinux_2_28 container) - - { - os: linux, - arch: x86_64, - runner: ubuntu-latest, - container: "quay.io/pypa/manylinux_2_28_x86_64", - python: "3.9", - } - - { - os: linux, - arch: x86_64, - runner: ubuntu-latest, - container: "quay.io/pypa/manylinux_2_28_x86_64", - python: "3.10", - } - - { - os: linux, - arch: x86_64, - runner: ubuntu-latest, - container: "quay.io/pypa/manylinux_2_28_x86_64", - python: "3.11", - } - - { - os: linux, - arch: x86_64, - runner: ubuntu-latest, - container: "quay.io/pypa/manylinux_2_28_x86_64", - python: "3.12", - } - - { - os: linux, - arch: x86_64, - runner: ubuntu-latest, - container: "quay.io/pypa/manylinux_2_28_x86_64", - python: "3.13", - } - - { - os: linux, - arch: x86_64, - runner: ubuntu-latest, - container: "quay.io/pypa/manylinux_2_28_x86_64", - python: "3.14", - } - # Linux aarch64 (manylinux_2_28 container) - - { - os: linux, - arch: aarch64, - runner: ubuntu-24.04-arm, - container: "quay.io/pypa/manylinux_2_28_aarch64", - python: "3.9", - } - - { - os: linux, - arch: aarch64, - runner: ubuntu-24.04-arm, - container: "quay.io/pypa/manylinux_2_28_aarch64", - python: "3.10", - } - - { - os: linux, - arch: aarch64, - runner: ubuntu-24.04-arm, - container: "quay.io/pypa/manylinux_2_28_aarch64", - python: "3.11", - } - - { - os: linux, - arch: aarch64, - runner: ubuntu-24.04-arm, - container: "quay.io/pypa/manylinux_2_28_aarch64", - python: "3.12", - } - - { - os: linux, - arch: aarch64, - runner: ubuntu-24.04-arm, - container: "quay.io/pypa/manylinux_2_28_aarch64", - python: "3.13", - } - - { - os: linux, - arch: aarch64, - runner: ubuntu-24.04-arm, - container: "quay.io/pypa/manylinux_2_28_aarch64", - python: "3.14", - } - # macOS arm64 - - { os: macos, arch: arm64, runner: macos-latest, python: "3.9" } - - { os: macos, arch: arm64, runner: macos-latest, python: "3.10" } - - { os: macos, arch: arm64, runner: macos-latest, python: "3.11" } - - { os: macos, arch: arm64, runner: macos-latest, python: "3.12" } - - { os: macos, arch: arm64, runner: macos-latest, python: "3.13" } - - { os: macos, arch: arm64, runner: macos-latest, python: "3.14" } + python: ["3.10", "3.11", "3.12", "3.13"] steps: - name: Checkout @@ -126,83 +30,88 @@ jobs: - name: Set up Python run: uv python install ${{ matrix.python }} - # ────────────────────────────────────────────── - # 1. System deps - # ────────────────────────────────────────────── - - - name: Install system deps (Linux manylinux) - if: runner.os == 'Linux' - run: | - yum -y install \ - git curl ca-certificates \ - zip unzip tar \ - cmake ninja-build pkgconfig \ - gcc gcc-c++ make \ - perl perl-IPC-Cmd perl-ExtUtils-MakeMaker \ - kernel-headers - - - name: Sanity check compilers - if: runner.os == 'Linux' + - name: Install system deps run: | - gcc --version - g++ --version - perl -MIPC::Cmd -e 'print "IPC::Cmd OK\n"' - - - name: Install system deps (macOS) - if: runner.os == 'macOS' - run: brew install ninja cmake curl openssl + sudo apt-get update + sudo apt-get install -y \ + cmake ninja-build \ + pkg-config patchelf \ + libx11-dev libx11-xcb-dev libfontenc-dev \ + libice-dev libsm-dev libxau-dev libxaw7-dev \ + libxcomposite-dev libxcursor-dev libxdamage-dev \ + libxdmcp-dev libxext-dev libxfixes-dev libxi-dev \ + libxinerama-dev libxkbfile-dev libxmu-dev \ + libxmuu-dev libxpm-dev libxrandr-dev libxrender-dev \ + libxres-dev libxss-dev libxt-dev libxtst-dev \ + libxv-dev libxxf86vm-dev libxcb-glx0-dev \ + libxcb-render0-dev libxcb-render-util0-dev \ + libxcb-xkb-dev libxcb-icccm4-dev libxcb-image0-dev \ + libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev \ + libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev \ + libxcb-dri3-dev uuid-dev libxcb-cursor-dev \ + libxcb-dri2-0-dev libxcb-present-dev \ + libxcb-composite0-dev libxcb-ewmh-dev libxcb-res0-dev \ + libasound2-dev - name: Install Python tools run: | - uv venv --python ${{ matrix.python }} - echo "${GITHUB_WORKSPACE}/.venv/bin" >> $GITHUB_PATH - uv pip install \ - scikit-build-core setuptools-scm h5py \ - build auditwheel delocate - - # ────────────────────────────────────────────── - # 2. vcpkg: install C++ deps (cached per platform) - # ────────────────────────────────────────────── - - - name: Bootstrap vcpkg - run: | - git clone https://github.com/microsoft/vcpkg.git ${GITHUB_WORKSPACE}/vcpkg - ${GITHUB_WORKSPACE}/vcpkg/bootstrap-vcpkg.sh - echo "VCPKG_ROOT=${GITHUB_WORKSPACE}/vcpkg" >> $GITHUB_ENV - echo "CMAKE_TOOLCHAIN_FILE=${GITHUB_WORKSPACE}/vcpkg/scripts/buildsystems/vcpkg.cmake" >> $GITHUB_ENV - - - name: Cache vcpkg packages + uv venv --clear --python ${{ matrix.python }} + echo "${{ github.workspace }}/.venv/bin" >> "$GITHUB_PATH" + uv pip install --python .venv/bin/python \ + scikit-build-core \ + setuptools-scm \ + h5py \ + build \ + auditwheel \ + delocate + + - name: Cache Conan packages uses: actions/cache@v4 with: - path: ~/.cache/vcpkg/archives - key: vcpkg-${{ matrix.os }}-${{ matrix.arch }}-${{ hashFiles('lib/vcpkg.json') }} - restore-keys: vcpkg-${{ matrix.os }}-${{ matrix.arch }}- + path: ~/.conan2 + key: conan-linux-x86_64-py${{ matrix.python }}-${{ hashFiles('lib/conanfile.py', 'pyproject.toml') }} + restore-keys: | + conan-linux-x86_64- + + - name: Conan install + working-directory: lib + run: | + uv tool install conan + conan profile detect --force + conan lock create . -s build_type=Release --lockfile-out=conan.lock + conan install . --lockfile=conan.lock --build=missing -of build \ + -c tools.system.package_manager:mode=install \ + -c tools.system.package_manager:sudo=True + + - name: Find Conan toolchain + shell: bash + run: | + set -euo pipefail + TOOLCHAIN=$(find "${{ github.workspace }}/lib/build" -name "conan_toolchain.cmake" | head -1) + if [ -z "$TOOLCHAIN" ]; then + echo "ERROR: conan_toolchain.cmake not found" + find "${{ github.workspace }}/lib/build" -type f -name "*.cmake" || true + exit 1 + fi + echo "CMAKE_TOOLCHAIN_FILE=$TOOLCHAIN" >> "$GITHUB_ENV" - # ────────────────────────────────────────────── - # 3. Discover h5py HDF5 + build wheel - # ────────────────────────────────────────────── - name: Discover h5py HDF5 location run: | - HDF5_DIR=$(${GITHUB_WORKSPACE}/.venv/bin/python -c " + H5PY_HDF5_DIR=$(.venv/bin/python -c " import h5py, os d = os.path.dirname(h5py.__file__) dylibs = os.path.join(d, '.dylibs') libs = os.path.join(os.path.dirname(d), 'h5py.libs') print(dylibs if os.path.exists(dylibs) else libs) ") - echo "HDF5_DIR=$HDF5_DIR" >> $GITHUB_ENV + echo "H5PY_HDF5_DIR=$H5PY_HDF5_DIR" >> "$GITHUB_ENV" - name: Build wheel - run: uv build --wheel --no-build-isolation --python ${GITHUB_WORKSPACE}/.venv/bin/python - - # ────────────────────────────────────────────── - # 4. Repair wheel for PyPI - # ────────────────────────────────────────────── + run: uv build --wheel --no-build-isolation --python ${{ github.workspace }}/.venv/bin/python - - name: Repair wheel (Linux) - if: runner.os == 'Linux' + - name: Repair wheel run: | - export LD_LIBRARY_PATH="${HDF5_DIR}:${LD_LIBRARY_PATH}" + export LD_LIBRARY_PATH="${H5PY_HDF5_DIR}:${LD_LIBRARY_PATH}" auditwheel show dist/*.whl auditwheel repair dist/*.whl -w wheelhouse/ \ --exclude libhdf5.so \ @@ -210,69 +119,142 @@ jobs: --exclude libhdf5_hl.so \ --exclude libhdf5_hl.so.310 - - name: Repair wheel (macOS) - if: runner.os == 'macOS' - run: | - export DYLD_LIBRARY_PATH="${HDF5_DIR}:${DYLD_LIBRARY_PATH}" - delocate-listdeps dist/*.whl - delocate-wheel -w wheelhouse/ dist/*.whl \ - --exclude libhdf5 \ - --exclude libhdf5_hl - - # ────────────────────────────────────────────── - # 5. Test + upload - # ────────────────────────────────────────────── - - name: Smoke test run: | - uv pip install wheelhouse/*.whl --force-reinstall - python3 -c "import arraymorph; print('arraymorph imported successfully')" + uv venv .test-venv --clear --python ${{ matrix.python }} + uv pip install --python .test-venv/bin/python wheelhouse/*.whl --force-reinstall + .test-venv/bin/python -c "import arraymorph; print('arraymorph imported successfully')" - name: Upload wheel artifact uses: actions/upload-artifact@v4 with: - name: wheels-${{ matrix.os }}-${{ matrix.arch }}-py${{ matrix.python }} + name: wheels-linux-x86_64-py${{ matrix.python }} path: wheelhouse/*.whl retention-days: 7 - - name: Extract native library from wheel - if: github.event_name == 'release' && matrix.python == '3.12' + build_linux_aarch64_wheels: + name: Build (linux / aarch64 / py${{ matrix.python }}) + runs-on: ubuntu-24.04-arm + strategy: + fail-fast: false + matrix: + python: ["3.10", "3.11", "3.12", "3.13"] + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install uv + uses: astral-sh/setup-uv@v7 + with: + version: "0.10.6" + + - name: Set up Python + run: uv python install ${{ matrix.python }} + + - name: Install system deps + run: | + sudo apt-get update + sudo apt-get install -y \ + cmake ninja-build \ + pkg-config patchelf \ + libx11-dev libx11-xcb-dev libfontenc-dev \ + libice-dev libsm-dev libxau-dev libxaw7-dev \ + libxcomposite-dev libxcursor-dev libxdamage-dev \ + libxdmcp-dev libxext-dev libxfixes-dev libxi-dev \ + libxinerama-dev libxkbfile-dev libxmu-dev \ + libxmuu-dev libxpm-dev libxrandr-dev libxrender-dev \ + libxres-dev libxss-dev libxt-dev libxtst-dev \ + libxv-dev libxxf86vm-dev libxcb-glx0-dev \ + libxcb-render0-dev libxcb-render-util0-dev \ + libxcb-xkb-dev libxcb-icccm4-dev libxcb-image0-dev \ + libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev \ + libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev \ + libxcb-dri3-dev uuid-dev libxcb-cursor-dev \ + libxcb-dri2-0-dev libxcb-present-dev \ + libxcb-composite0-dev libxcb-ewmh-dev libxcb-res0-dev \ + libasound2-dev + + - name: Install Python tools + run: | + uv venv --clear --python ${{ matrix.python }} + echo "${{ github.workspace }}/.venv/bin" >> "$GITHUB_PATH" + uv pip install --python .venv/bin/python \ + scikit-build-core \ + setuptools-scm \ + h5py \ + build \ + auditwheel \ + delocate + + - name: Cache Conan packages + uses: actions/cache@v4 + with: + path: ~/.conan2 + key: conan-linux-aarch64-py${{ matrix.python }}-${{ hashFiles('lib/conanfile.py', 'pyproject.toml') }} + restore-keys: | + conan-linux-aarch64- + + - name: Conan install + working-directory: lib + run: | + uv tool install conan + conan profile detect --force + conan lock create . -s build_type=Release --lockfile-out=conan.lock + conan install . --lockfile=conan.lock --build=missing -of build \ + -c tools.system.package_manager:mode=install \ + -c tools.system.package_manager:sudo=True + + - name: Find Conan toolchain shell: bash run: | - wheel_file=$(ls wheelhouse/*.whl | head -1) - mkdir -p extracted_lib - unzip -j "$wheel_file" "arraymorph/lib/lib_arraymorph*" -d extracted_lib/ - lib_file=$(ls extracted_lib/lib_arraymorph*) - ext="${lib_file##*.}" - cp "$lib_file" "lib_arraymorph-${{ matrix.os }}-${{ matrix.arch }}.$ext" - echo "LIB_ARTIFACT=lib_arraymorph-${{ matrix.os }}-${{ matrix.arch }}.$ext" >> $GITHUB_ENV - - - name: Fix HDF5 paths in standalone binary (macOS) - if: runner.os == 'macOS' && github.event_name == 'release' && matrix.python == '3.12' + set -euo pipefail + TOOLCHAIN=$(find "${{ github.workspace }}/lib/build" -name "conan_toolchain.cmake" | head -1) + if [ -z "$TOOLCHAIN" ]; then + echo "ERROR: conan_toolchain.cmake not found" + find "${{ github.workspace }}/lib/build" -type f -name "*.cmake" || true + exit 1 + fi + echo "CMAKE_TOOLCHAIN_FILE=$TOOLCHAIN" >> "$GITHUB_ENV" + + - name: Discover h5py HDF5 location run: | - LIB="lib_arraymorph-${{ matrix.os }}-${{ matrix.arch }}.dylib" - HDF5_REF=$(otool -L "$LIB" | grep libhdf5 | awk '{print $1}') - HDF5_FILENAME=$(basename "$HDF5_REF") - install_name_tool -change "$HDF5_REF" "@rpath/$HDF5_FILENAME" "$LIB" - echo "Fixed: $HDF5_REF → @rpath/$HDF5_FILENAME" - otool -L "$LIB" | grep hdf5 - - - name: Fix HDF5 paths in standalone binary (Linux) - if: runner.os == 'Linux' && github.event_name == 'release' && matrix.python == '3.12' + H5PY_HDF5_DIR=$(.venv/bin/python -c " + import h5py, os + d = os.path.dirname(h5py.__file__) + dylibs = os.path.join(d, '.dylibs') + libs = os.path.join(os.path.dirname(d), 'h5py.libs') + print(dylibs if os.path.exists(dylibs) else libs) + ") + echo "H5PY_HDF5_DIR=$H5PY_HDF5_DIR" >> "$GITHUB_ENV" + + - name: Build wheel + run: uv build --wheel --no-build-isolation --python ${{ github.workspace }}/.venv/bin/python + + - name: Repair wheel + run: | + export LD_LIBRARY_PATH="${H5PY_HDF5_DIR}:${LD_LIBRARY_PATH}" + auditwheel show dist/*.whl + auditwheel repair dist/*.whl -w wheelhouse/ \ + --exclude libhdf5.so \ + --exclude libhdf5.so.310 \ + --exclude libhdf5_hl.so \ + --exclude libhdf5_hl.so.310 + + - name: Smoke test run: | - LIB="lib_arraymorph-${{ matrix.os }}-${{ matrix.arch }}.so" - patchelf --replace-needed \ - "$(patchelf --print-needed "$LIB" | grep libhdf5)" \ - "libhdf5.so" \ - "$LIB" - echo "Fixed HDF5 dependency" - ldd "$LIB" | grep hdf5 || patchelf --print-needed "$LIB" | grep hdf5 - - - name: Attach native library to GitHub release - if: github.event_name == 'release' && matrix.python == '3.12' - uses: softprops/action-gh-release@v2 + uv venv .test-venv --clear --python ${{ matrix.python }} + uv pip install --python .test-venv/bin/python wheelhouse/*.whl --force-reinstall + .test-venv/bin/python -c "import arraymorph; print('arraymorph imported successfully')" + + - name: Upload wheel artifact + uses: actions/upload-artifact@v4 with: - files: ${{ env.LIB_ARTIFACT }} + name: wheels-linux-aarch64-py${{ matrix.python }} + path: wheelhouse/*.whl + retention-days: 7 build_sdist: name: Build sdist @@ -303,9 +285,9 @@ jobs: publish_test: name: Publish to TestPyPI - needs: [build_wheels, build_sdist] + needs: [build_linux_x86_64_wheels, build_linux_aarch64_wheels, build_sdist] runs-on: ubuntu-latest - if: github.event_name == 'release' + if: github.event_name == 'pull_request' || github.event_name == 'push' || github.event_name == 'release' permissions: id-token: write environment: @@ -332,17 +314,17 @@ jobs: path: dist/ - name: Publish to TestPyPI - run: uv publish dist/* --publish-url https://test.pypi.org/legacy/ + run: uv publish dist/* --publish-url https://test.pypi.org/legacy/ --check-url https://test.pypi.org/simple/ test_testpypi: name: Test TestPyPI (${{ matrix.os }}) needs: [publish_test] runs-on: ${{ matrix.runner }} strategy: + fail-fast: false matrix: include: - { os: linux, runner: ubuntu-latest } - - { os: macos, runner: macos-latest } steps: - name: Install uv @@ -355,17 +337,14 @@ jobs: - name: Install from TestPyPI run: | - uv venv --python 3.12 - source .venv/bin/activate - uv pip install \ + uv venv --clear --python 3.12 + uv pip install --python .venv/bin/python \ --index-url https://test.pypi.org/simple/ \ --extra-index-url https://pypi.org/simple/ \ arraymorph - name: Test - run: | - source .venv/bin/activate - python -c "import arraymorph; print('arraymorph imported successfully')" + run: .venv/bin/python -c "import arraymorph; print('arraymorph imported successfully')" publish: name: Publish to PyPI diff --git a/.github/workflows/conan-lock-test.yaml b/.github/workflows/conan-lock-test.yaml new file mode 100644 index 0000000..a3cb662 --- /dev/null +++ b/.github/workflows/conan-lock-test.yaml @@ -0,0 +1,48 @@ +name: Conan Lock Test + +on: + workflow_dispatch: + pull_request: + branches: [main] + paths: + - "Dockerfile.conan-lock" + - "Dockerfile" + - "lib/**" + - "src/**" + - "pyproject.toml" + - ".github/workflows/conan-lock-test.yaml" + push: + branches: [main] + paths: + - "Dockerfile.conan-lock" + - "Dockerfile" + - "lib/**" + - "src/**" + - "pyproject.toml" + - ".github/workflows/conan-lock-test.yaml" + +jobs: + docker_conan_lock: + name: Docker Conan lock build + runs-on: ubuntu-latest + permissions: + contents: read + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build Dockerfile.conan-lock + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile.conan-lock + platforms: linux/amd64 + push: false + load: false + cache-from: type=gha + cache-to: type=gha,mode=max + tags: arraymorph-conan-lock-test:ci diff --git a/.python-version b/.python-version index bd28b9c..c8cfe39 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.9 +3.10 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..7d1ad36 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,102 @@ +# ----------------------------- +# Stage 1: build +# ----------------------------- +FROM ubuntu:24.04 AS builder + +ENV DEBIAN_FRONTEND=noninteractive +WORKDIR /work + +RUN apt-get update && apt-get install -y \ + build-essential \ + cmake \ + ninja-build \ + pkg-config \ + git \ + curl \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +# Install uv + +COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ + +COPY pyproject.toml README.md /work/ +COPY lib/conanfile.py /work/lib/conanfile.py + +# Create environment +RUN uv python install 3.10 +RUN uv venv --python 3.10 + +# Install build deps +RUN uv pip install --python .venv/bin/python \ + build \ + scikit-build-core \ + conan \ + 'h5py>=3.10,<3.16' \ + ninja \ + cmake + +# Conan +RUN --mount=type=cache,target=/root/.conan2 \ + .venv/bin/conan profile detect --force +RUN --mount=type=cache,target=/root/.conan2 \ + .venv/bin/conan install lib --build=missing -s build_type=Release -of /work/lib/build + +COPY src /work/src +COPY lib/CMakeLists.txt /work/lib/CMakeLists.txt +COPY lib/include /work/lib/include +COPY lib/src /work/lib/src + +# Build wheel +RUN --mount=type=cache,target=/root/.conan2 \ + .venv/bin/python -c 'import h5py, os; d=os.path.dirname(h5py.__file__); libdir=os.path.join(d, ".dylibs") if os.path.exists(os.path.join(d, ".dylibs")) else os.path.join(os.path.dirname(d), "h5py.libs"); print(f"h5py {h5py.__version__} -> {libdir}")' && \ + export H5PY_HDF5_DIR="$(.venv/bin/python -c 'import h5py, os; d=os.path.dirname(h5py.__file__); print(os.path.join(d, ".dylibs") if os.path.exists(os.path.join(d, ".dylibs")) else os.path.join(os.path.dirname(d), "h5py.libs"))')" && \ + export CMAKE_ARGS="-DCMAKE_TOOLCHAIN_FILE=/work/lib/build/build/Release/generators/conan_toolchain.cmake" && \ + .venv/bin/python -m build --wheel --no-isolation + +# ----------------------------- +# Stage 2: runtime +# ----------------------------- +FROM ubuntu:24.04 + +ENV DEBIAN_FRONTEND=noninteractive +WORKDIR /app + +RUN apt-get update && apt-get install -y \ + curl \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/ +COPY --from=builder /work/dist /app/dist + +RUN uv venv --python 3.10 +RUN uv pip install --python .venv/bin/python dist/*.whl + +ENV STORAGE_PLATFORM=S3 +ENV AWS_REGION=us-east-1 + +COPY ./examples/python/write.py ./test.py + +CMD bash -lc '\ + set -e; \ + PLUGIN_PATH=$(./.venv/bin/python -c "import arraymorph; print(arraymorph.get_plugin_path())"); \ + PLUGIN_DIR=$(./.venv/bin/python -c "import arraymorph; print(arraymorph.get_plugin_dir())"); \ + H5PY_DIR=$(./.venv/bin/python -c "import h5py, os; d=os.path.dirname(h5py.__file__); print(os.path.join(d,\".dylibs\") if os.path.exists(os.path.join(d,\".dylibs\")) else os.path.join(os.path.dirname(d),\"h5py.libs\"))"); \ + export HDF5_PLUGIN_PATH="$PLUGIN_DIR"; \ + export HDF5_VOL_CONNECTOR="arraymorph under_vol=0;under_info={}"; \ + export LD_LIBRARY_PATH="$H5PY_DIR:${LD_LIBRARY_PATH:-}"; \ + echo "plugin path: $PLUGIN_PATH"; \ + echo "plugin dir: $PLUGIN_DIR"; \ + echo "h5py dir: $H5PY_DIR"; \ + echo "--- plugin dir ---"; \ + ls -la "$PLUGIN_DIR"; \ + echo "--- h5py dir ---"; \ + ls -la "$H5PY_DIR"; \ + echo "--- ldd plugin ---"; \ + ldd "$PLUGIN_PATH" || true; \ + echo "--- h5py check ---"; \ + ./.venv/bin/python -c "import h5py; print(\"h5py\", h5py.__version__); print(\"hdf5\", h5py.version.hdf5_version)"; \ + echo "--- run test ---"; \ + ./.venv/bin/python -u test.py' + diff --git a/examples/python/write.py b/examples/python/write.py index e489226..200858b 100644 --- a/examples/python/write.py +++ b/examples/python/write.py @@ -1,12 +1,14 @@ import h5py import numpy as np - -f = h5py.File('demo.h5', 'w') -dset = f.create_dataset("test", (100, 100), chunks=(10, 10), dtype='i4') +import sys +print("before open", file=sys.stderr, flush=True) +f = h5py.File("demo-mac.h5", "w") +dset = f.create_dataset("test", (100, 100), chunks=(10, 10), dtype="i4") # write 100 x 100 2d arrays filled with value 1 -data = np.ones((100, 100), dtype='i4') -for i in range(100): - for j in range(100): - data[i][j] = i + j -dset[:] = data +print("before open") +with f: + print("after open") + dset = f.create_dataset("x", data=[1, 2, 3]) + print("after create_dataset") +print("after close") diff --git a/justfile b/justfile index a79b73f..d185075 100644 --- a/justfile +++ b/justfile @@ -1,47 +1,55 @@ -# ArrayMorph — Top-Level Build Orchestration -# https://just.systems +# Required Python packages (install in .venv before running just): +# +# pip install \ +# build \ +# scikit-build-core \ +# cibuildwheel \ +# conan \ +# h5py==3.15.1 -# --- Settings --- -set dotenv-load := true -set export := true +set shell := ["bash", "-eu", "-o", "pipefail", "-c"] -# --- Variables --- -VCPKG_TOOLCHAIN := env("VCPKG_ROOT", home_directory() / ".vcpkg") / "scripts/buildsystems/vcpkg.cmake" -HDF5_DIR := `./.venv/bin/python3 -c "import h5py,os;d=os.path.dirname(h5py.__file__);print(os.path.join(d,'.dylibs') if os.path.exists(os.path.join(d,'.dylibs')) else os.path.join(os.path.dirname(d),'h5py.libs'))"` +BUILD_TYPE := env_var_or_default("BUILD_TYPE", "Release") -# --- Recipes --- +# Automatically discover the HDF5 directory shipped with h5py. +# This will FAIL if: +# • .venv does not exist +# • h5py is not installed +# which is intentional. + +H5PY_HDF5_DIR := `./.venv/bin/python3 -c "import h5py,os;d=os.path.dirname(h5py.__file__);print(os.path.join(d,'.dylibs') if os.path.exists(os.path.join(d,'.dylibs')) else os.path.join(os.path.dirname(d),'h5py.libs'))"` +CONAN_BUILD := justfile_directory() / "lib" / "build" / BUILD_TYPE +TOOLCHAIN := CONAN_BUILD / "generators" / "conan_toolchain.cmake" -# List available commands default: @just --list -# Build Python wheel (scikit-build-core handles CMake) -wheel: - CMAKE_TOOLCHAIN_FILE={{ VCPKG_TOOLCHAIN }} \ - HDF5_DIR={{ HDF5_DIR }} \ - uv build --wheel --no-build-isolation - -# Install editable into current venv (for development iteration) -dev: - CMAKE_TOOLCHAIN_FILE={{ VCPKG_TOOLCHAIN }} \ - HDF5_DIR={{ HDF5_DIR }} \ - uv pip install -e . +info: + @echo "BUILD_TYPE: {{ BUILD_TYPE }}" + @echo "H5PY_HDF5_DIR: {{ H5PY_HDF5_DIR }}" + @echo "CONAN_BUILD: {{ CONAN_BUILD }}" + @echo "TOOLCHAIN: {{ TOOLCHAIN }}" -# Full build from scratch: deps → wheel -build: wheel +deps: + conan install lib --build=missing -s build_type={{ BUILD_TYPE }} -# Full build + test -all: build +configure: deps + H5PY_HDF5_DIR={{ H5PY_HDF5_DIR }} \ + cmake -S lib -B {{ CONAN_BUILD }} \ + -DCMAKE_BUILD_TYPE={{ BUILD_TYPE }} \ + -DCMAKE_TOOLCHAIN_FILE={{ TOOLCHAIN }} -# Clean build artifacts -clean: - rm -rf lib/build lib/vcpkg_installed dist *.egg-info .test-venv +build: configure + cmake --build {{ CONAN_BUILD }} -# Full clean rebuild -rebuild: clean build +wheel: deps + CMAKE_TOOLCHAIN_FILE={{ TOOLCHAIN }} \ + H5PY_HDF5_DIR={{ H5PY_HDF5_DIR }} \ + ./.venv/bin/python -m build --wheel --no-isolation -# Show current env var values (for debugging) -info: - @echo "CMAKE_TOOLCHAIN_FILE: {{ VCPKG_TOOLCHAIN }}" - @echo "HDF5_DIR: {{ HDF5_DIR }}" - @echo "Plugin lib: $(find lib/build -name 'lib_array_morph*' 2>/dev/null || echo 'not built')" +clean: + rm -rf \ + lib/build \ + dist \ + wheelhouse \ + *.egg-info diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 51630db..365e0cc 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,107 +1,122 @@ cmake_minimum_required(VERSION 3.20) -project(ArrayMorph - VERSION 0.2.0 - LANGUAGES C CXX -) +project(ArrayMorph VERSION 0.2.0 LANGUAGES C CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -# Build type and optimization if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release) + set(CMAKE_BUILD_TYPE Release) endif() if(CMAKE_BUILD_TYPE STREQUAL "Release") - set(CMAKE_CXX_FLAGS_RELEASE "-O3") + set(CMAKE_CXX_FLAGS_RELEASE "-O3") endif() -# Include directory include_directories(${CMAKE_SOURCE_DIR}/include) -# --- Find External Dependencies --- - -# AWS SDK +# Dependencies from Conan/CMakeDeps find_package(AWSSDK REQUIRED COMPONENTS core s3) - -# Azure SDK -find_package(azure-storage-blobs-cpp CONFIG REQUIRED) - -# cURL and OpenSSL +find_package(AzureSDK REQUIRED) find_package(CURL REQUIRED) find_package(OpenSSL REQUIRED) -# --- HDF5: vcpkg headers + h5py runtime binary --- -# -# ArrayMorph is a VOL plugin that gets dlopen'd by HDF5 at runtime. -# We MUST link against the same HDF5 that h5py ships to avoid -# duplicate symbol conflicts. vcpkg provides headers, h5py provides -# the shared library. +# We keep Conan HDF5 only for headers. +find_package(HDF5 REQUIRED COMPONENTS C) + +# Need Python so we can discover h5py's bundled HDF5 if H5PY_HDF5_DIR +# is not passed in explicitly. +find_package(Python REQUIRED COMPONENTS Interpreter) + +# Conan's AWS package injects `c++` as a system lib on macOS, but AppleClang +# already links libc++ when using `-stdlib=libc++`, which causes a duplicate +# library warning at link time. +function(arraymorph_remove_interface_link_item target item) + if(NOT TARGET "${target}") + return() + endif() + + get_target_property(_link_libs "${target}" INTERFACE_LINK_LIBRARIES) + if(NOT _link_libs) + return() + endif() + + list(REMOVE_ITEM _link_libs "${item}") + set_property( + TARGET "${target}" + PROPERTY INTERFACE_LINK_LIBRARIES "${_link_libs}" + ) +endfunction() -# Accept HDF5_DIR from either CMake variable (-D) or env var -if(NOT HDF5_DIR AND DEFINED ENV{HDF5_DIR}) - set(HDF5_DIR "$ENV{HDF5_DIR}") +if(APPLE) + arraymorph_remove_interface_link_item(AWS::aws-sdk-cpp-core "c++") + arraymorph_remove_interface_link_item(AWS::aws-sdk-cpp-s3 "c++") + arraymorph_remove_interface_link_item(aws-sdk-cpp::aws-sdk-cpp "c++") endif() -if(NOT HDF5_DIR) - message(FATAL_ERROR - "HDF5_DIR not set. Run:\n" - " export HDF5_DIR=$(python3 -c \"" - "import h5py, os; " - "d=os.path.dirname(h5py.__file__); " - "print(os.path.join(d,'.dylibs') if os.path.exists(os.path.join(d,'.dylibs')) " - "else os.path.join(os.path.dirname(d),'h5py.libs'))\")" +# ------------------------------------------------------------ +# HDF5 strategy: +# - headers come from Conan's HDF5 package +# - runtime libhdf5 comes from the installed h5py wheel +# ------------------------------------------------------------ + +set(H5PY_LIB_DIR "$ENV{H5PY_HDF5_DIR}") + +if(NOT H5PY_LIB_DIR) + execute_process( + COMMAND "${Python_EXECUTABLE}" -c + "import h5py, os; d=os.path.dirname(h5py.__file__); dylibs=os.path.join(d,'.dylibs'); libs=os.path.join(os.path.dirname(d),'h5py.libs'); print(dylibs if os.path.exists(dylibs) else libs)" + OUTPUT_VARIABLE H5PY_LIB_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE + COMMAND_ERROR_IS_FATAL ANY ) endif() -set(H5PY_LIB_DIR "${HDF5_DIR}") - -# Find HDF5 headers (via vcpkg) -find_package(HDF5 REQUIRED COMPONENTS C) +if(NOT EXISTS "${H5PY_LIB_DIR}") + message(FATAL_ERROR + "Could not locate h5py's bundled HDF5 directory.\n" + "Set H5PY_HDF5_DIR or install h5py in the build environment." + ) +endif() -# Locate the actual shared library in h5py's bundled directory file(GLOB _h5py_hdf5_libs - "${H5PY_LIB_DIR}/libhdf5*.dylib" # macOS - "${H5PY_LIB_DIR}/libhdf5*.so*" # Linux + "${H5PY_LIB_DIR}/libhdf5.dylib" + "${H5PY_LIB_DIR}/libhdf5.*.dylib" + "${H5PY_LIB_DIR}/libhdf5.so" + "${H5PY_LIB_DIR}/libhdf5.so.*" + "${H5PY_LIB_DIR}/libhdf5-*.so" + "${H5PY_LIB_DIR}/libhdf5-*.so.*" ) if(NOT _h5py_hdf5_libs) - # List what's actually in the directory for debugging file(GLOB _h5py_dir_contents "${H5PY_LIB_DIR}/*") message(FATAL_ERROR - "No HDF5 shared library found in HDF5_DIR=${H5PY_LIB_DIR}\n" - "Directory contents: ${_h5py_dir_contents}\n" - "Expected libhdf5*.dylib (macOS) or libhdf5*.so* (Linux)" + "No core HDF5 shared library found in H5PY_HDF5_DIR=${H5PY_LIB_DIR}\n" + "Directory contents: ${_h5py_dir_contents}" ) endif() -# Pick the first match list(GET _h5py_hdf5_libs 0 _h5py_hdf5_lib) -# Create imported target: vcpkg headers + h5py binary -add_library(hdf5_custom SHARED IMPORTED) -set_target_properties(hdf5_custom PROPERTIES +add_library(hdf5_runtime SHARED IMPORTED GLOBAL) +set_target_properties(hdf5_runtime PROPERTIES IMPORTED_LOCATION "${_h5py_hdf5_lib}" INTERFACE_INCLUDE_DIRECTORIES "${HDF5_INCLUDE_DIRS}" ) -message(STATUS "HDF5 version: ${HDF5_VERSION}") -message(STATUS "HDF5 headers: ${HDF5_INCLUDE_DIRS}") -message(STATUS "HDF5 binary: ${_h5py_hdf5_lib}") +message(STATUS "HDF5 headers (Conan): ${HDF5_INCLUDE_DIRS}") +message(STATUS "HDF5 runtime (h5py): ${_h5py_hdf5_lib}") -# Collect all dependencies into a list set(ALL_DEPS - aws-cpp-sdk-core - aws-cpp-sdk-s3 + AWS::aws-sdk-cpp-core + AWS::aws-sdk-cpp-s3 Azure::azure-storage-blobs - hdf5_custom OpenSSL::SSL OpenSSL::Crypto CURL::libcurl + hdf5_runtime ) -# Enter src directory add_subdirectory(src) diff --git a/lib/conanfile.py b/lib/conanfile.py new file mode 100644 index 0000000..29980bf --- /dev/null +++ b/lib/conanfile.py @@ -0,0 +1,53 @@ +from conan import ConanFile +from conan.tools.cmake import cmake_layout, CMakeToolchain, CMakeDeps + + +class ArrayMorphRecipe(ConanFile): + name = "ArrayMorph" + version = "0.2.0" + + settings = "os", "compiler", "build_type", "arch" + + requires = ( + "aws-sdk-cpp/1.11.692", + "azure-sdk-for-cpp/1.16.1", + "hdf5/1.14.6", # headers only in practice; runtime comes from h5py + "libcurl/8.17.0", + "openssl/3.6.1", + ) + + default_options = { + # We do NOT want to ship Conan's HDF5 runtime in the wheel. + # Keeping this static reduces accidental runtime coupling. + "hdf5/*:shared": False, + + # Keep these static too so we don't need extra wheel bundling. + "aws-sdk-cpp/*:shared": False, + "azure-sdk-for-cpp/*:shared": False, + "libcurl/*:shared": True, + "openssl/*:shared": True, + + # AWS: only S3 + "aws-sdk-cpp/*:s3": True, + "aws-sdk-cpp/*:monitoring": False, + "aws-sdk-cpp/*:transfer": False, + "aws-sdk-cpp/*:queues": False, + "aws-sdk-cpp/*:identity-management": False, + "aws-sdk-cpp/*:access-management": False, + "aws-sdk-cpp/*:s3-encryption": False, + "aws-sdk-cpp/*:text-to-speech": False, + + # Azure: only Blob + "azure-sdk-for-cpp/*:with_storage_blobs": True, + "azure-sdk-for-cpp/*:with_storage_datalake": False, + } + + def layout(self): + cmake_layout(self) + + def generate(self): + tc = CMakeToolchain(self, generator="Ninja") + tc.generate() + + deps = CMakeDeps(self) + deps.generate() diff --git a/lib/include/arraymorph/core/constants.h b/lib/include/arraymorph/core/constants.h index 0dcbe93..b640dda 100644 --- a/lib/include/arraymorph/core/constants.h +++ b/lib/include/arraymorph/core/constants.h @@ -4,7 +4,7 @@ #include #include -// #define LOG_ENABLE +#define LOG_ENABLE // #define PROFILE_ENABLE #define ARRAYMORPH_SUCCESS 1 #define ARRAYMORPH_FAIL -1 diff --git a/lib/include/arraymorph/s3vl/initialize.h b/lib/include/arraymorph/s3vl/initialize.h index 2388528..a63285b 100644 --- a/lib/include/arraymorph/s3vl/initialize.h +++ b/lib/include/arraymorph/s3vl/initialize.h @@ -10,8 +10,8 @@ #include #include #include +#include #include - static Aws::SDKOptions g_sdk_options; class S3VLINITIALIZE { @@ -28,16 +28,6 @@ inline herr_t S3VLINITIALIZE::s3VL_initialize_init(hid_t vipl_id) { // Aws::SDKOptions options; // Changed to use global sdk options for proper // shutdown g_sdk_options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Off; - // curl and OpenSSL global init/cleanup must be called exactly once per - // process and are not re-entrant. Python (or another loaded library such as - // h5py) may already own those lifecycles. Delegating init/cleanup to the SDK - // causes double-free / use-after-free crashes inside CleanupHttp() and - // CleanupCrypto() when the HDF5 VOL terminate callback fires at process exit, - // after the host process has already torn down those subsystems. Disabling - // SDK-managed init/cleanup for both avoids the crashes while leaving curl and - // OpenSSL usable for the SDK. - g_sdk_options.httpOptions.initAndCleanupCurl = false; - g_sdk_options.cryptoOptions.initAndCleanupOpenSSL = false; std::set_terminate([]() { _exit(0); }); // Shutdown conflicts with Python's interpreter shutdown on macOS. @@ -75,7 +65,10 @@ inline herr_t S3VLINITIALIZE::s3VL_initialize_close() { // Proper SDK shutdown. global_cloud_client = std::monostate{}; // Reset the client's state to trigger destructor - Aws::ShutdownAPI(g_sdk_options); + //// Intentionally skip Aws::ShutdownAPI() — we're inside HDF5's atexit + // teardown where static destruction order is undefined. The process is + // exiting; the OS will reclaim all resources. + // Aws::ShutdownAPI(g_sdk_options); return ARRAYMORPH_SUCCESS; } diff --git a/lib/justfile b/lib/justfile index 4a56ffd..3c06c69 100644 --- a/lib/justfile +++ b/lib/justfile @@ -1,37 +1,62 @@ -# https://just.systems -# Variables +# ArrayMorph native build helpers +# Runs inside lib/ -BUILD_DIR := "build" -VCPKG_TOOLCHAIN := env("VCPKG_ROOT", home_directory() / ".vcpkg") / "scripts/buildsystems/vcpkg.cmake" +set shell := ["bash", "-cu"] -# Set this to the path where H5Py installs HDF5 binary. On macOS its .dylib, Linux .so, Windows .dll +BUILD_TYPE := env_var_or_default("BUILD_TYPE", "Release") -HDF5_DIR := `../.venv/bin/python -c "import h5py,os;d=os.path.dirname(h5py.__file__);print(os.path.join(d,'.dylibs') if os.path.exists(os.path.join(d,'.dylibs')) else os.path.join(os.path.dirname(d),'h5py.libs'))"` +# --- Recipes --- -# Settings -set dotenv-load := true -set export := true - -# Default recipe: list available commands +[default] default: @just --list -# Setup the CMake build environment -setup: - export HDF5_DIR={{ HDF5_DIR }}; \ - cmake -B {{ BUILD_DIR }} -S . \ - -DCMAKE_TOOLCHAIN_FILE={{ VCPKG_TOOLCHAIN }} \ - -DCMAKE_BUILD_TYPE=Release \ - -G Ninja - -build: - cmake --build {{ BUILD_DIR }} +# Detect conan profile +profile: + conan profile detect --force + +# Install dependencies +deps build_type=BUILD_TYPE: profile + conan install . \ + --build=missing \ + -s build_type={{build_type}} \ + -s compiler.cppstd=gnu20 + +# Configure CMake +configure build_type=BUILD_TYPE: (deps build_type) + cmake -S . -B build/{{build_type}} \ + -G Ninja \ + -DCMAKE_BUILD_TYPE={{build_type}} \ + -DCMAKE_TOOLCHAIN_FILE=build/{{build_type}}/generators/conan_toolchain.cmake + +# Build native library +build build_type=BUILD_TYPE: (configure build_type) + cmake --build build/{{build_type}} --config {{build_type}} + +# Run tests (if you add them later) +test build_type=BUILD_TYPE: (build build_type) + ctest --test-dir build/{{build_type}} + +# Show dependency tree +graph: + conan graph info . + +# Debug info +info build_type=BUILD_TYPE: + @echo "Build dir: build/{{build_type}}" + @echo "Toolchain:" + @echo " build/{{build_type}}/generators/conan_toolchain.cmake" + @echo "" + @echo "HDF5 libs:" + find build -name "*hdf5*" || true + +# Clean +clean: + rm -rf build -# Full build from scratch -full-build: setup build +# Full rebuild +rebuild build_type=BUILD_TYPE: clean (build build_type) -# Clean all build and dependency artifacts -clean: - rm -rf {{ BUILD_DIR }} vcpkg_installed +# Compatibility target +full-clean-build build_type=BUILD_TYPE: clean (build build_type) -full-clean-build: clean full-build diff --git a/lib/src/CMakeLists.txt b/lib/src/CMakeLists.txt index 4e30db2..f5bc269 100644 --- a/lib/src/CMakeLists.txt +++ b/lib/src/CMakeLists.txt @@ -1,54 +1,70 @@ -# 1. Include directories +# Internal include directories set(PROJECT_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../include ) -# 2. Internal Static Libraries +# Propagate Conan-provided include dirs, definitions, and link requirements +# to any target that includes third-party headers. +add_library(arraymorph_deps INTERFACE) +target_link_libraries(arraymorph_deps INTERFACE ${ALL_DEPS}) + +# Internal static libraries add_library(utils STATIC core/utils.cc) target_include_directories(utils PUBLIC ${PROJECT_INCLUDE_DIRS}) -target_link_libraries(utils PUBLIC ${HDF5_LIBRARIES}) +target_link_libraries(utils PRIVATE arraymorph_deps) add_library(constants STATIC core/constants.cc) target_include_directories(constants PUBLIC ${PROJECT_INCLUDE_DIRS}) add_library(operators STATIC core/operators.cc) target_include_directories(operators PUBLIC ${PROJECT_INCLUDE_DIRS}) -target_link_libraries(operators PRIVATE constants ${ALL_DEPS}) +target_link_libraries(operators PRIVATE constants arraymorph_deps) add_library(chunk_obj STATIC s3vl/chunk_obj.cc) target_include_directories(chunk_obj PUBLIC ${PROJECT_INCLUDE_DIRS}) -target_link_libraries(chunk_obj PRIVATE operators utils ${ALL_DEPS}) +target_link_libraries(chunk_obj PRIVATE operators utils arraymorph_deps) add_library(dataset_obj STATIC s3vl/dataset_obj.cc) target_include_directories(dataset_obj PUBLIC ${PROJECT_INCLUDE_DIRS}) -target_link_libraries(dataset_obj PRIVATE chunk_obj ${ALL_DEPS}) +target_link_libraries(dataset_obj PRIVATE chunk_obj arraymorph_deps) add_library(file_callbacks STATIC s3vl/file_callbacks.cc) target_include_directories(file_callbacks PUBLIC ${PROJECT_INCLUDE_DIRS}) -target_link_libraries(file_callbacks PRIVATE operators ${ALL_DEPS}) +target_link_libraries(file_callbacks PRIVATE operators arraymorph_deps) add_library(dataset_callbacks STATIC s3vl/dataset_callbacks.cc) target_include_directories(dataset_callbacks PUBLIC ${PROJECT_INCLUDE_DIRS}) -target_link_libraries(dataset_callbacks PRIVATE file_callbacks dataset_obj ${ALL_DEPS}) +target_link_libraries(dataset_callbacks PRIVATE file_callbacks dataset_obj arraymorph_deps) add_library(group_callbacks STATIC s3vl/group_callbacks.cc) target_include_directories(group_callbacks PUBLIC ${PROJECT_INCLUDE_DIRS}) -target_link_libraries(group_callbacks PRIVATE dataset_callbacks ${ALL_DEPS}) +target_link_libraries(group_callbacks PRIVATE dataset_callbacks arraymorph_deps) -# 3. The Final VOL Connector (shared library) +# Final VOL connector shared library add_library(arraymorph SHARED s3vl/vol_connector.cc) -set_target_properties(arraymorph PROPERTIES PREFIX "lib_") +set_target_properties(arraymorph PROPERTIES PREFIX "lib") target_include_directories(arraymorph PUBLIC ${PROJECT_INCLUDE_DIRS}) target_link_libraries(arraymorph PUBLIC group_callbacks - ${ALL_DEPS} + arraymorph_deps ) -# 4. Install into the Python package's lib/ directory -# scikit-build-core sets CMAKE_INSTALL_PREFIX to the wheel staging area -# so this puts the .so/.dylib at: arraymorph/lib/lib_arraymorph.so +# Runtime search path so the plugin can find h5py's bundled HDF5 +# after installation into site-packages/arraymorph/lib. +if(APPLE) + set_target_properties(arraymorph PROPERTIES + BUILD_WITH_INSTALL_RPATH ON + INSTALL_RPATH "@loader_path/../../h5py/.dylibs" + ) +elseif(UNIX) + set_target_properties(arraymorph PROPERTIES + BUILD_WITH_INSTALL_RPATH ON + INSTALL_RPATH "$ORIGIN/../../h5py.libs" + ) +endif() + install(TARGETS arraymorph - LIBRARY DESTINATION arraymorph/lib # .so (Linux) and .dylib (macOS) - RUNTIME DESTINATION arraymorph/lib # .dll (Windows) + LIBRARY DESTINATION arraymorph/lib + RUNTIME DESTINATION arraymorph/lib ) diff --git a/main.py b/main.py index aa77d2f..bafae9b 100644 --- a/main.py +++ b/main.py @@ -1,6 +1,18 @@ -def main(): - print("Hello from arraymorph!") +import numpy as np +import os +import h5py -if __name__ == "__main__": - main() +for k, v in os.environ.items(): + if "AWS" in k: + print(f"{k}={v}") + +f = h5py.File('demo.h5', 'w') +dset = f.create_dataset("test", (100, 100), chunks=(10, 10), dtype='i4') + +# write 100 x 100 2d arrays filled with value 1 +data = np.ones((100, 100), dtype='i4') +for i in range(100): + for j in range(100): + data[i][j] = i + j +dset[:] = data diff --git a/pyproject.toml b/pyproject.toml index 929b4c4..f7617bf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,36 +1,52 @@ [build-system] -requires = ["scikit-build-core", "setuptools-scm"] +requires = ["scikit-build-core>=0.11", "cmake>=3.20", "ninja"] build-backend = "scikit_build_core.build" [project] name = "arraymorph" -dynamic = ["version"] +version = "0.2.0" description = "HDF5 VOL connector for cloud object storage (AWS S3, Azure Blob)" readme = "README.md" license = { text = "MIT" } -authors = [{ name = "ruochenj123, wangtg2013" }] -requires-python = ">=3.9" -dependencies = ["h5py>=3.11.0"] +authors = [{ name = "ICICLE AI Institute" }] +requires-python = ">=3.10" + +# Important: pin h5py, because this package is intentionally coupled +# to h5py's bundled HDF5 at runtime. +dependencies = ["h5py>=3.10,<3.16"] [project.urls] Homepage = "https://github.com/ICICLE-ai/ArrayMorph" Repository = "https://github.com/ICICLE-ai/ArrayMorph" Issues = "https://github.com/ICICLE-ai/ArrayMorph/issues" - -# --- scikit-build-core settings --- - [tool.scikit-build] cmake.source-dir = "lib" cmake.build-type = "Release" -build.verbose = true +cmake.verbose = true wheel.packages = ["src/arraymorph"] -metadata.version.provider = "scikit_build_core.metadata.setuptools_scm" [tool.scikit-build.cmake.define] CMAKE_POSITION_INDEPENDENT_CODE = "ON" CMAKE_TOOLCHAIN_FILE = { env = "CMAKE_TOOLCHAIN_FILE", default = "" } -HDF5_DIR = { env = "HDF5_DIR", default = "" } +H5PY_HDF5_DIR = { env = "H5PY_HDF5_DIR", default = "" } + +[tool.cibuildwheel] +build = "cp310-* cp311-* cp312-* cp313-*" +skip = ["*-win32", "*-manylinux_i686", "*-musllinux*"] +test-command = "python -c 'import arraymorph, h5py; print(\"arraymorph+h5py imported successfully\")'" -[tool.setuptools_scm] -local_scheme = "no-local-version" +[tool.cibuildwheel.linux] +manylinux-x86_64-image = "manylinux_2_28" +manylinux-aarch64-image = "manylinux_2_28" +before-all = [ + "dnf install -y perl perl-IPC-Cmd ca-certificates", +] +before-build = [ + "pip install 'conan<3' 'h5py>=3.10,<3.16'", + "conan profile detect --force", + "conan lock create {package}/lib -s build_type=Release --lockfile-out={package}/lib/conan.lock", + "conan install {package}/lib --lockfile={package}/lib/conan.lock --build=missing -of {package}/lib/build", +] +environment = { CMAKE_TOOLCHAIN_FILE="/project/lib/build/build/Release/generators/conan_toolchain.cmake" } +repair-wheel-command = "" diff --git a/scripts/hdf5_loaded.py b/scripts/hdf5_loaded.py new file mode 100644 index 0000000..248750c --- /dev/null +++ b/scripts/hdf5_loaded.py @@ -0,0 +1,46 @@ +import json +import os +import re +import subprocess +import sys +from collections import OrderedDict + + +want_arraymorph = len(sys.argv) > 1 and sys.argv[1] == "1" + +import h5py # noqa: F401 + +if want_arraymorph: + import arraymorph # noqa: F401 + +paths = [] +if sys.platform.startswith("linux"): + with open("/proc/self/maps", "r", encoding="utf-8", errors="ignore") as f: + for line in f: + match = re.search(r"(/[^\s]*libhdf5[^\s]*)", line) + if match: + paths.append(match.group(1)) +elif sys.platform == "darwin": + out = subprocess.check_output( + ["vmmap", str(os.getpid())], + text=True, + stderr=subprocess.DEVNULL, + ) + for line in out.splitlines(): + match = re.search(r"(/.*libhdf5[^\s]*)", line) + if match: + paths.append(match.group(1)) +else: + raise SystemExit(f"Unsupported platform for this helper: {sys.platform}") + +unique = list(OrderedDict.fromkeys(paths)) +print( + json.dumps( + { + "imported_arraymorph": want_arraymorph, + "loaded_hdf5": unique, + "unique_hdf5_count": len(unique), + }, + indent=2, + ) +) diff --git a/scripts/show_hdf5_dir.py b/scripts/show_hdf5_dir.py new file mode 100644 index 0000000..78651f9 --- /dev/null +++ b/scripts/show_hdf5_dir.py @@ -0,0 +1,9 @@ +import os + +import h5py + + +base = os.path.dirname(h5py.__file__) +dylibs = os.path.join(base, ".dylibs") +libs = os.path.join(os.path.dirname(base), "h5py.libs") +print(dylibs if os.path.exists(dylibs) else libs) diff --git a/src/arraymorph/__init__.py b/src/arraymorph/__init__.py index a466105..e143a1f 100644 --- a/src/arraymorph/__init__.py +++ b/src/arraymorph/__init__.py @@ -1,161 +1,85 @@ -""" -ArrayMorph - HDF5 VOL connector for cloud object storage. - -Supports AWS S3 and Azure Blob Storage via HDF5's Virtual Object Layer. -""" - from __future__ import annotations import os +import sys from pathlib import Path -__version__ = "0.2.0" -# The compiled VOL plugin lives next to this file after installation -_PLUGIN_DIR = str(Path(__file__).parent / "lib") +# --------------------------------------------------------------------- +# Existing runtime configuration helpers (kept intentionally minimal) +# --------------------------------------------------------------------- -def get_plugin_path() -> str: - """Return the directory containing the ArrayMorph VOL plugin (.so/.dylib). +def _get_env(name: str, default: str | None = None) -> str | None: + """Helper to read environment variables safely.""" + return os.environ.get(name, default) - Use this to set HDF5_PLUGIN_PATH: - >>> import arraymorph - >>> os.environ["HDF5_PLUGIN_PATH"] = arraymorph.get_plugin_path() - """ - return _PLUGIN_DIR +# These are intentionally NOT modified by enable(). +# They remain user-controlled runtime configuration. +STORAGE_PLATFORM = _get_env("STORAGE_PLATFORM") +BUCKET_NAME = _get_env("BUCKET_NAME") +AWS_ACCESS_KEY_ID = _get_env("AWS_ACCESS_KEY_ID") +AWS_SECRET_ACCESS_KEY = _get_env("AWS_SECRET_ACCESS_KEY") +AWS_REGION = _get_env("AWS_REGION") -def enable() -> None: - """Configure HDF5 environment variables to use ArrayMorph. - Sets HDF5_PLUGIN_PATH and HDF5_VOL_CONNECTOR so that any - subsequent h5py calls route through the ArrayMorph VOL connector. +# --------------------------------------------------------------------- +# ArrayMorph VOL plugin helpers +# --------------------------------------------------------------------- + - Usage: - >>> import arraymorph - >>> arraymorph.enable() - >>> import h5py - >>> f = h5py.File("s3://bucket/data.h5", "r") +def _plugin_filename() -> str: + if sys.platform == "darwin": + return "libarraymorph.dylib" + if sys.platform.startswith("linux"): + return "libarraymorph.so" + + raise RuntimeError(f"Unsupported platform: {sys.platform}") + + +def get_plugin_path() -> str: + """ + Return the absolute path to the ArrayMorph VOL plugin library. """ - os.environ["HDF5_PLUGIN_PATH"] = _PLUGIN_DIR - os.environ["HDF5_VOL_CONNECTOR"] = "arraymorph" - - -def configure_s3( - bucket: str, - access_key: str = "", - secret_key: str = "", - endpoint: str | None = None, - region: str = "us-east-2", - use_tls: bool = False, - addressing_style: bool = False, - use_signed_payloads: bool = False, -) -> None: - """Configure AWS S3 credentials and client behavior for ArrayMorph. - - Sets the environment variables read by the VOL connector's S3 client - at initialization time. Call this before any h5py file operations. - - Args: - bucket: Name of the S3 bucket where HDF5 files are stored. - Maps to: BUCKET_NAME - access_key: Access key ID for authentication with the S3 service. - Maps to: AWS_ACCESS_KEY_ID - secret_key: Secret access key paired with access_key for authentication. - Maps to: AWS_SECRET_ACCESS_KEY - endpoint: Custom S3-compatible endpoint URL (e.g. 'http://localhost:3900'). - When None, the S3 client targets the default AWS endpoint. Required - for any non-AWS S3-compatible object store (MinIO, Ceph, etc.). - Maps to: AWS_ENDPOINT_URL_S3 - region: Region label used in SigV4 request signing. Must match the region - your bucket or S3-compatible store is configured with — a mismatch - produces signature validation errors. Defaults to 'us-east-2'. - Maps to: AWS_REGION - use_tls: Whether to use HTTPS (True) or HTTP (False) for S3 connections. - Set to False for object stores that do not have TLS configured. - Defaults to False. - Maps to: AWS_USE_TLS - addressing_style: URL addressing style for the S3 client. When True, - uses path-style ('endpoint/bucket/key'). When False, uses - virtual-hosted style ('bucket.endpoint/key'), which can cause the - S3 client to misinterpret the HDF5 filename as the bucket name. - Most S3-compatible stores require path-style addressing. - Defaults to False. - Maps to: AWS_S3_ADDRESSING_STYLE - use_signed_payloads: Whether to include the request body in the SigV4 - signature (PayloadSigningPolicy::Always). Some S3-compatible stores - require signed payloads and will reject requests with signature - validation errors if this is disabled. Defaults to False. - Maps to: AWS_SIGNED_PAYLOADS - - Example: - >>> import arraymorph - >>> arraymorph.configure_s3( - ... bucket="my-bucket", - ... access_key="my-access-key", - ... secret_key="my-secret-key", - ... endpoint="http://localhost:3900", - ... region="us-east-1", - ... use_tls=False, - ... addressing_style=True, - ... use_signed_payloads=True, - ... ) - >>> arraymorph.enable() + pkg_dir = Path(__file__).resolve().parent + lib_dir = pkg_dir / "lib" + + plugin = lib_dir / _plugin_filename() + + if plugin.exists(): + return str(plugin) + + # fallback for versioned library names + matches = list(lib_dir.glob("libarraymorph*")) + if matches: + return str(matches[0]) + + raise FileNotFoundError(f"Could not locate ArrayMorph plugin inside {lib_dir}") + + +def get_plugin_dir() -> str: + """Return the directory containing the VOL plugin.""" + return str(Path(get_plugin_path()).parent) + + +def enable() -> None: """ - if not (access_key and secret_key): - raise ValueError( - "configure_s3() requires both 'access_key' and 'secret_key'. " - "Set them explicitly or export AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY " - "before calling this function." - ) - - os.environ["AWS_ACCESS_KEY_ID"] = access_key - os.environ["AWS_SECRET_ACCESS_KEY"] = secret_key - os.environ["STORAGE_PLATFORM"] = "S3" - os.environ["BUCKET_NAME"] = bucket - os.environ["AWS_REGION"] = region - - if endpoint: - os.environ["AWS_ENDPOINT_URL_S3"] = endpoint - - os.environ["AWS_USE_TLS"] = str(use_tls).lower() - os.environ["AWS_S3_ADDRESSING_STYLE"] = "path" if addressing_style else "virtual" - os.environ["AWS_SIGNED_PAYLOADS"] = str(use_signed_payloads).lower() - - -def configure_azure( - container: str, - connection_string: str | None = None, -) -> None: - """Configure Azure Blob Storage credentials for ArrayMorph. - - Sets the environment variables read by the VOL connector's Azure client - at initialization time. Call this before any h5py file operations. - - Args: - container: Name of the Azure Blob Storage container where HDF5 files - are stored. Maps to: BUCKET_NAME - connection_string: Azure Storage connection string used to authenticate - and locate the storage account. If None, the connector will fall back - to the existing AZURE_STORAGE_CONNECTION_STRING environment variable. - Maps to: AZURE_STORAGE_CONNECTION_STRING - - Example: - >>> import arraymorph - >>> arraymorph.configure_azure( - ... container="my-container", - ... connection_string="DefaultEndpointsProtocol=https;AccountName=...", - ... ) - >>> arraymorph.enable() + Enable the ArrayMorph VOL connector for the current process. + + This only sets HDF5 plugin discovery variables. + Storage configuration variables are left untouched. """ - if not connection_string and not os.environ.get("AZURE_STORAGE_CONNECTION_STRING"): - raise ValueError( - "configure_azure() requires a 'connection_string'. " - "Set it explicitly or export AZURE_STORAGE_CONNECTION_STRING " - "before calling this function." - ) - - os.environ["STORAGE_PLATFORM"] = "Azure" - os.environ["BUCKET_NAME"] = container - if connection_string: - os.environ["AZURE_STORAGE_CONNECTION_STRING"] = connection_string + os.environ.setdefault("HDF5_PLUGIN_PATH", get_plugin_dir()) + os.environ.setdefault("HDF5_VOL_CONNECTOR", "arraymorph") + + +# --------------------------------------------------------------------- +# Public API +# --------------------------------------------------------------------- + +__all__ = [ + "enable", + "get_plugin_path", + "get_plugin_dir", +] diff --git a/uv.lock b/uv.lock index e24214f..9d1a0c2 100644 --- a/uv.lock +++ b/uv.lock @@ -1,109 +1,28 @@ version = 1 revision = 3 -requires-python = ">=3.8" +requires-python = ">=3.10" resolution-markers = [ "python_full_version >= '3.11'", - "python_full_version == '3.10.*'", - "python_full_version == '3.9.*'", - "python_full_version < '3.9'", + "python_full_version < '3.11'", ] [[package]] name = "arraymorph" -version = "0.1.0" +version = "0.2.0" source = { virtual = "." } dependencies = [ - { name = "h5py", version = "3.11.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, - { name = "h5py", version = "3.14.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, - { name = "h5py", version = "3.15.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "h5py" }, ] [package.metadata] -requires-dist = [{ name = "h5py", specifier = ">=3.11.0" }] - -[[package]] -name = "h5py" -version = "3.11.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.9'", -] -dependencies = [ - { name = "numpy", version = "1.24.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/52/8f/e557819155a282da36fb21f8de4730cfd10a964b52b3ae8d20157ac1c668/h5py-3.11.0.tar.gz", hash = "sha256:7b7e8f78072a2edec87c9836f25f34203fd492a4475709a18b417a33cfb21fa9", size = 406519, upload-time = "2024-04-10T10:52:39.585Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ac/25/a1cc81b3a742b73f9409bafe4762c9de0940cce0955d4b6754698fd5ce44/h5py-3.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1625fd24ad6cfc9c1ccd44a66dac2396e7ee74940776792772819fc69f3a3731", size = 3477113, upload-time = "2024-04-10T10:48:28.323Z" }, - { url = "https://files.pythonhosted.org/packages/d4/03/bbb9a992fb43d3ce46687b7c14107f0fa56e6c8704c9ca945a9392cbc8ce/h5py-3.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c072655ad1d5fe9ef462445d3e77a8166cbfa5e599045f8aa3c19b75315f10e5", size = 2939879, upload-time = "2024-04-10T10:48:38.094Z" }, - { url = "https://files.pythonhosted.org/packages/94/00/94bf8573e7487b7c37f2b613fc381880d48ec2311f2e859b8a5817deb4df/h5py-3.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77b19a40788e3e362b54af4dcf9e6fde59ca016db2c61360aa30b47c7b7cef00", size = 5306122, upload-time = "2024-04-10T10:48:51.581Z" }, - { url = "https://files.pythonhosted.org/packages/bb/0d/fbadb9c69e2a31f641bc24e8d21671129ef3b73f0c61bb16b094fadf1385/h5py-3.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:ef4e2f338fc763f50a8113890f455e1a70acd42a4d083370ceb80c463d803972", size = 2968816, upload-time = "2024-04-10T10:49:07.091Z" }, - { url = "https://files.pythonhosted.org/packages/a0/52/38bb74cc4362738cc7ef819503fc54d70f0c3a7378519ccb0ac309389122/h5py-3.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:bbd732a08187a9e2a6ecf9e8af713f1d68256ee0f7c8b652a32795670fb481ba", size = 3489913, upload-time = "2024-04-10T10:49:15.92Z" }, - { url = "https://files.pythonhosted.org/packages/f0/af/dfbea0c69fe725e9e77259d42f4e14eb582eb094200aaf697feb36f513d8/h5py-3.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75bd7b3d93fbeee40860fd70cdc88df4464e06b70a5ad9ce1446f5f32eb84007", size = 2946912, upload-time = "2024-04-10T10:49:25.757Z" }, - { url = "https://files.pythonhosted.org/packages/af/26/f231ee425c8df93c1abbead3d90ea4a5ff3d6aa49e0edfd3b4c017e74844/h5py-3.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52c416f8eb0daae39dabe71415cb531f95dce2d81e1f61a74537a50c63b28ab3", size = 5420165, upload-time = "2024-04-10T10:49:57.203Z" }, - { url = "https://files.pythonhosted.org/packages/d8/5e/b7b83cfe60504cc4d24746aed04353af7ea8ec104e597e5ae71b8d0390cb/h5py-3.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:083e0329ae534a264940d6513f47f5ada617da536d8dccbafc3026aefc33c90e", size = 2979079, upload-time = "2024-04-10T10:50:11.4Z" }, - { url = "https://files.pythonhosted.org/packages/58/a9/2655d4b8355d0ee783dc89dd40b5f0780e6f54a4c9b60721dc235fd6c457/h5py-3.11.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a76cae64080210389a571c7d13c94a1a6cf8cb75153044fd1f822a962c97aeab", size = 3466468, upload-time = "2024-04-10T10:50:30.236Z" }, - { url = "https://files.pythonhosted.org/packages/9d/3f/cf80ef55e0a9b18aae96c763fbd275c54d0723e0f2cc54f954f87cc5c69a/h5py-3.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3736fe21da2b7d8a13fe8fe415f1272d2a1ccdeff4849c1421d2fb30fd533bc", size = 2943214, upload-time = "2024-04-10T10:50:42.532Z" }, - { url = "https://files.pythonhosted.org/packages/db/7e/fedac8bb8c4729409e2dec5e4136a289116d701d54f69ce73c5617afc5f0/h5py-3.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa6ae84a14103e8dc19266ef4c3e5d7c00b68f21d07f2966f0ca7bdb6c2761fb", size = 5378375, upload-time = "2024-04-10T10:50:55.591Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b2/0ee327933ffa37af1fc7915df7fc067e6009adcd8445d55ad07a9bec11b5/h5py-3.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:21dbdc5343f53b2e25404673c4f00a3335aef25521bd5fa8c707ec3833934892", size = 2970991, upload-time = "2024-04-10T10:51:01.555Z" }, - { url = "https://files.pythonhosted.org/packages/33/97/c1a8f28329ad794d18fc61bf251268ac03959bf93b82fdd7701ac6931fed/h5py-3.11.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:754c0c2e373d13d6309f408325343b642eb0f40f1a6ad21779cfa9502209e150", size = 3470228, upload-time = "2024-04-10T10:51:14.716Z" }, - { url = "https://files.pythonhosted.org/packages/a4/1d/fd0b88c51c37bc8aeedecc4f4b48397f7ce13c87073aaf6912faec06e9f6/h5py-3.11.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:731839240c59ba219d4cb3bc5880d438248533366f102402cfa0621b71796b62", size = 2935809, upload-time = "2024-04-10T10:51:33.125Z" }, - { url = "https://files.pythonhosted.org/packages/86/43/fd0bd74462b3c3fb35d98568935d3e5a435c8ec24d45ef408ac8869166af/h5py-3.11.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ec9df3dd2018904c4cc06331951e274f3f3fd091e6d6cc350aaa90fa9b42a76", size = 5309045, upload-time = "2024-04-10T10:51:44.58Z" }, - { url = "https://files.pythonhosted.org/packages/15/9a/b5456e1acc4abb382938d4a730600823bfe77a4bbfd29140ccbf01ba5596/h5py-3.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:55106b04e2c83dfb73dc8732e9abad69d83a436b5b82b773481d95d17b9685e1", size = 2989172, upload-time = "2024-04-10T10:51:56.815Z" }, - { url = "https://files.pythonhosted.org/packages/c2/1f/36a84945616881bd47e6c40dcdca7e929bc811725d78d001eddba6864185/h5py-3.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f4e025e852754ca833401777c25888acb96889ee2c27e7e629a19aee288833f0", size = 3490090, upload-time = "2024-04-10T10:52:08.237Z" }, - { url = "https://files.pythonhosted.org/packages/3c/fb/e213586de5ea56f1747a843e725c62eef350512be57452186996ba660d52/h5py-3.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6c4b760082626120031d7902cd983d8c1f424cdba2809f1067511ef283629d4b", size = 2951710, upload-time = "2024-04-10T10:52:20.066Z" }, - { url = "https://files.pythonhosted.org/packages/71/28/69a881e01f198ccdb65c36f7adcfef22bfe85e38ffbfdf833af24f58eb5e/h5py-3.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67462d0669f8f5459529de179f7771bd697389fcb3faab54d63bf788599a48ea", size = 5326481, upload-time = "2024-04-10T10:52:30.029Z" }, - { url = "https://files.pythonhosted.org/packages/c3/61/0b35ad9aac0ab0a33365879556fdb824fc83013df69b247386690db59015/h5py-3.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:d9c944d364688f827dc889cf83f1fca311caf4fa50b19f009d1f2b525edd33a3", size = 2978689, upload-time = "2024-04-10T10:52:37.283Z" }, -] - -[[package]] -name = "h5py" -version = "3.14.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version == '3.9.*'", -] -dependencies = [ - { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.9.*'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5d/57/dfb3c5c3f1bf5f5ef2e59a22dec4ff1f3d7408b55bfcefcfb0ea69ef21c6/h5py-3.14.0.tar.gz", hash = "sha256:2372116b2e0d5d3e5e705b7f663f7c8d96fa79a4052d250484ef91d24d6a08f4", size = 424323, upload-time = "2025-06-06T14:06:15.01Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/52/89/06cbb421e01dea2e338b3154326523c05d9698f89a01f9d9b65e1ec3fb18/h5py-3.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:24df6b2622f426857bda88683b16630014588a0e4155cba44e872eb011c4eaed", size = 3332522, upload-time = "2025-06-06T14:04:13.775Z" }, - { url = "https://files.pythonhosted.org/packages/c3/e7/6c860b002329e408348735bfd0459e7b12f712c83d357abeef3ef404eaa9/h5py-3.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ff2389961ee5872de697054dd5a033b04284afc3fb52dc51d94561ece2c10c6", size = 2831051, upload-time = "2025-06-06T14:04:18.206Z" }, - { url = "https://files.pythonhosted.org/packages/fa/cd/3dd38cdb7cc9266dc4d85f27f0261680cb62f553f1523167ad7454e32b11/h5py-3.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:016e89d3be4c44f8d5e115fab60548e518ecd9efe9fa5c5324505a90773e6f03", size = 4324677, upload-time = "2025-06-06T14:04:23.438Z" }, - { url = "https://files.pythonhosted.org/packages/b1/45/e1a754dc7cd465ba35e438e28557119221ac89b20aaebef48282654e3dc7/h5py-3.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1223b902ef0b5d90bcc8a4778218d6d6cd0f5561861611eda59fa6c52b922f4d", size = 4557272, upload-time = "2025-06-06T14:04:28.863Z" }, - { url = "https://files.pythonhosted.org/packages/5c/06/f9506c1531645829d302c420851b78bb717af808dde11212c113585fae42/h5py-3.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:852b81f71df4bb9e27d407b43071d1da330d6a7094a588efa50ef02553fa7ce4", size = 2866734, upload-time = "2025-06-06T14:04:33.5Z" }, - { url = "https://files.pythonhosted.org/packages/61/1b/ad24a8ce846cf0519695c10491e99969d9d203b9632c4fcd5004b1641c2e/h5py-3.14.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f30dbc58f2a0efeec6c8836c97f6c94afd769023f44e2bb0ed7b17a16ec46088", size = 3352382, upload-time = "2025-06-06T14:04:37.95Z" }, - { url = "https://files.pythonhosted.org/packages/36/5b/a066e459ca48b47cc73a5c668e9924d9619da9e3c500d9fb9c29c03858ec/h5py-3.14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:543877d7f3d8f8a9828ed5df6a0b78ca3d8846244b9702e99ed0d53610b583a8", size = 2852492, upload-time = "2025-06-06T14:04:42.092Z" }, - { url = "https://files.pythonhosted.org/packages/08/0c/5e6aaf221557314bc15ba0e0da92e40b24af97ab162076c8ae009320a42b/h5py-3.14.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c497600c0496548810047257e36360ff551df8b59156d3a4181072eed47d8ad", size = 4298002, upload-time = "2025-06-06T14:04:47.106Z" }, - { url = "https://files.pythonhosted.org/packages/21/d4/d461649cafd5137088fb7f8e78fdc6621bb0c4ff2c090a389f68e8edc136/h5py-3.14.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:723a40ee6505bd354bfd26385f2dae7bbfa87655f4e61bab175a49d72ebfc06b", size = 4516618, upload-time = "2025-06-06T14:04:52.467Z" }, - { url = "https://files.pythonhosted.org/packages/db/0c/6c3f879a0f8e891625817637fad902da6e764e36919ed091dc77529004ac/h5py-3.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:d2744b520440a996f2dae97f901caa8a953afc055db4673a993f2d87d7f38713", size = 2874888, upload-time = "2025-06-06T14:04:56.95Z" }, - { url = "https://files.pythonhosted.org/packages/3e/77/8f651053c1843391e38a189ccf50df7e261ef8cd8bfd8baba0cbe694f7c3/h5py-3.14.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e0045115d83272090b0717c555a31398c2c089b87d212ceba800d3dc5d952e23", size = 3312740, upload-time = "2025-06-06T14:05:01.193Z" }, - { url = "https://files.pythonhosted.org/packages/ff/10/20436a6cf419b31124e59fefc78d74cb061ccb22213226a583928a65d715/h5py-3.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6da62509b7e1d71a7d110478aa25d245dd32c8d9a1daee9d2a42dba8717b047a", size = 2829207, upload-time = "2025-06-06T14:05:05.061Z" }, - { url = "https://files.pythonhosted.org/packages/3f/19/c8bfe8543bfdd7ccfafd46d8cfd96fce53d6c33e9c7921f375530ee1d39a/h5py-3.14.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:554ef0ced3571366d4d383427c00c966c360e178b5fb5ee5bb31a435c424db0c", size = 4708455, upload-time = "2025-06-06T14:05:11.528Z" }, - { url = "https://files.pythonhosted.org/packages/86/f9/f00de11c82c88bfc1ef22633557bfba9e271e0cb3189ad704183fc4a2644/h5py-3.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cbd41f4e3761f150aa5b662df991868ca533872c95467216f2bec5fcad84882", size = 4929422, upload-time = "2025-06-06T14:05:18.399Z" }, - { url = "https://files.pythonhosted.org/packages/7a/6d/6426d5d456f593c94b96fa942a9b3988ce4d65ebaf57d7273e452a7222e8/h5py-3.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:bf4897d67e613ecf5bdfbdab39a1158a64df105827da70ea1d90243d796d367f", size = 2862845, upload-time = "2025-06-06T14:05:23.699Z" }, - { url = "https://files.pythonhosted.org/packages/6c/c2/7efe82d09ca10afd77cd7c286e42342d520c049a8c43650194928bcc635c/h5py-3.14.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:aa4b7bbce683379b7bf80aaba68e17e23396100336a8d500206520052be2f812", size = 3289245, upload-time = "2025-06-06T14:05:28.24Z" }, - { url = "https://files.pythonhosted.org/packages/4f/31/f570fab1239b0d9441024b92b6ad03bb414ffa69101a985e4c83d37608bd/h5py-3.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ef9603a501a04fcd0ba28dd8f0995303d26a77a980a1f9474b3417543d4c6174", size = 2807335, upload-time = "2025-06-06T14:05:31.997Z" }, - { url = "https://files.pythonhosted.org/packages/0d/ce/3a21d87896bc7e3e9255e0ad5583ae31ae9e6b4b00e0bcb2a67e2b6acdbc/h5py-3.14.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8cbaf6910fa3983c46172666b0b8da7b7bd90d764399ca983236f2400436eeb", size = 4700675, upload-time = "2025-06-06T14:05:37.38Z" }, - { url = "https://files.pythonhosted.org/packages/e7/ec/86f59025306dcc6deee5fda54d980d077075b8d9889aac80f158bd585f1b/h5py-3.14.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d90e6445ab7c146d7f7981b11895d70bc1dd91278a4f9f9028bc0c95e4a53f13", size = 4921632, upload-time = "2025-06-06T14:05:43.464Z" }, - { url = "https://files.pythonhosted.org/packages/3f/6d/0084ed0b78d4fd3e7530c32491f2884140d9b06365dac8a08de726421d4a/h5py-3.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:ae18e3de237a7a830adb76aaa68ad438d85fe6e19e0d99944a3ce46b772c69b3", size = 2852929, upload-time = "2025-06-06T14:05:47.659Z" }, - { url = "https://files.pythonhosted.org/packages/ec/ac/9ea82488c8790ee5b6ad1a807cd7dc3b9dadfece1cd0e0e369f68a7a8937/h5py-3.14.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5cc1601e78027cedfec6dd50efb4802f018551754191aeb58d948bd3ec3bd7a", size = 3345097, upload-time = "2025-06-06T14:05:51.984Z" }, - { url = "https://files.pythonhosted.org/packages/6c/bc/a172ecaaf287e3af2f837f23b470b0a2229c79555a0da9ac8b5cc5bed078/h5py-3.14.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5e59d2136a8b302afd25acdf7a89b634e0eb7c66b1a211ef2d0457853768a2ef", size = 2843320, upload-time = "2025-06-06T14:05:55.754Z" }, - { url = "https://files.pythonhosted.org/packages/66/40/b423b57696514e05aa7bb06150ef96667d0e0006cc6de7ab52c71734ab51/h5py-3.14.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:573c33ad056ac7c1ab6d567b6db9df3ffc401045e3f605736218f96c1e0490c6", size = 4326368, upload-time = "2025-06-06T14:06:00.782Z" }, - { url = "https://files.pythonhosted.org/packages/f7/07/e088f89f04fdbe57ddf9de377f857158d3daa38cf5d0fb20ef9bd489e313/h5py-3.14.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ccbe17dc187c0c64178f1a10aa274ed3a57d055117588942b8a08793cc448216", size = 4559686, upload-time = "2025-06-06T14:06:07.416Z" }, - { url = "https://files.pythonhosted.org/packages/b4/e4/fb8032d0e5480b1db9b419b5b50737b61bb3c7187c49d809975d62129fb0/h5py-3.14.0-cp39-cp39-win_amd64.whl", hash = "sha256:4f025cf30ae738c4c4e38c7439a761a71ccfcce04c2b87b2a2ac64e8c5171d43", size = 2877166, upload-time = "2025-06-06T14:06:13.05Z" }, -] +requires-dist = [{ name = "h5py", specifier = ">=3.10,<3.16" }] [[package]] name = "h5py" version = "3.15.1" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.11'", - "python_full_version == '3.10.*'", -] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, { name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/4d/6a/0d79de0b025aa85dc8864de8e97659c94cf3d23148394a954dc5ca52f8c8/h5py-3.15.1.tar.gz", hash = "sha256:c86e3ed45c4473564de55aa83b6fc9e5ead86578773dfbd93047380042e26b69", size = 426236, upload-time = "2025-10-16T10:35:27.404Z" } @@ -149,105 +68,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d3/b7/4a806f85d62c20157e62e58e03b27513dc9c55499768530acc4f4c5ce4be/h5py-3.15.1-cp314-cp314-win_arm64.whl", hash = "sha256:a6d8c5a05a76aca9a494b4c53ce8a9c29023b7f64f625c6ce1841e92a362ccdf", size = 2465544, upload-time = "2025-10-16T10:35:25.695Z" }, ] -[[package]] -name = "numpy" -version = "1.24.4" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version < '3.9'", -] -sdist = { url = "https://files.pythonhosted.org/packages/a4/9b/027bec52c633f6556dba6b722d9a0befb40498b9ceddd29cbe67a45a127c/numpy-1.24.4.tar.gz", hash = "sha256:80f5e3a4e498641401868df4208b74581206afbee7cf7b8329daae82676d9463", size = 10911229, upload-time = "2023-06-26T13:39:33.218Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6b/80/6cdfb3e275d95155a34659163b83c09e3a3ff9f1456880bec6cc63d71083/numpy-1.24.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0bfb52d2169d58c1cdb8cc1f16989101639b34c7d3ce60ed70b19c63eba0b64", size = 19789140, upload-time = "2023-06-26T13:22:33.184Z" }, - { url = "https://files.pythonhosted.org/packages/64/5f/3f01d753e2175cfade1013eea08db99ba1ee4bdb147ebcf3623b75d12aa7/numpy-1.24.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ed094d4f0c177b1b8e7aa9cba7d6ceed51c0e569a5318ac0ca9a090680a6a1b1", size = 13854297, upload-time = "2023-06-26T13:22:59.541Z" }, - { url = "https://files.pythonhosted.org/packages/5a/b3/2f9c21d799fa07053ffa151faccdceeb69beec5a010576b8991f614021f7/numpy-1.24.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79fc682a374c4a8ed08b331bef9c5f582585d1048fa6d80bc6c35bc384eee9b4", size = 13995611, upload-time = "2023-06-26T13:23:22.167Z" }, - { url = "https://files.pythonhosted.org/packages/10/be/ae5bf4737cb79ba437879915791f6f26d92583c738d7d960ad94e5c36adf/numpy-1.24.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ffe43c74893dbf38c2b0a1f5428760a1a9c98285553c89e12d70a96a7f3a4d6", size = 17282357, upload-time = "2023-06-26T13:23:51.446Z" }, - { url = "https://files.pythonhosted.org/packages/c0/64/908c1087be6285f40e4b3e79454552a701664a079321cff519d8c7051d06/numpy-1.24.4-cp310-cp310-win32.whl", hash = "sha256:4c21decb6ea94057331e111a5bed9a79d335658c27ce2adb580fb4d54f2ad9bc", size = 12429222, upload-time = "2023-06-26T13:24:13.849Z" }, - { url = "https://files.pythonhosted.org/packages/22/55/3d5a7c1142e0d9329ad27cece17933b0e2ab4e54ddc5c1861fbfeb3f7693/numpy-1.24.4-cp310-cp310-win_amd64.whl", hash = "sha256:b4bea75e47d9586d31e892a7401f76e909712a0fd510f58f5337bea9572c571e", size = 14841514, upload-time = "2023-06-26T13:24:38.129Z" }, - { url = "https://files.pythonhosted.org/packages/a9/cc/5ed2280a27e5dab12994c884f1f4d8c3bd4d885d02ae9e52a9d213a6a5e2/numpy-1.24.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f136bab9c2cfd8da131132c2cf6cc27331dd6fae65f95f69dcd4ae3c3639c810", size = 19775508, upload-time = "2023-06-26T13:25:08.882Z" }, - { url = "https://files.pythonhosted.org/packages/c0/bc/77635c657a3668cf652806210b8662e1aff84b818a55ba88257abf6637a8/numpy-1.24.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2926dac25b313635e4d6cf4dc4e51c8c0ebfed60b801c799ffc4c32bf3d1254", size = 13840033, upload-time = "2023-06-26T13:25:33.417Z" }, - { url = "https://files.pythonhosted.org/packages/a7/4c/96cdaa34f54c05e97c1c50f39f98d608f96f0677a6589e64e53104e22904/numpy-1.24.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:222e40d0e2548690405b0b3c7b21d1169117391c2e82c378467ef9ab4c8f0da7", size = 13991951, upload-time = "2023-06-26T13:25:55.725Z" }, - { url = "https://files.pythonhosted.org/packages/22/97/dfb1a31bb46686f09e68ea6ac5c63fdee0d22d7b23b8f3f7ea07712869ef/numpy-1.24.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7215847ce88a85ce39baf9e89070cb860c98fdddacbaa6c0da3ffb31b3350bd5", size = 17278923, upload-time = "2023-06-26T13:26:25.658Z" }, - { url = "https://files.pythonhosted.org/packages/35/e2/76a11e54139654a324d107da1d98f99e7aa2a7ef97cfd7c631fba7dbde71/numpy-1.24.4-cp311-cp311-win32.whl", hash = "sha256:4979217d7de511a8d57f4b4b5b2b965f707768440c17cb70fbf254c4b225238d", size = 12422446, upload-time = "2023-06-26T13:26:49.302Z" }, - { url = "https://files.pythonhosted.org/packages/d8/ec/ebef2f7d7c28503f958f0f8b992e7ce606fb74f9e891199329d5f5f87404/numpy-1.24.4-cp311-cp311-win_amd64.whl", hash = "sha256:b7b1fc9864d7d39e28f41d089bfd6353cb5f27ecd9905348c24187a768c79694", size = 14834466, upload-time = "2023-06-26T13:27:16.029Z" }, - { url = "https://files.pythonhosted.org/packages/11/10/943cfb579f1a02909ff96464c69893b1d25be3731b5d3652c2e0cf1281ea/numpy-1.24.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1452241c290f3e2a312c137a9999cdbf63f78864d63c79039bda65ee86943f61", size = 19780722, upload-time = "2023-06-26T13:27:49.573Z" }, - { url = "https://files.pythonhosted.org/packages/a7/ae/f53b7b265fdc701e663fbb322a8e9d4b14d9cb7b2385f45ddfabfc4327e4/numpy-1.24.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:04640dab83f7c6c85abf9cd729c5b65f1ebd0ccf9de90b270cd61935eef0197f", size = 13843102, upload-time = "2023-06-26T13:28:12.288Z" }, - { url = "https://files.pythonhosted.org/packages/25/6f/2586a50ad72e8dbb1d8381f837008a0321a3516dfd7cb57fc8cf7e4bb06b/numpy-1.24.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5425b114831d1e77e4b5d812b69d11d962e104095a5b9c3b641a218abcc050e", size = 14039616, upload-time = "2023-06-26T13:28:35.659Z" }, - { url = "https://files.pythonhosted.org/packages/98/5d/5738903efe0ecb73e51eb44feafba32bdba2081263d40c5043568ff60faf/numpy-1.24.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd80e219fd4c71fc3699fc1dadac5dcf4fd882bfc6f7ec53d30fa197b8ee22dc", size = 17316263, upload-time = "2023-06-26T13:29:09.272Z" }, - { url = "https://files.pythonhosted.org/packages/d1/57/8d328f0b91c733aa9aa7ee540dbc49b58796c862b4fbcb1146c701e888da/numpy-1.24.4-cp38-cp38-win32.whl", hash = "sha256:4602244f345453db537be5314d3983dbf5834a9701b7723ec28923e2889e0bb2", size = 12455660, upload-time = "2023-06-26T13:29:33.434Z" }, - { url = "https://files.pythonhosted.org/packages/69/65/0d47953afa0ad569d12de5f65d964321c208492064c38fe3b0b9744f8d44/numpy-1.24.4-cp38-cp38-win_amd64.whl", hash = "sha256:692f2e0f55794943c5bfff12b3f56f99af76f902fc47487bdfe97856de51a706", size = 14868112, upload-time = "2023-06-26T13:29:58.385Z" }, - { url = "https://files.pythonhosted.org/packages/9a/cd/d5b0402b801c8a8b56b04c1e85c6165efab298d2f0ab741c2406516ede3a/numpy-1.24.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2541312fbf09977f3b3ad449c4e5f4bb55d0dbf79226d7724211acc905049400", size = 19816549, upload-time = "2023-06-26T13:30:36.976Z" }, - { url = "https://files.pythonhosted.org/packages/14/27/638aaa446f39113a3ed38b37a66243e21b38110d021bfcb940c383e120f2/numpy-1.24.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9667575fb6d13c95f1b36aca12c5ee3356bf001b714fc354eb5465ce1609e62f", size = 13879950, upload-time = "2023-06-26T13:31:01.787Z" }, - { url = "https://files.pythonhosted.org/packages/8f/27/91894916e50627476cff1a4e4363ab6179d01077d71b9afed41d9e1f18bf/numpy-1.24.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3a86ed21e4f87050382c7bc96571755193c4c1392490744ac73d660e8f564a9", size = 14030228, upload-time = "2023-06-26T13:31:26.696Z" }, - { url = "https://files.pythonhosted.org/packages/7a/7c/d7b2a0417af6428440c0ad7cb9799073e507b1a465f827d058b826236964/numpy-1.24.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d11efb4dbecbdf22508d55e48d9c8384db795e1b7b51ea735289ff96613ff74d", size = 17311170, upload-time = "2023-06-26T13:31:56.615Z" }, - { url = "https://files.pythonhosted.org/packages/18/9d/e02ace5d7dfccee796c37b995c63322674daf88ae2f4a4724c5dd0afcc91/numpy-1.24.4-cp39-cp39-win32.whl", hash = "sha256:6620c0acd41dbcb368610bb2f4d83145674040025e5536954782467100aa8835", size = 12454918, upload-time = "2023-06-26T13:32:16.8Z" }, - { url = "https://files.pythonhosted.org/packages/63/38/6cc19d6b8bfa1d1a459daf2b3fe325453153ca7019976274b6f33d8b5663/numpy-1.24.4-cp39-cp39-win_amd64.whl", hash = "sha256:befe2bf740fd8373cf56149a5c23a0f601e82869598d41f8e188a0e9869926f8", size = 14867441, upload-time = "2023-06-26T13:32:40.521Z" }, - { url = "https://files.pythonhosted.org/packages/a4/fd/8dff40e25e937c94257455c237b9b6bf5a30d42dd1cc11555533be099492/numpy-1.24.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:31f13e25b4e304632a4619d0e0777662c2ffea99fcae2029556b17d8ff958aef", size = 19156590, upload-time = "2023-06-26T13:33:10.36Z" }, - { url = "https://files.pythonhosted.org/packages/42/e7/4bf953c6e05df90c6d351af69966384fed8e988d0e8c54dad7103b59f3ba/numpy-1.24.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95f7ac6540e95bc440ad77f56e520da5bf877f87dca58bd095288dce8940532a", size = 16705744, upload-time = "2023-06-26T13:33:36.703Z" }, - { url = "https://files.pythonhosted.org/packages/fc/dd/9106005eb477d022b60b3817ed5937a43dad8fd1f20b0610ea8a32fcb407/numpy-1.24.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e98f220aa76ca2a977fe435f5b04d7b3470c0a2e6312907b37ba6068f26787f2", size = 14734290, upload-time = "2023-06-26T13:34:05.409Z" }, -] - -[[package]] -name = "numpy" -version = "2.0.2" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version == '3.9.*'", -] -sdist = { url = "https://files.pythonhosted.org/packages/a9/75/10dd1f8116a8b796cb2c737b674e02d02e80454bda953fa7e65d8c12b016/numpy-2.0.2.tar.gz", hash = "sha256:883c987dee1880e2a864ab0dc9892292582510604156762362d9326444636e78", size = 18902015, upload-time = "2024-08-26T20:19:40.945Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/21/91/3495b3237510f79f5d81f2508f9f13fea78ebfdf07538fc7444badda173d/numpy-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51129a29dbe56f9ca83438b706e2e69a39892b5eda6cedcb6b0c9fdc9b0d3ece", size = 21165245, upload-time = "2024-08-26T20:04:14.625Z" }, - { url = "https://files.pythonhosted.org/packages/05/33/26178c7d437a87082d11019292dce6d3fe6f0e9026b7b2309cbf3e489b1d/numpy-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f15975dfec0cf2239224d80e32c3170b1d168335eaedee69da84fbe9f1f9cd04", size = 13738540, upload-time = "2024-08-26T20:04:36.784Z" }, - { url = "https://files.pythonhosted.org/packages/ec/31/cc46e13bf07644efc7a4bf68df2df5fb2a1a88d0cd0da9ddc84dc0033e51/numpy-2.0.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8c5713284ce4e282544c68d1c3b2c7161d38c256d2eefc93c1d683cf47683e66", size = 5300623, upload-time = "2024-08-26T20:04:46.491Z" }, - { url = "https://files.pythonhosted.org/packages/6e/16/7bfcebf27bb4f9d7ec67332ffebee4d1bf085c84246552d52dbb548600e7/numpy-2.0.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:becfae3ddd30736fe1889a37f1f580e245ba79a5855bff5f2a29cb3ccc22dd7b", size = 6901774, upload-time = "2024-08-26T20:04:58.173Z" }, - { url = "https://files.pythonhosted.org/packages/f9/a3/561c531c0e8bf082c5bef509d00d56f82e0ea7e1e3e3a7fc8fa78742a6e5/numpy-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2da5960c3cf0df7eafefd806d4e612c5e19358de82cb3c343631188991566ccd", size = 13907081, upload-time = "2024-08-26T20:05:19.098Z" }, - { url = "https://files.pythonhosted.org/packages/fa/66/f7177ab331876200ac7563a580140643d1179c8b4b6a6b0fc9838de2a9b8/numpy-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:496f71341824ed9f3d2fd36cf3ac57ae2e0165c143b55c3a035ee219413f3318", size = 19523451, upload-time = "2024-08-26T20:05:47.479Z" }, - { url = "https://files.pythonhosted.org/packages/25/7f/0b209498009ad6453e4efc2c65bcdf0ae08a182b2b7877d7ab38a92dc542/numpy-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a61ec659f68ae254e4d237816e33171497e978140353c0c2038d46e63282d0c8", size = 19927572, upload-time = "2024-08-26T20:06:17.137Z" }, - { url = "https://files.pythonhosted.org/packages/3e/df/2619393b1e1b565cd2d4c4403bdd979621e2c4dea1f8532754b2598ed63b/numpy-2.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d731a1c6116ba289c1e9ee714b08a8ff882944d4ad631fd411106a30f083c326", size = 14400722, upload-time = "2024-08-26T20:06:39.16Z" }, - { url = "https://files.pythonhosted.org/packages/22/ad/77e921b9f256d5da36424ffb711ae79ca3f451ff8489eeca544d0701d74a/numpy-2.0.2-cp310-cp310-win32.whl", hash = "sha256:984d96121c9f9616cd33fbd0618b7f08e0cfc9600a7ee1d6fd9b239186d19d97", size = 6472170, upload-time = "2024-08-26T20:06:50.361Z" }, - { url = "https://files.pythonhosted.org/packages/10/05/3442317535028bc29cf0c0dd4c191a4481e8376e9f0db6bcf29703cadae6/numpy-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:c7b0be4ef08607dd04da4092faee0b86607f111d5ae68036f16cc787e250a131", size = 15905558, upload-time = "2024-08-26T20:07:13.881Z" }, - { url = "https://files.pythonhosted.org/packages/8b/cf/034500fb83041aa0286e0fb16e7c76e5c8b67c0711bb6e9e9737a717d5fe/numpy-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:49ca4decb342d66018b01932139c0961a8f9ddc7589611158cb3c27cbcf76448", size = 21169137, upload-time = "2024-08-26T20:07:45.345Z" }, - { url = "https://files.pythonhosted.org/packages/4a/d9/32de45561811a4b87fbdee23b5797394e3d1504b4a7cf40c10199848893e/numpy-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:11a76c372d1d37437857280aa142086476136a8c0f373b2e648ab2c8f18fb195", size = 13703552, upload-time = "2024-08-26T20:08:06.666Z" }, - { url = "https://files.pythonhosted.org/packages/c1/ca/2f384720020c7b244d22508cb7ab23d95f179fcfff33c31a6eeba8d6c512/numpy-2.0.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:807ec44583fd708a21d4a11d94aedf2f4f3c3719035c76a2bbe1fe8e217bdc57", size = 5298957, upload-time = "2024-08-26T20:08:15.83Z" }, - { url = "https://files.pythonhosted.org/packages/0e/78/a3e4f9fb6aa4e6fdca0c5428e8ba039408514388cf62d89651aade838269/numpy-2.0.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8cafab480740e22f8d833acefed5cc87ce276f4ece12fdaa2e8903db2f82897a", size = 6905573, upload-time = "2024-08-26T20:08:27.185Z" }, - { url = "https://files.pythonhosted.org/packages/a0/72/cfc3a1beb2caf4efc9d0b38a15fe34025230da27e1c08cc2eb9bfb1c7231/numpy-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a15f476a45e6e5a3a79d8a14e62161d27ad897381fecfa4a09ed5322f2085669", size = 13914330, upload-time = "2024-08-26T20:08:48.058Z" }, - { url = "https://files.pythonhosted.org/packages/ba/a8/c17acf65a931ce551fee11b72e8de63bf7e8a6f0e21add4c937c83563538/numpy-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13e689d772146140a252c3a28501da66dfecd77490b498b168b501835041f951", size = 19534895, upload-time = "2024-08-26T20:09:16.536Z" }, - { url = "https://files.pythonhosted.org/packages/ba/86/8767f3d54f6ae0165749f84648da9dcc8cd78ab65d415494962c86fac80f/numpy-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9ea91dfb7c3d1c56a0e55657c0afb38cf1eeae4544c208dc465c3c9f3a7c09f9", size = 19937253, upload-time = "2024-08-26T20:09:46.263Z" }, - { url = "https://files.pythonhosted.org/packages/df/87/f76450e6e1c14e5bb1eae6836478b1028e096fd02e85c1c37674606ab752/numpy-2.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c1c9307701fec8f3f7a1e6711f9089c06e6284b3afbbcd259f7791282d660a15", size = 14414074, upload-time = "2024-08-26T20:10:08.483Z" }, - { url = "https://files.pythonhosted.org/packages/5c/ca/0f0f328e1e59f73754f06e1adfb909de43726d4f24c6a3f8805f34f2b0fa/numpy-2.0.2-cp311-cp311-win32.whl", hash = "sha256:a392a68bd329eafac5817e5aefeb39038c48b671afd242710b451e76090e81f4", size = 6470640, upload-time = "2024-08-26T20:10:19.732Z" }, - { url = "https://files.pythonhosted.org/packages/eb/57/3a3f14d3a759dcf9bf6e9eda905794726b758819df4663f217d658a58695/numpy-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:286cd40ce2b7d652a6f22efdfc6d1edf879440e53e76a75955bc0c826c7e64dc", size = 15910230, upload-time = "2024-08-26T20:10:43.413Z" }, - { url = "https://files.pythonhosted.org/packages/45/40/2e117be60ec50d98fa08c2f8c48e09b3edea93cfcabd5a9ff6925d54b1c2/numpy-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:df55d490dea7934f330006d0f81e8551ba6010a5bf035a249ef61a94f21c500b", size = 20895803, upload-time = "2024-08-26T20:11:13.916Z" }, - { url = "https://files.pythonhosted.org/packages/46/92/1b8b8dee833f53cef3e0a3f69b2374467789e0bb7399689582314df02651/numpy-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8df823f570d9adf0978347d1f926b2a867d5608f434a7cff7f7908c6570dcf5e", size = 13471835, upload-time = "2024-08-26T20:11:34.779Z" }, - { url = "https://files.pythonhosted.org/packages/7f/19/e2793bde475f1edaea6945be141aef6c8b4c669b90c90a300a8954d08f0a/numpy-2.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9a92ae5c14811e390f3767053ff54eaee3bf84576d99a2456391401323f4ec2c", size = 5038499, upload-time = "2024-08-26T20:11:43.902Z" }, - { url = "https://files.pythonhosted.org/packages/e3/ff/ddf6dac2ff0dd50a7327bcdba45cb0264d0e96bb44d33324853f781a8f3c/numpy-2.0.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a842d573724391493a97a62ebbb8e731f8a5dcc5d285dfc99141ca15a3302d0c", size = 6633497, upload-time = "2024-08-26T20:11:55.09Z" }, - { url = "https://files.pythonhosted.org/packages/72/21/67f36eac8e2d2cd652a2e69595a54128297cdcb1ff3931cfc87838874bd4/numpy-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05e238064fc0610c840d1cf6a13bf63d7e391717d247f1bf0318172e759e692", size = 13621158, upload-time = "2024-08-26T20:12:14.95Z" }, - { url = "https://files.pythonhosted.org/packages/39/68/e9f1126d757653496dbc096cb429014347a36b228f5a991dae2c6b6cfd40/numpy-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0123ffdaa88fa4ab64835dcbde75dcdf89c453c922f18dced6e27c90d1d0ec5a", size = 19236173, upload-time = "2024-08-26T20:12:44.049Z" }, - { url = "https://files.pythonhosted.org/packages/d1/e9/1f5333281e4ebf483ba1c888b1d61ba7e78d7e910fdd8e6499667041cc35/numpy-2.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:96a55f64139912d61de9137f11bf39a55ec8faec288c75a54f93dfd39f7eb40c", size = 19634174, upload-time = "2024-08-26T20:13:13.634Z" }, - { url = "https://files.pythonhosted.org/packages/71/af/a469674070c8d8408384e3012e064299f7a2de540738a8e414dcfd639996/numpy-2.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec9852fb39354b5a45a80bdab5ac02dd02b15f44b3804e9f00c556bf24b4bded", size = 14099701, upload-time = "2024-08-26T20:13:34.851Z" }, - { url = "https://files.pythonhosted.org/packages/d0/3d/08ea9f239d0e0e939b6ca52ad403c84a2bce1bde301a8eb4888c1c1543f1/numpy-2.0.2-cp312-cp312-win32.whl", hash = "sha256:671bec6496f83202ed2d3c8fdc486a8fc86942f2e69ff0e986140339a63bcbe5", size = 6174313, upload-time = "2024-08-26T20:13:45.653Z" }, - { url = "https://files.pythonhosted.org/packages/b2/b5/4ac39baebf1fdb2e72585c8352c56d063b6126be9fc95bd2bb5ef5770c20/numpy-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:cfd41e13fdc257aa5778496b8caa5e856dc4896d4ccf01841daee1d96465467a", size = 15606179, upload-time = "2024-08-26T20:14:08.786Z" }, - { url = "https://files.pythonhosted.org/packages/43/c1/41c8f6df3162b0c6ffd4437d729115704bd43363de0090c7f913cfbc2d89/numpy-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9059e10581ce4093f735ed23f3b9d283b9d517ff46009ddd485f1747eb22653c", size = 21169942, upload-time = "2024-08-26T20:14:40.108Z" }, - { url = "https://files.pythonhosted.org/packages/39/bc/fd298f308dcd232b56a4031fd6ddf11c43f9917fbc937e53762f7b5a3bb1/numpy-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:423e89b23490805d2a5a96fe40ec507407b8ee786d66f7328be214f9679df6dd", size = 13711512, upload-time = "2024-08-26T20:15:00.985Z" }, - { url = "https://files.pythonhosted.org/packages/96/ff/06d1aa3eeb1c614eda245c1ba4fb88c483bee6520d361641331872ac4b82/numpy-2.0.2-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:2b2955fa6f11907cf7a70dab0d0755159bca87755e831e47932367fc8f2f2d0b", size = 5306976, upload-time = "2024-08-26T20:15:10.876Z" }, - { url = "https://files.pythonhosted.org/packages/2d/98/121996dcfb10a6087a05e54453e28e58694a7db62c5a5a29cee14c6e047b/numpy-2.0.2-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:97032a27bd9d8988b9a97a8c4d2c9f2c15a81f61e2f21404d7e8ef00cb5be729", size = 6906494, upload-time = "2024-08-26T20:15:22.055Z" }, - { url = "https://files.pythonhosted.org/packages/15/31/9dffc70da6b9bbf7968f6551967fc21156207366272c2a40b4ed6008dc9b/numpy-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e795a8be3ddbac43274f18588329c72939870a16cae810c2b73461c40718ab1", size = 13912596, upload-time = "2024-08-26T20:15:42.452Z" }, - { url = "https://files.pythonhosted.org/packages/b9/14/78635daab4b07c0930c919d451b8bf8c164774e6a3413aed04a6d95758ce/numpy-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b258c385842546006213344c50655ff1555a9338e2e5e02a0756dc3e803dd", size = 19526099, upload-time = "2024-08-26T20:16:11.048Z" }, - { url = "https://files.pythonhosted.org/packages/26/4c/0eeca4614003077f68bfe7aac8b7496f04221865b3a5e7cb230c9d055afd/numpy-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fec9451a7789926bcf7c2b8d187292c9f93ea30284802a0ab3f5be8ab36865d", size = 19932823, upload-time = "2024-08-26T20:16:40.171Z" }, - { url = "https://files.pythonhosted.org/packages/f1/46/ea25b98b13dccaebddf1a803f8c748680d972e00507cd9bc6dcdb5aa2ac1/numpy-2.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9189427407d88ff25ecf8f12469d4d39d35bee1db5d39fc5c168c6f088a6956d", size = 14404424, upload-time = "2024-08-26T20:17:02.604Z" }, - { url = "https://files.pythonhosted.org/packages/c8/a6/177dd88d95ecf07e722d21008b1b40e681a929eb9e329684d449c36586b2/numpy-2.0.2-cp39-cp39-win32.whl", hash = "sha256:905d16e0c60200656500c95b6b8dca5d109e23cb24abc701d41c02d74c6b3afa", size = 6476809, upload-time = "2024-08-26T20:17:13.553Z" }, - { url = "https://files.pythonhosted.org/packages/ea/2b/7fc9f4e7ae5b507c1a3a21f0f15ed03e794c1242ea8a242ac158beb56034/numpy-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:a3f4ab0caa7f053f6797fcd4e1e25caee367db3112ef2b6ef82d749530768c73", size = 15911314, upload-time = "2024-08-26T20:17:36.72Z" }, - { url = "https://files.pythonhosted.org/packages/8f/3b/df5a870ac6a3be3a86856ce195ef42eec7ae50d2a202be1f5a4b3b340e14/numpy-2.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7f0a0c6f12e07fa94133c8a67404322845220c06a9e80e85999afe727f7438b8", size = 21025288, upload-time = "2024-08-26T20:18:07.732Z" }, - { url = "https://files.pythonhosted.org/packages/2c/97/51af92f18d6f6f2d9ad8b482a99fb74e142d71372da5d834b3a2747a446e/numpy-2.0.2-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:312950fdd060354350ed123c0e25a71327d3711584beaef30cdaa93320c392d4", size = 6762793, upload-time = "2024-08-26T20:18:19.125Z" }, - { url = "https://files.pythonhosted.org/packages/12/46/de1fbd0c1b5ccaa7f9a005b66761533e2f6a3e560096682683a223631fe9/numpy-2.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26df23238872200f63518dd2aa984cfca675d82469535dc7162dc2ee52d9dd5c", size = 19334885, upload-time = "2024-08-26T20:18:47.237Z" }, - { url = "https://files.pythonhosted.org/packages/cc/dc/d330a6faefd92b446ec0f0dfea4c3207bb1fef3c4771d19cf4543efd2c78/numpy-2.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a46288ec55ebbd58947d31d72be2c63cbf839f0a63b49cb755022310792a3385", size = 15828784, upload-time = "2024-08-26T20:19:11.19Z" }, -] - [[package]] name = "numpy" version = "2.2.6" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.10.*'", + "python_full_version < '3.11'", ] sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" } wheels = [