@@ -8,8 +8,8 @@ The Python Shapefile Library (PyShp) reads and writes ESRI Shapefiles in pure Py
88
99- ** Author** : [ Joel Lawhead] ( https://github.com/GeospatialPython )
1010- ** Maintainers** : [ Karim Bahgat] ( https://github.com/karimbahgat )
11- - ** Version** : 2.3.1
12- - ** Date** : 28 July, 2022
11+ - ** Version** : 3.0.0-alpha
12+ - ** Date** : 31 July, 2025
1313- ** License** : [ MIT] ( https://github.com/GeospatialPython/pyshp/blob/master/LICENSE.TXT )
1414
1515## Contents
@@ -93,6 +93,30 @@ part of your geospatial project.
9393
9494# Version Changes
9595
96+ ## 3.0.0-alpha
97+
98+ ### Breaking Changes:
99+ - Python 2 and Python 3.8 support dropped.
100+ - Field info tuple is now a namedtuple (Field) instead of a list.
101+ - Field type codes are now FieldType enum members.
102+ - bbox, mbox and zbox attributes are all new Namedtuples.
103+ - Writer does not mutate shapes.
104+ - New custom subclasses for each shape type: Null, Multipatch, Point, Polyline,
105+ Multipoint, and Polygon, plus the latter 4's M and Z variants (Reader and
106+ Writer are still compatible with their base class, Shape, as before).
107+ - Shape sub classes are creatable from, and serializable to bytes streams,
108+ as per the shapefile spec.
109+
110+ ### Code quality
111+ - Statically typed, and checked with Mypy
112+ - Checked with Ruff.
113+ - f-strings
114+ - Remove Python 2 specific functions.
115+ - Run doctests against wheels.
116+ - Testing of wheels before publishing them
117+ - pyproject.toml src layout
118+ - Slow test marked.
119+
96120## 2.4.0
97121
98122### Breaking Change. Support for Python 2 and Pythons <= 3.8 to be dropped.
@@ -406,7 +430,7 @@ and the bounding box area the shapefile covers:
406430 >>> len(sf)
407431 663
408432 >>> sf.bbox
409- ( -122.515048, 37.652916, -122.327622, 37.863433)
433+ BBox(xmin= -122.515048, ymin= 37.652916, xmax= -122.327622, ymax= 37.863433)
410434
411435Finally, if you would prefer to work with the entire shapefile in a different
412436format, you can convert all of it to a GeoJSON dictionary, although you may lose
@@ -553,45 +577,34 @@ in the shp geometry file and the dbf attribute file.
553577
554578The field names of a shapefile are available as soon as you read a shapefile.
555579You can call the "fields" attribute of the shapefile as a Python list. Each
556- field is a Python list with the following information:
580+ field is a Python namedtuple (Field) with the following information:
557581
558- * Field name: the name describing the data at this column index.
559- * Field type: the type of data at this column index. Types can be:
582+ * name: the name describing the data at this column index (a string) .
583+ * field_type: a FieldType enum member determining the type of data at this column index. Names can be:
560584 * "C": Characters, text.
561585 * "N": Numbers, with or without decimals.
562586 * "F": Floats (same as "N").
563587 * "L": Logical, for boolean True/False values.
564588 * "D": Dates.
565589 * "M": Memo, has no meaning within a GIS and is part of the xbase spec instead.
566- * Field length: the length of the data found at this column index. Older GIS
590+ * size: Field length: the length of the data found at this column index. Older GIS
567591 software may truncate this length to 8 or 11 characters for "Character"
568592 fields.
569- * Decimal length: the number of decimal places found in "Number" fields.
593+ * deci: Decimal length. The number of decimal places found in "Number" fields.
594+
595+ A new field can be created directly from the type enum member etc., or as follows:
596+
597+ >>> shapefile.Field.from_unchecked("Population", "N", 10,0)
598+ Field(name="Population", field_type=FieldType.N, size=10, decimal=0)
599+
600+ Using this method the conversion from string to enum is done automatically.
570601
571602To see the fields for the Reader object above (sf) call the "fields"
572603attribute:
573604
574605
575- >>> fields = sf.fields
576-
577- >>> assert fields == [("DeletionFlag", "C", 1, 0), ["AREA", "N", 18, 5],
578- ... ["BKG_KEY", "C", 12, 0], ["POP1990", "N", 9, 0], ["POP90_SQMI", "N", 10, 1],
579- ... ["HOUSEHOLDS", "N", 9, 0],
580- ... ["MALES", "N", 9, 0], ["FEMALES", "N", 9, 0], ["WHITE", "N", 9, 0],
581- ... ["BLACK", "N", 8, 0], ["AMERI_ES", "N", 7, 0], ["ASIAN_PI", "N", 8, 0],
582- ... ["OTHER", "N", 8, 0], ["HISPANIC", "N", 8, 0], ["AGE_UNDER5", "N", 8, 0],
583- ... ["AGE_5_17", "N", 8, 0], ["AGE_18_29", "N", 8, 0], ["AGE_30_49", "N", 8, 0],
584- ... ["AGE_50_64", "N", 8, 0], ["AGE_65_UP", "N", 8, 0],
585- ... ["NEVERMARRY", "N", 8, 0], ["MARRIED", "N", 9, 0], ["SEPARATED", "N", 7, 0],
586- ... ["WIDOWED", "N", 8, 0], ["DIVORCED", "N", 8, 0], ["HSEHLD_1_M", "N", 8, 0],
587- ... ["HSEHLD_1_F", "N", 8, 0], ["MARHH_CHD", "N", 8, 0],
588- ... ["MARHH_NO_C", "N", 8, 0], ["MHH_CHILD", "N", 7, 0],
589- ... ["FHH_CHILD", "N", 7, 0], ["HSE_UNITS", "N", 9, 0], ["VACANT", "N", 7, 0],
590- ... ["OWNER_OCC", "N", 8, 0], ["RENTER_OCC", "N", 8, 0],
591- ... ["MEDIAN_VAL", "N", 7, 0], ["MEDIANRENT", "N", 4, 0],
592- ... ["UNITS_1DET", "N", 8, 0], ["UNITS_1ATT", "N", 7, 0], ["UNITS2", "N", 7, 0],
593- ... ["UNITS3_9", "N", 8, 0], ["UNITS10_49", "N", 8, 0],
594- ... ["UNITS50_UP", "N", 8, 0], ["MOBILEHOME", "N", 7, 0]]
606+ >>> sf.fields
607+ [Field(name="DeletionFlag", field_type=FieldType.C, size=1, decimal=0), Field(name="AREA", field_type=FieldType.N, size=18, decimal=5), Field(name="BKG_KEY", field_type=FieldType.C, size=12, decimal=0), Field(name="POP1990", field_type=FieldType.N, size=9, decimal=0), Field(name="POP90_SQMI", field_type=FieldType.N, size=10, decimal=1), Field(name="HOUSEHOLDS", field_type=FieldType.N, size=9, decimal=0), Field(name="MALES", field_type=FieldType.N, size=9, decimal=0), Field(name="FEMALES", field_type=FieldType.N, size=9, decimal=0), Field(name="WHITE", field_type=FieldType.N, size=9, decimal=0), Field(name="BLACK", field_type=FieldType.N, size=8, decimal=0), Field(name="AMERI_ES", field_type=FieldType.N, size=7, decimal=0), Field(name="ASIAN_PI", field_type=FieldType.N, size=8, decimal=0), Field(name="OTHER", field_type=FieldType.N, size=8, decimal=0), Field(name="HISPANIC", field_type=FieldType.N, size=8, decimal=0), Field(name="AGE_UNDER5", field_type=FieldType.N, size=8, decimal=0), Field(name="AGE_5_17", field_type=FieldType.N, size=8, decimal=0), Field(name="AGE_18_29", field_type=FieldType.N, size=8, decimal=0), Field(name="AGE_30_49", field_type=FieldType.N, size=8, decimal=0), Field(name="AGE_50_64", field_type=FieldType.N, size=8, decimal=0), Field(name="AGE_65_UP", field_type=FieldType.N, size=8, decimal=0), Field(name="NEVERMARRY", field_type=FieldType.N, size=8, decimal=0), Field(name="MARRIED", field_type=FieldType.N, size=9, decimal=0), Field(name="SEPARATED", field_type=FieldType.N, size=7, decimal=0), Field(name="WIDOWED", field_type=FieldType.N, size=8, decimal=0), Field(name="DIVORCED", field_type=FieldType.N, size=8, decimal=0), Field(name="HSEHLD_1_M", field_type=FieldType.N, size=8, decimal=0), Field(name="HSEHLD_1_F", field_type=FieldType.N, size=8, decimal=0), Field(name="MARHH_CHD", field_type=FieldType.N, size=8, decimal=0), Field(name="MARHH_NO_C", field_type=FieldType.N, size=8, decimal=0), Field(name="MHH_CHILD", field_type=FieldType.N, size=7, decimal=0), Field(name="FHH_CHILD", field_type=FieldType.N, size=7, decimal=0), Field(name="HSE_UNITS", field_type=FieldType.N, size=9, decimal=0), Field(name="VACANT", field_type=FieldType.N, size=7, decimal=0), Field(name="OWNER_OCC", field_type=FieldType.N, size=8, decimal=0), Field(name="RENTER_OCC", field_type=FieldType.N, size=8, decimal=0), Field(name="MEDIAN_VAL", field_type=FieldType.N, size=7, decimal=0), Field(name="MEDIANRENT", field_type=FieldType.N, size=4, decimal=0), Field(name="UNITS_1DET", field_type=FieldType.N, size=8, decimal=0), Field(name="UNITS_1ATT", field_type=FieldType.N, size=7, decimal=0), Field(name="UNITS2", field_type=FieldType.N, size=7, decimal=0), Field(name="UNITS3_9", field_type=FieldType.N, size=8, decimal=0), Field(name="UNITS10_49", field_type=FieldType.N, size=8, decimal=0), Field(name="UNITS50_UP", field_type=FieldType.N, size=8, decimal=0), Field(name="MOBILEHOME", field_type=FieldType.N, size=7, decimal=0)]
595608
596609The first field of a dbf file is always a 1-byte field called "DeletionFlag",
597610which indicates records that have been deleted but not removed. However,
@@ -919,8 +932,8 @@ You can also add attributes using keyword arguments where the keys are field nam
919932
920933
921934 >>> w = shapefile.Writer('shapefiles/test/dtype')
922- >>> w.field('FIRST_FLD','C','40' )
923- >>> w.field('SECOND_FLD','C','40' )
935+ >>> w.field('FIRST_FLD','C', 40 )
936+ >>> w.field('SECOND_FLD','C', 40 )
924937 >>> w.null()
925938 >>> w.null()
926939 >>> w.record('First', 'Line')
@@ -1375,7 +1388,7 @@ Shapefiles containing M-values can be examined in several ways:
13751388 >>> r = shapefile.Reader('shapefiles/test/linem')
13761389
13771390 >>> r.mbox # the lower and upper bound of M-values in the shapefile
1378- [ 0.0, 3.0]
1391+ MBox(mmin= 0.0, mmax= 3.0)
13791392
13801393 >>> r.shape(0).m # flat list of M-values
13811394 [0.0, None, 3.0, None, 0.0, None, None]
@@ -1408,7 +1421,7 @@ To examine a Z-type shapefile you can do:
14081421 >>> r = shapefile.Reader('shapefiles/test/linez')
14091422
14101423 >>> r.zbox # the lower and upper bound of Z-values in the shapefile
1411- [ 0.0, 22.0]
1424+ ZBox(zmin= 0.0, zmax= 22.0)
14121425
14131426 >>> r.shape(0).z # flat list of Z-values
14141427 [18.0, 20.0, 22.0, 0.0, 0.0, 0.0, 0.0, 15.0, 13.0, 14.0]
0 commit comments