Skip to content

Commit

Permalink
MAINT: Data view on coord getitem data copy on coord copy
Browse files Browse the repository at this point in the history
  • Loading branch information
cpelley committed Dec 9, 2016
1 parent f775801 commit 2810779
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 33 deletions.
18 changes: 6 additions & 12 deletions lib/iris/coords.py
Original file line number Diff line number Diff line change
Expand Up @@ -516,27 +516,19 @@ def copy(self, points=None, bounds=None):
.. note:: If the points argument is specified and bounds are not, the
resulting coordinate will have no bounds.
.. deprecated:: 1.12
By default the new coordinate's `points` and `bounds` will
be independent copies of the corresponding attributes of the
source coordinate.
The `share_data` attribute of `iris.FUTURE` can be used to
switch to the new data-sharing behaviour.
"""

if points is None and bounds is not None:
raise ValueError('If bounds are specified, points must also be '
'specified')

if iris.FUTURE.share_data:
if points is not None:
# We do not perform a deepcopy when we supply new points so as to
# not unnecessarily copy the old points.
new_coord = copy.copy(self)
new_coord.attributes = copy.deepcopy(self.attributes)
new_coord.coord_system = copy.deepcopy(self.coord_system)
else:
new_coord = copy.deepcopy(self)
if points is not None:

# Explicitly not using the points property as we don't want the
# shape the new points to be constrained by the shape of
# self.points
Expand All @@ -546,6 +538,8 @@ def copy(self, points=None, bounds=None):
# points will result in new bounds, discarding those copied from
# self.
new_coord.bounds = bounds
else:
new_coord = copy.deepcopy(self)

return new_coord

Expand Down
69 changes: 48 additions & 21 deletions lib/iris/tests/unit/coords/test_AuxCoord.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,32 +23,59 @@
# importing anything else.
import iris.tests as tests

import numpy as np
import mock

from iris.coords import AuxCoord
import iris


class Test___getitem__(tests.IrisTest):
def test_share_data(self):
# Ensure that slicing a coordinate behaves like slicing a numpy array
# i.e. that the points and bounds are views of the original.
original = AuxCoord([1, 2], bounds=[[1, 2], [2, 3]],
attributes={'dummy1': None},
coord_system=mock.sentinel.coord_system)
sliced_coord = original[:]
self.assertIs(sliced_coord._points.base, original._points.base)
self.assertIs(sliced_coord._bounds.base, original._bounds.base)
self.assertIsNot(sliced_coord.coord_system, original.coord_system)
self.assertIsNot(sliced_coord.attributes, original.attributes)


class Test_copy(tests.IrisTest):
def test_share_data_default(self):
original = AuxCoord(np.arange(4))
copy = original.copy()
original.points[1] = 999
self.assertArrayEqual(copy.points, [0, 1, 2, 3])

def test_share_data_false(self):
original = AuxCoord(np.arange(4))
with iris.FUTURE.context(share_data=False):
copy = original.copy()
original.points[1] = 999
self.assertArrayEqual(copy.points, [0, 1, 2, 3])

def test_share_data_true(self):
original = AuxCoord(np.arange(4))
with iris.FUTURE.context(share_data=True):
copy = original.copy()
original.points[1] = 999
self.assertArrayEqual(copy.points, [0, 999, 2, 3])
def setUp(self):
self.original = AuxCoord([1, 2], bounds=[[1, 2], [2, 3]],
attributes={'dummy1': None},
coord_system=mock.sentinel.coord_system)

def assert_data_no_share(self, coord_copy):
self.assertIsNot(coord_copy._points.base, self.original._points.base)
self.assertIsNot(coord_copy._bounds.base, self.original._bounds.base)
self.assertIsNot(coord_copy.coord_system, self.original.coord_system)
self.assertIsNot(coord_copy.attributes, self.original.attributes)

def test_existing_points(self):
# Ensure that copying a coordinate does not return a view of its
# points or bounds.
coord_copy = self.original.copy()
self.assert_data_no_share(coord_copy)

def test_existing_points_deepcopy_call(self):
# Ensure that the coordinate object itself is deepcopied called.
with mock.patch('copy.deepcopy') as mock_copy:
self.original.copy()
mock_copy.assert_called_once_with(self.original)

def test_new_points(self):
coord_copy = self.original.copy([1, 2], bounds=[[1, 2], [2, 3]])
self.assert_data_no_share(coord_copy)

def test_new_points_shallowcopy_call(self):
# Ensure that the coordinate object itself is shallow copied so that
# the points and bounds are not unnecessarily copied.
with mock.patch('copy.copy') as mock_copy:
self.original.copy([1, 2], bounds=[[1, 2], [2, 3]])
mock_copy.assert_called_once_with(self.original)


if __name__ == '__main__':
Expand Down

0 comments on commit 2810779

Please sign in to comment.