summaryrefslogtreecommitdiff
path: root/src/python
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2007-05-21 20:42:05 +0000
committerJohn Wiegley <johnw@newartisans.com>2008-04-13 03:39:06 -0400
commit7380da43ab403dacb41d2010093d11942bb7cec1 (patch)
tree1b9db99b018695254584fe9f8b9ca34a4aa073cb /src/python
parentf12d41f233d460bd6d2eb8efb90bf6e36e994a30 (diff)
downloadfork-ledger-7380da43ab403dacb41d2010093d11942bb7cec1.tar.gz
fork-ledger-7380da43ab403dacb41d2010093d11942bb7cec1.tar.bz2
fork-ledger-7380da43ab403dacb41d2010093d11942bb7cec1.zip
Many changes.
Diffstat (limited to 'src/python')
-rw-r--r--src/python/py_amount.cc320
-rw-r--r--src/python/py_commodity.cc63
-rw-r--r--src/python/py_times.cc132
-rw-r--r--src/python/py_utils.cc172
-rw-r--r--src/python/pyfstream.h169
-rw-r--r--src/python/pyinterp.cc238
-rw-r--r--src/python/pyinterp.h98
-rw-r--r--src/python/pyledger.cc46
-rw-r--r--src/python/pyledger.h47
-rw-r--r--src/python/pyutils.h112
-rw-r--r--src/python/tuples.hpp281
11 files changed, 1678 insertions, 0 deletions
diff --git a/src/python/py_amount.cc b/src/python/py_amount.cc
new file mode 100644
index 00000000..07be1512
--- /dev/null
+++ b/src/python/py_amount.cc
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pyinterp.h"
+#include "pyutils.h"
+#include "amount.h"
+
+#include <boost/python/exception_translator.hpp>
+#include <boost/python/implicit.hpp>
+#include <boost/python/args.hpp>
+
+namespace ledger {
+
+using namespace boost::python;
+
+amount_t py_round_0(const amount_t& amount) {
+ return amount.round();
+}
+amount_t py_round_1(const amount_t& amount, amount_t::precision_t prec) {
+ return amount.round(prec);
+}
+
+double py_to_double_0(amount_t& amount) {
+ return amount.to_double();
+}
+double py_to_double_1(amount_t& amount, bool no_check) {
+ return amount.to_double(no_check);
+}
+
+long py_to_long_0(amount_t& amount) {
+ return amount.to_long();
+}
+long py_to_long_1(amount_t& amount, bool no_check) {
+ return amount.to_long(no_check);
+}
+
+boost::optional<amount_t> py_value_0(const amount_t& amount) {
+ return amount.value();
+}
+boost::optional<amount_t> py_value_1(const amount_t& amount,
+ const boost::optional<moment_t>& moment) {
+ return amount.value(moment);
+}
+
+void py_parse_2(amount_t& amount, object in, unsigned char flags) {
+ if (PyFile_Check(in.ptr())) {
+ pyifstream instr(reinterpret_cast<PyFileObject *>(in.ptr()));
+ amount.parse(instr, flags);
+ } else {
+ PyErr_SetString(PyExc_IOError,
+ "Argument to amount.parse(file) is not a file object");
+ }
+}
+void py_parse_1(amount_t& amount, object in) {
+ py_parse_2(amount, in, 0);
+}
+
+void py_parse_str_1(amount_t& amount, const string& str) {
+ amount.parse(str);
+}
+void py_parse_str_2(amount_t& amount, const string& str, unsigned char flags) {
+ amount.parse(str, flags);
+}
+
+void py_read_1(amount_t& amount, object in) {
+ if (PyFile_Check(in.ptr())) {
+ pyifstream instr(reinterpret_cast<PyFileObject *>(in.ptr()));
+ amount.read(instr);
+ } else {
+ PyErr_SetString(PyExc_IOError,
+ "Argument to amount.parse(file) is not a file object");
+ }
+}
+void py_read_2(amount_t& amount, const std::string& str) {
+ const char * p = str.c_str();
+ amount.read(p);
+}
+
+#define EXC_TRANSLATOR(type) \
+ void exc_translate_ ## type(const type& err) { \
+ PyErr_SetString(PyExc_ArithmeticError, 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")
+#if 0
+ .def("initialize", &amount_t::initialize)
+ .staticmethod("initialize")
+ .def("shutdown", &amount_t::shutdown)
+ .staticmethod("shutdown")
+#endif
+
+ .add_static_property("current_pool",
+ make_getter(&amount_t::current_pool,
+ return_value_policy<reference_existing_object>()))
+
+ .add_static_property("keep_base", &amount_t::keep_base)
+
+ .add_static_property("keep_price", &amount_t::keep_price)
+ .add_static_property("keep_date", &amount_t::keep_date)
+ .add_static_property("keep_tag", &amount_t::keep_tag)
+
+ .add_static_property("stream_fullstrings", &amount_t::stream_fullstrings)
+
+ .def(init<double>())
+ .def(init<long>())
+ .def(init<std::string>())
+
+ .def("exact", &amount_t::exact, args("value"),
+ "Construct an amount object whose display precision is always equal to its\n\
+internal precision.")
+ .staticmethod("exact")
+
+ .def(init<amount_t>())
+
+ .def("compare", &amount_t::compare)
+
+ .def(self == self)
+ .def(self == long())
+ .def(long() == self)
+ .def(self == double())
+ .def(double() == self)
+
+ .def(self != self)
+ .def(self != long())
+ .def(long() != self)
+ .def(self != double())
+ .def(double() != self)
+
+ .def(! self)
+
+ .def(self < self)
+ .def(self < long())
+ .def(long() < self)
+ .def(self < double())
+ .def(double() < self)
+
+ .def(self <= self)
+ .def(self <= long())
+ .def(long() <= self)
+ .def(self <= double())
+ .def(double() <= self)
+
+ .def(self > self)
+ .def(self > long())
+ .def(long() > self)
+ .def(self > double())
+ .def(double() > self)
+
+ .def(self >= self)
+ .def(self >= long())
+ .def(long() >= self)
+ .def(self >= double())
+ .def(double() >= self)
+
+ .def(self += self)
+ .def(self += long())
+ .def(self += double())
+
+ .def(self + self)
+ .def(self + long())
+ .def(long() + self)
+ .def(self + double())
+ .def(double() + self)
+
+ .def(self -= self)
+ .def(self -= long())
+ .def(self -= double())
+
+ .def(self - self)
+ .def(self - long())
+ .def(long() - self)
+ .def(self - double())
+ .def(double() - self)
+
+ .def(self *= self)
+ .def(self *= long())
+ .def(self *= double())
+
+ .def(self * self)
+ .def(self * long())
+ .def(long() * self)
+ .def(self * double())
+ .def(double() * self)
+
+ .def(self /= self)
+ .def(self /= long())
+ .def(self /= double())
+
+ .def(self / self)
+ .def(self / long())
+ .def(long() / self)
+ .def(self / double())
+ .def(double() / self)
+
+ .add_property("precision", &amount_t::precision)
+
+ .def("negate", &amount_t::negate)
+ .def("in_place_negate", &amount_t::in_place_negate,
+ return_value_policy<reference_existing_object>())
+ .def(- self)
+
+ .def("abs", &amount_t::abs)
+ .def("__abs__", &amount_t::abs)
+
+ .def("round", py_round_0)
+ .def("round", py_round_1)
+ .def("unround", &amount_t::unround)
+
+ .def("reduce", &amount_t::reduce)
+ .def("in_place_reduce", &amount_t::in_place_reduce,
+ return_value_policy<reference_existing_object>())
+
+ .def("unreduce", &amount_t::unreduce)
+ .def("in_place_unreduce", &amount_t::in_place_unreduce,
+ return_value_policy<reference_existing_object>())
+
+ .def("value", py_value_0)
+ .def("value", py_value_1)
+
+ .def("sign", &amount_t::sign)
+ .def("__nonzero__", &amount_t::is_nonzero)
+ .def("is_nonzero", &amount_t::is_nonzero)
+ .def("is_zero", &amount_t::is_zero)
+ .def("is_realzero", &amount_t::is_realzero)
+ .def("is_null", &amount_t::is_null)
+
+ .def("to_double", py_to_double_0)
+ .def("to_double", py_to_double_1)
+ .def("__float__", py_to_double_0)
+ .def("to_long", py_to_long_0)
+ .def("to_long", py_to_long_1)
+ .def("__int__", py_to_long_0)
+ .def("to_string", &amount_t::to_string)
+ .def("__str__", &amount_t::to_string)
+ .def("to_fullstring", &amount_t::to_fullstring)
+ .def("__repr__", &amount_t::to_fullstring)
+
+ .def("fits_in_double", &amount_t::fits_in_double)
+ .def("fits_in_long", &amount_t::fits_in_long)
+
+ .add_property("quantity_string", &amount_t::quantity_string)
+
+ .add_property("commodity",
+ make_function(&amount_t::commodity,
+ return_value_policy<reference_existing_object>()),
+ make_function(&amount_t::set_commodity,
+ with_custodian_and_ward<1, 2>()))
+
+ .def("has_commodity", &amount_t::has_commodity)
+ .def("clear_commodity", &amount_t::clear_commodity)
+ .add_property("number", &amount_t::number)
+
+ .def("annotate_commodity", &amount_t::annotate_commodity)
+ .def("commodity_annotated", &amount_t::commodity_annotated)
+ .add_property("annotation_details", &amount_t::annotation_details)
+ .def("strip_annotations", &amount_t::strip_annotations)
+
+ .def("parse", py_parse_1)
+ .def("parse", py_parse_2)
+ .def("parse", py_parse_str_1)
+ .def("parse", py_parse_str_2)
+
+ .def("parse_conversion", &amount_t::parse_conversion)
+ .staticmethod("parse_conversion")
+
+ .def("read", py_read_1)
+ .def("read", py_read_2)
+ .def("write", &amount_t::write)
+
+ .def("valid", &amount_t::valid)
+ ;
+
+ register_optional_to_python<amount_t>();
+
+ implicitly_convertible<double, amount_t>();
+ implicitly_convertible<long, amount_t>();
+ implicitly_convertible<string, amount_t>();
+
+#define EXC_TRANSLATE(type) \
+ register_exception_translator<type>(&exc_translate_ ## type);
+
+ EXC_TRANSLATE(amount_error);
+}
+
+} // namespace ledger
diff --git a/src/python/py_commodity.cc b/src/python/py_commodity.cc
new file mode 100644
index 00000000..f857a448
--- /dev/null
+++ b/src/python/py_commodity.cc
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pyinterp.h"
+#include "pyutils.h"
+#include "amount.h"
+
+#include <boost/python/exception_translator.hpp>
+#include <boost/python/implicit.hpp>
+
+namespace ledger {
+
+using namespace boost::python;
+
+void export_commodity()
+{
+ 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_BUILTIN") = COMMODITY_STYLE_BUILTIN;
+
+ class_< commodity_t, bases<>,
+ commodity_t, boost::noncopyable > ("commodity", no_init)
+ .def(self == self)
+
+ .def("drop_flags", &commodity_t::drop_flags)
+
+ .add_property("precision", &commodity_t::precision)
+ ;
+}
+
+} // namespace ledger
diff --git a/src/python/py_times.cc b/src/python/py_times.cc
new file mode 100644
index 00000000..f7672dcc
--- /dev/null
+++ b/src/python/py_times.cc
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pyinterp.h"
+#include "pyutils.h"
+
+#include <boost/cast.hpp>
+#include <boost/python/module.hpp>
+#include <boost/python/def.hpp>
+#include <boost/python/to_python_converter.hpp>
+
+#include <Python.h>
+#include <datetime.h>
+
+// jww (2007-05-04): Convert time duration objects to PyDelta
+
+namespace ledger {
+
+using namespace boost::python;
+
+typedef boost::gregorian::date date;
+
+struct date_to_python
+{
+ static PyObject* convert(const date& dte)
+ {
+ PyDateTime_IMPORT;
+ return PyDate_FromDate(dte.year(), dte.month(), dte.day());
+ }
+};
+
+struct date_from_python
+{
+ static void* convertible(PyObject* obj_ptr)
+ {
+ PyDateTime_IMPORT;
+ if (PyDate_Check(obj_ptr)) return obj_ptr;
+ return 0;
+ }
+
+ static void construct(PyObject* obj_ptr, converter::rvalue_from_python_stage1_data* data)
+ {
+ PyDateTime_IMPORT;
+ int y = PyDateTime_GET_YEAR(obj_ptr);
+ int m = PyDateTime_GET_MONTH(obj_ptr);
+ int d = PyDateTime_GET_DAY(obj_ptr);
+ date* dte = new date(y,m,d);
+ data->convertible = (void*)dte;
+ }
+};
+
+typedef register_python_conversion<date, date_to_python, date_from_python>
+ date_python_conversion;
+
+
+struct datetime_to_python
+{
+ static PyObject* convert(const moment_t& moment)
+ {
+ PyDateTime_IMPORT;
+ date dte = moment.date();
+ moment_t::time_duration_type tod = moment.time_of_day();
+ return PyDateTime_FromDateAndTime(dte.year(), dte.month(), dte.day(),
+ tod.hours(), tod.minutes(), tod.seconds(),
+ tod.total_microseconds() % 1000000);
+ }
+};
+
+struct datetime_from_python
+{
+ static void* convertible(PyObject* obj_ptr)
+ {
+ PyDateTime_IMPORT;
+ if(PyDateTime_Check(obj_ptr)) return obj_ptr;
+ return 0;
+ }
+
+ static void construct(PyObject* obj_ptr, converter::rvalue_from_python_stage1_data* data)
+ {
+ PyDateTime_IMPORT;
+ int y = PyDateTime_GET_YEAR(obj_ptr);
+ int m = PyDateTime_GET_MONTH(obj_ptr);
+ int d = PyDateTime_GET_DAY(obj_ptr);
+ int h = PyDateTime_DATE_GET_HOUR(obj_ptr);
+ int min = PyDateTime_DATE_GET_MINUTE(obj_ptr);
+ int s = PyDateTime_DATE_GET_SECOND(obj_ptr);
+ moment_t* moment = new moment_t(date(y,m,d),
+ moment_t::time_duration_type(h, min, s));
+ data->convertible = (void*)moment;
+ }
+};
+
+typedef register_python_conversion<moment_t, datetime_to_python, datetime_from_python>
+ datetime_python_conversion;
+
+void export_times()
+{
+ date_python_conversion();
+ datetime_python_conversion();
+
+ register_optional_to_python<moment_t>();
+}
+
+} // namespace ledger
diff --git a/src/python/py_utils.cc b/src/python/py_utils.cc
new file mode 100644
index 00000000..73961d43
--- /dev/null
+++ b/src/python/py_utils.cc
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pyinterp.h"
+#include "pyutils.h"
+
+#include <boost/python/module.hpp>
+#include <boost/python/def.hpp>
+#include <boost/python/to_python_converter.hpp>
+
+namespace ledger {
+
+using namespace boost::python;
+
+struct bool_to_python
+{
+ static PyObject * convert(const bool truth)
+ {
+ if (truth)
+ Py_RETURN_TRUE;
+ else
+ Py_RETURN_FALSE;
+ }
+};
+
+struct bool_from_python
+{
+ static void* convertible(PyObject* obj_ptr)
+ {
+ if (!PyBool_Check(obj_ptr)) return 0;
+ return obj_ptr;
+ }
+
+ static void construct(PyObject* obj_ptr,
+ converter::rvalue_from_python_stage1_data* data)
+ {
+ void* storage = ((converter::rvalue_from_python_storage<bool>*) data)->storage.bytes;
+ if (obj_ptr == Py_True)
+ new (storage) bool(true);
+ else
+ new (storage) bool(false);
+ data->convertible = storage;
+ }
+};
+
+typedef register_python_conversion<bool, bool_to_python, bool_from_python>
+ bool_python_conversion;
+
+
+struct string_to_python
+{
+ static PyObject* convert(const string& str)
+ {
+ return incref(object(*boost::polymorphic_downcast<const std::string *>(&str)).ptr());
+ }
+};
+
+struct string_from_python
+{
+ static void* convertible(PyObject* obj_ptr)
+ {
+ if (!PyString_Check(obj_ptr)) return 0;
+ return obj_ptr;
+ }
+
+ static void construct(PyObject* obj_ptr, converter::rvalue_from_python_stage1_data* data)
+ {
+ const char* value = PyString_AsString(obj_ptr);
+ if (value == 0) throw_error_already_set();
+ void* storage = ((converter::rvalue_from_python_storage<string>*) data)->storage.bytes;
+ new (storage) string(value);
+ data->convertible = storage;
+ }
+};
+
+typedef register_python_conversion<string, string_to_python, string_from_python>
+ string_python_conversion;
+
+
+struct istream_to_python
+{
+ static PyObject* convert(const std::istream& str)
+ {
+ return incref(boost::python::detail::none());
+ }
+};
+
+struct istream_from_python
+{
+ static void* convertible(PyObject* obj_ptr)
+ {
+ if (!PyFile_Check(obj_ptr)) return 0;
+ return obj_ptr;
+ }
+
+ static void construct(PyObject* obj_ptr, converter::rvalue_from_python_stage1_data* data)
+ {
+ void* storage = ((converter::rvalue_from_python_storage<pyifstream>*) data)->storage.bytes;
+ new (storage) pyifstream(reinterpret_cast<PyFileObject *>(obj_ptr));
+ data->convertible = storage;
+ }
+};
+
+typedef register_python_conversion<std::istream, istream_to_python, istream_from_python>
+ istream_python_conversion;
+
+
+struct ostream_to_python
+{
+ static PyObject* convert(const std::ostream& str)
+ {
+ return incref(boost::python::detail::none());
+ }
+};
+
+struct ostream_from_python
+{
+ static void* convertible(PyObject* obj_ptr)
+ {
+ if (!PyFile_Check(obj_ptr)) return 0;
+ return obj_ptr;
+ }
+
+ static void construct(PyObject* obj_ptr, converter::rvalue_from_python_stage1_data* data)
+ {
+ void* storage = ((converter::rvalue_from_python_storage<pyofstream>*) data)->storage.bytes;
+ new (storage) pyofstream(reinterpret_cast<PyFileObject *>(obj_ptr));
+ data->convertible = storage;
+ }
+};
+
+typedef register_python_conversion<std::ostream, ostream_to_python, ostream_from_python>
+ ostream_python_conversion;
+
+
+void export_utils()
+{
+ bool_python_conversion();
+ string_python_conversion();
+ istream_python_conversion();
+ ostream_python_conversion();
+}
+
+} // namespace ledger
diff --git a/src/python/pyfstream.h b/src/python/pyfstream.h
new file mode 100644
index 00000000..f4650e0a
--- /dev/null
+++ b/src/python/pyfstream.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PYFSTREAM_H
+#define _PYFSTREAM_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
diff --git a/src/python/pyinterp.cc b/src/python/pyinterp.cc
new file mode 100644
index 00000000..d521a0ee
--- /dev/null
+++ b/src/python/pyinterp.cc
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "pyinterp.h"
+
+#include <boost/python/module_init.hpp>
+
+namespace ledger {
+
+using namespace boost::python;
+
+void export_utils();
+void export_times();
+void export_amount();
+void export_commodity();
+#if 0
+void export_balance();
+void export_value();
+void export_journal();
+void export_parser();
+void export_option();
+void export_walk();
+void export_report();
+void export_format();
+void export_valexpr();
+#endif
+
+void initialize_for_python()
+{
+ export_utils();
+ export_times();
+ export_amount();
+ export_commodity();
+#if 0
+ export_balance();
+ export_value();
+ export_journal();
+ export_parser();
+ export_option();
+ export_walk();
+ export_format();
+ export_report();
+ export_valexpr();
+#endif
+}
+
+struct python_run
+{
+ object result;
+
+ python_run(python_interpreter_t * intepreter,
+ const string& str, int input_mode)
+ : result(handle<>(borrowed(PyRun_String(str.c_str(), input_mode,
+ intepreter->nspace.ptr(),
+ intepreter->nspace.ptr())))) {}
+ operator object() {
+ return result;
+ }
+};
+
+python_interpreter_t::python_interpreter_t(xml::xpath_t::scope_t& parent)
+ : xml::xpath_t::symbol_scope_t(parent),
+ mmodule(borrowed(PyImport_AddModule("__main__"))),
+ nspace(handle<>(borrowed(PyModule_GetDict(mmodule.get()))))
+{
+ Py_Initialize();
+ boost::python::detail::init_module("ledger", &initialize_for_python);
+}
+
+object python_interpreter_t::import(const string& str)
+{
+ assert(Py_IsInitialized());
+
+ try {
+ PyObject * mod = PyImport_Import(PyString_FromString(str.c_str()));
+ if (! mod)
+ throw_(std::logic_error, "Failed to import Python module " << str);
+
+ object newmod(handle<>(borrowed(mod)));
+
+#if 1
+ // Import all top-level entries directly into the main namespace
+ dict m_nspace(handle<>(borrowed(PyModule_GetDict(mod))));
+ nspace.update(m_nspace);
+#else
+ nspace[string(PyModule_GetName(mod))] = newmod;
+#endif
+ return newmod;
+ }
+ catch (const error_already_set&) {
+ PyErr_Print();
+ throw_(std::logic_error, "Importing Python module " << str);
+ }
+ return object();
+}
+
+object python_interpreter_t::eval(std::istream& in, py_eval_mode_t mode)
+{
+ bool first = true;
+ 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(this, buffer, input_mode);
+ }
+ catch (const error_already_set&) {
+ PyErr_Print();
+ throw_(std::logic_error, "Evaluating Python code");
+ }
+ return object();
+}
+
+object python_interpreter_t::eval(const string& str, py_eval_mode_t mode)
+{
+ 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(this, str, input_mode);
+ }
+ catch (const error_already_set&) {
+ PyErr_Print();
+ throw_(std::logic_error, "Evaluating Python code");
+ }
+ return object();
+}
+
+value_t python_interpreter_t::functor_t::operator()
+ (xml::xpath_t::call_scope_t& args)
+{
+ try {
+ if (! PyCallable_Check(func.ptr())) {
+ return extract<value_t>(func.ptr());
+ } else {
+ if (args.size() > 0) {
+ list arglist;
+ if (args.value().is_sequence())
+ foreach (const value_t& value, args.value().as_sequence())
+ arglist.append(value);
+ else
+ arglist.append(args.value());
+
+ if (PyObject * val =
+ PyObject_CallObject(func.ptr(),
+ boost::python::tuple(arglist).ptr())) {
+ value_t result = extract<value_t>(val)();
+ Py_DECREF(val);
+ return result;
+ }
+ else if (PyObject * err = PyErr_Occurred()) {
+ PyErr_Print();
+ throw_(xml::xpath_t::calc_error,
+ "While calling Python function '" /*<< name() <<*/ "': " << err);
+ } else {
+ assert(false);
+ }
+ } else {
+ return call<value_t>(func.ptr());
+ }
+ }
+ }
+ catch (const error_already_set&) {
+ PyErr_Print();
+ throw_(xml::xpath_t::calc_error,
+ "While calling Python function '" /*<< name() <<*/ "'");
+ }
+ return NULL_VALUE;
+}
+
+value_t python_interpreter_t::lambda_t::operator()
+ (xml::xpath_t::call_scope_t& args)
+{
+ try {
+ assert(args.size() == 1);
+ value_t item = args[0];
+ assert(item.is_xml_node());
+ return call<value_t>(func.ptr(), item.as_xml_node());
+ }
+ catch (const error_already_set&) {
+ PyErr_Print();
+ throw_(xml::xpath_t::calc_error,
+ "While evaluating Python lambda expression");
+ }
+ return NULL_VALUE;
+}
+
+} // namespace ledger
diff --git a/src/python/pyinterp.h b/src/python/pyinterp.h
new file mode 100644
index 00000000..3d69d972
--- /dev/null
+++ b/src/python/pyinterp.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PYINTERP_H
+#define _PYINTERP_H
+
+#include "xpath.h"
+
+#include <boost/python.hpp>
+#include <Python.h>
+
+namespace ledger {
+
+class python_interpreter_t : public xml::xpath_t::symbol_scope_t
+{
+ boost::python::handle<> mmodule;
+
+ public:
+ boost::python::dict nspace;
+
+ python_interpreter_t(xml::xpath_t::scope_t& parent);
+
+ virtual ~python_interpreter_t() {
+ Py_Finalize();
+ }
+
+ boost::python::object import(const string& name);
+
+ enum py_eval_mode_t {
+ PY_EVAL_EXPR,
+ PY_EVAL_STMT,
+ PY_EVAL_MULTI
+ };
+
+ boost::python::object eval(std::istream& in,
+ py_eval_mode_t mode = PY_EVAL_EXPR);
+ boost::python::object eval(const string& str,
+ py_eval_mode_t mode = PY_EVAL_EXPR);
+ boost::python::object eval(const char * c_str,
+ py_eval_mode_t mode = PY_EVAL_EXPR) {
+ string str(c_str);
+ return eval(str, mode);
+ }
+
+ class functor_t {
+ protected:
+ boost::python::object func;
+ public:
+ functor_t(const string& name, boost::python::object _func) : func(_func) {}
+ virtual ~functor_t() {}
+ virtual value_t operator()(xml::xpath_t::call_scope_t& args);
+ };
+
+ virtual xml::xpath_t::ptr_op_t lookup(const string& name) {
+ if (boost::python::object func = eval(name))
+ return WRAP_FUNCTOR(functor_t(name, func));
+ else
+ return xml::xpath_t::symbol_scope_t::lookup(name);
+ }
+
+ class lambda_t : public functor_t {
+ public:
+ lambda_t(boost::python::object code) : functor_t("<lambda>", code) {}
+ virtual value_t operator()(xml::xpath_t::call_scope_t& args);
+ };
+};
+
+} // namespace ledger
+
+#endif // _PYINTERP_H
diff --git a/src/python/pyledger.cc b/src/python/pyledger.cc
new file mode 100644
index 00000000..ebbdc82e
--- /dev/null
+++ b/src/python/pyledger.cc
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <pyledger.h>
+
+using namespace boost::python;
+
+ledger::session_t python_session;
+
+namespace ledger {
+ extern void initialize_for_python();
+}
+
+BOOST_PYTHON_MODULE(ledger)
+{
+ ledger::set_session_context(&python_session);
+ ledger::initialize_for_python();
+}
diff --git a/src/python/pyledger.h b/src/python/pyledger.h
new file mode 100644
index 00000000..3ab82558
--- /dev/null
+++ b/src/python/pyledger.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#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 <pyinterp.h>
+
+#endif // _PYLEDGER_H
diff --git a/src/python/pyutils.h b/src/python/pyutils.h
new file mode 100644
index 00000000..216af8b7
--- /dev/null
+++ b/src/python/pyutils.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _PY_UTILS_H
+#define _PY_UTILS_H
+
+#include "pyfstream.h"
+
+template <typename T, typename TfromPy>
+struct object_from_python
+{
+ object_from_python() {
+ boost::python::converter::registry::push_back
+ (&TfromPy::convertible, &TfromPy::construct,
+ boost::python::type_id<T>());
+ }
+};
+
+template <typename T, typename TtoPy, typename TfromPy>
+struct register_python_conversion
+{
+ register_python_conversion() {
+ boost::python::to_python_converter<T, TtoPy>();
+ object_from_python<T, TfromPy>();
+ }
+};
+
+template <typename T>
+struct register_optional_to_python : public boost::noncopyable
+{
+ struct optional_to_python
+ {
+ static PyObject * convert(const boost::optional<T>& value)
+ {
+ return boost::python::incref
+ (value ? boost::python::to_python_value<T>()(*value) :
+ boost::python::detail::none());
+ }
+ };
+
+ struct optional_from_python
+ {
+ static void * convertible(PyObject * source)
+ {
+ using namespace boost::python::converter;
+
+ if (source == Py_None)
+ return source;
+
+ const registration& converters(registered<T>::converters);
+
+ if (implicit_rvalue_convertible_from_python(source, converters)) {
+ rvalue_from_python_stage1_data data =
+ rvalue_from_python_stage1(source, converters);
+ return rvalue_from_python_stage2(source, data, converters);
+ }
+ return NULL;
+ }
+
+ static void construct(PyObject * source,
+ boost::python::converter::rvalue_from_python_stage1_data * data)
+ {
+ using namespace boost::python::converter;
+
+ void * const storage = ((rvalue_from_python_storage<T> *) data)->storage.bytes;
+
+ if (data->convertible == source) // == None
+ new (storage) boost::optional<T>(); // A Boost uninitialized value
+ else
+ new (storage) boost::optional<T>(*static_cast<T *>(data->convertible));
+
+ data->convertible = storage;
+ }
+ };
+
+ explicit register_optional_to_python() {
+ register_python_conversion<boost::optional<T>,
+ optional_to_python, optional_from_python>();
+ }
+};
+
+//boost::python::register_ptr_to_python< boost::shared_ptr<Base> >();
+
+#endif // _PY_UTILS_H
diff --git a/src/python/tuples.hpp b/src/python/tuples.hpp
new file mode 100644
index 00000000..18cbdb83
--- /dev/null
+++ b/src/python/tuples.hpp
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * - Neither the name of New Artisans LLC nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// Copyright 2004-2007 Roman Yakovenko.
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef TUPLES_HPP_16_JAN_2007
+#define TUPLES_HPP_16_JAN_2007
+
+#include "boost/python.hpp"
+#include "boost/tuple/tuple.hpp"
+#include "boost/python/object.hpp" //len function
+#include <boost/mpl/int.hpp>
+#include <boost/mpl/next.hpp>
+
+/**
+ * Converts boost::tuples::tuple<...> to\from Python tuple
+ *
+ * The conversion is done "on-the-fly", you should only register the conversion
+ * with your tuple classes.
+ * For example:
+ *
+ * typedef boost::tuples::tuple< int, double, std::string > triplet;
+ * boost::python::register_tuple< triplet >();
+ *
+ * That's all. After this point conversion to\from next types will be handled
+ * by Boost.Python library:
+ *
+ * triplet
+ * triplet& ( return type only )
+ * const triplet
+ * const triplet&
+ *
+ * Implementation description.
+ * The conversion uses Boost.Python custom r-value converters. r-value converters
+ * is very powerful and undocumented feature of the library. The only documentation
+ * we have is http://boost.org/libs/python/doc/v2/faq.html#custom_string .
+ *
+ * The conversion consists from two parts: "to" and "from".
+ *
+ * "To" conversion
+ * The "to" part is pretty easy and well documented ( http://docs.python.org/api/api.html ).
+ * You should use Python C API to create an instance of a class and than you
+ * initialize the relevant members of the instance.
+ *
+ * "From" conversion
+ * Lets start from analyzing one of the use case Boost.Python library have to
+ * deal with:
+ *
+ * void do_smth( const triplet& arg ){...}
+ *
+ * In order to allow calling this function from Python, the library should keep
+ * parameter "arg" alive until the function returns. In other words, the library
+ * should provide instances life-time management. The provided interface is not
+ * ideal and could be improved. You have to implement two functions:
+ *
+ * void* convertible( PyObject* obj )
+ * Checks whether the "obj" could be converted to an instance of the desired
+ * class. If true, the function should return "obj", otherwise NULL
+ *
+ * void construct( PyObject* obj, converter::rvalue_from_python_stage1_data* data)
+ * Constructs the instance of the desired class. This function will be called
+ * if and only if "convertible" function returned true. The first argument
+ * is Python object, which was passed as parameter to "convertible" function.
+ * The second object is some kind of memory allocator for one object. Basically
+ * it keeps a memory chunk. You will use the memory for object allocation.
+ *
+ * For some unclear for me reason, the library implements "C style Inheritance"
+ * ( http://www.embedded.com/97/fe29712.htm ). So, in order to create new
+ * object in the storage you have to cast to the "right" class:
+ *
+ * typedef converter::rvalue_from_python_storage<your_type_t> storage_t;
+ * storage_t* the_storage = reinterpret_cast<storage_t*>( data );
+ * void* memory_chunk = the_storage->storage.bytes;
+ *
+ * "memory_chunk" points to the memory, where the instance will be allocated.
+ *
+ * In order to create object at specific location, you should use placement new
+ * operator:
+ *
+ * your_type_t* instance = new (memory_chunk) your_type_t();
+ *
+ * Now, you can continue to initialize the instance.
+ *
+ * instance->set_xyz = read xyz from obj
+ *
+ * If "your_type_t" constructor requires some arguments, "read" the Python
+ * object before you call the constructor:
+ *
+ * xyz_type xyz = read xyz from obj
+ * your_type_t* instance = new (memory_chunk) your_type_t(xyz);
+ *
+ * Hint:
+ * In most case you don't really need\have to work with C Python API. Let
+ * Boost.Python library to do some work for you!
+ *
+ **/
+
+namespace boost{ namespace python{
+
+namespace details{
+
+//Small helper function, introduced to allow short syntax for index incrementing
+template< int index>
+typename mpl::next< mpl::int_< index > >::type increment_index(){
+ typedef typename mpl::next< mpl::int_< index > >::type next_index_type;
+ return next_index_type();
+}
+
+}
+
+template< class TTuple >
+struct to_py_tuple{
+
+ typedef mpl::int_< tuples::length< TTuple >::value > length_type;
+
+ static PyObject* convert(const TTuple& c_tuple){
+ list values;
+ //add all c_tuple items to "values" list
+ convert_impl( c_tuple, values, mpl::int_< 0 >(), length_type() );
+ //create Python tuple from the list
+ return incref( python::tuple( values ).ptr() );
+ }
+
+private:
+
+ template< int index, int length >
+ static void
+ convert_impl( const TTuple &c_tuple, list& values, mpl::int_< index >, mpl::int_< length > ) {
+ values.append( c_tuple.template get< index >() );
+ convert_impl( c_tuple, values, details::increment_index<index>(), length_type() );
+ }
+
+ template< int length >
+ static void
+ convert_impl( const TTuple&, list& values, mpl::int_< length >, mpl::int_< length >)
+ {}
+
+};
+
+
+template< class TTuple>
+struct from_py_sequence{
+
+ typedef TTuple tuple_type;
+
+ typedef mpl::int_< tuples::length< TTuple >::value > length_type;
+
+ static void*
+ convertible(PyObject* py_obj){
+
+ if( !PySequence_Check( py_obj ) ){
+ return 0;
+ }
+
+ if( !PyObject_HasAttrString( py_obj, "__len__" ) ){
+ return 0;
+ }
+
+ python::object py_sequence( handle<>( borrowed( py_obj ) ) );
+
+ if( tuples::length< TTuple >::value != len( py_sequence ) ){
+ return 0;
+ }
+
+ if( convertible_impl( py_sequence, mpl::int_< 0 >(), length_type() ) ){
+ return py_obj;
+ }
+ else{
+ return 0;
+ }
+ }
+
+ static void
+ construct( PyObject* py_obj, converter::rvalue_from_python_stage1_data* data){
+ typedef converter::rvalue_from_python_storage<TTuple> storage_t;
+ storage_t* the_storage = reinterpret_cast<storage_t*>( data );
+ void* memory_chunk = the_storage->storage.bytes;
+ TTuple* c_tuple = new (memory_chunk) TTuple();
+ data->convertible = memory_chunk;
+
+ python::object py_sequence( handle<>( borrowed( py_obj ) ) );
+ construct_impl( py_sequence, *c_tuple, mpl::int_< 0 >(), length_type() );
+ }
+
+ static TTuple to_c_tuple( PyObject* py_obj ){
+ if( !convertible( py_obj ) ){
+ throw std::runtime_error( "Unable to construct boost::tuples::tuple from Python object!" );
+ }
+ TTuple c_tuple;
+ python::object py_sequence( handle<>( borrowed( py_obj ) ) );
+ construct_impl( py_sequence, c_tuple, mpl::int_< 0 >(), length_type() );
+ return c_tuple;
+ }
+
+private:
+
+ template< int index, int length >
+ static bool
+ convertible_impl( const python::object& py_sequence, mpl::int_< index >, mpl::int_< length > ){
+
+ typedef typename tuples::element< index, TTuple>::type element_type;
+
+ object element = py_sequence[index];
+ extract<element_type> type_checker( element );
+ if( !type_checker.check() ){
+ return false;
+ }
+ else{
+ return convertible_impl( py_sequence, details::increment_index<index>(), length_type() );
+ }
+ }
+
+ template< int length >
+ static bool
+ convertible_impl( const python::object& py_sequence, mpl::int_< length >, mpl::int_< length > ){
+ return true;
+ }
+
+ template< int index, int length >
+ static void
+ construct_impl( const python::object& py_sequence, TTuple& c_tuple, mpl::int_< index >, mpl::int_< length > ){
+
+ typedef typename tuples::element< index, TTuple>::type element_type;
+
+ object element = py_sequence[index];
+ c_tuple.template get< index >() = extract<element_type>( element );
+
+ construct_impl( py_sequence, c_tuple, details::increment_index<index>(), length_type() );
+ }
+
+ template< int length >
+ static void
+ construct_impl( const python::object& py_sequence, TTuple& c_tuple, mpl::int_< length >, mpl::int_< length > )
+ {}
+
+};
+
+template< class TTuple>
+void register_tuple(){
+
+ to_python_converter< TTuple, to_py_tuple<TTuple> >();
+
+ converter::registry::push_back( &from_py_sequence<TTuple>::convertible
+ , &from_py_sequence<TTuple>::construct
+ , type_id<TTuple>() );
+};
+
+} } //boost::python
+
+#endif//TUPLES_HPP_16_JAN_2007