From c5eb2f57ddb135dcc3501a90cbe35d1860c2c167 Mon Sep 17 00:00:00 2001 From: Ray Speth Date: Tue, 4 Mar 2014 17:39:21 +0000 Subject: [PATCH] [Python] Make Cantera objects explicitly non-picklable and uncopyable. Attempting to copy or pickle Cantera objects with an underlying C++ object previously led to unexpected errors. These operations will not raise excptions indicating the unimplemented feature. Resolves Issue 205. --- interfaces/cython/cantera/base.pyx | 6 ++++++ interfaces/cython/cantera/func1.pyx | 6 ++++++ interfaces/cython/cantera/onedim.pyx | 5 +++++ interfaces/cython/cantera/reactor.pyx | 12 ++++++++++++ interfaces/cython/cantera/test/test_func1.py | 12 ++++++++++++ interfaces/cython/cantera/test/test_onedim.py | 14 ++++++++++++++ interfaces/cython/cantera/test/test_reactor.py | 17 +++++++++++++++++ interfaces/cython/cantera/test/test_thermo.py | 10 ++++++++++ 8 files changed, 82 insertions(+) diff --git a/interfaces/cython/cantera/base.pyx b/interfaces/cython/cantera/base.pyx index b351b5489..0ff62fea1 100644 --- a/interfaces/cython/cantera/base.pyx +++ b/interfaces/cython/cantera/base.pyx @@ -76,6 +76,12 @@ cdef class _SolutionBase: for i,spec in enumerate(species): self._selected_species[i] = self.species_index(spec) + def __reduce__(self): + raise NotImplementedError('Solution object is not picklable') + + def __copy__(self): + raise NotImplementedError('Solution object is not copyable') + def __dealloc__(self): # only delete the C++ objects if this is the parent object if self.parent is None: diff --git a/interfaces/cython/cantera/func1.pyx b/interfaces/cython/cantera/func1.pyx index 20fad06cb..832a4d2ce 100644 --- a/interfaces/cython/cantera/func1.pyx +++ b/interfaces/cython/cantera/func1.pyx @@ -71,3 +71,9 @@ cdef class Func1: def __call__(self, t): return self.func.eval(t) + + def __reduce__(self): + raise NotImplementedError('Func1 object is not picklable') + + def __copy__(self): + raise NotImplementedError('Func1 object is not copyable') diff --git a/interfaces/cython/cantera/onedim.pyx b/interfaces/cython/cantera/onedim.pyx index 4ad798afa..9b2b5a652 100644 --- a/interfaces/cython/cantera/onedim.pyx +++ b/interfaces/cython/cantera/onedim.pyx @@ -160,6 +160,11 @@ cdef class Domain1D: def __set__(self, desc): self.domain.setDesc(stringify(desc)) + def __reduce__(self): + raise NotImplementedError('Domain1D object is not picklable') + + def __copy__(self): + raise NotImplementedError('Domain1D object is not copyable') cdef class Boundary1D(Domain1D): """ diff --git a/interfaces/cython/cantera/reactor.pyx b/interfaces/cython/cantera/reactor.pyx index e90b21ab7..e95d0af21 100644 --- a/interfaces/cython/cantera/reactor.pyx +++ b/interfaces/cython/cantera/reactor.pyx @@ -115,6 +115,12 @@ cdef class ReactorBase: """ self._walls.append(wall) + def __reduce__(self): + raise NotImplementedError('Reactor object is not picklable') + + def __copy__(self): + raise NotImplementedError('Reactor object is not copyable') + cdef class Reactor(ReactorBase): """ @@ -888,3 +894,9 @@ cdef class ReactorNet: """ def __get__(self): return self.net.neq() + + def __reduce__(self): + raise NotImplementedError('ReactorNet object is not picklable') + + def __copy__(self): + raise NotImplementedError('ReactorNet object is not copyable') diff --git a/interfaces/cython/cantera/test/test_func1.py b/interfaces/cython/cantera/test/test_func1.py index 22c907446..c92ef8a75 100644 --- a/interfaces/cython/cantera/test/test_func1.py +++ b/interfaces/cython/cantera/test/test_func1.py @@ -39,3 +39,15 @@ class TestFunc1(utilities.CanteraTest): f = ct.Func1(fails) self.assertRaises(ValueError, f, 0.1) + + def test_unpicklable(self): + import pickle + f = ct.Func1(np.sin) + with self.assertRaises(NotImplementedError): + pickle.dumps(f) + + def test_uncopyable(self): + import copy + f = ct.Func1(np.sin) + with self.assertRaises(NotImplementedError): + copy.copy(f) diff --git a/interfaces/cython/cantera/test/test_onedim.py b/interfaces/cython/cantera/test/test_onedim.py index f42eb8076..a6b0ec82a 100644 --- a/interfaces/cython/cantera/test/test_onedim.py +++ b/interfaces/cython/cantera/test/test_onedim.py @@ -47,6 +47,20 @@ class TestOnedim(utilities.CanteraTest): self.assertArrayNear(inlet.X, Xref) self.assertArrayNear(inlet.Y, Yref) + def test_unpicklable(self): + import pickle + gas = ct.Solution('h2o2.xml') + flame = ct.FreeFlow(gas) + with self.assertRaises(NotImplementedError): + pickle.dumps(flame) + + def test_uncopyable(self): + import copy + gas = ct.Solution('h2o2.xml') + flame = ct.FreeFlow(gas) + with self.assertRaises(NotImplementedError): + copy.copy(flame) + class TestFreeFlame(utilities.CanteraTest): tol_ss = [1.0e-5, 1.0e-14] # [rtol atol] for steady-state problem diff --git a/interfaces/cython/cantera/test/test_reactor.py b/interfaces/cython/cantera/test/test_reactor.py index e0cd2ebac..28799bf65 100644 --- a/interfaces/cython/cantera/test/test_reactor.py +++ b/interfaces/cython/cantera/test/test_reactor.py @@ -474,6 +474,23 @@ class TestReactor(utilities.CanteraTest): self.assertNear(p1a, p1b) self.assertNear(p2a, p2b) + def test_unpicklable(self): + self.make_reactors() + import pickle + with self.assertRaises(NotImplementedError): + pickle.dumps(self.r1) + with self.assertRaises(NotImplementedError): + pickle.dumps(self.net) + + def test_uncopyable(self): + self.make_reactors() + import copy + with self.assertRaises(NotImplementedError): + copy.copy(self.r1) + with self.assertRaises(NotImplementedError): + copy.copy(self.net) + + class TestIdealGasReactor(TestReactor): reactorClass = ct.IdealGasReactor diff --git a/interfaces/cython/cantera/test/test_thermo.py b/interfaces/cython/cantera/test/test_thermo.py index 173e867f5..732831fdf 100644 --- a/interfaces/cython/cantera/test/test_thermo.py +++ b/interfaces/cython/cantera/test/test_thermo.py @@ -307,6 +307,16 @@ class TestThermoPhase(utilities.CanteraTest): self.assertNear(self.phase.min_temp, 300.0) self.assertNear(self.phase.max_temp, 3500.0) + def test_unpicklable(self): + import pickle + with self.assertRaises(NotImplementedError): + pickle.dumps(self.phase) + + def test_uncopyable(self): + import copy + with self.assertRaises(NotImplementedError): + copy.copy(self.phase) + class TestThermo(utilities.CanteraTest): def setUp(self):