Skip to content

Commit e797520

Browse files
authored
Merge pull request #361 from mwtoews/fix-pathlike
Fix reading PathLike objects with shp, shx and dbf args
2 parents 1564f43 + 22ec58c commit e797520

File tree

3 files changed

+68
-11
lines changed

3 files changed

+68
-11
lines changed

run_benchmarks.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import os
88
import timeit
99
from collections.abc import Callable
10+
from os import PathLike
1011
from pathlib import Path
1112
from tempfile import TemporaryFile as TempF
1213
from typing import Iterable, Union, cast
@@ -50,14 +51,14 @@ def benchmark(
5051
shapeRecords = collections.defaultdict(list)
5152

5253

53-
def open_shapefile_with_PyShp(target: Union[str, os.PathLike]):
54+
def open_shapefile_with_PyShp(target: Union[str, PathLike]):
5455
with shapefile.Reader(target) as r:
5556
fields[target] = r.fields
5657
for shapeRecord in r.iterShapeRecords():
5758
shapeRecords[target].append(shapeRecord)
5859

5960

60-
def write_shapefile_with_PyShp(target: Union[str, os.PathLike]):
61+
def write_shapefile_with_PyShp(target: Union[str, PathLike]):
6162
with TempF("wb") as shp, TempF("wb") as dbf, TempF("wb") as shx:
6263
with shapefile.Writer(shp=shp, dbf=dbf, shx=shx) as w: # type: ignore [arg-type]
6364
for field_info_tuple in fields[target]:

src/shapefile.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import time
2121
import zipfile
2222
from datetime import date
23+
from os import PathLike
2324
from struct import Struct, calcsize, error, pack, unpack
2425
from types import TracebackType
2526
from typing import (
@@ -159,7 +160,7 @@ def read(self, size: int = -1) -> bytes: ...
159160

160161

161162
# File name, file object or anything with a read() method that returns bytes.
162-
BinaryFileT = Union[str, IO[bytes]]
163+
BinaryFileT = Union[str, PathLike[Any], IO[bytes]]
163164
BinaryFileStreamT = Union[IO[bytes], io.BytesIO, WriteSeekableBinStream]
164165

165166
FieldTypeT = Literal["C", "D", "F", "L", "M", "N"]
@@ -341,11 +342,11 @@ class GeoJSONFeatureCollectionWithBBox(GeoJSONFeatureCollection):
341342

342343

343344
@overload
344-
def fsdecode_if_pathlike(path: os.PathLike[Any]) -> str: ...
345+
def fsdecode_if_pathlike(path: PathLike[Any]) -> str: ...
345346
@overload
346347
def fsdecode_if_pathlike(path: T) -> T: ...
347348
def fsdecode_if_pathlike(path: Any) -> Any:
348-
if isinstance(path, os.PathLike):
349+
if isinstance(path, PathLike):
349350
return os.fsdecode(path) # str
350351

351352
return path
@@ -2243,7 +2244,7 @@ def _assert_ext_is_supported(self, ext: str) -> None:
22432244

22442245
def __init__(
22452246
self,
2246-
shapefile_path: Union[str, os.PathLike[Any]] = "",
2247+
shapefile_path: Union[str, PathLike[Any]] = "",
22472248
/,
22482249
*,
22492250
encoding: str = "utf-8",
@@ -2411,7 +2412,7 @@ def __init__(
24112412
return
24122413

24132414
if shp is not _NO_SHP_SENTINEL:
2414-
shp = cast(Union[str, IO[bytes], None], shp)
2415+
shp = cast(Union[str, PathLike[Any], IO[bytes], None], shp)
24152416
self.shp = self.__seek_0_on_file_obj_wrap_or_open_from_name("shp", shp)
24162417
self.shx = self.__seek_0_on_file_obj_wrap_or_open_from_name("shx", shx)
24172418

@@ -2432,7 +2433,7 @@ def __seek_0_on_file_obj_wrap_or_open_from_name(
24322433
if file_ is None:
24332434
return None
24342435

2435-
if isinstance(file_, str):
2436+
if isinstance(file_, (str, PathLike)):
24362437
baseName, __ = os.path.splitext(file_)
24372438
return self._load_constituent_file(baseName, ext)
24382439

@@ -3235,7 +3236,7 @@ class Writer:
32353236

32363237
def __init__(
32373238
self,
3238-
target: Union[str, os.PathLike[Any], None] = None,
3239+
target: Union[str, PathLike[Any], None] = None,
32393240
shapeType: Optional[int] = None,
32403241
autoBalance: bool = False,
32413242
*,

test_shapefile.py

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
# our imports
1414
import shapefile
1515

16+
shapefiles_dir = Path(__file__).parent / "shapefiles"
17+
1618
# define various test shape tuples of (type, points, parts indexes, and expected geo interface output)
1719
geo_interface_tests = [
1820
(
@@ -719,8 +721,7 @@ def test_reader_pathlike():
719721
"""
720722
Assert that path-like objects can be read.
721723
"""
722-
base = Path("shapefiles")
723-
with shapefile.Reader(base / "blockgroups") as sf:
724+
with shapefile.Reader(shapefiles_dir / "blockgroups") as sf:
724725
assert len(sf) == 663
725726

726727

@@ -736,6 +737,18 @@ def test_reader_dbf_only():
736737
assert record[1:3] == ["060750601001", 4715]
737738

738739

740+
def test_reader_dbf_only_from_Path():
741+
"""
742+
Assert that specifying just the
743+
dbf argument to the shapefile reader as a Path
744+
reads just the dbf file.
745+
"""
746+
with shapefile.Reader(dbf=shapefiles_dir / "blockgroups.dbf") as sf:
747+
assert len(sf) == 663
748+
record = sf.record(3)
749+
assert record[1:3] == ["060750601001", 4715]
750+
751+
739752
def test_reader_shp_shx_only():
740753
"""
741754
Assert that specifying just the
@@ -750,6 +763,20 @@ def test_reader_shp_shx_only():
750763
assert len(shape.points) == 173
751764

752765

766+
def test_reader_shp_shx_only_from_Paths():
767+
"""
768+
Assert that specifying just the
769+
shp and shx argument to the shapefile reader as Paths
770+
reads just the shp and shx file.
771+
"""
772+
with shapefile.Reader(
773+
shp=shapefiles_dir / "blockgroups.shp", shx=shapefiles_dir / "blockgroups.shx"
774+
) as sf:
775+
assert len(sf) == 663
776+
shape = sf.shape(3)
777+
assert len(shape.points) == 173
778+
779+
753780
def test_reader_shp_dbf_only():
754781
"""
755782
Assert that specifying just the
@@ -766,6 +793,22 @@ def test_reader_shp_dbf_only():
766793
assert record[1:3] == ["060750601001", 4715]
767794

768795

796+
def test_reader_shp_dbf_only_from_Paths():
797+
"""
798+
Assert that specifying just the
799+
shp and shx argument to the shapefile reader as Paths
800+
reads just the shp and dbf file.
801+
"""
802+
with shapefile.Reader(
803+
shp=shapefiles_dir / "blockgroups.shp", dbf=shapefiles_dir / "blockgroups.dbf"
804+
) as sf:
805+
assert len(sf) == 663
806+
shape = sf.shape(3)
807+
assert len(shape.points) == 173
808+
record = sf.record(3)
809+
assert record[1:3] == ["060750601001", 4715]
810+
811+
769812
def test_reader_shp_only():
770813
"""
771814
Assert that specifying just the
@@ -778,6 +821,18 @@ def test_reader_shp_only():
778821
assert len(shape.points) == 173
779822

780823

824+
def test_reader_shp_only_from_Path():
825+
"""
826+
Assert that specifying just the
827+
shp argument to the shapefile reader as a Path
828+
reads just the shp file (shx optional).
829+
"""
830+
with shapefile.Reader(shp=shapefiles_dir / "blockgroups.shp") as sf:
831+
assert len(sf) == 663
832+
shape = sf.shape(3)
833+
assert len(shape.points) == 173
834+
835+
781836
def test_reader_filelike_dbf_only():
782837
"""
783838
Assert that specifying just the

0 commit comments

Comments
 (0)