diff --git a/README.rst b/README.rst index 87571fa1d..c51ec86e8 100644 --- a/README.rst +++ b/README.rst @@ -300,8 +300,18 @@ the ``GDAL_VERSION`` environment variable (e.g. ``set GDAL_VERSION=2.1``). $ python setup.py build_ext -I -lgdal_i -L install --gdalversion 2.1 -Note: The GDAL DLL (``gdal111.dll`` or similar) and gdal-data directory need to -be in your Windows PATH otherwise Fiona will fail to work. + +Note: The following environment variables needs to be set so that Fiona works correctly: + +* The directory containing the GDAL DLL (``gdal304.dll`` or similar) needs to be in your + Windows ``PATH`` (e.g. ``C:\gdal\bin``). Only directories containing ``gdal`` in + their names are considered. Alternatively, the environment variable ``GDAL_HOME`` can + be used (e.g. ``C:\gdal``). A ``*\*gdal*\*`` directory in ``PATH`` has priority over + ``GDAL_HOME``. +* The gdal-data directory needs to be in your Windows ``PATH`` or the environment variable + ``GDAL_DATA`` must be set (e.g. ``C:\gdal\bin\gdal-data``). +* The environment variable ``PROJ_LIB`` must be set to the proj library directory (e.g. + ``C:\gdal\bin\proj6\share``) The `Appveyor CI build `__ uses the GISInternals GDAL binaries to build Fiona. This produces a binary wheel diff --git a/appveyor.yml b/appveyor.yml index 6ffd51301..f4788f2ed 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -38,13 +38,12 @@ environment: GIS_INTERNALS: "release-1911-x64-gdal-2-4-2-mapserver-7-4-0.zip" GIS_INTERNALS_LIBS: "release-1911-x64-gdal-2-4-2-mapserver-7-4-0-libs.zip" - # Use daily stable branch of gdal 3.0.x series until release with gdal >= 3.0.1 is released - PYTHON: "C:\\Python36-x64" PYTHON_VERSION: "3.6" PYTHON_ARCH: "64" - GDAL_VERSION: "3.0.0" - GIS_INTERNALS: "release-1911-x64-gdal-3-0-mapserver-7-4.zip" - GIS_INTERNALS_LIBS: "release-1911-x64-gdal-3-0-mapserver-7-4-libs.zip" + GDAL_VERSION: "3.0.4" + GIS_INTERNALS: "release-1911-x64-gdal-3-0-4-mapserver-7-4-3.zip" + GIS_INTERNALS_LIBS: "release-1911-x64-gdal-3-0-4-mapserver-7-4-3-libs.zip" PROJ_LIB: "C:\\gdal\\bin\\proj6\\share" - PYTHON: "C:\\Python37-x64" @@ -57,9 +56,9 @@ environment: - PYTHON: "C:\\Python37-x64" PYTHON_VERSION: "3.7" PYTHON_ARCH: "64" - GDAL_VERSION: "3.0.0" - GIS_INTERNALS: "release-1911-x64-gdal-3-0-mapserver-7-4.zip" - GIS_INTERNALS_LIBS: "release-1911-x64-gdal-3-0-mapserver-7-4-libs.zip" + GDAL_VERSION: "3.0.4" + GIS_INTERNALS: "release-1911-x64-gdal-3-0-4-mapserver-7-4-3.zip" + GIS_INTERNALS_LIBS: "release-1911-x64-gdal-3-0-4-mapserver-7-4-3-libs.zip" PROJ_LIB: "C:\\gdal\\bin\\proj6\\share" - PYTHON: "C:\\Python37-x64" @@ -70,6 +69,14 @@ environment: GIS_INTERNALS_LIBS: "release-1911-x64-gdal-3-0-mapserver-7-4-libs.zip" PROJ_LIB: "C:\\gdal\\bin\\proj6\\share" + - PYTHON: "C:\\Python38-x64" + PYTHON_VERSION: "3.8" + PYTHON_ARCH: "64" + GDAL_VERSION: "3.0.4" + GIS_INTERNALS: "release-1911-x64-gdal-3-0-4-mapserver-7-4-3.zip" + GIS_INTERNALS_LIBS: "release-1911-x64-gdal-3-0-4-mapserver-7-4-3-libs.zip" + PROJ_LIB: "C:\\gdal\\bin\\proj6\\share" + - PYTHON: "C:\\Python38-x64" PYTHON_VERSION: "3.8" PYTHON_ARCH: "64" diff --git a/fiona/__init__.py b/fiona/__init__.py index ecef54c2b..39f547db2 100644 --- a/fiona/__init__.py +++ b/fiona/__init__.py @@ -67,7 +67,6 @@ import os import sys import warnings - from six import string_types from collections import OrderedDict @@ -82,22 +81,24 @@ class Path: libdir = os.path.join(os.path.dirname(__file__), ".libs") os.environ["PATH"] = os.environ["PATH"] + ";" + libdir +import fiona._loading +with fiona._loading.add_gdal_dll_directories(): + from fiona._env import ( + calc_gdal_version_num, get_gdal_version_num, get_gdal_release_name, + get_gdal_version_tuple, driver_count) + from fiona.ogrext import _bounds, _listlayers, FIELD_TYPES_MAP, _remove, _remove_layer from fiona.collection import BytesCollection, Collection from fiona.drvsupport import supported_drivers from fiona.env import ensure_env_with_credentials, Env from fiona.errors import FionaDeprecationWarning -from fiona._env import driver_count -from fiona._env import ( - calc_gdal_version_num, get_gdal_version_num, get_gdal_release_name, - get_gdal_version_tuple) from fiona.io import MemoryFile -from fiona.ogrext import _bounds, _listlayers, FIELD_TYPES_MAP, _remove, _remove_layer from fiona.path import ParsedPath, parse_path, vsi_path from fiona.vfs import parse_paths as vfs_parse_paths # These modules are imported by fiona.ogrext, but are also import here to # help tools like cx_Freeze find them automatically -from fiona import _geometry, _err, rfc3339 +with fiona._loading.add_gdal_dll_directories(): + from fiona import _geometry, _err, rfc3339 import uuid diff --git a/fiona/_loading.py b/fiona/_loading.py new file mode 100644 index 000000000..9e6cf778a --- /dev/null +++ b/fiona/_loading.py @@ -0,0 +1,75 @@ +import glob +import os +import logging +import contextlib +import platform +import sys + +log = logging.getLogger(__name__) +log.addHandler(logging.NullHandler()) + +# With Python >= 3.8 on Windows directories in PATH are not automatically +# searched for DLL dependencies and must be added manually with +# os.add_dll_directory. +# see https://github.com/Toblerity/Fiona/issues/851 + +dll_directories = [] + + +def directory_contains_gdal_dll(path): + """ Checks if a directory contains a gdal dll """ + return len(glob.glob(os.path.join(path, "gdal*.dll"))) > 0 + + +def search_gdal_dll_directories(): + """ Search for gdal dlls + + Checks if a gdal dll is present in PATH directory. + If no directory is found, GDAL_HOME is used if available. + """ + + # Parse PATH for gdal/bin + for path in os.getenv('PATH', '').split(os.pathsep): + + if directory_contains_gdal_dll(path): + dll_directories.append(path) + + # Use GDAL_HOME if present + if len(dll_directories) == 0: + + gdal_home = os.getenv('GDAL_HOME', None) + + if gdal_home is not None and os.path.exists(gdal_home): + + if directory_contains_gdal_dll(gdal_home): + dll_directories.append(gdal_home) + elif directory_contains_gdal_dll(os.path.join(gdal_home, "bin")): + dll_directories.append(os.path.join(gdal_home, "bin")) + + elif gdal_home is not None and not os.path.exists(gdal_home): + log.warning("GDAL_HOME directory ({}) does not exist.".format(gdal_home)) + + if len(dll_directories) == 0: + log.warning("No dll directories found.") + + +if platform.system() == 'Windows' and (3, 8) <= sys.version_info: + + # if loading of extension modules fails, search for gdal dll directories + try: + import fiona.ogrext + except ImportError as e: + search_gdal_dll_directories() + + +@contextlib.contextmanager +def add_gdal_dll_directories(): + + dll_dirs = [] + for dll_directory in dll_directories: + dll_dirs.append(os.add_dll_directory(dll_directory)) + try: + yield None + finally: + for dll_dir in dll_dirs: + dll_dir.close()