88
99from __future__ import annotations
1010
11- __version__ = "3.0.0-alpha "
11+ __version__ = "3.0.0"
1212
1313import array
1414import doctest
@@ -325,7 +325,10 @@ class GeoJSONFeatureCollection(TypedDict):
325325
326326
327327class GeoJSONFeatureCollectionWithBBox (GeoJSONFeatureCollection ):
328- # bbox is technically optional under the spec
328+ # bbox is technically optional under the spec but this seems
329+ # a very minor improvement that would require NotRequired
330+ # from the typing-extensions backport for Python 3.9
331+ # (PyShp's resisted having any other dependencies so far!)
329332 bbox : list [float ]
330333
331334
@@ -726,6 +729,11 @@ def __init__(
726729 are designated by their starting index in geometry record's
727730 list of shapes. For MultiPatch geometry, partTypes designates
728731 the patch type of each of the parts.
732+ Lines allows the points-lists and parts to be denoted together
733+ in one argument. It is intended for multiple point shapes
734+ (polylines, polygons and multipatches) but if used as a length-1
735+ nested list for a multipoint (instead of points for some reason)
736+ PyShp will not complain, as multipoints only have 1 part internally.
729737 """
730738
731739 # Preserve previous behaviour for anyone who set self.shapeType = None
@@ -741,7 +749,6 @@ def __init__(
741749 default_points : PointsT = []
742750 default_parts : list [int ] = []
743751
744- # Make sure polygon rings (parts) are closed
745752 if lines is not None :
746753 if self .shapeType in Polygon_shapeTypes :
747754 lines = list (lines )
@@ -1072,7 +1079,7 @@ def __repr__(self) -> str:
10721079class NullShape (Shape ):
10731080 # Shape.shapeType = NULL already,
10741081 # to preserve handling of default args in Shape.__init__
1075- # Repeated for clarity .
1082+ # Repeated for the avoidance of doubt .
10761083 def __init__ (
10771084 self ,
10781085 oid : Optional [int ] = None ,
@@ -1117,8 +1124,8 @@ def write_to_byte_stream(
11171124
11181125class _CanHaveBBox (Shape ):
11191126 """As well as setting bounding boxes, we also utilize the
1120- fact that this mixin applies to all the shapes that are
1121- not a single point.
1127+ fact that this mixin only applies to all the shapes that are
1128+ not a single point (polylines, polygons, multipatches and multipoints) .
11221129 """
11231130
11241131 @staticmethod
@@ -1203,10 +1210,6 @@ def from_byte_stream(
12031210 b_io , nParts
12041211 )
12051212
1206- # else:
1207- # parts = None
1208- # partTypes = None
1209-
12101213 if nPoints :
12111214 kwargs ["points" ] = cast (
12121215 PointsT , cls ._read_points_from_byte_stream (b_io , nPoints )
@@ -1222,25 +1225,7 @@ def from_byte_stream(
12221225 b_io , nPoints , next_shape
12231226 )
12241227
1225- # else:
1226- # points = None
1227- # zbox, zs = None, None
1228- # mbox, ms = None, None
1229-
12301228 return ShapeClass (** kwargs )
1231- # return ShapeClass(
1232- # shapeType=shapeType,
1233- # # Mypy 1.17.1 doesn't figure out that an Optional[list[Point2D]] is an Optional[list[PointT]]
1234- # points=cast(Optional[PointsT], points),
1235- # parts=parts,
1236- # partTypes=partTypes,
1237- # oid=oid,
1238- # m=ms,
1239- # z=zs,
1240- # bbox=shape_bbox,
1241- # mbox=mbox,
1242- # zbox=zbox,
1243- # )
12441229
12451230 @staticmethod
12461231 def write_to_byte_stream (
@@ -1249,7 +1234,7 @@ def write_to_byte_stream(
12491234 i : int ,
12501235 ) -> int :
12511236 # We use static methods here and below,
1252- # to support s only being an instance of a the
1237+ # to support s only being an instance of the
12531238 # Shape base class (with shapeType set)
12541239 # i.e. not necessarily one of our newer shape specific
12551240 # sub classes.
@@ -2368,6 +2353,10 @@ def __init__(
23682353 # Close and delete the temporary zipfile
23692354 try :
23702355 zipfileobj .close ()
2356+ # TODO Does catching all possible exceptions really increase
2357+ # the chances of closing the zipfile successully, or does it
2358+ # just mean .close() failures will still fail, but fail
2359+ # silently?
23712360 except : # noqa: E722
23722361 pass
23732362 # Try to load shapefile
@@ -3610,15 +3599,16 @@ def shape(
36103599 # Check is shape or import from geojson
36113600 if not isinstance (s , Shape ):
36123601 if hasattr (s , "__geo_interface__" ):
3613- s = s .__geo_interface__ # type: ignore [assignment]
3602+ shape_dict = cast ( dict , s .__geo_interface__ )
36143603 if isinstance (s , dict ):
3615- s = Shape ._from_geojson (cast (GeoJSONHomogeneousGeometryObject , s ))
3604+ shape_dict = Shape ._from_geojson (cast (GeoJSONHomogeneousGeometryObject , s ))
36163605 else :
36173606 raise TypeError (
36183607 "Can only write Shape objects, GeoJSON dictionaries, "
36193608 "or objects with the __geo_interface__, "
36203609 f"not: { s } "
36213610 )
3611+ s = Shape ._from_geojson (shape_dict )
36223612 # Write to file
36233613 offset , length = self .__shpRecord (s )
36243614 if self .shx :
@@ -3660,7 +3650,8 @@ def __shpRecord(self, s: Shape) -> tuple[int, int]:
36603650 # Record number, Content length place holder
36613651 b_io .write (pack (">2i" , self .shpNum , - 1 ))
36623652
3663- # Track number of content bytes written. Excluding self.shpNum and length t.b.c.
3653+ # Track number of content bytes written, excluding
3654+ # self.shpNum and length (t.b.c.)
36643655 n = 0
36653656
36663657 n += b_io .write (pack ("<i" , s .shapeType ))
@@ -4022,7 +4013,7 @@ def _filter_network_doctests(
40224013
40234014def _replace_remote_url (
40244015 old_url : str ,
4025- # Default port of Python http.server and Python 2's SimpleHttpServer
4016+ # Default port of Python http.server
40264017 port : int = 8000 ,
40274018 scheme : str = "http" ,
40284019 netloc : str = "localhost" ,
0 commit comments