arraybridge 0.2.0__tar.gz → 0.2.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- arraybridge-0.2.9/.github/badges/coverage.svg +1 -0
- arraybridge-0.2.9/.github/workflows/ci.yml +158 -0
- arraybridge-0.2.9/.github/workflows/coverage-pages.yml +115 -0
- arraybridge-0.2.9/.github/workflows/gpu-tests.yml +79 -0
- arraybridge-0.2.9/ADDING_NEW_FRAMEWORKS.md +215 -0
- arraybridge-0.2.9/CI_ARTIFACT_UPDATE.md +47 -0
- arraybridge-0.2.9/CI_CUPY_FIX.md +104 -0
- arraybridge-0.2.9/COVERAGE_AUDIT_PLAN.md +410 -0
- arraybridge-0.2.9/DEPLOYMENT_SUMMARY.md +62 -0
- arraybridge-0.2.9/GPU_TESTING_SETUP.md +165 -0
- arraybridge-0.2.9/MIGRATION_SUMMARY.md +193 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/PKG-INFO +20 -5
- {arraybridge-0.2.0 → arraybridge-0.2.9}/README.md +3 -1
- arraybridge-0.2.9/SETUP_INSTRUCTIONS.md +64 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/docs/source/stack_utils.rst +43 -40
- arraybridge-0.2.9/paper.bib +76 -0
- arraybridge-0.2.9/paper.md +149 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/pyproject.toml +37 -7
- arraybridge-0.2.9/scripts/release.py +63 -0
- arraybridge-0.2.9/scripts/update_and_release.py +114 -0
- arraybridge-0.2.9/scripts/update_version.py +111 -0
- arraybridge-0.2.9/scripts/verify_release_ready.py +274 -0
- arraybridge-0.2.9/src/arraybridge/__init__.py +70 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/src/arraybridge/converters.py +17 -7
- arraybridge-0.2.9/src/arraybridge/converters_registry.py +196 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/src/arraybridge/decorators.py +91 -101
- {arraybridge-0.2.0 → arraybridge-0.2.9}/src/arraybridge/dtype_scaling.py +52 -30
- arraybridge-0.2.9/src/arraybridge/framework_config.py +442 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/src/arraybridge/framework_ops.py +0 -1
- {arraybridge-0.2.0 → arraybridge-0.2.9}/src/arraybridge/gpu_cleanup.py +20 -28
- {arraybridge-0.2.0 → arraybridge-0.2.9}/src/arraybridge/oom_recovery.py +8 -8
- {arraybridge-0.2.0 → arraybridge-0.2.9}/src/arraybridge/slice_processing.py +7 -3
- {arraybridge-0.2.0 → arraybridge-0.2.9}/src/arraybridge/stack_utils.py +28 -31
- {arraybridge-0.2.0 → arraybridge-0.2.9}/src/arraybridge/types.py +2 -25
- {arraybridge-0.2.0 → arraybridge-0.2.9}/src/arraybridge/utils.py +44 -30
- {arraybridge-0.2.0 → arraybridge-0.2.9}/tests/conftest.py +46 -5
- {arraybridge-0.2.0 → arraybridge-0.2.9}/tests/test_converters.py +7 -5
- arraybridge-0.2.9/tests/test_converters_registry.py +149 -0
- arraybridge-0.2.9/tests/test_decorators.py +234 -0
- arraybridge-0.2.9/tests/test_dtype_scaling.py +331 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/tests/test_exceptions.py +5 -7
- arraybridge-0.2.9/tests/test_framework_config.py +412 -0
- arraybridge-0.2.9/tests/test_gpu_cleanup.py +244 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/tests/test_integration.py +8 -6
- arraybridge-0.2.9/tests/test_oom_recovery.py +141 -0
- arraybridge-0.2.9/tests/test_registry_integration.py +131 -0
- arraybridge-0.2.9/tests/test_slice_processing.py +121 -0
- arraybridge-0.2.9/tests/test_stack_utils.py +169 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/tests/test_types.py +2 -1
- arraybridge-0.2.9/tests/test_utils.py +404 -0
- arraybridge-0.2.0/.github/workflows/ci.yml +0 -153
- arraybridge-0.2.0/.github/workflows/gpu-tests.yml +0 -69
- arraybridge-0.2.0/src/arraybridge/__init__.py +0 -37
- arraybridge-0.2.0/src/arraybridge/conversion_helpers.py +0 -150
- arraybridge-0.2.0/src/arraybridge/framework_config.py +0 -459
- arraybridge-0.2.0/tests/test_utils.py +0 -151
- {arraybridge-0.2.0 → arraybridge-0.2.9}/.github/workflows/publish.yml +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/.gitignore +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/.readthedocs.yml +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/CI_IMPROVEMENTS.md +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/CONFLICT_VISUALIZATION.md +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/CONTRIBUTING.md +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/LICENSE +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/MERGE_CONFLICT_RESOLUTION.md +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/QUICK_FIX_GUIDE.txt +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/RESOLVED_FILES.md +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/TESTING.md +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/docs/Makefile +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/docs/ci-cd.md +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/docs/source/.gitignore +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/docs/source/Makefile +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/docs/source/README.md +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/docs/source/advanced_topics.rst +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/docs/source/api/converters.rst +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/docs/source/api/decorators.rst +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/docs/source/api/exceptions.rst +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/docs/source/api/index.rst +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/docs/source/api/types.rst +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/docs/source/api/utils.rst +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/docs/source/api_reference.rst +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/docs/source/ci-cd.rst +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/docs/source/conf.py +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/docs/source/contributing.rst +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/docs/source/converters.rst +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/docs/source/decorators.rst +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/docs/source/examples/basic_conversion.rst +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/docs/source/examples/decorators.rst +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/docs/source/examples/index.rst +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/docs/source/examples/multi_framework.rst +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/docs/source/gpu_features.rst +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/docs/source/index.rst +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/docs/source/installation.rst +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/docs/source/quickstart.rst +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/docs/source/user_guide.rst +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/src/arraybridge/exceptions.py +0 -0
- {arraybridge-0.2.0 → arraybridge-0.2.9}/tests/__init__.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="114" height="20" role="img" aria-label="coverage: 77.70%"><title>coverage: 77.70%</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="114" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="61" height="20" fill="#555"/><rect x="61" width="53" height="20" fill="#97ca00"/><rect width="114" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="315" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="510">coverage</text><text x="315" y="140" transform="scale(.1)" fill="#fff" textLength="510">coverage</text><text aria-hidden="true" x="865" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="430">77.70%</text><text x="865" y="140" transform="scale(.1)" fill="#fff" textLength="430">77.70%</text></g></svg>
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, master]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
# Test across Python versions with CPU-compatible frameworks (no torch)
|
|
12
|
+
test:
|
|
13
|
+
runs-on: ${{ matrix.os }}
|
|
14
|
+
strategy:
|
|
15
|
+
fail-fast: false
|
|
16
|
+
matrix:
|
|
17
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
18
|
+
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
19
|
+
|
|
20
|
+
steps:
|
|
21
|
+
- name: Checkout
|
|
22
|
+
uses: actions/checkout@v4
|
|
23
|
+
|
|
24
|
+
- name: Setup Python ${{ matrix.python-version }}
|
|
25
|
+
uses: actions/setup-python@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version: ${{ matrix.python-version }}
|
|
28
|
+
|
|
29
|
+
- name: Install base dependencies
|
|
30
|
+
run: |
|
|
31
|
+
python -m pip install --upgrade pip
|
|
32
|
+
pip install -e ".[dev]"
|
|
33
|
+
|
|
34
|
+
- name: Run tests with coverage
|
|
35
|
+
run: |
|
|
36
|
+
pytest --cov=arraybridge --cov-report=xml --cov-report=html --cov-report=term-missing -v
|
|
37
|
+
|
|
38
|
+
- name: Upload coverage to Codecov
|
|
39
|
+
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12'
|
|
40
|
+
uses: codecov/codecov-action@v3
|
|
41
|
+
with:
|
|
42
|
+
file: ./coverage.xml
|
|
43
|
+
fail_ci_if_error: false
|
|
44
|
+
|
|
45
|
+
# GPU tests - includes framework testing
|
|
46
|
+
# Note: GitHub Actions ubuntu-latest doesn't have physical GPU,
|
|
47
|
+
# but tests will run the "unavailable GPU" code paths and mock GPU tests
|
|
48
|
+
gpu-test:
|
|
49
|
+
runs-on: ubuntu-latest
|
|
50
|
+
continue-on-error: true # Don't block PR merges if GPU not available
|
|
51
|
+
|
|
52
|
+
steps:
|
|
53
|
+
- name: Checkout
|
|
54
|
+
uses: actions/checkout@v4
|
|
55
|
+
|
|
56
|
+
- name: Setup Python
|
|
57
|
+
uses: actions/setup-python@v5
|
|
58
|
+
with:
|
|
59
|
+
python-version: "3.12"
|
|
60
|
+
|
|
61
|
+
- name: Install base dependencies
|
|
62
|
+
run: |
|
|
63
|
+
python -m pip install --upgrade pip
|
|
64
|
+
pip install -e ".[dev]"
|
|
65
|
+
|
|
66
|
+
- name: Install GPU frameworks (will use CPU versions in CI)
|
|
67
|
+
run: |
|
|
68
|
+
# PyTorch - CPU version will be installed in CI (no GPU available)
|
|
69
|
+
pip install torch torchvision torchaudio 2>&1 || echo "PyTorch install attempted"
|
|
70
|
+
|
|
71
|
+
# JAX - CPU version
|
|
72
|
+
pip install jax jaxlib 2>&1 || echo "JAX install skipped (optional)"
|
|
73
|
+
|
|
74
|
+
# CuPy - will fail without CUDA, that's ok
|
|
75
|
+
pip install cupy-cuda12x 2>&1 || echo "CuPy skipped (requires actual CUDA)"
|
|
76
|
+
|
|
77
|
+
- name: Check framework availability
|
|
78
|
+
run: |
|
|
79
|
+
python -c "
|
|
80
|
+
print('=== Framework Availability Check ===')
|
|
81
|
+
try:
|
|
82
|
+
import torch
|
|
83
|
+
print(f'✓ PyTorch available')
|
|
84
|
+
print(f' CUDA available: {torch.cuda.is_available()}')
|
|
85
|
+
print(f' (This is normal - GitHub Actions has no physical GPU)')
|
|
86
|
+
except ImportError:
|
|
87
|
+
print('✗ PyTorch not available')
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
import jax
|
|
91
|
+
print(f'✓ JAX available')
|
|
92
|
+
except ImportError:
|
|
93
|
+
print('✗ JAX not available')
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
import cupy
|
|
97
|
+
print(f'✓ CuPy available')
|
|
98
|
+
except ImportError:
|
|
99
|
+
print('✗ CuPy not available (normal - requires CUDA)')
|
|
100
|
+
" || true
|
|
101
|
+
|
|
102
|
+
- name: Run GPU cleanup tests
|
|
103
|
+
run: |
|
|
104
|
+
# Tests include:
|
|
105
|
+
# 1. Framework unavailable tests (always run)
|
|
106
|
+
# 2. GPU unavailable fallback paths (will run in CI)
|
|
107
|
+
# 3. Mocked GPU tests (test cleanup code with mocked GPU state)
|
|
108
|
+
pytest -v tests/test_gpu_cleanup.py \
|
|
109
|
+
--cov=arraybridge \
|
|
110
|
+
--cov-report=term-missing \
|
|
111
|
+
--cov-report=html \
|
|
112
|
+
--cov-report=xml \
|
|
113
|
+
-ra
|
|
114
|
+
|
|
115
|
+
- name: Upload GPU test coverage to Codecov
|
|
116
|
+
uses: codecov/codecov-action@v3
|
|
117
|
+
with:
|
|
118
|
+
file: ./coverage.xml
|
|
119
|
+
flags: gpu-tests
|
|
120
|
+
fail_ci_if_error: false
|
|
121
|
+
|
|
122
|
+
- name: Upload HTML coverage report
|
|
123
|
+
if: always()
|
|
124
|
+
uses: actions/upload-artifact@v4
|
|
125
|
+
with:
|
|
126
|
+
name: gpu-test-coverage-report
|
|
127
|
+
path: htmlcov/
|
|
128
|
+
|
|
129
|
+
# Code quality checks
|
|
130
|
+
code-quality:
|
|
131
|
+
runs-on: ubuntu-latest
|
|
132
|
+
|
|
133
|
+
steps:
|
|
134
|
+
- name: Checkout
|
|
135
|
+
uses: actions/checkout@v4
|
|
136
|
+
|
|
137
|
+
- name: Setup Python
|
|
138
|
+
uses: actions/setup-python@v5
|
|
139
|
+
with:
|
|
140
|
+
python-version: "3.12"
|
|
141
|
+
|
|
142
|
+
- name: Install dependencies
|
|
143
|
+
run: |
|
|
144
|
+
python -m pip install --upgrade pip
|
|
145
|
+
pip install ruff black mypy
|
|
146
|
+
|
|
147
|
+
- name: Run ruff (linting)
|
|
148
|
+
run: |
|
|
149
|
+
ruff check src/ --output-format=github
|
|
150
|
+
|
|
151
|
+
- name: Run black (formatting check)
|
|
152
|
+
run: |
|
|
153
|
+
black --check src/
|
|
154
|
+
|
|
155
|
+
- name: Run mypy (type checking)
|
|
156
|
+
continue-on-error: true # Scientific code often has complex types
|
|
157
|
+
run: |
|
|
158
|
+
mypy src/ --ignore-missing-imports
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
name: Tests, Coverage and GitHub Pages
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write # Needed for badge commit
|
|
10
|
+
pages: write
|
|
11
|
+
id-token: write
|
|
12
|
+
|
|
13
|
+
concurrency:
|
|
14
|
+
group: "pages"
|
|
15
|
+
cancel-in-progress: true
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
test-and-deploy:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
environment:
|
|
21
|
+
name: github-pages
|
|
22
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
23
|
+
steps:
|
|
24
|
+
- name: Checkout
|
|
25
|
+
uses: actions/checkout@v4
|
|
26
|
+
|
|
27
|
+
- name: Setup Python
|
|
28
|
+
uses: actions/setup-python@v5
|
|
29
|
+
with:
|
|
30
|
+
python-version: "3.12"
|
|
31
|
+
|
|
32
|
+
- name: Install dependencies
|
|
33
|
+
run: |
|
|
34
|
+
python -m pip install --upgrade pip
|
|
35
|
+
pip install -e ".[dev]"
|
|
36
|
+
pip install genbadge[coverage]
|
|
37
|
+
|
|
38
|
+
- name: Install PyTorch (CPU-only) for tests
|
|
39
|
+
run: |
|
|
40
|
+
pip install torch --index-url https://download.pytorch.org/whl/cpu
|
|
41
|
+
|
|
42
|
+
- name: Run tests with coverage
|
|
43
|
+
continue-on-error: true # Continue workflow even if tests fail
|
|
44
|
+
run: |
|
|
45
|
+
mkdir -p site
|
|
46
|
+
python -m pytest --cov=arraybridge \
|
|
47
|
+
--cov-report=xml \
|
|
48
|
+
--cov-report=html:site/coverage \
|
|
49
|
+
tests/
|
|
50
|
+
|
|
51
|
+
- name: Generate coverage badge
|
|
52
|
+
run: |
|
|
53
|
+
mkdir -p .github/badges
|
|
54
|
+
genbadge coverage -i coverage.xml -o .github/badges/coverage.svg -n "coverage"
|
|
55
|
+
|
|
56
|
+
- name: Create index.html and README
|
|
57
|
+
run: |
|
|
58
|
+
# Create index.html for redirection
|
|
59
|
+
cat > site/index.html << 'EOF'
|
|
60
|
+
<!DOCTYPE html>
|
|
61
|
+
<html>
|
|
62
|
+
<head>
|
|
63
|
+
<meta http-equiv="refresh" content="0; url=./coverage/">
|
|
64
|
+
<title>Redirecting to coverage report...</title>
|
|
65
|
+
</head>
|
|
66
|
+
<body>
|
|
67
|
+
<p>Redirecting to coverage report... <a href="./coverage/">Click here if not redirected</a></p>
|
|
68
|
+
</body>
|
|
69
|
+
</html>
|
|
70
|
+
EOF
|
|
71
|
+
|
|
72
|
+
# Create README.md in the site directory
|
|
73
|
+
echo "# arraybridge Code Coverage Reports" > site/README.md
|
|
74
|
+
echo "" >> site/README.md
|
|
75
|
+
echo "This site contains the code coverage reports for the [arraybridge](https://github.com/trissim/arraybridge) project." >> site/README.md
|
|
76
|
+
echo "" >> site/README.md
|
|
77
|
+
echo "## Navigation" >> site/README.md
|
|
78
|
+
echo "" >> site/README.md
|
|
79
|
+
echo "- [Coverage Report](./coverage/): View the HTML coverage report" >> site/README.md
|
|
80
|
+
echo "" >> site/README.md
|
|
81
|
+
echo "## About" >> site/README.md
|
|
82
|
+
echo "" >> site/README.md
|
|
83
|
+
echo "These reports are automatically generated by GitHub Actions whenever changes are pushed to the main branch." >> site/README.md
|
|
84
|
+
echo "They show the percentage of code that is covered by automated tests." >> site/README.md
|
|
85
|
+
echo "" >> site/README.md
|
|
86
|
+
echo "Last updated: $(date)" >> site/README.md
|
|
87
|
+
|
|
88
|
+
- name: Commit and push if coverage badge changed
|
|
89
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
90
|
+
run: |
|
|
91
|
+
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
|
92
|
+
git config --local user.name "github-actions[bot]"
|
|
93
|
+
git add .github/badges/coverage.svg -f
|
|
94
|
+
git commit -m "chore: update coverage badge" || exit 0
|
|
95
|
+
git push
|
|
96
|
+
|
|
97
|
+
- name: Check GitHub Pages status
|
|
98
|
+
run: |
|
|
99
|
+
echo "⚠️ IMPORTANT: Make sure GitHub Pages is enabled in your repository settings!"
|
|
100
|
+
echo "Go to https://github.com/trissim/arraybridge/settings/pages"
|
|
101
|
+
echo "Set 'Source' to 'GitHub Actions' to enable GitHub Pages deployment."
|
|
102
|
+
|
|
103
|
+
- name: Setup Pages
|
|
104
|
+
uses: actions/configure-pages@v4
|
|
105
|
+
|
|
106
|
+
- name: Upload Pages artifact
|
|
107
|
+
uses: actions/upload-pages-artifact@v3
|
|
108
|
+
with:
|
|
109
|
+
path: 'site'
|
|
110
|
+
retention-days: 1
|
|
111
|
+
|
|
112
|
+
- name: Deploy to GitHub Pages
|
|
113
|
+
id: deployment
|
|
114
|
+
uses: actions/deploy-pages@v4
|
|
115
|
+
timeout-minutes: 10
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
name: GPU Tests (Manual - Comprehensive GPU Testing)
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch: # Manual trigger only
|
|
5
|
+
|
|
6
|
+
jobs:
|
|
7
|
+
gpu-test:
|
|
8
|
+
runs-on: ubuntu-latest
|
|
9
|
+
|
|
10
|
+
steps:
|
|
11
|
+
- name: Checkout
|
|
12
|
+
uses: actions/checkout@v4
|
|
13
|
+
|
|
14
|
+
- name: Setup Python
|
|
15
|
+
uses: actions/setup-python@v5
|
|
16
|
+
with:
|
|
17
|
+
python-version: "3.12"
|
|
18
|
+
|
|
19
|
+
- name: Install base dependencies
|
|
20
|
+
run: |
|
|
21
|
+
python -m pip install --upgrade pip
|
|
22
|
+
pip install -e ".[dev]"
|
|
23
|
+
|
|
24
|
+
- name: Install CPU-available frameworks
|
|
25
|
+
run: |
|
|
26
|
+
# Install CPU versions of frameworks for testing
|
|
27
|
+
# (Real GPU tests would need actual CUDA infrastructure)
|
|
28
|
+
pip install torch --index-url https://download.pytorch.org/whl/cpu
|
|
29
|
+
pip install jax jaxlib
|
|
30
|
+
echo "Installed PyTorch (CPU) and JAX for testing"
|
|
31
|
+
|
|
32
|
+
- name: Check framework availability
|
|
33
|
+
run: |
|
|
34
|
+
python -c "
|
|
35
|
+
import sys
|
|
36
|
+
frameworks = ['numpy', 'torch', 'jax', 'cupy', 'tensorflow', 'pyclesperanto']
|
|
37
|
+
for fw in frameworks:
|
|
38
|
+
try:
|
|
39
|
+
__import__(fw)
|
|
40
|
+
print(f'✓ {fw} available')
|
|
41
|
+
except ImportError:
|
|
42
|
+
print(f'✗ {fw} not available (will be skipped)')
|
|
43
|
+
"
|
|
44
|
+
|
|
45
|
+
- name: Run comprehensive GPU cleanup tests
|
|
46
|
+
run: |
|
|
47
|
+
# Run all GPU cleanup tests
|
|
48
|
+
# Tests will use frameworks if available, skip gracefully if not
|
|
49
|
+
pytest -v tests/test_gpu_cleanup.py \
|
|
50
|
+
--cov=arraybridge \
|
|
51
|
+
--cov-report=term-missing \
|
|
52
|
+
--cov-report=html \
|
|
53
|
+
--tb=short \
|
|
54
|
+
-ra
|
|
55
|
+
|
|
56
|
+
- name: Run framework-specific GPU tests
|
|
57
|
+
run: |
|
|
58
|
+
# Run tests marked for specific frameworks
|
|
59
|
+
pytest -v tests/ -k "gpu or cupy or torch or tensorflow or jax or pyclesperanto" \
|
|
60
|
+
--cov=arraybridge \
|
|
61
|
+
--cov-report=term-missing \
|
|
62
|
+
-ra || true
|
|
63
|
+
|
|
64
|
+
- name: Test results summary
|
|
65
|
+
if: always()
|
|
66
|
+
run: |
|
|
67
|
+
echo "GPU Testing Complete!"
|
|
68
|
+
echo "Note: Full GPU testing requires NVIDIA CUDA infrastructure."
|
|
69
|
+
echo "For complete GPU testing, use a system with NVIDIA GPUs installed."
|
|
70
|
+
|
|
71
|
+
- name: Upload coverage report
|
|
72
|
+
if: always()
|
|
73
|
+
uses: actions/upload-artifact@v4
|
|
74
|
+
with:
|
|
75
|
+
name: gpu-test-coverage-report
|
|
76
|
+
path: |
|
|
77
|
+
htmlcov/
|
|
78
|
+
.coverage
|
|
79
|
+
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# Adding New Frameworks to arraybridge
|
|
2
|
+
|
|
3
|
+
With the metaclass-registry integration, adding a new framework is now simpler and requires no manual wiring.
|
|
4
|
+
|
|
5
|
+
## Quick Overview
|
|
6
|
+
|
|
7
|
+
To add a new framework (e.g., MXNet), you need to:
|
|
8
|
+
|
|
9
|
+
1. Add the new memory type to the `MemoryType` enum in `types.py`
|
|
10
|
+
2. Add framework configuration to `_FRAMEWORK_CONFIG` in `framework_config.py`
|
|
11
|
+
3. The converter automatically registers itself - no manual registration needed!
|
|
12
|
+
|
|
13
|
+
## Step-by-Step Guide
|
|
14
|
+
|
|
15
|
+
### Step 1: Add to MemoryType Enum
|
|
16
|
+
|
|
17
|
+
Edit `src/arraybridge/types.py`:
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
class MemoryType(Enum):
|
|
21
|
+
"""Enum representing different array/tensor framework types."""
|
|
22
|
+
|
|
23
|
+
NUMPY = "numpy"
|
|
24
|
+
CUPY = "cupy"
|
|
25
|
+
TORCH = "torch"
|
|
26
|
+
TENSORFLOW = "tensorflow"
|
|
27
|
+
JAX = "jax"
|
|
28
|
+
PYCLESPERANTO = "pyclesperanto"
|
|
29
|
+
MXNET = "mxnet" # <-- Add your new framework
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Step 2: Add Framework Configuration
|
|
33
|
+
|
|
34
|
+
Edit `src/arraybridge/framework_config.py` and add a new entry to `_FRAMEWORK_CONFIG`:
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
_FRAMEWORK_CONFIG = {
|
|
38
|
+
# ... existing configurations ...
|
|
39
|
+
|
|
40
|
+
MemoryType.MXNET: {
|
|
41
|
+
# Metadata
|
|
42
|
+
'import_name': 'mxnet',
|
|
43
|
+
'display_name': 'MXNet',
|
|
44
|
+
'is_gpu': True,
|
|
45
|
+
|
|
46
|
+
# Conversion operations - these define the converter methods
|
|
47
|
+
'conversion_ops': {
|
|
48
|
+
'to_numpy': 'data.asnumpy()', # How to convert to numpy
|
|
49
|
+
'from_numpy': '{mod}.nd.array(data, ctx={mod}.gpu(gpu_id))', # How to create from numpy
|
|
50
|
+
'from_dlpack': '{mod}.nd.from_dlpack(data)', # DLPack support (if available)
|
|
51
|
+
'move_to_device': 'data.as_in_context({mod}.gpu(device_id))', # Move between devices
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
# Device operations (optional)
|
|
55
|
+
'get_device_id': 'data.context.device_id',
|
|
56
|
+
'set_device': None,
|
|
57
|
+
|
|
58
|
+
# Other configuration...
|
|
59
|
+
'supports_dlpack': True,
|
|
60
|
+
'validate_dlpack': None,
|
|
61
|
+
|
|
62
|
+
# ... add other required fields based on existing frameworks
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Step 3: That's It!
|
|
68
|
+
|
|
69
|
+
The converter class is automatically created and registered when the module loads. You can verify it works:
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
from arraybridge.converters_registry import ConverterBase, get_converter
|
|
73
|
+
from arraybridge.types import MemoryType
|
|
74
|
+
|
|
75
|
+
# Check that it's registered
|
|
76
|
+
print(sorted(ConverterBase.__registry__.keys()))
|
|
77
|
+
# Output: ['cupy', 'jax', 'mxnet', 'numpy', 'pyclesperanto', 'tensorflow', 'torch']
|
|
78
|
+
|
|
79
|
+
# Get the converter
|
|
80
|
+
mxnet_converter = get_converter("mxnet")
|
|
81
|
+
print(mxnet_converter.memory_type) # Output: 'mxnet'
|
|
82
|
+
|
|
83
|
+
# Use via MemoryType enum
|
|
84
|
+
converter = MemoryType.MXNET.converter
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## What Happens Behind the Scenes
|
|
88
|
+
|
|
89
|
+
1. **Auto-generation**: A `MxnetConverter` class is created dynamically with methods from `conversion_ops`
|
|
90
|
+
2. **Auto-registration**: The metaclass `AutoRegisterMeta` automatically registers it in `ConverterBase.__registry__`
|
|
91
|
+
3. **Auto-validation**: Module load validates that all `MemoryType` values have registered converters
|
|
92
|
+
4. **Auto-methods**: The converter automatically gets `to_X()` methods for all other frameworks
|
|
93
|
+
|
|
94
|
+
## Benefits of This Approach
|
|
95
|
+
|
|
96
|
+
### Before (Manual Wiring - Old System)
|
|
97
|
+
```python
|
|
98
|
+
# Had to manually create converter class with all methods
|
|
99
|
+
class MxnetConverter(MemoryTypeConverter):
|
|
100
|
+
def to_numpy(self, data, gpu_id):
|
|
101
|
+
return data.asnumpy()
|
|
102
|
+
def from_numpy(self, data, gpu_id):
|
|
103
|
+
return mxnet.nd.array(data)
|
|
104
|
+
def to_torch(self, data, gpu_id):
|
|
105
|
+
# ... manual implementation
|
|
106
|
+
def to_cupy(self, data, gpu_id):
|
|
107
|
+
# ... manual implementation
|
|
108
|
+
# ... 6+ more methods
|
|
109
|
+
|
|
110
|
+
# Had to manually register
|
|
111
|
+
_CONVERTERS[MemoryType.MXNET] = MxnetConverter()
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### After (Auto-registration - New System)
|
|
115
|
+
```python
|
|
116
|
+
# Just add to enum and config - everything else is automatic!
|
|
117
|
+
MemoryType.MXNET = "mxnet"
|
|
118
|
+
|
|
119
|
+
_FRAMEWORK_CONFIG[MemoryType.MXNET] = {
|
|
120
|
+
'conversion_ops': {
|
|
121
|
+
'to_numpy': 'data.asnumpy()',
|
|
122
|
+
# ...
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Framework Configuration Reference
|
|
128
|
+
|
|
129
|
+
Required fields in `conversion_ops`:
|
|
130
|
+
- `to_numpy`: Expression to convert framework data to numpy
|
|
131
|
+
- `from_numpy`: Expression to create framework data from numpy
|
|
132
|
+
- `from_dlpack`: Expression for DLPack conversion (or `None`)
|
|
133
|
+
- `move_to_device`: Expression to move data between devices
|
|
134
|
+
|
|
135
|
+
Available template variables in expressions:
|
|
136
|
+
- `{mod}`: The imported module (e.g., `mxnet`)
|
|
137
|
+
- `data`: The input data
|
|
138
|
+
- `gpu_id` / `device_id`: Target device ID
|
|
139
|
+
|
|
140
|
+
## Testing Your New Framework
|
|
141
|
+
|
|
142
|
+
Add tests in `tests/test_converters.py`:
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
@pytest.mark.mxnet
|
|
146
|
+
def test_convert_numpy_to_mxnet(self, mxnet_available):
|
|
147
|
+
"""Test converting NumPy to MXNet."""
|
|
148
|
+
if not mxnet_available:
|
|
149
|
+
pytest.skip("MXNet not available")
|
|
150
|
+
|
|
151
|
+
import mxnet as mx
|
|
152
|
+
arr = np.array([1.0, 2.0, 3.0], dtype=np.float32)
|
|
153
|
+
result = convert_memory(arr, source_type="numpy", target_type="mxnet", gpu_id=0)
|
|
154
|
+
|
|
155
|
+
assert isinstance(result, mx.nd.NDArray)
|
|
156
|
+
np.testing.assert_array_almost_equal(result.asnumpy(), arr)
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Common Patterns
|
|
160
|
+
|
|
161
|
+
### GPU Framework with DLPack
|
|
162
|
+
```python
|
|
163
|
+
'conversion_ops': {
|
|
164
|
+
'to_numpy': 'data.cpu().numpy()',
|
|
165
|
+
'from_numpy': '{mod}.from_numpy(data).to(device=gpu_id)',
|
|
166
|
+
'from_dlpack': '{mod}.from_dlpack(data)',
|
|
167
|
+
'move_to_device': 'data.to(device=device_id)',
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### CPU-only Framework
|
|
172
|
+
```python
|
|
173
|
+
'conversion_ops': {
|
|
174
|
+
'to_numpy': 'np.array(data)',
|
|
175
|
+
'from_numpy': '{mod}.array(data)',
|
|
176
|
+
'from_dlpack': None, # Not supported
|
|
177
|
+
'move_to_device': 'data', # No-op for CPU
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Complex Operations with Helpers
|
|
182
|
+
If you need complex logic, define a helper function in `framework_config.py`:
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
def _mxnet_special_conversion(data, gpu_id, mod):
|
|
186
|
+
# Complex logic here
|
|
187
|
+
return result
|
|
188
|
+
|
|
189
|
+
_FRAMEWORK_CONFIG[MemoryType.MXNET] = {
|
|
190
|
+
'conversion_ops': {
|
|
191
|
+
'from_numpy': _mxnet_special_conversion, # Use callable instead of string
|
|
192
|
+
# ...
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Troubleshooting
|
|
198
|
+
|
|
199
|
+
### Converter not registered
|
|
200
|
+
Make sure:
|
|
201
|
+
1. You added the framework to `MemoryType` enum
|
|
202
|
+
2. You added configuration to `_FRAMEWORK_CONFIG`
|
|
203
|
+
3. The key in `_FRAMEWORK_CONFIG` matches the `MemoryType` enum value
|
|
204
|
+
|
|
205
|
+
### Import errors
|
|
206
|
+
If you get import errors, check:
|
|
207
|
+
1. The `import_name` matches the actual package name
|
|
208
|
+
2. The `conversion_ops` expressions use correct module syntax
|
|
209
|
+
|
|
210
|
+
### Validation errors
|
|
211
|
+
Run this to check registration:
|
|
212
|
+
```python
|
|
213
|
+
from arraybridge.converters_registry import _validate_registry
|
|
214
|
+
_validate_registry() # Raises RuntimeError if validation fails
|
|
215
|
+
```
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# GitHub Actions upload-artifact v3 → v4 Migration
|
|
2
|
+
|
|
3
|
+
## Issue
|
|
4
|
+
GitHub deprecated `actions/upload-artifact@v3` effective April 16, 2024. The CI workflows were failing with:
|
|
5
|
+
```
|
|
6
|
+
Error: This request has been automatically failed because it uses a deprecated version of `actions/upload-artifact: v3`.
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Solution
|
|
10
|
+
Updated all `upload-artifact` action references from `v3` to `v4` in CI workflows.
|
|
11
|
+
|
|
12
|
+
## Files Updated
|
|
13
|
+
|
|
14
|
+
### 1. `.github/workflows/ci.yml`
|
|
15
|
+
**Line 91**: Updated GPU test artifact upload
|
|
16
|
+
```yaml
|
|
17
|
+
# Before
|
|
18
|
+
uses: actions/upload-artifact@v3
|
|
19
|
+
|
|
20
|
+
# After
|
|
21
|
+
uses: actions/upload-artifact@v4
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### 2. `.github/workflows/gpu-tests.yml`
|
|
25
|
+
**Line 41**: Updated standalone GPU test artifact upload
|
|
26
|
+
```yaml
|
|
27
|
+
# Before
|
|
28
|
+
uses: actions/upload-artifact@v3
|
|
29
|
+
|
|
30
|
+
# After
|
|
31
|
+
uses: actions/upload-artifact@v4
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Changes Made
|
|
35
|
+
- ✅ Both workflow files now use `actions/upload-artifact@v4`
|
|
36
|
+
- ✅ Artifact upload configuration remains the same
|
|
37
|
+
- ✅ CI workflows will no longer fail due to deprecated action
|
|
38
|
+
|
|
39
|
+
## Reference
|
|
40
|
+
- [GitHub Blog: Deprecation Notice for Artifact Actions](https://github.blog/changelog/2024-04-16-deprecation-notice-v3-of-the-artifact-actions/)
|
|
41
|
+
- [Upload Artifact v4 Documentation](https://github.com/actions/upload-artifact/releases/tag/v4)
|
|
42
|
+
|
|
43
|
+
## Verification
|
|
44
|
+
The GPU test CI job should now:
|
|
45
|
+
1. ✅ Run without the deprecation error
|
|
46
|
+
2. ✅ Successfully upload test results and coverage reports
|
|
47
|
+
3. ✅ Display artifacts in the GitHub Actions UI
|