patchmatch-cython 0.1.0__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.
- patchmatch_cython-0.1.0/LICENSE +28 -0
- patchmatch_cython-0.1.0/MANIFEST.in +13 -0
- patchmatch_cython-0.1.0/PKG-INFO +207 -0
- patchmatch_cython-0.1.0/README.md +138 -0
- patchmatch_cython-0.1.0/local_build.py +249 -0
- patchmatch_cython-0.1.0/pyproject.toml +74 -0
- patchmatch_cython-0.1.0/setup.cfg +4 -0
- patchmatch_cython-0.1.0/setup.py +103 -0
- patchmatch_cython-0.1.0/src/patchmatch_cython/__init__.py +19 -0
- patchmatch_cython-0.1.0/src/patchmatch_cython/cython_solver.py +92 -0
- patchmatch_cython-0.1.0/src/patchmatch_cython/cython_solver_impl.cpp +35976 -0
- patchmatch_cython-0.1.0/src/patchmatch_cython/cython_solver_impl.pyx +297 -0
- patchmatch_cython-0.1.0/src/patchmatch_cython/imageops.py +200 -0
- patchmatch_cython-0.1.0/src/patchmatch_cython/inpaint.py +139 -0
- patchmatch_cython-0.1.0/src/patchmatch_cython/python_solver.py +318 -0
- patchmatch_cython-0.1.0/src/patchmatch_cython/solver.py +91 -0
- patchmatch_cython-0.1.0/src/patchmatch_cython.egg-info/PKG-INFO +207 -0
- patchmatch_cython-0.1.0/src/patchmatch_cython.egg-info/SOURCES.txt +21 -0
- patchmatch_cython-0.1.0/src/patchmatch_cython.egg-info/dependency_links.txt +1 -0
- patchmatch_cython-0.1.0/src/patchmatch_cython.egg-info/not-zip-safe +1 -0
- patchmatch_cython-0.1.0/src/patchmatch_cython.egg-info/requires.txt +12 -0
- patchmatch_cython-0.1.0/src/patchmatch_cython.egg-info/top_level.txt +1 -0
- patchmatch_cython-0.1.0/tests/test_basic_functionality.py +71 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025, Teriks
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
include *.py
|
|
2
|
+
include README.md
|
|
3
|
+
include LICENSE
|
|
4
|
+
recursive-include src *.py *.pyx
|
|
5
|
+
global-exclude *.pyc
|
|
6
|
+
global-exclude *.pyo
|
|
7
|
+
global-exclude *~
|
|
8
|
+
global-exclude .DS_Store
|
|
9
|
+
global-exclude __pycache__
|
|
10
|
+
prune build
|
|
11
|
+
prune dist
|
|
12
|
+
prune .git
|
|
13
|
+
prune .pytest_cache
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: patchmatch-cython
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: High-performance PatchMatch implementation for image inpainting using Cython
|
|
5
|
+
Home-page: https://github.com/Teriks/patchmatch-cython
|
|
6
|
+
Author: Teriks
|
|
7
|
+
Author-email: Teriks <Teriks999@gmail.com>
|
|
8
|
+
License: BSD 3-Clause License
|
|
9
|
+
|
|
10
|
+
Copyright (c) 2025, Teriks
|
|
11
|
+
|
|
12
|
+
Redistribution and use in source and binary forms, with or without
|
|
13
|
+
modification, are permitted provided that the following conditions are met:
|
|
14
|
+
|
|
15
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
16
|
+
list of conditions and the following disclaimer.
|
|
17
|
+
|
|
18
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
19
|
+
this list of conditions and the following disclaimer in the documentation
|
|
20
|
+
and/or other materials provided with the distribution.
|
|
21
|
+
|
|
22
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
23
|
+
contributors may be used to endorse or promote products derived from
|
|
24
|
+
this software without specific prior written permission.
|
|
25
|
+
|
|
26
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
27
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
28
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
29
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
30
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
31
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
32
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
33
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
34
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
35
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
36
|
+
Project-URL: Homepage, https://github.com/Teriks/patchmatch-cython
|
|
37
|
+
Project-URL: Repository, https://github.com/Teriks/patchmatch-cython
|
|
38
|
+
Project-URL: Issues, https://github.com/Teriks/patchmatch-cython/issues
|
|
39
|
+
Keywords: image-processing,inpainting,patchmatch,cython,computer-vision
|
|
40
|
+
Classifier: Development Status :: 4 - Beta
|
|
41
|
+
Classifier: Intended Audience :: Developers
|
|
42
|
+
Classifier: Intended Audience :: Science/Research
|
|
43
|
+
Classifier: Programming Language :: Python :: 3
|
|
44
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
45
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
46
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
47
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
48
|
+
Classifier: Programming Language :: Cython
|
|
49
|
+
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
|
50
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
51
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
52
|
+
Requires-Python: >=3.10
|
|
53
|
+
Description-Content-Type: text/markdown
|
|
54
|
+
License-File: LICENSE
|
|
55
|
+
Requires-Dist: numpy>=1.21.0
|
|
56
|
+
Provides-Extra: visualization
|
|
57
|
+
Requires-Dist: matplotlib>=3.5.0; extra == "visualization"
|
|
58
|
+
Requires-Dist: Pillow>=8.0.0; extra == "visualization"
|
|
59
|
+
Provides-Extra: dev
|
|
60
|
+
Requires-Dist: pytest>=6.0.0; extra == "dev"
|
|
61
|
+
Requires-Dist: build>=0.8.0; extra == "dev"
|
|
62
|
+
Requires-Dist: setuptools>=61.0; extra == "dev"
|
|
63
|
+
Requires-Dist: wheel; extra == "dev"
|
|
64
|
+
Requires-Dist: Cython>=0.29.0; extra == "dev"
|
|
65
|
+
Dynamic: author
|
|
66
|
+
Dynamic: home-page
|
|
67
|
+
Dynamic: license-file
|
|
68
|
+
Dynamic: requires-python
|
|
69
|
+
|
|
70
|
+
# PatchMatch Cython
|
|
71
|
+
|
|
72
|
+
High-performance PatchMatch implementation for pyramidical image inpainting in cython.
|
|
73
|
+
|
|
74
|
+
This package can function without `python-opencv` / `python-opencv-headless` with only slightly reduced quality.
|
|
75
|
+
|
|
76
|
+
Installing OpenCV results in this packaging using it for upsampling (`INTER_LINEAR`).
|
|
77
|
+
|
|
78
|
+
When OpenCV is not installed, an implementation of bilinear upscaling using numpy will be used.
|
|
79
|
+
|
|
80
|
+
This package provides a pure python solver and a cython solver, the cython solver is
|
|
81
|
+
around 30 to 50 times faster than the python solver depending on patch size.
|
|
82
|
+
|
|
83
|
+
## Installation
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
pip install patchmatch-cython
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**From source:**
|
|
90
|
+
```bash
|
|
91
|
+
git clone <repository-url>
|
|
92
|
+
cd patchmatch-cython
|
|
93
|
+
pip install .
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Usage
|
|
97
|
+
|
|
98
|
+
### With OpenCV
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
import cv2
|
|
102
|
+
from patchmatch_cython import inpaint_pyramid
|
|
103
|
+
|
|
104
|
+
# Load image and mask
|
|
105
|
+
image = cv2.imread('image.jpg')
|
|
106
|
+
mask = cv2.imread('mask.png', 0) > 128
|
|
107
|
+
|
|
108
|
+
# Inpaint
|
|
109
|
+
result = inpaint_pyramid(image, mask)
|
|
110
|
+
|
|
111
|
+
cv2.imwrite('result.jpg', result)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### With Pillow
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
import numpy as np
|
|
118
|
+
from PIL import Image
|
|
119
|
+
from patchmatch_cython import inpaint_pyramid
|
|
120
|
+
|
|
121
|
+
# Load image and mask
|
|
122
|
+
image = np.array(Image.open('image.jpg'))
|
|
123
|
+
mask = np.array(Image.open('mask.png').convert('L')) > 128
|
|
124
|
+
|
|
125
|
+
# Inpaint (auto-selects fastest available solver)
|
|
126
|
+
result = inpaint_pyramid(image, mask)
|
|
127
|
+
|
|
128
|
+
Image.fromarray(result).save('result.jpg')
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Explicit Solver Selection
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
from patchmatch_cython import PythonSolver, CythonSolver, inpaint_pyramid
|
|
135
|
+
|
|
136
|
+
# Force specific solver
|
|
137
|
+
result = inpaint_pyramid(image, mask, solver_class=CythonSolver) # Fast
|
|
138
|
+
result = inpaint_pyramid(image, mask, solver_class=PythonSolver) # Fallback
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Package Extras
|
|
142
|
+
|
|
143
|
+
**For examples:**
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
pip install patchmatch-cython[visualization]
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Adds: `matplotlib`, `Pillow`
|
|
150
|
+
|
|
151
|
+
**For development:**
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
pip install patchmatch-cython[dev]
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Adds: `pytest`, `build`, `cibuildwheel`, `setuptools`, `wheel`
|
|
158
|
+
|
|
159
|
+
## Building
|
|
160
|
+
|
|
161
|
+
To build wheels locally for distribution or development:
|
|
162
|
+
|
|
163
|
+
### Prerequisites
|
|
164
|
+
|
|
165
|
+
Install build dependencies:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
pip install cibuildwheel
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Build Wheels
|
|
172
|
+
|
|
173
|
+
Use the included `local_build.py` script to build locally:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
# Build for all supported Python versions (3.10-3.13)
|
|
177
|
+
python local_build.py
|
|
178
|
+
|
|
179
|
+
# Build for specific Python versions
|
|
180
|
+
python local_build.py --python 3.11 3.12
|
|
181
|
+
|
|
182
|
+
# Build for specific platform
|
|
183
|
+
python local_build.py --platform linux
|
|
184
|
+
|
|
185
|
+
# Build and test wheel installation
|
|
186
|
+
python local_build.py --test
|
|
187
|
+
|
|
188
|
+
# Build without cleaning previous artifacts
|
|
189
|
+
python local_build.py --no-clean
|
|
190
|
+
|
|
191
|
+
# View all options
|
|
192
|
+
python local_build.py --help
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Build Options
|
|
196
|
+
|
|
197
|
+
- `--python`: Specify Python versions to build for (e.g., `3.11 3.12`)
|
|
198
|
+
- `--platform`: Target platform (`auto`, `linux`, `macos`, `windows`)
|
|
199
|
+
- `--test`: Test wheel installation after building
|
|
200
|
+
- `--no-clean`: Skip cleaning build artifacts before building
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
## Requirements
|
|
204
|
+
|
|
205
|
+
- Python 3.10+
|
|
206
|
+
- NumPy (auto-installed)
|
|
207
|
+
- C++ compiler (for building from source)
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# PatchMatch Cython
|
|
2
|
+
|
|
3
|
+
High-performance PatchMatch implementation for pyramidical image inpainting in cython.
|
|
4
|
+
|
|
5
|
+
This package can function without `python-opencv` / `python-opencv-headless` with only slightly reduced quality.
|
|
6
|
+
|
|
7
|
+
Installing OpenCV results in this packaging using it for upsampling (`INTER_LINEAR`).
|
|
8
|
+
|
|
9
|
+
When OpenCV is not installed, an implementation of bilinear upscaling using numpy will be used.
|
|
10
|
+
|
|
11
|
+
This package provides a pure python solver and a cython solver, the cython solver is
|
|
12
|
+
around 30 to 50 times faster than the python solver depending on patch size.
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install patchmatch-cython
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**From source:**
|
|
21
|
+
```bash
|
|
22
|
+
git clone <repository-url>
|
|
23
|
+
cd patchmatch-cython
|
|
24
|
+
pip install .
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
### With OpenCV
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
import cv2
|
|
33
|
+
from patchmatch_cython import inpaint_pyramid
|
|
34
|
+
|
|
35
|
+
# Load image and mask
|
|
36
|
+
image = cv2.imread('image.jpg')
|
|
37
|
+
mask = cv2.imread('mask.png', 0) > 128
|
|
38
|
+
|
|
39
|
+
# Inpaint
|
|
40
|
+
result = inpaint_pyramid(image, mask)
|
|
41
|
+
|
|
42
|
+
cv2.imwrite('result.jpg', result)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### With Pillow
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
import numpy as np
|
|
49
|
+
from PIL import Image
|
|
50
|
+
from patchmatch_cython import inpaint_pyramid
|
|
51
|
+
|
|
52
|
+
# Load image and mask
|
|
53
|
+
image = np.array(Image.open('image.jpg'))
|
|
54
|
+
mask = np.array(Image.open('mask.png').convert('L')) > 128
|
|
55
|
+
|
|
56
|
+
# Inpaint (auto-selects fastest available solver)
|
|
57
|
+
result = inpaint_pyramid(image, mask)
|
|
58
|
+
|
|
59
|
+
Image.fromarray(result).save('result.jpg')
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Explicit Solver Selection
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from patchmatch_cython import PythonSolver, CythonSolver, inpaint_pyramid
|
|
66
|
+
|
|
67
|
+
# Force specific solver
|
|
68
|
+
result = inpaint_pyramid(image, mask, solver_class=CythonSolver) # Fast
|
|
69
|
+
result = inpaint_pyramid(image, mask, solver_class=PythonSolver) # Fallback
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Package Extras
|
|
73
|
+
|
|
74
|
+
**For examples:**
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
pip install patchmatch-cython[visualization]
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Adds: `matplotlib`, `Pillow`
|
|
81
|
+
|
|
82
|
+
**For development:**
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
pip install patchmatch-cython[dev]
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Adds: `pytest`, `build`, `cibuildwheel`, `setuptools`, `wheel`
|
|
89
|
+
|
|
90
|
+
## Building
|
|
91
|
+
|
|
92
|
+
To build wheels locally for distribution or development:
|
|
93
|
+
|
|
94
|
+
### Prerequisites
|
|
95
|
+
|
|
96
|
+
Install build dependencies:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
pip install cibuildwheel
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Build Wheels
|
|
103
|
+
|
|
104
|
+
Use the included `local_build.py` script to build locally:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# Build for all supported Python versions (3.10-3.13)
|
|
108
|
+
python local_build.py
|
|
109
|
+
|
|
110
|
+
# Build for specific Python versions
|
|
111
|
+
python local_build.py --python 3.11 3.12
|
|
112
|
+
|
|
113
|
+
# Build for specific platform
|
|
114
|
+
python local_build.py --platform linux
|
|
115
|
+
|
|
116
|
+
# Build and test wheel installation
|
|
117
|
+
python local_build.py --test
|
|
118
|
+
|
|
119
|
+
# Build without cleaning previous artifacts
|
|
120
|
+
python local_build.py --no-clean
|
|
121
|
+
|
|
122
|
+
# View all options
|
|
123
|
+
python local_build.py --help
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Build Options
|
|
127
|
+
|
|
128
|
+
- `--python`: Specify Python versions to build for (e.g., `3.11 3.12`)
|
|
129
|
+
- `--platform`: Target platform (`auto`, `linux`, `macos`, `windows`)
|
|
130
|
+
- `--test`: Test wheel installation after building
|
|
131
|
+
- `--no-clean`: Skip cleaning build artifacts before building
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
## Requirements
|
|
135
|
+
|
|
136
|
+
- Python 3.10+
|
|
137
|
+
- NumPy (auto-installed)
|
|
138
|
+
- C++ compiler (for building from source)
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Local wheel building script
|
|
4
|
+
|
|
5
|
+
This script builds wheels for all supported Python versions using cibuildwheel.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import sys
|
|
10
|
+
import subprocess
|
|
11
|
+
import shutil
|
|
12
|
+
import argparse
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
def run_command(cmd, env=None):
|
|
16
|
+
"""Run command and return success status"""
|
|
17
|
+
print(f"RUNNING: {cmd}")
|
|
18
|
+
try:
|
|
19
|
+
result = subprocess.run(cmd, shell=True, check=True, env=env)
|
|
20
|
+
return True
|
|
21
|
+
except subprocess.CalledProcessError as e:
|
|
22
|
+
print(f"ERROR: Command failed with exit code {e.returncode}")
|
|
23
|
+
return False
|
|
24
|
+
|
|
25
|
+
def clean_build_artifacts():
|
|
26
|
+
"""Clean previous build artifacts"""
|
|
27
|
+
artifacts_to_clean = [
|
|
28
|
+
"wheelhouse",
|
|
29
|
+
"build",
|
|
30
|
+
"dist",
|
|
31
|
+
"*.egg-info",
|
|
32
|
+
"src/**/*.cpp",
|
|
33
|
+
"src/**/*.c",
|
|
34
|
+
"src/**/*.so",
|
|
35
|
+
"src/**/*.pyd",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
print("Cleaning previous build artifacts...")
|
|
39
|
+
for pattern in artifacts_to_clean:
|
|
40
|
+
if '*' in pattern:
|
|
41
|
+
# Handle glob patterns
|
|
42
|
+
import glob
|
|
43
|
+
for path in glob.glob(pattern, recursive=True):
|
|
44
|
+
if os.path.isdir(path):
|
|
45
|
+
print(f" Removing directory: {path}")
|
|
46
|
+
shutil.rmtree(path, ignore_errors=True)
|
|
47
|
+
else:
|
|
48
|
+
print(f" Removing file: {path}")
|
|
49
|
+
os.remove(path)
|
|
50
|
+
else:
|
|
51
|
+
path = Path(pattern)
|
|
52
|
+
if path.exists():
|
|
53
|
+
if path.is_dir():
|
|
54
|
+
print(f" Removing directory: {path}")
|
|
55
|
+
shutil.rmtree(path)
|
|
56
|
+
else:
|
|
57
|
+
print(f" Removing file: {path}")
|
|
58
|
+
path.unlink()
|
|
59
|
+
|
|
60
|
+
def build_wheels(python_versions=None, platforms=None, clean=True):
|
|
61
|
+
"""Build wheels using cibuildwheel"""
|
|
62
|
+
|
|
63
|
+
if clean:
|
|
64
|
+
clean_build_artifacts()
|
|
65
|
+
|
|
66
|
+
# Create custom environment
|
|
67
|
+
env = os.environ.copy()
|
|
68
|
+
|
|
69
|
+
# Set build configuration
|
|
70
|
+
if python_versions:
|
|
71
|
+
# Build specific Python versions
|
|
72
|
+
if isinstance(python_versions, list):
|
|
73
|
+
build_pattern = " ".join([f"cp{v.replace('.', '')}-*" for v in python_versions])
|
|
74
|
+
else:
|
|
75
|
+
build_pattern = f"cp{python_versions.replace('.', '')}-*"
|
|
76
|
+
env["CIBW_BUILD"] = build_pattern
|
|
77
|
+
print(f"Building for Python versions: {python_versions}")
|
|
78
|
+
else:
|
|
79
|
+
# Build all supported versions (default)
|
|
80
|
+
env["CIBW_BUILD"] = "cp310-* cp311-* cp312-* cp313-*"
|
|
81
|
+
print("Building for all supported Python versions (3.10-3.13)")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# Set platform-specific configurations
|
|
86
|
+
if platforms:
|
|
87
|
+
if isinstance(platforms, list):
|
|
88
|
+
platform_arg = f"--platform {' '.join(platforms)}"
|
|
89
|
+
else:
|
|
90
|
+
platform_arg = f"--platform {platforms}"
|
|
91
|
+
else:
|
|
92
|
+
platform_arg = "" # Build for current platform
|
|
93
|
+
|
|
94
|
+
# Configure test command
|
|
95
|
+
env["CIBW_TEST_COMMAND"] = "python -c \"import patchmatch_cython; print('Import successful')\""
|
|
96
|
+
|
|
97
|
+
print(f"\n{'='*70}")
|
|
98
|
+
print("BUILDING WHEELS")
|
|
99
|
+
print(f"{'='*70}")
|
|
100
|
+
|
|
101
|
+
# Run cibuildwheel
|
|
102
|
+
cmd = f"cibuildwheel --output-dir wheelhouse {platform_arg}".strip()
|
|
103
|
+
success = run_command(cmd, env)
|
|
104
|
+
|
|
105
|
+
if not success:
|
|
106
|
+
# Try as Python module
|
|
107
|
+
cmd = f"{sys.executable} -m cibuildwheel --output-dir wheelhouse {platform_arg}".strip()
|
|
108
|
+
success = run_command(cmd, env)
|
|
109
|
+
|
|
110
|
+
if success:
|
|
111
|
+
print(f"\n{'='*70}")
|
|
112
|
+
print("BUILD SUCCESSFUL")
|
|
113
|
+
print(f"{'='*70}")
|
|
114
|
+
|
|
115
|
+
# List built wheels
|
|
116
|
+
wheelhouse = Path("wheelhouse")
|
|
117
|
+
if wheelhouse.exists():
|
|
118
|
+
wheels = list(wheelhouse.glob("*.whl"))
|
|
119
|
+
print(f"\nBuilt {len(wheels)} wheels:")
|
|
120
|
+
for wheel in wheels:
|
|
121
|
+
size_mb = wheel.stat().st_size / (1024 * 1024)
|
|
122
|
+
print(f" WHEEL: {wheel.name} ({size_mb:.1f} MB)")
|
|
123
|
+
|
|
124
|
+
return True
|
|
125
|
+
else:
|
|
126
|
+
print(f"\n{'='*70}")
|
|
127
|
+
print("BUILD FAILED")
|
|
128
|
+
print(f"{'='*70}")
|
|
129
|
+
return False
|
|
130
|
+
|
|
131
|
+
def validate_wheel_installation():
|
|
132
|
+
"""Test installing and importing built wheels"""
|
|
133
|
+
wheelhouse = Path("wheelhouse")
|
|
134
|
+
wheels = list(wheelhouse.glob("*.whl"))
|
|
135
|
+
|
|
136
|
+
if not wheels:
|
|
137
|
+
print("ERROR: No wheels found to test")
|
|
138
|
+
return False
|
|
139
|
+
|
|
140
|
+
print(f"\n{'='*70}")
|
|
141
|
+
print("TESTING WHEEL INSTALLATION")
|
|
142
|
+
print(f"{'='*70}")
|
|
143
|
+
|
|
144
|
+
# Test the first wheel
|
|
145
|
+
wheel = wheels[0]
|
|
146
|
+
print(f"Testing wheel: {wheel.name}")
|
|
147
|
+
|
|
148
|
+
# Install wheel in a temporary way
|
|
149
|
+
test_cmd = f"pip install {wheel} --force-reinstall --no-deps"
|
|
150
|
+
if run_command(test_cmd):
|
|
151
|
+
# Test import
|
|
152
|
+
test_import = "python -c \"import patchmatch_cython; print('SUCCESS: Import successful')\""
|
|
153
|
+
if run_command(test_import):
|
|
154
|
+
print("SUCCESS: Wheel installation and import test passed")
|
|
155
|
+
return True
|
|
156
|
+
|
|
157
|
+
print("ERROR: Wheel installation test failed")
|
|
158
|
+
return False
|
|
159
|
+
|
|
160
|
+
def main():
|
|
161
|
+
"""Main function"""
|
|
162
|
+
parser = argparse.ArgumentParser(description="Build wheels for patchmatch-cython")
|
|
163
|
+
parser.add_argument("--python", nargs="*",
|
|
164
|
+
help="Python versions to build for (e.g., 3.11 3.12). Default: all supported versions")
|
|
165
|
+
parser.add_argument("--platform", choices=["auto", "linux", "macos", "windows"],
|
|
166
|
+
help="Platform to build for. Default: current platform")
|
|
167
|
+
parser.add_argument("--no-clean", action="store_true",
|
|
168
|
+
help="Don't clean build artifacts before building")
|
|
169
|
+
parser.add_argument("--test", action="store_true",
|
|
170
|
+
help="Test wheel installation after building")
|
|
171
|
+
|
|
172
|
+
args = parser.parse_args()
|
|
173
|
+
|
|
174
|
+
print("PatchMatch Cython - Local Wheel Builder")
|
|
175
|
+
print("=" * 70)
|
|
176
|
+
|
|
177
|
+
# Check if cibuildwheel is installed
|
|
178
|
+
print("Checking for cibuildwheel...")
|
|
179
|
+
cibw_found = False
|
|
180
|
+
|
|
181
|
+
# Try importing cibuildwheel as a Python module first (most reliable)
|
|
182
|
+
try:
|
|
183
|
+
import cibuildwheel
|
|
184
|
+
print(f"SUCCESS: Found cibuildwheel module version: {cibuildwheel.__version__}")
|
|
185
|
+
cibw_found = True
|
|
186
|
+
except ImportError:
|
|
187
|
+
print("cibuildwheel module not importable, trying command line...")
|
|
188
|
+
|
|
189
|
+
# Try as a direct command with --help
|
|
190
|
+
try:
|
|
191
|
+
result = subprocess.run(["cibuildwheel", "--help"], check=True, capture_output=True, text=True)
|
|
192
|
+
print("SUCCESS: Found cibuildwheel (direct command)")
|
|
193
|
+
cibw_found = True
|
|
194
|
+
except subprocess.CalledProcessError as e:
|
|
195
|
+
print(f"Direct command failed with exit code {e.returncode}")
|
|
196
|
+
except FileNotFoundError:
|
|
197
|
+
print("Direct command not found")
|
|
198
|
+
|
|
199
|
+
# Try as a Python module with --help
|
|
200
|
+
if not cibw_found:
|
|
201
|
+
try:
|
|
202
|
+
result = subprocess.run([sys.executable, "-m", "cibuildwheel", "--help"], check=True, capture_output=True, text=True)
|
|
203
|
+
print("SUCCESS: Found cibuildwheel (Python module)")
|
|
204
|
+
cibw_found = True
|
|
205
|
+
except subprocess.CalledProcessError as e:
|
|
206
|
+
print(f"Python module failed with exit code {e.returncode}")
|
|
207
|
+
except FileNotFoundError:
|
|
208
|
+
print("Python module not found")
|
|
209
|
+
|
|
210
|
+
if not cibw_found:
|
|
211
|
+
print("ERROR: cibuildwheel not found. Install with: pip install cibuildwheel")
|
|
212
|
+
return 1
|
|
213
|
+
|
|
214
|
+
# Check if we're in the right directory
|
|
215
|
+
if not Path("src/patchmatch_cython/cython_solver_impl.pyx").exists():
|
|
216
|
+
print("ERROR: cython_solver_impl.pyx not found in src/patchmatch_cython/. Run from the project root.")
|
|
217
|
+
print("Current directory:", os.getcwd())
|
|
218
|
+
print("Looking for:", Path("src/patchmatch_cython/cython_solver_impl.pyx").absolute())
|
|
219
|
+
return 1
|
|
220
|
+
|
|
221
|
+
print("SUCCESS: Environment checks passed")
|
|
222
|
+
|
|
223
|
+
# Configure build parameters
|
|
224
|
+
python_versions = args.python
|
|
225
|
+
platforms = args.platform
|
|
226
|
+
|
|
227
|
+
# Build wheels
|
|
228
|
+
success = build_wheels(
|
|
229
|
+
python_versions=python_versions,
|
|
230
|
+
platforms=platforms,
|
|
231
|
+
clean=not args.no_clean
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
if not success:
|
|
235
|
+
return 1
|
|
236
|
+
|
|
237
|
+
# Test wheel installation if requested
|
|
238
|
+
if args.test:
|
|
239
|
+
validate_wheel_installation()
|
|
240
|
+
|
|
241
|
+
print(f"\n{'='*70}")
|
|
242
|
+
print("WHEEL BUILDING COMPLETE")
|
|
243
|
+
print(f"{'='*70}")
|
|
244
|
+
print("INFO: Check the 'wheelhouse' directory for built wheels")
|
|
245
|
+
|
|
246
|
+
return 0
|
|
247
|
+
|
|
248
|
+
if __name__ == "__main__":
|
|
249
|
+
sys.exit(main())
|