From 625c6c198ae82d0130c085adcd5868edda3c6f12 Mon Sep 17 00:00:00 2001 From: Kurt Schwehr Date: Sat, 24 Aug 2024 06:59:42 -0700 Subject: [PATCH 1/6] Drop python 2 and make python 3.8 be the minimum. Python 3.7 is end-of-life: https://devguide.python.org/versions/ --- .github/workflows/build.yml | 2 +- setup.cfg | 3 +- shapefile.py | 137 +++++++++++------------------------- test_shapefile.py | 11 +-- 4 files changed, 45 insertions(+), 108 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1f46613..df83f0d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["2.7.18", "3.5.10", "3.6.15", "3.7.17", "3.8.18", "3.9.18", "3.10.13", "3.11.7", "3.12.1", "3.13.0a2"] + python-version: ["3.8.18", "3.9.18", "3.10.13", "3.11.7", "3.12.1", "3.13.0a2"] runs-on: ubuntu-latest container: diff --git a/setup.cfg b/setup.cfg index 906abd3..f5e113c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,7 +16,6 @@ keywords = gis, geospatial, geographic, shapefile, shapefiles classifiers = Development Status :: 5 - Production/Stable Programming Language :: Python - Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 Topic :: Scientific/Engineering :: GIS Topic :: Software Development :: Libraries @@ -24,7 +23,7 @@ classifiers = [options] py_modules = shapefile -python_requires = >=2.7 +python_requires = >=3.8 [bdist_wheel] universal=1 diff --git a/shapefile.py b/shapefile.py index 3b4e529..9c7c49f 100644 --- a/shapefile.py +++ b/shapefile.py @@ -74,112 +74,55 @@ 5: 'RING'} -# Python 2-3 handling +xrange = range +izip = zip -PYTHON3 = sys.version_info[0] == 3 - -if PYTHON3: - xrange = range - izip = zip - - from urllib.parse import urlparse, urlunparse - from urllib.error import HTTPError - from urllib.request import urlopen, Request +from urllib.parse import urlparse, urlunparse +from urllib.error import HTTPError +from urllib.request import urlopen, Request -else: - from itertools import izip - - from urlparse import urlparse, urlunparse - from urllib2 import HTTPError - from urllib2 import urlopen, Request - - # Helpers MISSING = [None,''] NODATA = -10e38 # as per the ESRI shapefile spec, only used for m-values. -if PYTHON3: - def b(v, encoding='utf-8', encodingErrors='strict'): - if isinstance(v, str): - # For python 3 encode str to bytes. - return v.encode(encoding, encodingErrors) - elif isinstance(v, bytes): - # Already bytes. - return v - elif v is None: - # Since we're dealing with text, interpret None as "" - return b"" - else: - # Force string representation. - return str(v).encode(encoding, encodingErrors) - - def u(v, encoding='utf-8', encodingErrors='strict'): - if isinstance(v, bytes): - # For python 3 decode bytes to str. - return v.decode(encoding, encodingErrors) - elif isinstance(v, str): - # Already str. - return v - elif v is None: - # Since we're dealing with text, interpret None as "" - return "" - else: - # Force string representation. - return bytes(v).decode(encoding, encodingErrors) - - def is_string(v): - return isinstance(v, str) - -else: - def b(v, encoding='utf-8', encodingErrors='strict'): - if isinstance(v, unicode): - # For python 2 encode unicode to bytes. - return v.encode(encoding, encodingErrors) - elif isinstance(v, bytes): - # Already bytes. - return v - elif v is None: - # Since we're dealing with text, interpret None as "" - return "" - else: - # Force string representation. - return unicode(v).encode(encoding, encodingErrors) - - def u(v, encoding='utf-8', encodingErrors='strict'): - if isinstance(v, bytes): - # For python 2 decode bytes to unicode. - return v.decode(encoding, encodingErrors) - elif isinstance(v, unicode): - # Already unicode. - return v - elif v is None: - # Since we're dealing with text, interpret None as "" - return u"" - else: - # Force string representation. - return bytes(v).decode(encoding, encodingErrors) +def b(v, encoding='utf-8', encodingErrors='strict'): + if isinstance(v, str): + # For python 3 encode str to bytes. + return v.encode(encoding, encodingErrors) + elif isinstance(v, bytes): + # Already bytes. + return v + elif v is None: + # Since we're dealing with text, interpret None as "" + return b"" + else: + # Force string representation. + return str(v).encode(encoding, encodingErrors) + +def u(v, encoding='utf-8', encodingErrors='strict'): + if isinstance(v, bytes): + # For python 3 decode bytes to str. + return v.decode(encoding, encodingErrors) + elif isinstance(v, str): + # Already str. + return v + elif v is None: + # Since we're dealing with text, interpret None as "" + return "" + else: + # Force string representation. + return bytes(v).decode(encoding, encodingErrors) - def is_string(v): - return isinstance(v, basestring) +def is_string(v): + return isinstance(v, str) -if sys.version_info[0:2] >= (3, 6): - def pathlike_obj(path): - if isinstance(path, os.PathLike): - return os.fsdecode(path) - else: - return path -else: - def pathlike_obj(path): - if is_string(path): - return path - elif hasattr(path, "__fspath__"): - return path.__fspath__() - else: - try: - return str(path) - except: - return path + +def pathlike_obj(path): + if isinstance(path, os.PathLike): + return os.fsdecode(path) + else: + return path # Begin diff --git a/test_shapefile.py b/test_shapefile.py index 774e59c..501c215 100644 --- a/test_shapefile.py +++ b/test_shapefile.py @@ -2,18 +2,13 @@ This module tests the functionality of shapefile.py. """ # std lib imports +import datetime +import json import os.path -import sys -if sys.version_info.major == 3: - from pathlib import Path +from pathlib import Path # third party imports import pytest -import json -import datetime -if sys.version_info.major == 2: - # required by pytest for python <36 - from pathlib2 import Path # our imports import shapefile From 4b4743ab9e319afee297bc3b5eb1f1a6723dac27 Mon Sep 17 00:00:00 2001 From: Kurt Schwehr Date: Sat, 24 Aug 2024 07:17:21 -0700 Subject: [PATCH 2/6] shapefile.py: Drop inheriting from object. https://www.python.org/doc/newstyle/ > New-style classes has been integrated into Python 2.7 and old-style classes has been removed in Python 3. --- shapefile.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/shapefile.py b/shapefile.py index 9c7c49f..a9c3922 100644 --- a/shapefile.py +++ b/shapefile.py @@ -395,7 +395,7 @@ def organize_polygon_rings(rings, return_errors=None): polys = [[ext] for ext in exteriors] return polys -class Shape(object): +class Shape: def __init__(self, shapeType=NULL, points=None, parts=None, partTypes=None, oid=None): """Stores the geometry of the different shape types specified in the Shapefile spec. Shape types are @@ -768,7 +768,7 @@ def __dir__(self): fnames = list(self.__field_positions.keys()) # plus field names (random order if Python version < 3.6) return default + fnames -class ShapeRecord(object): +class ShapeRecord: """A ShapeRecord object containing a shape along with its attributes. Provides the GeoJSON __geo_interface__ to return a Feature dictionary.""" def __init__(self, shape=None, record=None): @@ -853,7 +853,7 @@ class ShapefileException(Exception): # msg = '\n'.join(messages) # logger.warning(msg) -class Reader(object): +class Reader: """Reads the three files of a shapefile as a unit or separately. If one of the three files (.shp, .shx, .dbf) is missing no exception is thrown until you try @@ -1735,7 +1735,7 @@ def iterShapeRecords(self, fields=None, bbox=None): yield ShapeRecord(shape=shape, record=record) -class Writer(object): +class Writer: """Provides write support for ESRI Shapefiles.""" def __init__(self, target=None, shapeType=None, autoBalance=False, **kwargs): self.target = target From d0259ae95c4951fb8f27e4de2e6c4bd252f4443a Mon Sep 17 00:00:00 2001 From: Kurt Schwehr Date: Sat, 24 Aug 2024 07:39:48 -0700 Subject: [PATCH 3/6] shapefile.py: Minor cleanup of module comment and order includes. --- shapefile.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/shapefile.py b/shapefile.py index a9c3922..4ecbde7 100644 --- a/shapefile.py +++ b/shapefile.py @@ -3,22 +3,26 @@ Provides read and write support for ESRI Shapefiles. authors: jlawheadgeospatialpython.com maintainer: karim.bahgat.norwaygmail.com -Compatible with Python versions 2.7-3.x +Compatible with Python versions >= 3.8 """ __version__ = "2.3.1" -from struct import pack, unpack, calcsize, error, Struct +import array +from datetime import date +import io +import logging import os +from struct import pack, unpack, calcsize, error, Struct import sys -import time -import array import tempfile -import logging -import io -from datetime import date +import time import zipfile +from urllib.error import HTTPError +from urllib.parse import urlparse, urlunparse +from urllib.request import urlopen, Request + # Create named logger logger = logging.getLogger(__name__) @@ -76,10 +80,6 @@ xrange = range izip = zip - -from urllib.parse import urlparse, urlunparse -from urllib.error import HTTPError -from urllib.request import urlopen, Request # Helpers From e364fa05a999660efa92125f0e78d631c7a4bb45 Mon Sep 17 00:00:00 2001 From: Kurt Schwehr Date: Sat, 24 Aug 2024 07:44:06 -0700 Subject: [PATCH 4/6] build.yml: Allow workflow on all branches when pushing. This should let me see the workflow run in my work on my fork. --- .github/workflows/build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index df83f0d..5ed591f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,7 +5,6 @@ name: build on: push: - branches: [ master ] pull_request: branches: [ master ] workflow_dispatch: From 0146bc6aefab2b3591f5ce2a118878430f6cf69c Mon Sep 17 00:00:00 2001 From: Kurt Schwehr Date: Sat, 24 Aug 2024 07:51:43 -0700 Subject: [PATCH 5/6] build.yml: Do not specify the patch level of python versions. - Still specify the exact python 3.13 as it is not yet released. - Updated to release candidate 1. 3.13 release is expected in October 2024. - Also fix the more information link. The old one one is dead. --- .github/workflows/build.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5ed591f..f808147 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,5 +1,6 @@ # This workflow will install Python dependencies, run tests and lint with a variety of Python versions -# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions +# For more information see: +# https://docs.github.com/en/actions/use-cases-and-examples/building-and-testing/building-and-testing-python name: build @@ -15,7 +16,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.8.18", "3.9.18", "3.10.13", "3.11.7", "3.12.1", "3.13.0a2"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13.0rc1"] runs-on: ubuntu-latest container: From 50d08ec3a147b28a139c12a6d2754ceba63b54ef Mon Sep 17 00:00:00 2001 From: Kurt Schwehr Date: Sat, 24 Aug 2024 08:03:04 -0700 Subject: [PATCH 6/6] build.yml: Used checkout v4 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f808147..242d898 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,7 +23,7 @@ jobs: image: python:${{ matrix.python-version }}-slim steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install dependencies run: | python -m pip install --upgrade pip