python integrated both ways (see sample.dat), and initialized on-demand

This commit is contained in:
John Wiegley 2004-09-14 04:47:41 -04:00
parent 6c66d1e0ef
commit a013b520ba
13 changed files with 296 additions and 73 deletions

View file

@ -1,16 +1,18 @@
lib_LTLIBRARIES = libamounts.la
libamounts_la_SOURCES = amount.cc balance.cc value.cc
libamounts_la_LDFLAGS = -version-info 2:0
lib_LTLIBRARIES += libledger.la
libledger_la_SOURCES = autoxact.cc binary.cc config.cc datetime.cc \
format.cc journal.cc option.cc parser.cc qif.cc quotes.cc \
textual.cc valexpr.cc walk.cc
lib_LTLIBRARIES = libledger.la
libledger_la_SOURCES = amount.cc balance.cc value.cc autoxact.cc \
binary.cc config.cc datetime.cc format.cc journal.cc option.cc \
parser.cc qif.cc quotes.cc textual.cc valexpr.cc walk.cc
if READ_GNUCASH
libledger_la_SOURCES += gnucash.cc
endif
if HAVE_BOOST_PYTHON
libledger_la_SOURCES += python.cc
libledger_la_CXXFLAGS = -DUSE_BOOST_PYTHON=1
else
libledger_la_CXXFLAGS =
endif
if DEBUG
libledger_la_CXXFLAGS = -DDEBUG_LEVEL=4
libledger_la_CXXFLAGS += -DDEBUG_LEVEL=4
libledger_la_SOURCES += debug.cc
endif
libledger_la_LDFLAGS = -version-info 2:0
@ -22,7 +24,7 @@ if DEBUG
ledger_CXXFLAGS = -DDEBUG_LEVEL=4
endif
ledger_SOURCES = main.cc
ledger_LDADD = $(LIBOBJS) libamounts.la libledger.la
ledger_LDADD = $(LIBOBJS) libledger.la -lboost_python -L/sw/lib/python2.3/config -lpython2.3
nobase_include_HEADERS = \
amount.h \
@ -48,17 +50,12 @@ info_TEXINFOS = ledger.texi
######################################################################
if HAVE_PYTHON
if HAVE_BOOST_PYTHON
noinst_LIBRARIES = libamounts_bpy.a
libamounts_bpy_a_SOURCES = amount.cc balance.cc value.cc
libamounts_bpy_a_CXXFLAGS = -DUSE_BOOST_PYTHON=1
noinst_LIBRARIES += libledger_bpy.a
libledger_bpy_a_SOURCES = autoxact.cc binary.cc config.cc datetime.cc \
format.cc journal.cc option.cc parser.cc qif.cc quotes.cc \
textual.cc valexpr.cc walk.cc
noinst_LIBRARIES = libledger_bpy.a
libledger_bpy_a_SOURCES = amount.cc balance.cc value.cc autoxact.cc \
binary.cc config.cc datetime.cc format.cc journal.cc option.cc \
parser.cc qif.cc quotes.cc textual.cc valexpr.cc walk.cc python.cc
libledger_bpy_a_CXXFLAGS = -DUSE_BOOST_PYTHON=1
if READ_GNUCASH
libledger_bpy_a_SOURCES += gnucash.cc
@ -68,17 +65,12 @@ libledger_bpy_a_CXXFLAGS += -DDEBUG_LEVEL=4
libledger_bpy_a_SOURCES += debug.cc
endif
bin_PROGRAMS += amounts.so ledger.so
bin_PROGRAMS += ledger.so
amounts.so: pyamounts.cc libamounts_bpy.a
CFLAGS="$(CPPFLAGS) -L." python setup_amounts.py build --build-lib=.
ledger.so: pyledger.cc libamounts_bpy.a libledger_bpy.a
CFLAGS="$(CPPFLAGS) -L." python setup_ledger.py build --build-lib=.
ledger.so: pyledger.cc libledger_bpy.a
CFLAGS="$(CPPFLAGS) -L." python setup.py build --build-lib=.
install-exec-hook:
CFLAGS="$(CPPFLAGS) -L." python setup_amounts.py install --prefix=$(DESTDIR)
CFLAGS="$(CPPFLAGS) -L." python setup_ledger.py install --prefix=$(DESTDIR)
CFLAGS="$(CPPFLAGS) -L." python setup.py install --prefix=$(DESTDIR)
endif
endif

View file

@ -328,8 +328,8 @@ Output customization:\n\
-Y, --yearly show yearly sub-totals\n\
-l, --limit EXPR calculate only transactions matching EXPR\n\
-d, --display EXPR display only transactions matching EXPR\n\
-t, --value EXPR set the value expression for all report types\n\
-T, --total EXPR set the total expression for all report types\n\
-t, --value-expr EXPR set the value expression for all report types\n\
-T, --total-expr EXPR set the total expression for all report types\n\
-j, --value-data print only raw value data (useful when scripting)\n\
-J, --total-data print only raw total data\n\n\
Commodity reporting:\n\
@ -531,13 +531,13 @@ OPT_BEGIN(display, "d:") {
config.display_predicate += ")";
} OPT_END(display);
OPT_BEGIN(value, "t:") {
OPT_BEGIN(value_expr, "t:") {
config.value_expr = optarg;
} OPT_END(value);
} OPT_END(value_expr);
OPT_BEGIN(total, "T:") {
OPT_BEGIN(total_expr, "T:") {
config.total_expr = optarg;
} OPT_END(total);
} OPT_END(total_expr);
OPT_BEGIN(value_data, "j") {
config.value_expr = "S" + config.value_expr;

View file

@ -28,7 +28,7 @@ class FormatTransaction (TransactionHandler):
def __call__ (self, xact):
print self.formatter.format(xact)
handler = FormatTransaction("%D %-20P %N")
handler = FormatTransaction("%D %-20P %N %('foo'100)")
handler = FilterTransactions (handler, "/Checking/")
expr = parse_value_expr ("a*2")

78
python.cc Normal file
View file

@ -0,0 +1,78 @@
#include "python.h"
#include "ledger.h"
#include "acconf.h"
#include <boost/python.hpp>
using namespace boost::python;
void export_amount();
void export_balance();
void export_value();
void export_journal();
void export_parser();
void export_textual();
void export_binary();
void export_qif();
#ifdef READ_GNUCASH
void export_gnucash();
#endif
void export_option();
void export_walk();
void export_format();
void export_valexpr();
void export_datetime();
namespace ledger {
python_support * python_interpretor = NULL;
static struct cleanup_python {
~cleanup_python() {
if (python_interpretor) {
Py_Finalize();
delete python_interpretor;
}
}
} _cleanup;
void init_module()
{
export_amount();
export_balance();
export_value();
export_journal();
export_parser();
export_textual();
export_binary();
export_qif();
#ifdef READ_GNUCASH
export_gnucash();
#endif
export_option();
export_walk();
export_format();
export_valexpr();
export_datetime();
}
void init_python()
{
assert(! python_interpretor);
Py_Initialize();
python_interpretor = new python_support;
#if 1
boost::python::detail::init_module("ledger", &init_module);
#else
object m_obj(python_interpretor->main_module);
scope current_module(m_obj);
python_interpretor->main_namespace.attr("bar") = 10;
handle_exception(init_module_main);
#endif
}
} // namespace ledger

55
python.h Normal file
View file

@ -0,0 +1,55 @@
#ifndef _PYTHON_H
#define _PYTHON_H
#include <boost/python.hpp>
using namespace boost::python;
namespace ledger {
struct python_support
{
handle<> main_module;
dict main_namespace;
python_support()
: main_module(borrowed(PyImport_AddModule("__main__"))),
main_namespace(handle<>(borrowed(PyModule_GetDict(main_module.get()))))
{}
~python_support() {
}
};
extern python_support * python_interpretor;
void init_python();
inline void python_eval(std::istream& in)
{
if (! python_interpretor)
init_python();
std::string buffer;
buffer.reserve(4096);
while (! in.eof()) {
char buf[256];
in.getline(buf, 255);
if (buf[0] == '!')
break;
buffer += buf;
buffer += "\n";
}
try {
handle<>(borrowed(PyRun_String(buffer.c_str(), Py_file_input,
python_interpretor->main_namespace.ptr(),
python_interpretor->main_namespace.ptr())));
}
catch(const boost::python::error_already_set&) {
PyErr_Print();
}
}
} // namespace ledger
#endif // _PYTHON_H

View file

@ -1,3 +1,14 @@
;; If Python support is enabled, then the following code may be
;; uncomment to demonstrate a rather useless example.
; !python
; from ledger import *
; def foo(d, val):
; return val + d.xact.amount
; !end
;
; --value-expr 'foo'{$100}
2004/05/01 Checking balance
Assets:Checking $500.00
Equity:Opening Balances
@ -9,4 +20,3 @@
2004/05/29 Restaurant
Expenses:Food $50.00
Liabilities:MasterCard

26
setup.py Executable file
View file

@ -0,0 +1,26 @@
#!/usr/bin/env python
from distutils.core import setup, Extension
setup(name = "Amounts",
version = "2.0b",
description = "Amounts Library",
author = "John Wiegley",
author_email = "johnw@newartisans.com",
url = "http://www.newartisans.com/johnw/",
ext_modules = [
Extension("amounts", ["pyamounts.cc"],
define_macros = [('PYTHON_MODULE', None)],
libraries = ["ledger_bpy", "boost_python", "gmp"])])
setup(name = "Ledger",
version = "2.0b",
description = "Ledger Accounting Tool",
author = "John Wiegley",
author_email = "johnw@newartisans.com",
url = "http://www.newartisans.com/johnw/",
ext_modules = [
Extension("ledger", ["pyledger.cc"],
define_macros = [('PYTHON_MODULE', None)],
libraries = ["ledger_bpy", "boost_python", "gmp",
"pcre", "xmlparse", "xmltok"])])

View file

@ -1,14 +0,0 @@
#!/usr/bin/env python
from distutils.core import setup, Extension
setup(name = "Amounts",
version = "2.0b",
description = "Amounts Library",
author = "John Wiegley",
author_email = "johnw@newartisans.com",
url = "http://www.newartisans.com/johnw/",
ext_modules = [
Extension("amounts", ["pyamounts.cc"],
define_macros = [('PYTHON_MODULE', None)],
libraries = ["amounts_bpy", "boost_python", "gmp"])])

View file

@ -1,16 +0,0 @@
#!/usr/bin/env python
from distutils.core import setup, Extension
setup(name = "Ledger",
version = "2.0b",
description = "Ledger Accounting Tool",
author = "John Wiegley",
author_email = "johnw@newartisans.com",
url = "http://www.newartisans.com/johnw/",
ext_modules = [
Extension("ledger", ["pyledger.cc"],
define_macros = [('PYTHON_MODULE', None)],
libraries = ["amounts_bpy", "gmp",
"ledger_bpy", "boost_python",
"pcre", "xmlparse", "xmltok"])])

View file

@ -7,6 +7,9 @@
#include "option.h"
#include "timing.h"
#include "util.h"
#ifdef USE_BOOST_PYTHON
#include "python.h"
#endif
#include <fstream>
#include <sstream>
@ -534,9 +537,11 @@ unsigned int textual_parser_t::parse(std::istream& in,
break;
}
case '!': // directive
in >> line;
if (std::string(line) == "!include") {
case '!': { // directive
std::string word;
in.get(c);
in >> word;
if (word == "include") {
in.getline(line, MAX_LINE);
linenum++;
@ -545,7 +550,14 @@ unsigned int textual_parser_t::parse(std::istream& in,
count += parse_journal_file(skip_ws(line), journal,
account_stack.front());
}
#ifdef USE_BOOST_PYTHON
else if (word == "python") {
in.getline(line, MAX_LINE);
python_eval(in);
}
#endif
break;
}
default: {
unsigned int first_line = linenum;

View file

@ -5,6 +5,9 @@
#include "datetime.h"
#include "debug.h"
#include "util.h"
#ifdef USE_BOOST_PYTHON
#include "python.h"
#endif
#include <pcre.h>
@ -277,6 +280,27 @@ void value_expr_t::compute(value_t& result, const details_t& details) const
break;
}
case F_INTERP_FUNC: {
#ifdef USE_BOOST_PYTHON
if (! python_interpretor)
init_python();
try {
object func = python_interpretor->main_namespace[constant_s];
if (right) {
right->compute(result, details);
result = call<value_t>(func.ptr(), details, result);
} else {
result = call<value_t>(func.ptr(), details);
}
}
catch(const boost::python::error_already_set&) {
PyErr_Print();
}
#endif
break;
}
case O_NOT:
left->compute(result, details);
result.negate();
@ -489,13 +513,25 @@ value_expr_t * parse_value_term(std::istream& in)
in.get(c);
node.reset(new value_expr_t(short_account_mask ?
value_expr_t::F_SHORT_ACCOUNT_MASK :
(payee_mask ? value_expr_t::F_PAYEE_MASK :
value_expr_t::F_ACCOUNT_MASK)));
value_expr_t::F_SHORT_ACCOUNT_MASK :
(payee_mask ? value_expr_t::F_PAYEE_MASK :
value_expr_t::F_ACCOUNT_MASK)));
node->mask = new mask_t(buf);
break;
}
case '\'': {
READ_INTO(in, buf, 255, c, c != '\'');
if (c != '\'')
throw value_expr_error("Missing closing '\''");
in.get(c);
node.reset(new value_expr_t(value_expr_t::F_INTERP_FUNC));
node->constant_s = buf;
node->right = parse_value_expr(in);
break;
}
case '(':
node.reset(parse_value_expr(in));
if (peek_next_nonws(in) == ')')
@ -781,6 +817,10 @@ void dump_value_expr(std::ostream& out, const value_expr_t * node)
out << ")";
break;
case value_expr_t::F_INTERP_FUNC:
out << "F_INTERP(" << node->constant_s << ")";
break;
case value_expr_t::O_NOT:
out << "!";
dump_value_expr(out, node->left);
@ -874,6 +914,20 @@ value_expr_t * py_parse_value_expr(const std::string& str) {
void export_valexpr()
{
class_< details_t > ("Details", init<const entry_t&>())
.def(init<const transaction_t&>())
.def(init<const account_t&>())
.add_property("entry",
make_getter(&details_t::entry,
return_value_policy<reference_existing_object>()))
.add_property("xact",
make_getter(&details_t::xact,
return_value_policy<reference_existing_object>()))
.add_property("account",
make_getter(&details_t::account,
return_value_policy<reference_existing_object>()))
;
class_< value_expr_t > ("ValueExpr", init<value_expr_t::kind_t>())
.def("compute", py_compute<account_t>)
.def("compute", py_compute<entry_t>)

View file

@ -72,6 +72,7 @@ struct value_expr_t
F_PAYEE_MASK,
F_ACCOUNT_MASK,
F_SHORT_ACCOUNT_MASK,
F_INTERP_FUNC,
// Binary operators
O_ADD,
@ -100,7 +101,8 @@ struct value_expr_t
std::time_t constant_t;
unsigned int constant_i;
};
amount_t constant_a;
std::string constant_s;
amount_t constant_a;
mask_t * mask;
value_expr_t(const kind_t _kind)

View file

@ -618,6 +618,30 @@ void export_value()
.def(init<unsigned int>())
.def(init<bool>())
.def(self + self)
.def(self + other<balance_pair_t>())
.def(self + other<balance_t>())
.def(self + other<amount_t>())
.def(self + int())
.def(self - self)
.def(self - other<balance_pair_t>())
.def(self - other<balance_t>())
.def(self - other<amount_t>())
.def(self - int())
.def(self * self)
.def(self * other<balance_pair_t>())
.def(self * other<balance_t>())
.def(self * other<amount_t>())
.def(self * int())
.def(self / self)
.def(self / other<balance_pair_t>())
.def(self / other<balance_t>())
.def(self / other<amount_t>())
.def(self / int())
.def(self += self)
.def(self += other<balance_pair_t>())
.def(self += other<balance_t>())