From 525228a2798e7571c706d6e261394709bac39f11 Mon Sep 17 00:00:00 2001 From: James Parrott <80779630+JamesParrott@users.noreply.github.com> Date: Mon, 9 Nov 2026 10:22:55 +0000 Subject: [PATCH] Replace while loop producing "xxxx" with "4x" (struct fmt string). v3.1.8.dev --- README.md | 4 ++-- src/shapefile.py | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index a91296f..060aad0 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,8 @@ The Python Shapefile Library (PyShp) reads and writes ESRI Shapefiles in pure Py - **Author**: [Joel Lawhead](https://github.com/GeospatialPython) - **Maintainers**: [James Parrott](https://github.com/JamesParrott) & [Karim Bahgat](https://github.com/karimbahgat) -- **Version**: 3.1.7 -- **Date**: 7th Nov 2026 +- **Version**: 3.1.8.dev +- **Date**: 9th Nov 2026 - **License**: [MIT](https://github.com/GeospatialPython/pyshp/blob/master/LICENSE.TXT) ## Contents diff --git a/src/shapefile.py b/src/shapefile.py index e307204..4986b43 100644 --- a/src/shapefile.py +++ b/src/shapefile.py @@ -8,7 +8,7 @@ from __future__ import annotations -__version__ = "3.1.7" +__version__ = "3.1.8.dev" import abc import array @@ -3038,7 +3038,7 @@ def data_fields(self) -> list[Field]: """All fields except the DeletionFlag.""" return self.fields[1:] - def _record_fmt(self, fields: Container[str] | None = None) -> tuple[str, int]: + def _record_fmt(self, fields: Container[str] | None = None) -> str: """Calculates the format and size of a .dbf record. Optional 'fields' arg specifies which fieldnames to unpack and which to ignore. Note that this always includes the DeletionFlag at index 0, regardless of the 'fields' arg. @@ -3056,11 +3056,12 @@ def _record_fmt(self, fields: Container[str] | None = None) -> tuple[str, int]: fmt = "".join(structcodes) fmt_size = calcsize(fmt) # total size of fields should add up to recordlength from the header - while fmt_size < self._record_length: - # if not, pad byte until reaches recordlength - fmt += "x" - fmt_size += 1 - return (fmt, fmt_size) + # so calculate the difference, and concat a field of that many pad bytes. + if fmt_size < self._record_length: + num_pad_bytes = self._record_length - fmt_size + fmt = f"{fmt}{num_pad_bytes}x" + + return fmt def _record_fields( self, fields: Iterable[str] | None = None @@ -3076,7 +3077,7 @@ def _record_fields( # first ignore repeated field names (order doesn't matter) unique_fields = list(set(fields)) # get the struct - fmt, __fmt_size = self._record_fmt(fields=unique_fields) + fmt = self._record_fmt(fields=unique_fields) recStruct = Struct(fmt) # make sure the given fieldnames exist for name in unique_fields: @@ -3109,7 +3110,7 @@ def _record( None is returned for 'deleted' records (those with their deletion flag marked). """ - # The only format chars in from self._record_fmt, in recStruct from _record_fields, + # The only format chars from self._record_fmt, in recStruct from _record_fields, # are s and x (ascii encoded str and pad byte) so everything in recordContents is bytes # https://docs.python.org/3/library/struct.html#format-characters recordContents = recStruct.unpack(self.file.read(recStruct.size))