Added a new level of Python integration

This commit is contained in:
John Wiegley 2009-02-24 19:48:14 -04:00
parent 3598abf9d2
commit 0814c5a23f
6 changed files with 141 additions and 41 deletions

View file

@ -70,9 +70,28 @@ void export_scope()
class_< scope_t, scope_wrapper, boost::noncopyable > ("Scope", no_init)
.def("define", py_scope_define)
.def("lookup", py_scope_lookup)
.def("resolve", &scope_t::resolve)
.def("__getattr__", py_scope_getattr)
;
class_< child_scope_t, bases<scope_t>,
boost::noncopyable > ("ChildScope")
.def(init<>())
.def(init<scope_t&>())
;
class_< symbol_scope_t, bases<child_scope_t>,
boost::noncopyable > ("SymbolScope")
.def(init<>())
.def(init<scope_t&>())
;
class_< call_scope_t, bases<child_scope_t>,
boost::noncopyable > ("CallScope", init<scope_t&>())
;
class_< bind_scope_t, bases<child_scope_t>,
boost::noncopyable > ("BindScope", init<scope_t&, scope_t&>())
;
}
} // namespace ledger

View file

@ -46,14 +46,13 @@ using namespace boost::python;
void export_session()
{
#if 0
class_< session_t > ("Session")
class_< session_t, bases<symbol_scope_t>,
shared_ptr<session_t>, boost::noncopyable > ("SessionBase")
;
#endif
//register_optional_to_python<amount_t>();
//implicitly_convertible<string, amount_t>();
class_< python_interpreter_t, bases<session_t>,
shared_ptr<python_interpreter_t>, boost::noncopyable > ("Session")
;
#define EXC_TRANSLATE(type) \
register_exception_translator<type>(&exc_translate_ ## type);

View file

@ -41,13 +41,13 @@ void export_amount();
void export_balance();
void export_chain();
void export_commodity();
void export_xact();
void export_expr();
void export_flags();
void export_format();
void export_global();
void export_item();
void export_journal();
void export_post();
void export_report();
void export_scope();
void export_session();
@ -55,7 +55,7 @@ void export_timelog();
void export_times();
void export_utils();
void export_value();
void export_post();
void export_xact();
void initialize_for_python()
{
@ -63,13 +63,13 @@ void initialize_for_python()
export_balance();
export_chain();
export_commodity();
export_xact();
export_expr();
export_flags();
export_format();
export_global();
export_item();
export_journal();
export_post();
export_report();
export_scope();
export_session();
@ -77,7 +77,9 @@ void initialize_for_python()
export_times();
export_utils();
export_value();
export_post();
export_xact();
scope().attr("session") = python_session;
}
struct python_run
@ -106,15 +108,53 @@ void python_interpreter_t::initialize()
object main_module = python::import("__main__");
if (! main_module)
throw_(std::logic_error, "Python failed to initialize");
throw_(std::logic_error,
"Python failed to initialize (couldn't find __main__)");
main_nspace = extract<dict>(main_module.attr("__dict__"));
if (! main_nspace)
throw_(std::logic_error, "Python failed to initialize");
throw_(std::logic_error,
"Python failed to initialize (couldn't find __dict__)");
python::detail::init_module("ledger", &initialize_for_python);
is_initialized = true;
// Hack ledger.__path__ so it points to a real location
python::object module_sys = import("sys");
python::object sys_dict = module_sys.attr("__dict__");
python::list paths(sys_dict["path"]);
bool path_initialized = false;
int n = python::extract<int>(paths.attr("__len__")());
for (int i = 0; i < n; i++) {
python::extract<std::string> str(paths[i]);
path pathname(str);
DEBUG("python.interp", "sys.path = " << pathname);
if (exists(pathname / "ledger" / "__init__.py")) {
if (python::object module_ledger = import("ledger")) {
DEBUG("python.interp",
"Setting ledger.__path__ = " << (pathname / "ledger"));
python::object ledger_dict = module_ledger.attr("__dict__");
python::list temp_list;
temp_list.append((pathname / "ledger").string());
ledger_dict["__path__"] = temp_list;
} else {
throw_(std::logic_error,
"Python failed to initialize (couldn't find ledger)");
}
path_initialized = true;
break;
}
}
if (! path_initialized)
std::cerr
<< "Warning: Ledger failed to find 'ledger/__init__.py' on the PYTHONPATH"
<< std::endl;
}
catch (const error_already_set&) {
PyErr_Print();
@ -130,17 +170,13 @@ object python_interpreter_t::import(const string& str)
initialize();
try {
TRACE_START(python_import, 1, "Imported Python module: " << str);
object mod = python::import(str.c_str());
if (! mod)
throw_(std::logic_error, "Failed to import Python module " << str);
// Import all top-level xacts directly into the main namespace
// Import all top-level entries directly into the main namespace
main_nspace.update(mod.attr("__dict__"));
TRACE_FINISH(python_import, 1);
return mod;
}
catch (const error_already_set&) {
@ -209,6 +245,17 @@ object python_interpreter_t::eval(const string& str, py_eval_mode_t mode)
return object();
}
option_t<python_interpreter_t> *
python_interpreter_t::lookup_option(const char * p)
{
switch (*p) {
case 'i':
OPT(import_);
break;
}
return NULL;
}
expr_t::ptr_op_t python_interpreter_t::lookup(const string& name)
{
// Give our superclass first dibs on symbol definitions
@ -218,16 +265,9 @@ expr_t::ptr_op_t python_interpreter_t::lookup(const string& name)
const char * p = name.c_str();
switch (*p) {
case 'o':
if (std::strncmp(p, "opt_", 4) == 0) {
p = p + 4;
switch (*p) {
case 'i':
if (std::strcmp(p, "import_") == 0)
return MAKE_FUNCTOR(python_interpreter_t::option_import_);
else if (std::strcmp(p, "import") == 0)
return expr_t::ptr_op_t();
break;
}
if (WANT_OPT()) { const char * q = p + OPT_PREFIX_LEN;
if (option_t<python_interpreter_t> * handler = lookup_option(q))
return MAKE_OPT_HANDLER(python_interpreter_t, handler);
}
break;
}
@ -245,12 +285,20 @@ expr_t::ptr_op_t python_interpreter_t::lookup(const string& name)
value_t python_interpreter_t::functor_t::operator()(call_scope_t& args)
{
try {
std::signal(SIGINT, SIG_DFL);
if (! PyCallable_Check(func.ptr())) {
extract<value_t> val(func);
std::signal(SIGINT, sigint_handler);
if (val.check())
return val();
#if 1
// jww (2009-02-24): Distinguish between "no return" and a value with an
// unconvertable type
return NULL_VALUE;
#else
throw_(calc_error,
"Could not evaluate Python variable '" << name << "'");
#endif
} else {
if (args.size() > 0) {
list arglist;
@ -272,6 +320,7 @@ value_t python_interpreter_t::functor_t::operator()(call_scope_t& args)
throw_(calc_error,
"Could not evaluate Python variable '" << name << "'");
}
std::signal(SIGINT, sigint_handler);
return result;
}
else if (PyErr_Occurred()) {
@ -281,15 +330,22 @@ value_t python_interpreter_t::functor_t::operator()(call_scope_t& args)
assert(false);
}
} else {
std::signal(SIGINT, sigint_handler);
return call<value_t>(func.ptr());
}
}
}
catch (const error_already_set&) {
std::signal(SIGINT, sigint_handler);
PyErr_Print();
throw_(calc_error,
"Failed call to Python function '" << name << "'");
}
catch (...) {
std::signal(SIGINT, sigint_handler);
}
std::signal(SIGINT, sigint_handler);
return NULL_VALUE;
}

View file

@ -32,7 +32,7 @@
#ifndef _PYINTERP_H
#define _PYINTERP_H
#include "scope.h"
#include "interactive.h"
#include "session.h"
#if defined(HAVE_BOOST_PYTHON)
@ -100,29 +100,36 @@ public:
virtual value_t operator()(call_scope_t& args);
};
option_t<python_interpreter_t> * lookup_option(const char * p);
virtual expr_t::ptr_op_t lookup(const string& name);
value_t option_import_(call_scope_t& args) {
path file(args[0].to_string());
OPTION_(python_interpreter_t, import_, DO_(scope) {
interactive_t args(scope, "s");
python::object module_sys = import("sys");
python::object sys_dict = module_sys.attr("__dict__");
path file(args.get<string>(0));
python::list paths(sys_dict["path"]);
python::object module_sys = parent->import("sys");
python::object sys_dict = module_sys.attr("__dict__");
python::list paths(sys_dict["path"]);
#if BOOST_VERSION >= 103700
paths.insert(0, file.parent_path().string());
paths.insert(0, file.parent_path().string());
#else
paths.insert(0, file.branch_path().string());
paths.insert(0, file.branch_path().string());
#endif
sys_dict["path"] = paths;
sys_dict["path"] = paths;
#if BOOST_VERSION >= 103700
import(file.stem());
string name = file.filename();
if (contains(name, ".py"))
parent->import(file.stem());
else
parent->import(name);
#else
import(file.string());
parent->import(file.leaf());
#endif
return true;
}
});
};
extern shared_ptr<python_interpreter_t> python_session;

View file

@ -317,6 +317,15 @@ namespace {
};
}
bool report_t::maybe_import(const string& module)
{
if (lookup(string(OPT_PREFIX) + "import_")) {
expr_t(string(OPT_PREFIX) + "import_(\"" + module + "\")").calc(*this);
return true;
}
return false;
}
option_t<report_t> * report_t::lookup_option(const char * p)
{
switch (*p) {
@ -607,6 +616,8 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
(reporter<post_t, post_handler_ptr, &report_t::commodities_report>
(new format_posts(*this, report_format(HANDLER(pricesdb_format_))),
*this));
else if (is_eq(q, "python") && maybe_import("ledger.interp"))
return session.lookup(string(CMD_PREFIX) + "python");
break;
case 'r':
@ -622,6 +633,8 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
case 's':
if (is_eq(q, "stats") || is_eq(q, "stat"))
return WRAP_FUNCTOR(reporter<>(new gather_statistics(*this), *this));
else if (is_eq(q, "server") && maybe_import("ledger.server"))
return session.lookup(string(CMD_PREFIX) + "server");
break;
}
}
@ -686,6 +699,10 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
if (is_eq(q, "format"))
return WRAP_FUNCTOR(format_command);
break;
case 'h':
if (is_eq(q, "hello") && maybe_import("ledger.hello"))
return session.lookup(string(PRECMD_PREFIX) + "hello");
break;
case 'p':
if (is_eq(q, "parse"))
return WRAP_FUNCTOR(parse_command);

View file

@ -171,6 +171,8 @@ public:
HANDLED(lots) || HANDLED(lot_tags));
}
bool maybe_import(const string& module);
option_t<report_t> * lookup_option(const char * p);
virtual void define(const string& name, expr_t::ptr_op_t def) {