Skip to content

Drop python 2 and make python 3.8 be the minimum. #275

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# 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

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
workflow_dispatch:
Expand All @@ -16,14 +16,14 @@ 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", "3.9", "3.10", "3.11", "3.12", "3.13.0rc1"]

runs-on: ubuntu-latest
container:
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
Expand Down
3 changes: 1 addition & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,14 @@ 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
Topic :: Software Development :: Libraries :: Python Modules

[options]
py_modules = shapefile
python_requires = >=2.7
python_requires = >=3.8

[bdist_wheel]
universal=1
161 changes: 52 additions & 109 deletions shapefile.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,26 @@
Provides read and write support for ESRI Shapefiles.
authors: jlawhead<at>geospatialpython.com
maintainer: karim.bahgat.norway<at>gmail.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__)

Expand Down Expand Up @@ -74,112 +78,51 @@
5: 'RING'}


# Python 2-3 handling

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
xrange = range
izip = zip

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
Expand Down Expand Up @@ -452,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
Expand Down Expand Up @@ -825,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):
Expand Down Expand Up @@ -910,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
Expand Down Expand Up @@ -1792,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
Expand Down
11 changes: 3 additions & 8 deletions test_shapefile.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down