From 30d641daa25dbe00a5d069359c18baf12193d2ef Mon Sep 17 00:00:00 2001 From: Mike Taves Date: Fri, 8 Aug 2025 14:02:54 +1200 Subject: [PATCH 1/4] Fix reading PathLike objects with shp, shx and dbf args --- run_benchmarks.py | 5 +- src/shapefile.py | 15 +++-- test_shapefile.py | 160 +++++++++++++++++++++++----------------------- 3 files changed, 91 insertions(+), 89 deletions(-) diff --git a/run_benchmarks.py b/run_benchmarks.py index 961357f..c3a583e 100644 --- a/run_benchmarks.py +++ b/run_benchmarks.py @@ -7,6 +7,7 @@ import os import timeit from collections.abc import Callable +from os import PathLike from pathlib import Path from tempfile import TemporaryFile as TempF from typing import Iterable, Union, cast @@ -50,14 +51,14 @@ def benchmark( shapeRecords = collections.defaultdict(list) -def open_shapefile_with_PyShp(target: Union[str, os.PathLike]): +def open_shapefile_with_PyShp(target: Union[str, PathLike]): with shapefile.Reader(target) as r: fields[target] = r.fields for shapeRecord in r.iterShapeRecords(): shapeRecords[target].append(shapeRecord) -def write_shapefile_with_PyShp(target: Union[str, os.PathLike]): +def write_shapefile_with_PyShp(target: Union[str, PathLike]): with TempF("wb") as shp, TempF("wb") as dbf, TempF("wb") as shx: with shapefile.Writer(shp=shp, dbf=dbf, shx=shx) as w: # type: ignore [arg-type] for field_info_tuple in fields[target]: diff --git a/src/shapefile.py b/src/shapefile.py index 5ec1077..1257b2b 100644 --- a/src/shapefile.py +++ b/src/shapefile.py @@ -20,6 +20,7 @@ import time import zipfile from datetime import date +from os import PathLike from struct import Struct, calcsize, error, pack, unpack from types import TracebackType from typing import ( @@ -159,7 +160,7 @@ def read(self, size: int = -1) -> bytes: ... # File name, file object or anything with a read() method that returns bytes. -BinaryFileT = Union[str, IO[bytes]] +BinaryFileT = Union[str, PathLike[Any], IO[bytes]] BinaryFileStreamT = Union[IO[bytes], io.BytesIO, WriteSeekableBinStream] FieldTypeT = Literal["C", "D", "F", "L", "M", "N"] @@ -341,11 +342,11 @@ class GeoJSONFeatureCollectionWithBBox(GeoJSONFeatureCollection): @overload -def fsdecode_if_pathlike(path: os.PathLike[Any]) -> str: ... +def fsdecode_if_pathlike(path: PathLike[Any]) -> str: ... @overload def fsdecode_if_pathlike(path: T) -> T: ... def fsdecode_if_pathlike(path: Any) -> Any: - if isinstance(path, os.PathLike): + if isinstance(path, PathLike): return os.fsdecode(path) # str return path @@ -2243,7 +2244,7 @@ def _assert_ext_is_supported(self, ext: str) -> None: def __init__( self, - shapefile_path: Union[str, os.PathLike[Any]] = "", + shapefile_path: Union[str, PathLike[Any]] = "", /, *, encoding: str = "utf-8", @@ -2411,7 +2412,7 @@ def __init__( return if shp is not _NO_SHP_SENTINEL: - shp = cast(Union[str, IO[bytes], None], shp) + shp = cast(Union[str, PathLike[Any], IO[bytes], None], shp) self.shp = self.__seek_0_on_file_obj_wrap_or_open_from_name("shp", shp) self.shx = self.__seek_0_on_file_obj_wrap_or_open_from_name("shx", shx) @@ -2432,7 +2433,7 @@ def __seek_0_on_file_obj_wrap_or_open_from_name( if file_ is None: return None - if isinstance(file_, str): + if isinstance(file_, (str, PathLike)): baseName, __ = os.path.splitext(file_) return self._load_constituent_file(baseName, ext) @@ -3235,7 +3236,7 @@ class Writer: def __init__( self, - target: Union[str, os.PathLike[Any], None] = None, + target: Union[str, PathLike[Any], None] = None, shapeType: Optional[int] = None, autoBalance: bool = False, *, diff --git a/test_shapefile.py b/test_shapefile.py index 2a10d3e..a20e0a2 100644 --- a/test_shapefile.py +++ b/test_shapefile.py @@ -13,6 +13,8 @@ # our imports import shapefile +shapefiles_dir = Path(__file__).parent / "shapefiles" + # define various test shape tuples of (type, points, parts indexes, and expected geo interface output) geo_interface_tests = [ ( @@ -450,7 +452,7 @@ def test_expected_shape_geo_interface(typ, points, parts, expected): def test_reader_geo_interface(): - with shapefile.Reader("shapefiles/blockgroups") as r: + with shapefile.Reader(shapefiles_dir / "blockgroups") as r: geoj = r.__geo_interface__ assert geoj["type"] == "FeatureCollection" assert "bbox" in geoj @@ -458,21 +460,21 @@ def test_reader_geo_interface(): def test_shapes_geo_interface(): - with shapefile.Reader("shapefiles/blockgroups") as r: + with shapefile.Reader(shapefiles_dir / "blockgroups") as r: geoj = r.shapes().__geo_interface__ assert geoj["type"] == "GeometryCollection" assert json.dumps(geoj) def test_shaperecords_geo_interface(): - with shapefile.Reader("shapefiles/blockgroups") as r: + with shapefile.Reader(shapefiles_dir / "blockgroups") as r: geoj = r.shapeRecords().__geo_interface__ assert geoj["type"] == "FeatureCollection" assert json.dumps(geoj) def test_shaperecord_geo_interface(): - with shapefile.Reader("shapefiles/blockgroups") as r: + with shapefile.Reader(shapefiles_dir / "blockgroups") as r: for shaperec in r: assert json.dumps(shaperec.__geo_interface__) @@ -532,7 +534,7 @@ def test_reader_zip(): Assert that Reader can open shapefiles inside a zipfile. """ # test reading zipfile only - with shapefile.Reader("shapefiles/blockgroups.zip") as sf: + with shapefile.Reader(shapefiles_dir / "blockgroups.zip") as sf: for __recShape in sf.iterShapeRecords(): pass assert len(sf) > 0 @@ -540,12 +542,12 @@ def test_reader_zip(): # test require specific path when reading multi-shapefile zipfile with pytest.raises(shapefile.ShapefileException): - with shapefile.Reader("shapefiles/blockgroups_multishapefile.zip") as sf: + with shapefile.Reader(shapefiles_dir / "blockgroups_multishapefile.zip") as sf: pass # test specifying the path when reading multi-shapefile zipfile (with extension) with shapefile.Reader( - "shapefiles/blockgroups_multishapefile.zip/blockgroups2.shp" + shapefiles_dir / "blockgroups_multishapefile.zip/blockgroups2.shp" ) as sf: for __recShape in sf.iterShapeRecords(): pass @@ -554,7 +556,7 @@ def test_reader_zip(): # test specifying the path when reading multi-shapefile zipfile (without extension) with shapefile.Reader( - "shapefiles/blockgroups_multishapefile.zip/blockgroups2" + shapefiles_dir / "blockgroups_multishapefile.zip/blockgroups2" ) as sf: for __recShape in sf.iterShapeRecords(): pass @@ -563,7 +565,7 @@ def test_reader_zip(): # test raising error when can't find shapefile inside zipfile with pytest.raises(shapefile.ShapefileException): - with shapefile.Reader("shapefiles/empty_zipfile.zip") as sf: + with shapefile.Reader(shapefiles_dir / "empty_zipfile.zip") as sf: pass @@ -575,7 +577,7 @@ def test_reader_close_path(): """ # note uses an actual shapefile from # the projects "shapefiles" directory - sf = shapefile.Reader("shapefiles/blockgroups.shp") + sf = shapefile.Reader(shapefiles_dir / "blockgroups.shp") sf.close() assert sf.shp.closed is True @@ -583,7 +585,7 @@ def test_reader_close_path(): assert sf.shx.closed is True # check that can read again - sf = shapefile.Reader("shapefiles/blockgroups.shp") + sf = shapefile.Reader(shapefiles_dir / "blockgroups.shp") sf.close() @@ -595,9 +597,9 @@ def test_reader_close_filelike(): """ # note uses an actual shapefile from # the projects "shapefiles" directory - shp = open("shapefiles/blockgroups.shp", mode="rb") - shx = open("shapefiles/blockgroups.shx", mode="rb") - dbf = open("shapefiles/blockgroups.dbf", mode="rb") + shp = open(shapefiles_dir / "blockgroups.shp", mode="rb") + shx = open(shapefiles_dir / "blockgroups.shx", mode="rb") + dbf = open(shapefiles_dir / "blockgroups.dbf", mode="rb") sf = shapefile.Reader(shp=shp, shx=shx, dbf=dbf) sf.close() @@ -618,7 +620,7 @@ def test_reader_context_path(): """ # note uses an actual shapefile from # the projects "shapefiles" directory - with shapefile.Reader("shapefiles/blockgroups") as sf: + with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: pass assert sf.shp.closed is True @@ -626,7 +628,7 @@ def test_reader_context_path(): assert sf.shx.closed is True # check that can read again - with shapefile.Reader("shapefiles/blockgroups") as sf: + with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: pass @@ -638,9 +640,9 @@ def test_reader_context_filelike(): """ # note uses an actual shapefile from # the projects "shapefiles" directory - shp = open("shapefiles/blockgroups.shp", mode="rb") - shx = open("shapefiles/blockgroups.shx", mode="rb") - dbf = open("shapefiles/blockgroups.dbf", mode="rb") + shp = open(shapefiles_dir / "blockgroups.shp", mode="rb") + shx = open(shapefiles_dir / "blockgroups.shx", mode="rb") + dbf = open(shapefiles_dir / "blockgroups.dbf", mode="rb") with shapefile.Reader(shp=shp, shx=shx, dbf=dbf) as sf: pass @@ -658,7 +660,7 @@ def test_reader_shapefile_type(): Assert that the type of the shapefile is returned correctly. """ - with shapefile.Reader("shapefiles/blockgroups") as sf: + with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: assert sf.shapeType == 5 # 5 means Polygon assert sf.shapeType == shapefile.POLYGON assert sf.shapeTypeName == "POLYGON" @@ -670,12 +672,12 @@ def test_reader_shapefile_length(): matches up with the number of records in the file. """ - with shapefile.Reader("shapefiles/blockgroups") as sf: + with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: assert len(sf) == len(sf.shapes()) def test_shape_metadata(): - with shapefile.Reader("shapefiles/blockgroups") as sf: + with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: shape = sf.shape(0) assert shape.shapeType == 5 # Polygon assert shape.shapeType == shapefile.POLYGON @@ -689,7 +691,7 @@ def test_reader_fields(): Assert that each field has a name, type, field length, and decimal length. """ - with shapefile.Reader("shapefiles/blockgroups") as sf: + with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: fields = sf.fields assert isinstance(fields, list) @@ -705,9 +707,8 @@ def test_reader_shapefile_extension_ignored(): Assert that the filename's extension is ignored when reading a shapefile. """ - base = "shapefiles/blockgroups" - ext = ".abc" - filename = base + ext + base = shapefiles_dir / "blockgroups" + filename = base.with_suffix(".abc") with shapefile.Reader(filename) as sf: assert len(sf) == 663 @@ -715,12 +716,11 @@ def test_reader_shapefile_extension_ignored(): assert not os.path.exists(filename) -def test_reader_pathlike(): +def test_reader_path_str(): """ - Assert that path-like objects can be read. + Assert that str paths can be read. """ - base = Path("shapefiles") - with shapefile.Reader(base / "blockgroups") as sf: + with shapefile.Reader(str(shapefiles_dir / "blockgroups")) as sf: assert len(sf) == 663 @@ -730,7 +730,7 @@ def test_reader_dbf_only(): dbf argument to the shapefile reader reads just the dbf file. """ - with shapefile.Reader(dbf="shapefiles/blockgroups.dbf") as sf: + with shapefile.Reader(dbf=shapefiles_dir / "blockgroups.dbf") as sf: assert len(sf) == 663 record = sf.record(3) assert record[1:3] == ["060750601001", 4715] @@ -743,7 +743,7 @@ def test_reader_shp_shx_only(): reads just the shp and shx file. """ with shapefile.Reader( - shp="shapefiles/blockgroups.shp", shx="shapefiles/blockgroups.shx" + shp=shapefiles_dir / "blockgroups.shp", shx=shapefiles_dir / "blockgroups.shx" ) as sf: assert len(sf) == 663 shape = sf.shape(3) @@ -757,7 +757,7 @@ def test_reader_shp_dbf_only(): reads just the shp and dbf file. """ with shapefile.Reader( - shp="shapefiles/blockgroups.shp", dbf="shapefiles/blockgroups.dbf" + shp=shapefiles_dir / "blockgroups.shp", dbf=shapefiles_dir / "blockgroups.dbf" ) as sf: assert len(sf) == 663 shape = sf.shape(3) @@ -772,7 +772,7 @@ def test_reader_shp_only(): shp argument to the shapefile reader reads just the shp file (shx optional). """ - with shapefile.Reader(shp="shapefiles/blockgroups.shp") as sf: + with shapefile.Reader(shp=shapefiles_dir / "blockgroups.shp") as sf: assert len(sf) == 663 shape = sf.shape(3) assert len(shape.points) == 173 @@ -784,7 +784,7 @@ def test_reader_filelike_dbf_only(): dbf argument to the shapefile reader reads just the dbf file. """ - with shapefile.Reader(dbf=open("shapefiles/blockgroups.dbf", "rb")) as sf: + with shapefile.Reader(dbf=open(shapefiles_dir / "blockgroups.dbf", "rb")) as sf: assert len(sf) == 663 record = sf.record(3) assert record[1:3] == ["060750601001", 4715] @@ -797,8 +797,8 @@ def test_reader_filelike_shp_shx_only(): reads just the shp and shx file. """ with shapefile.Reader( - shp=open("shapefiles/blockgroups.shp", "rb"), - shx=open("shapefiles/blockgroups.shx", "rb"), + shp=open(shapefiles_dir / "blockgroups.shp", "rb"), + shx=open(shapefiles_dir / "blockgroups.shx", "rb"), ) as sf: assert len(sf) == 663 shape = sf.shape(3) @@ -812,8 +812,8 @@ def test_reader_filelike_shp_dbf_only(): reads just the shp and dbf file. """ with shapefile.Reader( - shp=open("shapefiles/blockgroups.shp", "rb"), - dbf=open("shapefiles/blockgroups.dbf", "rb"), + shp=open(shapefiles_dir / "blockgroups.shp", "rb"), + dbf=open(shapefiles_dir / "blockgroups.dbf", "rb"), ) as sf: assert len(sf) == 663 shape = sf.shape(3) @@ -828,7 +828,7 @@ def test_reader_filelike_shp_only(): shp argument to the shapefile reader reads just the shp file (shx optional). """ - with shapefile.Reader(shp=open("shapefiles/blockgroups.shp", "rb")) as sf: + with shapefile.Reader(shp=open(shapefiles_dir / "blockgroups.shp", "rb")) as sf: assert len(sf) == 663 shape = sf.shape(3) assert len(shape.points) == 173 @@ -844,7 +844,7 @@ def test_reader_shapefile_delayed_load(): with pytest.raises(shapefile.ShapefileException): sf.shape(0) # assert that works after loading file manually - sf.load("shapefiles/blockgroups") + sf.load(shapefiles_dir / "blockgroups") assert len(sf) == 663 @@ -853,7 +853,7 @@ def test_records_match_shapes(): Assert that the number of records matches the number of shapes in the shapefile. """ - with shapefile.Reader("shapefiles/blockgroups") as sf: + with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: records = sf.records() shapes = sf.shapes() assert len(records) == len(shapes) @@ -867,7 +867,7 @@ def test_record_attributes(fields=None): # note # second element in fields matches first element # in record because records dont have DeletionFlag - with shapefile.Reader("shapefiles/blockgroups") as sf: + with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: for i in range(len(sf)): # full record full_record = sf.record(i) @@ -930,7 +930,7 @@ def test_record_subfields_duplicates(): fields = ["AREA", "AREA", "AREA", "MALES", "MALES", "MOBILEHOME"] test_record_attributes(fields=fields) # check that only 3 values - with shapefile.Reader("shapefiles/blockgroups") as sf: + with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: rec = sf.record(0, fields=fields) assert len(rec) == len(set(fields)) @@ -943,7 +943,7 @@ def test_record_subfields_empty(): fields = [] test_record_attributes(fields=fields) # check that only 0 values - with shapefile.Reader("shapefiles/blockgroups") as sf: + with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: rec = sf.record(0, fields=fields) assert len(rec) == 0 @@ -953,7 +953,7 @@ def test_record_as_dict(): Assert that a record object can be converted into a dictionary and data remains correct. """ - with shapefile.Reader("shapefiles/blockgroups") as sf: + with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: record = sf.record(0) as_dict = record.as_dict() @@ -967,7 +967,7 @@ def test_record_oid(): Assert that the record's oid attribute returns its index in the shapefile. """ - with shapefile.Reader("shapefiles/blockgroups") as sf: + with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: for i in range(len(sf)): record = sf.record(i) assert record.oid == i @@ -990,7 +990,7 @@ def test_iterRecords_start_stop(): by index with Reader.record """ - with shapefile.Reader("shapefiles/blockgroups") as sf: + with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: N = len(sf) # Arbitrary selection of record indices @@ -1028,7 +1028,7 @@ def test_shape_oid(): Assert that the shape's oid attribute returns its index in the shapefile. """ - with shapefile.Reader("shapefiles/blockgroups") as sf: + with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: for i in range(len(sf)): shape = sf.shape(i) assert shape.oid == i @@ -1048,9 +1048,9 @@ def test_shape_oid_no_shx(): Assert that the shape's oid attribute returns its index in the shapefile, when shx file is missing. """ - basename = "shapefiles/blockgroups" - shp = open(basename + ".shp", "rb") - dbf = open(basename + ".dbf", "rb") + basename = shapefiles_dir / "blockgroups" + shp = basename.with_suffix(".shp").open("rb") + dbf = basename.with_suffix(".dbf").open("rb") with shapefile.Reader(shp=shp, dbf=dbf) as sf: with shapefile.Reader(basename) as sf_expected: for i in range(len(sf)): @@ -1082,7 +1082,7 @@ def test_reader_offsets(): Assert that reader will not read the shx offsets unless necessary, i.e. requesting a shape index. """ - basename = "shapefiles/blockgroups" + basename = shapefiles_dir / "blockgroups" with shapefile.Reader(basename) as sf: # shx offsets should not be read during loading assert not sf._offsets @@ -1096,9 +1096,9 @@ def test_reader_offsets_no_shx(): Assert that reading a shapefile without a shx file will not build the offsets unless necessary, i.e. reading all the shapes. """ - basename = "shapefiles/blockgroups" - shp = open(basename + ".shp", "rb") - dbf = open(basename + ".dbf", "rb") + basename = shapefiles_dir / "blockgroups" + shp = basename.with_suffix(".shp").open("rb") + dbf = basename.with_suffix(".dbf").open("rb") with shapefile.Reader(shp=shp, dbf=dbf) as sf: # offsets should not be built during loading assert not sf._offsets @@ -1116,7 +1116,7 @@ def test_reader_numshapes(): Assert that reader reads the numShapes attribute from the shx file header during loading. """ - basename = "shapefiles/blockgroups" + basename = shapefiles_dir / "blockgroups" with shapefile.Reader(basename) as sf: # numShapes should be set during loading assert sf.numShapes is not None @@ -1130,9 +1130,9 @@ def test_reader_numshapes_no_shx(): an unknown value for the numShapes attribute (None), and that reading all the shapes will set the numShapes attribute. """ - basename = "shapefiles/blockgroups" - shp = open(basename + ".shp", "rb") - dbf = open(basename + ".dbf", "rb") + basename = shapefiles_dir / "blockgroups" + shp = basename.with_suffix(".shp").open("rb") + dbf = basename.with_suffix(".dbf").open("rb") with shapefile.Reader(shp=shp, dbf=dbf) as sf: # numShapes should be unknown due to missing shx file assert sf.numShapes is None @@ -1146,7 +1146,7 @@ def test_reader_len(): Assert that calling len() on reader is equal to length of all shapes and records. """ - basename = "shapefiles/blockgroups" + basename = shapefiles_dir / "blockgroups" with shapefile.Reader(basename) as sf: assert len(sf) == len(sf.records()) == len(sf.shapes()) @@ -1165,8 +1165,8 @@ def test_reader_len_dbf_only(): Assert that calling len() on reader when reading a dbf file only, is equal to length of all records. """ - basename = "shapefiles/blockgroups" - dbf = open(basename + ".dbf", "rb") + basename = shapefiles_dir / "blockgroups" + dbf = basename.with_suffix(".dbf").open("rb") with shapefile.Reader(dbf=dbf) as sf: assert len(sf) == len(sf.records()) @@ -1176,9 +1176,9 @@ def test_reader_len_no_dbf(): Assert that calling len() on reader when dbf file is missing, is equal to length of all shapes. """ - basename = "shapefiles/blockgroups" - shp = open(basename + ".shp", "rb") - shx = open(basename + ".shx", "rb") + basename = shapefiles_dir / "blockgroups" + shp = basename.with_suffix(".shp").open("rb") + shx = basename.with_suffix(".shx").open("rb") with shapefile.Reader(shp=shp, shx=shx) as sf: assert len(sf) == len(sf.shapes()) @@ -1188,18 +1188,18 @@ def test_reader_len_no_dbf_shx(): Assert that calling len() on reader when dbf and shx file is missing, is equal to length of all shapes. """ - basename = "shapefiles/blockgroups" - shp = open(basename + ".shp", "rb") + basename = shapefiles_dir / "blockgroups" + shp = basename.with_suffix(".shp").open("rb") with shapefile.Reader(shp=shp) as sf: assert len(sf) == len(sf.shapes()) -def test_reader_corrupt_files(): +def test_reader_corrupt_files(tmp_path): """ Assert that reader is able to handle corrupt files by strictly going off the header information. """ - basename = "shapefiles/test/corrupt_too_long" + basename = tmp_path / "corrupt_too_long" # write a shapefile with junk byte data at end of files with shapefile.Writer(basename) as w: @@ -1241,7 +1241,7 @@ def test_bboxfilter_shape(): outside = list(inside) outside[0] *= 10 outside[2] *= 10 - with shapefile.Reader("shapefiles/blockgroups") as sf: + with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: assert sf.shape(0, bbox=inside) is not None assert sf.shape(0, bbox=outside) is None @@ -1252,7 +1252,7 @@ def test_bboxfilter_shapes(): that fall outside, and returns those that fall inside. """ bbox = [-122.4, 37.8, -122.35, 37.82] - with shapefile.Reader("shapefiles/blockgroups") as sf: + with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: # apply bbox filter shapes = sf.shapes(bbox=bbox) # manually check bboxes @@ -1274,7 +1274,7 @@ def test_bboxfilter_shapes_outside(): no shapes when the bbox is outside the entire shapefile. """ bbox = [-180, 89, -179, 90] - with shapefile.Reader("shapefiles/blockgroups") as sf: + with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: shapes = sf.shapes(bbox=bbox) assert len(shapes) == 0 @@ -1285,7 +1285,7 @@ def test_bboxfilter_itershapes(): that fall outside, and returns those that fall inside. """ bbox = [-122.4, 37.8, -122.35, 37.82] - with shapefile.Reader("shapefiles/blockgroups") as sf: + with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: # apply bbox filter shapes = list(sf.iterShapes(bbox=bbox)) # manually check bboxes @@ -1310,7 +1310,7 @@ def test_bboxfilter_shaperecord(): outside = list(inside) outside[0] *= 10 outside[2] *= 10 - with shapefile.Reader("shapefiles/blockgroups") as sf: + with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: # inside shaperec = sf.shapeRecord(0, bbox=inside) assert shaperec is not None @@ -1325,7 +1325,7 @@ def test_bboxfilter_shaperecords(): that fall outside, and returns those that fall inside. """ bbox = [-122.4, 37.8, -122.35, 37.82] - with shapefile.Reader("shapefiles/blockgroups") as sf: + with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: # apply bbox filter shaperecs = sf.shapeRecords(bbox=bbox) # manually check bboxes @@ -1353,7 +1353,7 @@ def test_bboxfilter_itershaperecords(): that fall outside, and returns those that fall inside. """ bbox = [-122.4, 37.8, -122.35, 37.82] - with shapefile.Reader("shapefiles/blockgroups") as sf: + with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: # apply bbox filter shaperecs = list(sf.iterShapeRecords(bbox=bbox)) # manually check bboxes @@ -1382,7 +1382,7 @@ def test_shaperecords_shaperecord(): Assert that shapeRecord returns a single ShapeRecord at the given index. """ - with shapefile.Reader("shapefiles/blockgroups") as sf: + with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: shaperecs = sf.shapeRecords() shaperec = sf.shapeRecord(0) should_match = shaperecs[0] @@ -1401,7 +1401,7 @@ def test_shaperecord_shape(): Assert that a ShapeRecord object has a shape attribute that contains shape data. """ - with shapefile.Reader("shapefiles/blockgroups") as sf: + with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: shaperec = sf.shapeRecord(3) shape = shaperec.shape point = shape.points[0] @@ -1413,7 +1413,7 @@ def test_shaperecord_record(): Assert that a ShapeRecord object has a record attribute that contains record data. """ - with shapefile.Reader("shapefiles/blockgroups") as sf: + with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: shaperec = sf.shapeRecord(3) record = shaperec.record From 9737d6488a2515945bf2180ac351b7956ea23531 Mon Sep 17 00:00:00 2001 From: James Parrott <80779630+JamesParrott@users.noreply.github.com> Date: Sat, 9 Aug 2025 18:31:42 +0100 Subject: [PATCH 2/4] Restore string literal test file paths --- test_shapefile.py | 158 +++++++++++++++++++++++----------------------- 1 file changed, 80 insertions(+), 78 deletions(-) diff --git a/test_shapefile.py b/test_shapefile.py index a20e0a2..57d4bad 100644 --- a/test_shapefile.py +++ b/test_shapefile.py @@ -452,7 +452,7 @@ def test_expected_shape_geo_interface(typ, points, parts, expected): def test_reader_geo_interface(): - with shapefile.Reader(shapefiles_dir / "blockgroups") as r: + with shapefile.Reader("shapefiles/blockgroups") as r: geoj = r.__geo_interface__ assert geoj["type"] == "FeatureCollection" assert "bbox" in geoj @@ -460,21 +460,21 @@ def test_reader_geo_interface(): def test_shapes_geo_interface(): - with shapefile.Reader(shapefiles_dir / "blockgroups") as r: + with shapefile.Reader("shapefiles/blockgroups") as r: geoj = r.shapes().__geo_interface__ assert geoj["type"] == "GeometryCollection" assert json.dumps(geoj) def test_shaperecords_geo_interface(): - with shapefile.Reader(shapefiles_dir / "blockgroups") as r: + with shapefile.Reader("shapefiles/blockgroups") as r: geoj = r.shapeRecords().__geo_interface__ assert geoj["type"] == "FeatureCollection" assert json.dumps(geoj) def test_shaperecord_geo_interface(): - with shapefile.Reader(shapefiles_dir / "blockgroups") as r: + with shapefile.Reader("shapefiles/blockgroups") as r: for shaperec in r: assert json.dumps(shaperec.__geo_interface__) @@ -534,7 +534,7 @@ def test_reader_zip(): Assert that Reader can open shapefiles inside a zipfile. """ # test reading zipfile only - with shapefile.Reader(shapefiles_dir / "blockgroups.zip") as sf: + with shapefile.Reader("shapefiles/blockgroups.zip") as sf: for __recShape in sf.iterShapeRecords(): pass assert len(sf) > 0 @@ -542,12 +542,12 @@ def test_reader_zip(): # test require specific path when reading multi-shapefile zipfile with pytest.raises(shapefile.ShapefileException): - with shapefile.Reader(shapefiles_dir / "blockgroups_multishapefile.zip") as sf: + with shapefile.Reader("shapefiles/blockgroups_multishapefile.zip") as sf: pass # test specifying the path when reading multi-shapefile zipfile (with extension) with shapefile.Reader( - shapefiles_dir / "blockgroups_multishapefile.zip/blockgroups2.shp" + "shapefiles/blockgroups_multishapefile.zip/blockgroups2.shp" ) as sf: for __recShape in sf.iterShapeRecords(): pass @@ -556,7 +556,7 @@ def test_reader_zip(): # test specifying the path when reading multi-shapefile zipfile (without extension) with shapefile.Reader( - shapefiles_dir / "blockgroups_multishapefile.zip/blockgroups2" + "shapefiles/blockgroups_multishapefile.zip/blockgroups2" ) as sf: for __recShape in sf.iterShapeRecords(): pass @@ -565,7 +565,7 @@ def test_reader_zip(): # test raising error when can't find shapefile inside zipfile with pytest.raises(shapefile.ShapefileException): - with shapefile.Reader(shapefiles_dir / "empty_zipfile.zip") as sf: + with shapefile.Reader("shapefiles/empty_zipfile.zip") as sf: pass @@ -577,7 +577,7 @@ def test_reader_close_path(): """ # note uses an actual shapefile from # the projects "shapefiles" directory - sf = shapefile.Reader(shapefiles_dir / "blockgroups.shp") + sf = shapefile.Reader("shapefiles/blockgroups.shp") sf.close() assert sf.shp.closed is True @@ -585,7 +585,7 @@ def test_reader_close_path(): assert sf.shx.closed is True # check that can read again - sf = shapefile.Reader(shapefiles_dir / "blockgroups.shp") + sf = shapefile.Reader("shapefiles/blockgroups.shp") sf.close() @@ -597,9 +597,9 @@ def test_reader_close_filelike(): """ # note uses an actual shapefile from # the projects "shapefiles" directory - shp = open(shapefiles_dir / "blockgroups.shp", mode="rb") - shx = open(shapefiles_dir / "blockgroups.shx", mode="rb") - dbf = open(shapefiles_dir / "blockgroups.dbf", mode="rb") + shp = open("shapefiles/blockgroups.shp", mode="rb") + shx = open("shapefiles/blockgroups.shx", mode="rb") + dbf = open("shapefiles/blockgroups.dbf", mode="rb") sf = shapefile.Reader(shp=shp, shx=shx, dbf=dbf) sf.close() @@ -620,7 +620,7 @@ def test_reader_context_path(): """ # note uses an actual shapefile from # the projects "shapefiles" directory - with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: + with shapefile.Reader("shapefiles/blockgroups") as sf: pass assert sf.shp.closed is True @@ -628,7 +628,7 @@ def test_reader_context_path(): assert sf.shx.closed is True # check that can read again - with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: + with shapefile.Reader("shapefiles/blockgroups") as sf: pass @@ -640,9 +640,9 @@ def test_reader_context_filelike(): """ # note uses an actual shapefile from # the projects "shapefiles" directory - shp = open(shapefiles_dir / "blockgroups.shp", mode="rb") - shx = open(shapefiles_dir / "blockgroups.shx", mode="rb") - dbf = open(shapefiles_dir / "blockgroups.dbf", mode="rb") + shp = open("shapefiles/blockgroups.shp", mode="rb") + shx = open("shapefiles/blockgroups.shx", mode="rb") + dbf = open("shapefiles/blockgroups.dbf", mode="rb") with shapefile.Reader(shp=shp, shx=shx, dbf=dbf) as sf: pass @@ -660,7 +660,7 @@ def test_reader_shapefile_type(): Assert that the type of the shapefile is returned correctly. """ - with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: + with shapefile.Reader("shapefiles/blockgroups") as sf: assert sf.shapeType == 5 # 5 means Polygon assert sf.shapeType == shapefile.POLYGON assert sf.shapeTypeName == "POLYGON" @@ -672,12 +672,12 @@ def test_reader_shapefile_length(): matches up with the number of records in the file. """ - with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: + with shapefile.Reader("shapefiles/blockgroups") as sf: assert len(sf) == len(sf.shapes()) def test_shape_metadata(): - with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: + with shapefile.Reader("shapefiles/blockgroups") as sf: shape = sf.shape(0) assert shape.shapeType == 5 # Polygon assert shape.shapeType == shapefile.POLYGON @@ -691,7 +691,7 @@ def test_reader_fields(): Assert that each field has a name, type, field length, and decimal length. """ - with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: + with shapefile.Reader("shapefiles/blockgroups") as sf: fields = sf.fields assert isinstance(fields, list) @@ -707,8 +707,9 @@ def test_reader_shapefile_extension_ignored(): Assert that the filename's extension is ignored when reading a shapefile. """ - base = shapefiles_dir / "blockgroups" - filename = base.with_suffix(".abc") + base = "shapefiles/blockgroups" + ext = ".abc" + filename = base + ext with shapefile.Reader(filename) as sf: assert len(sf) == 663 @@ -716,11 +717,12 @@ def test_reader_shapefile_extension_ignored(): assert not os.path.exists(filename) -def test_reader_path_str(): +def test_reader_pathlike(): """ - Assert that str paths can be read. + Assert that path-like objects can be read. """ - with shapefile.Reader(str(shapefiles_dir / "blockgroups")) as sf: + base = Path("shapefiles") + with shapefile.Reader(base / "blockgroups") as sf: assert len(sf) == 663 @@ -730,7 +732,7 @@ def test_reader_dbf_only(): dbf argument to the shapefile reader reads just the dbf file. """ - with shapefile.Reader(dbf=shapefiles_dir / "blockgroups.dbf") as sf: + with shapefile.Reader(dbf="shapefiles/blockgroups.dbf") as sf: assert len(sf) == 663 record = sf.record(3) assert record[1:3] == ["060750601001", 4715] @@ -743,7 +745,7 @@ def test_reader_shp_shx_only(): reads just the shp and shx file. """ with shapefile.Reader( - shp=shapefiles_dir / "blockgroups.shp", shx=shapefiles_dir / "blockgroups.shx" + shp="shapefiles/blockgroups.shp", shx="shapefiles/blockgroups.shx" ) as sf: assert len(sf) == 663 shape = sf.shape(3) @@ -757,7 +759,7 @@ def test_reader_shp_dbf_only(): reads just the shp and dbf file. """ with shapefile.Reader( - shp=shapefiles_dir / "blockgroups.shp", dbf=shapefiles_dir / "blockgroups.dbf" + shp="shapefiles/blockgroups.shp", dbf="shapefiles/blockgroups.dbf" ) as sf: assert len(sf) == 663 shape = sf.shape(3) @@ -772,7 +774,7 @@ def test_reader_shp_only(): shp argument to the shapefile reader reads just the shp file (shx optional). """ - with shapefile.Reader(shp=shapefiles_dir / "blockgroups.shp") as sf: + with shapefile.Reader(shp="shapefiles/blockgroups.shp") as sf: assert len(sf) == 663 shape = sf.shape(3) assert len(shape.points) == 173 @@ -784,7 +786,7 @@ def test_reader_filelike_dbf_only(): dbf argument to the shapefile reader reads just the dbf file. """ - with shapefile.Reader(dbf=open(shapefiles_dir / "blockgroups.dbf", "rb")) as sf: + with shapefile.Reader(dbf=open("shapefiles/blockgroups.dbf", "rb")) as sf: assert len(sf) == 663 record = sf.record(3) assert record[1:3] == ["060750601001", 4715] @@ -797,8 +799,8 @@ def test_reader_filelike_shp_shx_only(): reads just the shp and shx file. """ with shapefile.Reader( - shp=open(shapefiles_dir / "blockgroups.shp", "rb"), - shx=open(shapefiles_dir / "blockgroups.shx", "rb"), + shp=open("shapefiles/blockgroups.shp", "rb"), + shx=open("shapefiles/blockgroups.shx", "rb"), ) as sf: assert len(sf) == 663 shape = sf.shape(3) @@ -812,8 +814,8 @@ def test_reader_filelike_shp_dbf_only(): reads just the shp and dbf file. """ with shapefile.Reader( - shp=open(shapefiles_dir / "blockgroups.shp", "rb"), - dbf=open(shapefiles_dir / "blockgroups.dbf", "rb"), + shp=open("shapefiles/blockgroups.shp", "rb"), + dbf=open("shapefiles/blockgroups.dbf", "rb"), ) as sf: assert len(sf) == 663 shape = sf.shape(3) @@ -828,7 +830,7 @@ def test_reader_filelike_shp_only(): shp argument to the shapefile reader reads just the shp file (shx optional). """ - with shapefile.Reader(shp=open(shapefiles_dir / "blockgroups.shp", "rb")) as sf: + with shapefile.Reader(shp=open("shapefiles/blockgroups.shp", "rb")) as sf: assert len(sf) == 663 shape = sf.shape(3) assert len(shape.points) == 173 @@ -844,7 +846,7 @@ def test_reader_shapefile_delayed_load(): with pytest.raises(shapefile.ShapefileException): sf.shape(0) # assert that works after loading file manually - sf.load(shapefiles_dir / "blockgroups") + sf.load("shapefiles/blockgroups") assert len(sf) == 663 @@ -853,7 +855,7 @@ def test_records_match_shapes(): Assert that the number of records matches the number of shapes in the shapefile. """ - with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: + with shapefile.Reader("shapefiles/blockgroups") as sf: records = sf.records() shapes = sf.shapes() assert len(records) == len(shapes) @@ -867,7 +869,7 @@ def test_record_attributes(fields=None): # note # second element in fields matches first element # in record because records dont have DeletionFlag - with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: + with shapefile.Reader("shapefiles/blockgroups") as sf: for i in range(len(sf)): # full record full_record = sf.record(i) @@ -930,7 +932,7 @@ def test_record_subfields_duplicates(): fields = ["AREA", "AREA", "AREA", "MALES", "MALES", "MOBILEHOME"] test_record_attributes(fields=fields) # check that only 3 values - with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: + with shapefile.Reader("shapefiles/blockgroups") as sf: rec = sf.record(0, fields=fields) assert len(rec) == len(set(fields)) @@ -943,7 +945,7 @@ def test_record_subfields_empty(): fields = [] test_record_attributes(fields=fields) # check that only 0 values - with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: + with shapefile.Reader("shapefiles/blockgroups") as sf: rec = sf.record(0, fields=fields) assert len(rec) == 0 @@ -953,7 +955,7 @@ def test_record_as_dict(): Assert that a record object can be converted into a dictionary and data remains correct. """ - with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: + with shapefile.Reader("shapefiles/blockgroups") as sf: record = sf.record(0) as_dict = record.as_dict() @@ -967,7 +969,7 @@ def test_record_oid(): Assert that the record's oid attribute returns its index in the shapefile. """ - with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: + with shapefile.Reader("shapefiles/blockgroups") as sf: for i in range(len(sf)): record = sf.record(i) assert record.oid == i @@ -990,7 +992,7 @@ def test_iterRecords_start_stop(): by index with Reader.record """ - with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: + with shapefile.Reader("shapefiles/blockgroups") as sf: N = len(sf) # Arbitrary selection of record indices @@ -1028,7 +1030,7 @@ def test_shape_oid(): Assert that the shape's oid attribute returns its index in the shapefile. """ - with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: + with shapefile.Reader("shapefiles/blockgroups") as sf: for i in range(len(sf)): shape = sf.shape(i) assert shape.oid == i @@ -1048,9 +1050,9 @@ def test_shape_oid_no_shx(): Assert that the shape's oid attribute returns its index in the shapefile, when shx file is missing. """ - basename = shapefiles_dir / "blockgroups" - shp = basename.with_suffix(".shp").open("rb") - dbf = basename.with_suffix(".dbf").open("rb") + basename = "shapefiles/blockgroups" + shp = open(basename + ".shp", "rb") + dbf = open(basename + ".dbf", "rb") with shapefile.Reader(shp=shp, dbf=dbf) as sf: with shapefile.Reader(basename) as sf_expected: for i in range(len(sf)): @@ -1082,7 +1084,7 @@ def test_reader_offsets(): Assert that reader will not read the shx offsets unless necessary, i.e. requesting a shape index. """ - basename = shapefiles_dir / "blockgroups" + basename = "shapefiles/blockgroups" with shapefile.Reader(basename) as sf: # shx offsets should not be read during loading assert not sf._offsets @@ -1096,9 +1098,9 @@ def test_reader_offsets_no_shx(): Assert that reading a shapefile without a shx file will not build the offsets unless necessary, i.e. reading all the shapes. """ - basename = shapefiles_dir / "blockgroups" - shp = basename.with_suffix(".shp").open("rb") - dbf = basename.with_suffix(".dbf").open("rb") + basename = "shapefiles/blockgroups" + shp = open(basename + ".shp", "rb") + dbf = open(basename + ".dbf", "rb") with shapefile.Reader(shp=shp, dbf=dbf) as sf: # offsets should not be built during loading assert not sf._offsets @@ -1116,7 +1118,7 @@ def test_reader_numshapes(): Assert that reader reads the numShapes attribute from the shx file header during loading. """ - basename = shapefiles_dir / "blockgroups" + basename = "shapefiles/blockgroups" with shapefile.Reader(basename) as sf: # numShapes should be set during loading assert sf.numShapes is not None @@ -1130,9 +1132,9 @@ def test_reader_numshapes_no_shx(): an unknown value for the numShapes attribute (None), and that reading all the shapes will set the numShapes attribute. """ - basename = shapefiles_dir / "blockgroups" - shp = basename.with_suffix(".shp").open("rb") - dbf = basename.with_suffix(".dbf").open("rb") + basename = "shapefiles/blockgroups" + shp = open(basename + ".shp", "rb") + dbf = open(basename + ".dbf", "rb") with shapefile.Reader(shp=shp, dbf=dbf) as sf: # numShapes should be unknown due to missing shx file assert sf.numShapes is None @@ -1146,7 +1148,7 @@ def test_reader_len(): Assert that calling len() on reader is equal to length of all shapes and records. """ - basename = shapefiles_dir / "blockgroups" + basename = "shapefiles/blockgroups" with shapefile.Reader(basename) as sf: assert len(sf) == len(sf.records()) == len(sf.shapes()) @@ -1165,8 +1167,8 @@ def test_reader_len_dbf_only(): Assert that calling len() on reader when reading a dbf file only, is equal to length of all records. """ - basename = shapefiles_dir / "blockgroups" - dbf = basename.with_suffix(".dbf").open("rb") + basename = "shapefiles/blockgroups" + dbf = open(basename + ".dbf", "rb") with shapefile.Reader(dbf=dbf) as sf: assert len(sf) == len(sf.records()) @@ -1176,9 +1178,9 @@ def test_reader_len_no_dbf(): Assert that calling len() on reader when dbf file is missing, is equal to length of all shapes. """ - basename = shapefiles_dir / "blockgroups" - shp = basename.with_suffix(".shp").open("rb") - shx = basename.with_suffix(".shx").open("rb") + basename = "shapefiles/blockgroups" + shp = open(basename + ".shp", "rb") + shx = open(basename + ".shx", "rb") with shapefile.Reader(shp=shp, shx=shx) as sf: assert len(sf) == len(sf.shapes()) @@ -1188,18 +1190,18 @@ def test_reader_len_no_dbf_shx(): Assert that calling len() on reader when dbf and shx file is missing, is equal to length of all shapes. """ - basename = shapefiles_dir / "blockgroups" - shp = basename.with_suffix(".shp").open("rb") + basename = "shapefiles/blockgroups" + shp = open(basename + ".shp", "rb") with shapefile.Reader(shp=shp) as sf: assert len(sf) == len(sf.shapes()) -def test_reader_corrupt_files(tmp_path): +def test_reader_corrupt_files(): """ Assert that reader is able to handle corrupt files by strictly going off the header information. """ - basename = tmp_path / "corrupt_too_long" + basename = "shapefiles/test/corrupt_too_long" # write a shapefile with junk byte data at end of files with shapefile.Writer(basename) as w: @@ -1241,7 +1243,7 @@ def test_bboxfilter_shape(): outside = list(inside) outside[0] *= 10 outside[2] *= 10 - with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: + with shapefile.Reader("shapefiles/blockgroups") as sf: assert sf.shape(0, bbox=inside) is not None assert sf.shape(0, bbox=outside) is None @@ -1252,7 +1254,7 @@ def test_bboxfilter_shapes(): that fall outside, and returns those that fall inside. """ bbox = [-122.4, 37.8, -122.35, 37.82] - with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: + with shapefile.Reader("shapefiles/blockgroups") as sf: # apply bbox filter shapes = sf.shapes(bbox=bbox) # manually check bboxes @@ -1274,7 +1276,7 @@ def test_bboxfilter_shapes_outside(): no shapes when the bbox is outside the entire shapefile. """ bbox = [-180, 89, -179, 90] - with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: + with shapefile.Reader("shapefiles/blockgroups") as sf: shapes = sf.shapes(bbox=bbox) assert len(shapes) == 0 @@ -1285,7 +1287,7 @@ def test_bboxfilter_itershapes(): that fall outside, and returns those that fall inside. """ bbox = [-122.4, 37.8, -122.35, 37.82] - with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: + with shapefile.Reader("shapefiles/blockgroups") as sf: # apply bbox filter shapes = list(sf.iterShapes(bbox=bbox)) # manually check bboxes @@ -1310,7 +1312,7 @@ def test_bboxfilter_shaperecord(): outside = list(inside) outside[0] *= 10 outside[2] *= 10 - with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: + with shapefile.Reader("shapefiles/blockgroups") as sf: # inside shaperec = sf.shapeRecord(0, bbox=inside) assert shaperec is not None @@ -1325,7 +1327,7 @@ def test_bboxfilter_shaperecords(): that fall outside, and returns those that fall inside. """ bbox = [-122.4, 37.8, -122.35, 37.82] - with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: + with shapefile.Reader("shapefiles/blockgroups") as sf: # apply bbox filter shaperecs = sf.shapeRecords(bbox=bbox) # manually check bboxes @@ -1353,7 +1355,7 @@ def test_bboxfilter_itershaperecords(): that fall outside, and returns those that fall inside. """ bbox = [-122.4, 37.8, -122.35, 37.82] - with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: + with shapefile.Reader("shapefiles/blockgroups") as sf: # apply bbox filter shaperecs = list(sf.iterShapeRecords(bbox=bbox)) # manually check bboxes @@ -1382,7 +1384,7 @@ def test_shaperecords_shaperecord(): Assert that shapeRecord returns a single ShapeRecord at the given index. """ - with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: + with shapefile.Reader("shapefiles/blockgroups") as sf: shaperecs = sf.shapeRecords() shaperec = sf.shapeRecord(0) should_match = shaperecs[0] @@ -1401,7 +1403,7 @@ def test_shaperecord_shape(): Assert that a ShapeRecord object has a shape attribute that contains shape data. """ - with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: + with shapefile.Reader("shapefiles/blockgroups") as sf: shaperec = sf.shapeRecord(3) shape = shaperec.shape point = shape.points[0] @@ -1413,7 +1415,7 @@ def test_shaperecord_record(): Assert that a ShapeRecord object has a record attribute that contains record data. """ - with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: + with shapefile.Reader("shapefiles/blockgroups") as sf: shaperec = sf.shapeRecord(3) record = shaperec.record From 79ded7e170a4a6f5d13c6bfd28282752ca40bf53 Mon Sep 17 00:00:00 2001 From: James Parrott <80779630+JamesParrott@users.noreply.github.com> Date: Sat, 9 Aug 2025 20:48:01 +0100 Subject: [PATCH 3/4] Add tests from the original PR that give Reader a Path as a kwarg (shp, dbf or shx) --- test_shapefile.py | 57 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/test_shapefile.py b/test_shapefile.py index 57d4bad..7970aae 100644 --- a/test_shapefile.py +++ b/test_shapefile.py @@ -721,8 +721,7 @@ def test_reader_pathlike(): """ Assert that path-like objects can be read. """ - base = Path("shapefiles") - with shapefile.Reader(base / "blockgroups") as sf: + with shapefile.Reader(shapefiles_dir / "blockgroups") as sf: assert len(sf) == 663 @@ -738,6 +737,18 @@ def test_reader_dbf_only(): assert record[1:3] == ["060750601001", 4715] +def test_reader_dbf_only_from_Path(): + """ + Assert that specifying just the + dbf argument to the shapefile reader as a Path + reads just the dbf file. + """ + with shapefile.Reader(dbf=shapefiles_dir / "blockgroups.dbf") as sf: + assert len(sf) == 663 + record = sf.record(3) + assert record[1:3] == ["060750601001", 4715] + + def test_reader_shp_shx_only(): """ Assert that specifying just the @@ -752,6 +763,20 @@ def test_reader_shp_shx_only(): assert len(shape.points) == 173 +def test_reader_shp_shx_only_from_Paths(): + """ + Assert that specifying just the + shp and shx argument to the shapefile reader as Paths + reads just the shp and shx file. + """ + with shapefile.Reader( + shp=shapefiles_dir / "blockgroups.shp", shx=shapefiles_dir / "blockgroups.shx" + ) as sf: + assert len(sf) == 663 + shape = sf.shape(3) + assert len(shape.points) == 173 + + def test_reader_shp_dbf_only(): """ Assert that specifying just the @@ -768,6 +793,21 @@ def test_reader_shp_dbf_only(): assert record[1:3] == ["060750601001", 4715] +def test_reader_shp_dbf_only_from_Paths(): + """ + Assert that specifying just the + shp and shx argument to the shapefile reader as Paths + reads just the shp and dbf file. + """ + with shapefile.Reader( + shp=shapefiles_dir / "blockgroups.shp", dbf=shapefiles_dir / "blockgroups.dbf" + ) as sf: + assert len(sf) == 663 + shape = sf.shape(3) + assert len(shape.points) == 173 + record = sf.record(3) + assert record[1:3] == ["060750601001", 4715] + def test_reader_shp_only(): """ Assert that specifying just the @@ -780,6 +820,18 @@ def test_reader_shp_only(): assert len(shape.points) == 173 +def test_reader_shp_only_from_Path(): + """ + Assert that specifying just the + shp argument to the shapefile reader as a Path + reads just the shp file (shx optional). + """ + with shapefile.Reader(shp=shapefiles_dir / "blockgroups.shp") as sf: + assert len(sf) == 663 + shape = sf.shape(3) + assert len(shape.points) == 173 + + def test_reader_filelike_dbf_only(): """ Assert that specifying just the @@ -1079,6 +1131,7 @@ def test_shape_oid_no_shx(): ) + def test_reader_offsets(): """ Assert that reader will not read the shx offsets unless necessary, From 22ec58cecd69cca26a37acf2f0066a2c659db2a0 Mon Sep 17 00:00:00 2001 From: James Parrott <80779630+JamesParrott@users.noreply.github.com> Date: Sat, 9 Aug 2025 20:49:32 +0100 Subject: [PATCH 4/4] Reformat --- test_shapefile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_shapefile.py b/test_shapefile.py index 7970aae..152be86 100644 --- a/test_shapefile.py +++ b/test_shapefile.py @@ -808,6 +808,7 @@ def test_reader_shp_dbf_only_from_Paths(): record = sf.record(3) assert record[1:3] == ["060750601001", 4715] + def test_reader_shp_only(): """ Assert that specifying just the @@ -1131,7 +1132,6 @@ def test_shape_oid_no_shx(): ) - def test_reader_offsets(): """ Assert that reader will not read the shx offsets unless necessary,