Repository: rapidsai/kvikio (language: python)

Issue:
add wheel output
closes #250 

I'm not sure if it makes sense to split the wheel into one dedicated wheel for the shared library, and another for the python wrapper. That's what @vyasr has been doing on other libraries, like https://github.com/rapidsai/rmm/pull/1529. If it makes sense here, I'll add that.


Discussion / hints:
Yeah for consistency with the rest of RAPIDS using separate C++ and Python wheels is the way to go. We'll be able to leverage that by using the libkvikio wheel during the libcudf wheel build since libcudf needs libkvikio headers. You can see that in [your cudf PR](https://github.com/rapidsai/cudf/pull/15483) those are [currently being bundled into the libcudf wheel](https://github.com/rapidsai/cudf/actions/runs/8714266105/job/23904261719?pr=15483#step:9:2552) which is not ideal. Using the libkvikio wheel from this PR instead will fix that.
Error in wheel build:

```
2024-04-23T14:25:13.7290506Z   CMake Debug Log at /tmp/pip-build-env-c44ywnqk/overlay/lib/python3.11/site-packages/cmake/data/share/cmake-3.29/Modules/FindCUDAToolkit.cmake:1093 (find_library):
2024-04-23T14:25:13.7290723Z     find_library called with the following settings:
2024-04-23T14:25:13.7291226Z 
2024-04-23T14:25:13.7292425Z       VAR: CUDA_nvgraph_LIBRARY
2024-04-23T14:25:13.7293410Z       NAMES: "nvgraph"
2024-04-23T14:25:13.7294445Z       Documentation: Path to a library.
2024-04-23T14:25:13.7295560Z       Framework
2024-04-23T14:25:13.7296475Z         Only Search Frameworks: 0
2024-04-23T14:25:13.7297410Z         Search Frameworks Last: 0
2024-04-23T14:25:13.7298380Z         Search Frameworks First: 0
2024-04-23T14:25:13.7299296Z       AppBundle
2024-04-23T14:25:13.7300297Z         Only Search AppBundle: 0
2024-04-23T14:25:13.7301260Z         Search AppBundle Last: 0
2024-04-23T14:25:13.7302243Z         Search AppBundle First: 0
2024-04-23T14:25:13.7303230Z       CMAKE_FIND_USE_CMAKE_PATH: 1
2024-04-23T14:25:13.7304297Z       CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH: 1
2024-04-23T14:25:13.7305256Z       CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH: 1
2024-04-23T14:25:13.7306202Z       CMAKE_FIND_USE_CMAKE_SYSTEM_PATH: 1
2024-04-23T14:25:13.7307195Z       CMAKE_FIND_USE_INSTALL_PREFIX: 1
2024-04-23T14:25:13.7308173Z 
2024-04-23T14:25:13.7309466Z     find_library considered the following locations:
2024-04-23T14:25:13.7310161Z 
2024-04-23T14:25:13.7311636Z       /pyenv/versions/3.11.9/lib/python3.11/site-packages/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7312856Z       /tmp/pip-build-env-c44ywnqk/overlay/lib/python3.11/site-packages/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7313631Z       /usr/local/cuda-11.8/targets/sbsa-linux/lib/stubs/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7314756Z       /usr/local/cuda-11.8/targets/sbsa-linux/lib/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7315623Z       /usr/local/cuda/targets/sbsa-linux/lib/stubs/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7316798Z       /usr/local/cuda/targets/sbsa-linux/lib/stubs/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7317635Z       /usr/local/cuda/targets/sbsa-linux/lib/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7318804Z       /tmp/pip-build-env-c44ywnqk/overlay/bin/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7319505Z       /pyenv/versions/3.11.9/bin/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7320537Z       /pyenv/libexec/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7321656Z       /pyenv/plugins/python-build/bin/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7322702Z       /pyenv/plugins/pyenv-virtualenv/bin/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7323693Z       /pyenv/plugins/pyenv-update/bin/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7324697Z       /pyenv/plugins/pyenv-doctor/bin/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7325540Z       /usr/share/Modules/bin/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7326723Z       /opt/rh/gcc-toolset-11/root/usr/bin/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7327383Z       /pyenv/bin/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7328500Z       /pyenv/shims/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7329497Z       /usr/local/cuda/bin/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7330462Z       /usr/local/sbin/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7331416Z       /usr/local/bin/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7332371Z       /usr/sbin/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7333400Z       /usr/bin/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7334331Z       /sbin/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7335402Z       /bin/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7336379Z       /usr/local/lib64/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7337336Z       /usr/local/lib/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7338301Z       /usr/local/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7339339Z       /usr/lib64/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7340462Z       /usr/lib/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7341307Z       /usr/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7342246Z       /lib64/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7343187Z       /lib/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7344763Z       /tmp/pip-build-env-c44ywnqk/overlay/lib/python3.11/site-packages/cmake/data/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7345173Z       /opt/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7346285Z       /usr/lib64/X11/libnvgraph(\.so|\.a)
2024-04-23T14:25:13.7347077Z 
2024-04-23T14:25:13.7348338Z     The item was not found.
```
Feel free to add this to the [devcontainer build job](https://github.com/rapidsai/kvikio/blob/7b0231c8241164c8a0511cd737e7098cb7ccea3e/.github/workflows/pr.yaml#L67-L70) to test the path adjustment in the devcontainers before merging:
```yaml
      build_command: |
        sudo yq -i '.repos[2].python[0].sub_dir = "python/kvikio"' /opt/rapids-build-utils/manifest.yaml;
        rapids-generate-scripts;
```
Youll need to add a line [like this](https://github.com/rapidsai/rmm/blob/branch-24.06/python/librmm/pyproject.toml#L55) indicating that the wheels built for libkvikio are compatible with any Python version. Without that, scikit-build assumes that the wheels are compiled against Python and are therefore only compatible with the minor version of Python that you built against (in this case, Python 3.11).
Finally have passing tests here. @vyasr please take one more pass on this.
For anyone new to this thread, the failures in the devcontainers builds are expected right now. The plan to proceed is to get approval from ops and kvikio code owners on this PR, then merge the related devcontainers PR (https://github.com/rapidsai/devcontainers/pull/283), then re-run CI here. Assuming all goes well with that re-run, this PR will be ready to merge.
Once we have approvals, the final steps to merge will be
1. Cleaning out the special logic in this PR for running devcontainer jobs without the upstream updates
2. Merging the devcontainer PR
3. Rerunning CI here to make sure everything passes
Marking this PR as a draft. I will split it into smaller PRs to make them easier to review.

Fix patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 8f318f43da..3d86cf7236 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -18,7 +18,7 @@ repos:
     hooks:
       - id: black
         files: python/.*
-        args: ["--config", "python/pyproject.toml"]
+        args: ["--config", "python/kvikio/pyproject.toml"]
   - repo: https://github.com/PyCQA/flake8
     rev: 6.0.0
     hooks:
@@ -37,11 +37,11 @@ repos:
     hooks:
       - id: mypy
         additional_dependencies: [types-cachetools]
-        args: ["--config-file=python/pyproject.toml",
-                "python/kvikio",
-                "python/tests",
-                "python/examples",
-                "python/benchmarks"]
+        args: ["--config-file=python/kvikio/pyproject.toml",
+                "python/kvikio/kvikio",
+                "python/kvikio/tests",
+                "python/kvikio/examples",
+                "python/kvikio/benchmarks"]
         pass_filenames: false
   - repo: https://github.com/pre-commit/mirrors-clang-format
     rev: v16.0.6
diff --git a/build.sh b/build.sh
index 3b790bf20a..ca9de72a49 100755
--- a/build.sh
+++ b/build.sh
@@ -151,7 +151,7 @@ fi
 # Build and install the kvikio Python package
 if (( NUMARGS == 0 )) || hasArg kvikio; then
     echo "building kvikio..."
-    cd ${REPODIR}/python
+    cd ${REPODIR}/python/kvikio
     SKBUILD_CMAKE_ARGS="-DCMAKE_PREFIX_PATH=${INSTALL_PREFIX};-DCMAKE_LIBRARY_PATH=${LIBKVIKIO_BUILD_DIR};${EXTRA_CMAKE_ARGS}" \
         python -m pip install --no-build-isolation --no-deps --config-settings rapidsai.disable-cuda=true .
 fi
diff --git a/dependencies.yaml b/dependencies.yaml
index c04583a4c7..9f7a133d87 100644
--- a/dependencies.yaml
+++ b/dependencies.yaml
@@ -39,14 +39,14 @@ files:
       - py_version
   py_build:
     output: pyproject
-    pyproject_dir: python
+    pyproject_dir: python/kvikio
     extras:
       table: build-system
     includes:
       - rapids_build_skbuild
   py_rapids_build:
     output: pyproject
-    pyproject_dir: python
+    pyproject_dir: python/kvikio
     extras:
       table: tool.rapids-build-backend
       key: requires
@@ -54,7 +54,7 @@ files:
       - build
   py_run:
     output: pyproject
-    pyproject_dir: python
+    pyproject_dir: python/kvikio
     extras:
       table: project
     includes:
@@ -62,7 +62,7 @@ files:
       - run
   py_optional_test:
     output: pyproject
-    pyproject_dir: python
+    pyproject_dir: python/kvikio
     extras:
       table: project.optional-dependencies
       key: test
diff --git a/docs/source/zarr.rst b/docs/source/zarr.rst
index f2a697d525..5b63ffd8b7 100644
--- a/docs/source/zarr.rst
+++ b/docs/source/zarr.rst
@@ -11,5 +11,5 @@ The following is an example of how to use the convenience function :py:meth:`kvi
 to create a new Zarr array and how open an existing Zarr array.
 
 
-.. literalinclude:: ../../python/examples/zarr_cupy_nvcomp.py
+.. literalinclude:: ../../python/kvikio/examples/zarr_cupy_nvcomp.py
     :language: python
diff --git a/python/README.md b/python/README.md
deleted file mode 120000
index 32d46ee883..0000000000
--- a/python/README.md
+++ /dev/null
@@ -1,1 +0,0 @@
-../README.md
\ No newline at end of file
diff --git a/python/.coveragerc b/python/kvikio/.coveragerc
similarity index 100%
rename from python/.coveragerc
rename to python/kvikio/.coveragerc
diff --git a/python/CMakeLists.txt b/python/kvikio/CMakeLists.txt
similarity index 91%
rename from python/CMakeLists.txt
rename to python/kvikio/CMakeLists.txt
index 039daf7ce9..e93ab99016 100644
--- a/python/CMakeLists.txt
+++ b/python/kvikio/CMakeLists.txt
@@ -14,7 +14,7 @@
 
 cmake_minimum_required(VERSION 3.26.4 FATAL_ERROR)
 
-include(../cpp/cmake/rapids_config.cmake)
+include(../../cpp/cmake/rapids_config.cmake)
 include(rapids-cpm)
 rapids_cpm_init()
 include(rapids-cuda)
@@ -31,7 +31,7 @@ option(FIND_KVIKIO_CPP
 )
 
 # TODO: Should we symlink FindcuFile.cmake into python/cmake? find cuFile
-include(../cpp/cmake/Modules/FindcuFile.cmake)
+include(../../cpp/cmake/Modules/FindcuFile.cmake)
 
 if(FIND_KVIKIO_CPP)
   find_package(KvikIO "${RAPIDS_VERSION}")
@@ -42,7 +42,7 @@ endif()
 find_package(CUDAToolkit REQUIRED)
 
 if(NOT KvikIO_FOUND)
-  add_subdirectory(../cpp kvikio-cpp)
+  add_subdirectory(../../cpp kvikio-cpp)
   set(cython_lib_dir kvikio)
   install(TARGETS kvikio DESTINATION ${cython_lib_dir})
 endif()
diff --git a/python/kvikio/README.md b/python/kvikio/README.md
new file mode 120000
index 0000000000..fe84005413
--- /dev/null
+++ b/python/kvikio/README.md
@@ -0,0 +1,1 @@
+../../README.md
\ No newline at end of file
diff --git a/python/kvikio/VERSION b/python/kvikio/VERSION
deleted file mode 120000
index 558194c5a5..0000000000
--- a/python/kvikio/VERSION
+++ /dev/null
@@ -1,1 +0,0 @@
-../../VERSION
\ No newline at end of file
diff --git a/python/benchmarks/single-node-io.py b/python/kvikio/benchmarks/single-node-io.py
similarity index 100%
rename from python/benchmarks/single-node-io.py
rename to python/kvikio/benchmarks/single-node-io.py
diff --git a/python/benchmarks/zarr-io.py b/python/kvikio/benchmarks/zarr-io.py
similarity index 100%
rename from python/benchmarks/zarr-io.py
rename to python/kvikio/benchmarks/zarr-io.py
diff --git a/python/cli/gpu_compressor.py b/python/kvikio/cli/gpu_compressor.py
similarity index 100%
rename from python/cli/gpu_compressor.py
rename to python/kvikio/cli/gpu_compressor.py
diff --git a/python/cmake/CMakeLists.txt b/python/kvikio/cmake/CMakeLists.txt
similarity index 100%
rename from python/cmake/CMakeLists.txt
rename to python/kvikio/cmake/CMakeLists.txt
diff --git a/python/cmake/thirdparty/get_nvcomp.cmake b/python/kvikio/cmake/thirdparty/get_nvcomp.cmake
similarity index 100%
rename from python/cmake/thirdparty/get_nvcomp.cmake
rename to python/kvikio/cmake/thirdparty/get_nvcomp.cmake
diff --git a/python/examples/hello_world.py b/python/kvikio/examples/hello_world.py
similarity index 100%
rename from python/examples/hello_world.py
rename to python/kvikio/examples/hello_world.py
diff --git a/python/examples/zarr_cupy_nvcomp.py b/python/kvikio/examples/zarr_cupy_nvcomp.py
similarity index 100%
rename from python/examples/zarr_cupy_nvcomp.py
rename to python/kvikio/examples/zarr_cupy_nvcomp.py
diff --git a/python/kvikio/kvikio/VERSION b/python/kvikio/kvikio/VERSION
new file mode 120000
index 0000000000..d62dc733ef
--- /dev/null
+++ b/python/kvikio/kvikio/VERSION
@@ -0,0 +1,1 @@
+../../../VERSION
\ No newline at end of file
diff --git a/python/kvikio/__init__.py b/python/kvikio/kvikio/__init__.py
similarity index 100%
rename from python/kvikio/__init__.py
rename to python/kvikio/kvikio/__init__.py
diff --git a/python/kvikio/_lib/CMakeLists.txt b/python/kvikio/kvikio/_lib/CMakeLists.txt
similarity index 100%
rename from python/kvikio/_lib/CMakeLists.txt
rename to python/kvikio/kvikio/_lib/CMakeLists.txt
diff --git a/python/kvikio/_lib/__init__.pxd b/python/kvikio/kvikio/_lib/__init__.pxd
similarity index 100%
rename from python/kvikio/_lib/__init__.pxd
rename to python/kvikio/kvikio/_lib/__init__.pxd
diff --git a/python/kvikio/_lib/__init__.py b/python/kvikio/kvikio/_lib/__init__.py
similarity index 100%
rename from python/kvikio/_lib/__init__.py
rename to python/kvikio/kvikio/_lib/__init__.py
diff --git a/python/kvikio/_lib/arr.pxd b/python/kvikio/kvikio/_lib/arr.pxd
similarity index 100%
rename from python/kvikio/_lib/arr.pxd
rename to python/kvikio/kvikio/_lib/arr.pxd
diff --git a/python/kvikio/_lib/arr.pyi b/python/kvikio/kvikio/_lib/arr.pyi
similarity index 100%
rename from python/kvikio/_lib/arr.pyi
rename to python/kvikio/kvikio/_lib/arr.pyi
diff --git a/python/kvikio/_lib/arr.pyx b/python/kvikio/kvikio/_lib/arr.pyx
similarity index 100%
rename from python/kvikio/_lib/arr.pyx
rename to python/kvikio/kvikio/_lib/arr.pyx
diff --git a/python/kvikio/_lib/kvikio_cxx_api.pxd b/python/kvikio/kvikio/_lib/kvikio_cxx_api.pxd
similarity index 100%
rename from python/kvikio/_lib/kvikio_cxx_api.pxd
rename to python/kvikio/kvikio/_lib/kvikio_cxx_api.pxd
diff --git a/python/kvikio/_lib/libkvikio.pyx b/python/kvikio/kvikio/_lib/libkvikio.pyx
similarity index 100%
rename from python/kvikio/_lib/libkvikio.pyx
rename to python/kvikio/kvikio/_lib/libkvikio.pyx
diff --git a/python/kvikio/_lib/libnvcomp.pyx b/python/kvikio/kvikio/_lib/libnvcomp.pyx
similarity index 100%
rename from python/kvikio/_lib/libnvcomp.pyx
rename to python/kvikio/kvikio/_lib/libnvcomp.pyx
diff --git a/python/kvikio/_lib/libnvcomp_ll.pyx b/python/kvikio/kvikio/_lib/libnvcomp_ll.pyx
similarity index 100%
rename from python/kvikio/_lib/libnvcomp_ll.pyx
rename to python/kvikio/kvikio/_lib/libnvcomp_ll.pyx
diff --git a/python/kvikio/_lib/nvcomp_cxx_api.pxd b/python/kvikio/kvikio/_lib/nvcomp_cxx_api.pxd
similarity index 100%
rename from python/kvikio/_lib/nvcomp_cxx_api.pxd
rename to python/kvikio/kvikio/_lib/nvcomp_cxx_api.pxd
diff --git a/python/kvikio/_lib/nvcomp_ll_cxx_api.pxd b/python/kvikio/kvikio/_lib/nvcomp_ll_cxx_api.pxd
similarity index 100%
rename from python/kvikio/_lib/nvcomp_ll_cxx_api.pxd
rename to python/kvikio/kvikio/_lib/nvcomp_ll_cxx_api.pxd
diff --git a/python/kvikio/_version.py b/python/kvikio/kvikio/_version.py
similarity index 100%
rename from python/kvikio/_version.py
rename to python/kvikio/kvikio/_version.py
diff --git a/python/kvikio/cufile.py b/python/kvikio/kvikio/cufile.py
similarity index 100%
rename from python/kvikio/cufile.py
rename to python/kvikio/kvikio/cufile.py
diff --git a/python/kvikio/defaults.py b/python/kvikio/kvikio/defaults.py
similarity index 100%
rename from python/kvikio/defaults.py
rename to python/kvikio/kvikio/defaults.py
diff --git a/python/kvikio/numcodecs.py b/python/kvikio/kvikio/numcodecs.py
similarity index 100%
rename from python/kvikio/numcodecs.py
rename to python/kvikio/kvikio/numcodecs.py
diff --git a/python/kvikio/numpy.py b/python/kvikio/kvikio/numpy.py
similarity index 100%
rename from python/kvikio/numpy.py
rename to python/kvikio/kvikio/numpy.py
diff --git a/python/kvikio/nvcomp.py b/python/kvikio/kvikio/nvcomp.py
similarity index 100%
rename from python/kvikio/nvcomp.py
rename to python/kvikio/kvikio/nvcomp.py
diff --git a/python/kvikio/nvcomp_codec.py b/python/kvikio/kvikio/nvcomp_codec.py
similarity index 100%
rename from python/kvikio/nvcomp_codec.py
rename to python/kvikio/kvikio/nvcomp_codec.py
diff --git a/python/kvikio/zarr.py b/python/kvikio/kvikio/zarr.py
similarity index 100%
rename from python/kvikio/zarr.py
rename to python/kvikio/kvikio/zarr.py
diff --git a/python/pyproject.toml b/python/kvikio/pyproject.toml
similarity index 87%
rename from python/pyproject.toml
rename to python/kvikio/pyproject.toml
index c1b99ed583..671413a275 100644
--- a/python/pyproject.toml
+++ b/python/kvikio/pyproject.toml
@@ -1,4 +1,4 @@
-# Copyright (c) 2021-2022, NVIDIA CORPORATION. All rights reserved.
+# Copyright (c) 2021-2024, NVIDIA CORPORATION. All rights reserved.
 # See file LICENSE for terms.
 
 [build-system]
@@ -6,7 +6,7 @@ build-backend = "rapids_build_backend.build"
 requires = [
     "rapids-build-backend>=0.3.0,<0.4.0.dev0",
     "scikit-build-core[pyproject]>=0.7.0",
-] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../dependencies.yaml and run `rapids-dependency-file-generator`.
+] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`.
 
 [project]
 name = "kvikio"
@@ -24,7 +24,7 @@ dependencies = [
     "numpy>=1.23,<2.0a0",
     "packaging",
     "zarr",
-] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../dependencies.yaml and run `rapids-dependency-file-generator`.
+] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`.
 classifiers = [
     "Intended Audience :: Developers",
     "Topic :: Database",
@@ -42,7 +42,7 @@ test = [
     "dask>=2022.05.2",
     "pytest",
     "pytest-cov",
-] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../dependencies.yaml and run `rapids-dependency-file-generator`.
+] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`.
 
 [project.urls]
 Homepage = "https://github.com/rapidsai/kvikio"
@@ -114,7 +114,7 @@ requires = [
     "cmake>=3.26.4",
     "cython>=3.0.0",
     "ninja",
-] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../dependencies.yaml and run `rapids-dependency-file-generator`.
+] # This list was generated by `rapids-dependency-file-generator`. To make changes, edit ../../dependencies.yaml and run `rapids-dependency-file-generator`.
 
 [tool.scikit-build]
 build-dir = "build/{wheel_tag}"


Test patch:
diff --git a/ci/run_pytests.sh b/ci/run_pytests.sh
index 73de68d03b..b2c93dbe56 100755
--- a/ci/run_pytests.sh
+++ b/ci/run_pytests.sh
@@ -4,6 +4,6 @@
 set -euo pipefail
 
 # Support invoking run_pytests.sh outside the script directory
-cd "$(dirname "$(realpath "${BASH_SOURCE[0]}")")"/../python/
+cd "$(dirname "$(realpath "${BASH_SOURCE[0]}")")"/../python/kvikio
 
 pytest --cache-clear --verbose "$@" tests
diff --git a/python/tests/conftest.py b/python/kvikio/tests/conftest.py
similarity index 100%
rename from python/tests/conftest.py
rename to python/kvikio/tests/conftest.py
diff --git a/python/tests/test_async_io.py b/python/kvikio/tests/test_async_io.py
similarity index 100%
rename from python/tests/test_async_io.py
rename to python/kvikio/tests/test_async_io.py
diff --git a/python/tests/test_basic_io.py b/python/kvikio/tests/test_basic_io.py
similarity index 100%
rename from python/tests/test_basic_io.py
rename to python/kvikio/tests/test_basic_io.py
diff --git a/python/tests/test_benchmarks.py b/python/kvikio/tests/test_benchmarks.py
similarity index 100%
rename from python/tests/test_benchmarks.py
rename to python/kvikio/tests/test_benchmarks.py
diff --git a/python/tests/test_defaults.py b/python/kvikio/tests/test_defaults.py
similarity index 100%
rename from python/tests/test_defaults.py
rename to python/kvikio/tests/test_defaults.py
diff --git a/python/tests/test_examples.py b/python/kvikio/tests/test_examples.py
similarity index 100%
rename from python/tests/test_examples.py
rename to python/kvikio/tests/test_examples.py
diff --git a/python/tests/test_numpy.py b/python/kvikio/tests/test_numpy.py
similarity index 100%
rename from python/tests/test_numpy.py
rename to python/kvikio/tests/test_numpy.py
diff --git a/python/tests/test_nvcomp.py b/python/kvikio/tests/test_nvcomp.py
similarity index 100%
rename from python/tests/test_nvcomp.py
rename to python/kvikio/tests/test_nvcomp.py
diff --git a/python/tests/test_nvcomp_codec.py b/python/kvikio/tests/test_nvcomp_codec.py
similarity index 100%
rename from python/tests/test_nvcomp_codec.py
rename to python/kvikio/tests/test_nvcomp_codec.py
diff --git a/python/tests/test_version.py b/python/kvikio/tests/test_version.py
similarity index 100%
rename from python/tests/test_version.py
rename to python/kvikio/tests/test_version.py
diff --git a/python/tests/test_zarr.py b/python/kvikio/tests/test_zarr.py
similarity index 100%
rename from python/tests/test_zarr.py
rename to python/kvikio/tests/test_zarr.py