From a15ddb46c88d70563f9eac582d866e1affca445d Mon Sep 17 00:00:00 2001 From: Sean Gillies Date: Fri, 1 Mar 2024 16:15:11 -0700 Subject: [PATCH] Require Python 3.9+ (#1334) * Require Python 3.9+ Drop older versions from test matrix, update GDAL versions. * Remove unused c++ cdef class and directive * Add numpy dependency for conda builds * Second try at numpy spec * Again with the version spec * Fix broken YAML * Address weird Windows path parsing * Adjust a few tests * Moar windows/posix path bs * import Path * Assert that drive letter remains and ! is gone in win32 path test * Stop using as_posix in _path.py * Fix archive/path parsing bug * Update RTD build environment, adding numpy * Remove unused imports in tests. --- .github/workflows/tests.yml | 16 +++++++--------- CHANGES.txt | 8 ++++++++ README.rst | 2 +- environment.yml | 1 + fiona/_path.py | 32 +++++++++++++++----------------- fiona/_vsiopener.pyx | 13 ------------- pyproject.toml | 5 ++--- tests/test__path.py | 23 +++++++++++++++++++++++ tests/test_layer.py | 2 ++ 9 files changed, 59 insertions(+), 43 deletions(-) create mode 100644 tests/test__path.py diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index acc240df4..96003e3e0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -39,18 +39,16 @@ jobs: fail-fast: false matrix: include: - - python-version: '3.7' - gdal-version: '3.4.3' - - python-version: '3.8' + - python-version: '3.9' gdal-version: '3.4.3' - python-version: '3.9' gdal-version: '3.5.3' - python-version: '3.10' gdal-version: '3.6.4' - python-version: '3.11' - gdal-version: '3.7.1' + gdal-version: '3.7.3' - python-version: '3.12' - gdal-version: '3.7.1' + gdal-version: '3.8.3' steps: - uses: actions/checkout@v4 @@ -94,9 +92,9 @@ jobs: matrix: include: - os: macos-latest - python-version: '3.10' + python-version: '3.11' - os: windows-latest - python-version: '3.10' + python-version: '3.11' steps: - uses: actions/checkout@v4 @@ -111,7 +109,7 @@ jobs: run: | conda config --prepend channels conda-forge conda config --set channel_priority strict - conda create -n test python=${{ matrix.python-version }} libgdal geos=3.10.3 cython=3 + conda create -n test python=${{ matrix.python-version }} libgdal geos=3.10.3 cython=3 "numpy>=1.25,<2" source activate test python -m pip install -e . || python -m pip install -e . python -m pip install -r requirements-dev.txt @@ -122,7 +120,7 @@ jobs: run: | conda config --prepend channels conda-forge conda config --set channel_priority strict - conda create -n test python=${{ matrix.python-version }} libgdal geos=3.10.3 cython=3 + conda create -n test python=${{ matrix.python-version }} libgdal geos=3.10.3 cython=3 "numpy>=1.25,<2" source activate test GDAL_VERSION="3.5" python setup.py build_ext -I"C:\\Miniconda\\envs\\test\\Library\\include" -lgdal_i -L"C:\\Miniconda\\envs\\test\\Library\\lib" install python -m pip install -r requirements-dev.txt diff --git a/CHANGES.txt b/CHANGES.txt index f0c2f0280..c18b0dd3e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -3,6 +3,14 @@ Changes All issue numbers are relative to https://github.com/Toblerity/Fiona/issues. +Next +---- + +Python version: + +Fiona 1.10 will be compatible with Numpy versions 1 and 2 and will require +Python version 3.9 or higher. + 1.9.5 (2023-10-11) ------------------ diff --git a/README.rst b/README.rst index e75fc8394..76c749048 100644 --- a/README.rst +++ b/README.rst @@ -47,7 +47,7 @@ environment variable. Many users find Anaconda and conda-forge a good way to install Fiona and get access to more optional format drivers (like GML). -Fiona 2.0 requires Python 3.7 or higher and GDAL 3.2 or higher. +Fiona 1.10 requires Python 3.9 or higher and GDAL 3.4 or higher. Python Usage ============ diff --git a/environment.yml b/environment.yml index 4b5348b50..ec05a395b 100644 --- a/environment.yml +++ b/environment.yml @@ -7,6 +7,7 @@ dependencies: - python=3.9.* - libgdal=3.4.* - cython=3 +- numpy>=1.25,<2 - sphinx-click - sphinx-rtd-theme - pip: diff --git a/fiona/_path.py b/fiona/_path.py index 045a62a09..f25842f93 100644 --- a/fiona/_path.py +++ b/fiona/_path.py @@ -5,6 +5,7 @@ """ +import os import pathlib import re import sys @@ -65,7 +66,14 @@ class _ParsedPath(_Path): @classmethod def from_uri(cls, uri): parts = urlparse(uri) - path = pathlib.Path(parts.path).as_posix() if parts.path else parts.path + if sys.platform == "win32" and re.match(r"^[a-zA-Z]\:", parts.netloc): + parsed_path = f"{parts.netloc}{parts.path}" + parsed_netloc = None + else: + parsed_path = parts.path + parsed_netloc = parts.netloc + + path = parsed_path scheme = parts.scheme or None if parts.query: @@ -78,11 +86,11 @@ def from_uri(cls, uri): else: archive = None - if parts.scheme and parts.netloc: + if scheme and parsed_netloc: if archive: - archive = parts.netloc + archive + archive = parsed_netloc + archive else: - path = parts.netloc + path + path = parsed_netloc + path return _ParsedPath(path, archive, scheme) @@ -144,31 +152,21 @@ def _parse_path(path): """ if isinstance(path, _Path): return path - - elif pathlib and isinstance(path, pathlib.PurePath): - return _ParsedPath(path.as_posix(), None, None) - + elif isinstance(path, pathlib.PurePath): + return _ParsedPath(os.fspath(path), None, None) elif isinstance(path, str): - if sys.platform == "win32" and re.match(r"^[a-zA-Z]\:", path): - if pathlib: - return _ParsedPath(pathlib.Path(path).as_posix(), None, None) - else: - return _UnparsedPath(path) - + return _ParsedPath(path, None, None) elif path.startswith('/vsi'): return _UnparsedPath(path) - else: parts = urlparse(path) - else: raise PathError("invalid path '{!r}'".format(path)) # if the scheme is not one of Rasterio's supported schemes, we # return an UnparsedPath. if parts.scheme: - if all(p in SCHEMES for p in parts.scheme.split('+')): return _ParsedPath.from_uri(path) diff --git a/fiona/_vsiopener.pyx b/fiona/_vsiopener.pyx index c94be1f24..9bc6966ff 100644 --- a/fiona/_vsiopener.pyx +++ b/fiona/_vsiopener.pyx @@ -1,12 +1,8 @@ -# cython: language_level=3, boundscheck=False -# distutils: language = c++ """Bridge between Python file openers and GDAL VSI. Based on _filepath.pyx. """ -# include "gdal.pxi" - import contextlib from contextvars import ContextVar import logging @@ -25,15 +21,6 @@ from fiona.errors import OpenerRegistrationError log = logging.getLogger(__name__) - -# NOTE: This has to be defined outside of gdal.pxi or other C extensions will -# try to compile C++ only code included in this header. -cdef extern from "cpl_vsi_virtual.h": - cdef cppclass VSIFileManager: - @staticmethod - void* GetHandler(const char*) - - # Prefix for all in-memory paths used by GDAL's VSI system # Except for errors and log messages this shouldn't really be seen by the user cdef str PREFIX = "/vsipyopener/" diff --git a/pyproject.toml b/pyproject.toml index 7f9c8359f..9561c640f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,14 +20,13 @@ classifiers = [ "Intended Audience :: Science/Research", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Topic :: Scientific/Engineering :: GIS", ] -requires-python = ">=3.7" +requires-python = ">=3.9" dependencies = [ "attrs>=19.2.0", "certifi", diff --git a/tests/test__path.py b/tests/test__path.py new file mode 100644 index 000000000..bafe859fb --- /dev/null +++ b/tests/test__path.py @@ -0,0 +1,23 @@ +"""_path tests.""" + +import sys + +from fiona._path import _parse_path, _vsi_path + + +def test_parse_zip_windows(monkeypatch): + """Parse a zip+ Windows path.""" + monkeypatch.setattr(sys, "platform", "win32") + path = _parse_path("zip://D:\\a\\Fiona\\Fiona\\tests\\data\\coutwildrnp.zip!coutwildrnp.shp") + vsi_path = _vsi_path(path) + assert vsi_path.startswith("/vsizip/D") + assert vsi_path.endswith("coutwildrnp.zip/coutwildrnp.shp") + + +def test_parse_zip_windows(monkeypatch): + """Parse a tar+ Windows path.""" + monkeypatch.setattr(sys, "platform", "win32") + path = _parse_path("tar://D:\\a\\Fiona\\Fiona\\tests\\data\\coutwildrnp.tar!testing/coutwildrnp.shp") + vsi_path = _vsi_path(path) + assert vsi_path.startswith("/vsitar/D") + assert vsi_path.endswith("coutwildrnp.tar/testing/coutwildrnp.shp") diff --git a/tests/test_layer.py b/tests/test_layer.py index 9e6b1196e..d80aa01d9 100644 --- a/tests/test_layer.py +++ b/tests/test_layer.py @@ -1,3 +1,5 @@ +"""Layer tests.""" + import pytest import fiona