422 lines
10 KiB
OpenEdge ABL
422 lines
10 KiB
OpenEdge ABL
/*
|
|
* This was the cpl_exceptions.i code. But since python is the only one
|
|
* different (should support old method as well as new one)
|
|
* it was moved into this file.
|
|
*/
|
|
%{
|
|
#include "cpl_string.h"
|
|
#include "cpl_conv.h"
|
|
|
|
static int bUseExceptions=0;
|
|
static int bUserHasSpecifiedIfUsingExceptions = FALSE;
|
|
static thread_local int bUseExceptionsLocal = -1;
|
|
|
|
struct PythonBindingErrorHandlerContext
|
|
{
|
|
std::string osInitialMsg{};
|
|
std::string osFailureMsg{};
|
|
CPLErrorNum nLastCode = CPLE_None;
|
|
bool bMemoryError = false;
|
|
};
|
|
|
|
static void CPL_STDCALL
|
|
PythonBindingErrorHandler(CPLErr eclass, CPLErrorNum err_no, const char *msg )
|
|
{
|
|
PythonBindingErrorHandlerContext* ctxt = static_cast<
|
|
PythonBindingErrorHandlerContext*>(CPLGetErrorHandlerUserData());
|
|
|
|
/*
|
|
** Generally we want to suppress error reporting if we have exceptions
|
|
** enabled as the error message will be in the exception thrown in
|
|
** Python.
|
|
*/
|
|
|
|
/* If the error class is CE_Fatal, we want to have a message issued
|
|
because the CPL support code does an abort() before any exception
|
|
can be generated */
|
|
if (eclass == CE_Fatal ) {
|
|
CPLCallPreviousHandler(eclass, err_no, msg );
|
|
}
|
|
|
|
/*
|
|
** We do not want to interfere with non-failure messages since
|
|
** they won't be translated into exceptions.
|
|
*/
|
|
else if (eclass != CE_Failure ) {
|
|
CPLCallPreviousHandler(eclass, err_no, msg );
|
|
}
|
|
else {
|
|
ctxt->nLastCode = err_no;
|
|
try
|
|
{
|
|
if( ctxt->osFailureMsg.empty() ) {
|
|
ctxt->osFailureMsg = msg;
|
|
ctxt->osInitialMsg = ctxt->osFailureMsg;
|
|
} else {
|
|
if( ctxt->osFailureMsg.size() < 10000 ) {
|
|
std::string osTmp(msg);
|
|
osTmp += "\nMay be caused by: ";
|
|
osTmp += ctxt->osFailureMsg;
|
|
ctxt->osFailureMsg = std::move(osTmp);
|
|
ctxt->osInitialMsg = ctxt->osFailureMsg;
|
|
}
|
|
else
|
|
{
|
|
std::string osTmp(msg);
|
|
osTmp += "\n[...]\nMay be caused by: ";
|
|
osTmp += ctxt->osInitialMsg;
|
|
ctxt->osFailureMsg = std::move(osTmp);
|
|
}
|
|
}
|
|
}
|
|
catch( const std::exception& )
|
|
{
|
|
ctxt->bMemoryError = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
%}
|
|
|
|
%exception GetUseExceptions
|
|
{
|
|
%#ifdef SED_HACKS
|
|
if( ReturnSame(TRUE) ) bLocalUseExceptionsCode = FALSE;
|
|
%#endif
|
|
result = GetUseExceptions();
|
|
}
|
|
|
|
%exception _GetExceptionsLocal
|
|
{
|
|
%#ifdef SED_HACKS
|
|
if( ReturnSame(TRUE) ) bLocalUseExceptionsCode = FALSE;
|
|
%#endif
|
|
$action
|
|
}
|
|
|
|
%exception _SetExceptionsLocal
|
|
{
|
|
%#ifdef SED_HACKS
|
|
if( ReturnSame(TRUE) ) bLocalUseExceptionsCode = FALSE;
|
|
%#endif
|
|
$action
|
|
}
|
|
|
|
%exception _UserHasSpecifiedIfUsingExceptions
|
|
{
|
|
%#ifdef SED_HACKS
|
|
if( ReturnSame(TRUE) ) bLocalUseExceptionsCode = FALSE;
|
|
%#endif
|
|
$action
|
|
}
|
|
|
|
%inline %{
|
|
|
|
static
|
|
int GetUseExceptions() {
|
|
return bUseExceptionsLocal >= 0 ? bUseExceptionsLocal : bUseExceptions;
|
|
}
|
|
|
|
static int _GetExceptionsLocal()
|
|
{
|
|
return bUseExceptionsLocal;
|
|
}
|
|
|
|
static void _SetExceptionsLocal(int bVal)
|
|
{
|
|
bUseExceptionsLocal = bVal;
|
|
}
|
|
|
|
static
|
|
void _UseExceptions() {
|
|
CPLErrorReset();
|
|
bUserHasSpecifiedIfUsingExceptions = TRUE;
|
|
if( !bUseExceptions )
|
|
{
|
|
bUseExceptions = 1;
|
|
}
|
|
}
|
|
|
|
static
|
|
void _DontUseExceptions() {
|
|
CPLErrorReset();
|
|
bUserHasSpecifiedIfUsingExceptions = TRUE;
|
|
if( bUseExceptions )
|
|
{
|
|
bUseExceptions = 0;
|
|
}
|
|
}
|
|
|
|
static int _UserHasSpecifiedIfUsingExceptions()
|
|
{
|
|
return bUserHasSpecifiedIfUsingExceptions || bUseExceptionsLocal >= 0;
|
|
}
|
|
|
|
%}
|
|
|
|
%{
|
|
/* Completely unrelated: just to avoid Coverity warnings */
|
|
|
|
static int bReturnSame = 1;
|
|
|
|
void NeverCallMePlease() {
|
|
bReturnSame = 0;
|
|
}
|
|
|
|
/* Some SWIG code generates dead code, which Coverity warns about */
|
|
template<class T> static T ReturnSame(T x)
|
|
{
|
|
if( bReturnSame )
|
|
return x;
|
|
return 0;
|
|
}
|
|
|
|
static void pushErrorHandler()
|
|
{
|
|
CPLErrorReset();
|
|
PythonBindingErrorHandlerContext* ctxt = new PythonBindingErrorHandlerContext();
|
|
CPLPushErrorHandlerEx(PythonBindingErrorHandler, ctxt);
|
|
}
|
|
|
|
static void popErrorHandler()
|
|
{
|
|
PythonBindingErrorHandlerContext* ctxt = static_cast<
|
|
PythonBindingErrorHandlerContext*>(CPLGetErrorHandlerUserData());
|
|
CPLPopErrorHandler();
|
|
if( ctxt->bMemoryError )
|
|
{
|
|
CPLErrorSetState(
|
|
CE_Failure, CPLE_OutOfMemory, "Out of memory");
|
|
}
|
|
else if( !ctxt->osFailureMsg.empty() )
|
|
{
|
|
CPLErrorSetState(
|
|
CPLGetLastErrorType() == CE_Failure ? CE_Failure: CE_Warning,
|
|
ctxt->nLastCode, ctxt->osFailureMsg.c_str());
|
|
}
|
|
delete ctxt;
|
|
}
|
|
|
|
%}
|
|
|
|
%include exception.i
|
|
|
|
%exception {
|
|
const int bLocalUseExceptions = GetUseExceptions();
|
|
if ( bLocalUseExceptions ) {
|
|
pushErrorHandler();
|
|
}
|
|
$action
|
|
if ( bLocalUseExceptions ) {
|
|
popErrorHandler();
|
|
}
|
|
%#ifndef SED_HACKS
|
|
if ( bLocalUseExceptions ) {
|
|
CPLErr eclass = CPLGetLastErrorType();
|
|
if ( eclass == CE_Failure || eclass == CE_Fatal ) {
|
|
SWIG_exception( SWIG_RuntimeError, CPLGetLastErrorMsg() );
|
|
}
|
|
}
|
|
%#endif
|
|
}
|
|
|
|
%feature("except") Open {
|
|
const int bLocalUseExceptions = GetUseExceptions();
|
|
if ( bLocalUseExceptions ) {
|
|
pushErrorHandler();
|
|
}
|
|
$action
|
|
if ( bLocalUseExceptions ) {
|
|
popErrorHandler();
|
|
}
|
|
%#ifndef SED_HACKS
|
|
if( result == NULL && bLocalUseExceptions ) {
|
|
CPLErr eclass = CPLGetLastErrorType();
|
|
if ( eclass == CE_Failure || eclass == CE_Fatal ) {
|
|
SWIG_exception( SWIG_RuntimeError, CPLGetLastErrorMsg() );
|
|
}
|
|
}
|
|
%#endif
|
|
if( result != NULL && bLocalUseExceptions ) {
|
|
%#ifdef SED_HACKS
|
|
bLocalUseExceptionsCode = FALSE;
|
|
%#endif
|
|
}
|
|
}
|
|
|
|
%feature("except") OpenShared {
|
|
const int bLocalUseExceptions = GetUseExceptions();
|
|
if ( bLocalUseExceptions ) {
|
|
pushErrorHandler();
|
|
}
|
|
$action
|
|
if ( bLocalUseExceptions ) {
|
|
popErrorHandler();
|
|
}
|
|
%#ifndef SED_HACKS
|
|
if( result == NULL && bLocalUseExceptions ) {
|
|
CPLErr eclass = CPLGetLastErrorType();
|
|
if ( eclass == CE_Failure || eclass == CE_Fatal ) {
|
|
SWIG_exception( SWIG_RuntimeError, CPLGetLastErrorMsg() );
|
|
}
|
|
}
|
|
%#endif
|
|
if( result != NULL && bLocalUseExceptions ) {
|
|
%#ifdef SED_HACKS
|
|
bLocalUseExceptionsCode = FALSE;
|
|
%#endif
|
|
}
|
|
}
|
|
|
|
%feature("except") OpenEx {
|
|
const int bLocalUseExceptions = GetUseExceptions();
|
|
if ( bLocalUseExceptions ) {
|
|
pushErrorHandler();
|
|
}
|
|
$action
|
|
if ( bLocalUseExceptions ) {
|
|
popErrorHandler();
|
|
}
|
|
%#ifndef SED_HACKS
|
|
if( result == NULL && bLocalUseExceptions ) {
|
|
CPLErr eclass = CPLGetLastErrorType();
|
|
if ( eclass == CE_Failure || eclass == CE_Fatal ) {
|
|
SWIG_exception( SWIG_RuntimeError, CPLGetLastErrorMsg() );
|
|
}
|
|
}
|
|
%#endif
|
|
if( result != NULL && bLocalUseExceptions ) {
|
|
%#ifdef SED_HACKS
|
|
bLocalUseExceptionsCode = FALSE;
|
|
%#endif
|
|
}
|
|
}
|
|
|
|
%pythoncode %{
|
|
class ExceptionMgr:
|
|
"""
|
|
Context manager to manage Python Exception state
|
|
for GDAL/OGR/OSR/GNM.
|
|
|
|
Separate exception state is maintained for each
|
|
module (gdal, ogr, etc), and this class appears independently
|
|
in all of them. This is built in top of calls to the older
|
|
UseExceptions()/DontUseExceptions() functions.
|
|
|
|
Example::
|
|
|
|
>>> print(gdal.GetUseExceptions())
|
|
0
|
|
>>> with gdal.ExceptionMgr():
|
|
... # Exceptions are now in use
|
|
... print(gdal.GetUseExceptions())
|
|
1
|
|
>>>
|
|
>>> # Exception state has now been restored
|
|
>>> print(gdal.GetUseExceptions())
|
|
0
|
|
|
|
"""
|
|
def __init__(self, useExceptions=True):
|
|
"""
|
|
Save whether or not this context will be using exceptions
|
|
"""
|
|
self.requestedUseExceptions = useExceptions
|
|
|
|
def __enter__(self):
|
|
"""
|
|
On context entry, save the current GDAL exception state, and
|
|
set it to the state requested for the context
|
|
|
|
"""
|
|
self.currentUseExceptions = _GetExceptionsLocal()
|
|
_SetExceptionsLocal(self.requestedUseExceptions)
|
|
if ExceptionMgr.__module__ == "osgeo.gdal":
|
|
try:
|
|
from . import gdal_array
|
|
except ImportError:
|
|
gdal_array = None
|
|
if gdal_array:
|
|
gdal_array._SetExceptionsLocal(self.requestedUseExceptions)
|
|
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
"""
|
|
On exit, restore the GDAL/OGR/OSR/GNM exception state which was
|
|
current on entry to the context
|
|
"""
|
|
_SetExceptionsLocal(self.currentUseExceptions)
|
|
if ExceptionMgr.__module__ == "osgeo.gdal":
|
|
try:
|
|
from . import gdal_array
|
|
except ImportError:
|
|
gdal_array = None
|
|
if gdal_array:
|
|
gdal_array._SetExceptionsLocal(self.currentUseExceptions)
|
|
|
|
%}
|
|
|
|
|
|
%pythoncode %{
|
|
|
|
def UseExceptions():
|
|
""" Enable exceptions in all GDAL related modules (osgeo.gdal, osgeo.ogr, osgeo.osr, osgeo.gnm).
|
|
Note: prior to GDAL 3.7, this only affected the calling module"""
|
|
|
|
try:
|
|
from . import gdal
|
|
gdal._UseExceptions()
|
|
except ImportError:
|
|
pass
|
|
try:
|
|
from . import gdal_array
|
|
gdal_array._UseExceptions()
|
|
except ImportError:
|
|
pass
|
|
try:
|
|
from . import ogr
|
|
ogr._UseExceptions()
|
|
except ImportError:
|
|
pass
|
|
try:
|
|
from . import osr
|
|
osr._UseExceptions()
|
|
except ImportError:
|
|
pass
|
|
try:
|
|
from . import gnm
|
|
gnm._UseExceptions()
|
|
except ImportError:
|
|
pass
|
|
|
|
def DontUseExceptions():
|
|
""" Disable exceptions in all GDAL related modules (osgeo.gdal, osgeo.ogr, osgeo.osr, osgeo.gnm).
|
|
Note: prior to GDAL 3.7, this only affected the calling module"""
|
|
|
|
try:
|
|
from . import gdal
|
|
gdal._DontUseExceptions()
|
|
except ImportError:
|
|
pass
|
|
try:
|
|
from . import gdal_array
|
|
gdal_array._DontUseExceptions()
|
|
except ImportError:
|
|
pass
|
|
try:
|
|
from . import ogr
|
|
ogr._DontUseExceptions()
|
|
except ImportError:
|
|
pass
|
|
try:
|
|
from . import osr
|
|
osr._DontUseExceptions()
|
|
except ImportError:
|
|
pass
|
|
try:
|
|
from . import gnm
|
|
gnm._DontUseExceptions()
|
|
except ImportError:
|
|
pass
|
|
|
|
%}
|