Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Python wheels #1487

Merged
merged 30 commits into from
Jul 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b110f56
Let's see if the wheels are building
sanguinariojoe Jul 11, 2023
ccbe080
Enable the building on the python branch to can test it
sanguinariojoe Jul 11, 2023
522c8a4
Wrong path
sanguinariojoe Jul 11, 2023
3da8e25
Windows...
sanguinariojoe Jul 11, 2023
1bd9077
Copy the wrapper source files on the main folder
sanguinariojoe Jul 11, 2023
5944c70
Skip the OpenEXR examples
sanguinariojoe Jul 11, 2023
7ec72a9
Fixed the library names
sanguinariojoe Jul 11, 2023
d6a9eeb
Install pytest to run the tests
sanguinariojoe Jul 11, 2023
430d89e
Let's try the cibuildwheel way
sanguinariojoe Jul 11, 2023
64f3372
Maybe this way...
sanguinariojoe Jul 11, 2023
1a9f7a0
Use require instead of custom command
sanguinariojoe Jul 11, 2023
3e6ce3f
Simplified the test
sanguinariojoe Jul 11, 2023
fb74e92
Specifically instruct pytest to run the tests in the folder
sanguinariojoe Jul 11, 2023
81441b3
Fix the test
sanguinariojoe Jul 11, 2023
fc6b546
Disable threading
sanguinariojoe Jul 11, 2023
bd85dee
Reenable threading
sanguinariojoe Jul 11, 2023
753886b
Link also OpenEXRCore
sanguinariojoe Jul 12, 2023
fe8085f
CIBW_BEFORE_BUILD is the same in all platforms
sanguinariojoe Jul 12, 2023
eea1217
Imath 3.1.9
sanguinariojoe Jul 12, 2023
9ff4fc7
List the exported symbols
sanguinariojoe Jul 12, 2023
da15ae3
List the exported symbols
sanguinariojoe Jul 12, 2023
429f474
Link everything available
sanguinariojoe Jul 12, 2023
569c68a
Link everything available
sanguinariojoe Jul 12, 2023
f929767
Link statically also libdeflate
sanguinariojoe Jul 14, 2023
8e01bab
tar.gz, not zip
sanguinariojoe Jul 14, 2023
140cc97
libdeflate static lib name is not found by openexr on windows
sanguinariojoe Jul 14, 2023
58621ff
Syntax error
sanguinariojoe Jul 14, 2023
e88b0c2
Try to fix the deflate linking on windows
sanguinariojoe Jul 14, 2023
ad5a6d0
Remove the need of the wildcard while testing
sanguinariojoe Jul 14, 2023
670bd05
BSD license
sanguinariojoe Jul 18, 2023
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
116 changes: 116 additions & 0 deletions .github/workflows/python-wheels.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
name: Python wheels

on:
push:
branches: [ $default-branch, main, python ]

jobs:
build_wheels:
name: Build Python wheels
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-22.04, windows-latest, macOS-latest]
env:
# Skip 32-bit wheels builds
CIBW_SKIP: "*-win32 *_i686"
CIBW_BEFORE_BUILD: >
echo "Installing Zlib..." &&
cd zlib.build &&
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../openexr.install -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_PREFIX_PATH=../openexr.install -DCMAKE_INSTALL_LIBDIR=lib ../zlib &&
cmake --build ./ --config Release --clean-first &&
cmake --install ./ --config Release &&
cd .. &&
echo "Installing libDeflate..." &&
cd libdeflate.build &&
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../openexr.install -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_PREFIX_PATH=../openexr.install -DCMAKE_INSTALL_LIBDIR=lib -DLIBDEFLATE_BUILD_SHARED_LIB=OFF -DLIBDEFLATE_USE_SHARED_LIB=OFF -DBUILD_SHARED_LIBS=OFF ../libdeflate &&
cmake --build ./ --config Release --clean-first &&
cmake --install ./ --config Release &&
cd .. &&
echo "Installing Imath-3.1.9..." &&
cd imath.build &&
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../openexr.install -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_PREFIX_PATH=../openexr.install -DCMAKE_INSTALL_LIBDIR=lib -DBUILD_TESTING=OFF -DBUILD_SHARED_LIBS=OFF ../imath &&
cmake --build ./ --config Release --clean-first &&
cmake --install ./ --config Release &&
cd .. &&
echo "Installing OpenEXR..." &&
cd openexr.build &&
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../openexr.install -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_PREFIX_PATH=../openexr.install -DCMAKE_INSTALL_LIBDIR=lib -DBUILD_TESTING=OFF -DOPENEXR_INSTALL_EXAMPLES=OFF -DBUILD_SHARED_LIBS=OFF -DOPENEXR_FORCE_INTERNAL_DEFLATE=ON ../ &&
cmake --build ./ --config Release --clean-first &&
cmake --install ./ --config Release &&
cd ..
CIBW_TEST_REQUIRES: pytest
CIBW_TEST_COMMAND: pytest {project}/src/wrappers/python/tests/

steps:
- uses: actions/checkout@v3

# Used to host cibuildwheel
- uses: actions/setup-python@v4
with:
python-version: '3.x'

- name: Install cibuildwheel
run: python -m pip install cibuildwheel==2.8.1

- name: Create setup.py
run: |
mv ${{github.workspace}}/src/wrappers/python/setup.py ${{github.workspace}}/setup.py
mv ${{github.workspace}}/src/wrappers/python/Imath.py ${{github.workspace}}/Imath.py
mv ${{github.workspace}}/src/wrappers/python/OpenEXR.cpp ${{github.workspace}}/OpenEXR.cpp

- name: Create folders
run: |
mkdir -p ${{github.workspace}}/zlib.build
mkdir -p ${{github.workspace}}/libdeflate.build
mkdir -p ${{github.workspace}}/imath.build
mkdir -p ${{github.workspace}}/openexr.build
mkdir -p ${{github.workspace}}/openexr.install

- name: download Zlib source code
uses: suisei-cn/[email protected]
with:
url: https://github.com/madler/zlib/releases/download/v1.2.13/zlib-1.2.13.tar.gz
target: ${{github.workspace}}/

- name: Extract Zlib
run: |
tar -xvzf zlib-1.2.13.tar.gz -C ${{github.workspace}}/
mv zlib-1.2.13 zlib
rm zlib-1.2.13.tar.gz

- name: download libDeflate source code
uses: suisei-cn/[email protected]
with:
url: https://github.com/ebiggers/libdeflate/archive/refs/tags/v1.18.tar.gz
target: ${{github.workspace}}/

- name: Extract libDeflate
run: |
tar -xvzf v1.18.tar.gz -C ${{github.workspace}}/
mv libdeflate-1.18 libdeflate
rm v1.18.tar.gz

- name: Patch libDeflate
run: |
patch -u libdeflate/CMakeLists.txt -i ${{github.workspace}}/src/wrappers/python/libdeflate.patch

- name: download Imath source code
uses: suisei-cn/[email protected]
with:
url: https://github.com/AcademySoftwareFoundation/Imath/archive/refs/tags/v3.1.9.tar.gz
target: ${{github.workspace}}/

- name: Extract Imath
run: |
tar -xvzf v3.1.9.tar.gz -C ${{github.workspace}}/
mv Imath-3.1.9 imath
rm v3.1.9.tar.gz

- name: Build wheels
run: python -m cibuildwheel --output-dir wheelhouse

- uses: actions/upload-artifact@v3
with:
name: "Python wheels"
path: ./wheelhouse/*.whl
275 changes: 275 additions & 0 deletions src/wrappers/python/Imath.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright (c) Contributors to the OpenEXR Project.

"""
:mod:`Imath` --- Support types for OpenEXR library
==================================================
"""

class chromaticity(object):
"""Store chromaticity coordinates in *x* and *y*."""
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return repr((self.x, self.y))
def __eq__(self, other):
return (self.x, self.y) == (other.x, other.y)

class point(object):
"""Point is a 2D point, with members *x* and *y*."""
def __init__(self, x, y):
self.x = x;
self.y = y;
def __repr__(self):
return repr((self.x, self.y))
def __eq__(self, other):
return (self.x, self.y) == (other.x, other.y)

class V2i(point):
"""V2i is a 2D point, with members *x* and *y*."""
pass

class V2f(point):
"""V2f is a 2D point, with members *x* and *y*."""
pass

class Box:
"""Box is a 2D box, specified by its two corners *min* and *max*, both of which are :class:`point` """
def __init__(self, min = None, max = None):
self.min = min
self.max = max
def __repr__(self):
return repr(self.min) + " - " + repr(self.max)
def __eq__(self, other):
return (self.min, self.max) == (other.min, other.max)

class Box2i(Box):
"""Box2i is a 2D box, specified by its two corners *min* and *max*."""
pass

class Box2f(Box):
"""Box2f is a 2D box, specified by its two corners *min* and *max*."""
pass

class Chromaticities:
"""
Chromaticities holds the set of chromaticity coordinates for *red*, *green*, *blue*, and *white*.
Each primary is a :class:`chromaticity`.
"""
def __init__(self, red = None, green = None, blue = None, white = None):
self.red = red
self.green = green
self.blue = blue
self.white = white
def __repr__(self):
return repr(self.red) + " " + repr(self.green) + " " + repr(self.blue) + " " + repr(self.white)

class Enumerated(object):
def __init__(self, v):
if v in self.names:
self.v = eval("self." + v)
else:
self.v = v
def __repr__(self):
return self.names[self.v]
def __cmp__(self, other):
return self.v - other.v
def __eq__(self, other):
return self.v == other.v

class LineOrder(Enumerated):
"""
.. index:: INCREASING_Y, DECREASING_Y, RANDOM_Y

LineOrder can have three possible values:
``INCREASING_Y``,
``DECREASING_Y``,
``RANDOM_Y``.

.. doctest::

>>> import Imath
>>> print Imath.LineOrder(Imath.LineOrder.DECREASING_Y)
DECREASING_Y
"""
INCREASING_Y = 0
DECREASING_Y = 1
RANDOM_Y = 2
names = ["INCREASING_Y", "DECREASING_Y", "RANDOM_Y"]

class Compression(Enumerated):
"""
.. index:: NO_COMPRESSION, RLE_COMPRESSION, ZIPS_COMPRESSION, ZIP_COMPRESSION, PIZ_COMPRESSION, PXR24_COMPRESSION, B44_COMPRESSION, B44A_COMPRESSION, DWAA_COMPRESSION, DWAB_COMPRESSION,

Compression can have possible values:
``NO_COMPRESSION``,
``RLE_COMPRESSION``,
``ZIPS_COMPRESSION``,
``ZIP_COMPRESSION``,
``PIZ_COMPRESSION``,
``PXR24_COMPRESSION``,
``B44_COMPRESSION``,
``B44A_COMPRESSION``,
``DWAA_COMPRESSION``,
``DWAB_COMPRESSION``.

.. doctest::

>>> import Imath
>>> print Imath.Compression(Imath.Compression.RLE_COMPRESSION)
RLE_COMPRESSION
"""
NO_COMPRESSION = 0
RLE_COMPRESSION = 1
ZIPS_COMPRESSION = 2
ZIP_COMPRESSION = 3
PIZ_COMPRESSION = 4
PXR24_COMPRESSION = 5
B44_COMPRESSION = 6
B44A_COMPRESSION = 7
DWAA_COMPRESSION = 8
DWAB_COMPRESSION = 9
names = [
"NO_COMPRESSION", "RLE_COMPRESSION", "ZIPS_COMPRESSION", "ZIP_COMPRESSION", "PIZ_COMPRESSION", "PXR24_COMPRESSION",
"B44_COMPRESSION", "B44A_COMPRESSION", "DWAA_COMPRESSION", "DWAB_COMPRESSION"
]

class PixelType(Enumerated):
"""
.. index:: UINT, HALF, FLOAT

PixelType can have possible values ``UINT``, ``HALF``, ``FLOAT``.

.. doctest::

>>> import Imath
>>> print Imath.PixelType(Imath.PixelType.HALF)
HALF
"""
UINT = 0
HALF = 1
FLOAT = 2
names = ["UINT", "HALF", "FLOAT"]

class Channel:
"""
Channel defines the type and spatial layout of a channel.
*type* is a :class:`PixelType`.
*xSampling* is the number of X-axis pixels between samples.
*ySampling* is the number of Y-axis pixels between samples.

.. doctest::

>>> import Imath
>>> print Imath.Channel(Imath.PixelType(Imath.PixelType.FLOAT), 4, 4)
FLOAT (4, 4)
"""

def __init__(self, type = PixelType(PixelType.HALF), xSampling = 1, ySampling = 1):
self.type = type
self.xSampling = xSampling
self.ySampling = ySampling
def __repr__(self):
return repr(self.type) + " " + repr((self.xSampling, self.ySampling))
def __eq__(self, other):
return (self.type, self.xSampling, self.ySampling) == (other.type, other.xSampling, other.ySampling)

class Rational(object):
def __init__(self, n, d):
self.n = n
self.d = d
def __repr__(self):
return repr("%s/%s (%.3f)" % (self.n, self.d, self.n/float(self.d)))
def __eq__(self, other):
return self.n == other.n and self.d == other.d


class TimeCode:
def __init__(self, hours, minutes, seconds, frame, dropFrame=False, colorFrame=False, fieldPhase=False, bgf0=False, bgf1=False, bgf2=False, binaryGroup1=0, binaryGroup2=0, binaryGroup3=0, binaryGroup4=0, binaryGroup5=0, binaryGroup6=0, binaryGroup7=0, binaryGroup8=0):
self.hours = hours
self.minutes = minutes
self.seconds = seconds
self.frame = frame
self.dropFrame = dropFrame
self.colorFrame = colorFrame
self.fieldPhase = fieldPhase
self.bgf0 = bgf0
self.bgf1 = bgf1
self.bgf2 = bgf2
self.binaryGroup1 = binaryGroup1
self.binaryGroup2 = binaryGroup2
self.binaryGroup3 = binaryGroup3
self.binaryGroup4 = binaryGroup4
self.binaryGroup5 = binaryGroup5
self.binaryGroup6 = binaryGroup6
self.binaryGroup7 = binaryGroup7
self.binaryGroup8 = binaryGroup8

def __repr__(self):
# ignoring binaryGroups for now
return "<Imath.TimeCode instance { time: %s:%s:%s:%s, dropFrame: %s, colorFrame: %s, fieldPhase: %s, bgf0: %s, bgf1: %s, bgf2: %s" % (self.hours, self.minutes, self.seconds, self.frame, self.dropFrame, self.colorFrame, self.fieldPhase, self.bgf0, self.bgf1, self.bgf2)

def __eq__(self, other):
return self.__dict__ == other.__dict__

class KeyCode:
def __init__(self, filmMfcCode=0, filmType=0, prefix=0, count=0, perfOffset=0, perfsPerFrame=4, perfsPerCount=64):
self.filmMfcCode = filmMfcCode
self.filmType = filmType
self.prefix = prefix
self.count = count
self.perfOffset = perfOffset
self.perfsPerFrame = perfsPerFrame
self.perfsPerCount = perfsPerCount
def __repr__(self):
return "<Imath.KeyCode instance { filmMfcCode: %s, filmType: %s, prefix: %s, count: %s, perfOffset: %s, perfsPerFrame: %s, perfsPerCount: %s }" % (self.filmMfcCode, self.filmType, self.prefix, self.count, self.perfOffset, self.perfsPerFrame, self.perfsPerCount)

def __eq__(self, other):
return self.__dict__ == other.__dict__

class PreviewImage:
"""
.. index:: RGBA, thumbnail, preview, JPEG, PIL, Python Imaging Library

PreviewImage is a small preview image, intended as a thumbnail version of the full image.
The image has size (*width*, *height*) and 8-bit pixel values are
given by string *pixels* in RGBA order from top-left to bottom-right.

For example, to create a preview image from a JPEG file using the popular
`Python Imaging Library <http://www.pythonware.com/library/pil/handbook/index.htm>`_:

.. doctest::

>>> import Image
>>> import Imath
>>> im = Image.open("lena.jpg").resize((100, 100)).convert("RGBA")
>>> print Imath.PreviewImage(im.size[0], im.size[1], im.tostring())
<Imath.PreviewImage instance 100x100>
"""
def __init__(self, width, height, pixels):
self.width = width
self.height = height
self.pixels = pixels
def __repr__(self):
return "<Imath.PreviewImage instance %dx%d>" % (self.width, self.height)

class LevelMode(Enumerated):
ONE_LEVEL = 0
MIPMAP_LEVELS = 1
RIPMAP_LEVELS = 2
names = ["ONE_LEVEL", "MIPMAP_LEVELS", "RIPMAP_LEVELS"]

class LevelRoundingMode(Enumerated):
ROUND_DOWN = 0
ROUND_UP = 1
names = ["ROUND_DOWN", "ROUND_UP"]

class TileDescription:
def __init__(self, xs = 32, ys = 32, m = LevelMode(LevelMode.ONE_LEVEL), r =LevelRoundingMode(LevelRoundingMode.ROUND_DOWN)):
self.xSize = xs
self.ySize = ys
self.mode = m
self.roundingMode = r
def __repr__(self):
return "<Imath.TileDescription instance %dx%d %s %s>" % (self.xSize, self.ySize, repr(self.mode), repr(self.roundingMode))
Loading