Skip to content

Commit 705250c

Browse files
committed
Support path-like objects for reading/writing
1 parent d3d83a9 commit 705250c

File tree

2 files changed

+46
-5
lines changed

2 files changed

+46
-5
lines changed

shapefile.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -930,14 +930,19 @@ def __init__(self, *args, **kwargs):
930930
self.encodingErrors = kwargs.pop('encodingErrors', 'strict')
931931
# See if a shapefile name was passed as the first argument
932932
if len(args) > 0:
933-
if is_string(args[0]):
934-
path = args[0]
935-
933+
path = args[0]
934+
try:
935+
# Convert path-like objects to str
936+
path = os.fsdecode(path)
937+
except (AttributeError, TypeError):
938+
pass
939+
if is_string(path):
940+
936941
if '.zip' in path:
937942
# Shapefile is inside a zipfile
938943
if path.count('.zip') > 1:
939944
# Multiple nested zipfiles
940-
raise ShapefileException('Reading from multiple nested zipfiles is not supported: %s' % args[0])
945+
raise ShapefileException('Reading from multiple nested zipfiles is not supported: %s' % path)
941946
# Split into zipfile and shapefile paths
942947
if path.endswith('.zip'):
943948
zpath = path
@@ -1708,8 +1713,13 @@ def __init__(self, target=None, shapeType=None, autoBalance=False, **kwargs):
17081713
self.shapeType = shapeType
17091714
self.shp = self.shx = self.dbf = None
17101715
if target:
1716+
try:
1717+
# Convert path-like objects to str
1718+
target = os.fsdecode(target)
1719+
except (AttributeError, TypeError):
1720+
pass
17111721
if not is_string(target):
1712-
raise Exception('The target filepath {} must be of type str/unicode, not {}.'.format(repr(target), type(target)) )
1722+
raise Exception('The target filepath {} must be of type str/unicode or path-like, not {}.'.format(repr(target), type(target)) )
17131723
self.shp = self.__getFileObj(os.path.splitext(target)[0] + '.shp')
17141724
self.shx = self.__getFileObj(os.path.splitext(target)[0] + '.shx')
17151725
self.dbf = self.__getFileObj(os.path.splitext(target)[0] + '.dbf')

test_shapefile.py

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

710
# third party imports
811
import pytest
@@ -12,6 +15,10 @@
1215
# our imports
1316
import shapefile
1417

18+
19+
min_py3 = pytest.mark.skipif(
20+
sys.version_info[0] < 3, reason="minimum Python 3 required")
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,16 @@ def test_reader_shapefile_extension_ignored():
403410
assert not os.path.exists(filename)
404411

405412

413+
@min_py3
414+
def test_reader_pathlike():
415+
"""
416+
Assert that path-like objects can be read.
417+
"""
418+
base = Path("shapefiles")
419+
with shapefile.Reader(base / "blockgroups") as sf:
420+
assert len(sf) == 663
421+
422+
406423
def test_reader_filelike_dbf_only():
407424
"""
408425
Assert that specifying just the
@@ -888,6 +905,20 @@ def test_write_default_shp_shx_dbf(tmpdir):
888905
assert os.path.exists(filename + ".dbf")
889906

890907

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

0 commit comments

Comments
 (0)