Skip to content

Commit

Permalink
Python copy support (AcademySoftwareFoundation#1575)
Browse files Browse the repository at this point in the history
* Add deepcopy support for object with createEditableCopy method

Signed-off-by: Rémi Achard <[email protected]>

* Minor fixes

Signed-off-by: Rémi Achard <[email protected]>

* Removing python shallow copy method

Signed-off-by: Rémi Achard <[email protected]>

* Update comment and formating

Signed-off-by: Rémi Achard <[email protected]>

* Testing COnfig FileRules deepCopy

Signed-off-by: Rémi Achard <[email protected]>

* Add comment for ConfigTest

Signed-off-by: Rémi Achard <[email protected]>

Co-authored-by: Patrick Hodoul <[email protected]>
  • Loading branch information
remia and hodoulp committed Mar 8, 2022
1 parent 14f13c8 commit 51d5968
Show file tree
Hide file tree
Showing 21 changed files with 344 additions and 38 deletions.
46 changes: 22 additions & 24 deletions include/OpenColorIO/OpenColorIO.h
Original file line number Diff line number Diff line change
Expand Up @@ -1328,30 +1328,28 @@ extern OCIOEXPORT std::ostream & operator<< (std::ostream &, const FileRules &);




// TODO: Move to .rst
//
// ViewingRules
// ************
// Viewing Rules allow config authors to filter the list of views an application should offer
// based on the color space of an image. For example, a config may define a large number of
// views but not all of them may be appropriate for use with all color spaces. E.g., some views
// may be intended for use with scene-linear color space encodings and others with video color
// space encodings.
//
// Each rule has a name key for applications to refer to the rule. Name values must be unique
// (using case insensitive comparison). Viewing Rules may also have the following keys:
//
// * colorspaces: Either a single colorspace name or a list of names.
//
// * encodings: One or more strings to be found in the colorspace's encoding attribute.
// Either this attribute or colorspaces must be present, but not both.
//
// * custom : Allows arbitrary key / value string pairs, similar to FileRules.
//
// Getters and setters are using the rule position, they will throw if the position is not
// valid.

/**
* ViewingRules
*
* Viewing Rules allow config authors to filter the list of views an application should offer
* based on the color space of an image. For example, a config may define a large number of
* views but not all of them may be appropriate for use with all color spaces. E.g., some views
* may be intended for use with scene-linear color space encodings and others with video color
* space encodings.
*
* Each rule has a name key for applications to refer to the rule. Name values must be unique
* (using case insensitive comparison). Viewing Rules may also have the following keys:
*
* * colorspaces: Either a single colorspace name or a list of names.
*
* * encodings: One or more strings to be found in the colorspace's encoding attribute.
* Either this attribute or colorspaces must be present, but not both.
*
* * custom : Allows arbitrary key / value string pairs, similar to FileRules.
*
* Getters and setters are using the rule position, they will throw if the position is not
* valid.
*/
class OCIOEXPORT ViewingRules
{
public:
Expand Down
6 changes: 6 additions & 0 deletions src/bindings/python/PyBaker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ Each item is a tuple containing format name and format extension.
"shaperSize"_a = DEFAULT->getShaperSize(),
DOC(Baker, Create))

.def("__deepcopy__", [](const ConstBakerRcPtr & self, py::dict)
{
return self->createEditableCopy();
},
"memo"_a)

.def_static("getFormats", []()
{
return FormatIterator(nullptr);
Expand Down
6 changes: 6 additions & 0 deletions src/bindings/python/PyColorSpace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@ void bindPyColorSpace(py::module & m)
"categories"_a = getCategoriesStdVec(DEFAULT),
DOC(ColorSpace, Create, 2))

.def("__deepcopy__", [](const ConstColorSpaceRcPtr & self, py::dict)
{
return self->createEditableCopy();
},
"memo"_a)

.def("getName", &ColorSpace::getName,
DOC(ColorSpace, getName))
.def("setName", &ColorSpace::setName, "name"_a.none(false),
Expand Down
6 changes: 6 additions & 0 deletions src/bindings/python/PyColorSpaceSet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ void bindPyColorSpaceSet(py::module & m)
.def(py::init(&ColorSpaceSet::Create),
DOC(ColorSpaceSet, Create))

.def("__deepcopy__", [](const ConstColorSpaceSetRcPtr & self, py::dict)
{
return self->createEditableCopy();
},
"memo"_a)

.def("__eq__", &ColorSpaceSet::operator==, py::is_operator(),
DOC(ColorSpaceSet, operator, eq))
.def("__ne__", &ColorSpaceSet::operator!=, py::is_operator(),
Expand Down
6 changes: 6 additions & 0 deletions src/bindings/python/PyConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,12 @@ void bindPyConfig(py::module & m)
.def(py::init(&Config::Create),
DOC(Config, Create))

.def("__deepcopy__", [](const ConstConfigRcPtr & self, py::dict)
{
return self->createEditableCopy();
},
"memo"_a)

.def_static("CreateRaw", &Config::CreateRaw,
DOC(Config, CreateRaw))
.def_static("CreateFromEnv", &Config::CreateFromEnv,
Expand Down
8 changes: 7 additions & 1 deletion src/bindings/python/PyContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,13 @@ void bindPyContext(py::module & m)
"searchPaths"_a = getSearchPathsStdVec(DEFAULT),
"stringVars"_a = getStringVarsStdMap(DEFAULT),
"environmentMode"_a = DEFAULT->getEnvironmentMode(),
DOC(Context, Create))
DOC(Context, Create))

.def("__deepcopy__", [](const ConstContextRcPtr & self, py::dict)
{
return self->createEditableCopy();
},
"memo"_a)

.def("__iter__", [](ContextRcPtr & self)
{
Expand Down
6 changes: 6 additions & 0 deletions src/bindings/python/PyFileRules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ void bindPyFileRules(py::module & m)
.def(py::init(&FileRules::Create),
DOC(FileRules, Create))

.def("__deepcopy__", [](const ConstFileRulesRcPtr & self, py::dict)
{
return self->createEditableCopy();
},
"memo"_a)

.def("getNumEntries", &FileRules::getNumEntries,
DOC(FileRules, getNumEntries))
.def("getIndexForRule", &FileRules::getIndexForRule, "ruleName"_a,
Expand Down
6 changes: 6 additions & 0 deletions src/bindings/python/PyLook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ void bindPyLook(py::module & m)
"description"_a = DEFAULT->getDescription(),
DOC(Look, Create))

.def("__deepcopy__", [](const ConstLookRcPtr & self, py::dict)
{
return self->createEditableCopy();
},
"memo"_a)

.def("getName", &Look::getName,
DOC(Look, getName))
.def("setName", &Look::setName, "name"_a.none(false),
Expand Down
8 changes: 7 additions & 1 deletion src/bindings/python/PyNamedTransform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,12 @@ void bindPyNamedTransform(py::module & m)
"categories"_a = getCategoriesStdVec(DEFAULT),
DOC(NamedTransform, Create))

.def("__deepcopy__", [](const ConstNamedTransformRcPtr & self, py::dict)
{
return self->createEditableCopy();
},
"memo"_a)

.def("getName", &NamedTransform::getName,
DOC(NamedTransform, getName))
.def("setName", &NamedTransform::setName, "name"_a.none(false),
Expand Down Expand Up @@ -145,7 +151,7 @@ void bindPyNamedTransform(py::module & m)
DOC(NamedTransform, setDescription))
.def("getEncoding", &NamedTransform::getEncoding,
DOC(NamedTransform, getEncoding))
.def("setEncoding", &NamedTransform::setEncoding, "encodig"_a.none(false),
.def("setEncoding", &NamedTransform::setEncoding, "encoding"_a.none(false),
DOC(NamedTransform, setEncoding))

// Transform
Expand Down
6 changes: 6 additions & 0 deletions src/bindings/python/PyTransform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ void bindPyTransform(py::module & m)
py::class_<Transform, TransformRcPtr>(
m.attr("Transform"))

.def("__deepcopy__", [](const ConstTransformRcPtr & self, py::dict)
{
return self->createEditableCopy();
},
"memo"_a)

.def("validate", &Transform::validate,
DOC(Transform, validate))
.def("getTransformType", &Transform::getTransformType,
Expand Down
8 changes: 7 additions & 1 deletion src/bindings/python/PyViewTransform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,13 @@ void bindPyViewTransform(py::module & m)
"toReference"_a = DEFAULT->getTransform(VIEWTRANSFORM_DIR_TO_REFERENCE),
"fromReference"_a = DEFAULT->getTransform(VIEWTRANSFORM_DIR_FROM_REFERENCE),
"categories"_a = getCategoriesStdVec(DEFAULT),
DOC(ViewTransform, Create))
DOC(ViewTransform, Create))

.def("__deepcopy__", [](const ConstViewTransformRcPtr & self, py::dict)
{
return self->createEditableCopy();
},
"memo"_a)

.def("getName", &ViewTransform::getName,
DOC(ViewTransform, getName))
Expand Down
8 changes: 7 additions & 1 deletion src/bindings/python/PyViewingRules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,13 @@ void bindPyViewingRules(py::module & m)
clsViewingRules
.def(py::init(&ViewingRules::Create),
DOC(ViewingRules, Create))


.def("__deepcopy__", [](const ConstViewingRulesRcPtr & self, py::dict)
{
return self->createEditableCopy();
},
"memo"_a)

.def("getNumEntries", &ViewingRules::getNumEntries,
DOC(ViewingRules, getNumEntries))
.def("getIndexForRule", &ViewingRules::getIndexForRule, "ruleName"_a,
Expand Down
28 changes: 27 additions & 1 deletion tests/python/BakerTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

# TODO: Add getFormatMetadata tests.

import unittest, os, sys
import copy, unittest, os, sys
import PyOpenColorIO as OCIO

class BakerTest(unittest.TestCase):
Expand Down Expand Up @@ -55,6 +55,32 @@ class BakerTest(unittest.TestCase):
"""

def test_copy(self):
"""
Test the deepcopy() method.
"""
cfg = OCIO.Config().CreateFromStream(self.SIMPLE_PROFILE)

bake = OCIO.Baker()
bake.setConfig(cfg)
bake.setFormat("cinespace")
bake.setInputSpace("lnh")
bake.setLooks("foo, +bar")
bake.setTargetSpace("test")
bake.setShaperSize(4)
bake.setCubeSize(2)

other = copy.deepcopy(bake)
self.assertFalse(other is bake)

self.assertEqual(other.getConfig(), bake.getConfig())
self.assertEqual(other.getFormat(), bake.getFormat())
self.assertEqual(other.getInputSpace(), bake.getInputSpace())
self.assertEqual(other.getLooks(), bake.getLooks())
self.assertEqual(other.getTargetSpace(), bake.getTargetSpace())
self.assertEqual(other.getShaperSize(), bake.getShaperSize())
self.assertEqual(other.getCubeSize(), bake.getCubeSize())

def test_interface(self):
"""
Test similar to C++ CPU test.
Expand Down
38 changes: 38 additions & 0 deletions tests/python/ColorSpaceTest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright Contributors to the OpenColorIO Project.

import copy
import unittest
import os
import sys
Expand All @@ -10,6 +11,7 @@


class ColorSpaceTest(unittest.TestCase):

def setUp(self):
self.colorspace = OCIO.ColorSpace()
self.log_tr = OCIO.LogTransform(10)
Expand All @@ -18,6 +20,42 @@ def tearDown(self):
self.colorspace = None
self.log_tr = None

def test_copy(self):
"""
Test the deepcopy() method.
"""
self.colorspace.setName('colorspace1')
self.colorspace.setFamily('family')
self.colorspace.setEqualityGroup('group')
self.colorspace.setDescription('description')
self.colorspace.setBitDepth(OCIO.BIT_DEPTH_UINT8)
self.colorspace.setEncoding('encoding')
self.colorspace.setIsData(False)
self.colorspace.setAllocation(OCIO.ALLOCATION_LG2)
self.colorspace.setAllocationVars([-8, 5, 0.00390625])
mat = OCIO.MatrixTransform()
self.colorspace.setTransform(mat, OCIO.COLORSPACE_DIR_TO_REFERENCE)
self.colorspace.setTransform(direction=OCIO.COLORSPACE_DIR_FROM_REFERENCE, transform=mat)
self.colorspace.addAlias('alias')
self.colorspace.addCategory('cat')

other = copy.deepcopy(self.colorspace)
self.assertFalse(other is self.colorspace)

self.assertEqual(other.getName(), self.colorspace.getName())
self.assertEqual(other.getFamily(), self.colorspace.getFamily())
self.assertEqual(other.getEqualityGroup(), self.colorspace.getEqualityGroup())
self.assertEqual(other.getDescription(), self.colorspace.getDescription())
self.assertEqual(other.getBitDepth(), self.colorspace.getBitDepth())
self.assertEqual(other.getEncoding(), self.colorspace.getEncoding())
self.assertEqual(other.isData(), self.colorspace.isData())
self.assertEqual(other.getAllocation(), self.colorspace.getAllocation())
self.assertEqual(other.getAllocationVars(), self.colorspace.getAllocationVars())
self.assertTrue(other.getTransform(OCIO.COLORSPACE_DIR_TO_REFERENCE).equals(self.colorspace.getTransform(OCIO.COLORSPACE_DIR_TO_REFERENCE)))
self.assertTrue(other.getTransform(OCIO.COLORSPACE_DIR_FROM_REFERENCE).equals(self.colorspace.getTransform(OCIO.COLORSPACE_DIR_FROM_REFERENCE)))
self.assertEqual(list(other.getAliases()), list(self.colorspace.getAliases()))
self.assertEqual(list(other.getCategories()), list(self.colorspace.getCategories()))

def test_allocation(self):
"""
Test the setAllocation() and getAllocation() methods.
Expand Down
40 changes: 40 additions & 0 deletions tests/python/ConfigTest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright Contributors to the OpenColorIO Project.

import copy
import unittest
import os
import sys
Expand Down Expand Up @@ -241,6 +242,45 @@

class ConfigTest(unittest.TestCase):

def test_copy(self):
"""
Test the deepcopy() method.
"""
cfg = OCIO.Config.CreateRaw()
cfg.setMajorVersion(2)
cfg.setMinorVersion(1)
cfg.setName('test config')
cfg.setDescription('test description')

cfg.addColorSpace(
OCIO.ColorSpace(OCIO.REFERENCE_SPACE_DISPLAY,
"display_cs",
toReference=OCIO.CDLTransform(sat=1.5)))
cfg.addColorSpace(
OCIO.ColorSpace(OCIO.REFERENCE_SPACE_SCENE,
"raw",
isData=True))

rules = OCIO.FileRules()
rules.insertRule(0, 'A', 'raw', '*', 'exr')
rules.insertRule(1, 'B', 'display_cs', '*', 'png')
cfg.setFileRules(rules)

other = copy.deepcopy(cfg)
self.assertFalse(other is cfg)

self.assertEqual(other.getMajorVersion(), cfg.getMajorVersion())
self.assertEqual(other.getMinorVersion(), cfg.getMinorVersion())
self.assertEqual(other.getName(), cfg.getName())
self.assertEqual(other.getDescription(), cfg.getDescription())
self.assertEqual(list(other.getColorSpaceNames()), list(cfg.getColorSpaceNames()))
self.assertEqual(other.getFileRules().getNumEntries(), cfg.getFileRules().getNumEntries())

# Check that the file rules are not shared between the two config instances.
rules.removeRule(0)
other.setFileRules(rules)
self.assertEqual(other.getFileRules().getNumEntries(), cfg.getFileRules().getNumEntries() - 1)

def test_shared_views(self):
# Test these Config functions: addSharedView, getSharedViews, removeSharedView.

Expand Down
22 changes: 21 additions & 1 deletion tests/python/ContextTest.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,31 @@
# SPDX-License-Identifier: BSD-3-Clause
# Copyright Contributors to the OpenColorIO Project.

import unittest, os, sys
import copy, unittest, os, sys
import PyOpenColorIO as OCIO

class ContextTest(unittest.TestCase):

def test_copy(self):
"""
Test the deepcopy() method.
"""
cont = OCIO.Context()
cont.setSearchPath('testing123:testing456')
cont.setWorkingDir('/dir/123')
cont.setEnvironmentMode(OCIO.ENV_ENVIRONMENT_LOAD_PREDEFINED)
cont['TeSt'] = 'foobar'
cont['Bar'] = 'Foo'

other = copy.deepcopy(cont)
self.assertFalse(other is cont)

self.assertEqual(other.getCacheID(), cont.getCacheID())
self.assertEqual(other.getSearchPath(), cont.getSearchPath())
self.assertEqual(other.getWorkingDir(), cont.getWorkingDir())
self.assertEqual(other.getEnvironmentMode(), cont.getEnvironmentMode())
self.assertEqual(list(other), list(cont))

def test_interface(self):
"""
Construct and use Context.
Expand Down
Loading

0 comments on commit 51d5968

Please sign in to comment.