diff options
author | 2007-09-10 09:50:32 +0000 | |
---|---|---|
committer | 2007-09-10 09:50:32 +0000 | |
commit | 7f30d2d58c9649051616d0691d9e68ed6c823295 (patch) | |
tree | a2544d43a94894dfe868428c75117d2a79fd9a0c /paludis | |
parent | f0b9aa0c2d9f8058ec8c73e961bfb14591ce04ee (diff) | |
download | paludis-7f30d2d58c9649051616d0691d9e68ed6c823295.tar.gz paludis-7f30d2d58c9649051616d0691d9e68ed6c823295.tar.xz |
Python Hooks.
Diffstat (limited to 'paludis')
-rw-r--r-- | paludis/Makefile.am.m4 | 16 | ||||
-rw-r--r-- | paludis/hooker.cc | 104 | ||||
-rw-r--r-- | paludis/hooker.hh | 15 | ||||
-rw-r--r-- | paludis/hooker_TEST.cc | 25 | ||||
-rwxr-xr-x | paludis/hooker_TEST_setup.sh | 25 | ||||
-rw-r--r-- | paludis/python_hooks.cc | 436 |
6 files changed, 601 insertions, 20 deletions
diff --git a/paludis/Makefile.am.m4 b/paludis/Makefile.am.m4 index b8dcd71f1..685ab4051 100644 --- a/paludis/Makefile.am.m4 +++ b/paludis/Makefile.am.m4 @@ -77,7 +77,8 @@ DEFS= \ -DSYSCONFDIR=\"$(sysconfdir)\" \ -DLIBEXECDIR=\"$(libexecdir)\" \ -DDATADIR=\"$(datadir)\" \ - -DLIBDIR=\"$(libdir)\" + -DLIBDIR=\"$(libdir)\" \ + -DPYTHONINSTALLDIR=\"$(PYTHON_INSTALL_DIR)\" EXTRA_DIST = about.hh.in Makefile.am.m4 paludis.hh.m4 files.m4 \ testscriptlist srlist srcleanlist selist secleanlist \ repository_blacklist.txt hooker.bash @@ -87,6 +88,10 @@ BUILT_SOURCES = srcleanlist secleanlist libpaludis_la_SOURCES = filelist libpaludis_la_LDFLAGS = -version-info @VERSION_LIB_CURRENT@:@VERSION_LIB_REVISION@:0 $(PTHREAD_LIBS) +libpaludispythonhooks_la_SOURCES = python_hooks.cc +libpaludispythonhooks_la_CXXFLAGS = $(AM_CXXFLAGS) -I@PYTHON_INCLUDE_DIR@ +libpaludispythonhooks_la_LDFLAGS = -version-info @VERSION_LIB_CURRENT@:@VERSION_LIB_REVISION@:0 @BOOST_PYTHON_LIB@ -lpython@PYTHON_VERSION@ + libpaludismanpagethings_la_SOURCES = name.cc libpaludissohooks_TEST_la_SOURCES = sohooks_TEST.cc @@ -125,8 +130,13 @@ else lib_LTLIBRARIES = libpaludis.la noinst_LTLIBRARIES = libpaludismanpagethings.la +if ENABLE_PYTHON +lib_LTLIBRARIES += libpaludispythonhooks.la +endif + endif + paludis_includedir = $(includedir)/paludis-$(PALUDIS_PC_SLOT)/paludis/ paludis_include_HEADERS = headerlist srheaderlist seheaderlist @@ -157,5 +167,9 @@ TESTS_ENVIRONMENT = env \ PALUDIS_REPOSITORY_SO_DIR="$(top_builddir)/paludis/repositories" \ TEST_SCRIPT_DIR="$(srcdir)/" \ SO_SUFFIX=@VERSION_LIB_CURRENT@ \ + PYTHONPATH="$(top_builddir)/python/" \ + PALUDIS_PYTHON_DIR="$(top_srcdir)/python/" \ + LD_LIBRARY_PATH="`echo $$LD_LIBRARY_PATH: | sed -e 's,^:,,'`` \ + $(top_srcdir)/paludis/repositories/e/ebuild/utils/canonicalise $(top_builddir)/paludis/.libs/`" \ bash $(top_srcdir)/test/run_test.sh diff --git a/paludis/hooker.cc b/paludis/hooker.cc index 90c535007..0f82ade24 100644 --- a/paludis/hooker.cc +++ b/paludis/hooker.cc @@ -19,6 +19,7 @@ */ #include "hooker.hh" +#include "config.h" #include <paludis/environment.hh> #include <paludis/hook.hh> #include <paludis/hashed_containers.hh> @@ -30,7 +31,6 @@ #include <paludis/util/system.hh> #include <paludis/util/private_implementation_pattern-impl.hh> #include <paludis/util/strip.hh> -#include <paludis/util/graph.hh> #include <paludis/util/graph-impl.hh> #include <paludis/util/pstream.hh> #include <paludis/util/tokeniser.hh> @@ -40,25 +40,17 @@ #include <iterator> #include <dlfcn.h> +#ifdef ENABLE_PYTHON +# include <boost/version.hpp> +# if BOOST_VERSION >= 103400 +# define PYTHON_HOOKS 1 +# endif +#endif + using namespace paludis; namespace { - class HookFile; - - class HookFile : - private InstantiationPolicy<HookFile, instantiation_method::NonCopyableTag> - { - public: - virtual ~HookFile() - { - } - - virtual HookResult run(const Hook &) const PALUDIS_ATTRIBUTE((warn_unused_result)) = 0; - virtual const FSEntry file_name() const = 0; - virtual void add_dependencies(const Hook &, DirectedGraph<std::string, int> &) = 0; - }; - class BashHookFile : public HookFile { @@ -95,7 +87,7 @@ namespace const bool _run_prefixed; const Environment * const _env; - virtual void _add_dependency_class(const Hook &, DirectedGraph<std::string, int> &, bool); + void _add_dependency_class(const Hook &, DirectedGraph<std::string, int> &, bool); public: FancyHookFile(const FSEntry & f, const bool r, const Environment * const e) : @@ -390,6 +382,31 @@ Hooker::add_dir(const FSEntry & dir, const bool v) _imp->dirs.push_back(std::make_pair(dir, v)); } +namespace +{ + struct PyHookFileHandle + { + Mutex mutex; + void * handle; + tr1::shared_ptr<HookFile> (* create_py_hook_file_handle)(const FSEntry &, + const bool, const Environment * const); + + + PyHookFileHandle() : + handle(0), + create_py_hook_file_handle(0) + { + } + + ~PyHookFileHandle() + { + if (0 != handle) + dlclose(handle); + } + + } pyhookfilehandle; +} + HookResult Hooker::perform_hook(const Hook & hook) const { @@ -482,6 +499,59 @@ Hooker::perform_hook(const Hook & hook) const + "' because of naming conflict with '" + stringify( hook_files.find(stringify(strip_trailing_string(e->basename(), so_suffix)))->second->file_name()) + "'"); +#ifdef PYTHON_HOOKS + if (is_file_with_extension(*e, ".py", IsFileWithOptions())) + { + static bool load_try(false); + static bool load_ok(false); + + { + Lock lock(pyhookfilehandle.mutex); + + if (! load_try) + { + load_try = true; + + pyhookfilehandle.handle = dlopen("libpaludispythonhooks.so", RTLD_NOW | RTLD_GLOBAL); + if (pyhookfilehandle.handle) + { + pyhookfilehandle.create_py_hook_file_handle = + reinterpret_cast<tr1::shared_ptr<HookFile> (*)( + const FSEntry &, const bool, const Environment * const)>( + reinterpret_cast<uintptr_t>(dlsym( + pyhookfilehandle.handle, "create_py_hook_file"))); + if (pyhookfilehandle.create_py_hook_file_handle) + { + load_ok = true; + } + else + { + Log::get_instance()->message(ll_warning, lc_context, + "dlsym(libpaludispythonhooks.so, create_py_hook_file) " + "failed due to error '" + stringify(dlerror()) + "'"); + } + } + else + { + Log::get_instance()->message(ll_warning, lc_context, + "dlopen(libpaludispythonhooks.so) " + "failed due to error '" + stringify(dlerror()) + "'"); + } + } + } + if (load_ok) + { + if (! hook_files.insert(std::make_pair(strip_trailing_string(e->basename(), ".py"), + tr1::shared_ptr<HookFile>(pyhookfilehandle.create_py_hook_file_handle( + *e, d->second, _imp->env)))).second) + Log::get_instance()->message(ll_warning, lc_context, + "Discarding hook file '" + stringify(*e) + + "' because of naming conflict with '" + + stringify(hook_files.find(stringify(strip_trailing_string( + e->basename(), ".py")))->second->file_name()) + "'"); + } + } +#endif } } diff --git a/paludis/hooker.hh b/paludis/hooker.hh index 2668ec8bf..1791aa82f 100644 --- a/paludis/hooker.hh +++ b/paludis/hooker.hh @@ -22,6 +22,8 @@ #include <paludis/util/instantiation_policy.hh> #include <paludis/util/private_implementation_pattern.hh> +#include <paludis/util/graph-fwd.hh> +#include <string> namespace paludis { @@ -30,6 +32,19 @@ namespace paludis class Hook; class HookResult; + class HookFile : + private InstantiationPolicy<HookFile, instantiation_method::NonCopyableTag> + { + public: + virtual ~HookFile() + { + } + + virtual HookResult run(const Hook &) const PALUDIS_ATTRIBUTE((warn_unused_result)) = 0; + virtual const FSEntry file_name() const = 0; + virtual void add_dependencies(const Hook &, DirectedGraph<std::string, int> &) = 0; + }; + /** * Handles executing hooks for an Environment. * diff --git a/paludis/hooker_TEST.cc b/paludis/hooker_TEST.cc index 429c46c68..714a2766e 100644 --- a/paludis/hooker_TEST.cc +++ b/paludis/hooker_TEST.cc @@ -17,6 +17,7 @@ * Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "config.h" #include <paludis/hooker.hh> #include <paludis/hook.hh> #include <paludis/environments/test/test_environment.hh> @@ -26,6 +27,13 @@ #include <fstream> #include <iterator> +#ifdef ENABLE_PYTHON +# include <boost/version.hpp> +# if BOOST_VERSION >= 103400 +# define PYTHON_HOOKS 1 +# endif +#endif + using namespace test; using namespace paludis; @@ -42,6 +50,10 @@ namespace test_cases HookResult result(0, ""); hooker.add_dir(FSEntry("hooker_TEST_dir/"), false); +#ifdef PYTHON_HOOKS + result = hooker.perform_hook(Hook("py_hook")); + TEST_CHECK_EQUAL(result.max_exit_status, 0); +#endif result = hooker.perform_hook(Hook("simple_hook")); TEST_CHECK_EQUAL(result.max_exit_status, 3); TEST_CHECK_EQUAL(result.output, ""); @@ -77,7 +89,12 @@ namespace test_cases std::ifstream f(stringify(FSEntry("hooker_TEST_dir/ordering.out")).c_str()); std::string line((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>()); +#ifdef PYTHON_HOOKS + TEST_CHECK_EQUAL(line, "e\nc\nf\nd\nb\na\npy_hook\ng\ni\nh\nsohook\nk\nj\n"); +#else TEST_CHECK_EQUAL(line, "e\nc\nf\nd\nb\na\ng\ni\nh\nsohook\nk\nj\n"); +#endif + } } test_hooker_ordering; @@ -156,6 +173,13 @@ namespace test_cases TEST_CHECK_EQUAL(result.max_exit_status, 0); TEST_CHECK_EQUAL(result.output, "foo"); +#ifdef PYTHON_HOOKS + result = hooker.perform_hook(Hook("py_hook_output") + .grab_output(Hook::AllowedOutputValues()("foo"))); + TEST_CHECK_EQUAL(result.max_exit_status, 0); + TEST_CHECK_EQUAL(result.output, "foo"); +#endif + result = hooker.perform_hook(Hook("several_hooks_output") .grab_output(Hook::AllowedOutputValues())); TEST_CHECK_EQUAL(result.max_exit_status, 0); @@ -208,6 +232,5 @@ namespace test_cases TEST_CHECK_EQUAL(line, "one\ntwo\nthree\n"); } } test_hooker_bad_output_hooks; - } diff --git a/paludis/hooker_TEST_setup.sh b/paludis/hooker_TEST_setup.sh index ec5338dbe..19c382c54 100755 --- a/paludis/hooker_TEST_setup.sh +++ b/paludis/hooker_TEST_setup.sh @@ -56,6 +56,18 @@ ln -s ../../.libs/libpaludissohooks_TEST.so.${SO_SUFFIX} so_hook mkdir so_hook_output ln -s ../../.libs/libpaludissohooks_TEST.so.${SO_SUFFIX} so_hook_output +mkdir py_hook + +cat <<"END" > py_hook/hook.py +def hook_run_py_hook(env, hook_env): + pass +END + +mkdir py_hook_output +cat <<"END" > py_hook_output/hook.py +def hook_run_py_hook_output(env, hook_env): + return "foo" +END mkdir several_hooks cat <<"END" > several_hooks/one.hook @@ -198,7 +210,7 @@ hook_after_ordering() { echo x ;; h) - echo i + echo i py_hook ;; j) echo k libpaludissohooks_TEST @@ -212,6 +224,12 @@ for a in a b c d e f g h i j k ; do ln -s ../ordering.common ordering/${a}.hook done ln -s ../../.libs/libpaludissohooks_TEST.so.${SO_SUFFIX} ordering +cat <<"END" > ordering/py_hook.py +def hook_run_ordering(env, hook_env): + file("hooker_TEST_dir/ordering.out", "a").write("py_hook\n") +def hook_depend_ordering(hook_env): + return ["f"] +END mkdir bad_hooks cat <<"END" > bad_hooks.common @@ -230,6 +248,11 @@ asdf END chmod +x bad_hooks/two.hook +cat <<"END" > bad_hooks/four.py +def hook_run_bad_hooks(env, hook_env): + 1/0 +END + mkdir cycles cat <<"END" > cycles.common hook_run_cycles() { diff --git a/paludis/python_hooks.cc b/paludis/python_hooks.cc new file mode 100644 index 000000000..be68e43c8 --- /dev/null +++ b/paludis/python_hooks.cc @@ -0,0 +1,436 @@ +/* vim: set sw=4 sts=4 et foldmethod=syntax : */ + +#include <boost/python.hpp> + +#include <paludis/hooker.hh> +#include <paludis/hook.hh> +#include <paludis/environment.hh> +#include <paludis/util/fs_entry.hh> +#include <paludis/util/graph-impl.hh> +#include <paludis/util/log.hh> +#include <paludis/util/stringify.hh> +#include <paludis/util/strip.hh> +#include <paludis/util/system.hh> +#include <paludis/util/mutex.hh> + +#include <set> + +using namespace paludis; +namespace bp = boost::python; + +extern "C" +{ + tr1::shared_ptr<HookFile> create_py_hook_file(const FSEntry &, const bool, const Environment * const) + PALUDIS_VISIBLE; +} + +namespace +{ + class PyHookFile : + public HookFile + { + private: + static Mutex _mutex; + + static bp::dict _local_namespace_base; + static bp::dict _output_wrapper_namespace; + static bp::object _format_exception; + + const FSEntry _file_name; + const Environment * const _env; + const bool _run_prefixed; + bool _loaded; + bp::dict _local_namespace; + + friend class Prefix; + + class Prefix + { + private: + const PyHookFile * const phf; + + public: + Prefix(const PyHookFile * const, const std::string &); + ~Prefix(); + }; + + std::string _get_traceback() const; + + void _add_dependency_class(const Hook &, DirectedGraph<std::string, int> &, bool); + + public: + PyHookFile(const FSEntry &, const bool, const Environment * const); + + virtual HookResult run(const Hook &) const PALUDIS_ATTRIBUTE((warn_unused_result)); + + virtual const FSEntry file_name() const + { + return _file_name; + } + + virtual void add_dependencies(const Hook &, DirectedGraph<std::string, int> &); + }; + Mutex PyHookFile::_mutex; + bp::dict PyHookFile::_output_wrapper_namespace; + bp::dict PyHookFile::_local_namespace_base; + bp::object PyHookFile::_format_exception; +} + +PyHookFile::PyHookFile(const FSEntry & f, const bool r, const Environment * const e) : + _file_name(f), + _env(e), + _run_prefixed(r), + _loaded(false) +{ + Lock l(_mutex); + + static bool initialized(false); + + if (! initialized) + { + initialized = true; + try + { + Py_Initialize(); + + bp::object main = bp::import("__main__"); + bp::object global_namespace = main.attr("__dict__"); + + _local_namespace_base["__builtins__"] = global_namespace["__builtins__"]; + _output_wrapper_namespace = _local_namespace_base.copy(); + + bp::object traceback = bp::import("traceback"); + bp::object traceback_namespace = traceback.attr("__dict__"); + _format_exception = traceback_namespace["format_exception"]; + + bp::exec_file( + (getenv_with_default("PALUDIS_PYTHON_DIR", PYTHONINSTALLDIR) + + "/paludis_output_wrapper.py").c_str(), + _output_wrapper_namespace, _output_wrapper_namespace); + bp::import("paludis"); + } + catch (const bp::error_already_set &) + { + Log::get_instance()->message(ll_warning, lc_no_context, "Initializing Python interpreter failed:"); + PyErr_Print(); + return; + } + catch (const std::exception & ex) + { + Log::get_instance()->message(ll_warning, lc_no_context, + std::string("Initializing Python interpreter failed: '") + ex.what() + "'"); + return; + } + } + _local_namespace = _local_namespace_base.copy(); + + try + { + bp::exec_file(stringify(f).c_str(), _local_namespace, _local_namespace); + _loaded = true; + } + catch (const bp::error_already_set &) + { + Log::get_instance()->message(ll_warning, lc_no_context, "Loading hook '" + stringify(f) + "' failed: '" + + _get_traceback() + "'"); + } + catch (const std::exception & ex) + { + Log::get_instance()->message(ll_warning, lc_no_context, "Loading hook '" + stringify(f) + "' failed: '" + + ex.what() + "'"); + } +} + +HookResult +PyHookFile::run(const Hook & hook) const +{ + Context c("When running hook '" + stringify(file_name()) + "' for hook '" + hook.name() + "':"); + + if (! _loaded) + return HookResult(0, ""); + + Lock l(_mutex); + + bp::object _run; + + try + { + _run = _local_namespace["hook_run_" + hook.name()]; + } + catch (const bp::error_already_set &) + { + if (PyErr_ExceptionMatches(PyExc_KeyError)) + { + Log::get_instance()->message(ll_warning, lc_no_context, + "Hook '" + stringify(file_name()) + "' does not define the hook_run_" + + hook.name() + " function"); + PyErr_Clear(); + } + else + { + Log::get_instance()->message(ll_warning, lc_no_context, + "Hook '" + stringify(file_name()) + "' failed unexpectedly: '" + + _get_traceback() + "'"); + } + + return HookResult(1, ""); + } + + Prefix p(this, _run_prefixed ? strip_trailing_string(file_name().basename(), ".py") + "> " : ""); + + Log::get_instance()->message(ll_debug, lc_no_context, "Starting hook '" + + stringify(file_name()) + "' for '" + hook.name() + "'"); + + bp::dict hook_env; + hook_env["HOOK"] = hook.name(); + hook_env["HOOK_FILE"] = stringify(file_name()); + + for (Hook::Iterator x(hook.begin()), x_end(hook.end()) ; x != x_end ; ++x) + hook_env[x->first] = x->second; + + bp::object result; + try + { + result = _run(bp::ptr(_env), hook_env); + } + catch (const bp::error_already_set &) + { + Log::get_instance()->message(ll_warning, lc_no_context, + "Hook '" + stringify(file_name()) + "': running hook_run_" + hook.name() + + " function failed: '" + _get_traceback() + "'"); + return HookResult(1, ""); + } + + if (hook.output_dest == hod_grab) + { + if (bp::extract<std::string>(result).check()) + { + std::string result_s = bp::extract<std::string>(result); + + Log::get_instance()->message(ll_debug, lc_no_context, + "Hook '" + stringify(file_name()) + "': hook_run_" + hook.name() + + " function returned '" + result_s + "'"); + + return HookResult(0, result_s); + } + else + { + Log::get_instance()->message(ll_warning, lc_no_context, + "Hook '" + stringify(file_name()) + "': hook_run_" + hook.name() + + " function returned not a string."); + return HookResult(1, ""); + } + } + else + return HookResult(0, ""); +} + +void +PyHookFile::add_dependencies(const Hook & hook, DirectedGraph<std::string, int> & g) +{ + Context c("When finding dependencies of hook script '" + stringify(file_name()) + + "' for hook '" + hook.name() + "':"); + + if (! _loaded) + return; + + Lock l(_mutex); + + Prefix p(this, _run_prefixed ? strip_trailing_string(file_name().basename(), ".py") + "> " : ""); + + _add_dependency_class(hook, g, false); + _add_dependency_class(hook, g, true); +} + +void +PyHookFile::_add_dependency_class(const Hook & hook, DirectedGraph<std::string, int> & g, bool depend) +{ + Context context("When adding dependency class '" + stringify(depend ? "depend" : "after") + "' for hook '" + + stringify(hook.name()) + "' file '" + stringify(file_name()) + "':"); + + bp::object _run; + try + { + _run = _local_namespace["hook_" + stringify(depend ? "depend" : "after") + "_" + hook.name()]; + } + catch (const bp::error_already_set &) + { + if (PyErr_ExceptionMatches(PyExc_KeyError)) + { + Log::get_instance()->message(ll_debug, lc_no_context, + "Hook '" + stringify(file_name()) + "' does not define the hook_" + + stringify(depend ? "depend" : "after") + "_" + hook.name() + " function"); + PyErr_Clear(); + } + else + { + Log::get_instance()->message(ll_warning, lc_no_context, + "Hook '" + stringify(file_name()) + "' failed unexpectedly: '" + + _get_traceback() + "'"); + } + + return; + } + + bp::dict hook_env; + hook_env["HOOK"] = hook.name(); + hook_env["HOOK_FILE"] = stringify(file_name()); + + for (Hook::Iterator x(hook.begin()), x_end(hook.end()) ; x != x_end ; ++x) + hook_env[x->first] = x->second; + + bp::object result; + try + { + result = _run(hook_env); + } + catch (const bp::error_already_set &) + { + Log::get_instance()->message(ll_warning, lc_no_context, + "Hook '" + stringify(file_name()) + "': running hook_" + + stringify(depend ? "depend" : "after") + "_" + hook.name() + " function failed: '" + + _get_traceback() + "'"); + return; + } + + bp::str py_deps(result); + std::string deps = bp::extract<std::string>(py_deps); + + std::set<std::string> deps_s; + + if (bp::extract<bp::list>(result).check()) + { + bp::list l = bp::extract<bp::list>(result); + + while (PyList_Size(l.ptr())) + { + bp::object o(l.pop()); + if (bp::extract<std::string>(o).check()) + { + deps_s.insert(bp::extract<std::string>(o)); + } + else + { + Log::get_instance()->message(ll_warning, lc_no_context, + "Hook '" + stringify(file_name()) + "': hook_" + + stringify(depend ? "depend" : "after") + "_" + hook.name() + + " function returned not a list of strings: '" + deps + "'"); + return; + } + } + } + else + { + Log::get_instance()->message(ll_warning, lc_no_context, + "Hook '" + stringify(file_name()) + "': hook_" + + stringify(depend ? "depend" : "after") + "_" + hook.name() + + " function returned not a list: '" + deps + "'"); + return; + } + + Log::get_instance()->message(ll_debug, lc_no_context, + "Hook '" + stringify(file_name()) + "': hook_" + + stringify(depend ? "depend" : "after") + "_" + hook.name() + " function returned '" + deps + "'"); + + for (std::set<std::string>::const_iterator d(deps_s.begin()), d_end(deps_s.end()) ; + d != d_end ; ++d) + { + if (g.has_node(*d)) + g.add_edge(strip_trailing_string(file_name().basename(), ".py"), *d, 0); + else if (depend) + Log::get_instance()->message(ll_warning, lc_context, "Hook dependency '" + stringify(*d) + + "' for '" + stringify(file_name()) + "' not found"); + else + Log::get_instance()->message(ll_debug, lc_context, "Hook after '" + stringify(*d) + + "' for '" + stringify(file_name()) + "' not found"); + } +} + +PyHookFile::Prefix::Prefix(const PyHookFile * const f, const std::string & prefix): + phf(f) +{ + try + { + phf->_output_wrapper_namespace["set_prefix"](prefix); + } + catch (const bp::error_already_set &) + { + Log::get_instance()->message(ll_warning, lc_no_context, + "Hook '" + stringify(phf->file_name()) + "' failed unexpectedly: '" + + phf->_get_traceback() + "'"); + } +} + +PyHookFile::Prefix::~Prefix() +{ + try + { + phf->_output_wrapper_namespace["restore_prefix"](); + } + catch (const bp::error_already_set &) + { + Log::get_instance()->message(ll_warning, lc_no_context, + "Hook '" + stringify(phf->file_name()) + "' failed unexpectedly: '" + + phf->_get_traceback() + "'"); + } +} + +std::string +PyHookFile::_get_traceback() const +{ + if (! PyErr_Occurred()) + return ""; + + Context c("When getting traceback"); + + PyObject * ptype, * pvalue, * ptraceback; + PyErr_Fetch(&ptype, &pvalue, &ptraceback); + + if (ptype == NULL) + ptype = Py_None; + if (pvalue == NULL) + pvalue = Py_None; + if (ptraceback == NULL) + ptraceback = Py_None; + + PyObject * result(PyObject_CallFunctionObjArgs(_format_exception.ptr(), ptype, pvalue, ptraceback, NULL)); + + if (result == NULL) + { + Log::get_instance()->message(ll_warning, lc_context, + "Hook '" + stringify(file_name()) + "': _get_traceback(): traceback.format_exception failed"); + return "Getting traceback failed"; + } + + bp::list l; + if (bp::extract<bp::list>(result).check()) + { + l = bp::extract<bp::list>(result); + } + else + { + Log::get_instance()->message(ll_warning, lc_context, + "Hook '" + stringify(file_name()) + "': _get_traceback(): cannot extract list of lines"); + return "Getting traceback failed"; + } + + bp::str result_str; + try + { + result_str = result_str.join(l); + } + catch (const bp::error_already_set &) + { + Log::get_instance()->message(ll_warning, lc_context, + "Hook '" + stringify(file_name()) + "': _get_traceback(): joining list of lines failed"); + return "Getting traceback failed"; + } + + return strip_trailing(bp::extract<std::string>(result_str)(), "\n"); +} + +tr1::shared_ptr<HookFile> +create_py_hook_file(const FSEntry & f, const bool b, const Environment * const e) +{ + return tr1::shared_ptr<HookFile>(new PyHookFile(f, b, e)); +} |