pyogrio 0.10.0__tar.gz → 0.11.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pyogrio might be problematic. Click here for more details.
- {pyogrio-0.10.0/pyogrio.egg-info → pyogrio-0.11.1}/PKG-INFO +25 -30
- pyogrio-0.11.1/README.md +70 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/__init__.py +12 -10
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/_compat.py +8 -0
- pyogrio-0.11.1/pyogrio/_err.pxd +9 -0
- pyogrio-0.11.1/pyogrio/_err.pyx +441 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/_geometry.pxd +1 -1
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/_geometry.pyx +48 -48
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/_io.pyx +463 -251
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/_ogr.pxd +130 -68
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/_ogr.pyx +58 -37
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/_version.py +3 -3
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/_vsi.pxd +1 -1
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/_vsi.pyx +8 -9
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/geopandas.py +91 -43
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/tests/conftest.py +8 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/tests/test_arrow.py +3 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/tests/test_core.py +8 -4
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/tests/test_geopandas_io.py +270 -45
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/tests/test_path.py +10 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/tests/test_raw_io.py +6 -2
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/util.py +15 -2
- {pyogrio-0.10.0 → pyogrio-0.11.1/pyogrio.egg-info}/PKG-INFO +25 -30
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyproject.toml +12 -6
- {pyogrio-0.10.0 → pyogrio-0.11.1}/setup.py +1 -1
- pyogrio-0.10.0/README.md +0 -76
- pyogrio-0.10.0/pyogrio/_err.pxd +0 -4
- pyogrio-0.10.0/pyogrio/_err.pyx +0 -250
- {pyogrio-0.10.0 → pyogrio-0.11.1}/LICENSE +0 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/MANIFEST.in +0 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/_env.py +0 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/_io.pxd +0 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/arrow_bridge.h +0 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/core.py +0 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/errors.py +0 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/raw.py +0 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/tests/__init__.py +0 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/tests/fixtures/README.md +0 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/tests/fixtures/curve.gpkg +0 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/tests/fixtures/curvepolygon.gpkg +0 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/tests/fixtures/line_zm.gpkg +0 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/tests/fixtures/multisurface.gpkg +0 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/tests/fixtures/naturalearth_lowres/naturalearth_lowres.cpg +0 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/tests/fixtures/naturalearth_lowres/naturalearth_lowres.dbf +0 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/tests/fixtures/naturalearth_lowres/naturalearth_lowres.prj +0 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/tests/fixtures/naturalearth_lowres/naturalearth_lowres.shp +0 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/tests/fixtures/naturalearth_lowres/naturalearth_lowres.shx +0 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/tests/fixtures/sample.osm.pbf +0 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/tests/fixtures/test_gpkg_nulls.gpkg +0 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio/tests/test_util.py +0 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio.egg-info/SOURCES.txt +0 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio.egg-info/dependency_links.txt +0 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio.egg-info/requires.txt +0 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/pyogrio.egg-info/top_level.txt +0 -0
- {pyogrio-0.10.0 → pyogrio-0.11.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: pyogrio
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.11.1
|
|
4
4
|
Summary: Vectorized spatial vector file format I/O using GDAL/OGR
|
|
5
5
|
Author: pyogrio contributors
|
|
6
6
|
Author-email: "Brendan C. Ward" <bcward@astutespruce.com>
|
|
@@ -50,39 +50,34 @@ Provides-Extra: benchmark
|
|
|
50
50
|
Requires-Dist: pytest-benchmark; extra == "benchmark"
|
|
51
51
|
Provides-Extra: geopandas
|
|
52
52
|
Requires-Dist: geopandas; extra == "geopandas"
|
|
53
|
+
Dynamic: license-file
|
|
53
54
|
|
|
54
|
-
# pyogrio -
|
|
55
|
+
# pyogrio - bulk-oriented spatial vector file I/O using GDAL/OGR
|
|
55
56
|
|
|
56
|
-
Pyogrio provides
|
|
57
|
-
[
|
|
58
|
-
|
|
59
|
-
have geometries, such as points, lines, or
|
|
60
|
-
with potentially many columns worth of data.
|
|
57
|
+
Pyogrio provides fast, bulk-oriented read and write access to
|
|
58
|
+
[GDAL/OGR](https://gdal.org/en/latest/drivers/vector/index.html) vector data
|
|
59
|
+
sources, such as ESRI Shapefile, GeoPackage, GeoJSON, and several others.
|
|
60
|
+
Vector data sources typically have geometries, such as points, lines, or
|
|
61
|
+
polygons, and associated records with potentially many columns worth of data.
|
|
61
62
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
63
|
+
The typical use is to read or write these data sources to/from
|
|
64
|
+
[GeoPandas](https://github.com/geopandas/geopandas) `GeoDataFrames`. Because
|
|
65
|
+
the geometry column is optional, reading or writing only non-spatial data is
|
|
66
|
+
also possible. Hence, GeoPackage attribute tables, DBF files, or CSV files are
|
|
67
|
+
also supported.
|
|
67
68
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
`GeoDataFrames`, read just the non-geometry columns into Pandas `DataFrames`,
|
|
74
|
-
or even read non-spatial data sources that exist alongside vector data sources,
|
|
75
|
-
such as tables in a ESRI File Geodatabase, or antiquated DBF files.
|
|
69
|
+
Pyogrio is fast because it uses pre-compiled bindings for GDAL/OGR to read and
|
|
70
|
+
write the data records in bulk. This approach avoids multiple steps of
|
|
71
|
+
converting to and from Python data types within Python, so performance becomes
|
|
72
|
+
primarily limited by the underlying I/O speed of data source drivers in
|
|
73
|
+
GDAL/OGR.
|
|
76
74
|
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
We have seen \>5-10x speedups reading files and \>5-20x speedups writing files
|
|
76
|
+
compared to using row-per-row approaches (e.g. Fiona).
|
|
79
77
|
|
|
80
78
|
Read the documentation for more information:
|
|
81
79
|
[https://pyogrio.readthedocs.io](https://pyogrio.readthedocs.io/en/latest/).
|
|
82
80
|
|
|
83
|
-
WARNING: Pyogrio is still at an early version and the API is subject to
|
|
84
|
-
substantial change. Please see [CHANGES](CHANGES.md).
|
|
85
|
-
|
|
86
81
|
## Requirements
|
|
87
82
|
|
|
88
83
|
Supports Python 3.9 - 3.13 and GDAL 3.4.x - 3.9.x.
|
|
@@ -105,9 +100,9 @@ for more information.
|
|
|
105
100
|
|
|
106
101
|
## Supported vector formats
|
|
107
102
|
|
|
108
|
-
Pyogrio supports
|
|
109
|
-
|
|
110
|
-
|
|
103
|
+
Pyogrio supports most common vector data source formats (provided they are also
|
|
104
|
+
supported by GDAL/OGR), including ESRI Shapefile, GeoPackage, GeoJSON, and
|
|
105
|
+
FlatGeobuf.
|
|
111
106
|
|
|
112
107
|
Please see the [list of supported formats](https://pyogrio.readthedocs.io/en/latest/supported_formats.html)
|
|
113
108
|
for more information.
|
|
@@ -117,7 +112,7 @@ for more information.
|
|
|
117
112
|
Please read the [introduction](https://pyogrio.readthedocs.io/en/latest/supported_formats.html)
|
|
118
113
|
for more information and examples to get started using Pyogrio.
|
|
119
114
|
|
|
120
|
-
You can also check out the
|
|
115
|
+
You can also check out the [API documentation](https://pyogrio.readthedocs.io/en/latest/api.html)
|
|
121
116
|
for full details on using the API.
|
|
122
117
|
|
|
123
118
|
## Credits
|
pyogrio-0.11.1/README.md
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# pyogrio - bulk-oriented spatial vector file I/O using GDAL/OGR
|
|
2
|
+
|
|
3
|
+
Pyogrio provides fast, bulk-oriented read and write access to
|
|
4
|
+
[GDAL/OGR](https://gdal.org/en/latest/drivers/vector/index.html) vector data
|
|
5
|
+
sources, such as ESRI Shapefile, GeoPackage, GeoJSON, and several others.
|
|
6
|
+
Vector data sources typically have geometries, such as points, lines, or
|
|
7
|
+
polygons, and associated records with potentially many columns worth of data.
|
|
8
|
+
|
|
9
|
+
The typical use is to read or write these data sources to/from
|
|
10
|
+
[GeoPandas](https://github.com/geopandas/geopandas) `GeoDataFrames`. Because
|
|
11
|
+
the geometry column is optional, reading or writing only non-spatial data is
|
|
12
|
+
also possible. Hence, GeoPackage attribute tables, DBF files, or CSV files are
|
|
13
|
+
also supported.
|
|
14
|
+
|
|
15
|
+
Pyogrio is fast because it uses pre-compiled bindings for GDAL/OGR to read and
|
|
16
|
+
write the data records in bulk. This approach avoids multiple steps of
|
|
17
|
+
converting to and from Python data types within Python, so performance becomes
|
|
18
|
+
primarily limited by the underlying I/O speed of data source drivers in
|
|
19
|
+
GDAL/OGR.
|
|
20
|
+
|
|
21
|
+
We have seen \>5-10x speedups reading files and \>5-20x speedups writing files
|
|
22
|
+
compared to using row-per-row approaches (e.g. Fiona).
|
|
23
|
+
|
|
24
|
+
Read the documentation for more information:
|
|
25
|
+
[https://pyogrio.readthedocs.io](https://pyogrio.readthedocs.io/en/latest/).
|
|
26
|
+
|
|
27
|
+
## Requirements
|
|
28
|
+
|
|
29
|
+
Supports Python 3.9 - 3.13 and GDAL 3.4.x - 3.9.x.
|
|
30
|
+
|
|
31
|
+
Reading to GeoDataFrames requires `geopandas>=0.12` with `shapely>=2`.
|
|
32
|
+
|
|
33
|
+
Additionally, installing `pyarrow` in combination with GDAL 3.6+ enables
|
|
34
|
+
a further speed-up when specifying `use_arrow=True`.
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
Pyogrio is currently available on
|
|
39
|
+
[conda-forge](https://anaconda.org/conda-forge/pyogrio)
|
|
40
|
+
and [PyPI](https://pypi.org/project/pyogrio/)
|
|
41
|
+
for Linux, MacOS, and Windows.
|
|
42
|
+
|
|
43
|
+
Please read the
|
|
44
|
+
[installation documentation](https://pyogrio.readthedocs.io/en/latest/install.html)
|
|
45
|
+
for more information.
|
|
46
|
+
|
|
47
|
+
## Supported vector formats
|
|
48
|
+
|
|
49
|
+
Pyogrio supports most common vector data source formats (provided they are also
|
|
50
|
+
supported by GDAL/OGR), including ESRI Shapefile, GeoPackage, GeoJSON, and
|
|
51
|
+
FlatGeobuf.
|
|
52
|
+
|
|
53
|
+
Please see the [list of supported formats](https://pyogrio.readthedocs.io/en/latest/supported_formats.html)
|
|
54
|
+
for more information.
|
|
55
|
+
|
|
56
|
+
## Getting started
|
|
57
|
+
|
|
58
|
+
Please read the [introduction](https://pyogrio.readthedocs.io/en/latest/supported_formats.html)
|
|
59
|
+
for more information and examples to get started using Pyogrio.
|
|
60
|
+
|
|
61
|
+
You can also check out the [API documentation](https://pyogrio.readthedocs.io/en/latest/api.html)
|
|
62
|
+
for full details on using the API.
|
|
63
|
+
|
|
64
|
+
## Credits
|
|
65
|
+
|
|
66
|
+
This project is made possible by the tremendous efforts of the GDAL, Fiona, and
|
|
67
|
+
Geopandas communities.
|
|
68
|
+
|
|
69
|
+
- Core I/O methods and supporting functions adapted from [Fiona](https://github.com/Toblerity/Fiona)
|
|
70
|
+
- Inspired by [Fiona PR](https://github.com/Toblerity/Fiona/pull/540/files)
|
|
@@ -4,7 +4,9 @@ try:
|
|
|
4
4
|
# we try importing shapely, to ensure it is imported (and it can load its
|
|
5
5
|
# own GEOS copy) before we load GDAL and its linked GEOS
|
|
6
6
|
import shapely
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
if shapely.__version__ < "2.0.0":
|
|
9
|
+
import shapely.geos
|
|
8
10
|
except Exception:
|
|
9
11
|
pass
|
|
10
12
|
|
|
@@ -32,24 +34,24 @@ __version__ = get_versions()["version"]
|
|
|
32
34
|
del get_versions
|
|
33
35
|
|
|
34
36
|
__all__ = [
|
|
35
|
-
"
|
|
37
|
+
"__gdal_geos_version__",
|
|
38
|
+
"__gdal_version__",
|
|
39
|
+
"__gdal_version_string__",
|
|
40
|
+
"__version__",
|
|
36
41
|
"detect_write_driver",
|
|
37
|
-
"list_layers",
|
|
38
|
-
"read_bounds",
|
|
39
|
-
"read_info",
|
|
40
|
-
"set_gdal_config_options",
|
|
41
42
|
"get_gdal_config_option",
|
|
42
43
|
"get_gdal_data_path",
|
|
44
|
+
"list_drivers",
|
|
45
|
+
"list_layers",
|
|
43
46
|
"open_arrow",
|
|
44
47
|
"read_arrow",
|
|
48
|
+
"read_bounds",
|
|
45
49
|
"read_dataframe",
|
|
50
|
+
"read_info",
|
|
51
|
+
"set_gdal_config_options",
|
|
46
52
|
"vsi_listtree",
|
|
47
53
|
"vsi_rmtree",
|
|
48
54
|
"vsi_unlink",
|
|
49
55
|
"write_arrow",
|
|
50
56
|
"write_dataframe",
|
|
51
|
-
"__gdal_version__",
|
|
52
|
-
"__gdal_version_string__",
|
|
53
|
-
"__gdal_geos_version__",
|
|
54
|
-
"__version__",
|
|
55
57
|
]
|
|
@@ -33,15 +33,23 @@ HAS_ARROW_API = __gdal_version__ >= (3, 6, 0)
|
|
|
33
33
|
HAS_ARROW_WRITE_API = __gdal_version__ >= (3, 8, 0)
|
|
34
34
|
HAS_PYARROW = pyarrow is not None
|
|
35
35
|
HAS_PYPROJ = pyproj is not None
|
|
36
|
+
PYARROW_GE_19 = pyarrow is not None and Version(pyarrow.__version__) >= Version(
|
|
37
|
+
"19.0.0"
|
|
38
|
+
)
|
|
36
39
|
|
|
37
40
|
HAS_GEOPANDAS = geopandas is not None
|
|
38
41
|
|
|
39
42
|
PANDAS_GE_15 = pandas is not None and Version(pandas.__version__) >= Version("1.5.0")
|
|
40
43
|
PANDAS_GE_20 = pandas is not None and Version(pandas.__version__) >= Version("2.0.0")
|
|
41
44
|
PANDAS_GE_22 = pandas is not None and Version(pandas.__version__) >= Version("2.2.0")
|
|
45
|
+
PANDAS_GE_30 = pandas is not None and Version(pandas.__version__) >= Version("3.0.0dev")
|
|
42
46
|
|
|
47
|
+
GDAL_GE_352 = __gdal_version__ >= (3, 5, 2)
|
|
48
|
+
GDAL_GE_37 = __gdal_version__ >= (3, 7, 0)
|
|
43
49
|
GDAL_GE_38 = __gdal_version__ >= (3, 8, 0)
|
|
50
|
+
GDAL_GE_311 = __gdal_version__ >= (3, 11, 0)
|
|
44
51
|
|
|
45
52
|
HAS_GDAL_GEOS = __gdal_geos_version__ is not None
|
|
46
53
|
|
|
47
54
|
HAS_SHAPELY = shapely is not None and Version(shapely.__version__) >= Version("2.0.0")
|
|
55
|
+
SHAPELY_GE_21 = shapely is not None and Version(shapely.__version__) >= Version("2.1.0")
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
cdef object check_last_error()
|
|
2
|
+
cdef int check_int(int retval) except -1
|
|
3
|
+
cdef void *check_pointer(void *ptr) except NULL
|
|
4
|
+
|
|
5
|
+
cdef class ErrorHandler:
|
|
6
|
+
cdef object error_stack
|
|
7
|
+
cdef int check_int(self, int retval, bint squash_errors) except -1
|
|
8
|
+
cdef void *check_pointer(self, void *ptr, bint squash_errors) except NULL
|
|
9
|
+
cdef void _handle_error_stack(self, bint squash_errors)
|
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
"""Error handling code for GDAL/OGR.
|
|
2
|
+
|
|
3
|
+
Ported from fiona::_err.pyx
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import contextlib
|
|
7
|
+
import warnings
|
|
8
|
+
from contextvars import ContextVar
|
|
9
|
+
from itertools import zip_longest
|
|
10
|
+
|
|
11
|
+
from pyogrio._ogr cimport (
|
|
12
|
+
CE_Warning, CE_Failure, CE_Fatal, CPLErrorReset,
|
|
13
|
+
CPLGetLastErrorType, CPLGetLastErrorNo, CPLGetLastErrorMsg,
|
|
14
|
+
OGRERR_NONE, CPLErr, CPLErrorHandler, CPLDefaultErrorHandler,
|
|
15
|
+
CPLPopErrorHandler, CPLPushErrorHandler)
|
|
16
|
+
|
|
17
|
+
_ERROR_STACK = ContextVar("error_stack")
|
|
18
|
+
_ERROR_STACK.set([])
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class CPLE_BaseError(Exception):
|
|
22
|
+
"""Base CPL error class.
|
|
23
|
+
|
|
24
|
+
For internal use within Cython only.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, error, errno, errmsg):
|
|
28
|
+
self.error = error
|
|
29
|
+
self.errno = errno
|
|
30
|
+
self.errmsg = errmsg
|
|
31
|
+
|
|
32
|
+
def __str__(self):
|
|
33
|
+
return self.__unicode__()
|
|
34
|
+
|
|
35
|
+
def __unicode__(self):
|
|
36
|
+
return u"{}".format(self.errmsg)
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def args(self):
|
|
40
|
+
return self.error, self.errno, self.errmsg
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class CPLE_AppDefinedError(CPLE_BaseError):
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class CPLE_OutOfMemoryError(CPLE_BaseError):
|
|
48
|
+
pass
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class CPLE_FileIOError(CPLE_BaseError):
|
|
52
|
+
pass
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class CPLE_OpenFailedError(CPLE_BaseError):
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class CPLE_IllegalArgError(CPLE_BaseError):
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class CPLE_NotSupportedError(CPLE_BaseError):
|
|
64
|
+
pass
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class CPLE_AssertionFailedError(CPLE_BaseError):
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class CPLE_NoWriteAccessError(CPLE_BaseError):
|
|
72
|
+
pass
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class CPLE_UserInterruptError(CPLE_BaseError):
|
|
76
|
+
pass
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class ObjectNullError(CPLE_BaseError):
|
|
80
|
+
pass
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class CPLE_HttpResponseError(CPLE_BaseError):
|
|
84
|
+
pass
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class CPLE_AWSBucketNotFoundError(CPLE_BaseError):
|
|
88
|
+
pass
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class CPLE_AWSObjectNotFoundError(CPLE_BaseError):
|
|
92
|
+
pass
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class CPLE_AWSAccessDeniedError(CPLE_BaseError):
|
|
96
|
+
pass
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class CPLE_AWSInvalidCredentialsError(CPLE_BaseError):
|
|
100
|
+
pass
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class CPLE_AWSSignatureDoesNotMatchError(CPLE_BaseError):
|
|
104
|
+
pass
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class CPLE_AWSError(CPLE_BaseError):
|
|
108
|
+
pass
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class NullPointerError(CPLE_BaseError):
|
|
112
|
+
"""
|
|
113
|
+
Returned from check_pointer when a NULL pointer is passed, but no GDAL
|
|
114
|
+
error was raised.
|
|
115
|
+
"""
|
|
116
|
+
pass
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class CPLError(CPLE_BaseError):
|
|
120
|
+
"""
|
|
121
|
+
Returned from check_int when a error code is returned, but no GDAL
|
|
122
|
+
error was set.
|
|
123
|
+
"""
|
|
124
|
+
pass
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
# Map of GDAL error numbers to the Python exceptions.
|
|
128
|
+
exception_map = {
|
|
129
|
+
1: CPLE_AppDefinedError,
|
|
130
|
+
2: CPLE_OutOfMemoryError,
|
|
131
|
+
3: CPLE_FileIOError,
|
|
132
|
+
4: CPLE_OpenFailedError,
|
|
133
|
+
5: CPLE_IllegalArgError,
|
|
134
|
+
6: CPLE_NotSupportedError,
|
|
135
|
+
7: CPLE_AssertionFailedError,
|
|
136
|
+
8: CPLE_NoWriteAccessError,
|
|
137
|
+
9: CPLE_UserInterruptError,
|
|
138
|
+
10: ObjectNullError,
|
|
139
|
+
|
|
140
|
+
# error numbers 11-16 are introduced in GDAL 2.1. See
|
|
141
|
+
# https://github.com/OSGeo/gdal/pull/98.
|
|
142
|
+
11: CPLE_HttpResponseError,
|
|
143
|
+
12: CPLE_AWSBucketNotFoundError,
|
|
144
|
+
13: CPLE_AWSObjectNotFoundError,
|
|
145
|
+
14: CPLE_AWSAccessDeniedError,
|
|
146
|
+
15: CPLE_AWSInvalidCredentialsError,
|
|
147
|
+
16: CPLE_AWSSignatureDoesNotMatchError,
|
|
148
|
+
17: CPLE_AWSError
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
cdef inline object check_last_error():
|
|
153
|
+
"""Checks if the last GDAL error was a fatal or non-fatal error.
|
|
154
|
+
|
|
155
|
+
When a non-fatal error is found, an appropriate exception is raised.
|
|
156
|
+
|
|
157
|
+
When a fatal error is found, SystemExit is called.
|
|
158
|
+
|
|
159
|
+
Returns
|
|
160
|
+
-------
|
|
161
|
+
An Exception, SystemExit, or None
|
|
162
|
+
"""
|
|
163
|
+
err_type = CPLGetLastErrorType()
|
|
164
|
+
err_no = CPLGetLastErrorNo()
|
|
165
|
+
err_msg = clean_error_message(CPLGetLastErrorMsg())
|
|
166
|
+
if err_msg == "":
|
|
167
|
+
err_msg = "No error message."
|
|
168
|
+
|
|
169
|
+
if err_type == CE_Failure:
|
|
170
|
+
CPLErrorReset()
|
|
171
|
+
return exception_map.get(
|
|
172
|
+
err_no, CPLE_BaseError)(err_type, err_no, err_msg)
|
|
173
|
+
|
|
174
|
+
if err_type == CE_Fatal:
|
|
175
|
+
return SystemExit("Fatal error: {0}".format((err_type, err_no, err_msg)))
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
cdef clean_error_message(const char* err_msg):
|
|
179
|
+
"""Cleans up error messages from GDAL.
|
|
180
|
+
|
|
181
|
+
Parameters
|
|
182
|
+
----------
|
|
183
|
+
err_msg : const char*
|
|
184
|
+
The error message to clean up.
|
|
185
|
+
|
|
186
|
+
Returns
|
|
187
|
+
-------
|
|
188
|
+
str
|
|
189
|
+
The cleaned up error message or empty string
|
|
190
|
+
"""
|
|
191
|
+
if err_msg != NULL:
|
|
192
|
+
# Reformat message.
|
|
193
|
+
msg_b = err_msg
|
|
194
|
+
try:
|
|
195
|
+
msg = msg_b.decode("utf-8")
|
|
196
|
+
msg = msg.replace("`", "'")
|
|
197
|
+
msg = msg.replace("\n", " ")
|
|
198
|
+
except UnicodeDecodeError as exc:
|
|
199
|
+
msg = f"Could not decode error message to UTF-8. Raw error: {msg_b}"
|
|
200
|
+
|
|
201
|
+
else:
|
|
202
|
+
msg = ""
|
|
203
|
+
|
|
204
|
+
return msg
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
cdef void *check_pointer(void *ptr) except NULL:
|
|
208
|
+
"""Check the pointer returned by a GDAL/OGR function.
|
|
209
|
+
|
|
210
|
+
If `ptr` is `NULL`, an exception inheriting from CPLE_BaseError is raised.
|
|
211
|
+
When the last error registered by GDAL/OGR was a non-fatal error, the
|
|
212
|
+
exception raised will be customized appropriately. Otherwise a
|
|
213
|
+
NullPointerError is raised.
|
|
214
|
+
"""
|
|
215
|
+
if ptr == NULL:
|
|
216
|
+
exc = check_last_error()
|
|
217
|
+
if exc:
|
|
218
|
+
raise exc
|
|
219
|
+
else:
|
|
220
|
+
# null pointer was passed, but no error message from GDAL
|
|
221
|
+
raise NullPointerError(-1, -1, "NULL pointer error")
|
|
222
|
+
|
|
223
|
+
return ptr
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
cdef int check_int(int err) except -1:
|
|
227
|
+
"""Check the CPLErr (int) value returned by a GDAL/OGR function.
|
|
228
|
+
|
|
229
|
+
If `err` is not OGRERR_NONE, an exception inheriting from CPLE_BaseError is raised.
|
|
230
|
+
When the last error registered by GDAL/OGR was a non-fatal error, the
|
|
231
|
+
exception raised will be customized appropriately. Otherwise a CPLError is
|
|
232
|
+
raised.
|
|
233
|
+
"""
|
|
234
|
+
if err != OGRERR_NONE:
|
|
235
|
+
exc = check_last_error()
|
|
236
|
+
if exc:
|
|
237
|
+
raise exc
|
|
238
|
+
else:
|
|
239
|
+
# no error message from GDAL
|
|
240
|
+
raise CPLError(-1, -1, "Unspecified OGR / GDAL error")
|
|
241
|
+
|
|
242
|
+
return err
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
cdef void error_handler(
|
|
246
|
+
CPLErr err_class, int err_no, const char* err_msg
|
|
247
|
+
) noexcept nogil:
|
|
248
|
+
"""Custom CPL error handler to match the Python behaviour.
|
|
249
|
+
|
|
250
|
+
For non-fatal errors (CE_Failure), error printing to stderr (behaviour of
|
|
251
|
+
the default GDAL error handler) is suppressed, because we already raise a
|
|
252
|
+
Python exception that includes the error message.
|
|
253
|
+
|
|
254
|
+
Warnings are converted to Python warnings.
|
|
255
|
+
"""
|
|
256
|
+
if err_class == CE_Fatal:
|
|
257
|
+
# If the error class is CE_Fatal, we want to have a message issued
|
|
258
|
+
# because the CPL support code does an abort() before any exception
|
|
259
|
+
# can be generated
|
|
260
|
+
CPLDefaultErrorHandler(err_class, err_no, err_msg)
|
|
261
|
+
return
|
|
262
|
+
|
|
263
|
+
if err_class == CE_Failure:
|
|
264
|
+
# For Failures, do nothing as those are explicitly caught
|
|
265
|
+
# with error return codes and translated into Python exceptions
|
|
266
|
+
return
|
|
267
|
+
|
|
268
|
+
if err_class == CE_Warning:
|
|
269
|
+
with gil:
|
|
270
|
+
warnings.warn(clean_error_message(err_msg), RuntimeWarning)
|
|
271
|
+
return
|
|
272
|
+
|
|
273
|
+
# Fall back to the default handler for non-failure messages since
|
|
274
|
+
# they won't be translated into exceptions.
|
|
275
|
+
CPLDefaultErrorHandler(err_class, err_no, err_msg)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def _register_error_handler():
|
|
279
|
+
CPLPushErrorHandler(<CPLErrorHandler>error_handler)
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
cdef class ErrorHandler:
|
|
283
|
+
|
|
284
|
+
def __init__(self, error_stack=None):
|
|
285
|
+
self.error_stack = error_stack or {}
|
|
286
|
+
|
|
287
|
+
cdef int check_int(self, int err, bint squash_errors) except -1:
|
|
288
|
+
"""Check the CPLErr (int) value returned by a GDAL/OGR function.
|
|
289
|
+
|
|
290
|
+
If `err` is not OGRERR_NONE, an exception inheriting from CPLE_BaseError is
|
|
291
|
+
raised.
|
|
292
|
+
When a non-fatal GDAL/OGR error was captured in the error stack, the
|
|
293
|
+
exception raised will be customized appropriately. Otherwise, a
|
|
294
|
+
CPLError is raised.
|
|
295
|
+
|
|
296
|
+
Parameters
|
|
297
|
+
----------
|
|
298
|
+
err : int
|
|
299
|
+
The CPLErr returned by a GDAL/OGR function.
|
|
300
|
+
squash_errors : bool
|
|
301
|
+
True to squash all errors captured to one error with the exception type of
|
|
302
|
+
the last error and all error messages concatenated.
|
|
303
|
+
|
|
304
|
+
Returns
|
|
305
|
+
-------
|
|
306
|
+
int
|
|
307
|
+
The `err` input parameter if it is OGRERR_NONE. Otherwise an exception is
|
|
308
|
+
raised.
|
|
309
|
+
|
|
310
|
+
"""
|
|
311
|
+
if err != OGRERR_NONE:
|
|
312
|
+
if self.error_stack.get():
|
|
313
|
+
self._handle_error_stack(squash_errors)
|
|
314
|
+
else:
|
|
315
|
+
raise CPLError(CE_Failure, err, "Unspecified OGR / GDAL error")
|
|
316
|
+
|
|
317
|
+
return err
|
|
318
|
+
|
|
319
|
+
cdef void *check_pointer(self, void *ptr, bint squash_errors) except NULL:
|
|
320
|
+
"""Check the pointer returned by a GDAL/OGR function.
|
|
321
|
+
|
|
322
|
+
If `ptr` is `NULL`, an exception inheriting from CPLE_BaseError is
|
|
323
|
+
raised.
|
|
324
|
+
When a non-fatal GDAL/OGR error was captured in the error stack, the
|
|
325
|
+
exception raised will be customized appropriately. Otherwise, a
|
|
326
|
+
NullPointerError is raised.
|
|
327
|
+
|
|
328
|
+
Parameters
|
|
329
|
+
----------
|
|
330
|
+
ptr : pointer
|
|
331
|
+
The pointer returned by a GDAL/OGR function.
|
|
332
|
+
squash_errors : bool
|
|
333
|
+
True to squash all errors captured to one error with the exception type of
|
|
334
|
+
the last error and all error messages concatenated.
|
|
335
|
+
|
|
336
|
+
Returns
|
|
337
|
+
-------
|
|
338
|
+
pointer
|
|
339
|
+
The `ptr` input parameter if it is not `NULL`. Otherwise an exception is
|
|
340
|
+
raised.
|
|
341
|
+
|
|
342
|
+
"""
|
|
343
|
+
if ptr == NULL:
|
|
344
|
+
if self.error_stack.get():
|
|
345
|
+
self._handle_error_stack(squash_errors)
|
|
346
|
+
else:
|
|
347
|
+
raise NullPointerError(-1, -1, "NULL pointer error")
|
|
348
|
+
|
|
349
|
+
return ptr
|
|
350
|
+
|
|
351
|
+
cdef void _handle_error_stack(self, bint squash_errors):
|
|
352
|
+
"""Handle the errors in `error_stack`."""
|
|
353
|
+
stack = self.error_stack.get()
|
|
354
|
+
for error, cause in zip_longest(stack[::-1], stack[::-1][1:]):
|
|
355
|
+
if error is not None and cause is not None:
|
|
356
|
+
error.__cause__ = cause
|
|
357
|
+
|
|
358
|
+
last = stack.pop()
|
|
359
|
+
if last is not None:
|
|
360
|
+
if squash_errors:
|
|
361
|
+
# Concatenate all error messages, and raise a single exception
|
|
362
|
+
errmsg = str(last)
|
|
363
|
+
inner = last.__cause__
|
|
364
|
+
while inner is not None:
|
|
365
|
+
errmsg = f"{errmsg}; {inner}"
|
|
366
|
+
inner = inner.__cause__
|
|
367
|
+
|
|
368
|
+
if errmsg == "":
|
|
369
|
+
errmsg = "No error message."
|
|
370
|
+
|
|
371
|
+
raise type(last)(-1, -1, errmsg)
|
|
372
|
+
|
|
373
|
+
raise last
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
cdef void stacking_error_handler(
|
|
377
|
+
CPLErr err_class,
|
|
378
|
+
int err_no,
|
|
379
|
+
const char* err_msg
|
|
380
|
+
) noexcept nogil:
|
|
381
|
+
"""Custom CPL error handler that adds non-fatal errors to a stack.
|
|
382
|
+
|
|
383
|
+
All non-fatal errors (CE_Failure) are not printed to stderr (behaviour
|
|
384
|
+
of the default GDAL error handler), but they are converted to python
|
|
385
|
+
exceptions and added to a stack, so they can be dealt with afterwards.
|
|
386
|
+
|
|
387
|
+
Warnings are converted to Python warnings.
|
|
388
|
+
"""
|
|
389
|
+
if err_class == CE_Fatal:
|
|
390
|
+
# If the error class is CE_Fatal, we want to have a message issued
|
|
391
|
+
# because the CPL support code does an abort() before any exception
|
|
392
|
+
# can be generated
|
|
393
|
+
CPLDefaultErrorHandler(err_class, err_no, err_msg)
|
|
394
|
+
return
|
|
395
|
+
|
|
396
|
+
if err_class == CE_Failure:
|
|
397
|
+
# For Failures, add them to the error exception stack
|
|
398
|
+
with gil:
|
|
399
|
+
stack = _ERROR_STACK.get()
|
|
400
|
+
stack.append(
|
|
401
|
+
exception_map.get(err_no, CPLE_BaseError)(
|
|
402
|
+
err_class, err_no, clean_error_message(err_msg)
|
|
403
|
+
),
|
|
404
|
+
)
|
|
405
|
+
_ERROR_STACK.set(stack)
|
|
406
|
+
|
|
407
|
+
return
|
|
408
|
+
|
|
409
|
+
if err_class == CE_Warning:
|
|
410
|
+
with gil:
|
|
411
|
+
warnings.warn(clean_error_message(err_msg), RuntimeWarning)
|
|
412
|
+
return
|
|
413
|
+
|
|
414
|
+
# Fall back to the default handler for non-failure messages since
|
|
415
|
+
# they won't be translated into exceptions.
|
|
416
|
+
CPLDefaultErrorHandler(err_class, err_no, err_msg)
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
@contextlib.contextmanager
|
|
420
|
+
def capture_errors():
|
|
421
|
+
"""A context manager that captures all GDAL non-fatal errors occuring.
|
|
422
|
+
|
|
423
|
+
It adds all errors to a single stack, so it assumes that no more than one
|
|
424
|
+
GDAL function is called.
|
|
425
|
+
|
|
426
|
+
Yields an ErrorHandler object that can be used to handle the errors
|
|
427
|
+
if any were captured.
|
|
428
|
+
"""
|
|
429
|
+
CPLErrorReset()
|
|
430
|
+
_ERROR_STACK.set([])
|
|
431
|
+
|
|
432
|
+
# stacking_error_handler records GDAL errors in the order they occur and
|
|
433
|
+
# converts them to exceptions.
|
|
434
|
+
CPLPushErrorHandler(<CPLErrorHandler>stacking_error_handler)
|
|
435
|
+
|
|
436
|
+
# Run code in the `with` block.
|
|
437
|
+
yield ErrorHandler(_ERROR_STACK)
|
|
438
|
+
|
|
439
|
+
CPLPopErrorHandler()
|
|
440
|
+
_ERROR_STACK.set([])
|
|
441
|
+
CPLErrorReset()
|