[Git][debian-gis-team/fiona][upstream] New upstream version 1.8.1
Bas Couwenberg
gitlab at salsa.debian.org
Fri Nov 16 06:38:59 GMT 2018
Bas Couwenberg pushed to branch upstream at Debian GIS Project / fiona
Commits:
652d6524 by Bas Couwenberg at 2018-11-16T05:54:58Z
New upstream version 1.8.1
- - - - -
27 changed files:
- CHANGES.txt
- fiona/__init__.py
- fiona/_drivers.pyx
- fiona/_env.pyx
- fiona/_err.pyx
- fiona/_geometry.pxd
- fiona/_geometry.pyx
- fiona/_shim1.pxd
- fiona/_shim2.pxd
- fiona/_shim2.pyx
- fiona/_shim22.pxd
- fiona/_shim22.pyx
- fiona/collection.py
- fiona/env.py
- fiona/ogrext.pyx
- fiona/ogrext1.pxd
- fiona/ogrext2.pxd
- setup.py
- tests/conftest.py
- + tests/test__env.py
- tests/test_bigint.py
- tests/test_collection.py
- + tests/test_compound_crs.py
- tests/test_datetime.py
- tests/test_env.py
- tests/test_schema.py
- tests/test_unicode.py
Changes:
=====================================
CHANGES.txt
=====================================
@@ -3,6 +3,25 @@ Changes
All issue numbers are relative to https://github.com/Toblerity/Fiona/issues.
+1.8.1 (2018-11-15)
+------------------
+
+Bug fixes:
+
+- Add checks around OSRGetAuthorityName and OSRGetAuthorityCode calls that will
+ log problems with looking up these items.
+- Opened data sources are now released before we raise exceptions in
+ WritingSession.start (#676). This fixes an issue with locked files on
+ Windows.
+- We now ensure that an Env instance exists when getting the crs or crs_wkt
+ properties of a Collection (#673, #690). Otherwise, required GDAL and PROJ
+ data files included in Fiona wheels can not be found.
+- GDAL and PROJ data search has been refactored to improve testability (#678).
+- In the project's Cython code, void* pointers have been replaced with proper
+ GDAL types (#672).
+- Pervasive warning level log messages about ENCODING creation options (#668)
+ have been eliminated.
+
1.8.0 (2018-10-31)
------------------
@@ -74,8 +93,8 @@ Refactoring:
Deprecations:
-- The ``rasterio.drivers()`` context manager is officially deprecated. All
- users should switch to ``rasterio.Env()``, which registers format drivers and
+- The ``fiona.drivers()`` context manager is officially deprecated. All
+ users should switch to ``fiona.Env()``, which registers format drivers and
manages GDAL configuration in a reversible manner.
Bug fixes:
=====================================
fiona/__init__.py
=====================================
@@ -85,12 +85,12 @@ 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.compat import OrderedDict
from fiona.io import MemoryFile
from fiona.ogrext import _bounds, _listlayers, FIELD_TYPES_MAP, _remove, _remove_layer
-from fiona.ogrext import (
- calc_gdal_version_num, get_gdal_version_num, get_gdal_release_name,
- get_gdal_version_tuple)
from fiona.path import ParsedPath, parse_path, vsi_path
from fiona.vfs import parse_paths as vfs_parse_paths
@@ -101,12 +101,13 @@ import uuid
__all__ = ['bounds', 'listlayers', 'open', 'prop_type', 'prop_width']
-__version__ = "1.8.0"
+__version__ = "1.8.1"
__gdal_version__ = get_gdal_release_name()
gdal_version = get_gdal_version_tuple()
log = logging.getLogger(__name__)
+log.addHandler(logging.NullHandler())
@ensure_env_with_credentials
=====================================
fiona/_drivers.pyx
=====================================
@@ -18,7 +18,8 @@ cdef extern from "cpl_conv.h":
cdef extern from "cpl_error.h":
- void CPLSetErrorHandler (void *handler)
+ ctypedef void (*CPLErrorHandler)(int, int, const char*);
+ void CPLSetErrorHandler (CPLErrorHandler handler)
cdef extern from "gdal.h":
@@ -105,7 +106,7 @@ cdef class GDALEnv(object):
GDALAllRegister()
if OGRGetDriverCount() == 0:
OGRRegisterAll()
- CPLSetErrorHandler(<void *>errorHandler)
+ CPLSetErrorHandler(<CPLErrorHandler>errorHandler)
if OGRGetDriverCount() == 0:
raise ValueError("Drivers not registered")
=====================================
fiona/_env.pyx
=====================================
@@ -10,6 +10,7 @@ option is set to a new value inside the thread.
include "gdal.pxi"
+from collections import namedtuple
import logging
import os
import os.path
@@ -53,14 +54,61 @@ log = logging.getLogger(__name__)
cdef bint is_64bit = sys.maxsize > 2 ** 32
+def calc_gdal_version_num(maj, min, rev):
+ """Calculates the internal gdal version number based on major, minor and revision
+
+ GDAL Version Information macro changed with GDAL version 1.10.0 (April 2013)
+
+ """
+ if (maj, min, rev) >= (1, 10, 0):
+ return int(maj * 1000000 + min * 10000 + rev * 100)
+ else:
+ return int(maj * 1000 + min * 100 + rev * 10)
+
+
+def get_gdal_version_num():
+ """Return current internal version number of gdal"""
+ return int(GDALVersionInfo("VERSION_NUM"))
+
+
+def get_gdal_release_name():
+ """Return release name of gdal"""
+ cdef const char *name_c = NULL
+ name_c = GDALVersionInfo("RELEASE_NAME")
+ name = name_c
+ return name
+
+
+GDALVersion = namedtuple("GDALVersion", ["major", "minor", "revision"])
+
+
+def get_gdal_version_tuple():
+ """
+ Calculates gdal version tuple from gdal's internal version number.
+
+ GDAL Version Information macro changed with GDAL version 1.10.0 (April 2013)
+ """
+ gdal_version_num = get_gdal_version_num()
+
+ if gdal_version_num >= calc_gdal_version_num(1, 10, 0):
+ major = gdal_version_num // 1000000
+ minor = (gdal_version_num - (major * 1000000)) // 10000
+ revision = (gdal_version_num - (major * 1000000) - (minor * 10000)) // 100
+ return GDALVersion(major, minor, revision)
+ else:
+ major = gdal_version_num // 1000
+ minor = (gdal_version_num - (major * 1000)) // 100
+ revision = (gdal_version_num - (major * 1000) - (minor * 100)) // 10
+ return GDALVersion(major, minor, revision)
+
+
cdef void log_error(CPLErr err_class, int err_no, const char* msg) with gil:
"""Send CPL debug messages and warnings to Python's logger."""
log = logging.getLogger(__name__)
- if err_class < 3:
- if err_no in code_map:
- log.log(level_map[err_class], "%s in %s", code_map[err_no], msg)
- else:
- log.info("Unknown error number %r", err_no)
+ if err_no in code_map:
+ log.log(level_map[err_class], "%s", msg)
+ else:
+ log.info("Unknown error number %r", err_no)
# Definition of GDAL callback functions, one for Windows and one for
@@ -186,6 +234,59 @@ cdef class ConfigEnv(object):
return {k: get_gdal_config(k) for k in self.options}
+class GDALDataFinder(object):
+ """Finds GDAL and PROJ data files"""
+
+ def search(self, prefix=None):
+ """Returns GDAL_DATA location"""
+ path = self.search_wheel(prefix or __file__)
+ if not path:
+ path = self.search_prefix(prefix or sys.prefix)
+ if not path:
+ path = self.search_debian(prefix or sys.prefix)
+ return path
+
+ def search_wheel(self, prefix=None):
+ """Check wheel location"""
+ if prefix is None:
+ prefix = __file__
+ datadir = os.path.abspath(os.path.join(os.path.dirname(prefix), "gdal_data"))
+ return datadir if os.path.exists(os.path.join(datadir, 'pcs.csv')) else None
+
+ def search_prefix(self, prefix=sys.prefix):
+ """Check sys.prefix location"""
+ datadir = os.path.join(prefix, 'share', 'gdal')
+ return datadir if os.path.exists(os.path.join(datadir, 'pcs.csv')) else None
+
+ def search_debian(self, prefix=sys.prefix):
+ """Check Debian locations"""
+ gdal_version = get_gdal_version_tuple()
+ datadir = os.path.join(prefix, 'share', 'gdal', '{}.{}'.format(gdal_version.major, gdal_version.minor))
+ return datadir if os.path.exists(os.path.join(datadir, 'pcs.csv')) else None
+
+
+class PROJDataFinder(object):
+
+ def search(self, prefix=None):
+ """Returns PROJ_LIB location"""
+ path = self.search_wheel(prefix or __file__)
+ if not path:
+ path = self.search_prefix(prefix or sys.prefix)
+ return path
+
+ def search_wheel(self, prefix=None):
+ """Check wheel location"""
+ if prefix is None:
+ prefix = __file__
+ datadir = os.path.abspath(os.path.join(os.path.dirname(prefix), "proj_data"))
+ return datadir if os.path.exists(datadir) else None
+
+ def search_prefix(self, prefix=sys.prefix):
+ """Check sys.prefix location"""
+ datadir = os.path.join(prefix, 'share', 'proj')
+ return datadir if os.path.exists(datadir) else None
+
+
cdef class GDALEnv(ConfigEnv):
"""Configuration and driver management"""
@@ -210,40 +311,22 @@ cdef class GDALEnv(ConfigEnv):
if 'GDAL_DATA' not in os.environ:
- # We will try a few well-known paths, starting with the
- # official wheel path.
- whl_datadir = os.path.abspath(
- os.path.join(os.path.dirname(__file__), "gdal_data"))
- fhs_share_datadir = os.path.join(sys.prefix, 'share/gdal')
-
- # Debian supports multiple GDAL installs.
- gdal_release_name = GDALVersionInfo("RELEASE_NAME")
- deb_share_datadir = os.path.join(
- fhs_share_datadir,
- "{}.{}".format(*gdal_release_name.split('.')[:2]))
+ path = GDALDataFinder().search()
- # If we find GDAL data at the well-known paths, we will
- # add a GDAL_DATA key to the config options dict.
- if os.path.exists(os.path.join(whl_datadir, 'pcs.csv')):
- self.update_config_options(GDAL_DATA=whl_datadir)
+ if path:
+ log.debug("GDAL data found in %r", path)
+ self.update_config_options(GDAL_DATA=path)
- elif os.path.exists(os.path.join(deb_share_datadir, 'pcs.csv')):
- self.update_config_options(GDAL_DATA=deb_share_datadir)
-
- elif os.path.exists(os.path.join(fhs_share_datadir, 'pcs.csv')):
- self.update_config_options(GDAL_DATA=fhs_share_datadir)
+ else:
+ self.update_config_options(GDAL_DATA=os.environ['GDAL_DATA'])
if 'PROJ_LIB' not in os.environ:
- whl_datadir = os.path.abspath(
- os.path.join(os.path.dirname(__file__), 'proj_data'))
- share_datadir = os.path.join(sys.prefix, 'share/proj')
-
- if os.path.exists(whl_datadir):
- os.environ['PROJ_LIB'] = whl_datadir
+ path = PROJDataFinder().search()
- elif os.path.exists(share_datadir):
- os.environ['PROJ_LIB'] = share_datadir
+ if path:
+ log.debug("PROJ data found in %r", path)
+ os.environ['PROJ_LIB'] = path
if driver_count() == 0:
CPLPopErrorHandler()
@@ -274,8 +357,8 @@ cdef class GDALEnv(ConfigEnv):
result = {}
for i in range(OGRGetDriverCount()):
drv = OGRGetDriver(i)
- key = OGR_Dr_GetName(drv)
- val = OGR_Dr_GetName(drv)
+ key = <char *>OGR_Dr_GetName(drv)
+ val = <char *>OGR_Dr_GetName(drv)
result[key] = val
return result
=====================================
fiona/_err.pyx
=====================================
@@ -66,7 +66,7 @@ class CPLE_BaseError(Exception):
return self.__unicode__()
def __unicode__(self):
- return "{}".format(self.errmsg)
+ return u"{}".format(self.errmsg)
@property
def args(self):
@@ -203,7 +203,7 @@ cdef class GDALErrCtxManager:
cdef inline object exc_check():
"""Checks GDAL error stack for fatal or non-fatal errors
-
+
Returns
-------
An Exception, SystemExit, or None
=====================================
fiona/_geometry.pxd
=====================================
@@ -3,6 +3,81 @@
ctypedef int OGRErr
+cdef extern from "ogr_core.h":
+ ctypedef enum OGRwkbGeometryType:
+ wkbUnknown
+ wkbPoint
+ wkbLineString
+ wkbPolygon
+ wkbMultiPoint
+ wkbMultiLineString
+ wkbMultiPolygon
+ wkbGeometryCollection
+ wkbCircularString
+ wkbCompoundCurve
+ wkbCurvePolygon
+ wkbMultiCurve
+ wkbMultiSurface
+ wkbCurve
+ wkbSurface
+ wkbPolyhedralSurface
+ wkbTIN
+ wkbTriangle
+ wkbNone
+ wkbLinearRing
+ wkbCircularStringZ
+ wkbCompoundCurveZ
+ wkbCurvePolygonZ
+ wkbMultiCurveZ
+ wkbMultiSurfaceZ
+ wkbCurveZ
+ wkbSurfaceZ
+ wkbPolyhedralSurfaceZ
+ wkbTINZ
+ wkbTriangleZ
+ wkbPointM
+ wkbLineStringM
+ wkbPolygonM
+ wkbMultiPointM
+ wkbMultiLineStringM
+ wkbMultiPolygonM
+ wkbGeometryCollectionM
+ wkbCircularStringM
+ wkbCompoundCurveM
+ wkbCurvePolygonM
+ wkbMultiCurveM
+ wkbMultiSurfaceM
+ wkbCurveM
+ wkbSurfaceM
+ wkbPolyhedralSurfaceM
+ wkbTINM
+ wkbTriangleM
+ wkbPointZM
+ wkbLineStringZM
+ wkbPolygonZM
+ wkbMultiPointZM
+ wkbMultiLineStringZM
+ wkbMultiPolygonZM
+ wkbGeometryCollectionZM
+ wkbCircularStringZM
+ wkbCompoundCurveZM
+ wkbCurvePolygonZM
+ wkbMultiCurveZM
+ wkbMultiSurfaceZM
+ wkbCurveZM
+ wkbSurfaceZM
+ wkbPolyhedralSurfaceZM
+ wkbTINZM
+ wkbTriangleZM
+ wkbPoint25D
+ wkbLineString25D
+ wkbPolygon25D
+ wkbMultiPoint25D
+ wkbMultiLineString25D
+ wkbMultiPolygon25D
+ wkbGeometryCollection25D
+
+
ctypedef struct OGREnvelope:
double MinX
double MaxX
@@ -15,7 +90,7 @@ cdef extern from "ogr_api.h":
void OGR_G_AddPoint (void *geometry, double x, double y, double z)
void OGR_G_AddPoint_2D (void *geometry, double x, double y)
void OGR_G_CloseRings (void *geometry)
- void * OGR_G_CreateGeometry (int wkbtypecode)
+ void * OGR_G_CreateGeometry (OGRwkbGeometryType wkbtypecode)
void OGR_G_DestroyGeometry (void *geometry)
unsigned char * OGR_G_ExportToJson (void *geometry)
void OGR_G_ExportToWkb (void *geometry, int endianness, char *buffer)
=====================================
fiona/_geometry.pyx
=====================================
@@ -85,7 +85,7 @@ cdef void * _createOgrGeomFromWKB(object wkb) except NULL:
"""Make an OGR geometry from a WKB string"""
wkbtype = bytearray(wkb)[1]
cdef unsigned char *buffer = wkb
- cdef void *cogr_geometry = OGR_G_CreateGeometry(wkbtype)
+ cdef void *cogr_geometry = OGR_G_CreateGeometry(<OGRwkbGeometryType>wkbtype)
if cogr_geometry is not NULL:
OGR_G_ImportFromWkb(cogr_geometry, buffer, len(wkb))
return cogr_geometry
@@ -191,7 +191,7 @@ cdef class OGRGeomBuilder:
"""Builds OGR geometries from Fiona geometries.
"""
cdef void * _createOgrGeometry(self, int geom_type) except NULL:
- cdef void *cogr_geometry = OGR_G_CreateGeometry(geom_type)
+ cdef void *cogr_geometry = OGR_G_CreateGeometry(<OGRwkbGeometryType>geom_type)
if cogr_geometry == NULL:
raise Exception("Could not create OGR Geometry of type: %i" % geom_type)
return cogr_geometry
@@ -286,11 +286,6 @@ cdef class OGRGeomBuilder:
raise ValueError("Unsupported geometry type %s" % typename)
-cdef geometry(void *geom):
- """Factory for Fiona geometries"""
- return GeomBuilder().build(geom)
-
-
def geometryRT(geometry):
# For testing purposes only, leaks the JSON data
cdef void *cogr_geometry = OGRGeomBuilder().build(geometry)
=====================================
fiona/_shim1.pxd
=====================================
@@ -10,7 +10,7 @@ ctypedef enum OGRFieldSubType:
cdef bint is_field_null(void *feature, int n)
cdef void set_field_null(void *feature, int n)
cdef void gdal_flush_cache(void *cogr_ds)
-cdef void* gdal_open_vector(char* path_c, int mode, drivers, options) except NULL
+cdef void* gdal_open_vector(const char* path_c, int mode, drivers, options) except NULL
cdef void* gdal_create(void* cogr_driver, const char *path_c, options) except NULL
cdef OGRErr gdal_start_transaction(void *cogr_ds, int force)
cdef OGRErr gdal_commit_transaction(void *cogr_ds)
=====================================
fiona/_shim2.pxd
=====================================
@@ -3,7 +3,7 @@ include "ogrext2.pxd"
cdef bint is_field_null(void *feature, int n)
cdef void set_field_null(void *feature, int n)
cdef void gdal_flush_cache(void *cogr_ds)
-cdef void* gdal_open_vector(char* path_c, int mode, drivers, options) except NULL
+cdef void* gdal_open_vector(const char* path_c, int mode, drivers, options) except NULL
cdef void* gdal_create(void* cogr_driver, const char *path_c, options) except NULL
cdef OGRErr gdal_start_transaction(void *cogr_ds, int force)
cdef OGRErr gdal_commit_transaction(void *cogr_ds)
=====================================
fiona/_shim2.pyx
=====================================
@@ -25,7 +25,7 @@ cdef void gdal_flush_cache(void *cogr_ds):
GDALFlushCache(cogr_ds)
-cdef void* gdal_open_vector(char* path_c, int mode, drivers, options) except NULL:
+cdef void* gdal_open_vector(const char* path_c, int mode, drivers, options) except NULL:
cdef void* cogr_ds = NULL
cdef char **drvs = NULL
cdef char **open_opts = NULL
=====================================
fiona/_shim22.pxd
=====================================
@@ -3,7 +3,7 @@ include "ogrext2.pxd"
cdef bint is_field_null(void *feature, int n)
cdef void set_field_null(void *feature, int n)
cdef void gdal_flush_cache(void *cogr_ds)
-cdef void* gdal_open_vector(char* path_c, int mode, drivers, options) except NULL
+cdef void* gdal_open_vector(const char *path_c, int mode, drivers, options) except NULL
cdef void* gdal_create(void* cogr_driver, const char *path_c, options) except NULL
cdef OGRErr gdal_start_transaction(void *cogr_ds, int force)
cdef OGRErr gdal_commit_transaction(void *cogr_ds)
=====================================
fiona/_shim22.pyx
=====================================
@@ -30,7 +30,7 @@ cdef void gdal_flush_cache(void *cogr_ds):
GDALFlushCache(cogr_ds)
-cdef void* gdal_open_vector(char* path_c, int mode, drivers, options) except NULL:
+cdef void* gdal_open_vector(const char* path_c, int mode, drivers, options) except NULL:
cdef void* cogr_ds = NULL
cdef char **drvs = NULL
cdef void* drv = NULL
@@ -64,7 +64,7 @@ cdef void* gdal_open_vector(char* path_c, int mode, drivers, options) except NUL
try:
cogr_ds = exc_wrap_pointer(
- GDALOpenEx(path_c, flags, <const char *const *>drvs, open_opts, NULL)
+ GDALOpenEx(path_c, flags, <const char *const *>drvs, <const char *const *>open_opts, NULL)
)
return cogr_ds
except FionaNullPointerError:
=====================================
fiona/collection.py
=====================================
@@ -8,12 +8,12 @@ import warnings
from fiona import compat, vfs
from fiona.ogrext import Iterator, ItemsIterator, KeysIterator
from fiona.ogrext import Session, WritingSession
-from fiona.ogrext import (
- calc_gdal_version_num, get_gdal_version_num, get_gdal_release_name)
from fiona.ogrext import buffer_to_virtual_file, remove_virtual_file, GEOMETRY_TYPES
from fiona.errors import (DriverError, SchemaError, CRSError, UnsupportedGeometryTypeError, DriverSupportError)
from fiona.logutils import FieldSkipLogFilter
from fiona._env import driver_count
+from fiona._env import (
+ calc_gdal_version_num, get_gdal_version_num, get_gdal_release_name)
from fiona.env import Env
from fiona.errors import FionaDeprecationWarning
from fiona.drvsupport import supported_drivers
=====================================
fiona/env.py
=====================================
@@ -1,5 +1,6 @@
"""Fiona's GDAL/AWS environment"""
+from contextlib import contextmanager
from functools import wraps, total_ordering
import logging
import re
@@ -9,7 +10,8 @@ import attr
from six import string_types
from fiona._env import (
- GDALEnv, get_gdal_config, set_gdal_config)
+ GDALEnv, calc_gdal_version_num, get_gdal_version_num, get_gdal_config,
+ set_gdal_config, get_gdal_release_name)
from fiona.compat import getargspec
from fiona.errors import EnvError, GDALVersionError
from fiona.session import Session, DummySession
@@ -160,15 +162,16 @@ class Env(object):
self.context_options = {}
@classmethod
- def from_defaults(cls, *args, **kwargs):
+ def from_defaults(cls, session=None, **options):
"""Create an environment with default config options
Parameters
----------
- args : optional
- Positional arguments for Env()
- kwargs : optional
- Keyword arguments for Env()
+ session : optional
+ A Session object.
+ **options : optional
+ A mapping of GDAL configuration options, e.g.,
+ `CPL_DEBUG=True, CHECK_WITH_INVERT_PROJ=False`.
Returns
-------
@@ -179,9 +182,9 @@ class Env(object):
The items in kwargs will be overlaid on the default values.
"""
- options = Env.default_options()
- options.update(**kwargs)
- return Env(*args, **options)
+ opts = Env.default_options()
+ opts.update(**options)
+ return Env(session=session, **opts)
@property
def is_credentialized(self):
@@ -313,16 +316,55 @@ def delenv():
local._env = None
+class NullContextManager(object):
+ def __init__(self):
+ pass
+ def __enter__(self):
+ return self
+ def __exit__(self, *args):
+ pass
+
+
+def env_ctx_if_needed():
+ """Return an Env if one does not exist
+
+ Returns
+ -------
+ Env or a do-nothing context manager
+
+ """
+ if local._env:
+ return NullContextManager()
+ else:
+ return Env.from_defaults()
+
+
def ensure_env(f):
"""A decorator that ensures an env exists before a function
- calls any GDAL C functions."""
+ calls any GDAL C functions.
+
+ Parameters
+ ----------
+ f : function
+ A function.
+
+ Returns
+ -------
+ A function wrapper.
+
+ Notes
+ -----
+ If there is already an existing environment, the wrapper does
+ nothing and immediately calls f with the given arguments.
+
+ """
@wraps(f)
- def wrapper(*args, **kwds):
+ def wrapper(*args, **kwargs):
if local._env:
- return f(*args, **kwds)
+ return f(*args, **kwargs)
else:
with Env.from_defaults():
- return f(*args, **kwds)
+ return f(*args, **kwargs)
return wrapper
@@ -344,25 +386,23 @@ def ensure_env_with_credentials(f):
credentializes the environment if the first argument is a URI with
scheme "s3".
+ If there is already an existing environment, the wrapper does
+ nothing and immediately calls f with the given arguments.
+
"""
@wraps(f)
- def wrapper(*args, **kwds):
+ def wrapper(*args, **kwargs):
if local._env:
- env_ctor = Env
- else:
- env_ctor = Env.from_defaults
-
- if hascreds():
- session = DummySession()
- elif isinstance(args[0], str):
- session = Session.from_path(args[0])
+ return f(*args, **kwargs)
else:
- session = Session.from_path(None)
-
- with env_ctor(session=session):
- log.debug("Credentialized: {!r}".format(getenv()))
- return f(*args, **kwds)
-
+ if isinstance(args[0], str):
+ session = Session.from_path(args[0])
+ else:
+ session = Session.from_path(None)
+
+ with Env.from_defaults(session=session):
+ log.debug("Credentialized: {!r}".format(getenv()))
+ return f(*args, **kwargs)
return wrapper
@@ -425,7 +465,6 @@ class GDALVersion(object):
@classmethod
def runtime(cls):
"""Return GDALVersion of current GDAL runtime"""
- from fiona.ogrext import get_gdal_release_name # to avoid circular import
return cls.parse(get_gdal_release_name())
def at_least(self, other):
=====================================
fiona/ogrext.pyx
=====================================
@@ -22,7 +22,9 @@ from fiona._geometry cimport (
from fiona._err cimport exc_wrap_int, exc_wrap_pointer, exc_wrap_vsilfile
import fiona
-from fiona._err import cpl_errs, FionaNullPointerError, CPLE_BaseError
+from fiona.env import env_ctx_if_needed
+from fiona._env import GDALVersion, get_gdal_version_num
+from fiona._err import cpl_errs, FionaNullPointerError, CPLE_BaseError, CPLE_OpenFailedError
from fiona._geometry import GEOMETRY_TYPES
from fiona import compat
from fiona.errors import (
@@ -102,54 +104,8 @@ def _bounds(geometry):
return None
-def calc_gdal_version_num(maj, min, rev):
- """Calculates the internal gdal version number based on major, minor and revision
-
- GDAL Version Information macro changed with GDAL version 1.10.0 (April 2013)
-
- """
- if (maj, min, rev) >= (1, 10, 0):
- return int(maj * 1000000 + min * 10000 + rev * 100)
- else:
- return int(maj * 1000 + min * 100 + rev * 10)
-
-
-def get_gdal_version_num():
- """Return current internal version number of gdal"""
- return int(GDALVersionInfo("VERSION_NUM"))
-
-
-def get_gdal_release_name():
- """Return release name of gdal"""
- cdef const char *name_c = NULL
- name_c = GDALVersionInfo("RELEASE_NAME")
- name_b = name_c
- return name_b.decode('utf-8')
-
-
cdef int GDAL_VERSION_NUM = get_gdal_version_num()
-GDALVersion = namedtuple("GDALVersion", ["major", "minor", "revision"])
-
-
-def get_gdal_version_tuple():
- """
- Calculates gdal version tuple from gdal's internal version number.
-
- GDAL Version Information macro changed with GDAL version 1.10.0 (April 2013)
- """
- gdal_version_num = get_gdal_version_num()
-
- if gdal_version_num >= calc_gdal_version_num(1, 10, 0):
- major = gdal_version_num // 1000000
- minor = (gdal_version_num - (major * 1000000)) // 10000
- revision = (gdal_version_num - (major * 1000000) - (minor * 10000)) // 100
- return GDALVersion(major, minor, revision)
- else:
- major = gdal_version_num // 1000
- minor = (gdal_version_num - (major * 1000)) // 100
- revision = (gdal_version_num - (major * 1000) - (minor * 100)) // 10
- return GDALVersion(major, minor, revision)
# Feature extension classes and functions follow.
@@ -520,7 +476,7 @@ cdef class Session:
OGR_L_TestCapability(
self.cogr_layer, OLC_STRINGSASUTF8) and
'utf-8') or (
- self.get_driver() == "ESRI Shapefile" and
+ "Shapefile" in self.get_driver() and
'ISO-8859-1') or locale.getpreferredencoding().upper()
if collection.ignore_fields:
@@ -531,7 +487,7 @@ cdef class Session:
except AttributeError:
raise TypeError("Ignored field \"{}\" has type \"{}\", expected string".format(name, name.__class__.__name__))
ignore_fields = CSLAddString(ignore_fields, <const char *>name)
- OGR_L_SetIgnoredFields(self.cogr_layer, ignore_fields)
+ OGR_L_SetIgnoredFields(self.cogr_layer, <const char**>ignore_fields)
finally:
CSLDestroy(ignore_fields)
@@ -650,79 +606,135 @@ cdef class Session:
return ret
def get_crs(self):
+ """Get the layer's CRS
+
+ Returns
+ -------
+ CRS
+
+ """
cdef char *proj_c = NULL
cdef const char *auth_key = NULL
cdef const char *auth_val = NULL
cdef void *cogr_crs = NULL
+
if self.cogr_layer == NULL:
raise ValueError("Null layer")
- cogr_crs = OGR_L_GetSpatialRef(self.cogr_layer)
- crs = {}
- if cogr_crs is not NULL:
- log.debug("Got coordinate system")
-
- retval = OSRAutoIdentifyEPSG(cogr_crs)
- if retval > 0:
- log.info("Failed to auto identify EPSG: %d", retval)
-
- auth_key = OSRGetAuthorityName(cogr_crs, NULL)
- auth_val = OSRGetAuthorityCode(cogr_crs, NULL)
-
- if auth_key != NULL and auth_val != NULL:
- key_b = auth_key
- key = key_b.decode('utf-8')
- if key == 'EPSG':
- val_b = auth_val
- val = val_b.decode('utf-8')
- crs['init'] = "epsg:" + val
- else:
- OSRExportToProj4(cogr_crs, &proj_c)
- if proj_c == NULL:
- raise ValueError("Null projection")
- proj_b = proj_c
- log.debug("Params: %s", proj_b)
- value = proj_b.decode()
- value = value.strip()
- for param in value.split():
- kv = param.split("=")
- if len(kv) == 2:
- k, v = kv
- try:
- v = float(v)
- if v % 1 == 0:
- v = int(v)
- except ValueError:
- # Leave v as a string
- pass
- elif len(kv) == 1:
- k, v = kv[0], True
+
+ # We can't simply wrap a method in Python 2.7 so we
+ # bring the context manager inside like so.
+ with env_ctx_if_needed():
+
+ try:
+ cogr_crs = exc_wrap_pointer(OGR_L_GetSpatialRef(self.cogr_layer))
+ # TODO: we don't intend to use try/except for flow control
+ # this is a work around for a GDAL issue.
+ except FionaNullPointerError:
+ log.debug("Layer has no coordinate system")
+
+ if cogr_crs is not NULL:
+
+ log.debug("Got coordinate system")
+ crs = {}
+
+ try:
+
+ retval = OSRAutoIdentifyEPSG(cogr_crs)
+ if retval > 0:
+ log.info("Failed to auto identify EPSG: %d", retval)
+
+ try:
+ auth_key = <const char *>exc_wrap_pointer(<void *>OSRGetAuthorityName(cogr_crs, NULL))
+ auth_val = <const char *>exc_wrap_pointer(<void *>OSRGetAuthorityCode(cogr_crs, NULL))
+
+ except CPLE_BaseError as exc:
+ log.debug("{}".format(exc))
+
+ if auth_key != NULL and auth_val != NULL:
+ key_b = auth_key
+ key = key_b.decode('utf-8')
+ if key == 'EPSG':
+ val_b = auth_val
+ val = val_b.decode('utf-8')
+ crs['init'] = "epsg:" + val
+
else:
- raise ValueError("Unexpected proj parameter %s" % param)
- k = k.lstrip("+")
- crs[k] = v
+ OSRExportToProj4(cogr_crs, &proj_c)
+ if proj_c == NULL:
+ raise ValueError("Null projection")
+ proj_b = proj_c
+ log.debug("Params: %s", proj_b)
+ value = proj_b.decode()
+ value = value.strip()
+ for param in value.split():
+ kv = param.split("=")
+ if len(kv) == 2:
+ k, v = kv
+ try:
+ v = float(v)
+ if v % 1 == 0:
+ v = int(v)
+ except ValueError:
+ # Leave v as a string
+ pass
+ elif len(kv) == 1:
+ k, v = kv[0], True
+ else:
+ raise ValueError("Unexpected proj parameter %s" % param)
+ k = k.lstrip("+")
+ crs[k] = v
- CPLFree(proj_c)
- else:
- log.debug("Projection not found (cogr_crs was NULL)")
- return crs
+ finally:
+ CPLFree(proj_c)
+ return crs
+
+ else:
+ log.debug("Projection not found (cogr_crs was NULL)")
+
+ return {}
def get_crs_wkt(self):
cdef char *proj_c = NULL
+ cdef void *cogr_crs = NULL
+
if self.cogr_layer == NULL:
raise ValueError("Null layer")
- cogr_crs = OGR_L_GetSpatialRef(self.cogr_layer)
- crs_wkt = ""
- if cogr_crs is not NULL:
- log.debug("Got coordinate system")
- OSRExportToWkt(cogr_crs, &proj_c)
- if proj_c == NULL:
- raise ValueError("Null projection")
- proj_b = proj_c
- crs_wkt = proj_b.decode('utf-8')
- CPLFree(proj_c)
- else:
- log.debug("Projection not found (cogr_crs was NULL)")
- return crs_wkt
+
+ # We can't simply wrap a method in Python 2.7 so we
+ # bring the context manager inside like so.
+ with env_ctx_if_needed():
+
+ try:
+ cogr_crs = exc_wrap_pointer(OGR_L_GetSpatialRef(self.cogr_layer))
+
+ # TODO: we don't intend to use try/except for flow control
+ # this is a work around for a GDAL issue.
+ except FionaNullPointerError:
+ log.debug("Layer has no coordinate system")
+ except fiona._err.CPLE_OpenFailedError as exc:
+ log.debug("A support file wasn't opened. See the preceding ERROR level message.")
+ cogr_crs = OGR_L_GetSpatialRef(self.cogr_layer)
+ log.debug("Called OGR_L_GetSpatialRef() again without error checking.")
+ if cogr_crs == NULL:
+ raise exc
+
+ if cogr_crs is not NULL:
+ log.debug("Got coordinate system")
+
+ try:
+ OSRExportToWkt(cogr_crs, &proj_c)
+ if proj_c == NULL:
+ raise ValueError("Null projection")
+ proj_b = proj_c
+ crs_wkt = proj_b.decode('utf-8')
+
+ finally:
+ CPLFree(proj_c)
+ return crs_wkt
+
+ else:
+ log.debug("Projection not found (cogr_crs was NULL)")
+ return ""
def get_extent(self):
cdef OGREnvelope extent
@@ -822,46 +834,49 @@ cdef class WritingSession(Session):
cdef const char *proj_c = NULL
cdef const char *fileencoding_c = NULL
cdef OGRFieldSubType field_subtype
+ cdef int ret
path = collection.path
self.collection = collection
userencoding = kwargs.get('encoding')
if collection.mode == 'a':
- if os.path.exists(path):
- try:
- path_b = path.encode('utf-8')
- except UnicodeDecodeError:
- path_b = path
- path_c = path_b
- self.cogr_ds = gdal_open_vector(path_c, 1, None, kwargs)
- cogr_driver = GDALGetDatasetDriver(self.cogr_ds)
- if cogr_driver == NULL:
- raise ValueError("Null driver")
+ if not os.path.exists(path):
+ raise OSError("No such file or directory %s" % path)
+
+ try:
+ path_b = path.encode('utf-8')
+ except UnicodeDecodeError:
+ path_b = path
+ path_c = path_b
+
+ try:
+ self.cogr_ds = gdal_open_vector(path_c, 1, None, kwargs)
if isinstance(collection.name, string_types):
name_b = collection.name.encode()
name_c = name_b
- self.cogr_layer = GDALDatasetGetLayerByName(
- self.cogr_ds, name_c)
+ self.cogr_layer = exc_wrap_pointer(GDALDatasetGetLayerByName(self.cogr_ds, name_c))
+
elif isinstance(collection.name, int):
- self.cogr_layer = GDALDatasetGetLayer(
- self.cogr_ds, collection.name)
+ self.cogr_layer = exc_wrap_pointer(GDALDatasetGetLayer(self.cogr_ds, collection.name))
- if self.cogr_layer == NULL:
- raise RuntimeError(
- "Failed to get layer %s" % collection.name)
- else:
- raise OSError("No such file or directory %s" % path)
+ except CPLE_BaseError as exc:
+ OGRReleaseDataSource(self.cogr_ds)
+ self.cogr_ds = NULL
+ self.cogr_layer = NULL
+ raise DriverError(u"{}".format(exc))
- self._fileencoding = (userencoding or (
- OGR_L_TestCapability(self.cogr_layer, OLC_STRINGSASUTF8) and
- 'utf-8') or (
- self.get_driver() == "ESRI Shapefile" and
- 'ISO-8859-1') or locale.getpreferredencoding()).upper()
+ else:
+ self._fileencoding = (userencoding or (
+ OGR_L_TestCapability(self.cogr_layer, OLC_STRINGSASUTF8) and
+ 'utf-8') or (
+ self.get_driver() == "ESRI Shapefile" and
+ 'ISO-8859-1') or locale.getpreferredencoding()).upper()
elif collection.mode == 'w':
+
try:
path_b = path.encode('utf-8')
except UnicodeDecodeError:
@@ -870,11 +885,7 @@ cdef class WritingSession(Session):
driver_b = collection.driver.encode()
driver_c = driver_b
-
-
- cogr_driver = GDALGetDriverByName(driver_c)
- if cogr_driver == NULL:
- raise ValueError("Null driver")
+ cogr_driver = exc_wrap_pointer(GDALGetDriverByName(driver_c))
# Our most common use case is the creation of a new data
# file and historically we've assumed that it's a file on
@@ -883,7 +894,7 @@ cdef class WritingSession(Session):
# TODO: remove the assumption.
if not os.path.exists(path):
log.debug("File doesn't exist. Creating a new one...")
- cogr_ds = gdal_create(cogr_driver, path_c, kwargs)
+ cogr_ds = gdal_create(cogr_driver, path_c, {})
# TODO: revisit the logic in the following blocks when we
# change the assumption above.
@@ -915,59 +926,68 @@ cdef class WritingSession(Session):
# collection constructor. We by-pass the crs_wkt and crs
# properties because they aren't accessible until the layer
# is constructed (later).
- col_crs = collection._crs_wkt or collection._crs
- if col_crs:
- cogr_srs = OSRNewSpatialReference(NULL)
- if cogr_srs == NULL:
- raise ValueError("NULL spatial reference")
- # First, check for CRS strings like "EPSG:3857".
- if isinstance(col_crs, string_types):
- proj_b = col_crs.encode('utf-8')
- proj_c = proj_b
- OSRSetFromUserInput(cogr_srs, proj_c)
- elif isinstance(col_crs, compat.DICT_TYPES):
- # EPSG is a special case.
- init = col_crs.get('init')
- if init:
- log.debug("Init: %s", init)
- auth, val = init.split(':')
- if auth.upper() == 'EPSG':
- log.debug("Setting EPSG: %s", val)
- OSRImportFromEPSG(cogr_srs, int(val))
- else:
- params = []
- col_crs['wktext'] = True
- for k, v in col_crs.items():
- if v is True or (k in ('no_defs', 'wktext') and v):
- params.append("+%s" % k)
- else:
- params.append("+%s=%s" % (k, v))
- proj = " ".join(params)
- log.debug("PROJ.4 to be imported: %r", proj)
- proj_b = proj.encode('utf-8')
+ try:
+
+ col_crs = collection._crs_wkt or collection._crs
+
+ if col_crs:
+ cogr_srs = exc_wrap_pointer(OSRNewSpatialReference(NULL))
+
+ # First, check for CRS strings like "EPSG:3857".
+ if isinstance(col_crs, string_types):
+ proj_b = col_crs.encode('utf-8')
proj_c = proj_b
- OSRImportFromProj4(cogr_srs, proj_c)
- else:
- raise ValueError("Invalid CRS")
+ OSRSetFromUserInput(cogr_srs, proj_c)
+
+ elif isinstance(col_crs, compat.DICT_TYPES):
+ # EPSG is a special case.
+ init = col_crs.get('init')
+ if init:
+ log.debug("Init: %s", init)
+ auth, val = init.split(':')
+ if auth.upper() == 'EPSG':
+ log.debug("Setting EPSG: %s", val)
+ OSRImportFromEPSG(cogr_srs, int(val))
+ else:
+ params = []
+ col_crs['wktext'] = True
+ for k, v in col_crs.items():
+ if v is True or (k in ('no_defs', 'wktext') and v):
+ params.append("+%s" % k)
+ else:
+ params.append("+%s=%s" % (k, v))
+ proj = " ".join(params)
+ log.debug("PROJ.4 to be imported: %r", proj)
+ proj_b = proj.encode('utf-8')
+ proj_c = proj_b
+ OSRImportFromProj4(cogr_srs, proj_c)
+
+ else:
+ raise ValueError("Invalid CRS")
+
+ # Fixup, export to WKT, and set the GDAL dataset's projection.
+ OSRFixup(cogr_srs)
+
+ except (ValueError, CPLE_BaseError) as exc:
+ OGRReleaseDataSource(self.cogr_ds)
+ self.cogr_ds = NULL
+ self.cogr_layer = NULL
+ raise CRSError(u"{}".format(exc))
- # Fixup, export to WKT, and set the GDAL dataset's projection.
- OSRFixup(cogr_srs)
# Figure out what encoding to use. The encoding parameter given
# to the collection constructor takes highest precedence, then
# 'iso-8859-1', then the system's default encoding as last resort.
sysencoding = locale.getpreferredencoding()
self._fileencoding = (userencoding or (
- collection.driver == "ESRI Shapefile" and
+ "Shapefile" in collection.driver and
'ISO-8859-1') or sysencoding).upper()
- # The ENCODING option makes no sense for some drivers and
- # will result in a warning. Fixing is a TODO.
- fileencoding = self.get_fileencoding()
- if fileencoding:
- fileencoding_b = fileencoding.encode('utf-8')
- fileencoding_c = fileencoding_b
- with cpl_errs:
+ if "Shapefile" in collection.driver:
+ fileencoding = self.get_fileencoding()
+ if fileencoding:
+ fileencoding_b = fileencoding.encode('utf-8')
+ fileencoding_c = fileencoding_b
options = CSLSetNameValue(options, "ENCODING", fileencoding_c)
# Does the layer exist already? If so, we delete it.
@@ -995,7 +1015,14 @@ cdef class WritingSession(Session):
name_c = name_b
for k, v in kwargs.items():
+
+ # We need to remove encoding from the layer creation
+ # options if we're not creating a shapefile.
+ if k == 'encoding' and "Shapefile" not in collection.driver:
+ continue
+
k = k.upper().encode('utf-8')
+
if isinstance(v, bool):
v = ('ON' if v else 'OFF').encode('utf-8')
else:
@@ -1018,9 +1045,13 @@ cdef class WritingSession(Session):
self.cogr_layer = exc_wrap_pointer(
GDALDatasetCreateLayer(
self.cogr_ds, name_c, cogr_srs,
- geometry_code, options))
+ <OGRwkbGeometryType>geometry_code, options))
+
except Exception as exc:
- raise DriverIOError(str(exc))
+ OGRReleaseDataSource(self.cogr_ds)
+ self.cogr_ds = NULL
+ raise DriverIOError(u"{}".format(exc))
+
finally:
if options != NULL:
CSLDestroy(options)
@@ -1032,9 +1063,6 @@ cdef class WritingSession(Session):
if cogr_srs != NULL:
OSRRelease(cogr_srs)
- if self.cogr_layer == NULL:
- raise ValueError("Null layer")
-
log.debug("Created layer %s", collection.name)
# Next, make a layer definition from the given schema properties,
@@ -1076,29 +1104,26 @@ cdef class WritingSession(Session):
field_type = FIELD_TYPES.index(value)
encoding = self.get_internalencoding()
- key_bytes = key.encode(encoding)
-
- cogr_fielddefn = exc_wrap_pointer(OGR_Fld_Create(key_bytes, field_type))
-
- if cogr_fielddefn == NULL:
- raise ValueError("Field {} definition is NULL".format(key))
-
- if width:
- OGR_Fld_SetWidth(cogr_fielddefn, width)
- if precision:
- OGR_Fld_SetPrecision(cogr_fielddefn, precision)
- if field_subtype != OFSTNone:
- # subtypes are new in GDAL 2.x, ignored in 1.x
- set_field_subtype(cogr_fielddefn, field_subtype)
-
try:
+ key_bytes = key.encode(encoding)
+ cogr_fielddefn = exc_wrap_pointer(OGR_Fld_Create(key_bytes, <OGRFieldType>field_type))
+ if width:
+ OGR_Fld_SetWidth(cogr_fielddefn, width)
+ if precision:
+ OGR_Fld_SetPrecision(cogr_fielddefn, precision)
+ if field_subtype != OFSTNone:
+ # subtypes are new in GDAL 2.x, ignored in 1.x
+ set_field_subtype(cogr_fielddefn, field_subtype)
exc_wrap_int(OGR_L_CreateField(self.cogr_layer, cogr_fielddefn, 1))
- except CPLE_BaseError as exc:
- raise SchemaError(str(exc))
-
- OGR_Fld_Destroy(cogr_fielddefn)
+ except (UnicodeEncodeError, CPLE_BaseError) as exc:
+ OGRReleaseDataSource(self.cogr_ds)
+ self.cogr_ds = NULL
+ self.cogr_layer = NULL
+ raise SchemaError(u"{}".format(exc))
- log.debug("End creating field %r", key)
+ else:
+ OGR_Fld_Destroy(cogr_fielddefn)
+ log.debug("End creating field %r", key)
# Mapping of the Python collection schema to the munged
# OGR schema.
=====================================
fiona/ogrext1.pxd
=====================================
@@ -52,6 +52,94 @@ ctypedef struct OGREnvelope:
double MaxY
cdef extern from "ogr_core.h":
+ ctypedef enum OGRwkbGeometryType:
+ wkbUnknown
+ wkbPoint
+ wkbLineString
+ wkbPolygon
+ wkbMultiPoint
+ wkbMultiLineString
+ wkbMultiPolygon
+ wkbGeometryCollection
+ wkbCircularString
+ wkbCompoundCurve
+ wkbCurvePolygon
+ wkbMultiCurve
+ wkbMultiSurface
+ wkbCurve
+ wkbSurface
+ wkbPolyhedralSurface
+ wkbTIN
+ wkbTriangle
+ wkbNone
+ wkbLinearRing
+ wkbCircularStringZ
+ wkbCompoundCurveZ
+ wkbCurvePolygonZ
+ wkbMultiCurveZ
+ wkbMultiSurfaceZ
+ wkbCurveZ
+ wkbSurfaceZ
+ wkbPolyhedralSurfaceZ
+ wkbTINZ
+ wkbTriangleZ
+ wkbPointM
+ wkbLineStringM
+ wkbPolygonM
+ wkbMultiPointM
+ wkbMultiLineStringM
+ wkbMultiPolygonM
+ wkbGeometryCollectionM
+ wkbCircularStringM
+ wkbCompoundCurveM
+ wkbCurvePolygonM
+ wkbMultiCurveM
+ wkbMultiSurfaceM
+ wkbCurveM
+ wkbSurfaceM
+ wkbPolyhedralSurfaceM
+ wkbTINM
+ wkbTriangleM
+ wkbPointZM
+ wkbLineStringZM
+ wkbPolygonZM
+ wkbMultiPointZM
+ wkbMultiLineStringZM
+ wkbMultiPolygonZM
+ wkbGeometryCollectionZM
+ wkbCircularStringZM
+ wkbCompoundCurveZM
+ wkbCurvePolygonZM
+ wkbMultiCurveZM
+ wkbMultiSurfaceZM
+ wkbCurveZM
+ wkbSurfaceZM
+ wkbPolyhedralSurfaceZM
+ wkbTINZM
+ wkbTriangleZM
+ wkbPoint25D
+ wkbLineString25D
+ wkbPolygon25D
+ wkbMultiPoint25D
+ wkbMultiLineString25D
+ wkbMultiPolygon25D
+ wkbGeometryCollection25D
+
+ ctypedef enum OGRFieldType:
+ OFTInteger
+ OFTIntegerList
+ OFTReal
+ OFTRealList
+ OFTString
+ OFTStringList
+ OFTWideString
+ OFTWideStringList
+ OFTBinary
+ OFTDate
+ OFTTime
+ OFTDateTime
+ OFTMaxType
+
char * OGRGeometryTypeToName(int)
char * ODsCCreateLayer = "CreateLayer"
@@ -79,7 +167,7 @@ cdef extern from "ogr_srs_api.h":
int OCTTransform (void *ct, int nCount, double *x, double *y, double *z)
cdef extern from "ogr_api.h":
- char * OGR_Dr_GetName (void *driver)
+ const char * OGR_Dr_GetName (void *driver)
void * OGR_Dr_CreateDataSource (void *driver, const char *path, char **options)
int OGR_Dr_DeleteDataSource (void *driver, char *)
void * OGR_Dr_Open (void *driver, const char *path, int bupdate)
@@ -119,7 +207,7 @@ cdef extern from "ogr_api.h":
void * OGR_FD_GetFieldDefn (void *featuredefn, int n)
int OGR_FD_GetGeomType (void *featuredefn)
char * OGR_FD_GetName (void *featuredefn)
- void * OGR_Fld_Create (char *name, int fieldtype)
+ void * OGR_Fld_Create (char *name, OGRFieldType fieldtype)
void OGR_Fld_Destroy (void *fielddefn)
char * OGR_Fld_GetNameRef (void *fielddefn)
int OGR_Fld_GetPrecision (void *fielddefn)
@@ -148,7 +236,7 @@ cdef extern from "ogr_api.h":
void OGR_G_ImportFromWkb (void *geometry, unsigned char *bytes, int nbytes)
int OGR_G_WkbSize (void *geometry)
OGRErr OGR_L_CreateFeature (void *layer, void *feature)
- int OGR_L_CreateField (void *layer, void *fielddefn, int flexible)
+ OGRErr OGR_L_CreateField (void *layer, void *fielddefn, int flexible)
OGRErr OGR_L_GetExtent (void *layer, void *extent, int force)
void * OGR_L_GetFeature (void *layer, int n)
int OGR_L_GetFeatureCount (void *layer, int m)
=====================================
fiona/ogrext2.pxd
=====================================
@@ -9,13 +9,102 @@ cdef extern from "ogr_core.h":
ctypedef int OGRErr
+ ctypedef enum OGRwkbGeometryType:
+ wkbUnknown
+ wkbPoint
+ wkbLineString
+ wkbPolygon
+ wkbMultiPoint
+ wkbMultiLineString
+ wkbMultiPolygon
+ wkbGeometryCollection
+ wkbCircularString
+ wkbCompoundCurve
+ wkbCurvePolygon
+ wkbMultiCurve
+ wkbMultiSurface
+ wkbCurve
+ wkbSurface
+ wkbPolyhedralSurface
+ wkbTIN
+ wkbTriangle
+ wkbNone
+ wkbLinearRing
+ wkbCircularStringZ
+ wkbCompoundCurveZ
+ wkbCurvePolygonZ
+ wkbMultiCurveZ
+ wkbMultiSurfaceZ
+ wkbCurveZ
+ wkbSurfaceZ
+ wkbPolyhedralSurfaceZ
+ wkbTINZ
+ wkbTriangleZ
+ wkbPointM
+ wkbLineStringM
+ wkbPolygonM
+ wkbMultiPointM
+ wkbMultiLineStringM
+ wkbMultiPolygonM
+ wkbGeometryCollectionM
+ wkbCircularStringM
+ wkbCompoundCurveM
+ wkbCurvePolygonM
+ wkbMultiCurveM
+ wkbMultiSurfaceM
+ wkbCurveM
+ wkbSurfaceM
+ wkbPolyhedralSurfaceM
+ wkbTINM
+ wkbTriangleM
+ wkbPointZM
+ wkbLineStringZM
+ wkbPolygonZM
+ wkbMultiPointZM
+ wkbMultiLineStringZM
+ wkbMultiPolygonZM
+ wkbGeometryCollectionZM
+ wkbCircularStringZM
+ wkbCompoundCurveZM
+ wkbCurvePolygonZM
+ wkbMultiCurveZM
+ wkbMultiSurfaceZM
+ wkbCurveZM
+ wkbSurfaceZM
+ wkbPolyhedralSurfaceZM
+ wkbTINZM
+ wkbTriangleZM
+ wkbPoint25D
+ wkbLineString25D
+ wkbPolygon25D
+ wkbMultiPoint25D
+ wkbMultiLineString25D
+ wkbMultiPolygon25D
+ wkbGeometryCollection25D
+
+ ctypedef enum OGRFieldType:
+ OFTInteger
+ OFTIntegerList
+ OFTReal
+ OFTRealList
+ OFTString
+ OFTStringList
+ OFTWideString
+ OFTWideStringList
+ OFTBinary
+ OFTDate
+ OFTTime
+ OFTDateTime
+ OFTInteger64
+ OFTInteger64List
+ OFTMaxType
+
ctypedef int OGRFieldSubType
- ctypedef enum OGRFieldSubType:
- OFSTNone = 0
- OFSTBoolean = 1
- OFSTInt16 = 2
- OFSTFloat32 = 3
- OFSTMaxSubType = 3
+ cdef int OFSTNone = 0
+ cdef int OFSTBoolean = 1
+ cdef int OFSTInt16 = 2
+ cdef int OFSTFloat32 = 3
+ cdef int OFSTMaxSubType = 3
ctypedef struct OGREnvelope:
double MinX
@@ -35,9 +124,9 @@ cdef extern from "gdal.h":
void * GDALGetDriverByName(const char * pszName)
void * GDALOpenEx(const char * pszFilename,
unsigned int nOpenFlags,
- const char ** papszAllowedDrivers,
- const char ** papszOpenOptions,
- const char *const *papszSibling1Files
+ const char *const *papszAllowedDrivers,
+ const char *const *papszOpenOptions,
+ const char *const *papszSiblingFiles
)
int GDAL_OF_UPDATE
int GDAL_OF_READONLY
@@ -147,7 +236,7 @@ cdef extern from "ogr_srs_api.h":
cdef extern from "ogr_api.h":
- char * OGR_Dr_GetName (void *driver)
+ const char * OGR_Dr_GetName (void *driver)
void * OGR_Dr_CreateDataSource (void *driver, const char *path, char **options)
int OGR_Dr_DeleteDataSource (void *driver, char *)
void * OGR_Dr_Open (void *driver, const char *path, int bupdate)
@@ -178,7 +267,7 @@ cdef extern from "ogr_api.h":
void * OGR_FD_GetFieldDefn (void *featuredefn, int n)
int OGR_FD_GetGeomType (void *featuredefn)
char * OGR_FD_GetName (void *featuredefn)
- void * OGR_Fld_Create (char *name, int fieldtype)
+ void * OGR_Fld_Create (char *name, OGRFieldType fieldtype)
void OGR_Fld_Destroy (void *fielddefn)
char * OGR_Fld_GetNameRef (void *fielddefn)
int OGR_Fld_GetPrecision (void *fielddefn)
@@ -209,7 +298,7 @@ cdef extern from "ogr_api.h":
void OGR_G_ImportFromWkb (void *geometry, unsigned char *bytes, int nbytes)
int OGR_G_WkbSize (void *geometry)
OGRErr OGR_L_CreateFeature (void *layer, void *feature)
- int OGR_L_CreateField (void *layer, void *fielddefn, int flexible)
+ OGRErr OGR_L_CreateField (void *layer, void *fielddefn, int flexible)
OGRErr OGR_L_GetExtent (void *layer, void *extent, int force)
void * OGR_L_GetFeature (void *layer, int n)
int OGR_L_GetFeatureCount (void *layer, int m)
=====================================
setup.py
=====================================
@@ -104,6 +104,7 @@ libraries = []
extra_link_args = []
gdal_output = [None for i in range(4)]
gdalversion = None
+language = None
if 'clean' not in sys.argv:
try:
@@ -172,6 +173,11 @@ if 'clean' not in sys.argv:
log.info("Copying proj data from %s" % projdatadir)
copy_data_tree(projdatadir, 'fiona/proj_data')
+ if "--cython-language" in sys.argv:
+ index = sys.argv.index("--cython-language")
+ sys.argv.pop(index)
+ language = sys.argv.pop(index).lower()
+
gdal_version_parts = gdalversion.split('.')
gdal_major_version = int(gdal_version_parts[0])
gdal_minor_version = int(gdal_version_parts[1])
@@ -182,11 +188,15 @@ ext_options = dict(
libraries=libraries,
extra_link_args=extra_link_args)
-ext_options_cpp = ext_options.copy()
# GDAL 2.3+ requires C++11
-if sys.platform == "win32":
- ext_options_cpp["extra_compile_args"] = ["/std:c++11"]
-else:
+
+if language == "c++":
+ ext_options["language"] = "c++"
+ if sys.platform != "win32":
+ ext_options["extra_compile_args"] = ["-std=c++11"]
+
+ext_options_cpp = ext_options.copy()
+if sys.platform != "win32":
ext_options_cpp["extra_compile_args"] = ["-std=c++11"]
=====================================
tests/conftest.py
=====================================
@@ -1,9 +1,11 @@
"""pytest fixtures and automatic test data generation."""
+
+import copy
import json
import os.path
+import shutil
import tarfile
import zipfile
-import copy
from click.testing import CliRunner
import pytest
@@ -54,6 +56,14 @@ def data_dir():
return os.path.abspath(os.path.join(os.path.dirname(__file__), 'data'))
+ at pytest.fixture(scope='function')
+def data(tmpdir, data_dir):
+ """A temporary directory containing a copy of the files in data."""
+ for filename in _COUTWILDRNP_FILES:
+ shutil.copy(os.path.join(data_dir, filename), str(tmpdir))
+ return tmpdir
+
+
@pytest.fixture(scope='session')
def path_curves_line_csv(data_dir):
"""Path to ```curves_line.csv``"""
@@ -250,4 +260,4 @@ def unittest_data_dir(data_dir, request):
@pytest.fixture(scope="class")
def unittest_path_coutwildrnp_shp(path_coutwildrnp_shp, request):
"""Makes shapefile path available to unittest tests"""
- request.cls.path_coutwildrnp_shp = path_coutwildrnp_shp
\ No newline at end of file
+ request.cls.path_coutwildrnp_shp = path_coutwildrnp_shp
=====================================
tests/test__env.py
=====================================
@@ -0,0 +1,125 @@
+"""Tests of _env util module"""
+
+import pytest
+
+from fiona._env import GDALDataFinder, PROJDataFinder
+
+from .conftest import gdal_version
+
+
+ at pytest.fixture
+def mock_wheel(tmpdir):
+ """A fake rasterio wheel"""
+ moduledir = tmpdir.mkdir("rasterio")
+ moduledir.ensure("__init__.py")
+ moduledir.ensure("_env.py")
+ moduledir.ensure("gdal_data/pcs.csv")
+ moduledir.ensure("proj_data/epsg")
+ return moduledir
+
+
+ at pytest.fixture
+def mock_fhs(tmpdir):
+ """A fake FHS system"""
+ tmpdir.ensure("share/gdal/pcs.csv")
+ tmpdir.ensure("share/proj/epsg")
+ return tmpdir
+
+
+ at pytest.fixture
+def mock_debian(tmpdir):
+ """A fake Debian multi-install system"""
+ tmpdir.ensure("share/gdal/1.11/pcs.csv")
+ tmpdir.ensure("share/gdal/2.0/pcs.csv")
+ tmpdir.ensure("share/gdal/2.1/pcs.csv")
+ tmpdir.ensure("share/gdal/2.2/pcs.csv")
+ tmpdir.ensure("share/gdal/2.3/pcs.csv")
+ tmpdir.ensure("share/gdal/2.4/pcs.csv")
+ tmpdir.ensure("share/proj/epsg")
+ return tmpdir
+
+
+def test_search_wheel_gdal_data_failure(tmpdir):
+ """Fail to find GDAL data in a non-wheel"""
+ finder = GDALDataFinder()
+ assert not finder.search_wheel(str(tmpdir))
+
+
+def test_search_wheel_gdal_data(mock_wheel):
+ """Find GDAL data in a wheel"""
+ finder = GDALDataFinder()
+ assert finder.search_wheel(str(mock_wheel.join("_env.py"))) == str(mock_wheel.join("gdal_data"))
+
+
+def test_search_prefix_gdal_data_failure(tmpdir):
+ """Fail to find GDAL data in a bogus prefix"""
+ finder = GDALDataFinder()
+ assert not finder.search_prefix(str(tmpdir))
+
+
+def test_search_prefix_gdal_data(mock_fhs):
+ """Find GDAL data under prefix"""
+ finder = GDALDataFinder()
+ assert finder.search_prefix(str(mock_fhs)) == str(mock_fhs.join("share").join("gdal"))
+
+
+def test_search_debian_gdal_data_failure(tmpdir):
+ """Fail to find GDAL data in a bogus Debian location"""
+ finder = GDALDataFinder()
+ assert not finder.search_debian(str(tmpdir))
+
+
+def test_search_debian_gdal_data(mock_debian):
+ """Find GDAL data under Debian locations"""
+ finder = GDALDataFinder()
+ assert finder.search_debian(str(mock_debian)) == str(mock_debian.join("share").join("gdal").join("{}.{}".format(gdal_version.major, gdal_version.minor)))
+
+
+def test_search_gdal_data_wheel(mock_wheel):
+ finder = GDALDataFinder()
+ assert finder.search(str(mock_wheel.join("_env.py"))) == str(mock_wheel.join("gdal_data"))
+
+
+def test_search_gdal_data_fhs(mock_fhs):
+ finder = GDALDataFinder()
+ assert finder.search(str(mock_fhs)) == str(mock_fhs.join("share").join("gdal"))
+
+
+def test_search_gdal_data_debian(mock_debian):
+ """Find GDAL data under Debian locations"""
+ finder = GDALDataFinder()
+ assert finder.search(str(mock_debian)) == str(mock_debian.join("share").join("gdal").join("{}.{}".format(gdal_version.major, gdal_version.minor)))
+
+
+def test_search_wheel_proj_data_failure(tmpdir):
+ """Fail to find GDAL data in a non-wheel"""
+ finder = PROJDataFinder()
+ assert not finder.search_wheel(str(tmpdir))
+
+
+def test_search_wheel_proj_data(mock_wheel):
+ """Find GDAL data in a wheel"""
+ finder = PROJDataFinder()
+ assert finder.search_wheel(str(mock_wheel.join("_env.py"))) == str(mock_wheel.join("proj_data"))
+
+
+def test_search_prefix_proj_data_failure(tmpdir):
+ """Fail to find GDAL data in a bogus prefix"""
+ finder = PROJDataFinder()
+ assert not finder.search_prefix(str(tmpdir))
+
+
+def test_search_prefix_proj_data(mock_fhs):
+ """Find GDAL data under prefix"""
+ finder = PROJDataFinder()
+ assert finder.search_prefix(str(mock_fhs)) == str(mock_fhs.join("share").join("proj"))
+
+
+def test_search_proj_data_wheel(mock_wheel):
+ finder = PROJDataFinder()
+ assert finder.search(str(mock_wheel.join("_env.py"))) == str(mock_wheel.join("proj_data"))
+
+
+def test_search_proj_data_fhs(mock_fhs):
+ finder = PROJDataFinder()
+ assert finder.search(str(mock_fhs)) == str(mock_fhs.join("share").join("proj"))
=====================================
tests/test_bigint.py
=====================================
@@ -18,7 +18,7 @@ of 64 bit integer, OFTReal is chosen)
import pytest
import fiona
-from fiona.ogrext import calc_gdal_version_num, get_gdal_version_num
+from fiona.env import calc_gdal_version_num, get_gdal_version_num
@pytest.mark.xfail(fiona.gdal_version.major < 2,
=====================================
tests/test_collection.py
=====================================
@@ -206,6 +206,7 @@ class TestReading(object):
with fiona.open(path_coutwildrnp_shp, "r") as c:
assert c.name == 'coutwildrnp'
assert len(c) == 67
+ assert c.crs
assert c.closed
def test_iter_one(self):
@@ -866,3 +867,10 @@ def test_collection_zip_http():
ds = fiona.Collection('http://raw.githubusercontent.com/OSGeo/gdal/master/autotest/ogr/data/poly.zip', vsi='zip+http')
assert ds.path == '/vsizip/vsicurl/http://raw.githubusercontent.com/OSGeo/gdal/master/autotest/ogr/data/poly.zip'
assert len(ds) == 10
+
+
+def test_encoding_option_warning(tmpdir, caplog):
+ """There is no ENCODING creation option log warning for GeoJSON"""
+ ds = fiona.Collection(str(tmpdir.join("test.geojson")), "w", driver="GeoJSON", crs="epsg:4326",
+ schema={"geometry": "Point", "properties": {"foo": "int"}})
+ assert not caplog.text
=====================================
tests/test_compound_crs.py
=====================================
@@ -0,0 +1,11 @@
+"""Test of compound CRS crash avoidance"""
+
+import fiona
+
+
+def test_compound_crs(data):
+ """Don't crash"""
+ prj = data.join("coutwildrnp.prj")
+ prj.write("""COMPD_CS["unknown",GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],TOWGS84[0,0,0,0,0,0,0],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433],AUTHORITY["EPSG","4326"]],VERT_CS["unknown",VERT_DATUM["unknown",2005],UNIT["metre",1.0,AUTHORITY["EPSG","9001"]],AXIS["Up",UP]]]""")
+ with fiona.open(str(data.join("coutwildrnp.shp"))) as collection:
+ assert collection.crs == {}
=====================================
tests/test_datetime.py
=====================================
@@ -50,10 +50,10 @@ class TestDateFieldSupport:
}
},
]
- with fiona.open(path, "w", driver=driver, schema=schema) as collection:
+ with fiona.Env(), fiona.open(path, "w", driver=driver, schema=schema) as collection:
collection.writerecords(records)
-
- with fiona.open(path, "r") as collection:
+
+ with fiona.Env(), fiona.open(path, "r") as collection:
schema = collection.schema
features = list(collection)
@@ -64,7 +64,7 @@ class TestDateFieldSupport:
def test_shapefile(self):
driver = "ESRI Shapefile"
schema, features = self.write_data(driver)
-
+
assert schema["properties"]["date"] == "date"
assert features[0]["properties"]["date"] == DATE_EXAMPLE
assert features[1]["properties"]["date"] is None
@@ -73,7 +73,7 @@ class TestDateFieldSupport:
def test_gpkg(self):
driver = "GPKG"
schema, features = self.write_data(driver)
-
+
assert schema["properties"]["date"] == "date"
assert features[0]["properties"]["date"] == DATE_EXAMPLE
assert features[1]["properties"]["date"] is None
@@ -83,7 +83,7 @@ class TestDateFieldSupport:
# GDAL 1: date string format uses / instead of -
driver = "GeoJSON"
schema, features = self.write_data(driver)
-
+
if GDAL_MAJOR_VER >= 2:
assert schema["properties"]["date"] == "date"
assert features[0]["properties"]["date"] == DATE_EXAMPLE
@@ -95,7 +95,7 @@ class TestDateFieldSupport:
def test_mapinfo(self):
driver = "MapInfo File"
schema, features = self.write_data(driver)
-
+
assert schema["properties"]["date"] == "date"
assert features[0]["properties"]["date"] == DATE_EXAMPLE
assert features[1]["properties"]["date"] is None
@@ -126,10 +126,10 @@ class TestDatetimeFieldSupport:
}
},
]
- with fiona.open(path, "w", driver=driver, schema=schema) as collection:
+ with fiona.Env(), fiona.open(path, "w", driver=driver, schema=schema) as collection:
collection.writerecords(records)
-
- with fiona.open(path, "r") as collection:
+
+ with fiona.Env(), fiona.open(path, "r") as collection:
schema = collection.schema
features = list(collection)
@@ -143,7 +143,7 @@ class TestDatetimeFieldSupport:
with pytest.raises(DriverSupportError):
schema, features = self.write_data(driver)
-
+
# assert schema["properties"]["datetime"] == "date"
# assert features[0]["properties"]["datetime"] == "2018-03-25"
# assert features[1]["properties"]["datetime"] is None
@@ -152,7 +152,7 @@ class TestDatetimeFieldSupport:
def test_gpkg(self):
# GDAL 1: datetime silently downgraded to date
driver = "GPKG"
-
+
if GDAL_MAJOR_VER >= 2:
schema, features = self.write_data(driver)
assert schema["properties"]["datetime"] == "datetime"
@@ -167,7 +167,7 @@ class TestDatetimeFieldSupport:
# GDAL 1: date string format uses / instead of -
driver = "GeoJSON"
schema, features = self.write_data(driver)
-
+
if GDAL_MAJOR_VER >= 2:
assert schema["properties"]["datetime"] == "datetime"
assert features[0]["properties"]["datetime"] == DATETIME_EXAMPLE
@@ -179,7 +179,7 @@ class TestDatetimeFieldSupport:
def test_mapinfo(self):
driver = "MapInfo File"
schema, features = self.write_data(driver)
-
+
assert schema["properties"]["datetime"] == "datetime"
assert features[0]["properties"]["datetime"] == DATETIME_EXAMPLE
assert features[1]["properties"]["datetime"] is None
@@ -210,10 +210,10 @@ class TestTimeFieldSupport:
}
},
]
- with fiona.open(path, "w", driver=driver, schema=schema) as collection:
+ with fiona.Env(), fiona.open(path, "w", driver=driver, schema=schema) as collection:
collection.writerecords(records)
-
- with fiona.open(path, "r") as collection:
+
+ with fiona.Env(), fiona.open(path, "r") as collection:
schema = collection.schema
features = list(collection)
@@ -235,7 +235,7 @@ class TestTimeFieldSupport:
with pytest.raises(DriverSupportError):
schema, features = self.write_data(driver)
-
+
# if GDAL_MAJOR_VER >= 2:
# assert schema["properties"]["time"] == "str"
# assert features[0]["properties"]["time"] == TIME_EXAMPLE
@@ -247,7 +247,7 @@ class TestTimeFieldSupport:
# GDAL 1: time field silently converted to string
driver = "GeoJSON"
schema, features = self.write_data(driver)
-
+
if GDAL_MAJOR_VER >= 2:
assert schema["properties"]["time"] == "time"
else:
@@ -259,7 +259,7 @@ class TestTimeFieldSupport:
# GDAL 2: null time is converted to 00:00:00 (regression?)
driver = "MapInfo File"
schema, features = self.write_data(driver)
-
+
assert schema["properties"]["time"] == "time"
assert features[0]["properties"]["time"] == TIME_EXAMPLE
if GDAL_MAJOR_VER >= 2:
=====================================
tests/test_env.py
=====================================
@@ -1,16 +1,20 @@
"""Tests of fiona.env"""
+import os
+import sys
+
import pytest
import fiona
-import fiona.env
+from fiona import _env
+from fiona.env import getenv, ensure_env, ensure_env_with_credentials
from fiona.session import AWSSession
def test_nested_credentials(monkeypatch):
"""Check that rasterio.open() doesn't wipe out surrounding credentials"""
- @fiona.env.ensure_env_with_credentials
+ @ensure_env_with_credentials
def fake_opener(path):
return fiona.env.getenv()
@@ -23,3 +27,65 @@ def test_nested_credentials(monkeypatch):
gdalenv = fake_opener('s3://foo/bar')
assert gdalenv['AWS_ACCESS_KEY_ID'] == 'foo'
assert gdalenv['AWS_SECRET_ACCESS_KEY'] == 'bar'
+
+
+def test_ensure_env_decorator(gdalenv):
+ @ensure_env
+ def f():
+ return getenv()['RASTERIO_ENV']
+ assert f() is True
+
+
+def test_ensure_env_decorator_sets_gdal_data(gdalenv, monkeypatch):
+ """fiona.env.ensure_env finds GDAL from environment"""
+ @ensure_env
+ def f():
+ return getenv()['GDAL_DATA']
+
+ monkeypatch.setenv('GDAL_DATA', '/lol/wut')
+ assert f() == '/lol/wut'
+
+
+def test_ensure_env_decorator_sets_gdal_data_prefix(gdalenv, monkeypatch, tmpdir):
+ """fiona.env.ensure_env finds GDAL data under a prefix"""
+ @ensure_env
+ def f():
+ return getenv()['GDAL_DATA']
+
+ tmpdir.ensure("share/gdal/pcs.csv")
+ monkeypatch.delenv('GDAL_DATA', raising=False)
+ monkeypatch.setattr(_env, '__file__', str(tmpdir.join("fake.py")))
+ monkeypatch.setattr(sys, 'prefix', str(tmpdir))
+
+ assert f() == str(tmpdir.join("share").join("gdal"))
+
+
+def test_ensure_env_decorator_sets_gdal_data_wheel(gdalenv, monkeypatch, tmpdir):
+ """fiona.env.ensure_env finds GDAL data in a wheel"""
+ @ensure_env
+ def f():
+ return getenv()['GDAL_DATA']
+
+ tmpdir.ensure("gdal_data/pcs.csv")
+ monkeypatch.delenv('GDAL_DATA', raising=False)
+ monkeypatch.setattr(_env, '__file__', str(tmpdir.join(os.path.basename(_env.__file__))))
+
+ assert f() == str(tmpdir.join("gdal_data"))
+
+
+def test_ensure_env_with_decorator_sets_gdal_data_wheel(gdalenv, monkeypatch, tmpdir):
+ """fiona.env.ensure_env finds GDAL data in a wheel"""
+ @ensure_env_with_credentials
+ def f(*args):
+ return getenv()['GDAL_DATA']
+
+ tmpdir.ensure("gdal_data/pcs.csv")
+ monkeypatch.delenv('GDAL_DATA', raising=False)
+ monkeypatch.setattr(_env, '__file__', str(tmpdir.join(os.path.basename(_env.__file__))))
+
+ assert f("foo") == str(tmpdir.join("gdal_data"))
+
+
+def test_ensure_env_crs(path_coutwildrnp_shp):
+ """Decoration of .crs works"""
+ assert fiona.open(path_coutwildrnp_shp).crs
=====================================
tests/test_schema.py
=====================================
@@ -6,7 +6,7 @@ import pytest
import fiona
from fiona.errors import SchemaError, UnsupportedGeometryTypeError
from fiona.schema import FIELD_TYPES, normalize_field_type
-from fiona.ogrext import calc_gdal_version_num, get_gdal_version_num
+from fiona.env import calc_gdal_version_num, get_gdal_version_num
def test_schema_ordering_items(tmpdir):
=====================================
tests/test_unicode.py
=====================================
@@ -5,10 +5,12 @@ import os
import shutil
import sys
import tempfile
+from collections import OrderedDict
import pytest
import fiona
+from fiona.errors import SchemaError
class TestUnicodePath(object):
@@ -116,3 +118,34 @@ class TestUnicodeStringField(object):
f = next(iter(c))
assert f['properties']['label'] == u'徐汇区'
assert f['properties']['num'] == 0
+
+ @pytest.mark.skipif(sys.platform == 'win32', reason="GDAL binary used on AppVeyor does not have a working libiconv")
+ def test_gb2312_field_wrong_encoding(self):
+ """Attempt to create field with a name not supported by the encoding
+
+ ESRI Shapefile driver defaults to ISO-8859-1 encoding if none is
+ specified. This doesn't support the field name used. Previously this
+ went undetected and would raise a KeyError later when the user tried
+ to write a feature to the layer. Instead we raise a more useful error.
+
+ See GH#595.
+ """
+ field_name = u"区县名称"
+ meta = {
+ "schema": {
+ "properties": OrderedDict([(field_name, "int")]),
+ "geometry": "Point",
+ },
+ "driver": "ESRI Shapefile",
+ }
+ feature = {
+ "properties": {field_name: 123},
+ "geometry": {"type": "Point", "coordinates": [1, 2]}
+ }
+ # when encoding is specified, write is successful
+ with fiona.open(os.path.join(self.tempdir, "test1.shp"), "w", encoding="GB2312", **meta) as collection:
+ collection.write(feature)
+ # no encoding
+ with pytest.raises(SchemaError):
+ fiona.open(os.path.join(self.tempdir, "test2.shp"), "w", **meta)
+
View it on GitLab: https://salsa.debian.org/debian-gis-team/fiona/commit/652d6524a2d2f22a232e856626e16abfa30351f3
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/fiona/commit/652d6524a2d2f22a232e856626e16abfa30351f3
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20181116/f93c3807/attachment-0001.html>
More information about the Pkg-grass-devel
mailing list