python plugin: catch and report exceptions, closes #1422

This commit is contained in:
Dane Springmeyer 2012-10-19 17:05:51 -07:00
parent 8c2604e69c
commit bc4a74f5b0
4 changed files with 128 additions and 69 deletions

View file

@ -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()
) )

View file

@ -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());
}
}

View 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);
}
}

View file

@ -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