Skip to content

Commit f9543e7

Browse files
authored
Merge pull request #396 from JamesParrott/base_dbfReader.Records_on_iterRecords
Base dbfReader.records on iterRecords, and add deleted_as_None option
2 parents efeedc2 + be1e277 commit f9543e7

2 files changed

Lines changed: 49 additions & 19 deletions

File tree

src/shapefile.py

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2650,6 +2650,8 @@ def _record(
26502650
"""Reads and returns a dbf record row as a list of values. Requires specifying
26512651
a list of field info Field namedtuples 'fieldTuples', a record name-index dict 'recLookup',
26522652
and a Struct instance 'recStruct' for unpacking these fields.
2653+
2654+
None is returned for 'deleted' records (those with their deletion flag marked).
26532655
"""
26542656
f = self.file
26552657

@@ -2744,6 +2746,9 @@ def record(self, i: int = 0, fields: list[str] | None = None) -> _Record | None:
27442746
"""Returns a specific dbf record based on the supplied index.
27452747
To only read some of the fields, specify the 'fields' arg as a
27462748
list of one or more fieldnames.
2749+
2750+
2751+
Returns None if record's deletion flag is marked.
27472752
"""
27482753
f = self.file
27492754

@@ -2756,30 +2761,37 @@ def record(self, i: int = 0, fields: list[str] | None = None) -> _Record | None:
27562761
oid=i, fieldTuples=fieldTuples, recLookup=recLookup, recStruct=recStruct
27572762
)
27582763

2759-
def records(self, fields: list[str] | None = None) -> list[_Record]:
2760-
"""Returns all records in a dbf file.
2764+
def records(
2765+
self,
2766+
fields: list[str] | None = None,
2767+
start: int = 0,
2768+
stop: int | None = None,
2769+
deleted_as_None: bool = False,
2770+
) -> list[_Record | None]:
2771+
"""Returns a list of records in a dbf file.
27612772
To only read some of the fields, specify the 'fields' arg as a
27622773
list of one or more fieldnames.
2763-
"""
2764-
f = self.file
2765-
2766-
records = []
2767-
f.seek(self.__dbfHdrLength)
2768-
fieldTuples, recLookup, recStruct = self._record_fields(fields)
2774+
By default returns all records. Otherwise, specify start
2775+
(default: 0) or stop (default: number_of_records)
2776+
to only yield record numbers i, where
2777+
start <= i < stop, (or
2778+
start <= i < number_of_records + stop
2779+
if stop < 0).
27692780
2770-
for i in range(self.numRecords):
2771-
r = self._record(
2772-
oid=i, fieldTuples=fieldTuples, recLookup=recLookup, recStruct=recStruct
2773-
)
2774-
if r:
2775-
records.append(r)
2776-
return records
2781+
Excludes 'deleted' records (those whose deletion flag is marked).
2782+
Set deleted_as_None=True to insert None for these, to preserve
2783+
the indexing, as for DbfReader.record
2784+
"""
2785+
return [
2786+
record for record in self.iterRecords(fields, start, stop, deleted_as_None)
2787+
]
27772788

27782789
def iterRecords(
27792790
self,
27802791
fields: list[str] | None = None,
27812792
start: int = 0,
27822793
stop: int | None = None,
2794+
deleted_as_None: bool = False,
27832795
) -> Iterator[_Record | None]:
27842796
"""Returns a generator of records in a dbf file.
27852797
Useful for large shapefiles or dbf files.
@@ -2791,13 +2803,22 @@ def iterRecords(
27912803
start <= i < stop, (or
27922804
start <= i < number_of_records + stop
27932805
if stop < 0).
2806+
2807+
Excludes 'deleted' records (those whose deletion flag is marked).
2808+
Set deleted_as_None=True to insert None for these, to preserve
2809+
the indexing, as for DbfReader.record
27942810
"""
27952811
f = self.file
27962812

27972813
if not isinstance(self.numRecords, int):
27982814
raise ShapefileException(
27992815
"Error when reading number of Records in dbf file header"
28002816
)
2817+
2818+
# Exit early if there are no records
2819+
if self.numRecords == 0:
2820+
return
2821+
28012822
start = ensure_within_bounds(start, self.numRecords)
28022823
if stop is None:
28032824
stop = self.numRecords
@@ -2814,7 +2835,7 @@ def iterRecords(
28142835
r = self._record(
28152836
oid=i, fieldTuples=fieldTuples, recLookup=recLookup, recStruct=recStruct
28162837
)
2817-
if r:
2838+
if r is not None or deleted_as_None:
28182839
yield r
28192840

28202841

@@ -2966,16 +2987,23 @@ def fields(self) -> list[Field]:
29662987
def record(self, i: int = 0, fields: list[str] | None = None) -> _Record | None:
29672988
return self.dbf_reader.record(i, fields)
29682989

2969-
def records(self, fields: list[str] | None = None) -> list[_Record]:
2970-
return self.dbf_reader.records(fields)
2990+
def records(
2991+
self,
2992+
fields: list[str] | None = None,
2993+
start: int = 0,
2994+
stop: int | None = None,
2995+
deleted_as_None: bool = False,
2996+
) -> list[_Record | None]:
2997+
return self.dbf_reader.records(fields, start, stop, deleted_as_None)
29712998

29722999
def iterRecords(
29733000
self,
29743001
fields: list[str] | None = None,
29753002
start: int = 0,
29763003
stop: int | None = None,
3004+
deleted_as_None: bool = False,
29773005
) -> Iterator[_Record | None]:
2978-
return self.dbf_reader.iterRecords(fields, start, stop)
3006+
return self.dbf_reader.iterRecords(fields, start, stop, deleted_as_None)
29793007

29803008
def _seek_0_on_file_obj_wrap_or_open_from_name(
29813009
self,

test_shapefile.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1923,6 +1923,8 @@ def test_write_empty_shapefile(tmpdir, shape_type):
19231923
# test length 0
19241924
assert len(r) == r.numRecords == r.numShapes == 0
19251925
# test records are empty
1926+
for record in r.iterRecords():
1927+
pass
19261928
assert len(r.records()) == 0
19271929
# test shapes are empty
19281930
assert len(r.shapes()) == 0

0 commit comments

Comments
 (0)