python integrated both ways (see sample.dat), and initialized on-demand
This commit is contained in:
parent
6c66d1e0ef
commit
a013b520ba
13 changed files with 296 additions and 73 deletions
48
Makefile.am
48
Makefile.am
|
|
@ -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
|
||||
|
|
|
|||
12
config.cc
12
config.cc
|
|
@ -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;
|
||||
|
|
|
|||
2
main.py
2
main.py
|
|
@ -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
78
python.cc
Normal 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
55
python.h
Normal 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
|
||||
12
sample.dat
12
sample.dat
|
|
@ -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
26
setup.py
Executable 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"])])
|
||||
|
|
@ -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"])])
|
||||
|
|
@ -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"])])
|
||||
18
textual.cc
18
textual.cc
|
|
@ -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;
|
||||
|
|
|
|||
60
valexpr.cc
60
valexpr.cc
|
|
@ -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>)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
24
value.cc
24
value.cc
|
|
@ -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>())
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue