Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,9 @@ jobs:
- name: Build the package
run: python -m build

# Uncomment to publish on pypi
#- name: Publish to PyPI
# env:
# TWINE_USERNAME: __token__ # Use API token
# TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
# run: twine upload dist/*
- name: Publish to PyPI
env:
TWINE_USERNAME: __token__ # Use API token
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: twine upload dist/*

4 changes: 0 additions & 4 deletions .github/workflows/testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,6 @@ jobs:
pip install --upgrade pip
pip install ".[dev]"

- name: Check installation
run: |
template-python

- name: Run tests
run: |
pytest .
Expand Down
18 changes: 12 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# template-python
# CuNumpy

Template repository for python projects

Documentation: https://max-models.github.io/template-python/
Simple wrapper for numpy and cupy. Replace `import numpy as np` with `import cunumpy as xp`.

# Install

Expand All @@ -20,10 +18,18 @@ Install the code and requirements with pip
pip install -e .
```

Run the code with
Example usage:

```
template-python
export ARRAY_BACKEND=cupy
```

```python
import cunumpy as xp
arr = xp.array([1,2])

print(type(arr))
print(xp.__version__)
```

# Build docs
Expand Down
6 changes: 3 additions & 3 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import os
import shutil


def copy_tutorials(app):
src = os.path.abspath("../tutorials")
dst = os.path.abspath("source/tutorials")
Expand All @@ -26,7 +27,7 @@ def setup(app):
# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information

project = "python-template"
project = "cunumpy"
copyright = "2025, Max"
author = "Max"

Expand Down Expand Up @@ -68,10 +69,9 @@ def setup(app):
"icon_links": [
{
"name": "GitHub",
"url": "https://github.com/max-models/template-python",
"url": "https://github.com/max-models/cunumpy",
"icon": "fab fa-github",
"type": "fontawesome",
},
],
}

18 changes: 13 additions & 5 deletions docs/source/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ Clone the repo
git clone ...
```

# Install

Create and activate python environment

```
Expand All @@ -20,19 +22,25 @@ Install the code and requirements with pip
pip install -e .
```

Run the code with
Example usage:

```
template-python
export ARRAY_BACKEND=cupy
```

```python
import cunumpy as xp
arr = xp.array([1,2])

print(type(arr))
print(xp.__version__)
```

# Build docs


```
make html
cd ../
open docs/_build/html/index.html
```

```{toctree}
:maxdepth: 1
10 changes: 4 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ build-backend = "setuptools.build_meta"
requires = [ "setuptools", "wheel" ]

[project]
name = "template-python"
name = "cunumpy"
version = "0.1"
description = "Template python repository."
description = "Simple wrapper for numpy and cupy. Replace `import numpy as np` with `import cunumpy as xp`."
readme = "README.md"
keywords = [ "python" ]
license = { file = "LICENSE.txt" }
Expand All @@ -29,8 +29,7 @@ dependencies = [
optional-dependencies.dev = [
"black[jupyter]",
"isort",
"ruff",
"template-python[test,docs]",
"cunumpy[test,docs]",
]
# https://medium.com/@pratikdomadiya123/build-project-documentation-quickly-with-the-sphinx-python-2a9732b66594
optional-dependencies.docs = [
Expand All @@ -45,8 +44,7 @@ optional-dependencies.docs = [
"sphinx-book-theme",
]
optional-dependencies.test = [ "coverage", "pytest" ]
urls."Source" = "https://github.com/max-models/template-python"
scripts.template-python = "app.main:main"
urls."Source" = "https://github.com/max-models/cunumpy"

[tool.setuptools.packages.find]
where = [ "src" ]
9 changes: 9 additions & 0 deletions src/cunumpy/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# cunumpy/__init__.py
from . import xp

__all__ = ["xp"]


def __getattr__(name: str):
"""Set cunumpy.<name> to cunumpy.xp.<name> (NumPy/CuPy)."""
return getattr(xp.xp, name)
File renamed without changes.
64 changes: 64 additions & 0 deletions src/cunumpy/xp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import os
from types import ModuleType
from typing import TYPE_CHECKING, Literal

BackendType = Literal["numpy", "cupy"]


class ArrayBackend:
def __init__(
self,
backend: BackendType = "numpy",
verbose: bool = False,
) -> None:
assert backend.lower() in [
"numpy",
"cupy",
], "Array backend must be either 'numpy' or 'cupy'."

self._backend: BackendType = "cupy" if backend.lower() == "cupy" else "numpy"

# Import numpy/cupy
if self.backend == "cupy":
try:
import cupy as cp

self._xp = cp
except ImportError:
if verbose:
print("CuPy not available.")
self._backend = "numpy"

if self.backend == "numpy":
import numpy as np

self._xp = np

assert isinstance(self.xp, ModuleType)

if verbose:
print(f"Using {self.xp.__name__} backend.")

@property
def backend(self) -> BackendType:
return self._backend

@property
def xp(self) -> ModuleType:
return self._xp


# TODO: Make this configurable via environment variable or config file.
array_backend = ArrayBackend(
backend=(
"cupy" if os.getenv("ARRAY_BACKEND", "numpy").lower() == "cupy" else "numpy"
),
verbose=False,
)

# TYPE_CHECKING is True when type checking (e.g., mypy), but False at runtime.
# This allows us to use autocompletion for xp (i.e., numpy/cupy) as if numpy was imported.
if TYPE_CHECKING:
import numpy as xp
else:
xp = array_backend.xp
14 changes: 9 additions & 5 deletions tests/unit/test_app.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
def test_import_app():
from app.main import main
import cunumpy as xp

print("app imported")
main()

def test_xp_array():

arr = xp.array([1, 2])
arr *= 2

print(f"{arr = } {type(arr) = }")


if __name__ == "__main__":
test_import_app()
test_xp_array()
11 changes: 8 additions & 3 deletions tutorials/example_tutorial.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,17 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Example tutorial which will be published in the docs!\n"
"arr = array([2, 4]) type(arr) = <class 'cupy.ndarray'>\n"
]
}
],
"source": [
"print(\"Example tutorial which will be published in the docs!\")"
"import cunumpy as xp\n",
"\n",
"arr = xp.array([1, 2])\n",
"arr *= 2\n",
"\n",
"print(f\"{arr = } {type(arr) = }\")"
]
}
],
Expand All @@ -43,7 +48,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.3"
"version": "3.12.3"
}
},
"nbformat": 4,
Expand Down