From dc7115d7770d689a9b76951e3ca57cd02daac0de Mon Sep 17 00:00:00 2001 From: James Parrott <80779630+JamesParrott@users.noreply.github.com> Date: Fri, 25 Jul 2025 17:33:14 +0100 Subject: [PATCH 1/9] Suppress TODO warnings --- src/shapefile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shapefile.py b/src/shapefile.py index b255dba..330096b 100644 --- a/src/shapefile.py +++ b/src/shapefile.py @@ -100,7 +100,7 @@ BBox = tuple[float, float, float, float] # File name, file object or anything with a read() method that returns bytes. -# TODO: Create simple Protocol with a read() method +# TODO: Create simple Protocol with a read() method pylint: disable=fixme BinaryFileT = Union[str, IO[bytes]] BinaryFileStreamT = Union[IO[bytes], io.BytesIO] @@ -1972,7 +1972,7 @@ def iterShapeRecords( yield ShapeRecord(shape=shape, record=record) else: # only iterate where shape.bbox overlaps with the given bbox - # TODO: internal __record method should be faster but would have to + # TODO: internal __record method should be faster but would have to pylint: disable=fixme # make sure to seek to correct file location... # fieldTuples,recLookup,recStruct = self.__recordFields(fields) From 0eb6a68cfd72cf9acfbb4358bc63083713d9b6bc Mon Sep 17 00:00:00 2001 From: James Parrott <80779630+JamesParrott@users.noreply.github.com> Date: Fri, 25 Jul 2025 17:43:33 +0100 Subject: [PATCH 2/9] Remove some unnecessary elses, and replace elif with ifs; dedent returns therein --- src/shapefile.py | 189 ++++++++++++++++++++++++----------------------- 1 file changed, 95 insertions(+), 94 deletions(-) diff --git a/src/shapefile.py b/src/shapefile.py index 330096b..6d46417 100644 --- a/src/shapefile.py +++ b/src/shapefile.py @@ -128,15 +128,14 @@ def b( if isinstance(v, str): # For python 3 encode str to bytes. return v.encode(encoding, encodingErrors) - elif isinstance(v, bytes): + if isinstance(v, bytes): # Already bytes. return v - elif v is None: + if v is None: # Since we're dealing with text, interpret None as "" return b"" - else: - # Force string representation. - return str(v).encode(encoding, encodingErrors) + # Force string representation. + return str(v).encode(encoding, encodingErrors) def u( @@ -145,15 +144,14 @@ def u( if isinstance(v, bytes): # For python 3 decode bytes to str. return v.decode(encoding, encodingErrors) - elif isinstance(v, str): + if isinstance(v, str): # Already str. return v - elif v is None: + if v is None: # Since we're dealing with text, interpret None as "" return "" - else: - # Force string representation. - return bytes(v).decode(encoding, encodingErrors) + # Force string representation. + return bytes(v).decode(encoding, encodingErrors) def is_string(v: Any) -> bool: @@ -163,8 +161,8 @@ def is_string(v: Any) -> bool: def pathlike_obj(path: Any) -> Any: if isinstance(path, os.PathLike): return os.fsdecode(path) - else: - return path + + return path # Begin @@ -193,8 +191,8 @@ def signed_area( area2 = sum(xs[i] * (ys[i + 1] - ys[i - 1]) for i in range(1, len(coords))) if fast: return area2 - else: - return area2 / 2.0 + + return area2 / 2.0 def is_cw(coords: Coords) -> bool: @@ -374,7 +372,7 @@ def organize_polygon_rings( # multiple exteriors, ie multi-polygon, have to group holes with correct exterior # shapefile format does not specify which holes belong to which exteriors # so have to do efficient multi-stage checking of hole-to-exterior containment - elif len(exteriors) > 1: + if len(exteriors) > 1: # exit early if no holes if not holes: polys = [] @@ -457,13 +455,12 @@ def organize_polygon_rings( return polys # no exteriors, be nice and assume due to incorrect winding order - else: - if return_errors is not None: - return_errors["polygon_only_holes"] = len(holes) - exteriors = holes - # add as single exterior without any holes - polys = [[ext] for ext in exteriors] - return polys + if return_errors is not None: + return_errors["polygon_only_holes"] = len(holes) + exteriors = holes + # add as single exterior without any holes + polys = [[ext] for ext in exteriors] + return polys class GeoJSON_Error(Exception): @@ -515,107 +512,111 @@ def __geo_interface__(self) -> GeoJsonShapeT: # however, it does allow geometry types with 'empty' coordinates to be interpreted as null-geometries return {"type": "Point", "coordinates": ()} # return {"type": "Point", "coordinates": tuple()} #type: ignore - else: - return {"type": "Point", "coordinates": self.points[0]} - # return {"type": "Point", "coordinates": tuple(self.points[0])} # type: ignore - elif self.shapeType in [MULTIPOINT, MULTIPOINTM, MULTIPOINTZ]: + + return {"type": "Point", "coordinates": self.points[0]} + # return {"type": "Point", "coordinates": tuple(self.points[0])} # type: ignore + + if self.shapeType in [MULTIPOINT, MULTIPOINTM, MULTIPOINTZ]: if len(self.points) == 0: # the shape has no coordinate information, i.e. is 'empty' # the geojson spec does not define a proper null-geometry type # however, it does allow geometry types with 'empty' coordinates to be interpreted as null-geometries return {"type": "MultiPoint", "coordinates": []} - else: - # multipoint - return { - "type": "MultiPoint", - "coordinates": self.points, - # "coordinates": [tuple(p) for p in self.points], #type: ignore - } - elif self.shapeType in [POLYLINE, POLYLINEM, POLYLINEZ]: + + # multipoint + return { + "type": "MultiPoint", + "coordinates": self.points, + # "coordinates": [tuple(p) for p in self.points], #type: ignore + } + + if self.shapeType in [POLYLINE, POLYLINEM, POLYLINEZ]: if len(self.parts) == 0: # the shape has no coordinate information, i.e. is 'empty' # the geojson spec does not define a proper null-geometry type # however, it does allow geometry types with 'empty' coordinates to be interpreted as null-geometries return {"type": "LineString", "coordinates": []} - elif len(self.parts) == 1: + + if len(self.parts) == 1: # linestring return { "type": "LineString", "coordinates": self.points, # "coordinates": [tuple(p) for p in self.points], #type: ignore } - else: - # multilinestring - ps = None - coordinates = [] - for part in self.parts: - if ps is None: - ps = part - continue - else: - # coordinates.append([tuple(p) for p in self.points[ps:part]]) - coordinates.append([p for p in self.points[ps:part]]) - ps = part - # coordinates.append([tuple(p) for p in self.points[part:]]) - coordinates.append([p for p in self.points[part:]]) # pylint: disable=undefined-loop-variable + # multilinestring + ps = None + coordinates = [] + for part in self.parts: + if ps is None: + ps = part + continue + else: + # coordinates.append([tuple(p) for p in self.points[ps:part]]) + coordinates.append([p for p in self.points[ps:part]]) + ps = part + + # coordinates.append([tuple(p) for p in self.points[part:]]) + coordinates.append([p for p in self.points[part:]]) # pylint: disable=undefined-loop-variable - return {"type": "MultiLineString", "coordinates": coordinates} - elif self.shapeType in [POLYGON, POLYGONM, POLYGONZ]: + return {"type": "MultiLineString", "coordinates": coordinates} + + if self.shapeType in [POLYGON, POLYGONM, POLYGONZ]: if len(self.parts) == 0: # the shape has no coordinate information, i.e. is 'empty' # the geojson spec does not define a proper null-geometry type # however, it does allow geometry types with 'empty' coordinates to be interpreted as null-geometries return {"type": "Polygon", "coordinates": []} - else: - # get all polygon rings - rings = [] - for i in range(len(self.parts)): - # get indexes of start and end points of the ring - start = self.parts[i] - try: - end = self.parts[i + 1] - except IndexError: - end = len(self.points) - - # extract the points that make up the ring - # ring = [tuple(p) for p in self.points[start:end]] - ring = [p for p in self.points[start:end]] - rings.append(ring) - - # organize rings into list of polygons, where each polygon is defined as list of rings. - # the first ring is the exterior and any remaining rings are holes (same as GeoJSON). - polys = organize_polygon_rings(rings, self._errors) - - # if VERBOSE is True, issue detailed warning about any shape errors - # encountered during the Shapefile to GeoJSON conversion - if VERBOSE and self._errors: - header = f"Possible issue encountered when converting Shape #{self.oid} to GeoJSON: " - orphans = self._errors.get("polygon_orphaned_holes", None) - if orphans: - msg = ( - header - + "Shapefile format requires that all polygon interior holes be contained by an exterior ring, \ + + # get all polygon rings + rings = [] + for i in range(len(self.parts)): + # get indexes of start and end points of the ring + start = self.parts[i] + try: + end = self.parts[i + 1] + except IndexError: + end = len(self.points) + + # extract the points that make up the ring + # ring = [tuple(p) for p in self.points[start:end]] + ring = [p for p in self.points[start:end]] + rings.append(ring) + + # organize rings into list of polygons, where each polygon is defined as list of rings. + # the first ring is the exterior and any remaining rings are holes (same as GeoJSON). + polys = organize_polygon_rings(rings, self._errors) + + # if VERBOSE is True, issue detailed warning about any shape errors + # encountered during the Shapefile to GeoJSON conversion + if VERBOSE and self._errors: + header = f"Possible issue encountered when converting Shape #{self.oid} to GeoJSON: " + orphans = self._errors.get("polygon_orphaned_holes", None) + if orphans: + msg = ( + header + + "Shapefile format requires that all polygon interior holes be contained by an exterior ring, \ but the Shape contained interior holes (defined by counter-clockwise orientation in the shapefile format) that were \ orphaned, i.e. not contained by any exterior rings. The rings were still included but were \ encoded as GeoJSON exterior rings instead of holes." - ) - logger.warning(msg) - only_holes = self._errors.get("polygon_only_holes", None) - if only_holes: - msg = ( - header - + "Shapefile format requires that polygons contain at least one exterior ring, \ + ) + logger.warning(msg) + only_holes = self._errors.get("polygon_only_holes", None) + if only_holes: + msg = ( + header + + "Shapefile format requires that polygons contain at least one exterior ring, \ but the Shape was entirely made up of interior holes (defined by counter-clockwise orientation in the shapefile format). The rings were \ still included but were encoded as GeoJSON exterior rings instead of holes." - ) - logger.warning(msg) + ) + logger.warning(msg) - # return as geojson - if len(polys) == 1: - return {"type": "Polygon", "coordinates": polys[0]} - else: - return {"type": "MultiPolygon", "coordinates": polys} + # return as geojson + if len(polys) == 1: + return {"type": "Polygon", "coordinates": polys[0]} + + return {"type": "MultiPolygon", "coordinates": polys} else: raise GeoJSON_Error( From f5752a00233db9b46ec1122605a88822c6704443 Mon Sep 17 00:00:00 2001 From: James Parrott <80779630+JamesParrott@users.noreply.github.com> Date: Fri, 25 Jul 2025 18:50:43 +0100 Subject: [PATCH 3/9] Replace .append on trivial list comps with .extend. Remove more elses. --- src/shapefile.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/shapefile.py b/src/shapefile.py index 6d46417..30a1482 100644 --- a/src/shapefile.py +++ b/src/shapefile.py @@ -552,13 +552,13 @@ def __geo_interface__(self) -> GeoJsonShapeT: if ps is None: ps = part continue - else: - # coordinates.append([tuple(p) for p in self.points[ps:part]]) - coordinates.append([p for p in self.points[ps:part]]) - ps = part + + # coordinates.append([tuple(p) for p in self.points[ps:part]]) + coordinates.extend(self.points[ps:part]) + ps = part # coordinates.append([tuple(p) for p in self.points[part:]]) - coordinates.append([p for p in self.points[part:]]) # pylint: disable=undefined-loop-variable + coordinates.extend(self.points[part:]) # pylint: disable=undefined-loop-variable return {"type": "MultiLineString", "coordinates": coordinates} @@ -581,7 +581,7 @@ def __geo_interface__(self) -> GeoJsonShapeT: # extract the points that make up the ring # ring = [tuple(p) for p in self.points[start:end]] - ring = [p for p in self.points[start:end]] + ring = list(self.points[start:end]) rings.append(ring) # organize rings into list of polygons, where each polygon is defined as list of rings. @@ -813,8 +813,8 @@ def __getitem__(self, item): index = None if index is not None: return list.__getitem__(self, index) - else: - raise IndexError(f'"{item}" is not a field name and not an int') + + raise IndexError(f'"{item}" is not a field name and not an int') def __setitem__(self, key, value): """ @@ -831,8 +831,8 @@ def __setitem__(self, key, value): index = self.__field_positions.get(key) if index is not None: return list.__setitem__(self, index, value) - else: - raise IndexError(f"{key} is not a field name and not an int") # pylint: disable=raise-missing-from + + raise IndexError(f"{key} is not a field name and not an int") # pylint: disable=raise-missing-from @property def oid(self) -> int: @@ -937,7 +937,7 @@ class ShapefileException(Exception): """An exception to handle shapefile specific problems.""" -class _NoShpSentinel(object): +class _NoShpSentinel: """For use as a default value for shp to preserve the behaviour (from when all keyword args were gathered in the **kwargs dict) in case someone explictly From 7b07b5627b301b65b5032d7cc0a5357b8969918f Mon Sep 17 00:00:00 2001 From: James Parrott <80779630+JamesParrott@users.noreply.github.com> Date: Fri, 25 Jul 2025 18:58:09 +0100 Subject: [PATCH 4/9] Remove last of unnecessary else: s --- src/shapefile.py | 94 +++++++++++++++++++++++------------------------- 1 file changed, 44 insertions(+), 50 deletions(-) diff --git a/src/shapefile.py b/src/shapefile.py index 30a1482..f02c6a3 100644 --- a/src/shapefile.py +++ b/src/shapefile.py @@ -618,10 +618,9 @@ def __geo_interface__(self) -> GeoJsonShapeT: return {"type": "MultiPolygon", "coordinates": polys} - else: - raise GeoJSON_Error( - f'Shape type "{SHAPETYPE_LOOKUP[self.shapeType]}" cannot be represented as GeoJSON.' - ) + raise GeoJSON_Error( + f'Shape type "{SHAPETYPE_LOOKUP[self.shapeType]}" cannot be represented as GeoJSON.' + ) @staticmethod def _from_geojson(geoj) -> Shape: @@ -1053,7 +1052,7 @@ def __init__( raise ShapefileException( "Zipfile does not contain any shapefiles" ) - elif len(shapefiles) == 1: + if len(shapefiles) == 1: shapefile = shapefiles[0] else: raise ShapefileException( @@ -1088,12 +1087,12 @@ def __init__( # Load and exit early self.load() return - else: - raise ShapefileException( - f"No shp or dbf file found in zipfile: {path}" - ) - elif path.startswith("http"): + raise ShapefileException( + f"No shp or dbf file found in zipfile: {path}" + ) + + if path.startswith("http"): # Shapefile is from a url # Download each file to temporary path and treat as normal shapefile path urlinfo = urlparse(path) @@ -1126,16 +1125,13 @@ def __init__( # Load and exit early self.load() return - else: - raise ShapefileException( - f"No shp or dbf file found at url: {path}" - ) - else: - # Local file path to a shapefile - # Load and exit early - self.load(path) - return + raise ShapefileException(f"No shp or dbf file found at url: {path}") + + # Local file path to a shapefile + # Load and exit early + self.load(path) + return if not isinstance(shp, _NoShpSentinel): self.shp = self.__seek_0_on_file_obj_wrap_or_open_from_name("shp", shp) @@ -1208,7 +1204,7 @@ def __len__(self): return self.numRecords - elif self.shp: + if self.shp: # Otherwise use shape count if self.shx: if self.numShapes is None: @@ -1216,36 +1212,34 @@ def __len__(self): return self.numShapes - else: - # Index file not available, iterate all shapes to get total count - if self.numShapes is None: - # Determine length of shp file - shp = self.shp - checkpoint = shp.tell() - shp.seek(0, 2) - shpLength = shp.tell() - shp.seek(100) - # Do a fast shape iteration until end of file. - offsets = [] - pos = shp.tell() - while pos < shpLength: - offsets.append(pos) - # Unpack the shape header only - (__recNum, recLength) = unpack_2_int32_be(shp.read(8)) - # Jump to next shape position - pos += 8 + (2 * recLength) - shp.seek(pos) - # Set numShapes and offset indices - self.numShapes = len(offsets) - self._offsets = offsets - # Return to previous file position - shp.seek(checkpoint) - - return self.numShapes - - else: - # No file loaded yet, treat as 'empty' shapefile - return 0 + # Index file not available, iterate all shapes to get total count + if self.numShapes is None: + # Determine length of shp file + shp = self.shp + checkpoint = shp.tell() + shp.seek(0, 2) + shpLength = shp.tell() + shp.seek(100) + # Do a fast shape iteration until end of file. + offsets = [] + pos = shp.tell() + while pos < shpLength: + offsets.append(pos) + # Unpack the shape header only + (__recNum, recLength) = unpack_2_int32_be(shp.read(8)) + # Jump to next shape position + pos += 8 + (2 * recLength) + shp.seek(pos) + # Set numShapes and offset indices + self.numShapes = len(offsets) + self._offsets = offsets + # Return to previous file position + shp.seek(checkpoint) + + return self.numShapes + + # No file loaded yet, treat as 'empty' shapefile + return 0 def __iter__(self): """Iterates through the shapes/records in the shapefile.""" From c8dce92419b820f3f76be79bf3a604a259f784bd Mon Sep 17 00:00:00 2001 From: James Parrott <80779630+JamesParrott@users.noreply.github.com> Date: Fri, 25 Jul 2025 19:20:10 +0100 Subject: [PATCH 5/9] Add a gen exp and a contains test, and suppress weird yield next() issue --- pyproject.toml | 25 ++++++++++++++++++++----- src/shapefile.py | 6 +++--- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index af799f7..ac03959 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -115,14 +115,29 @@ load-plugins=[ [tool.pylint.'MESSAGES CONTROL'] # Silence warning: shapefile.py:2076:20: W0212: Access to a protected # member _from_geojson of a client class (protected-access) -# +# Silence remarks: +# src\shapefile.py:338:0: R0914: Too many local variables (21/15) (too-many-locals) +# src\shapefile.py:338:0: R0912: Too many branches (24/12) (too-many-branches) +# src\shapefile.py:338:0: R0915: Too many statements (52/50) (too-many-statements) +# src\shapefile.py:470:0: R0902: Too many instance attributes (9/7) (too-many-instance-attributes) +# src\shapefile.py:471:4: R0913: Too many arguments (6/5) (too-many-arguments) +# src\shapefile.py:471:4: R0917: Too many positional arguments (6/5) (too-many-positional-arguments) +# src\shapefile.py:506:4: R0911: Too many return statements (10/6) (too-many-return-statements) +# src\shapefile.py:878:0: R0903: Too few public methods (0/2) (too-few-public-methods) # Silence warnings: test_shapefile.py:{783,786,799,803,06,1195}:19: # W0212: Access to a protected member _offsets of a # client class (protected-access) # # Toml multi-line string used instead of array due to: # https://github.com/christopherpickering/pylint-per-file-ignores/issues/160 -per-file-ignores = """ - shapefile.py:W0212 - test_shapefile.py:W0212 -""" +per-file-ignores = [ + "src/shapefile.py:W0212", + "src/shapefile.py:R0902", + "src/shapefile.py:R0903", + "src/shapefile.py:R0911", + "src/shapefile.py:R0912", + "src/shapefile.py:R0914", + "src/shapefile.py:R0915", + "src/shapefile.py:R0917", + "test_shapefile.py:W0212", +] diff --git a/src/shapefile.py b/src/shapefile.py index f02c6a3..d60e9ed 100644 --- a/src/shapefile.py +++ b/src/shapefile.py @@ -2314,7 +2314,7 @@ def __dbfHeader(self): raise ShapefileException( "Shapefile dbf header length exceeds maximum length." ) - recordLength = sum([int(field[2]) for field in fields]) + 1 + recordLength = sum(int(field[2]) for field in fields) + 1 header = pack( " Date: Fri, 25 Jul 2025 19:28:22 +0100 Subject: [PATCH 6/9] Enable R level Pylint warnings --- .github/workflows/run_checks_build_and_test.yml | 2 +- pyproject.toml | 15 +++++---------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/.github/workflows/run_checks_build_and_test.yml b/.github/workflows/run_checks_build_and_test.yml index f965e50..419b45b 100644 --- a/.github/workflows/run_checks_build_and_test.yml +++ b/.github/workflows/run_checks_build_and_test.yml @@ -29,7 +29,7 @@ jobs: - name: run Pylint for errors and warnings only continue-on-error: true run: | - pylint --disable=R,C test_shapefile.py src/shapefile.py + pylint --disable=C test_shapefile.py src/shapefile.py build_wheel_and_sdist: runs-on: ubuntu-latest diff --git a/pyproject.toml b/pyproject.toml index ac03959..aa11da4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -112,7 +112,6 @@ load-plugins=[ "pylint_per_file_ignores", ] -[tool.pylint.'MESSAGES CONTROL'] # Silence warning: shapefile.py:2076:20: W0212: Access to a protected # member _from_geojson of a client class (protected-access) # Silence remarks: @@ -124,20 +123,16 @@ load-plugins=[ # src\shapefile.py:471:4: R0917: Too many positional arguments (6/5) (too-many-positional-arguments) # src\shapefile.py:506:4: R0911: Too many return statements (10/6) (too-many-return-statements) # src\shapefile.py:878:0: R0903: Too few public methods (0/2) (too-few-public-methods) +# src\shapefile.py:1981:0: R0904: Too many public methods (23/20) (too-many-public-methods) +# src\shapefile.py:2117:17: R1732: Consider using 'with' for resource-allocating operations (consider-using-with) # Silence warnings: test_shapefile.py:{783,786,799,803,06,1195}:19: # W0212: Access to a protected member _offsets of a # client class (protected-access) # # Toml multi-line string used instead of array due to: # https://github.com/christopherpickering/pylint-per-file-ignores/issues/160 +[tool.pylint.'messages control'] per-file-ignores = [ - "src/shapefile.py:W0212", - "src/shapefile.py:R0902", - "src/shapefile.py:R0903", - "src/shapefile.py:R0911", - "src/shapefile.py:R0912", - "src/shapefile.py:R0914", - "src/shapefile.py:R0915", - "src/shapefile.py:R0917", - "test_shapefile.py:W0212", + "/src/shapefile.py:W0212,R0902,R0903,R0904,R0911,R0912,R0913,R0914,R0915,R0917,R1732", + "test_shapefile.py:W0212,R1732", ] From f51f0f24464c6bd255c8ea8fa06619545e8a6a0b Mon Sep 17 00:00:00 2001 From: James Parrott <80779630+JamesParrott@users.noreply.github.com> Date: Fri, 25 Jul 2025 19:33:33 +0100 Subject: [PATCH 7/9] Fix bug --- src/shapefile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shapefile.py b/src/shapefile.py index d60e9ed..3ca0e41 100644 --- a/src/shapefile.py +++ b/src/shapefile.py @@ -554,11 +554,11 @@ def __geo_interface__(self) -> GeoJsonShapeT: continue # coordinates.append([tuple(p) for p in self.points[ps:part]]) - coordinates.extend(self.points[ps:part]) + coordinates.append(list(self.points[ps:part])) ps = part # coordinates.append([tuple(p) for p in self.points[part:]]) - coordinates.extend(self.points[part:]) # pylint: disable=undefined-loop-variable + coordinates.append(list(self.points[part:])) return {"type": "MultiLineString", "coordinates": coordinates} From ac0142a0823efb36261d2e4033dd22d9387e52ab Mon Sep 17 00:00:00 2001 From: James Parrott <80779630+JamesParrott@users.noreply.github.com> Date: Fri, 25 Jul 2025 21:22:03 +0100 Subject: [PATCH 8/9] Allow pylint step to fail CI. Suppress undefined-loop-variable --- .github/workflows/run_checks_build_and_test.yml | 1 - src/shapefile.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/run_checks_build_and_test.yml b/.github/workflows/run_checks_build_and_test.yml index 419b45b..f9e4b2e 100644 --- a/.github/workflows/run_checks_build_and_test.yml +++ b/.github/workflows/run_checks_build_and_test.yml @@ -27,7 +27,6 @@ jobs: pip install pytest pylint pylint-per-file-ignores pip install -e . - name: run Pylint for errors and warnings only - continue-on-error: true run: | pylint --disable=C test_shapefile.py src/shapefile.py diff --git a/src/shapefile.py b/src/shapefile.py index 3ca0e41..ee4ebe7 100644 --- a/src/shapefile.py +++ b/src/shapefile.py @@ -558,7 +558,7 @@ def __geo_interface__(self) -> GeoJsonShapeT: ps = part # coordinates.append([tuple(p) for p in self.points[part:]]) - coordinates.append(list(self.points[part:])) + coordinates.append(list(self.points[part:])) # pylint: disable=undefined-loop-variable (assert len(self.parts) >1) return {"type": "MultiLineString", "coordinates": coordinates} From f442a6fb8a5d6eb1e052ebf85ce62b68f0b74c06 Mon Sep 17 00:00:00 2001 From: James Parrott <80779630+JamesParrott@users.noreply.github.com> Date: Fri, 25 Jul 2025 21:28:01 +0100 Subject: [PATCH 9/9] Remove extra comments after pylint directive --- src/shapefile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shapefile.py b/src/shapefile.py index ee4ebe7..5ba3c61 100644 --- a/src/shapefile.py +++ b/src/shapefile.py @@ -558,8 +558,8 @@ def __geo_interface__(self) -> GeoJsonShapeT: ps = part # coordinates.append([tuple(p) for p in self.points[part:]]) - coordinates.append(list(self.points[part:])) # pylint: disable=undefined-loop-variable (assert len(self.parts) >1) - + # assert len(self.parts) >1 # so disable pylint rule + coordinates.append(list(self.points[part:])) # pylint: disable=undefined-loop-variable return {"type": "MultiLineString", "coordinates": coordinates} if self.shapeType in [POLYGON, POLYGONM, POLYGONZ]: