Added a new level of Python integration
This commit is contained in:
parent
3598abf9d2
commit
0814c5a23f
6 changed files with 141 additions and 41 deletions
|
|
@ -70,9 +70,28 @@ void export_scope()
|
||||||
class_< scope_t, scope_wrapper, boost::noncopyable > ("Scope", no_init)
|
class_< scope_t, scope_wrapper, boost::noncopyable > ("Scope", no_init)
|
||||||
.def("define", py_scope_define)
|
.def("define", py_scope_define)
|
||||||
.def("lookup", py_scope_lookup)
|
.def("lookup", py_scope_lookup)
|
||||||
.def("resolve", &scope_t::resolve)
|
|
||||||
.def("__getattr__", py_scope_getattr)
|
.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
|
} // namespace ledger
|
||||||
|
|
|
||||||
|
|
@ -46,14 +46,13 @@ using namespace boost::python;
|
||||||
|
|
||||||
void export_session()
|
void export_session()
|
||||||
{
|
{
|
||||||
#if 0
|
class_< session_t, bases<symbol_scope_t>,
|
||||||
class_< session_t > ("Session")
|
shared_ptr<session_t>, boost::noncopyable > ("SessionBase")
|
||||||
;
|
;
|
||||||
#endif
|
|
||||||
|
|
||||||
//register_optional_to_python<amount_t>();
|
class_< python_interpreter_t, bases<session_t>,
|
||||||
|
shared_ptr<python_interpreter_t>, boost::noncopyable > ("Session")
|
||||||
//implicitly_convertible<string, amount_t>();
|
;
|
||||||
|
|
||||||
#define EXC_TRANSLATE(type) \
|
#define EXC_TRANSLATE(type) \
|
||||||
register_exception_translator<type>(&exc_translate_ ## type);
|
register_exception_translator<type>(&exc_translate_ ## type);
|
||||||
|
|
|
||||||
|
|
@ -41,13 +41,13 @@ void export_amount();
|
||||||
void export_balance();
|
void export_balance();
|
||||||
void export_chain();
|
void export_chain();
|
||||||
void export_commodity();
|
void export_commodity();
|
||||||
void export_xact();
|
|
||||||
void export_expr();
|
void export_expr();
|
||||||
void export_flags();
|
void export_flags();
|
||||||
void export_format();
|
void export_format();
|
||||||
void export_global();
|
void export_global();
|
||||||
void export_item();
|
void export_item();
|
||||||
void export_journal();
|
void export_journal();
|
||||||
|
void export_post();
|
||||||
void export_report();
|
void export_report();
|
||||||
void export_scope();
|
void export_scope();
|
||||||
void export_session();
|
void export_session();
|
||||||
|
|
@ -55,7 +55,7 @@ void export_timelog();
|
||||||
void export_times();
|
void export_times();
|
||||||
void export_utils();
|
void export_utils();
|
||||||
void export_value();
|
void export_value();
|
||||||
void export_post();
|
void export_xact();
|
||||||
|
|
||||||
void initialize_for_python()
|
void initialize_for_python()
|
||||||
{
|
{
|
||||||
|
|
@ -63,13 +63,13 @@ void initialize_for_python()
|
||||||
export_balance();
|
export_balance();
|
||||||
export_chain();
|
export_chain();
|
||||||
export_commodity();
|
export_commodity();
|
||||||
export_xact();
|
|
||||||
export_expr();
|
export_expr();
|
||||||
export_flags();
|
export_flags();
|
||||||
export_format();
|
export_format();
|
||||||
export_global();
|
export_global();
|
||||||
export_item();
|
export_item();
|
||||||
export_journal();
|
export_journal();
|
||||||
|
export_post();
|
||||||
export_report();
|
export_report();
|
||||||
export_scope();
|
export_scope();
|
||||||
export_session();
|
export_session();
|
||||||
|
|
@ -77,7 +77,9 @@ void initialize_for_python()
|
||||||
export_times();
|
export_times();
|
||||||
export_utils();
|
export_utils();
|
||||||
export_value();
|
export_value();
|
||||||
export_post();
|
export_xact();
|
||||||
|
|
||||||
|
scope().attr("session") = python_session;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct python_run
|
struct python_run
|
||||||
|
|
@ -106,15 +108,53 @@ void python_interpreter_t::initialize()
|
||||||
|
|
||||||
object main_module = python::import("__main__");
|
object main_module = python::import("__main__");
|
||||||
if (! main_module)
|
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__"));
|
main_nspace = extract<dict>(main_module.attr("__dict__"));
|
||||||
if (! main_nspace)
|
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);
|
python::detail::init_module("ledger", &initialize_for_python);
|
||||||
|
|
||||||
is_initialized = true;
|
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&) {
|
catch (const error_already_set&) {
|
||||||
PyErr_Print();
|
PyErr_Print();
|
||||||
|
|
@ -130,17 +170,13 @@ object python_interpreter_t::import(const string& str)
|
||||||
initialize();
|
initialize();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
TRACE_START(python_import, 1, "Imported Python module: " << str);
|
|
||||||
|
|
||||||
object mod = python::import(str.c_str());
|
object mod = python::import(str.c_str());
|
||||||
if (! mod)
|
if (! mod)
|
||||||
throw_(std::logic_error, "Failed to import Python module " << str);
|
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__"));
|
main_nspace.update(mod.attr("__dict__"));
|
||||||
|
|
||||||
TRACE_FINISH(python_import, 1);
|
|
||||||
|
|
||||||
return mod;
|
return mod;
|
||||||
}
|
}
|
||||||
catch (const error_already_set&) {
|
catch (const error_already_set&) {
|
||||||
|
|
@ -209,6 +245,17 @@ object python_interpreter_t::eval(const string& str, py_eval_mode_t mode)
|
||||||
return object();
|
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)
|
expr_t::ptr_op_t python_interpreter_t::lookup(const string& name)
|
||||||
{
|
{
|
||||||
// Give our superclass first dibs on symbol definitions
|
// 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();
|
const char * p = name.c_str();
|
||||||
switch (*p) {
|
switch (*p) {
|
||||||
case 'o':
|
case 'o':
|
||||||
if (std::strncmp(p, "opt_", 4) == 0) {
|
if (WANT_OPT()) { const char * q = p + OPT_PREFIX_LEN;
|
||||||
p = p + 4;
|
if (option_t<python_interpreter_t> * handler = lookup_option(q))
|
||||||
switch (*p) {
|
return MAKE_OPT_HANDLER(python_interpreter_t, handler);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
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)
|
value_t python_interpreter_t::functor_t::operator()(call_scope_t& args)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
std::signal(SIGINT, SIG_DFL);
|
||||||
if (! PyCallable_Check(func.ptr())) {
|
if (! PyCallable_Check(func.ptr())) {
|
||||||
extract<value_t> val(func);
|
extract<value_t> val(func);
|
||||||
|
std::signal(SIGINT, sigint_handler);
|
||||||
if (val.check())
|
if (val.check())
|
||||||
return val();
|
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,
|
throw_(calc_error,
|
||||||
"Could not evaluate Python variable '" << name << "'");
|
"Could not evaluate Python variable '" << name << "'");
|
||||||
|
#endif
|
||||||
} else {
|
} else {
|
||||||
if (args.size() > 0) {
|
if (args.size() > 0) {
|
||||||
list arglist;
|
list arglist;
|
||||||
|
|
@ -272,6 +320,7 @@ value_t python_interpreter_t::functor_t::operator()(call_scope_t& args)
|
||||||
throw_(calc_error,
|
throw_(calc_error,
|
||||||
"Could not evaluate Python variable '" << name << "'");
|
"Could not evaluate Python variable '" << name << "'");
|
||||||
}
|
}
|
||||||
|
std::signal(SIGINT, sigint_handler);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
else if (PyErr_Occurred()) {
|
else if (PyErr_Occurred()) {
|
||||||
|
|
@ -281,15 +330,22 @@ value_t python_interpreter_t::functor_t::operator()(call_scope_t& args)
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
std::signal(SIGINT, sigint_handler);
|
||||||
return call<value_t>(func.ptr());
|
return call<value_t>(func.ptr());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (const error_already_set&) {
|
catch (const error_already_set&) {
|
||||||
|
std::signal(SIGINT, sigint_handler);
|
||||||
PyErr_Print();
|
PyErr_Print();
|
||||||
throw_(calc_error,
|
throw_(calc_error,
|
||||||
"Failed call to Python function '" << name << "'");
|
"Failed call to Python function '" << name << "'");
|
||||||
}
|
}
|
||||||
|
catch (...) {
|
||||||
|
std::signal(SIGINT, sigint_handler);
|
||||||
|
}
|
||||||
|
std::signal(SIGINT, sigint_handler);
|
||||||
|
|
||||||
return NULL_VALUE;
|
return NULL_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@
|
||||||
#ifndef _PYINTERP_H
|
#ifndef _PYINTERP_H
|
||||||
#define _PYINTERP_H
|
#define _PYINTERP_H
|
||||||
|
|
||||||
#include "scope.h"
|
#include "interactive.h"
|
||||||
#include "session.h"
|
#include "session.h"
|
||||||
|
|
||||||
#if defined(HAVE_BOOST_PYTHON)
|
#if defined(HAVE_BOOST_PYTHON)
|
||||||
|
|
@ -100,29 +100,36 @@ public:
|
||||||
virtual value_t operator()(call_scope_t& args);
|
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);
|
virtual expr_t::ptr_op_t lookup(const string& name);
|
||||||
|
|
||||||
value_t option_import_(call_scope_t& args) {
|
OPTION_(python_interpreter_t, import_, DO_(scope) {
|
||||||
path file(args[0].to_string());
|
interactive_t args(scope, "s");
|
||||||
|
|
||||||
python::object module_sys = import("sys");
|
path file(args.get<string>(0));
|
||||||
python::object sys_dict = module_sys.attr("__dict__");
|
|
||||||
|
|
||||||
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
|
#if BOOST_VERSION >= 103700
|
||||||
paths.insert(0, file.parent_path().string());
|
paths.insert(0, file.parent_path().string());
|
||||||
#else
|
#else
|
||||||
paths.insert(0, file.branch_path().string());
|
paths.insert(0, file.branch_path().string());
|
||||||
#endif
|
#endif
|
||||||
sys_dict["path"] = paths;
|
sys_dict["path"] = paths;
|
||||||
|
|
||||||
#if BOOST_VERSION >= 103700
|
#if BOOST_VERSION >= 103700
|
||||||
import(file.stem());
|
string name = file.filename();
|
||||||
|
if (contains(name, ".py"))
|
||||||
|
parent->import(file.stem());
|
||||||
|
else
|
||||||
|
parent->import(name);
|
||||||
#else
|
#else
|
||||||
import(file.string());
|
parent->import(file.leaf());
|
||||||
#endif
|
#endif
|
||||||
return true;
|
});
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern shared_ptr<python_interpreter_t> python_session;
|
extern shared_ptr<python_interpreter_t> python_session;
|
||||||
|
|
|
||||||
|
|
@ -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)
|
option_t<report_t> * report_t::lookup_option(const char * p)
|
||||||
{
|
{
|
||||||
switch (*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>
|
(reporter<post_t, post_handler_ptr, &report_t::commodities_report>
|
||||||
(new format_posts(*this, report_format(HANDLER(pricesdb_format_))),
|
(new format_posts(*this, report_format(HANDLER(pricesdb_format_))),
|
||||||
*this));
|
*this));
|
||||||
|
else if (is_eq(q, "python") && maybe_import("ledger.interp"))
|
||||||
|
return session.lookup(string(CMD_PREFIX) + "python");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'r':
|
case 'r':
|
||||||
|
|
@ -622,6 +633,8 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
|
||||||
case 's':
|
case 's':
|
||||||
if (is_eq(q, "stats") || is_eq(q, "stat"))
|
if (is_eq(q, "stats") || is_eq(q, "stat"))
|
||||||
return WRAP_FUNCTOR(reporter<>(new gather_statistics(*this), *this));
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -686,6 +699,10 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
|
||||||
if (is_eq(q, "format"))
|
if (is_eq(q, "format"))
|
||||||
return WRAP_FUNCTOR(format_command);
|
return WRAP_FUNCTOR(format_command);
|
||||||
break;
|
break;
|
||||||
|
case 'h':
|
||||||
|
if (is_eq(q, "hello") && maybe_import("ledger.hello"))
|
||||||
|
return session.lookup(string(PRECMD_PREFIX) + "hello");
|
||||||
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
if (is_eq(q, "parse"))
|
if (is_eq(q, "parse"))
|
||||||
return WRAP_FUNCTOR(parse_command);
|
return WRAP_FUNCTOR(parse_command);
|
||||||
|
|
|
||||||
|
|
@ -171,6 +171,8 @@ public:
|
||||||
HANDLED(lots) || HANDLED(lot_tags));
|
HANDLED(lots) || HANDLED(lot_tags));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool maybe_import(const string& module);
|
||||||
|
|
||||||
option_t<report_t> * lookup_option(const char * p);
|
option_t<report_t> * lookup_option(const char * p);
|
||||||
|
|
||||||
virtual void define(const string& name, expr_t::ptr_op_t def) {
|
virtual void define(const string& name, expr_t::ptr_op_t def) {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue