Removed Python integration support.

This commit is contained in:
John Wiegley 2006-02-15 20:10:49 +00:00
parent 2eafddc91b
commit ce3491c99f
38 changed files with 37 additions and 3648 deletions

View file

@ -33,10 +33,6 @@ if HAVE_LIBOFX
libledger_la_CXXFLAGS += -DHAVE_LIBOFX=1
libledger_la_SOURCES += ofx.cc
endif
if HAVE_BOOST_PYTHON
libledger_la_CXXFLAGS += -DUSE_BOOST_PYTHON=1
libledger_la_SOURCES += py_eval.cc
endif
if DEBUG
libledger_la_CXXFLAGS += -DDEBUG_LEVEL=4
libledger_la_SOURCES += debug.cc
@ -61,8 +57,6 @@ pkginclude_HEADERS = \
mask.h \
option.h \
parser.h \
py_eval.h \
pyledger.h \
qif.h \
quotes.h \
reconcile.h \
@ -79,12 +73,7 @@ pkginclude_HEADERS = \
bin_PROGRAMS = ledger
ledger_CXXFLAGS =
ledger_SOURCES = main.cc
if HAVE_BOOST_PYTHON
ledger_CXXFLAGS += -DUSE_BOOST_PYTHON=1
ledger_LDADD = $(LIBOBJS) libledger.la -lboost_python -lpython$(PYTHON_VERSION)
else
ledger_LDADD = $(LIBOBJS) libledger.la
endif
if HAVE_EXPAT
ledger_CXXFLAGS += -DHAVE_EXPAT=1
ledger_LDADD += -lexpat
@ -106,48 +95,11 @@ info_TEXINFOS = ledger.texi
######################################################################
if HAVE_BOOST_PYTHON
noinst_PROGRAMS = ledger.so
if HAVE_EXPAT
HAVE_EXPAT_VALUE = true
else
HAVE_EXPAT_VALUE = false
endif
if HAVE_XMLPARSE
HAVE_XMLPARSE_VALUE = true
else
HAVE_XMLPARSE_VALUE = false
endif
if HAVE_LIBOFX
HAVE_LIBOFX_VALUE = true
else
HAVE_LIBOFX_VALUE = false
endif
ledger.so: py_eval.cc libledger.la
CFLAGS="$(CPPFLAGS)" LDFLAGS="$(LDFLAGS) -L. -L.libs" \
HAVE_EXPAT="$(HAVE_EXPAT_VALUE)" \
HAVE_XMLPARSE="$(HAVE_XMLPARSE_VALUE)" \
HAVE_LIBOFX="$(HAVE_LIBOFX_VALUE)" \
python setup.py build --build-lib=.
install-exec-hook:
CFLAGS="$(CPPFLAGS)" LDFLAGS="$(LDFLAGS) -L. -L.libs" \
HAVE_EXPAT="$(HAVE_EXPAT_VALUE)" \
HAVE_XMLPARSE="$(HAVE_XMLPARSE_VALUE)" \
HAVE_LIBOFX="$(HAVE_LIBOFX_VALUE)" \
python setup.py install --prefix=$(prefix)
endif
all-clean: maintainer-clean
rm -fr *~ .*~ .\#* *.html *.info *.pdf *.a *.so *.o *.lo *.la \
*.elc *.aux *.cp *.fn *.ky *.log *.pg *.toc *.tp *.vr *.pyc \
*.elc *.aux *.cp *.fn *.ky *.log *.pg *.toc *.tp *.vr \
.gdb_history gmon.out h out TAGS ledger valexpr .deps \
.libs build AUTHORS COPYING INSTALL Makefile acconf.h \
acconf.h.in aclocal.m4 autom4te config.guess config.sub \
configure depcomp install-sh libtool ltconfig ltmain.sh \
missing stamp texinfo.tex Makefile.in mkinstalldirs \
py-compile
missing stamp texinfo.tex Makefile.in mkinstalldirs

4
NEWS
View file

@ -84,6 +84,10 @@
- Added a new value expression regexp command:
C// compare against transaction amount's commodity symbol
- Added a new "csv" command, for outputting results in CSV format.
- Completely removed Python integration.
* 2.4
- Both "-$100.00" and "$-100.00" are now equivalent amounts.

12
README
View file

@ -71,15 +71,3 @@ join the Ledger mailing list at the following Web address:
You can also find help at the #ledger channel on the IRC server
irc.freenode.net.
Building Ledger as a Python Module
==================================
If you have Python 2.2 or higher installed, and Boost.Python, then
Ledger can also be built as a Python module if --enable-python is
passed to the configure script. This means you can interact with your
Ledger data from Python, making it easier to write custom reports.
This feature is mostly undocumented in version 2.0, although main.py
gives a working example.

10
acprep
View file

@ -12,17 +12,14 @@ else
fi
autoconf
INCDIRS="-I/sw/include -I/usr/local/include/boost-1_33 -I/usr/include/httpd/xml -I/usr/include/python2.3"
INCDIRS="-I/sw/include -I/usr/local/include/boost-1_33 -I/usr/include/httpd/xml"
#INCDIRS="$INCDIRS -I/sw/include/libofx"
INCDIRS="$INCDIRS -Wno-long-double"
LIBDIRS="-L/sw/lib -L/usr/local/lib -L/usr/lib/python2.3/config"
LIBDIRS="-L/sw/lib -L/usr/local/lib"
if [ "$1" = "--debug" ]; then
./configure CPPFLAGS="$INCDIRS" LDFLAGS="$LIBDIRS" CXXFLAGS="-g" \
--enable-debug
elif [ "$1" = "--debug-python" ]; then
./configure CPPFLAGS="$INCDIRS" LDFLAGS="$LIBDIRS" CXXFLAGS="-g" \
--enable-debug --enable-python
elif [ "$1" = "--opt" ]; then
./configure CPPFLAGS="$INCDIRS" LDFLAGS="$LIBDIRS" \
CXXFLAGS="-fomit-frame-pointer -O3 -mcpu=7450 -fPIC"
@ -31,8 +28,7 @@ elif [ "$1" = "--flat-opt" ]; then
CXXFLAGS="-fomit-frame-pointer -O3 -mcpu=7450"
elif [ "$1" = "--safe-opt" ]; then
./configure CPPFLAGS="$INCDIRS" LDFLAGS="$LIBDIRS" \
CXXFLAGS="-fomit-frame-pointer -O3 -mcpu=7450 -fPIC -DDEBUG_LEVEL=1" \
--enable-python
CXXFLAGS="-fomit-frame-pointer -O3 -mcpu=7450 -fPIC -DDEBUG_LEVEL=1"
elif [ "$1" = "--perf" ]; then
./configure CPPFLAGS="$INCDIRS" LDFLAGS="$LIBDIRS" CXXFLAGS="-g -pg"
fi

165
amount.cc
View file

@ -1245,168 +1245,3 @@ amount_t commodity_t::value(const std::time_t moment)
}
} // namespace ledger
#ifdef USE_BOOST_PYTHON
#include <boost/python.hpp>
#include <Python.h>
using namespace boost::python;
using namespace ledger;
void py_parse_1(amount_t& amount, const std::string& str,
unsigned short flags) {
amount.parse(str, flags);
}
void py_parse_2(amount_t& amount, const std::string& str) {
amount.parse(str);
}
struct commodity_updater_wrap : public commodity_t::updater_t
{
PyObject * self;
commodity_updater_wrap(PyObject * self_) : self(self_) {}
virtual void operator()(commodity_t& commodity,
const std::time_t moment,
const std::time_t date,
const std::time_t last,
amount_t& price) {
call_method<void>(self, "__call__", commodity, moment, date, last, price);
}
};
commodity_t * py_find_commodity_1(const std::string& symbol)
{
return commodity_t::find_commodity(symbol);
}
commodity_t * py_find_commodity_2(const std::string& symbol, bool auto_create)
{
return commodity_t::find_commodity(symbol, auto_create);
}
#define EXC_TRANSLATOR(type) \
void exc_translate_ ## type(const type& err) { \
PyErr_SetString(PyExc_RuntimeError, err.what()); \
}
EXC_TRANSLATOR(amount_error)
void export_amount()
{
scope().attr("AMOUNT_PARSE_NO_MIGRATE") = AMOUNT_PARSE_NO_MIGRATE;
scope().attr("AMOUNT_PARSE_NO_REDUCE") = AMOUNT_PARSE_NO_REDUCE;
class_< amount_t > ("Amount")
.def(init<amount_t>())
.def(init<std::string>())
.def(init<char *>())
.def(init<bool>())
.def(init<long>())
.def(init<unsigned long>())
.def(init<double>())
.def("commodity", &amount_t::commodity,
return_value_policy<reference_existing_object>())
.def("set_commodity", &amount_t::set_commodity)
.def("clear_commodity", &amount_t::clear_commodity)
.def("quantity_string", &amount_t::quantity_string)
.def(self += self)
.def(self += long())
.def(self + self)
.def(self + long())
.def(self -= self)
.def(self -= long())
.def(self - self)
.def(self - long())
.def(self *= self)
.def(self *= long())
.def(self * self)
.def(self * long())
.def(self /= self)
.def(self /= long())
.def(self / self)
.def(self / long())
.def(- self)
.def(self < self)
.def(self < long())
.def(self <= self)
.def(self <= long())
.def(self > self)
.def(self > long())
.def(self >= self)
.def(self >= long())
.def(self == self)
.def(self == long())
.def(self != self)
.def(self != long())
.def(! self)
.def(self_ns::int_(self))
.def(self_ns::float_(self))
.def(self_ns::str(self))
.def(abs(self))
.def("negate", &amount_t::negate)
.def("parse", py_parse_1)
.def("parse", py_parse_2)
.def("reduce", &amount_t::reduce)
.def("valid", &amount_t::valid)
;
class_< commodity_t::updater_t, commodity_updater_wrap, boost::noncopyable >
("Updater")
;
scope().attr("COMMODITY_STYLE_DEFAULTS") = COMMODITY_STYLE_DEFAULTS;
scope().attr("COMMODITY_STYLE_SUFFIXED") = COMMODITY_STYLE_SUFFIXED;
scope().attr("COMMODITY_STYLE_SEPARATED") = COMMODITY_STYLE_SEPARATED;
scope().attr("COMMODITY_STYLE_EUROPEAN") = COMMODITY_STYLE_EUROPEAN;
scope().attr("COMMODITY_STYLE_THOUSANDS") = COMMODITY_STYLE_THOUSANDS;
scope().attr("COMMODITY_STYLE_NOMARKET") = COMMODITY_STYLE_NOMARKET;
scope().attr("COMMODITY_STYLE_VARIABLE") = COMMODITY_STYLE_VARIABLE;
class_< commodity_t > ("Commodity")
.def(init<std::string, optional<unsigned int, unsigned int> >())
.def_readonly("symbol", &commodity_t::symbol)
.def("set_symbol", &commodity_t::set_symbol)
.def_readwrite("name", &commodity_t::name)
.def_readwrite("note", &commodity_t::name)
.def_readwrite("precision", &commodity_t::precision)
.def_readwrite("flags", &commodity_t::flags)
.def_readwrite("ident", &commodity_t::ident)
.def_readwrite("updater", &commodity_t::updater)
.add_property("smaller",
make_getter(&commodity_t::smaller,
return_value_policy<reference_existing_object>()))
.add_property("larger",
make_getter(&commodity_t::larger,
return_value_policy<reference_existing_object>()))
.def(self_ns::str(self))
.def("add_price", &commodity_t::add_price)
.def("remove_price", &commodity_t::remove_price)
.def("value", &commodity_t::value)
.def("valid", &commodity_t::valid)
;
def("add_commodity", &commodity_t::add_commodity);
def("remove_commodity", &commodity_t::remove_commodity);
def("find_commodity", py_find_commodity_1,
return_value_policy<reference_existing_object>());
def("find_commodity", py_find_commodity_2,
return_value_policy<reference_existing_object>());
#define EXC_TRANSLATE(type) \
register_exception_translator<type>(&exc_translate_ ## type);
EXC_TRANSLATE(amount_error);
}
#endif // USE_BOOST_PYTHON

View file

@ -86,194 +86,3 @@ void balance_t::write(std::ostream& out,
}
} // namespace ledger
#ifdef USE_BOOST_PYTHON
#include <boost/python.hpp>
using namespace boost::python;
using namespace ledger;
unsigned int balance_len(balance_t& bal)
{
return bal.amounts.size();
}
amount_t balance_getitem(balance_t& bal, int i)
{
std::size_t len = bal.amounts.size();
if (abs(i) >= len) {
PyErr_SetString(PyExc_IndexError, "Index out of range");
throw_error_already_set();
}
int x = i < 0 ? len + i : i;
amounts_map::iterator elem = bal.amounts.begin();
while (--x >= 0)
elem++;
return (*elem).second;
}
unsigned int balance_pair_len(balance_pair_t& bal_pair)
{
return balance_len(bal_pair.quantity);
}
amount_t balance_pair_getitem(balance_pair_t& bal_pair, int i)
{
return balance_getitem(bal_pair.quantity, i);
}
void export_balance()
{
class_< balance_t > ("Balance")
.def(init<balance_t>())
.def(init<amount_t>())
.def(init<long>())
.def(init<unsigned long>())
.def(init<double>())
.def(self += self)
.def(self += other<amount_t>())
.def(self += long())
.def(self + self)
.def(self + other<amount_t>())
.def(self + long())
.def(self -= self)
.def(self -= other<amount_t>())
.def(self -= long())
.def(self - self)
.def(self - other<amount_t>())
.def(self - long())
.def(self *= self)
.def(self *= other<amount_t>())
.def(self *= long())
.def(self * self)
.def(self * other<amount_t>())
.def(self * long())
.def(self /= self)
.def(self /= other<amount_t>())
.def(self /= long())
.def(self / self)
.def(self / other<amount_t>())
.def(self / long())
.def(- self)
.def(self < self)
.def(self < other<amount_t>())
.def(self < long())
.def(self <= self)
.def(self <= other<amount_t>())
.def(self <= long())
.def(self > self)
.def(self > other<amount_t>())
.def(self > long())
.def(self >= self)
.def(self >= other<amount_t>())
.def(self >= long())
.def(self == self)
.def(self == other<amount_t>())
.def(self == long())
.def(self != self)
.def(self != other<amount_t>())
.def(self != long())
.def(! self)
.def(abs(self))
.def(self_ns::str(self))
.def("__len__", balance_len)
.def("__getitem__", balance_getitem)
.def("negate", &balance_t::negate)
.def("amount", &balance_t::amount)
.def("value", &balance_t::value)
.def("write", &balance_t::write)
.def("valid", &balance_t::valid)
;
class_< balance_pair_t > ("BalancePair")
.def(init<balance_pair_t>())
.def(init<balance_t>())
.def(init<amount_t>())
.def(init<long>())
.def(init<unsigned long>())
.def(init<double>())
.def(self += self)
.def(self += other<balance_t>())
.def(self += other<amount_t>())
.def(self += long())
.def(self + self)
.def(self + other<balance_t>())
.def(self + other<amount_t>())
.def(self + long())
.def(self -= self)
.def(self -= other<balance_t>())
.def(self -= other<amount_t>())
.def(self -= long())
.def(self - self)
.def(self - other<balance_t>())
.def(self - other<amount_t>())
.def(self - long())
.def(self *= self)
.def(self *= other<balance_t>())
.def(self *= other<amount_t>())
.def(self *= long())
.def(self * self)
.def(self * other<balance_t>())
.def(self * other<amount_t>())
.def(self * long())
.def(self /= self)
.def(self /= other<balance_t>())
.def(self /= other<amount_t>())
.def(self /= long())
.def(self / self)
.def(self / other<balance_t>())
.def(self / other<amount_t>())
.def(self / long())
.def(- self)
.def(self < self)
.def(self < other<balance_t>())
.def(self < other<amount_t>())
.def(self < long())
.def(self <= self)
.def(self <= other<balance_t>())
.def(self <= other<amount_t>())
.def(self <= long())
.def(self > self)
.def(self > other<balance_t>())
.def(self > other<amount_t>())
.def(self > long())
.def(self >= self)
.def(self >= other<balance_t>())
.def(self >= other<amount_t>())
.def(self >= long())
.def(self == self)
.def(self == other<balance_t>())
.def(self == other<amount_t>())
.def(self == long())
.def(self != self)
.def(self != other<balance_t>())
.def(self != other<amount_t>())
.def(self != long())
.def(! self)
.def(abs(self))
.def(self_ns::str(self))
.def("__len__", balance_pair_len)
.def("__getitem__", balance_pair_getitem)
.def("negate", &balance_pair_t::negate)
.def("amount", &balance_pair_t::amount)
.def("value", &balance_pair_t::value)
.def("write", &balance_pair_t::write)
.def("valid", &balance_pair_t::valid)
;
}
#endif // USE_BOOST_PYTHON

View file

@ -795,30 +795,3 @@ void write_binary_journal(std::ostream& out, journal_t * journal)
}
} // namespace ledger
#ifdef USE_BOOST_PYTHON
#include <boost/python.hpp>
using namespace boost::python;
using namespace ledger;
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(binary_parse_overloads,
binary_parser_t::parse, 2, 4)
void py_write_binary_journal(const std::string& path, journal_t * journal)
{
std::ofstream out(path.c_str());
write_binary_journal(out, journal);
}
void export_binary() {
class_< binary_parser_t, bases<parser_t> > ("BinaryParser")
.def("test", &binary_parser_t::test)
.def("parse", &binary_parser_t::parse, binary_parse_overloads())
;
def("write_binary_journal", py_write_binary_journal);
}
#endif // USE_BOOST_PYTHON

150
config.cc
View file

@ -5,9 +5,6 @@
#include "quotes.h"
#include "valexpr.h"
#include "walk.h"
#ifdef USE_BOOST_PYTHON
#include "py_eval.h"
#endif
#include <fstream>
#include <ctime>
@ -43,12 +40,7 @@ void config_t::reset()
write_hdr_format = "%d %Y%C%P\n";
write_xact_format = " %-34W %12o%n\n";
equity_format = "\n%D %Y%C%P\n%/ %-34W %12t\n";
#ifndef USE_BOOST_PYTHON
prices_format = "%[%Y/%m/%d %H:%M:%S %Z] %-10A %12t %12T\n";
#else
prices_format = ("%[%Y/%m/%d %H:%M:%S %Z] %-8A "
"%10t %10(@vmin(t)) %10(@vmax(t)) %12T\n");
#endif
pricesdb_format = "P %[%Y/%m/%d %H:%M:%S] %A %t\n";
predicate = "";
@ -444,9 +436,6 @@ static void show_version(std::ostream& out)
This program is made available under the terms of the BSD Public License.\n\
See LICENSE file included with the distribution for details and disclaimer.\n";
out << "\n(modules: gmp, pcre";
#ifdef USE_BOOST_PYTHON
out << ", python";
#endif
#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
out << ", xml";
#endif
@ -523,12 +512,6 @@ Commodity reporting:\n\
-V, --market report last known market value\n\
-g, --performance report gain/loss for each displayed transaction\n\
-G, --gain report net gain/loss\n\n";
#ifdef USE_BOOST_PYTHON
out
<< "Python support:\n\
--import MODULE on startup, import the given Python MODULE\n\
--import-stdin on start, read Python code from standard input\n\n";
#endif
out
<< "Commands:\n\
balance [REGEXP]... show balance totals for matching accounts\n\
@ -547,9 +530,6 @@ Use -H to see all the help text on one page, or:\n\
--help-calc calculation options\n\
--help-disp display options\n\
--help-comm commodity options\n";
#ifdef USE_BOOST_PYTHON
out << " --help-python Python options\n";
#endif
out << "\nBasic options:\n\
-h, --help display this help text\n\
-v, --version show version information\n\
@ -635,15 +615,6 @@ void option_comm_help(std::ostream& out)
-G, --gain report net gain/loss\n";
}
#ifdef USE_BOOST_PYTHON
void option_python_help(std::ostream& out)
{
out << "Python support options:\n\
--import MODULE on startup, import the given Python MODULE\n\
--import-stdin on start, read Python code from standard input\n\n";
}
#endif
//////////////////////////////////////////////////////////////////////
//
// Basic options
@ -673,13 +644,6 @@ OPT_BEGIN(help_comm, "") {
throw 0;
} OPT_END(help_comm);
#ifdef USE_BOOST_PYTHON
OPT_BEGIN(help_python, "") {
option_python_help(std::cout);
throw 0;
} OPT_END(help_python);
#endif
OPT_BEGIN(version, "v") {
show_version(std::cout);
throw 0;
@ -1076,118 +1040,4 @@ OPT_BEGIN(percentage, "%") {
config->total_expr_template = "^#&{100.0%}*(#/^#)";
} OPT_END(percentage);
#ifdef USE_BOOST_PYTHON
//////////////////////////////////////////////////////////////////////
//
// Python support
OPT_BEGIN(import, ":") {
python_eval(std::string("import ") + optarg, PY_EVAL_STMT);
} OPT_END(import);
OPT_BEGIN(import_stdin, "") {
python_eval(std::cin, PY_EVAL_MULTI);
} OPT_END(import_stdin);
#endif // USE_BOOST_PYTHON
} // namespace ledger
#ifdef USE_BOOST_PYTHON
#include <boost/python.hpp>
#include <boost/python/detail/api_placeholder.hpp>
using namespace boost::python;
using namespace ledger;
void py_process_options(config_t& config, const std::string& command,
list args)
{
strings_list strs;
int l = len(args);
for (int i = 0; i < l; i++)
strs.push_back(std::string(extract<char *>(args[i])));
config.process_options(command, strs.begin(), strs.end());
}
void add_other_option_handlers(const std::list<option_t>& other);
void py_add_config_option_handlers()
{
add_other_option_handlers(config_options);
}
void py_option_help()
{
option_help(std::cout);
}
void export_config()
{
class_< config_t > ("Config")
.def_readwrite("init_file", &config_t::init_file)
.def_readwrite("data_file", &config_t::data_file)
.def_readwrite("cache_file", &config_t::cache_file)
.def_readwrite("price_db", &config_t::price_db)
.def_readwrite("output_file", &config_t::output_file)
.def_readwrite("account", &config_t::account)
.def_readwrite("predicate", &config_t::predicate)
.def_readwrite("display_predicate", &config_t::display_predicate)
.def_readwrite("report_period", &config_t::report_period)
.def_readwrite("report_period_sort", &config_t::report_period_sort)
.def_readwrite("format_string", &config_t::format_string)
.def_readwrite("balance_format", &config_t::balance_format)
.def_readwrite("register_format", &config_t::register_format)
.def_readwrite("wide_register_format", &config_t::wide_register_format)
.def_readwrite("csv_register_format", &config_t::csv_register_format)
.def_readwrite("plot_amount_format", &config_t::plot_amount_format)
.def_readwrite("plot_total_format", &config_t::plot_total_format)
.def_readwrite("print_format", &config_t::print_format)
.def_readwrite("write_hdr_format", &config_t::write_hdr_format)
.def_readwrite("write_xact_format", &config_t::write_xact_format)
.def_readwrite("equity_format", &config_t::equity_format)
.def_readwrite("prices_format", &config_t::prices_format)
.def_readwrite("pricesdb_format", &config_t::pricesdb_format)
.def_readwrite("date_format", &config_t::date_format)
.def_readwrite("sort_string", &config_t::sort_string)
.def_readwrite("amount_expr", &config_t::amount_expr)
.def_readwrite("total_expr", &config_t::total_expr)
.def_readwrite("total_expr_template", &config_t::total_expr_template)
.def_readwrite("forecast_limit", &config_t::forecast_limit)
.def_readwrite("reconcile_balance", &config_t::reconcile_balance)
.def_readwrite("reconcile_date", &config_t::reconcile_date)
.def_readwrite("budget_flags", &config_t::budget_flags)
.def_readwrite("pricing_leeway", &config_t::pricing_leeway)
.def_readwrite("show_collapsed", &config_t::show_collapsed)
.def_readwrite("show_subtotal", &config_t::show_subtotal)
.def_readwrite("show_totals", &config_t::show_totals)
.def_readwrite("show_related", &config_t::show_related)
.def_readwrite("show_all_related", &config_t::show_all_related)
.def_readwrite("show_inverted", &config_t::show_inverted)
.def_readwrite("show_empty", &config_t::show_empty)
.def_readwrite("head_entries", &config_t::head_entries)
.def_readwrite("tail_entries", &config_t::tail_entries)
.def_readwrite("pager", &config_t::pager)
.def_readwrite("days_of_the_week", &config_t::days_of_the_week)
.def_readwrite("by_payee", &config_t::by_payee)
.def_readwrite("comm_as_payee", &config_t::comm_as_payee)
.def_readwrite("show_revalued", &config_t::show_revalued)
.def_readwrite("show_revalued_only", &config_t::show_revalued_only)
.def_readwrite("download_quotes", &config_t::download_quotes)
.def_readwrite("use_cache", &config_t::use_cache)
.def_readwrite("cache_dirty", &config_t::cache_dirty)
.def("process_options", py_process_options)
;
scope().attr("config") = ptr(config);
def("option_help", py_option_help);
def("add_config_option_handlers", py_add_config_option_handlers);
}
#endif // USE_BOOST_PYTHON

View file

@ -187,46 +187,6 @@ else
AM_CONDITIONAL(HAVE_LIBOFX, false)
fi
# check for Python
AC_ARG_ENABLE(python,
[ --enable-python Turn on Python support],
[case "${enableval}" in
yes) python=true ;;
no) python=false ;;
*) AC_MSG_ERROR(bad value ${enableval} for --enable-python) ;;
esac],[python=false])
AM_CONDITIONAL(USE_PYTHON, test x$python = xtrue)
if [test x$python = xtrue ]; then
AM_PATH_PYTHON(2.2,, :)
if [test "$PYTHON" != :]; then
AC_CACHE_CHECK(
[if boost_python is available],
[boost_python_cpplib_avail],
[boost_python_save_libs=$LIBS
LIBS="-lboost_python -lpython$PYTHON_VERSION $LIBS"
AC_LANG_PUSH(C++)
AC_TRY_LINK(
[#include <boost/python.hpp>
using namespace boost::python;
class foo {};
BOOST_PYTHON_MODULE(samp) {
class_< foo > ("foo") ;
}],
[return 0],
[boost_python_cpplib_avail=true],
[boost_python_cpplib_avail=false])
AC_LANG_POP
LIBS=$boost_python_save_libs])
AM_CONDITIONAL(HAVE_BOOST_PYTHON,
test x$boost_python_cpplib_avail = xtrue)
else
AM_CONDITIONAL(HAVE_BOOST_PYTHON, false)
fi
else
AM_CONDITIONAL(HAVE_BOOST_PYTHON, false)
fi
# Check for options
AC_ARG_ENABLE(debug,
[ --enable-debug Turn on debugging],

View file

@ -373,82 +373,3 @@ bool quick_parse_date(const char * date_str, std::time_t * result)
}
} // namespace ledger
#ifdef USE_BOOST_PYTHON
#include <boost/python.hpp>
using namespace boost::python;
using namespace ledger;
unsigned int interval_len(interval_t& interval)
{
int periods = 1;
std::time_t when = interval.first();
while (interval.end && when < interval.end) {
when = interval.increment(when);
if (when < interval.end)
periods++;
}
return periods;
}
std::time_t interval_getitem(interval_t& interval, int i)
{
static std::time_t last_index = 0;
static std::time_t last_moment = 0;
if (i == 0) {
last_index = 0;
last_moment = interval.first();
}
else {
last_moment = interval.increment(last_moment);
if (interval.end && last_moment >= interval.end) {
PyErr_SetString(PyExc_IndexError, "Index out of range");
throw_error_already_set();
}
}
return last_moment;
}
std::time_t py_parse_date(const char * date_str)
{
std::time_t temp;
if (parse_date(date_str, &temp))
return temp;
return 0;
}
std::time_t py_parse_date_yr(const char * date_str, const int year)
{
std::time_t temp;
if (parse_date(date_str, &temp, year))
return temp;
return 0;
}
void export_datetime()
{
class_< interval_t >
("Interval", init<optional<int, int, int, std::time_t, std::time_t> >())
.def(init<std::string>())
.def(! self)
.def_readwrite("years", &interval_t::years)
.def_readwrite("months", &interval_t::months)
.def_readwrite("seconds", &interval_t::seconds)
.def_readwrite("begin", &interval_t::begin)
.def_readwrite("end", &interval_t::end)
.def("__len__", interval_len)
.def("__getitem__", interval_getitem)
.def("increment", &interval_t::increment)
;
def("parse_date", py_parse_date);
def("parse_date", py_parse_date_yr);
}
#endif // USE_BOOST_PYTHON

View file

@ -132,30 +132,3 @@ entry_t * derive_new_entry(journal_t& journal,
}
} // namespace ledger
#ifdef USE_BOOST_PYTHON
#include <boost/python.hpp>
#include <boost/python/detail/api_placeholder.hpp>
using namespace boost::python;
using namespace ledger;
entry_t * py_derive_new_entry(journal_t& journal, list args)
{
strings_list strs;
int l = len(args);
for (int i = 0; i < l; i++)
strs.push_back(extract<std::string>(args[i]));
return derive_new_entry(journal, strs.begin(), strs.end());
}
void export_derive()
{
def("derive_new_entry", py_derive_new_entry,
return_value_policy<manage_new_object>());
}
#endif // USE_BOOST_PYTHON

View file

@ -77,26 +77,3 @@ void format_emacs_transactions::operator()(transaction_t& xact)
}
} // namespace ledger
#ifdef USE_BOOST_PYTHON
#include <boost/python.hpp>
using namespace boost::python;
using namespace ledger;
void export_emacs()
{
typedef
pystream_handler_wrap<format_emacs_transactions, transaction_t>
format_emacs_transactions_wrap;
class_< format_emacs_transactions_wrap, bases<item_handler<transaction_t> > >
("FormatEmacsTransactions",
init<PyObject *>()[with_custodian_and_ward<1, 2>()])
.def("flush", &format_emacs_transactions_wrap::flush)
.def("__call__", &format_emacs_transactions_wrap::operator())
;
}
#endif // USE_BOOST_PYTHON

117
format.cc
View file

@ -1,9 +1,6 @@
#include "format.h"
#include "error.h"
#include "util.h"
#ifdef USE_BOOST_PYTHON
#include "py_eval.h"
#endif
#include <cstdlib>
#include <ctime>
@ -175,18 +172,6 @@ element_t * format_t::parse_elements(const std::string& fmt)
break;
}
case '@': {
const char * s = ++p;
while (*p && *p != '(')
p++;
if (*p && *++p != ')')
throw format_error("Missing ')'");
current->type = element_t::INTERP_FUNC;
current->chars = std::string(s, (p - s) - 1);
break;
}
case '[': {
++p;
const char * b = p;
@ -585,20 +570,6 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
}
break;
case element_t::INTERP_FUNC:
#ifdef USE_BOOST_PYTHON
try {
object func = python_eval(elem->chars);
out << call<std::string>(func.ptr(), details);
}
catch(const boost::python::error_already_set&) {
PyErr_Print();
throw format_error(std::string("While calling Python function '") +
elem->chars + "'");
}
#endif
break;
default:
assert(0);
break;
@ -842,91 +813,3 @@ void format_equity::operator()(account_t& account)
}
} // namespace ledger
#ifdef USE_BOOST_PYTHON
#include <boost/python.hpp>
using namespace boost::python;
using namespace ledger;
std::string py_format_1(format_t& format, const details_t& item)
{
std::ostringstream out;
format.format(out, item);
return out.str();
}
template <typename T>
std::string py_format(format_t& format, const T& item)
{
std::ostringstream out;
format.format(out, details_t(item));
return out.str();
}
void export_format()
{
typedef
pystream_handler_wrap<format_transactions, transaction_t, std::string>
format_transactions_wrap;
class_< format_transactions_wrap, bases<item_handler<transaction_t> > >
("FormatTransactions",
init<PyObject *, std::string>()[with_custodian_and_ward<1, 2>()])
.def("flush", &format_transactions_wrap::flush)
.def("__call__", &format_transactions_wrap::operator())
;
typedef
pystream_handler_wrap<format_entries, transaction_t, std::string>
format_entries_wrap;
class_< format_entries_wrap, bases<item_handler<transaction_t> > >
("FormatEntries",
init<PyObject *, std::string>()[with_custodian_and_ward<1, 2>()])
.def("flush", &format_entries_wrap::flush)
.def("__call__", &format_entries_wrap::operator())
;
typedef
pystream_handler_wrap<format_account, account_t, std::string, std::string>
format_account_wrap;
class_< format_account_wrap, bases<item_handler<transaction_t> > >
("FormatAccount",
init<PyObject *, std::string, std::string>()
[with_custodian_and_ward<1, 2>()])
.def("flush", &format_account_wrap::flush)
.def("__call__", &format_account_wrap::operator())
;
typedef
pystream_handler_wrap<format_equity, account_t, std::string, std::string>
format_equity_wrap;
class_< format_equity_wrap, bases<item_handler<transaction_t> > >
("FormatEquity",
init<PyObject *, std::string, std::string>()
[with_custodian_and_ward<1, 2>()])
.def("flush", &format_equity_wrap::flush)
.def("__call__", &format_equity_wrap::operator())
;
class_< format_t > ("Format")
.def(init<std::string>())
.def("reset", &format_t::reset)
.def("format", py_format_1)
.def("format", py_format<account_t>)
.def("format", py_format<entry_t>)
.def("format", py_format<transaction_t>)
;
def("truncated", truncated);
#if 0
def("partial_account_name", partial_account_name);
#endif
def("display_account", display_account);
}
#endif // USE_BOOST_PYTHON

View file

@ -38,8 +38,7 @@ struct element_t
NOTE,
OPT_NOTE,
SPACER,
DEPTH_SPACER,
INTERP_FUNC
DEPTH_SPACER
};
bool align_left;
@ -189,40 +188,4 @@ class format_equity : public item_handler<account_t>
} // namespace ledger
#ifdef USE_BOOST_PYTHON
#include "pyfstream.h"
template <typename T, typename U, typename V = int, typename W = int>
struct pystream_handler_wrap : public ledger::item_handler<U>
{
PyFileObject * file;
pyofstream * output;
T handler;
pystream_handler_wrap(PyObject * file_)
: file((PyFileObject *)file_), output(new pyofstream(file)),
handler(*output) {}
pystream_handler_wrap(PyObject * file_, const V& arg)
: file((PyFileObject *)file_), output(new pyofstream(file)),
handler(*output, arg) {}
pystream_handler_wrap(PyObject * file_, const V& arg1, const W& arg2)
: file((PyFileObject *)file_), output(new pyofstream(file)),
handler(*output, arg1, arg2) {}
virtual ~pystream_handler_wrap() {
delete output;
}
virtual void flush() {
handler.flush();
}
virtual void operator()(U& item) {
handler.operator()(item);
}
};
#endif // USE_BOOST_PYTHON
#endif // _REPORT_H
#endif // _FORMAT_H

View file

@ -415,22 +415,3 @@ unsigned int gnucash_parser_t::parse(std::istream& in,
}
} // namespace ledger
#ifdef USE_BOOST_PYTHON
#include <boost/python.hpp>
using namespace boost::python;
using namespace ledger;
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(gnucash_parse_overloads,
gnucash_parser_t::parse, 2, 4)
void export_gnucash() {
class_< gnucash_parser_t, bases<parser_t> > ("GnucashParser")
.def("test", &gnucash_parser_t::test)
.def("parse", &gnucash_parser_t::parse, gnucash_parse_overloads())
;
}
#endif // USE_BOOST_PYTHON

View file

@ -3,9 +3,6 @@
#include "valexpr.h"
#include "mask.h"
#include "error.h"
#ifdef USE_BOOST_PYTHON
#include "py_eval.h"
#endif
#include "acconf.h"
#include <fstream>
@ -290,14 +287,6 @@ void auto_entry_t::extend_entry(entry_base_t& entry)
if (fullname == "$account")
account = (*i)->account;
#ifdef USE_BOOST_PYTHON
else if (fullname[0] == '@') {
value_expr_t * expr = parse_value_expr(fullname);
if (expr->kind == value_expr_t::F_INTERP_FUNC)
python_call(expr->constant_s, expr->right, details_t(**i),
*(*t)->account);
}
#endif
transaction_t * xact
= new transaction_t(account, amt, (*t)->flags | TRANSACTION_AUTO);
@ -525,358 +514,3 @@ bool journal_t::valid() const
}
} // namespace ledger
#ifdef USE_BOOST_PYTHON
#include <boost/python.hpp>
#include <boost/python/exception_translator.hpp>
using namespace boost::python;
using namespace ledger;
entry_t& transaction_entry(const transaction_t& xact)
{
return *xact.entry;
}
unsigned int transactions_len(entry_base_t& entry)
{
return entry.transactions.size();
}
transaction_t& transactions_getitem(entry_base_t& entry, int i)
{
static int last_index = 0;
static entry_base_t * last_entry = NULL;
static transactions_list::iterator elem;
std::size_t len = entry.transactions.size();
if (abs(i) >= len) {
PyErr_SetString(PyExc_IndexError, "Index out of range");
throw_error_already_set();
}
if (&entry == last_entry && i == last_index + 1) {
last_index = i;
return **++elem;
}
int x = i < 0 ? len + i : i;
elem = entry.transactions.begin();
while (--x >= 0)
elem++;
last_entry = &entry;
last_index = i;
return **elem;
}
unsigned int entries_len(journal_t& journal)
{
return journal.entries.size();
}
entry_t& entries_getitem(journal_t& journal, int i)
{
static int last_index = 0;
static journal_t * last_journal = NULL;
static entries_list::iterator elem;
std::size_t len = journal.entries.size();
if (abs(i) >= len) {
PyErr_SetString(PyExc_IndexError, "Index out of range");
throw_error_already_set();
}
if (&journal == last_journal && i == last_index + 1) {
last_index = i;
return **++elem;
}
int x = i < 0 ? len + i : i;
elem = journal.entries.begin();
while (--x >= 0)
elem++;
last_journal = &journal;
last_index = i;
return **elem;
}
unsigned int accounts_len(account_t& account)
{
return account.accounts.size();
}
account_t& accounts_getitem(account_t& account, int i)
{
static int last_index = 0;
static account_t * last_account = NULL;
static accounts_map::iterator elem;
std::size_t len = account.accounts.size();
if (abs(i) >= len) {
PyErr_SetString(PyExc_IndexError, "Index out of range");
throw_error_already_set();
}
if (&account == last_account && i == last_index + 1) {
last_index = i;
return *(*++elem).second;
}
int x = i < 0 ? len + i : i;
elem = account.accounts.begin();
while (--x >= 0)
elem++;
last_account = &account;
last_index = i;
return *(*elem).second;
}
PyObject * py_account_get_data(account_t& account)
{
return (PyObject *) account.data;
}
void py_account_set_data(account_t& account, PyObject * obj)
{
account.data = obj;
}
account_t * py_find_account_1(journal_t& journal, const std::string& name)
{
return journal.find_account(name);
}
account_t * py_find_account_2(journal_t& journal, const std::string& name,
const bool auto_create)
{
return journal.find_account(name, auto_create);
}
bool py_add_entry(journal_t& journal, entry_t * entry) {
return journal.add_entry(new entry_t(*entry));
}
void py_add_transaction(entry_base_t& entry, transaction_t * xact) {
return entry.add_transaction(new transaction_t(*xact));
}
struct entry_base_wrap : public entry_base_t
{
PyObject * self;
entry_base_wrap(PyObject * self_) : self(self_) {}
virtual bool valid() const {
return call_method<bool>(self, "valid");
}
};
struct py_entry_finalizer_t : public entry_finalizer_t {
object pyobj;
py_entry_finalizer_t() {}
py_entry_finalizer_t(object obj) : pyobj(obj) {}
py_entry_finalizer_t(const py_entry_finalizer_t& other)
: pyobj(other.pyobj) {}
virtual bool operator()(entry_t& entry) {
return call<bool>(pyobj.ptr(), entry);
}
};
std::list<py_entry_finalizer_t> py_finalizers;
void py_add_entry_finalizer(journal_t& journal, object x)
{
py_finalizers.push_back(py_entry_finalizer_t(x));
journal.add_entry_finalizer(&py_finalizers.back());
}
void py_remove_entry_finalizer(journal_t& journal, object x)
{
for (std::list<py_entry_finalizer_t>::iterator i = py_finalizers.begin();
i != py_finalizers.end();
i++)
if ((*i).pyobj == x) {
journal.remove_entry_finalizer(&(*i));
py_finalizers.erase(i);
return;
}
}
void py_run_entry_finalizers(journal_t& journal, entry_t& entry)
{
run_hooks(journal.entry_finalize_hooks, entry);
}
#define EXC_TRANSLATOR(type) \
void exc_translate_ ## type(const type& err) { \
PyErr_SetString(PyExc_RuntimeError, err.what()); \
}
EXC_TRANSLATOR(error)
EXC_TRANSLATOR(interval_expr_error)
EXC_TRANSLATOR(format_error)
EXC_TRANSLATOR(parse_error)
void export_journal()
{
scope().attr("TRANSACTION_NORMAL") = TRANSACTION_NORMAL;
scope().attr("TRANSACTION_VIRTUAL") = TRANSACTION_VIRTUAL;
scope().attr("TRANSACTION_BALANCE") = TRANSACTION_BALANCE;
scope().attr("TRANSACTION_AUTO") = TRANSACTION_AUTO;
scope().attr("TRANSACTION_BULK_ALLOC") = TRANSACTION_BULK_ALLOC;
class_< transaction_t > ("Transaction")
.def(init<account_t *, amount_t, optional<unsigned int, std::string> >())
.def(self == self)
.def(self != self)
.add_property("entry",
make_getter(&transaction_t::entry,
return_value_policy<reference_existing_object>()))
.add_property("account",
make_getter(&transaction_t::account,
return_value_policy<reference_existing_object>()))
.def_readwrite("amount", &transaction_t::amount)
.add_property("cost",
make_getter(&transaction_t::cost,
return_internal_reference<1>()))
.def_readwrite("state", &transaction_t::state)
.def_readwrite("flags", &transaction_t::flags)
.def_readwrite("note", &transaction_t::note)
#if 0
.def_readwrite("data", &transaction_t::data)
#endif
.def("valid", &transaction_t::valid)
;
enum_< transaction_t::state_t > ("State")
.value("UNCLEARED", transaction_t::UNCLEARED)
.value("CLEARED", transaction_t::CLEARED)
.value("PENDING", transaction_t::PENDING)
;
class_< account_t >
("Account",
init<optional<account_t *, std::string, std::string> >()
[with_custodian_and_ward<1, 2>()])
.def(self == self)
.def(self != self)
.def_readonly("journal", &account_t::journal)
.add_property("parent",
make_getter(&account_t::parent,
return_internal_reference<1>()))
.def_readwrite("name", &account_t::name)
.def_readwrite("note", &account_t::note)
.def_readonly("depth", &account_t::depth)
.add_property("data", py_account_get_data, py_account_set_data)
.def_readonly("ident", &account_t::ident)
.def(self_ns::str(self))
.def("fullname", &account_t::fullname)
.def("add_account", &account_t::add_account)
.def("remove_account", &account_t::remove_account)
.def("find_account", &account_t::find_account,
return_value_policy<reference_existing_object>())
.def("valid", &account_t::valid)
;
class_< journal_t > ("Journal")
.def(self == self)
.def(self != self)
.add_property("master",
make_getter(&journal_t::master,
return_internal_reference<1>()))
.add_property("basket",
make_getter(&journal_t::basket,
return_internal_reference<1>()))
.def_readwrite("price_db", &journal_t::price_db)
.def_readonly("sources", &journal_t::sources)
.def("__len__", entries_len)
.def("__getitem__", entries_getitem, return_internal_reference<1>())
.def("add_account", &journal_t::add_account)
.def("remove_account", &journal_t::remove_account)
.def("find_account", py_find_account_1, return_internal_reference<1>())
.def("find_account", py_find_account_2, return_internal_reference<1>())
.def("find_account_re", &journal_t::find_account_re,
return_internal_reference<1>())
.def("add_entry", py_add_entry)
.def("remove_entry", &journal_t::remove_entry)
.def("add_entry_finalizer", py_add_entry_finalizer)
.def("remove_entry_finalizer", py_remove_entry_finalizer)
.def("run_entry_finalizers", py_run_entry_finalizers)
.def("valid", &journal_t::valid)
;
class_< entry_base_t, entry_base_wrap, boost::noncopyable > ("EntryBase")
.def("__len__", transactions_len)
.def("__getitem__", transactions_getitem,
return_internal_reference<1>())
.def_readonly("journal", &entry_base_t::journal)
.def_readonly("src_idx", &entry_base_t::src_idx)
.def_readonly("beg_pos", &entry_base_t::beg_pos)
.def_readonly("beg_line", &entry_base_t::beg_line)
.def_readonly("end_pos", &entry_base_t::end_pos)
.def_readonly("end_line", &entry_base_t::end_line)
.def("add_transaction", py_add_transaction)
.def("remove_transaction", &entry_base_t::remove_transaction)
.def(self == self)
.def(self != self)
.def("valid", &entry_base_t::valid)
;
scope in_entry = class_< entry_t, bases<entry_base_t> > ("Entry")
.def_readwrite("date", &entry_t::date)
.def_readwrite("code", &entry_t::code)
.def_readwrite("payee", &entry_t::payee)
.def("valid", &entry_t::valid)
;
class_< auto_entry_t, bases<entry_base_t> > ("AutoEntry")
.add_property("predicate",
make_getter(&auto_entry_t::predicate,
return_internal_reference<1>()))
.def_readonly("predicate_string", &auto_entry_t::predicate_string)
.def("valid", &auto_entry_t::valid)
;
class_< period_entry_t, bases<entry_base_t> > ("PeriodEntry")
.def_readonly("period", &period_entry_t::period)
.def_readonly("period_string", &period_entry_t::period_string)
.def("valid", &period_entry_t::valid)
;
#define EXC_TRANSLATE(type) \
register_exception_translator<type>(&exc_translate_ ## type);
EXC_TRANSLATE(error);
EXC_TRANSLATE(interval_expr_error);
EXC_TRANSLATE(format_error);
EXC_TRANSLATE(parse_error);
}
#endif // USE_BOOST_PYTHON

View file

@ -64,7 +64,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* Running Ledger::
* Keeping a ledger::
* Using XML::
* Extending with Python::
@end menu
@node Introduction, Running Ledger, Top, Top
@ -2260,14 +2259,6 @@ precedence order of operators.
@item [DATE]
Useful specifying a date in plain terms. For example, you could say
@samp{[2004/06/01]}.
@item @@STR(ARGS,...)
If Python support is compiled in, this calls the Python function
@code{STR}. It is always be passed at least one argument, of type
@var{ledger.Details}, representing the known account, entry, and
transaction details at the time the value expression is computed.
Other value expression arguments may also be passed by the user, all
of type @var{Value}.
@end table
@node Period expressions, File format, Value expressions, Running Ledger
@ -2431,13 +2422,6 @@ transactions that follow, until @samp{!end} is seen.
@item !end
Ends an account block.
@item !python
If Python support is available, all of the lines following
@samp{!python} will be passed to the Python interpretor. Any
functions defined will be available to later Python blocks, and can be
called from a value expression. The Python code block must be ended
with @samp{!end}.
@end table
@item ;
@ -3699,8 +3683,8 @@ description is a ledger account name, these in/out pairs may be viewed
as virtual transactions, adding time commodities (hours) to that
account.
For example, the command-line version of the timeclock tool (which is
written in Python) could be used to begin a timelog file like:
For example, the command-line version of the timeclock tool could be
used to begin a timelog file like:
@example
export TIMELOG=$HOME/.timelog
@ -3778,7 +3762,7 @@ accounting ledger, with the attached prefix @samp{Billable}:
Receivable:ClientOne
@end smallexample
@node Using XML, Extending with Python, Keeping a ledger, Top
@node Using XML, , Keeping a ledger, Top
@chapter Using XML
By default, Ledger uses a human-readable data format, and displays its
@ -3930,29 +3914,4 @@ output such data if the @command{xml} command is used, and can read
the same data as long as the @file{expat} library was available
when Ledger was built.
@node Extending with Python, , Using XML, Top
@chapter Extending with Python
Ledger fully supports Python as an extension language. It may be used
in a few different forms, which fall into three basic categories:
@enumerate
@item
Defining Python functions to use in value expressions
@item
Using the ledger library as a Python module
@item
Setting up custom initialization using Python
@end enumerate
Note that this feature, while functional, is still under development.
It will not be documented until it has been fully proven, probably in
the next version of ledger. For now, if you wish to make this of this
functionality and are willing to debug problems that come up, pass the
option @samp{--enable-python} to configure, and contact the author via
email.
One example of using Python to create a more complex report is in the
script file @file{scripts/trend}.
@bye

30
main.cc
View file

@ -18,9 +18,6 @@
#endif
#include "ledger.h"
#ifdef USE_BOOST_PYTHON
#include "py_eval.h"
#endif
#include "timing.h"
using namespace ledger;
@ -232,33 +229,6 @@ int parse_and_report(int argc, char * argv[], char * envp[])
else
format = &config.print_format;
#ifdef USE_BOOST_PYTHON
// If Python support is compiled, we can easily report minimum and
// maximum values for each commodity. There is a line in config.cc
// which configures the prices report to call these two functions,
// if Python is available.
if (command == "P")
python_eval("\
min_val = 0\n\
def vmin(d, val):\n\
global min_val\n\
if not min_val or val < min_val:\n\
min_val = val\n\
return val\n\
return min_val\n\
\n\
max_val = 0\n\
def vmax(d, val):\n\
global max_val\n\
if not max_val or val > max_val:\n\
max_val = val\n\
return val\n\
return max_val\n", PY_EVAL_MULTI);
#endif // USE_BOOST_PYTHON
TIMER_STOP(process);
// Walk the entries based on the report type and the options

373
main.py
View file

@ -1,373 +0,0 @@
#!/usr/bin/env python
# Ledger, the command-line accounting tool
#
# Copyright (c) 2003-2004, New Artisans LLC. All rights reserved.
#
# This program is made available under the terms of the BSD Public
# License. See the LICENSE file included with the distribution for
# details and disclaimer.
#
# This script provides a Python front-end to the ledger library, and
# replicates the functionality of the C++ front-end, main.cc. It is
# provided as an example, and as a starting point for creating custom
# front-ends based on the Ledger module. See the documentation for an
# API reference, and how to use this module.
import os
import sys
import string
import time
true, false = 1, 0
from ledger import *
# Create the main journal object, into which all entries will be
# recorded. Once done, the 'journal' may be iterated to yield those
# entries, in the same order as which they appeared in the journal
# file.
journal = Journal ()
# This call registers all of the default command-line options that
# Ledger supports into the option handling mechanism. Skip this call
# if you wish to do all of your own processing -- in which case simply
# modify the 'config' object however you like.
add_config_option_handlers ()
averages = {}
compute_monthly_avg = false
def get_index (xact):
return time.strftime ("%Y/%m", time.localtime (xact.entry.date))
class ComputeMonthlyAvg (TransactionHandler):
def __call__ (self, xact):
global averages
index = get_index (xact)
if not averages.has_key(index):
averages[index] = [Value (), 0]
add_transaction_to (xact, averages[index][0])
averages[index][1] += 1
TransactionHandler.__call__ (self, xact)
def monthly_avg (details):
index = get_index (xact)
return averages[index][0] / averages[index][1]
def show_monthly_averages (arg):
global compute_monthly_avg
compute_monthly_avg = true
config.report_period = "monthly";
config.total_expr = "@monthly_avg()"
add_option_handler ("monthly-avg", "", show_monthly_averages)
# Process the command-line arguments, test whether caching should be
# enabled, and then process any option settings from the execution
# environment. Some historical environment variable names are also
# supported.
args = process_arguments (sys.argv[1:])
config.use_cache = not config.data_file
process_environment (os.environ, "LEDGER_")
if os.environ.has_key ("LEDGER"):
process_option ("file", os.getenv ("LEDGER"))
if os.environ.has_key ("PRICE_HIST"):
process_option ("price-db", os.getenv ("PRICE_HIST"))
if os.environ.has_key ("PRICE_EXP"):
process_option ("price-exp", os.getenv ("PRICE_EXP"))
# If no argument remain, then no command word was given. Report the
# default help text and exit.
if len (args) == 0:
option_help ()
sys.exit (0)
# The command word is in the first argument. Canonicalize it to a
# unique, simple form that the remaining code can use to find out
# which command was specified.
command = args.pop (0);
if command == "balance" or command == "bal" or command == "b":
command = "b"
elif command == "register" or command == "reg" or command == "r":
command = "r"
elif command == "print" or command == "p":
command = "p"
elif command == "output":
command = "w"
elif command == "emacs":
command = "x"
elif command == "xml":
command = "X"
elif command == "entry":
command = "e"
elif command == "equity":
command = "E"
elif command == "prices":
command = "P"
elif command == "pricesdb":
command = "D";
else:
print "Unrecognized command:", command
sys.exit (1)
# Create all the parser objects to be used. They are all registered,
# so that Ledger will try each one in turn whenever it is presented
# with a data file. They are attempted in reverse order to their
# registry. Note that Gnucash parsing is only available if the Ledger
# module was built with such support (which requires the expat C
# library).
bin_parser = BinaryParser ()
gnucash_parser = None
xml_parser = None
try: xml_parser = GnucashParser ()
except: pass
try: gnucash_parser = GnucashParser ()
except: pass
try: ofx_parser = OfxParser ()
except: pass
qif_parser = QifParser ()
text_parser = TextualParser ()
register_parser (bin_parser)
if xml_parser:
register_parser (xml_parser)
if gnucash_parser:
register_parser (gnucash_parser)
if ofx_parser:
register_parser (ofx_parser)
register_parser (qif_parser)
register_parser (text_parser)
# Parse all entries from the user specified locations (found in
# 'config') into the journal object we created. The two parsers given
# as explicit arguments indicate: the parser to be used for standard
# input, and the parser to be used for cache files.
parse_ledger_data (journal, bin_parser)
# Now that everything has been correctly parsed (parse_ledger_data
# would have thrown an exception if not), we can take time to further
# process the configuration options. This changes the configuration a
# bit based on previous option settings, the command word, and the
# remaining arguments.
config.process_options (command, args);
# If the command is "e", use the method journal.derive_entry to create
# a brand new entry based on the arguments given.
new_entry = None
if command == "e":
new_entry = derive_new_entry (journal, args)
if new_entry is None:
sys.exit (1)
# Determine the format string to used, based on the command.
if config.format_string:
format = config.format_string
elif command == "b":
format = config.balance_format
elif command == "r":
format = config.register_format
elif command == "E":
format = config.equity_format
elif command == "P":
min_val = 0
def vmin(d, val):
global min_val
if not min_val or val < min_val:
min_val = val
return val
return min_val
max_val = 0
def vmax(d, val):
global max_val
if not max_val or val > max_val:
max_val = val
return val
return max_val
format = config.prices_format
elif command == "D":
format = config.pricesdb_format
elif command == "w":
format = config.write_xact_format
else:
format = config.print_format
# Configure the output file
if config.output_file:
out = open (config.output_file, "w")
else:
out = sys.stdout
# Set the final transaction handler: for balances and equity reports,
# it will simply add the value of the transaction to the account's
# xdata, which is used a bit later to report those totals. For all
# other reports, the transaction data is sent to the configured output
# location (default is sys.stdout).
if command == "b" or command == "E":
handler = SetAccountValue ()
elif command == "p" or command == "e":
handler = FormatEntries (out, format)
elif command == "x":
handler = FormatEmacsTransactions (out)
elif command == "X":
handler = FormatXmlEntries (out, config.show_totals)
else:
handler = FormatTransactions (out, format)
if command == "w":
write_textual_journal(journal, args, handler, out);
else:
# Chain transaction filters on top of the base handler. Most of these
# filters customize the output for reporting. None of this is done
# for balance or equity reports, which don't need it.
if not (command == "b" or command == "E"):
if config.head_entries or config.tail_entries:
handler = TruncateEntries (handler, config.head_entries,
config.tail_entries)
if config.display_predicate:
handler = FilterTransactions (handler, config.display_predicate)
handler = CalcTransactions (handler)
if config.reconcile_balance:
reconcilable = False
if config.reconcile_balance == "<all>":
reconcilable = True
else:
target_balance = Value (config.reconcile_balance)
cutoff = time.time ()
if config.reconcile_date:
cutoff = parse_date (config.reconcile_date)
handler = ReconcileTransactions (handler, target_balance,
cutoff, reconcilable)
if config.sort_string:
handler = SortTransactions (handler, config.sort_string)
if config.show_revalued:
handler = ChangedValueTransactions (handler,
config.show_revalued_only)
if config.show_collapsed:
handler = CollapseTransactions (handler);
if config.show_subtotal and not (command == "b" or command == "E"):
handler = SubtotalTransactions (handler)
if config.days_of_the_week:
handler = DowTransactions (handler)
elif config.by_payee:
handler = ByPayeeTransactions (handler)
if config.report_period:
handler = IntervalTransactions (handler, config.report_period,
config.report_period_sort)
handler = SortTransactions (handler, "d")
if compute_monthly_avg:
handler = ComputeMonthlyAvg (handler)
# The next set of transaction filters are used by all reports.
if config.show_inverted:
handler = InvertTransactions (handler)
if config.show_related:
handler = RelatedTransactions (handler, config.show_all_related)
if config.predicate:
handler = FilterTransactions (handler, config.predicate)
if config.budget_flags:
handler = BudgetTransactions (handler, config.budget_flags)
handler.add_period_entries (journal)
elif config.forecast_limit:
handler = ForecastTransactions (handler, config.forecast_limit)
handler.add_period_entries (journal)
if config.comm_as_payee:
handler = SetCommAsPayee (handler)
# Walk the journal's entries, and pass each entry's transaction to the
# handler chain established above. And although a journal's entries
# can be walked using Python, it is significantly faster to do this
# simple walk in C++, using `walk_entries'.
#
# if command == "e":
# for xact in new_entry:
# handler (xact)
# else:
# for entry in journal:
# for xact in entry:
# handler (xact)
if command == "e":
walk_transactions (new_entry, handler)
elif command == "P" or command == "D":
walk_commodities (handler)
else:
walk_entries (journal, handler)
# Flush the handlers, causing them to output whatever data is still
# pending.
if command != "P" and command != "D":
handler.flush ()
# For the balance and equity reports, the account totals now need to
# be displayed. This is different from outputting transactions, in
# that we are now outputting account totals to display a summary of
# the transactions that were just walked.
if command == "b":
acct_formatter = FormatAccount (out, format, config.display_predicate)
sum_accounts (journal.master)
walk_accounts (journal.master, acct_formatter, config.sort_string)
acct_formatter.final (journal.master)
acct_formatter.flush ()
if account_has_xdata (journal.master):
xdata = account_xdata (journal.master)
if not config.show_collapsed and xdata.total:
out.write("--------------------\n")
xdata.value = xdata.total
# jww (2005-02-15): yet to convert
#acct_formatter.format.format (out, details_t (journal.master))
elif command == "E":
acct_formatter = FormatEquity (out, format, config.display_predicate)
sum_accounts (journal.master)
walk_accounts (journal.master, acct_formatter, config.sort_string)
acct_formatter.flush ()
# If it were important to clean things up, we would have to clear out
# the accumulated xdata at this point:
#clear_all_xdata ()
# If the cache is being used, and is dirty, update it now.
if config.use_cache and config.cache_dirty and config.cache_file:
write_binary_journal (config.cache_file, journal);
# We're done!

19
ofx.cc
View file

@ -220,22 +220,3 @@ unsigned int ofx_parser_t::parse(std::istream& in,
}
} // namespace ledger
#ifdef USE_BOOST_PYTHON
#include <boost/python.hpp>
using namespace boost::python;
using namespace ledger;
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(ofx_parse_overloads,
ofx_parser_t::parse, 2, 4)
void export_ofx() {
class_< ofx_parser_t, bases<parser_t> > ("OfxParser")
.def("test", &ofx_parser_t::test)
.def("parse", &ofx_parser_t::parse, ofx_parse_overloads())
;
}
#endif // USE_BOOST_PYTHON

View file

@ -168,94 +168,3 @@ void process_environment(std::list<option_t>& options,
process_option(options, buf, q + 1);
}
}
#ifdef USE_BOOST_PYTHON
#include <boost/python.hpp>
#include <boost/python/detail/api_placeholder.hpp>
#include <vector>
using namespace boost::python;
struct func_option_wrapper : public option_handler
{
object self;
func_option_wrapper(object _self) : self(_self) {}
virtual void operator()(const char * arg) {
call<void>(self.ptr(), arg);
}
};
namespace {
std::list<func_option_wrapper> wrappers;
std::list<option_t> options;
}
void py_add_option_handler(const std::string& long_opt,
const std::string& short_opt, object func)
{
wrappers.push_back(func_option_wrapper(func));
add_option_handler(options, long_opt, short_opt, wrappers.back());
}
void add_other_option_handlers(const std::list<option_t>& other)
{
options.insert(options.begin(), other.begin(), other.end());
}
bool py_process_option(const std::string& opt, const char * arg)
{
return process_option(options, opt, arg);
}
list py_process_arguments(list args, bool anywhere = false)
{
std::vector<char *> strs;
int l = len(args);
for (int i = 0; i < l; i++)
strs.push_back(extract<char *>(args[i]));
std::list<std::string> newargs;
process_arguments(options, strs.size(), &strs.front(), anywhere, newargs);
list py_newargs;
for (std::list<std::string>::iterator i = newargs.begin();
i != newargs.end();
i++)
py_newargs.append(*i);
return py_newargs;
}
BOOST_PYTHON_FUNCTION_OVERLOADS(py_proc_args_overloads,
py_process_arguments, 1, 2)
void py_process_environment(object env, const std::string& tag)
{
std::vector<char *> strs;
std::vector<std::string> storage;
list items = call_method<list>(env.ptr(), "items");
int l = len(items);
for (int i = 0; i < l; i++) {
tuple pair = extract<tuple>(items[i]);
std::string s = extract<std::string>(pair[0]);
s += "=";
s += extract<std::string>(pair[1]);
storage.push_back(s);
strs.push_back(const_cast<char *>(storage.back().c_str()));
}
process_environment(options, &strs.front(), tag);
}
void export_option()
{
def("add_option_handler", py_add_option_handler);
def("process_option", py_process_option);
def("process_arguments", py_process_arguments, py_proc_args_overloads());
def("process_environment", py_process_environment);
}
#endif // USE_BOOST_PYTHON

View file

@ -191,51 +191,3 @@ unsigned int parse_ledger_data(journal_t * journal, config_t& config)
}
} // namespace ledger
#ifdef USE_BOOST_PYTHON
#include <boost/python.hpp>
#include <Python.h>
using namespace boost::python;
using namespace ledger;
struct parser_wrap : public parser_t
{
PyObject * self;
parser_wrap(PyObject * self_) : self(self_) {}
virtual bool test(std::istream& in) const {
return call_method<bool>(self, "test", in);
}
virtual unsigned int parse(std::istream& in,
journal_t * journal,
account_t * master = NULL,
const std::string * original_file = NULL) {
return call_method<unsigned int>(self, "__call__", in, journal, master,
original_file);
}
};
BOOST_PYTHON_FUNCTION_OVERLOADS(parse_journal_overloads, parse_journal, 2, 4)
BOOST_PYTHON_FUNCTION_OVERLOADS(parse_journal_file_overloads,
parse_journal_file, 2, 4)
BOOST_PYTHON_FUNCTION_OVERLOADS(parse_ledger_data_overloads,
parse_ledger_data, 1, 2)
void export_parser() {
class_< parser_t, parser_wrap, boost::noncopyable > ("Parser")
;
def("register_parser", register_parser);
def("unregister_parser", unregister_parser);
def("parse_journal", parse_journal, parse_journal_overloads());
def("parse_journal_file", parse_journal_file, parse_journal_file_overloads());
#if 0
def("parse_ledger_data", parse_ledger_data, parse_ledger_data_overloads());
#endif
}
#endif // USE_BOOST_PYTHON

View file

@ -1,163 +0,0 @@
#include "py_eval.h"
#include "journal.h"
#include "error.h"
#include "acconf.h"
#include <sstream>
#include <map>
namespace {
bool python_initialized = false;
bool module_initialized = false;
}
void export_amount();
void export_balance();
void export_value();
void export_journal();
void export_parser();
void export_textual();
void export_binary();
void export_qif();
#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
void export_xml();
void export_gnucash();
#endif
#ifdef HAVE_LIBOFX
void export_ofx();
#endif
void export_option();
void export_config();
void export_walk();
void export_format();
void export_valexpr();
void export_datetime();
void export_derive();
void export_reconcile();
void export_emacs();
void initialize_ledger_for_python()
{
export_amount();
export_balance();
export_value();
export_journal();
export_parser();
export_textual();
export_binary();
export_qif();
#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
export_xml();
export_gnucash();
#endif
#ifdef HAVE_LIBOFX
export_ofx();
#endif
export_option();
export_config();
export_walk();
export_format();
export_valexpr();
export_datetime();
export_derive();
export_reconcile();
export_emacs();
module_initialized = true;
}
namespace ledger {
static struct python_main_t
{
handle<> mmodule;
dict nspace;
python_main_t()
: mmodule(borrowed(PyImport_AddModule("__main__"))),
nspace(handle<>(borrowed(PyModule_GetDict(mmodule.get())))) {}
}
* python_main = NULL;
struct python_run
{
object result;
python_run(const std::string& str, int input_mode)
: result(handle<>(borrowed(PyRun_String(str.c_str(), input_mode,
python_main->nspace.ptr(),
python_main->nspace.ptr())))) {}
operator object() {
return result;
}
};
static struct cleanup_python {
~cleanup_python() {
if (python_main) {
delete python_main;
python_main = NULL;
}
if (python_initialized)
Py_Finalize();
}
} _cleanup;
void init_python()
{
if (! module_initialized) {
Py_Initialize();
python_initialized = true;
detail::init_module("ledger", &initialize_ledger_for_python);
}
python_main = new python_main_t;
}
object python_eval(std::istream& in, py_eval_mode_t mode)
{
if (! python_initialized)
init_python();
bool first = true;
std::string buffer;
buffer.reserve(4096);
while (! in.eof()) {
char buf[256];
in.getline(buf, 255);
if (buf[0] == '!')
break;
if (first)
first = false;
else
buffer += "\n";
buffer += buf;
}
try {
int input_mode;
switch (mode) {
case PY_EVAL_EXPR: input_mode = Py_eval_input; break;
case PY_EVAL_STMT: input_mode = Py_single_input; break;
case PY_EVAL_MULTI: input_mode = Py_file_input; break;
}
assert(Py_IsInitialized());
return python_run(buffer, input_mode);
}
catch(const boost::python::error_already_set&) {
PyErr_Print();
throw error("Evaluating Python code");
}
}
object python_eval(const std::string& str, py_eval_mode_t mode)
{
std::istringstream stream(str);
return python_eval(stream, mode);
}
object python_eval(const char * c_str, py_eval_mode_t mode)
{
std::string str(c_str);
return python_eval(str, mode);
}
} // namespace ledger

View file

@ -1,75 +0,0 @@
#ifndef _PYTHON_H
#define _PYTHON_H
#include "valexpr.h"
#include <string>
#include <iostream>
#include <boost/python.hpp>
using namespace boost::python;
namespace ledger {
enum py_eval_mode_t {
PY_EVAL_EXPR,
PY_EVAL_STMT,
PY_EVAL_MULTI
};
object python_eval(std::istream& in, py_eval_mode_t mode = PY_EVAL_EXPR);
object python_eval(const std::string& str, py_eval_mode_t mode = PY_EVAL_EXPR);
object python_eval(const char * c_str, py_eval_mode_t mode = PY_EVAL_EXPR);
template <typename T>
bool python_call(const std::string& func_name, value_expr_t * arg_expr,
const details_t& details, T& result)
{
try {
object func = python_eval(func_name);
if (arg_expr) {
if (arg_expr->right) {
list args;
args.append(details);
for (value_expr_t * arg = arg_expr; arg; arg = arg->right) {
assert(arg->kind == value_expr_t::O_ARG);
value_t value;
arg->left->compute(value, details);
args.append(value);
}
if (PyObject * val = PyObject_CallObject(func.ptr(),
tuple(args).ptr())) {
result = extract<T>(val)();
Py_DECREF(val);
}
else if (PyObject * err = PyErr_Occurred()) {
PyErr_Print();
throw value_expr_error(std::string("While calling Python function '") +
func_name + "'");
}
else {
return false;
}
} else {
assert(arg_expr->kind == value_expr_t::O_ARG);
value_t value;
arg_expr->left->compute(value, details);
result = call<T>(func.ptr(), details, value);
}
} else {
result = call<T>(func.ptr(), details);
}
return true;
}
catch(const boost::python::error_already_set&) {
PyErr_Print();
throw value_expr_error(std::string("While calling Python function '") +
func_name + "'");
}
}
} // namespace ledger
#endif // _PYTHON_H

View file

@ -1,146 +0,0 @@
#ifndef _PYFSTREAM_H
#define _PYFSTREAM_H
#include <istream>
#include <ostream>
#include <streambuf>
#include <cstdio>
#include <cstring>
#include "Python.h"
// pyofstream
// - a stream that writes on a Python file object
class pyoutbuf : public std::streambuf {
protected:
PyFileObject * fo; // Python file object
public:
// constructor
pyoutbuf (PyFileObject * _fo) : fo(_fo) {}
protected:
// write one character
virtual int_type overflow (int_type c) {
if (c != EOF) {
char z[2];
z[0] = c;
z[1] = '\0';
if (PyFile_WriteString(z, (PyObject *)fo) < 0) {
return EOF;
}
}
return c;
}
// write multiple characters
virtual std::streamsize xsputn (const char* s, std::streamsize num) {
char * buf = new char[num + 1];
std::strncpy(buf, s, num);
buf[num] = '\0';
if (PyFile_WriteString(buf, (PyObject *)fo) < 0)
num = 0;
delete[] buf;
return num;
}
};
class pyofstream : public std::ostream {
protected:
pyoutbuf buf;
public:
pyofstream (PyFileObject * fo) : std::ostream(0), buf(fo) {
rdbuf(&buf);
}
};
// pyifstream
// - a stream that reads on a file descriptor
class pyinbuf : public std::streambuf {
protected:
PyFileObject * fo; // Python file object
protected:
/* data buffer:
* - at most, pbSize characters in putback area plus
* - at most, bufSize characters in ordinary read buffer
*/
static const int pbSize = 4; // size of putback area
static const int bufSize = 1024; // size of the data buffer
char buffer[bufSize + pbSize]; // data buffer
public:
/* constructor
* - initialize file descriptor
* - initialize empty data buffer
* - no putback area
* => force underflow()
*/
pyinbuf (PyFileObject * _fo) : fo(_fo) {
setg (buffer+pbSize, // beginning of putback area
buffer+pbSize, // read position
buffer+pbSize); // end position
}
protected:
// insert new characters into the buffer
virtual int_type underflow () {
#ifndef _MSC_VER
using std::memmove;
#endif
// is read position before end of buffer?
if (gptr() < egptr()) {
return traits_type::to_int_type(*gptr());
}
/* process size of putback area
* - use number of characters read
* - but at most size of putback area
*/
int numPutback;
numPutback = gptr() - eback();
if (numPutback > pbSize) {
numPutback = pbSize;
}
/* copy up to pbSize characters previously read into
* the putback area
*/
memmove (buffer+(pbSize-numPutback), gptr()-numPutback,
numPutback);
// read at most bufSize new characters
int num;
PyObject *line = PyFile_GetLine((PyObject *)fo, bufSize);
if (! line || ! PyString_Check(line)) {
// ERROR or EOF
return EOF;
}
num = PyString_Size(line);
if (num == 0)
return EOF;
memmove (buffer+pbSize, PyString_AsString(line), num);
// reset buffer pointers
setg (buffer+(pbSize-numPutback), // beginning of putback area
buffer+pbSize, // read position
buffer+pbSize+num); // end of buffer
// return next character
return traits_type::to_int_type(*gptr());
}
};
class pyifstream : public std::istream {
protected:
pyinbuf buf;
public:
pyifstream (PyFileObject * fo) : std::istream(0), buf(fo) {
rdbuf(&buf);
}
};
#endif // _PYFSTREAM_H

View file

@ -1,9 +0,0 @@
#include <boost/python.hpp>
using namespace boost::python;
void initialize_ledger_for_python();
BOOST_PYTHON_MODULE(ledger) {
initialize_ledger_for_python();
}

View file

@ -1,16 +0,0 @@
#ifndef _PYLEDGER_H
#define _PYLEDGER_H
//////////////////////////////////////////////////////////////////////
//
// Ledger Accounting Tool (with Python support via Boost.Python)
//
// A command-line tool for general double-entry accounting.
//
// Copyright (c) 2003,2004 John Wiegley <johnw@newartisans.com>
//
#include <ledger.h>
#include <py_eval.h>
#endif // _PYLEDGER_H

19
qif.cc
View file

@ -246,22 +246,3 @@ unsigned int qif_parser_t::parse(std::istream& in,
}
} // namespace ledger
#ifdef USE_BOOST_PYTHON
#include <boost/python.hpp>
using namespace boost::python;
using namespace ledger;
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(qif_parse_overloads,
qif_parser_t::parse, 2, 4)
void export_qif() {
class_< qif_parser_t, bases<parser_t> > ("QifParser")
.def("test", &qif_parser_t::test)
.def("parse", &qif_parser_t::parse, qif_parse_overloads())
;
}
#endif // USE_BOOST_PYTHON

View file

@ -86,23 +86,3 @@ void reconcile_transactions::flush()
}
} // namespace ledger
#ifdef USE_BOOST_PYTHON
#include <boost/python.hpp>
using namespace boost::python;
using namespace ledger;
void export_reconcile()
{
class_< reconcile_transactions, bases<item_handler<transaction_t> > >
("ReconcileTransactions",
init<item_handler<transaction_t> *, const value_t&, time_t>()
[with_custodian_and_ward<1, 2>()])
.def("flush", &reconcile_transactions::flush)
.def("__call__", &reconcile_transactions::operator())
;
}
#endif // USE_BOOST_PYTHON

View file

@ -1,30 +0,0 @@
#!/usr/bin/env python
from distutils.core import setup, Extension
import os
libs = ["ledger", "boost_python", "gmp", "pcre"]
if os.environ.has_key ("HAVE_EXPAT") and\
os.environ["HAVE_EXPAT"] == "true":
libs.extend (["expat"])
if os.environ.has_key ("HAVE_XMLPARSE") and\
os.environ["HAVE_XMLPARSE"] == "true":
libs.extend (["xmlparse", "xmltok"])
if os.environ.has_key ("HAVE_LIBOFX") and\
os.environ["HAVE_LIBOFX"] == "true":
libs.extend (["ofx"])
setup(name = "Ledger",
version = "2.5",
description = "Ledger Accounting Tool",
author = "John Wiegley",
author_email = "johnw@newartisans.com",
url = "http://www.newartisans.com/ledger.html",
ext_modules = [
Extension("ledger", ["pyledger.cc"],
define_macros = [('PYTHON_MODULE', 1)],
libraries = libs)])

View file

@ -12,9 +12,6 @@
#include "timing.h"
#include "util.h"
#include "acconf.h"
#ifdef USE_BOOST_PYTHON
#include "py_eval.h"
#endif
#include <fstream>
#include <sstream>
@ -733,11 +730,6 @@ unsigned int textual_parser_t::parse(std::istream& in,
else if (word == "end") {
account_stack.pop_front();
}
#ifdef USE_BOOST_PYTHON
else if (word == "python") {
python_eval(in, PY_EVAL_MULTI);
}
#endif
else if (word == "alias") {
char * b = skip_ws(p);
if (char * e = std::strchr(b, '=')) {
@ -904,22 +896,3 @@ void write_textual_journal(journal_t& journal, std::string path,
}
} // namespace ledger
#ifdef USE_BOOST_PYTHON
#include <boost/python.hpp>
using namespace boost::python;
using namespace ledger;
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(textual_parse_overloads,
textual_parser_t::parse, 2, 4)
void export_textual() {
class_< textual_parser_t, bases<parser_t> > ("TextualParser")
.def("test", &textual_parser_t::test)
.def("parse", &textual_parser_t::parse, textual_parse_overloads())
;
}
#endif // USE_BOOST_PYTHON

461
timeclock
View file

@ -1,461 +0,0 @@
#!/usr/bin/env python
# timeclock, a time-keeping program based on the Ledger library
#
# Copyright (c) 2003-2004, New Artisans LLC. All rights reserved.
#
# This program is made available under the terms of the BSD Public
# License. See the LICENSE file included with the distribution for
# details and disclaimer.
#
# This program implements a simple timeclock, using the identical
# format as my timeclock.el module for Emacs (which is part of the
# Emacs 21 distribution). This allows you to use either this script
# or that module for creating time events.
#
# Usage is very simple: Set the environment variable TIMELOG to the
# path to your timelog file (if this variable is unset, any events
# created will simply be printed to stdout). Once this variable is
# set:
#
# timeclock in "project" what aspect of the project will I do today
# timeclock out what did I accomplish
#
# The description text is optional, but the project is required when
# clocking in. This project should be a account name, which means it
# can use ":" to separate the project from the task, for example:
#
# timeclock in Client:Meetings at the code review meeting
#
# To generate a balance report of time spent, use "timeclock" with no
# arguments, or "timeclock balance". The options available are the
# same as those used for ledger.
import os
import sys
import string
import time
true, false = 1, 0
from ledger import *
home = os.getenv ("HOME")
config.init_file = home + "/.timeclockrc";
config.cache_file = home + "/.timeclock-cache";
# Define some functions for reporting time quantities
workday = 8 * 60 * 60 # length of a nominal workday
def secstr (secs):
return "%d:%02d" % (abs (secs) / 60 / 60, (abs (secs) / 60) % 60)
def daystr (amt):
dy = int (amt) / int (workday)
amt = amt - (dy * workday)
if dy >= 5:
wk = dy / 5
dy = dy % 5
if dy: amt = "%sw %sd %s" % (wk, dy, secstr (amt))
else: amt = "%sw %s" % (wk, secstr (amt))
else:
if dy: amt = "%sd %s" % (dy, secstr (amt))
else: amt = secstr (amt)
return amt
def adaystr (details):
result = ""
for amt in account_xdata(details.account).total:
if amt.commodity ().symbol == "s":
result = daystr (float (amt))
break
return result
config.amount_expr = "{1.00h}*(a/{3600.00h})"
config.total_expr = "{1.00h}*(O/{3600.00h})"
config.balance_format = "%20@adaystr() %8T %2_%-a\n";
# Help text specific to timeclock
def show_version (arg):
print """Timeclock, a command-line timekeeping tool
Copyright (c) 2003-2004, New Artisans LLC. All rights reserved.
This program is made available under the terms of the BSD Public
License. See the LICENSE file included with the distribution for
details and disclaimer."""
sys.exit (0)
def option_help (arg):
print """usage: timeclock [options] COMMAND [ACCT REGEX]...
Basic options:
-h, --help display this help text
-v, --version show version information
-i, --init FILE initialize ledger by loading FILE (def: ~/.ledgerrc)
--cache FILE use FILE as a binary cache when --file is not used
-f, --file FILE read ledger data from FILE
-o, --output FILE write output to FILE
Report filtering:
-b, --begin DATE set report begin date
-e, --end DATE set report end date
-c, --current show only current and past entries (not future)
-C, --cleared consider only cleared transactions
-U, --uncleared consider only uncleared transactions
-R, --real consider only real (non-virtual) transactions
-Z, --actual consider only actual (non-automated) transactions
-r, --related calculate report using related transactions
Output customization:
-F, --format STR use STR as the format; for each report type, use:
--balance-format --register-format
--plot-amount-format --plot-total-format
-y, --date-format STR use STR as the date format (def: %Y/%m/%d)
--wide for the default register report, use 132 columns
-E, --empty balance: show accounts with zero balance
-n, --collapse register: collapse entries with multiple transactions
-s, --subtotal balance: show sub-accounts; register: show subtotals
-S, --sort EXPR sort report according to the value expression EXPR
-p, --period STR report using the given period
--period-sort EXPR sort each report period's entries by EXPR
--dow show a days-of-the-week report
-W, --weekly show weekly sub-totals
-M, --monthly show monthly sub-totals
-Y, --yearly show yearly sub-totals
-l, --limit EXPR calculate only transactions matching EXPR
-d, --display EXPR display only transactions matching EXPR
-t, --amount EXPR use EXPR to calculate the displayed amount
-T, --total EXPR use EXPR to calculate the displayed total
-j, --amount-data print only raw amount data (useful for scripting)
-J, --total-data print only raw total data
Commodity reporting:
-A, --average report average transaction amount
-D, --deviation report deviation from the average
Commands:
balance [REGEXP]... show balance totals for matching accounts
register [REGEXP]... show register of matching events"""
sys.exit (0)
# This call registers all of the default command-line options that
# Ledger supports into the option handling mechanism. Skip this call
# if you wish to do all of your own processing -- in which case simply
# modify the 'config' object however you like.
add_config_option_handlers ()
add_option_handler ("help", "h", option_help)
add_option_handler ("version", "v", show_version)
# Process the command-line arguments, test whether caching should be
# enabled, and then process any option settings from the execution
# environment. Some historical environment variable names are also
# supported.
args = process_arguments (sys.argv[1:])
config.use_cache = not config.data_file
process_environment (os.environ, "TIMECLOCK_")
if os.environ.has_key ("TIMELOG"):
process_option ("file", os.getenv ("TIMELOG"))
# The command word is in the first argument. Canonicalize it to a
# unique, simple form that the remaining code can use to find out
# which command was specified.
if len (args) == 0:
args = ["balance"]
command = args.pop (0);
if command == "balance" or command == "bal" or command == "b":
command = "b"
elif command == "register" or command == "reg" or command == "r":
command = "r"
elif command == "entry":
command = "e"
elif command == "in" or command == "out":
if config.data_file:
log = open (config.data_file, "a")
else:
log = sys.stdout
if command == "in":
if len (args) == 0:
print "A project name is required when clocking in."
sys.exit (1)
log.write ("i %s %s" % (time.strftime ("%Y/%m/%d %H:%M:%S"),
args.pop (0)))
if len (args) > 0:
log.write (" %s\n" % string.join (args, " "))
else:
log.write ("o %s" % time.strftime ("%Y/%m/%d %H:%M:%S"))
if len (args) > 0:
log.write (" %s" % string.join (args, " "))
log.write ("\n")
log.close ()
sys.exit (0)
else:
print "Unrecognized command:", command
sys.exit (1)
# Create the main journal object, into which all entries will be
# recorded. Once done, the 'journal' may be iterated to yield those
# entries, in the same order as which they appeared in the journal
# file.
journal = Journal ()
# This parser is intended only for timelog files.
class Event:
def __init__(self, kind, when, desc):
self.kind = kind
self.when = when
self.desc = desc
class Interval:
def __init__(self, begin, end):
self.begin = begin
self.end = end
def length(self):
"Return the length of the interval in seconds."
return self.end.when - self.begin.when
def parse_timelog(path, journal):
import re
if not os.path.exists (path):
print "Cannot read timelog file '%s'" % path
sys.exit (1)
file = open(path)
history = []
begin = None
linenum = 0
for line in file:
linenum += 1
match = re.match("([iIoO])\s+([0-9/]+\s+[0-9:]+)\s*(.+)", line)
if match:
(kind, when, desc) = match.groups()
when = time.strptime(when, "%Y/%m/%d %H:%M:%S")
when = time.mktime(when)
event = Event(kind, when, desc)
if kind == "i" or kind == "I":
begin = event
else:
if begin.desc:
match = re.match ("(.+?) (.+)", begin.desc)
if match:
acct = match.group (1)
desc = match.group (2)
else:
acct = begin.desc
desc = ""
else:
acct = "Misc"
desc = event.desc
l = Interval(begin, event).length ()
e = Entry ()
e.date = int (begin.when)
e.payee = desc
x = Transaction (journal.find_account (acct),
Amount ("%ss" % l), TRANSACTION_VIRTUAL)
e.add_transaction (x)
if not journal.add_entry (e):
print "%s, %d: Failed to entry" % (path, linenum)
sys.exit (1)
parse_timelog (config.data_file, journal)
# Now that everything has been correctly parsed (parse_ledger_data
# would have thrown an exception if not), we can take time to further
# process the configuration options. This changes the configuration a
# bit based on previous option settings, the command word, and the
# remaining arguments.
if command == "b" and \
config.amount_expr == "{1.00h}*(a/{3600.00h})":
config.amount_expr = "a"
config.process_options (command, args);
# Determine the format string to used, based on the command.
if config.format_string:
format = config.format_string
elif command == "b":
format = config.balance_format
elif command == "r":
format = config.register_format
else:
format = config.print_format
# The following two classes are responsible for outputing transactions
# and accounts to the user. There are corresponding C++ versions to
# these, but they rely on I/O streams, which Boost.Python does not
# provide a conversion layer for.
class FormatTransactions (TransactionHandler):
last_entry = None
output = None
def __init__ (self, fmt):
try:
i = string.index (fmt, '%/')
self.formatter = Format (fmt[: i])
self.nformatter = Format (fmt[i + 2 :])
except ValueError:
self.formatter = Format (fmt)
self.nformatter = None
self.last_entry = None
if config.output_file:
self.output = open (config.output_file, "w")
else:
self.output = sys.stdout
TransactionHandler.__init__ (self)
def __del__ (self):
if config.output_file:
self.output.close ()
def flush (self):
self.output.flush ()
def __call__ (self, xact):
if not transaction_has_xdata (xact) or \
not transaction_xdata (xact).dflags & TRANSACTION_DISPLAYED:
if self.nformatter is not None and \
self.last_entry is not None and \
xact.entry == self.last_entry:
self.output.write (self.nformatter.format (xact))
else:
self.output.write (self.formatter.format (xact))
self.last_entry = xact.entry
transaction_xdata (xact).dflags |= TRANSACTION_DISPLAYED
class FormatAccounts (AccountHandler):
output = None
def __init__ (self, fmt, pred):
self.formatter = Format (fmt)
self.predicate = AccountPredicate (pred)
if config.output_file:
self.output = open (config.output_file, "w")
else:
self.output = sys.stdout
AccountHandler.__init__ (self)
def __del__ (self):
if config.output_file:
self.output.close ()
def final (self, account):
if account_has_xdata (account):
xdata = account_xdata (account)
if xdata.dflags & ACCOUNT_TO_DISPLAY:
print "-------------------- ---------"
xdata.value = xdata.total
self.output.write (self.formatter.format (account))
def flush (self):
self.output.flush ()
def __call__ (self, account):
if display_account (account, self.predicate):
if not account.parent:
account_xdata (account).dflags |= ACCOUNT_TO_DISPLAY
else:
self.output.write (self.formatter.format (account))
account_xdata (account).dflags |= ACCOUNT_DISPLAYED
# Set the final transaction handler: for balances and equity reports,
# it will simply add the value of the transaction to the account's
# xdata, which is used a bit later to report those totals. For all
# other reports, the transaction data is sent to the configured output
# location (default is sys.stdout).
if command == "b":
handler = SetAccountValue ()
else:
handler = FormatTransactions (format)
# Chain transaction filters on top of the base handler. Most of these
# filters customize the output for reporting. None of this is done
# for balance or equity reports, which don't need it.
if command != "b":
if config.display_predicate:
handler = FilterTransactions (handler, config.display_predicate)
handler = CalcTransactions (handler)
if config.sort_string:
handler = SortTransactions (handler, config.sort_string)
if config.show_revalued:
handler = ChangedValueTransactions (handler, config.show_revalued_only)
if config.show_collapsed:
handler = CollapseTransactions (handler);
if config.show_subtotal and not (command == "b" or command == "E"):
handler = SubtotalTransactions (handler)
if config.days_of_the_week:
handler = DowTransactions (handler)
elif config.by_payee:
handler = ByPayeeTransactions (handler)
if config.report_period:
handler = IntervalTransactions (handler, config.report_period,
config.report_period_sort)
handler = SortTransactions (handler, "d")
# The next two transaction filters are used by all reports.
if config.show_inverted:
handler = InvertTransactions (handler)
if config.show_related:
handler = RelatedTransactions (handler, config.show_all_related)
if config.predicate:
handler = FilterTransactions (handler, config.predicate)
if config.comm_as_payee:
handler = SetCommAsPayee (handler)
# Walk the journal's entries, and pass each entry's transaction to the
# handler chain established above.
walk_entries (journal, handler)
# Flush the handlers, causing them to output whatever data is still
# pending.
handler.flush ()
# For the balance and equity reports, the account totals now need to
# be displayed. This is different from outputting transactions, in
# that we are now outputting account totals to display a summary of
# the transactions that were just walked.
if command == "b":
acct_formatter = FormatAccounts (format, config.display_predicate)
sum_accounts (journal.master)
walk_accounts (journal.master, acct_formatter, config.sort_string)
acct_formatter.final (journal.master)
acct_formatter.flush ()

View file

@ -284,10 +284,10 @@ display (non-nil means on)."
(> (prefix-numeric-value arg) 0)
(not timeclock-modeline-display))))
(if on-p
(progn
(or (memq 'timeclock-mode-string global-mode-string)
(setq global-mode-string
(append global-mode-string '(timeclock-mode-string))))
(progn
(or (memq 'timeclock-mode-string global-mode-string)
(setq global-mode-string
(append global-mode-string '(timeclock-mode-string))))
(unless (memq 'timeclock-update-modeline timeclock-event-hook)
(add-hook 'timeclock-event-hook 'timeclock-update-modeline))
(when timeclock-update-timer
@ -296,13 +296,13 @@ display (non-nil means on)."
(if (boundp 'display-time-hook)
(remove-hook 'display-time-hook 'timeclock-update-modeline))
(if timeclock-use-display-time
(progn
;; Update immediately so there is a visible change
;; on calling this function.
(if display-time-mode (timeclock-update-modeline)
(message "Activate `display-time-mode' to see \
(progn
;; Update immediately so there is a visible change
;; on calling this function.
(if display-time-mode (timeclock-update-modeline)
(message "Activate `display-time-mode' to see \
timeclock information"))
(add-hook 'display-time-hook 'timeclock-update-modeline))
(add-hook 'display-time-hook 'timeclock-update-modeline))
(setq timeclock-update-timer
(run-at-time nil 60 'timeclock-update-modeline))))
(setq global-mode-string
@ -358,9 +358,9 @@ discover the name of the project."
(timeclock-reread-log))
;; Either no log file, or day has rolled over.
(unless (and timeclock-last-event
(equal (timeclock-time-to-date
(cadr timeclock-last-event))
(timeclock-time-to-date (current-time))))
(equal (timeclock-time-to-date
(cadr timeclock-last-event))
(timeclock-time-to-date (current-time))))
(let ((workday (or (and (numberp arg) arg)
(and arg 0)
(and timeclock-get-workday-function
@ -412,8 +412,8 @@ If TODAY-ONLY is non-nil, the value returned will be relative only to
the time worked today, and not to past time."
(let ((discrep (timeclock-find-discrep)))
(if discrep
(- (if today-only (cadr discrep)
(car discrep)))
(- (if today-only (cadr discrep)
(car discrep)))
0.0)))
;;;###autoload
@ -424,8 +424,8 @@ If TODAY-ONLY is non-nil, the display will be relative only to time
worked today, ignoring the time worked on previous days."
(interactive "P")
(let ((remainder (timeclock-workday-remaining)) ; today-only?
(last-in (equal (car timeclock-last-event) "i"))
status)
(last-in (equal (car timeclock-last-event) "i"))
status)
(setq status
(format "Currently %s since %s (%s), %s %s, leave at %s"
(if last-in "IN" "OUT")
@ -619,7 +619,7 @@ The value of `timeclock-relative' affects the display as described in
that variable's documentation."
(interactive)
(let ((remainder (timeclock-workday-remaining (not timeclock-relative)))
(last-in (equal (car timeclock-last-event) "i")))
(last-in (equal (car timeclock-last-event) "i")))
(when (and (< remainder 0)
(not (and timeclock-day-over
(equal timeclock-day-over
@ -629,12 +629,12 @@ that variable's documentation."
(timeclock-time-to-date (current-time)))
(run-hooks 'timeclock-day-over-hook))
(setq timeclock-mode-string
(propertize
(format " %c%s%c "
(if last-in ?< ?[)
(timeclock-seconds-to-string remainder nil t)
(propertize
(format " %c%s%c "
(if last-in ?< ?[)
(timeclock-seconds-to-string remainder nil t)
(if last-in ?> ?]))
'help-echo "timeclock: time remaining"))))
'help-echo "timeclock: time remaining"))))
(put 'timeclock-mode-string 'risky-local-variable t)

View file

@ -4,9 +4,6 @@
#include "datetime.h"
#include "debug.h"
#include "util.h"
#ifdef USE_BOOST_PYTHON
#include "py_eval.h"
#endif
namespace ledger {
@ -325,16 +322,6 @@ void value_expr_t::compute(value_t& result, const details_t& details) const
break;
}
case F_INTERP_FUNC: {
#ifdef USE_BOOST_PYTHON
if (! python_call(constant_s, right, details, result))
result = 0L;
#else
result = 0L;
#endif
break;
}
case O_NOT:
left->compute(result, details);
result.negate();
@ -594,34 +581,6 @@ value_expr_t * parse_value_term(std::istream& in)
break;
}
case '@': {
READ_INTO(in, buf, 255, c, c != '(');
if (c != '(')
unexpected(c, '(');
node.reset(new value_expr_t(value_expr_t::F_INTERP_FUNC));
node->constant_s = buf;
in.get(c);
if (peek_next_nonws(in) == ')') {
in.get(c);
} else {
node->right = new value_expr_t(value_expr_t::O_ARG);
value_expr_t * cur = node->right;
cur->left = parse_value_expr(in, true);
in.get(c);
while (! in.eof() && c == ',') {
cur->right = new value_expr_t(value_expr_t::O_ARG);
cur = cur->right;
cur->left = parse_value_expr(in, true);
in.get(c);
}
if (c != ')')
unexpected(c, ')');
}
break;
}
case '(':
node.reset(parse_value_expr(in, true));
in.get(c);
@ -935,12 +894,6 @@ 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 << "](";
dump_value_expr(out, node->right);
out << ')';
break;
case value_expr_t::O_NOT:
out << '!';
dump_value_expr(out, node->left);
@ -1022,95 +975,6 @@ void dump_value_expr(std::ostream& out, const value_expr_t * node)
} // namespace ledger
#ifdef USE_BOOST_PYTHON
#include <boost/python.hpp>
using namespace boost::python;
using namespace ledger;
value_t py_compute_1(value_expr_t& value_expr, const details_t& item)
{
value_t result;
value_expr.compute(result, item);
return result;
}
template <typename T>
value_t py_compute(value_expr_t& value_expr, const T& item)
{
value_t result;
value_expr.compute(result, details_t(item));
return result;
}
value_expr_t * py_parse_value_expr_1(const std::string& str)
{
return parse_value_expr(str);
}
value_expr_t * py_parse_value_expr_2(const std::string& str, const bool partial)
{
return parse_value_expr(str, partial);
}
#define EXC_TRANSLATOR(type) \
void exc_translate_ ## type(const type& err) { \
PyErr_SetString(PyExc_RuntimeError, err.what()); \
}
EXC_TRANSLATOR(value_expr_error)
EXC_TRANSLATOR(compute_error)
EXC_TRANSLATOR(mask_error)
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_1)
.def("compute", py_compute<account_t>)
.def("compute", py_compute<entry_t>)
.def("compute", py_compute<transaction_t>)
;
def("parse_value_expr", py_parse_value_expr_1,
return_value_policy<manage_new_object>());
def("parse_value_expr", py_parse_value_expr_2,
return_value_policy<manage_new_object>());
class_< item_predicate<transaction_t> >
("TransactionPredicate", init<std::string>())
.def("__call__", &item_predicate<transaction_t>::operator())
;
class_< item_predicate<account_t> >
("AccountPredicate", init<std::string>())
.def("__call__", &item_predicate<account_t>::operator())
;
#define EXC_TRANSLATE(type) \
register_exception_translator<type>(&exc_translate_ ## type);
EXC_TRANSLATE(value_expr_error);
EXC_TRANSLATE(compute_error);
EXC_TRANSLATE(mask_error);
}
#endif // USE_BOOST_PYTHON
#ifdef TEST
int main(int argc, char *argv[])

View file

@ -73,7 +73,6 @@ struct value_expr_t
F_ACCOUNT_MASK,
F_SHORT_ACCOUNT_MASK,
F_COMMODITY_MASK,
F_INTERP_FUNC,
// Binary operators
O_ADD,

270
value.cc
View file

@ -790,273 +790,3 @@ value_t& value_t::add(const amount_t& amount, const amount_t * cost)
}
} // namespace ledger
#ifdef USE_BOOST_PYTHON
#include <boost/python.hpp>
using namespace boost::python;
using namespace ledger;
long balance_len(balance_t& bal);
amount_t balance_getitem(balance_t& bal, int i);
long balance_pair_len(balance_pair_t& bal_pair);
amount_t balance_pair_getitem(balance_pair_t& bal_pair, int i);
long value_len(value_t& value)
{
switch (value.type) {
case value_t::BOOLEAN:
case value_t::INTEGER:
case value_t::AMOUNT:
return 1;
case value_t::BALANCE:
return balance_len(*((balance_t *) value.data));
case value_t::BALANCE_PAIR:
return balance_pair_len(*((balance_pair_t *) value.data));
default:
assert(0);
break;
}
assert(0);
return 0;
}
amount_t value_getitem(value_t& value, int i)
{
std::size_t len = value_len(value);
if (abs(i) >= len) {
PyErr_SetString(PyExc_IndexError, "Index out of range");
throw_error_already_set();
}
switch (value.type) {
case value_t::BOOLEAN:
case value_t::INTEGER:
return long(value);
case value_t::AMOUNT:
return *((amount_t *) value.data);
case value_t::BALANCE:
return balance_getitem(*((balance_t *) value.data), i);
case value_t::BALANCE_PAIR:
return balance_pair_getitem(*((balance_pair_t *) value.data), i);
default:
assert(0);
break;
}
assert(0);
return 0L;
}
double py_to_float(value_t& value)
{
return double(value);
}
void export_value()
{
scope in_value = class_< value_t > ("Value")
.def(init<value_t>())
.def(init<balance_pair_t>())
.def(init<balance_t>())
.def(init<amount_t>())
.def(init<std::string>())
.def(init<double>())
.def(init<long>())
.def(self + self)
.def(self + other<balance_pair_t>())
.def(self + other<balance_t>())
.def(self + other<amount_t>())
.def(self + long())
.def(self + double())
.def(other<balance_pair_t>() + self)
.def(other<balance_t>() + self)
.def(other<amount_t>() + self)
.def(long() + self)
.def(double() + self)
.def(self - self)
.def(self - other<balance_pair_t>())
.def(self - other<balance_t>())
.def(self - other<amount_t>())
.def(self - long())
.def(self - double())
.def(other<balance_pair_t>() - self)
.def(other<balance_t>() - self)
.def(other<amount_t>() - self)
.def(long() - self)
.def(double() - self)
.def(self * self)
.def(self * other<balance_pair_t>())
.def(self * other<balance_t>())
.def(self * other<amount_t>())
.def(self * long())
.def(self * double())
.def(other<balance_pair_t>() * self)
.def(other<balance_t>() * self)
.def(other<amount_t>() * self)
.def(long() * self)
.def(double() * self)
.def(self / self)
.def(self / other<balance_pair_t>())
.def(self / other<balance_t>())
.def(self / other<amount_t>())
.def(self / long())
.def(self / double())
.def(other<balance_pair_t>() / self)
.def(other<balance_t>() / self)
.def(other<amount_t>() / self)
.def(long() / self)
.def(double() / self)
.def(- self)
.def(self += self)
.def(self += other<balance_pair_t>())
.def(self += other<balance_t>())
.def(self += other<amount_t>())
.def(self += long())
.def(self += double())
.def(self -= self)
.def(self -= other<balance_pair_t>())
.def(self -= other<balance_t>())
.def(self -= other<amount_t>())
.def(self -= long())
.def(self -= double())
.def(self *= self)
.def(self *= other<balance_pair_t>())
.def(self *= other<balance_t>())
.def(self *= other<amount_t>())
.def(self *= long())
.def(self *= double())
.def(self /= self)
.def(self /= other<balance_pair_t>())
.def(self /= other<balance_t>())
.def(self /= other<amount_t>())
.def(self /= long())
.def(self /= double())
.def(self < self)
.def(self < other<balance_pair_t>())
.def(self < other<balance_t>())
.def(self < other<amount_t>())
.def(self < long())
.def(self < double())
.def(other<balance_pair_t>() < self)
.def(other<balance_t>() < self)
.def(other<amount_t>() < self)
.def(long() < self)
.def(double() < self)
.def(self <= self)
.def(self <= other<balance_pair_t>())
.def(self <= other<balance_t>())
.def(self <= other<amount_t>())
.def(self <= long())
.def(self <= double())
.def(other<balance_pair_t>() <= self)
.def(other<balance_t>() <= self)
.def(other<amount_t>() <= self)
.def(long() <= self)
.def(double() <= self)
.def(self > self)
.def(self > other<balance_pair_t>())
.def(self > other<balance_t>())
.def(self > other<amount_t>())
.def(self > long())
.def(self > double())
.def(other<balance_pair_t>() > self)
.def(other<balance_t>() > self)
.def(other<amount_t>() > self)
.def(long() > self)
.def(double() > self)
.def(self >= self)
.def(self >= other<balance_pair_t>())
.def(self >= other<balance_t>())
.def(self >= other<amount_t>())
.def(self >= long())
.def(self >= double())
.def(other<balance_pair_t>() >= self)
.def(other<balance_t>() >= self)
.def(other<amount_t>() >= self)
.def(long() >= self)
.def(double() >= self)
.def(self == self)
.def(self == other<balance_pair_t>())
.def(self == other<balance_t>())
.def(self == other<amount_t>())
.def(self == long())
.def(self == double())
.def(other<balance_pair_t>() == self)
.def(other<balance_t>() == self)
.def(other<amount_t>() == self)
.def(long() == self)
.def(double() == self)
.def(self != self)
.def(self != other<balance_pair_t>())
.def(self != other<balance_t>())
.def(self != other<amount_t>())
.def(self != long())
.def(self != double())
.def(other<balance_pair_t>() != self)
.def(other<balance_t>() != self)
.def(other<amount_t>() != self)
.def(long() != self)
.def(double() != self)
.def(! self)
.def(self_ns::int_(self))
.def(self_ns::float_(self))
.def(self_ns::str(self))
.def(abs(self))
.def_readonly("type", &value_t::type)
.def("__len__", value_len)
.def("__getitem__", value_getitem)
.def("cast", &value_t::cast)
.def("negate", &value_t::negate)
.def("cost", &value_t::cost)
.def("add", &value_t::add, return_internal_reference<1>())
;
enum_< value_t::type_t > ("ValueType")
.value("BOOLEAN", value_t::BOOLEAN)
.value("INTEGER", value_t::INTEGER)
.value("AMOUNT", value_t::AMOUNT)
.value("BALANCE", value_t::BALANCE)
.value("BALANCE_PAIR", value_t::BALANCE_PAIR)
;
}
#endif // USE_BOOST_PYTHON

305
walk.cc
View file

@ -843,308 +843,3 @@ void walk_commodities(commodities_map& commodities,
}
} // namespace ledger
#ifdef USE_BOOST_PYTHON
#include <boost/python.hpp>
using namespace boost::python;
using namespace ledger;
template <typename T>
struct item_handler_wrap : public item_handler<T>
{
PyObject * self;
item_handler_wrap(PyObject * self_) : self(self_) {}
item_handler_wrap(PyObject * self_, const item_handler<T>& _handler)
: item_handler<T>(const_cast<item_handler<T> *>(&_handler)),
self(self_) {}
item_handler_wrap(PyObject * self_, item_handler<T> * _handler)
: item_handler<T>(_handler), self(self_) {}
virtual void flush() {
call_method<void>(self, "flush");
}
void default_flush() {
item_handler<T>::flush();
}
virtual void operator()(T& item) {
call_method<void>(self, "__call__", ptr(&item));
}
void default_call(T& item) {
item_handler<T>::operator()(item);
}
};
void (subtotal_transactions::*subtotal_transactions_flush)() =
&subtotal_transactions::flush;
void py_walk_entries(journal_t& journal,
item_handler<transaction_t>& handler) {
walk_entries(journal.entries, handler);
}
void py_walk_transactions(entry_t& entry,
item_handler<transaction_t>& handler) {
walk_transactions(entry.transactions, handler);
}
void py_walk_accounts_1(account_t& account,
item_handler<account_t>& handler) {
walk_accounts(account, handler);
}
void py_walk_accounts_2(account_t& account,
item_handler<account_t>& handler,
const value_expr_t * sort_order) {
walk_accounts(account, handler, sort_order);
}
void py_walk_accounts_3(account_t& account,
item_handler<account_t>& handler,
const std::string& sort_string) {
walk_accounts(account, handler, sort_string);
}
void py_walk_commodities(item_handler<transaction_t>& handler) {
walk_commodities(commodity_t::commodities, handler);
}
void py_add_period_entries(generate_transactions& handler,
journal_t * journal) {
handler.add_period_entries(journal->period_entries);
}
void export_walk()
{
typedef item_handler<transaction_t> xact_handler_t;
scope().attr("TRANSACTION_RECEIVED") = TRANSACTION_RECEIVED;
scope().attr("TRANSACTION_HANDLED") = TRANSACTION_HANDLED;
scope().attr("TRANSACTION_TO_DISPLAY") = TRANSACTION_TO_DISPLAY;
scope().attr("TRANSACTION_DISPLAYED") = TRANSACTION_DISPLAYED;
scope().attr("TRANSACTION_NO_TOTAL") = TRANSACTION_NO_TOTAL;
scope().attr("TRANSACTION_SORT_CALC") = TRANSACTION_SORT_CALC;
scope().attr("TRANSACTION_COMPOSITE") = TRANSACTION_COMPOSITE;
scope().attr("TRANSACTION_MATCHES") = TRANSACTION_MATCHES;
class_< transaction_xdata_t > ("TransactionXData")
.def_readwrite("total", &transaction_xdata_t::total)
.def_readwrite("sort_value", &transaction_xdata_t::sort_value)
.def_readwrite("composite_amount", &transaction_xdata_t::composite_amount)
.def_readwrite("index", &transaction_xdata_t::index)
.def_readwrite("dflags", &transaction_xdata_t::dflags)
;
def("transaction_has_xdata", transaction_has_xdata);
def("transaction_xdata", transaction_xdata, return_internal_reference<1>());
def("add_transaction_to", add_transaction_to);
class_< xact_handler_t, item_handler_wrap<transaction_t> >
("TransactionHandler")
.def(init<xact_handler_t *>()[with_custodian_and_ward<1, 2>()])
.def("flush", &xact_handler_t::flush,
&item_handler_wrap<transaction_t>::default_flush)
.def("__call__", &xact_handler_t::operator(),
&item_handler_wrap<transaction_t>::default_call)
;
class_< ignore_transactions, bases<xact_handler_t> >
("IgnoreTransactions")
.def("flush", &xact_handler_t::flush)
.def("__call__", &ignore_transactions::operator());
;
class_< clear_transaction_xdata, bases<xact_handler_t> >
("ClearTransactionXData")
.def("flush", &xact_handler_t::flush)
.def("__call__", &clear_transaction_xdata::operator());
;
class_< truncate_entries, bases<xact_handler_t> >
("TruncateEntries", init<xact_handler_t *, int, int>()
[with_custodian_and_ward<1, 2>()])
.def("flush", &truncate_entries::flush)
.def("__call__", &truncate_entries::operator());
;
class_< set_account_value, bases<xact_handler_t> > ("SetAccountValue")
.def(init<xact_handler_t *>()[with_custodian_and_ward<1, 2>()])
.def("flush", &xact_handler_t::flush)
.def("__call__", &set_account_value::operator());
;
class_< sort_transactions, bases<xact_handler_t> >
("SortTransactions", init<xact_handler_t *, const value_expr_t *>()
[with_custodian_and_ward<1, 2>()])
.def(init<xact_handler_t *, const std::string&>()
[with_custodian_and_ward<1, 2>()])
.def("flush", &sort_transactions::flush)
.def("__call__", &sort_transactions::operator());
;
class_< filter_transactions, bases<xact_handler_t> >
("FilterTransactions", init<xact_handler_t *, std::string>()
[with_custodian_and_ward<1, 2>()])
.def(init<xact_handler_t *, const std::string&>()
[with_custodian_and_ward<1, 2>()])
.def("flush", &xact_handler_t::flush)
.def("__call__", &filter_transactions::operator());
;
class_< calc_transactions, bases<xact_handler_t> >
("CalcTransactions", init<xact_handler_t *>()
[with_custodian_and_ward<1, 2>()])
.def("flush", &xact_handler_t::flush)
.def("__call__", &calc_transactions::operator());
;
class_< invert_transactions, bases<xact_handler_t> >
("InvertTransactions", init<xact_handler_t *>()
[with_custodian_and_ward<1, 2>()])
.def("flush", &xact_handler_t::flush)
.def("__call__", &invert_transactions::operator());
;
class_< collapse_transactions, bases<xact_handler_t> >
("CollapseTransactions", init<xact_handler_t *>()
[with_custodian_and_ward<1, 2>()])
.def("flush", &collapse_transactions::flush)
.def("__call__", &collapse_transactions::operator());
;
class_< related_transactions, bases<xact_handler_t> >
("RelatedTransactions", init<xact_handler_t *, optional<bool> >()
[with_custodian_and_ward<1, 2>()])
.def("flush", &xact_handler_t::flush)
.def("__call__", &related_transactions::operator());
;
class_< changed_value_transactions, bases<xact_handler_t> >
("ChangeValueTransactions", init<xact_handler_t *, bool>()
[with_custodian_and_ward<1, 2>()])
.def("flush", &changed_value_transactions::flush)
.def("__call__", &changed_value_transactions::operator());
;
class_< subtotal_transactions, bases<xact_handler_t> >
("SubtotalTransactions", init<xact_handler_t *>()
[with_custodian_and_ward<1, 2>()])
.def("flush", subtotal_transactions_flush)
.def("__call__", &subtotal_transactions::operator());
;
class_< interval_transactions, bases<xact_handler_t> >
("IntervalTransactions",
init<xact_handler_t *, interval_t, optional<value_expr_t *> >()
[with_custodian_and_ward<1, 2>()])
.def(init<xact_handler_t *, const std::string&,
optional<const std::string&> >()
[with_custodian_and_ward<1, 2>()])
.def("flush", &xact_handler_t::flush)
.def("__call__", &interval_transactions::operator());
;
class_< by_payee_transactions, bases<xact_handler_t> >
("ByPayeeTransactions", init<xact_handler_t *>()
[with_custodian_and_ward<1, 2>()])
.def("flush", &by_payee_transactions::flush)
.def("__call__", &by_payee_transactions::operator());
;
class_< set_comm_as_payee, bases<xact_handler_t> >
("SetCommAsPayee", init<xact_handler_t *>()
[with_custodian_and_ward<1, 2>()])
.def("flush", &xact_handler_t::flush)
.def("__call__", &xact_handler_t::operator());
;
class_< dow_transactions, bases<xact_handler_t> >
("DowTransactions", init<xact_handler_t *>()
[with_custodian_and_ward<1, 2>()])
.def("flush", &dow_transactions::flush)
.def("__call__", &dow_transactions::operator());
;
scope().attr("BUDGET_BUDGETED") = BUDGET_BUDGETED;
scope().attr("BUDGET_UNBUDGETED") = BUDGET_UNBUDGETED;
class_< generate_transactions, bases<xact_handler_t> >
("GenerateTransactions", init<xact_handler_t *>()
[with_custodian_and_ward<1, 2>()])
.def("add_transaction", &generate_transactions::add_transaction)
.def("add_period_entries", py_add_period_entries)
.def("flush", &xact_handler_t::flush)
.def("__call__", &xact_handler_t::operator());
;
class_< budget_transactions, bases<generate_transactions> >
("BudgetTransactions",
init<xact_handler_t *, unsigned long>()
[with_custodian_and_ward<1, 2>()])
.def("add_transaction", &generate_transactions::add_transaction)
.def("add_period_entries", py_add_period_entries)
.def("flush", &budget_transactions::flush)
.def("__call__", &xact_handler_t::operator());
;
class_< forecast_transactions, bases<generate_transactions> >
("ForecastTransactions",
init<xact_handler_t *, std::string>()
[with_custodian_and_ward<1, 2>()])
.def("add_transaction", &forecast_transactions::add_transaction)
.def("add_period_entries", py_add_period_entries)
.def("flush", &forecast_transactions::flush)
.def("__call__", &xact_handler_t::operator());
;
def("walk_entries", py_walk_entries);
def("walk_transactions", py_walk_transactions);
typedef item_handler<account_t> account_handler_t;
scope().attr("ACCOUNT_TO_DISPLAY") = ACCOUNT_TO_DISPLAY;
scope().attr("ACCOUNT_DISPLAYED") = ACCOUNT_DISPLAYED;
scope().attr("ACCOUNT_SORT_CALC") = ACCOUNT_SORT_CALC;
class_< account_xdata_t > ("AccountXData")
.def_readwrite("value", &account_xdata_t::value)
.def_readwrite("total", &account_xdata_t::total)
.def_readwrite("sort_value", &account_xdata_t::sort_value)
.def_readwrite("count", &account_xdata_t::count)
.def_readwrite("total_count", &account_xdata_t::total_count)
.def_readwrite("virtuals", &account_xdata_t::virtuals)
.def_readwrite("dflags", &account_xdata_t::dflags)
;
def("account_has_xdata", account_has_xdata);
def("account_xdata", account_xdata, return_internal_reference<1>());
class_< account_handler_t, item_handler_wrap<account_t> >
("AccountHandler")
.def(init<account_handler_t *>()[with_custodian_and_ward<1, 2>()])
.def("flush", &account_handler_t::flush,
&item_handler_wrap<account_t>::default_flush)
.def("__call__", &account_handler_t::operator(),
&item_handler_wrap<account_t>::default_call)
;
class_< clear_account_xdata, bases<account_handler_t> >
("ClearAccountXData")
.def("flush", &account_handler_t::flush)
.def("__call__", &clear_account_xdata::operator());
;
def("sum_accounts", sum_accounts);
def("walk_accounts", py_walk_accounts_1);
def("walk_accounts", py_walk_accounts_2);
def("walk_accounts", py_walk_accounts_3);
def("walk_commodities", py_walk_commodities);
}
#endif // USE_BOOST_PYTHON

30
xml.cc
View file

@ -442,33 +442,3 @@ void format_xml_entries::format_last_entry()
}
} // namespace ledger
#ifdef USE_BOOST_PYTHON
#include <boost/python.hpp>
using namespace boost::python;
using namespace ledger;
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(xml_parse_overloads,
xml_parser_t::parse, 2, 4)
void export_xml() {
class_< xml_parser_t, bases<parser_t> > ("XmlParser")
.def("test", &xml_parser_t::test)
.def("parse", &xml_parser_t::parse, xml_parse_overloads())
;
typedef
pystream_handler_wrap<format_xml_entries, transaction_t, bool>
format_xml_entries_wrap;
class_< format_xml_entries_wrap, bases<item_handler<transaction_t> > >
("FormatXmlEntries",
init<PyObject *, bool>()[with_custodian_and_ward<1, 2>()])
.def("flush", &format_xml_entries_wrap::flush)
.def("__call__", &format_xml_entries_wrap::operator())
;
}
#endif // USE_BOOST_PYTHON