Repository: rasterio/rasterio

Issue:
Python and Numpy version support for 1.2.0
Rasterio 1.2.0 supports Python 3.6-3.9 and Numpy 1.15+ per #1813 and https://numpy.org/neps/nep-0029-deprecation_policy.html. 

I propose we treat GDAL the same as Numpy, meaning support releases made within 24 months of our expected release date, with a minimum of 3 minor versions. That would mean supporting GDAL versions 3.2, 3.1, 3.0, 2.4, and 2.3 since 2.3.3 was released within 24 months of rasterio 1.2a1.

Make sure README, setup, pyproject, CI config is up to date.


Discussion:
Summarizing the wheel situation: Multibuild and Travis CI can cover Pythons 3.6-3.8 for manylinux and macos.

Travis CI and Python 3.9 + manylinux1 ARM64 just succeeded: https://travis-ci.com/github/rasterio/rasterio-wheels/jobs/459601126.

Travis CI isn't supporting Python 3.9 for macos yet, I see other projects switching to AppVeyor or GitHub Actions. I think we will eventually have to do so too, and pick up Windows along the way.

Resolving patch:
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -17,31 +17,16 @@
 import platform
 import pprint
 import shutil
-import subprocess
+from subprocess import check_output
 import sys
 
 from setuptools import setup
 from setuptools.extension import Extension
 
-
 logging.basicConfig(stream=sys.stderr, level=logging.INFO)
 log = logging.getLogger()
 
 
-def check_output(cmd):
-    # since subprocess.check_output doesn't exist in 2.6
-    # we wrap it here.
-    try:
-        out = subprocess.check_output(cmd)
-        return out.decode('utf')
-    except AttributeError:
-        # For some reasone check_output doesn't exist
-        # So fall back on Popen
-        p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
-        out, err = p.communicate()
-        return out
-
-
 def copy_data_tree(datadir, destdir):
     try:
         shutil.rmtree(destdir)
@@ -51,11 +36,11 @@ def copy_data_tree(datadir, destdir):
 
 
 # python -W all setup.py ...
-if 'all' in sys.warnoptions:
+if "all" in sys.warnoptions:
     log.level = logging.DEBUG
 
 # Parse the version from the rasterio module.
-with open('rasterio/__init__.py') as f:
+with open("rasterio/__init__.py") as f:
     for line in f:
         if line.find("__version__") >= 0:
             version = line.split("=")[1].strip()
@@ -63,7 +48,7 @@ def copy_data_tree(datadir, destdir):
             version = version.strip("'")
             continue
 
-with open('VERSION.txt', 'w') as f:
+with open("VERSION.txt", "w") as f:
     f.write(version)
 
 # Use Cython if available.
@@ -87,6 +72,7 @@ def copy_data_tree(datadir, destdir):
 
 try:
     import numpy as np
+
     include_dirs.append(np.get_include())
 except ImportError:
     sys.exit("ERROR: Numpy and its headers are required to run setup().")
@@ -95,7 +81,7 @@ def copy_data_tree(datadir, destdir):
     try:
         gdal_config = os.environ.get('GDAL_CONFIG', 'gdal-config')
         for i, flag in enumerate(("--cflags", "--libs", "--datadir", "--version")):
-            gdal_output[i] = check_output([gdal_config, flag]).strip()
+            gdal_output[i] = check_output([gdal_config, flag]).decode("utf-8").strip()
 
         for item in gdal_output[0].split():
             if item.startswith("-I"):
@@ -123,7 +109,6 @@ def copy_data_tree(datadir, destdir):
         else:
             log.warning("Failed to get options via gdal-config: %s", str(e))
 
-
     # Get GDAL API version from environment variable.
     if 'GDAL_VERSION' in os.environ:
         gdalversion = os.environ['GDAL_VERSION']
@@ -310,44 +295,38 @@ def run(self):
 else:
 
     ext_modules = [
-        Extension(
-            'rasterio._base', ['rasterio/_base.c'], **ext_options),
-        Extension(
-            'rasterio._io', ['rasterio/_io.c'], **ext_options),
-        Extension(
-            'rasterio._features', ['rasterio/_features.c'], **ext_options),
-        Extension(
-            'rasterio._env', ['rasterio/_env.c'], **ext_options),
-        Extension(
-            'rasterio._warp', ['rasterio/_warp.cpp'], **cpp_ext_options),
-        Extension(
-            'rasterio._fill', sdist_fill, **cpp_ext_options),
-        Extension(
-            'rasterio._err', ['rasterio/_err.c'], **ext_options),
-        Extension(
-            'rasterio._example', ['rasterio/_example.c'], **ext_options),
-        Extension(
-            'rasterio._crs', ['rasterio/_crs.c'], **ext_options),
-        Extension(
-            'rasterio.shutil', ['rasterio/shutil.c'], **ext_options),
-        Extension(
-            'rasterio._transform', ['rasterio/_transform.c'], **ext_options)]
+        Extension("rasterio._base", ["rasterio/_base.c"], **ext_options),
+        Extension("rasterio._io", ["rasterio/_io.c"], **ext_options),
+        Extension("rasterio._features", ["rasterio/_features.c"], **ext_options),
+        Extension("rasterio._env", ["rasterio/_env.c"], **ext_options),
+        Extension("rasterio._warp", ["rasterio/_warp.cpp"], **cpp_ext_options),
+        Extension("rasterio._fill", sdist_fill, **cpp_ext_options),
+        Extension("rasterio._err", ["rasterio/_err.c"], **ext_options),
+        Extension("rasterio._example", ["rasterio/_example.c"], **ext_options),
+        Extension("rasterio._crs", ["rasterio/_crs.c"], **ext_options),
+        Extension("rasterio.shutil", ["rasterio/shutil.c"], **ext_options),
+        Extension("rasterio._transform", ["rasterio/_transform.c"], **ext_options),
+    ]
 
     # Copy the GDAL version-specific shim module to _shim.pyx.
     if gdal_major_version == 3 and gdal_minor_version >= 0:
         ext_modules.append(
-            Extension('rasterio._shim', ['rasterio/_shim30.c'], **ext_options))
+            Extension("rasterio._shim", ["rasterio/_shim30.c"], **ext_options)
+        )
     elif gdal_major_version == 2 and gdal_minor_version >= 1:
         ext_modules.append(
-            Extension('rasterio._shim', ['rasterio/_shim21.c'], **ext_options))
+            Extension("rasterio._shim", ["rasterio/_shim21.c"], **ext_options)
+        )
     elif gdal_major_version == 2 and gdal_minor_version == 0:
         ext_modules.append(
-            Extension('rasterio._shim', ['rasterio/_shim20.c'], **ext_options))
+            Extension("rasterio._shim", ["rasterio/_shim20.c"], **ext_options)
+        )
     elif gdal_major_version == 1:
         ext_modules.append(
-            Extension('rasterio._shim', ['rasterio/_shim1.c'], **ext_options))
+            Extension("rasterio._shim", ["rasterio/_shim1.c"], **ext_options)
+        )
 
-with open('README.rst') as f:
+with open("README.rst") as f:
     readme = f.read()
 
 # Runtime requirements.
@@ -360,7 +339,6 @@ def run(self):
     "numpy",
     "snuggs>=1.4.1",
     "click-plugins",
-    'enum34 ; python_version < "3.4"',
 ]
 
 extra_reqs = {
@@ -375,43 +353,42 @@ def run(self):
         "pytest-cov>=2.2.0",
         "pytest>=2.8.2",
         "shapely",
-        'futures;python_version<"3.2"',
-        'mock;python_version<"3.2"',
     ],
 }
 
 # Add all extra requirements
-extra_reqs['all'] = list(set(itertools.chain(*extra_reqs.values())))
+extra_reqs["all"] = list(set(itertools.chain(*extra_reqs.values())))
 
 setup_args = dict(
-    cmdclass={'sdist': sdist_multi_gdal},
-    name='rasterio',
+    cmdclass={"sdist": sdist_multi_gdal},
+    name="rasterio",
     version=version,
     description="Fast and direct raster I/O for use with Numpy and SciPy",
     long_description=readme,
     classifiers=[
-        'Development Status :: 4 - Beta',
-        'Intended Audience :: Developers',
-        'Intended Audience :: Information Technology',
-        'Intended Audience :: Science/Research',
-        'License :: OSI Approved :: BSD License',
-        'Programming Language :: C',
-        'Programming Language :: Cython',
-        'Programming Language :: Python :: 3.6',
-        'Programming Language :: Python :: 3.7',
-        'Programming Language :: Python :: 3.8',
-        'Programming Language :: Python :: 3.9',
-        'Programming Language :: Python :: 3',
-        'Topic :: Multimedia :: Graphics :: Graphics Conversion',
-        'Topic :: Scientific/Engineering :: GIS'],
-    keywords='raster gdal',
-    author='Sean Gillies',
-    author_email='sean@mapbox.com',
-    url='https://github.com/mapbox/rasterio',
-    license='BSD',
-    package_dir={'': '.'},
-    packages=['rasterio', 'rasterio.rio'],
-    entry_points='''
+        "Development Status :: 5 - Production",
+        "Intended Audience :: Developers",
+        "Intended Audience :: Information Technology",
+        "Intended Audience :: Science/Research",
+        "License :: OSI Approved :: BSD License",
+        "Programming Language :: C",
+        "Programming Language :: Cython",
+        "Programming Language :: Python :: 3.6",
+        "Programming Language :: Python :: 3.7",
+        "Programming Language :: Python :: 3.8",
+        "Programming Language :: Python :: 3.9",
+        "Programming Language :: Python :: 3",
+        "Topic :: Multimedia :: Graphics :: Graphics Conversion",
+        "Topic :: Scientific/Engineering :: GIS",
+    ],
+    keywords="raster gdal",
+    author="Sean Gillies",
+    author_email="sean@mapbox.com",
+    url="https://github.com/mapbox/rasterio",
+    license="BSD",
+    package_dir={"": "."},
+    packages=["rasterio", "rasterio.rio"],
+    entry_points="""
         [console_scripts]
         rio=rasterio.rio.main:main_group
 
@@ -436,7 +413,7 @@ def run(self):
         stack=rasterio.rio.stack:stack
         transform=rasterio.rio.transform:transform
         warp=rasterio.rio.warp:warp
-    ''',
+    """,
     include_package_data=True,
     ext_modules=ext_modules,
     zip_safe=False,


Test patch: