Skip to content

Commit 186933b

Browse files
authored
Merge pull request #233 from mwtoews/path-like
Support path-like objects for reading/writing
2 parents 45fe4a7 + d9f917a commit 186933b

File tree

2 files changed

+54
-5
lines changed

2 files changed

+54
-5
lines changed

shapefile.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,24 @@ def u(v, encoding='utf-8', encodingErrors='strict'):
161161
def is_string(v):
162162
return isinstance(v, basestring)
163163

164+
if sys.version_info[0:2] >= (3, 6):
165+
def pathlike_obj(path):
166+
if isinstance(path, os.PathLike):
167+
return os.fsdecode(path)
168+
else:
169+
return path
170+
else:
171+
def pathlike_obj(path):
172+
if is_string(path):
173+
return path
174+
elif hasattr(path, "__fspath__"):
175+
return path.__fspath__()
176+
else:
177+
try:
178+
return str(path)
179+
except:
180+
return path
181+
164182

165183
# Begin
166184

@@ -930,14 +948,14 @@ def __init__(self, *args, **kwargs):
930948
self.encodingErrors = kwargs.pop('encodingErrors', 'strict')
931949
# See if a shapefile name was passed as the first argument
932950
if len(args) > 0:
933-
if is_string(args[0]):
934-
path = args[0]
935-
951+
path = pathlike_obj(args[0])
952+
if is_string(path):
953+
936954
if '.zip' in path:
937955
# Shapefile is inside a zipfile
938956
if path.count('.zip') > 1:
939957
# Multiple nested zipfiles
940-
raise ShapefileException('Reading from multiple nested zipfiles is not supported: %s' % args[0])
958+
raise ShapefileException('Reading from multiple nested zipfiles is not supported: %s' % path)
941959
# Split into zipfile and shapefile paths
942960
if path.endswith('.zip'):
943961
zpath = path
@@ -1708,8 +1726,9 @@ def __init__(self, target=None, shapeType=None, autoBalance=False, **kwargs):
17081726
self.shapeType = shapeType
17091727
self.shp = self.shx = self.dbf = None
17101728
if target:
1729+
target = pathlike_obj(target)
17111730
if not is_string(target):
1712-
raise Exception('The target filepath {} must be of type str/unicode, not {}.'.format(repr(target), type(target)) )
1731+
raise Exception('The target filepath {} must be of type str/unicode or path-like, not {}.'.format(repr(target), type(target)) )
17131732
self.shp = self.__getFileObj(os.path.splitext(target)[0] + '.shp')
17141733
self.shx = self.__getFileObj(os.path.splitext(target)[0] + '.shx')
17151734
self.dbf = self.__getFileObj(os.path.splitext(target)[0] + '.dbf')

test_shapefile.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,22 @@
33
"""
44
# std lib imports
55
import os.path
6+
import sys
7+
if sys.version_info.major == 3:
8+
from pathlib import Path
69

710
# third party imports
811
import pytest
912
import json
1013
import datetime
14+
if sys.version_info.major == 2:
15+
# required by pytest for python <36
16+
from pathlib2 import Path
1117

1218
# our imports
1319
import shapefile
1420

21+
1522
# define various test shape tuples of (type, points, parts indexes, and expected geo interface output)
1623
geo_interface_tests = [ (shapefile.POINT, # point
1724
[(1,1)],
@@ -403,6 +410,15 @@ def test_reader_shapefile_extension_ignored():
403410
assert not os.path.exists(filename)
404411

405412

413+
def test_reader_pathlike():
414+
"""
415+
Assert that path-like objects can be read.
416+
"""
417+
base = Path("shapefiles")
418+
with shapefile.Reader(base / "blockgroups") as sf:
419+
assert len(sf) == 663
420+
421+
406422
def test_reader_filelike_dbf_only():
407423
"""
408424
Assert that specifying just the
@@ -888,6 +904,20 @@ def test_write_default_shp_shx_dbf(tmpdir):
888904
assert os.path.exists(filename + ".dbf")
889905

890906

907+
def test_write_pathlike(tmpdir):
908+
"""
909+
Assert that path-like objects can be written.
910+
Similar to test_write_default_shp_shx_dbf.
911+
"""
912+
filename = tmpdir.join("test")
913+
assert not isinstance(filename, str)
914+
with shapefile.Writer(filename) as writer:
915+
writer.field('field1', 'C')
916+
assert (filename + ".shp").ensure()
917+
assert (filename + ".shx").ensure()
918+
assert (filename + ".dbf").ensure()
919+
920+
891921
def test_write_shapefile_extension_ignored(tmpdir):
892922
"""
893923
Assert that the filename's extension is

0 commit comments

Comments
 (0)