summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2009-02-24 19:48:14 -0400
committerJohn Wiegley <johnw@newartisans.com>2009-02-24 19:48:14 -0400
commit0814c5a23f44120088b6139bb7bf1f934f1d1057 (patch)
tree1c2132245b93881b42b99aa0dd5b7c5d00aff2e0
parent3598abf9d235c24618856d4e0d5092e818e6332e (diff)
downloadfork-ledger-0814c5a23f44120088b6139bb7bf1f934f1d1057.tar.gz
fork-ledger-0814c5a23f44120088b6139bb7bf1f934f1d1057.tar.bz2
fork-ledger-0814c5a23f44120088b6139bb7bf1f934f1d1057.zip
Added a new level of Python integration
-rw-r--r--src/py_scope.cc21
-rw-r--r--src/py_session.cc11
-rw-r--r--src/pyinterp.cc98
-rw-r--r--src/pyinterp.h33
-rw-r--r--src/report.cc17
-rw-r--r--src/report.h2
6 files changed, 141 insertions, 41 deletions
diff --git a/src/py_scope.cc b/src/py_scope.cc
index 55f779d2..897e2a84 100644
--- a/src/py_scope.cc
+++ b/src/py_scope.cc
@@ -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
diff --git a/src/py_session.cc b/src/py_session.cc
index 108dcc3c..df0c6dd8 100644
--- a/src/py_session.cc
+++ b/src/py_session.cc
@@ -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);
diff --git a/src/pyinterp.cc b/src/pyinterp.cc
index 9782f3a0..33b3ca9b 100644
--- a/src/pyinterp.cc
+++ b/src/pyinterp.cc
@@ -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;
}
diff --git a/src/pyinterp.h b/src/pyinterp.h
index 4863cbb9..1b0f4059 100644
--- a/src/pyinterp.h
+++ b/src/pyinterp.h
@@ -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");
+
+ path file(args.get<string>(0));
- python::object module_sys = import("sys");
- python::object sys_dict = module_sys.attr("__dict__");
+ python::object module_sys = parent->import("sys");
+ python::object sys_dict = module_sys.attr("__dict__");
- python::list paths(sys_dict["path"]);
+ 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;
diff --git a/src/report.cc b/src/report.cc
index dfbbf65b..90a8399d 100644
--- a/src/report.cc
+++ b/src/report.cc
@@ -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);
diff --git a/src/report.h b/src/report.h
index 7585493f..a98286a9 100644
--- a/src/report.h
+++ b/src/report.h
@@ -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) {