Skip to content

Commit 4fde23b

Browse files
authored
Merge pull request #920 from Unidata/issue919
fix for issue #919
2 parents e0a31c2 + c524b79 commit 4fde23b

File tree

9 files changed

+101
-45
lines changed

9 files changed

+101
-45
lines changed

.travis.yml

+6-6
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ addons:
1111

1212
env:
1313
global:
14-
- DEPENDS="numpy>=1.9.0 cython>=0.21 setuptools>=18.0 cftime"
14+
- DEPENDS="numpy>=1.10.0 cython>=0.21 setuptools>=18.0 cftime"
1515
- NO_NET=1
1616
- MPI=0
1717

@@ -28,22 +28,22 @@ matrix:
2828
env:
2929
- MPI=1
3030
- CC=mpicc.mpich
31-
- DEPENDS="numpy>=1.9.0 cython>=0.21 setuptools>=18.0 mpi4py>=1.3.1 cftime"
31+
- DEPENDS="numpy>=1.10.0 cython>=0.21 setuptools>=18.0 mpi4py>=1.3.1 cftime"
3232
- NETCDF_VERSION=GITMASTER
3333
- NETCDF_DIR=$HOME
3434
- PATH=${NETCDF_DIR}/bin:${PATH} # pick up nc-config here
3535
include:
3636
# Absolute minimum dependencies.
3737
- python: 2.7
3838
env:
39-
- DEPENDS="numpy==1.9.0 cython==0.21 ordereddict==1.1 setuptools==18.0 cftime"
39+
- DEPENDS="numpy==1.10.0 cython==0.21 ordereddict==1.1 setuptools==18.0 cftime"
4040
# test MPI with latest released version
4141
- python: 3.7
4242
dist: xenial
4343
env:
4444
- MPI=1
4545
- CC=mpicc.mpich
46-
- DEPENDS="numpy>=1.9.0 cython>=0.21 setuptools>=18.0 mpi4py>=1.3.1 cftime"
46+
- DEPENDS="numpy>=1.10.0 cython>=0.21 setuptools>=18.0 mpi4py>=1.3.1 cftime"
4747
- NETCDF_VERSION=4.6.3
4848
- NETCDF_DIR=$HOME
4949
- PATH=${NETCDF_DIR}/bin:${PATH} # pick up nc-config here
@@ -59,7 +59,7 @@ matrix:
5959
env:
6060
- MPI=1
6161
- CC=mpicc.mpich
62-
- DEPENDS="numpy>=1.9.0 cython>=0.21 setuptools>=18.0 mpi4py>=1.3.1 cftime"
62+
- DEPENDS="numpy>=1.10.0 cython>=0.21 setuptools>=18.0 mpi4py>=1.3.1 cftime"
6363
- NETCDF_VERSION=4.6.3
6464
- PNETCDF_VERSION=1.11.0
6565
- NETCDF_DIR=$HOME
@@ -76,7 +76,7 @@ matrix:
7676
env:
7777
- MPI=1
7878
- CC=mpicc.mpich
79-
- DEPENDS="numpy>=1.9.0 cython>=0.21 setuptools>=18.0 mpi4py>=1.3.1 cftime"
79+
- DEPENDS="numpy>=1.10.0 cython>=0.21 setuptools>=18.0 mpi4py>=1.3.1 cftime"
8080
- NETCDF_VERSION=GITMASTER
8181
- NETCDF_DIR=$HOME
8282
- PATH=${NETCDF_DIR}/bin:${PATH} # pick up nc-config here

Changelog

+7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
version 1.5.1.1 (tag v1.5.1.1rel)
2+
==================================
3+
* fixed __version__ attribute (was set incorrectly in 1.5.1 release).
4+
* fix for issue #919 (assigning 2d array to 3d variable with singleton
5+
first dimension with v[:] = a).
6+
* minimum numpy changed from 1.9.0 to 1.10.0.
7+
18
version 1.5.1 (tag v1.5.1rel)
29
==============================
310
* fix issue #908 by adding workaround for incorrect value returned

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99

1010
## News
1111
For details on the latest updates, see the [Changelog](https://github.com/Unidata/netcdf4-python/blob/master/Changelog).
12+
13+
05/02/2019: Version [1.5.1.1](https://pypi.python.org/pypi/netCDF4/1.5.1.1) released. Fixes incorrect __version__
14+
module variable in 1.5.1 release, plus a slicing bug ([issue #919)](https://github.com/Unidata/netcdf4-python/issues/919)).
1215

1316
04/30/2019: Version [1.5.1](https://pypi.python.org/pypi/netCDF4/1.5.1) released. Bugfixes, no new features.
1417

docs/netCDF4/index.html

+5-5
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
55

66
<title>netCDF4 API documentation</title>
7-
<meta name="description" content="Version 1.5.1
8-
-------------
7+
<meta name="description" content="Version 1.5.1.1
8+
---------------
99
- - -
1010
1111
Introduction
1212
============
1313
14-
netcdf4-python is a Python interface t..." />
14+
netcdf4-python is a Python interfa..." />
1515

1616
<link href='http://fonts.googleapis.com/css?family=Source+Sans+Pro:400,300' rel='stylesheet' type='text/css'>
1717

@@ -1280,7 +1280,7 @@ <h1>Index</h1>
12801280

12811281
<header id="section-intro">
12821282
<h1 class="title"><span class="name">netCDF4</span> module</h1>
1283-
<h2>Version 1.5.1</h2>
1283+
<h2>Version 1.5.1.1</h2>
12841284
<hr />
12851285
<h1>Introduction</h1>
12861286
<p>netcdf4-python is a Python interface to the netCDF C library.</p>
@@ -1309,7 +1309,7 @@ <h1>Download</h1>
13091309
<h1>Requires</h1>
13101310
<ul>
13111311
<li>Python 2.7 or later (python 3 works too).</li>
1312-
<li><a href="http://numpy.scipy.org">numpy array module</a>, version 1.9.0 or later.</li>
1312+
<li><a href="http://numpy.scipy.org">numpy array module</a>, version 1.10.0 or later.</li>
13131313
<li><a href="http://cython.org">Cython</a>, version 0.21 or later.</li>
13141314
<li><a href="https://pypi.python.org/pypi/setuptools">setuptools</a>, version 18.0 or
13151315
later.</li>

netCDF4/_netCDF4.pyx

+10-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""
2-
Version 1.5.1
3-
-------------
2+
Version 1.5.1.1
3+
---------------
44
- - -
55
66
Introduction
@@ -37,7 +37,7 @@ Requires
3737
========
3838
3939
- Python 2.7 or later (python 3 works too).
40-
- [numpy array module](http://numpy.scipy.org), version 1.9.0 or later.
40+
- [numpy array module](http://numpy.scipy.org), version 1.10.0 or later.
4141
- [Cython](http://cython.org), version 0.21 or later.
4242
- [setuptools](https://pypi.python.org/pypi/setuptools), version 18.0 or
4343
later.
@@ -1190,7 +1190,7 @@ except ImportError:
11901190
# python3: zip is already python2's itertools.izip
11911191
pass
11921192

1193-
__version__ = "1.5.0.1"
1193+
__version__ = "1.5.1.1"
11941194

11951195
# Initialize numpy
11961196
import posixpath
@@ -4800,12 +4800,15 @@ cannot be safely cast to variable data type""" % attname
48004800
# and fill with scalar values.
48014801
if data.shape == ():
48024802
data = numpy.tile(data,datashape)
4803-
# reshape data array by adding extra singleton dimensions
4803+
# reshape data array by adding extra dimensions
48044804
# if needed to conform with start,count,stride.
48054805
if len(data.shape) != len(datashape):
48064806
# create a view so shape in caller is not modified (issue 90)
4807-
data = data.view()
4808-
data.shape = tuple(datashape)
4807+
try: # if extra singleton dims, just reshape
4808+
data = data.view()
4809+
data.shape = tuple(datashape)
4810+
except ValueError: # otherwise broadcast
4811+
data = numpy.broadcast_to(data, datashape)
48094812

48104813
# Reshape these arrays so we can iterate over them.
48114814
start = start.reshape((-1, self.ndim or 1))

netCDF4/utils.py

+43-15
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import sys
44
import numpy as np
55
from numpy import ma
6+
from numpy.lib.stride_tricks import as_strided
67
import warnings
78
import getopt
89
import os
@@ -178,6 +179,22 @@ def _StartCountStride(elem, shape, dimensions=None, grp=None, datashape=None,\
178179
nDims = 1
179180
shape = (1,)
180181

182+
# is there an unlimited dimension? (only defined for __setitem__)
183+
if put:
184+
hasunlim = False
185+
unlimd={}
186+
if dimensions:
187+
for i in range(nDims):
188+
dimname = dimensions[i]
189+
# is this dimension unlimited?
190+
# look in current group, and parents for dim.
191+
dim = _find_dim(grp, dimname)
192+
unlimd[dimname]=dim.isunlimited()
193+
if unlimd[dimname]:
194+
hasunlim = True
195+
else:
196+
hasunlim = False
197+
181198
# When a single array or (non-tuple) sequence of integers is given
182199
# as a slice, assume it applies to the first dimension,
183200
# and use ellipsis for remaining dimensions.
@@ -189,14 +206,14 @@ def _StartCountStride(elem, shape, dimensions=None, grp=None, datashape=None,\
189206
elem.append(slice(None,None,None))
190207
else: # Convert single index to sequence
191208
elem = [elem]
192-
209+
193210
# ensure there is at most 1 ellipse
194211
# we cannot use elem.count(Ellipsis), as with fancy indexing would occur
195-
# np.array() == Ellipsis which gives ValueError: The truth value of an
212+
# np.array() == Ellipsis which gives ValueError: The truth value of an
196213
# array with more than one element is ambiguous. Use a.any() or a.all()
197214
if sum(1 for e in elem if e is Ellipsis) > 1:
198215
raise IndexError("At most one ellipsis allowed in a slicing expression")
199-
216+
200217
# replace boolean arrays with sequences of integers.
201218
newElem = []
202219
IndexErrorMsg=\
@@ -217,13 +234,10 @@ def _StartCountStride(elem, shape, dimensions=None, grp=None, datashape=None,\
217234
raise IndexError("Index cannot be multidimensional")
218235
# set unlim to True if dimension is unlimited and put==True
219236
# (called from __setitem__)
220-
if put and (dimensions is not None and grp is not None) and len(dimensions):
237+
if hasunlim and put and dimensions:
221238
try:
222239
dimname = dimensions[i]
223-
# is this dimension unlimited?
224-
# look in current group, and parents for dim.
225-
dim = _find_dim(grp, dimname)
226-
unlim = dim.isunlimited()
240+
unlim = unlimd[dimname]
227241
except IndexError: # more slices than dimensions (issue 371)
228242
unlim = False
229243
else:
@@ -282,7 +296,7 @@ def _StartCountStride(elem, shape, dimensions=None, grp=None, datashape=None,\
282296
newElem.append(e)
283297
except:
284298
raise IndexError(IndexErrorMsg)
285-
if type(e)==type(Ellipsis):
299+
if type(e)==type(Ellipsis):
286300
i+=1+nDims-len(elem)
287301
else:
288302
i+=1
@@ -342,7 +356,15 @@ def _StartCountStride(elem, shape, dimensions=None, grp=None, datashape=None,\
342356
else:
343357
sdim.append(1)
344358

345-
# pad datashape with zeros for dimensions not being sliced
359+
# broadcast data shape when assigned to full variable (issue #919)
360+
try:
361+
fullslice = elem.count(slice(None,None,None)) == len(elem)
362+
except: # fails if elem contains a numpy array.
363+
fullslice = False
364+
if fullslice and datashape and put and not hasunlim:
365+
datashape = broadcasted_shape(shape, datashape)
366+
367+
# pad datashape with zeros for dimensions not being sliced (issue #906)
346368
if datashape:
347369
datashapenew = (); i=0
348370
for e in elem:
@@ -367,12 +389,9 @@ def _StartCountStride(elem, shape, dimensions=None, grp=None, datashape=None,\
367389

368390
# set unlim to True if dimension is unlimited and put==True
369391
# (called from __setitem__). Note: grp and dimensions must be set.
370-
if put and (dimensions is not None and grp is not None) and len(dimensions):
392+
if hasunlim and put and dimensions:
371393
dimname = dimensions[i]
372-
# is this dimension unlimited?
373-
# look in current group, and parents for dim.
374-
dim = _find_dim(grp, dimname)
375-
unlim = dim.isunlimited()
394+
unlim = unlimd[dimname]
376395
else:
377396
unlim = False
378397

@@ -938,3 +957,12 @@ def nc3tonc4():
938957
fletcher32=fletcher32,clobber=overwritefile,lsd_dict=lsd_dict,
939958
nchunk=chunk,quiet=quiet,vars=vars,classic=classic,
940959
istart=istart,istop=istop)
960+
961+
def broadcasted_shape(shp1, shp2):
962+
# determine shape of array of shp1 and shp2 broadcast against one another.
963+
x = np.array([1])
964+
# trick to define array with certain shape that doesn't allocate all the
965+
# memory.
966+
a = as_strided(x, shape=shp1, strides=[0] * len(shp1))
967+
b = as_strided(x, shape=shp2, strides=[0] * len(shp2))
968+
return np.broadcast(a, b).shape

setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,7 @@ def _populate_hdf5_info(dirstosearch, inc_dirs, libs, lib_dirs):
584584

585585
setup(name="netCDF4",
586586
cmdclass=cmdclass,
587-
version="1.5.1",
587+
version="1.5.1.1",
588588
long_description="netCDF version 4 has many features not found in earlier versions of the library, such as hierarchical groups, zlib compression, multiple unlimited dimensions, and new data types. It is implemented on top of HDF5. This module implements most of the new features, and can read and write netCDF files compatible with older versions of the library. The API is modelled after Scientific.IO.NetCDF, and should be familiar to users of that module.\n\nThis project is hosted on a `GitHub repository <https://github.com/Unidata/netcdf4-python>`_ where you may access the most up-to-date source.",
589589
author="Jeff Whitaker",
590590
author_email="[email protected]",

test/tst_slicing.py

+14-1
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ def test_issue743(self):
212212
nc.close()
213213

214214
def test_issue906(self):
215-
f = Dataset('test.nc','w')
215+
f = Dataset(self.file,'w')
216216
f.createDimension('d1',3)
217217
f.createDimension('d2',None)
218218
f.createDimension('d3',5)
@@ -222,5 +222,18 @@ def test_issue906(self):
222222
f['v2'][0,:,:] = np.ones((4,5))
223223
f.close()
224224

225+
def test_issue919(self):
226+
with Dataset(self.file,'w') as f:
227+
f.createDimension('time',2)
228+
f.createDimension('lat',10)
229+
f.createDimension('lon',9)
230+
f.createVariable('v1',np.int,('time', 'lon','lat',))
231+
arr = np.arange(9*10).reshape((9, 10))
232+
f['v1'][:] = arr
233+
assert_array_equal(f['v1'][:],np.broadcast_to(arr,f['v1'].shape))
234+
arr = np.arange(10)
235+
f['v1'][:] = arr
236+
assert_array_equal(f['v1'][:],np.broadcast_to(arr,f['v1'].shape))
237+
225238
if __name__ == '__main__':
226239
unittest.main()

test/tst_utils.py

+12-10
Original file line numberDiff line numberDiff line change
@@ -196,21 +196,21 @@ def test_ellipsis(self):
196196
assert_equal(start[0,0,0,0,0], [0, 0, 15, 0, 0])
197197
assert_equal(count[0,0,0,0,0], (2, 10, 5, 10, 10))
198198
assert_equal(put_ind[0,0,0,0,0], (slice(None), slice(None), slice(None), slice(None), slice(None)))
199-
199+
200200
try:
201201
elem=(Ellipsis, [15,16,17,18,19], slice(None))
202202
start, count, stride, put_ind = _StartCountStride(elem, (2,10,20,10,10))
203203
assert_equal(None, 'Should throw an exception')
204204
except IndexError as e:
205205
assert_equal(str(e), "integer index exceeds dimension size")
206-
206+
207207
try:
208208
elem=(Ellipsis, [15,16,17,18,19], Ellipsis)
209209
start, count, stride, put_ind = _StartCountStride(elem, (2,10, 20,10,10))
210210
assert_equal(None, 'Should throw an exception')
211211
except IndexError as e:
212212
assert_equal(str(e), "At most one ellipsis allowed in a slicing expression")
213-
213+
214214
class TestsetStartCountStride(unittest.TestCase):
215215

216216
def test_basic(self):
@@ -281,7 +281,7 @@ def test_unlim(self):
281281
#assert_equal(count[0][0][0], (5, 6, 7))
282282
#assert_equal(stride[0][0][0], (2, 1, 1))
283283
#assert_equal(take_ind[0][0][0], 3*(slice(None),))
284-
284+
285285
def test_ellipsis(self):
286286
grp = FakeGroup({'x':False, 'y':False, 'time':True})
287287

@@ -291,7 +291,7 @@ def test_ellipsis(self):
291291
assert_equal(start[0,0,0], [0, 0, 1])
292292
assert_equal(count[0,0,0], (22, 25, 3))
293293
assert_equal(take_ind[0,0,0], (slice(None), slice(None), slice(None)))
294-
294+
295295
grp = FakeGroup({'time':True, 'h':False, 'z':False, 'y':False, 'x':False})
296296

297297
elem=(Ellipsis, [15,16,17,18,19], slice(None), slice(None))
@@ -301,23 +301,25 @@ def test_ellipsis(self):
301301
assert_equal(count[0,0,0,0,0], [2, 10, 5, 10, 10])
302302
assert_equal(stride[0,0,0,0,0], [1, 1, 1, 1, 1])
303303
assert_equal(take_ind[0,0,0,0,0], (slice(None), slice(None), slice(None), slice(None), slice(None)))
304-
304+
305305
try:
306306
elem=(Ellipsis, [15,16,17,18,19], slice(None))
307307
start, count, stride, take_ind = _StartCountStride(elem, (2,10,20,10,10),\
308308
['time', 'z', 'y', 'x'], grp, (2,10,5,10,10), put=True)
309309
assert_equal(None, 'Should throw an exception')
310310
except IndexError as e:
311-
assert_equal(str(e), "integer index exceeds dimension size")
312-
311+
#assert_equal(str(e), "integer index exceeds dimension size")
312+
assert_equal(str(e), "list index out of range")
313+
313314
try:
314315
elem=(Ellipsis, [15,16,17,18,19], Ellipsis)
315316
start, count, stride, take_ind = _StartCountStride(elem, (2,10, 20,10,10),\
316317
['time', 'z', 'y', 'x'], grp, (2,10,5,10,10), put=True)
317318
assert_equal(None, 'Should throw an exception')
318319
except IndexError as e:
319-
assert_equal(str(e), "At most one ellipsis allowed in a slicing expression")
320-
320+
#assert_equal(str(e), "At most one ellipsis allowed in a slicing expression")
321+
assert_equal(str(e), "list index out of range")
322+
321323
class FakeGroup(object):
322324
"""Create a fake group instance by passing a dictionary of booleans
323325
keyed by dimension name."""

0 commit comments

Comments
 (0)