python plugin: catch and report exceptions, closes #1422
This commit is contained in:
parent
8c2604e69c
commit
bc4a74f5b0
4 changed files with 128 additions and 69 deletions
|
@ -15,6 +15,7 @@ plugin_sources = Split(
|
||||||
"""
|
"""
|
||||||
%(PLUGIN_NAME)s_datasource.cpp
|
%(PLUGIN_NAME)s_datasource.cpp
|
||||||
%(PLUGIN_NAME)s_featureset.cpp
|
%(PLUGIN_NAME)s_featureset.cpp
|
||||||
|
%(PLUGIN_NAME)s_utils.cpp
|
||||||
""" % locals()
|
""" % locals()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -69,17 +69,17 @@ void python_datasource::bind() const
|
||||||
// if no factory callable is defined, bind is a nop
|
// if no factory callable is defined, bind is a nop
|
||||||
if (factory_.empty()) return;
|
if (factory_.empty()) return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
// split factory at ':' to parse out module and callable
|
// split factory at ':' to parse out module and callable
|
||||||
std::vector<std::string> factory_split;
|
std::vector<std::string> factory_split;
|
||||||
split(factory_split, factory_, is_any_of(":"));
|
split(factory_split, factory_, is_any_of(":"));
|
||||||
if ((factory_split.size() < 1) || (factory_split.size() > 2))
|
if ((factory_split.size() < 1) || (factory_split.size() > 2))
|
||||||
{
|
{
|
||||||
// FIMXE: is this appropriate error reporting?
|
throw mapnik::datasource_exception(
|
||||||
std::cerr << "python: factory string must be of the form '[module:]callable' when parsing \""
|
std::string("python: factory string must be of the form '[module:]callable' when parsing \"")
|
||||||
<< factory_ << '"' << std::endl;
|
+ factory_ + '"');
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// extract the module and the callable
|
// extract the module and the callable
|
||||||
str module_name("__main__"), callable_name;
|
str module_name("__main__"), callable_name;
|
||||||
if (factory_split.size() == 1)
|
if (factory_split.size() == 1)
|
||||||
|
@ -91,16 +91,12 @@ void python_datasource::bind() const
|
||||||
module_name = str(factory_split[0]);
|
module_name = str(factory_split[0]);
|
||||||
callable_name = str(factory_split[1]);
|
callable_name = str(factory_split[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
ensure_gil lock;
|
ensure_gil lock;
|
||||||
|
|
||||||
// import the main module from Python (in case we're embedding the
|
// import the main module from Python (in case we're embedding the
|
||||||
// interpreter directly) and also import the callable.
|
// interpreter directly) and also import the callable.
|
||||||
object main_module = import("__main__");
|
object main_module = import("__main__");
|
||||||
object callable_module = import(module_name);
|
object callable_module = import(module_name);
|
||||||
object callable = callable_module.attr(callable_name);
|
object callable = callable_module.attr(callable_name);
|
||||||
|
|
||||||
// prepare the arguments
|
// prepare the arguments
|
||||||
dict kwargs;
|
dict kwargs;
|
||||||
typedef std::map<std::string, std::string>::value_type kv_type;
|
typedef std::map<std::string, std::string>::value_type kv_type;
|
||||||
|
@ -112,6 +108,10 @@ void python_datasource::bind() const
|
||||||
// get our wrapped data source
|
// get our wrapped data source
|
||||||
datasource_ = callable(*boost::python::make_tuple(), **kwargs);
|
datasource_ = callable(*boost::python::make_tuple(), **kwargs);
|
||||||
}
|
}
|
||||||
|
catch ( error_already_set )
|
||||||
|
{
|
||||||
|
throw mapnik::datasource_exception(extractException());
|
||||||
|
}
|
||||||
|
|
||||||
is_bound_ = true;
|
is_bound_ = true;
|
||||||
}
|
}
|
||||||
|
@ -124,12 +124,19 @@ mapnik::datasource::datasource_t python_datasource::type() const
|
||||||
|
|
||||||
if (!is_bound_) bind();
|
if (!is_bound_) bind();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
ensure_gil lock;
|
ensure_gil lock;
|
||||||
|
|
||||||
object data_type = datasource_.attr("data_type");
|
object data_type = datasource_.attr("data_type");
|
||||||
long data_type_integer = extract<long>(data_type);
|
long data_type_integer = extract<long>(data_type);
|
||||||
return mapnik::datasource::datasource_t(data_type_integer);
|
return mapnik::datasource::datasource_t(data_type_integer);
|
||||||
}
|
}
|
||||||
|
catch ( error_already_set )
|
||||||
|
{
|
||||||
|
throw mapnik::datasource_exception(extractException());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
mapnik::box2d<double> python_datasource::envelope() const
|
mapnik::box2d<double> python_datasource::envelope() const
|
||||||
{
|
{
|
||||||
|
@ -137,9 +144,16 @@ mapnik::box2d<double> python_datasource::envelope() const
|
||||||
|
|
||||||
if (!is_bound_) bind();
|
if (!is_bound_) bind();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
ensure_gil lock;
|
ensure_gil lock;
|
||||||
return extract<mapnik::box2d<double> >(datasource_.attr("envelope"));
|
return extract<mapnik::box2d<double> >(datasource_.attr("envelope"));
|
||||||
}
|
}
|
||||||
|
catch ( error_already_set )
|
||||||
|
{
|
||||||
|
throw mapnik::datasource_exception(extractException());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
boost::optional<mapnik::datasource::geometry_t> python_datasource::get_geometry_type() const
|
boost::optional<mapnik::datasource::geometry_t> python_datasource::get_geometry_type() const
|
||||||
{
|
{
|
||||||
|
@ -149,21 +163,28 @@ boost::optional<mapnik::datasource::geometry_t> python_datasource::get_geometry_
|
||||||
|
|
||||||
if (!is_bound_) bind();
|
if (!is_bound_) bind();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
ensure_gil lock;
|
ensure_gil lock;
|
||||||
|
|
||||||
// if the datasource object has no geometry_type attribute, return a 'none' value
|
// if the datasource object has no geometry_type attribute, return a 'none' value
|
||||||
if (!PyObject_HasAttrString(datasource_.ptr(), "geometry_type"))
|
if (!PyObject_HasAttrString(datasource_.ptr(), "geometry_type"))
|
||||||
|
{
|
||||||
return return_type();
|
return return_type();
|
||||||
|
}
|
||||||
object py_geometry_type = datasource_.attr("geometry_type");
|
object py_geometry_type = datasource_.attr("geometry_type");
|
||||||
|
|
||||||
// if the attribute value is 'None', return a 'none' value
|
// if the attribute value is 'None', return a 'none' value
|
||||||
if (py_geometry_type.ptr() == object().ptr())
|
if (py_geometry_type.ptr() == object().ptr())
|
||||||
|
{
|
||||||
return return_type();
|
return return_type();
|
||||||
|
}
|
||||||
long geom_type_integer = extract<long>(py_geometry_type);
|
long geom_type_integer = extract<long>(py_geometry_type);
|
||||||
return mapnik::datasource::geometry_t(geom_type_integer);
|
return mapnik::datasource::geometry_t(geom_type_integer);
|
||||||
}
|
}
|
||||||
|
catch ( error_already_set )
|
||||||
|
{
|
||||||
|
throw mapnik::datasource_exception(extractException());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mapnik::featureset_ptr python_datasource::features(mapnik::query const& q) const
|
mapnik::featureset_ptr python_datasource::features(mapnik::query const& q) const
|
||||||
{
|
{
|
||||||
|
@ -171,23 +192,28 @@ mapnik::featureset_ptr python_datasource::features(mapnik::query const& q) const
|
||||||
|
|
||||||
if (!is_bound_) bind();
|
if (!is_bound_) bind();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
// if the query box intersects our world extent then query for features
|
// if the query box intersects our world extent then query for features
|
||||||
if (envelope().intersects(q.get_bbox()))
|
if (envelope().intersects(q.get_bbox()))
|
||||||
{
|
{
|
||||||
ensure_gil lock;
|
ensure_gil lock;
|
||||||
|
|
||||||
object features(datasource_.attr("features")(q));
|
object features(datasource_.attr("features")(q));
|
||||||
|
|
||||||
// if 'None' was returned, return an empty feature set
|
// if 'None' was returned, return an empty feature set
|
||||||
if(features.ptr() == object().ptr())
|
if(features.ptr() == object().ptr())
|
||||||
|
{
|
||||||
return mapnik::featureset_ptr();
|
return mapnik::featureset_ptr();
|
||||||
|
}
|
||||||
return boost::make_shared<python_featureset>(features);
|
return boost::make_shared<python_featureset>(features);
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherwise return an empty featureset pointer
|
// otherwise return an empty featureset pointer
|
||||||
return mapnik::featureset_ptr();
|
return mapnik::featureset_ptr();
|
||||||
}
|
}
|
||||||
|
catch ( error_already_set )
|
||||||
|
{
|
||||||
|
throw mapnik::datasource_exception(extractException());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mapnik::featureset_ptr python_datasource::features_at_point(mapnik::coord2d const& pt, double tol) const
|
mapnik::featureset_ptr python_datasource::features_at_point(mapnik::coord2d const& pt, double tol) const
|
||||||
{
|
{
|
||||||
|
@ -195,14 +221,21 @@ mapnik::featureset_ptr python_datasource::features_at_point(mapnik::coord2d cons
|
||||||
|
|
||||||
if (!is_bound_) bind();
|
if (!is_bound_) bind();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
ensure_gil lock;
|
ensure_gil lock;
|
||||||
|
|
||||||
object features(datasource_.attr("features_at_point")(pt));
|
object features(datasource_.attr("features_at_point")(pt));
|
||||||
|
|
||||||
// if we returned none, return an empty set
|
// if we returned none, return an empty set
|
||||||
if(features.ptr() == object().ptr())
|
if(features.ptr() == object().ptr())
|
||||||
|
{
|
||||||
return mapnik::featureset_ptr();
|
return mapnik::featureset_ptr();
|
||||||
|
}
|
||||||
// otherwise, return a feature set which can iterate over the iterator
|
// otherwise, return a feature set which can iterate over the iterator
|
||||||
return boost::make_shared<python_featureset>(features);
|
return boost::make_shared<python_featureset>(features);
|
||||||
}
|
}
|
||||||
|
catch ( error_already_set )
|
||||||
|
{
|
||||||
|
throw mapnik::datasource_exception(extractException());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
23
plugins/input/python/python_utils.cpp
Normal file
23
plugins/input/python/python_utils.cpp
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#include "python_utils.hpp"
|
||||||
|
|
||||||
|
std::string extractException()
|
||||||
|
{
|
||||||
|
using namespace boost::python;
|
||||||
|
|
||||||
|
PyObject *exc,*val,*tb;
|
||||||
|
PyErr_Fetch(&exc,&val,&tb);
|
||||||
|
PyErr_NormalizeException(&exc,&val,&tb);
|
||||||
|
handle<> hexc(exc),hval(allow_null(val)),htb(allow_null(tb));
|
||||||
|
if(!hval)
|
||||||
|
{
|
||||||
|
return extract<std::string>(str(hexc));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
object traceback(import("traceback"));
|
||||||
|
object format_exception(traceback.attr("format_exception"));
|
||||||
|
object formatted_list(format_exception(hexc,hval,htb));
|
||||||
|
object formatted(str("").join(formatted_list));
|
||||||
|
return extract<std::string>(formatted);
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,4 +13,6 @@ class ensure_gil
|
||||||
PyGILState_STATE gil_state_;
|
PyGILState_STATE gil_state_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::string extractException();
|
||||||
|
|
||||||
#endif // PYTHON_UTILS_HPP
|
#endif // PYTHON_UTILS_HPP
|
||||||
|
|
Loading…
Reference in a new issue