Skip to content

Commit 73a10be

Browse files
committed
Only close files that the Reader or Writer opened, not ones given by user, more testing on this
1 parent 7647436 commit 73a10be

19 files changed

+241
-37
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,18 @@ write to them:
722722
>>> w.record()
723723
>>> w.null()
724724
>>> w.close()
725+
725726
>>> # To read back the files you could call the "StringIO.getvalue()" method later.
727+
>>> assert shp.getvalue()
728+
>>> assert shx.getvalue()
729+
>>> assert dbf.getvalue()
730+
731+
>>> # In fact, you can read directly from them using the Reader
732+
>>> r = shapefile.Reader(shp=shp, shx=shx, dbf=dbf)
733+
>>> len(r)
734+
1
735+
736+
726737

727738
#### Writing Shapefiles Using the Context Manager
728739

shapefile.py

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -932,6 +932,7 @@ def __init__(self, *args, **kwargs):
932932
self.shp = None
933933
self.shx = None
934934
self.dbf = None
935+
self._files_to_close = []
935936
self.shapeName = "Not specified"
936937
self._offsets = []
937938
self.shpLength = None
@@ -998,6 +999,7 @@ def __init__(self, *args, **kwargs):
998999
fileobj.write(member.read())
9991000
fileobj.seek(0)
10001001
setattr(self, ext, fileobj)
1002+
self._files_to_close.append(fileobj)
10011003
except:
10021004
pass
10031005
# Close and delete the temporary zipfile
@@ -1030,6 +1032,7 @@ def __init__(self, *args, **kwargs):
10301032
fileobj.write(resp.read())
10311033
fileobj.seek(0)
10321034
setattr(self, ext, fileobj)
1035+
self._files_to_close.append(fileobj)
10331036
except HTTPError:
10341037
pass
10351038
if (self.shp or self.dbf):
@@ -1202,9 +1205,11 @@ def load_shp(self, shapefile_name):
12021205
shp_ext = 'shp'
12031206
try:
12041207
self.shp = open("%s.%s" % (shapefile_name, shp_ext), "rb")
1208+
self._files_to_close.append(self.shp)
12051209
except IOError:
12061210
try:
12071211
self.shp = open("%s.%s" % (shapefile_name, shp_ext.upper()), "rb")
1212+
self._files_to_close.append(self.shp)
12081213
except IOError:
12091214
pass
12101215

@@ -1215,9 +1220,11 @@ def load_shx(self, shapefile_name):
12151220
shx_ext = 'shx'
12161221
try:
12171222
self.shx = open("%s.%s" % (shapefile_name, shx_ext), "rb")
1223+
self._files_to_close.append(self.shx)
12181224
except IOError:
12191225
try:
12201226
self.shx = open("%s.%s" % (shapefile_name, shx_ext.upper()), "rb")
1227+
self._files_to_close.append(self.shx)
12211228
except IOError:
12221229
pass
12231230

@@ -1228,28 +1235,26 @@ def load_dbf(self, shapefile_name):
12281235
dbf_ext = 'dbf'
12291236
try:
12301237
self.dbf = open("%s.%s" % (shapefile_name, dbf_ext), "rb")
1238+
self._files_to_close.append(self.dbf)
12311239
except IOError:
12321240
try:
12331241
self.dbf = open("%s.%s" % (shapefile_name, dbf_ext.upper()), "rb")
1242+
self._files_to_close.append(self.dbf)
12341243
except IOError:
12351244
pass
12361245

12371246
def __del__(self):
12381247
self.close()
12391248

12401249
def close(self):
1241-
for attribute in ('shp','shx','dbf'):
1242-
try:
1243-
obj = getattr(self, attribute)
1244-
except AttributeError:
1245-
# deepcopies fail to copy these attributes and raises exception during
1246-
# garbage collection - https://github.com/mattijn/topojson/issues/120
1247-
obj = None
1248-
if obj and hasattr(obj, 'close'):
1250+
# Close any files that the reader opened (but not those given by user)
1251+
for attribute in self._files_to_close:
1252+
if hasattr(attribute, 'close'):
12491253
try:
1250-
obj.close()
1254+
attribute.close()
12511255
except IOError:
12521256
pass
1257+
self._files_to_close = []
12531258

12541259
def __getFileObj(self, f):
12551260
"""Checks to see if the requested shapefile file object is
@@ -1786,6 +1791,7 @@ def __init__(self, target=None, shapeType=None, autoBalance=False, **kwargs):
17861791
self.fields = []
17871792
self.shapeType = shapeType
17881793
self.shp = self.shx = self.dbf = None
1794+
self._files_to_close = []
17891795
if target:
17901796
target = pathlike_obj(target)
17911797
if not is_string(target):
@@ -1866,13 +1872,22 @@ def close(self):
18661872
if self.dbf and dbf_open:
18671873
self.__dbfHeader()
18681874

1869-
# Close files
1875+
# Flush files
18701876
for attribute in (self.shp, self.shx, self.dbf):
1877+
if hasattr(attribute, 'flush') and not (hasattr(attribute, 'closed') and attribute.closed):
1878+
try:
1879+
attribute.flush()
1880+
except IOError:
1881+
pass
1882+
1883+
# Close any files that the writer opened (but not those given by user)
1884+
for attribute in self._files_to_close:
18711885
if hasattr(attribute, 'close'):
18721886
try:
18731887
attribute.close()
18741888
except IOError:
18751889
pass
1890+
self._files_to_close = []
18761891

18771892
def __getFileObj(self, f):
18781893
"""Safety handler to verify file-like objects"""
@@ -1884,7 +1899,9 @@ def __getFileObj(self, f):
18841899
pth = os.path.split(f)[0]
18851900
if pth and not os.path.exists(pth):
18861901
os.makedirs(pth)
1887-
return open(f, "wb+")
1902+
fp = open(f, "wb+")
1903+
self._files_to_close.append(fp)
1904+
return fp
18881905

18891906
def __shpFileLength(self):
18901907
"""Calculates the file length of the shp file."""

shapefiles/test/balancing.dbf

0 Bytes
Binary file not shown.

shapefiles/test/contextwriter.dbf

0 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.

shapefiles/test/dtype.dbf

0 Bytes
Binary file not shown.

shapefiles/test/edit.dbf

0 Bytes
Binary file not shown.

shapefiles/test/line.dbf

0 Bytes
Binary file not shown.

shapefiles/test/linem.dbf

0 Bytes
Binary file not shown.

shapefiles/test/linez.dbf

0 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)