Skip to content

Commit 03a4a3c

Browse files
authored
Merge pull request #1 from max-models/add-arrays-module
Initial setup
2 parents 488a085 + 3dc3476 commit 03a4a3c

10 files changed

Lines changed: 121 additions & 31 deletions

File tree

.github/workflows/testing.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,6 @@ jobs:
4444
pip install --upgrade pip
4545
pip install ".[dev]"
4646
47-
- name: Check installation
48-
run: |
49-
template-python
50-
5147
- name: Run tests
5248
run: |
5349
pytest .

README.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
# template-python
1+
# CuNumpy
22

3-
Template repository for python projects
4-
5-
Documentation: https://max-models.github.io/template-python/
3+
Simple wrapper for numpy and cupy. Replace `import numpy as np` with `import cunumpy as xp`.
64

75
# Install
86

@@ -20,10 +18,18 @@ Install the code and requirements with pip
2018
pip install -e .
2119
```
2220

23-
Run the code with
21+
Example usage:
2422

2523
```
26-
template-python
24+
export ARRAY_BACKEND=cupy
25+
```
26+
27+
```python
28+
import cunumpy as xp
29+
arr = xp.array([1,2])
30+
31+
print(type(arr))
32+
print(xp.__version__)
2733
```
2834

2935
# Build docs

docs/source/conf.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import os
77
import shutil
88

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

29-
project = "python-template"
30+
project = "cunumpy"
3031
copyright = "2025, Max"
3132
author = "Max"
3233

@@ -68,10 +69,9 @@ def setup(app):
6869
"icon_links": [
6970
{
7071
"name": "GitHub",
71-
"url": "https://github.com/max-models/template-python",
72+
"url": "https://github.com/max-models/cunumpy",
7273
"icon": "fab fa-github",
7374
"type": "fontawesome",
7475
},
7576
],
7677
}
77-

docs/source/quickstart.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ Clone the repo
66
git clone ...
77
```
88

9+
# Install
10+
911
Create and activate python environment
1012

1113
```
@@ -20,19 +22,25 @@ Install the code and requirements with pip
2022
pip install -e .
2123
```
2224

23-
Run the code with
25+
Example usage:
2426

2527
```
26-
template-python
28+
export ARRAY_BACKEND=cupy
29+
```
30+
31+
```python
32+
import cunumpy as xp
33+
arr = xp.array([1,2])
34+
35+
print(type(arr))
36+
print(xp.__version__)
2737
```
2838

2939
# Build docs
3040

41+
3142
```
3243
make html
3344
cd ../
3445
open docs/_build/html/index.html
3546
```
36-
37-
```{toctree}
38-
:maxdepth: 1

pyproject.toml

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ build-backend = "setuptools.build_meta"
44
requires = [ "setuptools", "wheel" ]
55

66
[project]
7-
name = "template-python"
7+
name = "cunumpy"
88
version = "0.1"
9-
description = "Template python repository."
9+
description = "Simple wrapper for numpy and cupy. Replace `import numpy as np` with `import cunumpy as xp`."
1010
readme = "README.md"
1111
keywords = [ "python" ]
1212
license = { file = "LICENSE.txt" }
@@ -29,8 +29,7 @@ dependencies = [
2929
optional-dependencies.dev = [
3030
"black[jupyter]",
3131
"isort",
32-
"ruff",
33-
"template-python[test,docs]",
32+
"cunumpy[test,docs]",
3433
]
3534
# https://medium.com/@pratikdomadiya123/build-project-documentation-quickly-with-the-sphinx-python-2a9732b66594
3635
optional-dependencies.docs = [
@@ -45,8 +44,7 @@ optional-dependencies.docs = [
4544
"sphinx-book-theme",
4645
]
4746
optional-dependencies.test = [ "coverage", "pytest" ]
48-
urls."Source" = "https://github.com/max-models/template-python"
49-
scripts.template-python = "app.main:main"
47+
urls."Source" = "https://github.com/max-models/cunumpy"
5048

5149
[tool.setuptools.packages.find]
5250
where = [ "src" ]

src/cunumpy/__init__.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# cunumpy/__init__.py
2+
from . import xp
3+
4+
__all__ = ["xp"]
5+
6+
7+
def __getattr__(name: str):
8+
"""Set cunumpy.<name> to cunumpy.xp.<name> (NumPy/CuPy)."""
9+
return getattr(xp.xp, name)
File renamed without changes.

src/cunumpy/xp.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import os
2+
from types import ModuleType
3+
from typing import TYPE_CHECKING, Literal
4+
5+
BackendType = Literal["numpy", "cupy"]
6+
7+
8+
class ArrayBackend:
9+
def __init__(
10+
self,
11+
backend: BackendType = "numpy",
12+
verbose: bool = False,
13+
) -> None:
14+
assert backend.lower() in [
15+
"numpy",
16+
"cupy",
17+
], "Array backend must be either 'numpy' or 'cupy'."
18+
19+
self._backend: BackendType = "cupy" if backend.lower() == "cupy" else "numpy"
20+
21+
# Import numpy/cupy
22+
if self.backend == "cupy":
23+
try:
24+
import cupy as cp
25+
26+
self._xp = cp
27+
except ImportError:
28+
if verbose:
29+
print("CuPy not available.")
30+
self._backend = "numpy"
31+
32+
if self.backend == "numpy":
33+
import numpy as np
34+
35+
self._xp = np
36+
37+
assert isinstance(self.xp, ModuleType)
38+
39+
if verbose:
40+
print(f"Using {self.xp.__name__} backend.")
41+
42+
@property
43+
def backend(self) -> BackendType:
44+
return self._backend
45+
46+
@property
47+
def xp(self) -> ModuleType:
48+
return self._xp
49+
50+
51+
# TODO: Make this configurable via environment variable or config file.
52+
array_backend = ArrayBackend(
53+
backend=(
54+
"cupy" if os.getenv("ARRAY_BACKEND", "numpy").lower() == "cupy" else "numpy"
55+
),
56+
verbose=False,
57+
)
58+
59+
# TYPE_CHECKING is True when type checking (e.g., mypy), but False at runtime.
60+
# This allows us to use autocompletion for xp (i.e., numpy/cupy) as if numpy was imported.
61+
if TYPE_CHECKING:
62+
import numpy as xp
63+
else:
64+
xp = array_backend.xp

tests/unit/test_app.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1-
def test_import_app():
2-
from app.main import main
1+
import cunumpy as xp
32

4-
print("app imported")
5-
main()
3+
4+
def test_xp_array():
5+
6+
arr = xp.array([1, 2])
7+
arr *= 2
8+
9+
print(f"{arr = } {type(arr) = }")
610

711

812
if __name__ == "__main__":
9-
test_import_app()
13+
test_xp_array()

tutorials/example_tutorial.ipynb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
},
1111
{
1212
"cell_type": "code",
13-
"execution_count": 1,
13+
"execution_count": null,
1414
"id": "68ad1562-4953-444c-96c7-9026bcc54cc7",
1515
"metadata": {},
1616
"outputs": [
@@ -23,7 +23,12 @@
2323
}
2424
],
2525
"source": [
26-
"print(\"Example tutorial which will be published in the docs!\")"
26+
"import cunumpy as xp\n",
27+
"\n",
28+
"arr = xp.array([1, 2])\n",
29+
"arr *= 2\n",
30+
"\n",
31+
"print(f\"{arr = } {type(arr) = }\")"
2732
]
2833
}
2934
],

0 commit comments

Comments
 (0)