Merge branch 'next'

This commit is contained in:
John Wiegley 2009-11-11 14:45:14 -05:00
commit a034435c42
41 changed files with 854 additions and 439 deletions

19
acprep
View file

@ -255,6 +255,9 @@ class PrepareBuild(CommandLineApp):
op.add_option('', '--no-patch', action='store_true', dest='no_patch', op.add_option('', '--no-patch', action='store_true', dest='no_patch',
default=False, default=False,
help='Do not patch the Makefile for prettier output') help='Do not patch the Makefile for prettier output')
op.add_option('', '--no-git', action='store_true', dest='no_git',
default=False,
help='Do not call out to Git; useful for offline builds')
op.add_option('', '--output', metavar='DIR', action="callback", op.add_option('', '--output', metavar='DIR', action="callback",
callback=self.option_output, callback=self.option_output,
help='Build in the specified directory') help='Build in the specified directory')
@ -382,9 +385,12 @@ class PrepareBuild(CommandLineApp):
sys.exit(1) sys.exit(1)
return dirname return dirname
def git_working_tree(self):
return exists('.git') and isdir('.git') and not self.options.no_git
def current_version(self): def current_version(self):
if not self.current_ver: if not self.current_ver:
if exists('.git') and isdir('.git'): if self.git_working_tree():
#date = self.get_stdout('git', 'log', '--format=%ci', '-1', 'HEAD') #date = self.get_stdout('git', 'log', '--format=%ci', '-1', 'HEAD')
#date = re.sub(" [-+][0-9][0-9][0-9][0-9]$", "", date) #date = re.sub(" [-+][0-9][0-9][0-9][0-9]$", "", date)
#when = datetime.datetime.strptime(date, "%Y-%m-%d %H:%M:%S") #when = datetime.datetime.strptime(date, "%Y-%m-%d %H:%M:%S")
@ -394,7 +400,7 @@ class PrepareBuild(CommandLineApp):
tag = self.get_stdout('git', 'describe', '--all', '--long') tag = self.get_stdout('git', 'describe', '--all', '--long')
self.current_ver = re.sub('heads/', '', tag) self.current_ver = re.sub('heads/', '', tag)
else: else:
self.current_ver = "unknown" self.current_ver = "no-git"
return self.current_ver return self.current_ver
def need_to_prepare_autotools(self): def need_to_prepare_autotools(self):
@ -522,15 +528,13 @@ class PrepareBuild(CommandLineApp):
def phase_submodule(self, *args): def phase_submodule(self, *args):
self.log.info('Executing phase: submodule') self.log.info('Executing phase: submodule')
if exists('.git') and isdir('.git'): if self.git_working_tree():
self.execute('git', 'submodule', 'init') self.execute('git', 'submodule', 'init')
self.execute('git', 'submodule', 'update') self.execute('git', 'submodule', 'update')
def phase_pull(self, *args): def phase_pull(self, *args):
self.log.info('Executing phase: pull') self.log.info('Executing phase: pull')
if not exists('.git') and not isdir('.git'): if self.git_working_tree():
self.log.error("This is not a Git clone.")
sys.exit(1)
self.execute('git', 'pull') self.execute('git', 'pull')
self.phase_submodule() self.phase_submodule()
@ -653,7 +657,7 @@ class PrepareBuild(CommandLineApp):
self.log.error('Failed to locate the Boost sources') self.log.error('Failed to locate the Boost sources')
sys.exit(1) sys.exit(1)
if not exists('cppunit'): if not exists('cppunit') and self.git_working_tree():
self.execute('git', 'clone', 'git://github.com/jwiegley/cppunit.git') self.execute('git', 'clone', 'git://github.com/jwiegley/cppunit.git')
if not exists('cppunit'): if not exists('cppunit'):
@ -1239,6 +1243,7 @@ class PrepareBuild(CommandLineApp):
def phase_gitclean(self, *args): def phase_gitclean(self, *args):
self.log.info('Executing phase: gitclean') self.log.info('Executing phase: gitclean')
if self.git_working_tree():
self.execute('git', 'clean', '-dfx') self.execute('git', 'clean', '-dfx')
######################################################################### #########################################################################

View file

@ -1,7 +0,0 @@
import ledger
def precmd_hello():
hello = ledger.Value()
hello.set_string("Well, hello yourself! This is Ledger, coming to you from Python Land.")
print hello
return hello

View file

@ -20,7 +20,7 @@ class LedgerHandler(BaseHTTPRequestHandler):
except Exception: except Exception:
print "Saw exception in POST handler" print "Saw exception in POST handler"
def cmd_server(): def main(*args):
try: try:
port = 9000 port = 9000
server = HTTPServer(('', port), LedgerHandler) server = HTTPServer(('', port), LedgerHandler)
@ -31,3 +31,6 @@ def cmd_server():
print "Shutting down server" print "Shutting down server"
server.socket.close() server.socket.close()
print __name__
if __name__ == '__main__':
main()

View file

@ -114,6 +114,99 @@ shared_ptr<commodity_pool_t> amount_t::current_pool;
bool amount_t::is_initialized = false; bool amount_t::is_initialized = false;
namespace {
void stream_out_mpq(std::ostream& out,
mpq_t quant,
amount_t::precision_t prec,
int zeros_prec = -1,
const optional<commodity_t&>& comm = none)
{
char * buf = NULL;
try {
IF_DEBUG("amount.convert") {
char * tbuf = mpq_get_str(NULL, 10, quant);
DEBUG("amount.convert", "Rational to convert = " << tbuf);
std::free(tbuf);
}
// Convert the rational number to a floating-point, extending the
// floating-point to a large enough size to get a precise answer.
const std::size_t bits = (mpz_sizeinbase(mpq_numref(quant), 2) +
mpz_sizeinbase(mpq_denref(quant), 2));
mpfr_set_prec(tempfb, bits + amount_t::extend_by_digits*8);
mpfr_set_q(tempfb, quant, GMP_RNDN);
mpfr_asprintf(&buf, "%.*Rf", prec, tempfb);
DEBUG("amount.convert",
"mpfr_print = " << buf << " (precision " << prec << ")");
if (zeros_prec >= 0) {
string::size_type index = std::strlen(buf);
string::size_type point = 0;
for (string::size_type i = 0; i < index; i++) {
if (buf[i] == '.') {
point = i;
break;
}
}
if (point > 0) {
while (--index >= (point + 1 + zeros_prec) && buf[index] == '0')
buf[index] = '\0';
if (index >= (point + zeros_prec) && buf[index] == '.')
buf[index] = '\0';
}
}
if (comm) {
int integer_digits = 0;
if (comm && comm->has_flags(COMMODITY_STYLE_THOUSANDS)) {
// Count the number of integer digits
for (const char * p = buf; *p; p++) {
if (*p == '.')
break;
else if (*p != '-')
integer_digits++;
}
}
for (const char * p = buf; *p; p++) {
if (*p == '.') {
if (commodity_t::european_by_default ||
(comm && comm->has_flags(COMMODITY_STYLE_EUROPEAN)))
out << ',';
else
out << *p;
assert(integer_digits <= 3);
}
else if (*p == '-') {
out << *p;
}
else {
out << *p;
if (integer_digits > 3 && --integer_digits % 3 == 0) {
if (commodity_t::european_by_default ||
(comm && comm->has_flags(COMMODITY_STYLE_EUROPEAN)))
out << '.';
else
out << ',';
}
}
}
} else {
out << buf;
}
}
catch (...) {
if (buf != NULL)
mpfr_free_str(buf);
throw;
}
if (buf != NULL)
mpfr_free_str(buf);
}
}
void amount_t::initialize(shared_ptr<commodity_pool_t> pool) void amount_t::initialize(shared_ptr<commodity_pool_t> pool)
{ {
if (! is_initialized) { if (! is_initialized) {
@ -498,6 +591,19 @@ void amount_t::in_place_round()
set_keep_precision(false); set_keep_precision(false);
} }
void amount_t::in_place_floor()
{
if (! quantity)
throw_(amount_error, _("Cannot floor an uninitialized amount"));
_dup();
std::ostringstream out;
stream_out_mpq(out, MP(quantity), 0);
mpq_set_str(MP(quantity), out.str().c_str(), 10);
}
void amount_t::in_place_unround() void amount_t::in_place_unround()
{ {
if (! quantity) if (! quantity)
@ -612,99 +718,6 @@ int amount_t::sign() const
return mpq_sgn(MP(quantity)); return mpq_sgn(MP(quantity));
} }
namespace {
void stream_out_mpq(std::ostream& out,
mpq_t quant,
amount_t::precision_t prec,
int zeros_prec = -1,
const optional<commodity_t&>& comm = none)
{
char * buf = NULL;
try {
IF_DEBUG("amount.convert") {
char * tbuf = mpq_get_str(NULL, 10, quant);
DEBUG("amount.convert", "Rational to convert = " << tbuf);
std::free(tbuf);
}
// Convert the rational number to a floating-point, extending the
// floating-point to a large enough size to get a precise answer.
const std::size_t bits = (mpz_sizeinbase(mpq_numref(quant), 2) +
mpz_sizeinbase(mpq_denref(quant), 2));
mpfr_set_prec(tempfb, bits + amount_t::extend_by_digits*8);
mpfr_set_q(tempfb, quant, GMP_RNDN);
mpfr_asprintf(&buf, "%.*Rf", prec, tempfb);
DEBUG("amount.convert",
"mpfr_print = " << buf << " (precision " << prec << ")");
if (zeros_prec >= 0) {
string::size_type index = std::strlen(buf);
string::size_type point = 0;
for (string::size_type i = 0; i < index; i++) {
if (buf[i] == '.') {
point = i;
break;
}
}
if (point > 0) {
while (--index >= (point + 1 + zeros_prec) && buf[index] == '0')
buf[index] = '\0';
if (index >= (point + zeros_prec) && buf[index] == '.')
buf[index] = '\0';
}
}
if (comm) {
int integer_digits = 0;
if (comm && comm->has_flags(COMMODITY_STYLE_THOUSANDS)) {
// Count the number of integer digits
for (const char * p = buf; *p; p++) {
if (*p == '.')
break;
else if (*p != '-')
integer_digits++;
}
}
for (const char * p = buf; *p; p++) {
if (*p == '.') {
if (commodity_t::european_by_default ||
(comm && comm->has_flags(COMMODITY_STYLE_EUROPEAN)))
out << ',';
else
out << *p;
assert(integer_digits <= 3);
}
else if (*p == '-') {
out << *p;
}
else {
out << *p;
if (integer_digits > 3 && --integer_digits % 3 == 0) {
if (commodity_t::european_by_default ||
(comm && comm->has_flags(COMMODITY_STYLE_EUROPEAN)))
out << '.';
else
out << ',';
}
}
}
} else {
out << buf;
}
}
catch (...) {
if (buf != NULL)
mpfr_free_str(buf);
throw;
}
if (buf != NULL)
mpfr_free_str(buf);
}
}
bool amount_t::is_zero() const bool amount_t::is_zero() const
{ {
if (! quantity) if (! quantity)
@ -1238,17 +1251,24 @@ void serialize(Archive& ar, long unsigned int& integer,
BOOST_CLASS_EXPORT(ledger::annotated_commodity_t) BOOST_CLASS_EXPORT(ledger::annotated_commodity_t)
template void boost::serialization::serialize(boost::archive::binary_iarchive&,
MP_INT&, const unsigned int);
template void boost::serialization::serialize(boost::archive::binary_oarchive&, template void boost::serialization::serialize(boost::archive::binary_oarchive&,
MP_INT&, const unsigned int); MP_INT&, const unsigned int);
template void boost::serialization::serialize(boost::archive::binary_iarchive&, template void boost::serialization::serialize(boost::archive::binary_iarchive&,
MP_RAT&, const unsigned int); MP_RAT&, const unsigned int);
template void boost::serialization::serialize(boost::archive::binary_oarchive&,
MP_RAT&, const unsigned int);
template void boost::serialization::serialize(boost::archive::binary_iarchive&, template void boost::serialization::serialize(boost::archive::binary_iarchive&,
long unsigned int&, long unsigned int&,
const unsigned int); const unsigned int);
template void boost::serialization::serialize(boost::archive::binary_oarchive&,
template void ledger::amount_t::serialize(boost::archive::binary_oarchive&, long unsigned int&,
const unsigned int); const unsigned int);
template void ledger::amount_t::serialize(boost::archive::binary_iarchive&, template void ledger::amount_t::serialize(boost::archive::binary_iarchive&,
const unsigned int); const unsigned int);
template void ledger::amount_t::serialize(boost::archive::binary_oarchive&,
const unsigned int);
#endif // HAVE_BOOST_SERIALIZATION #endif // HAVE_BOOST_SERIALIZATION

View file

@ -354,6 +354,15 @@ public:
*this = amount_t(to_string()); *this = amount_t(to_string());
} }
/** Yields an amount which has lost all of its extra precision, beyond what
the display precision of the commodity would have printed. */
amount_t floored() const {
amount_t temp(*this);
temp.in_place_floor();
return temp;
}
void in_place_floor();
/** Yields an amount whose display precision is never truncated, even /** Yields an amount whose display precision is never truncated, even
though its commodity normally displays only rounded values. */ though its commodity normally displays only rounded values. */
amount_t unrounded() const { amount_t unrounded() const {

View file

@ -43,7 +43,7 @@
#include "xact.h" #include "xact.h"
#define LEDGER_MAGIC 0x4c454447 #define LEDGER_MAGIC 0x4c454447
#define ARCHIVE_VERSION 0x03000004 #define ARCHIVE_VERSION 0x03000005
//BOOST_IS_ABSTRACT(ledger::scope_t) //BOOST_IS_ABSTRACT(ledger::scope_t)
BOOST_CLASS_EXPORT(ledger::scope_t) BOOST_CLASS_EXPORT(ledger::scope_t)

View file

@ -339,6 +339,18 @@ public:
*this = temp; *this = temp;
} }
balance_t floored() const {
balance_t temp(*this);
temp.in_place_floor();
return temp;
}
void in_place_floor() {
balance_t temp;
foreach (const amounts_map::value_type& pair, amounts)
temp += pair.second.floored();
*this = temp;
}
balance_t unrounded() const { balance_t unrounded() const {
balance_t temp(*this); balance_t temp(*this);
temp.in_place_unround(); temp.in_place_unround();

View file

@ -420,6 +420,7 @@ xact_t * draft_t::insert(journal_t& journal)
} }
} }
} }
assert(new_post->account);
if (new_post.get() && ! new_post->amount.is_null()) { if (new_post.get() && ! new_post->amount.is_null()) {
found_commodity = &new_post->amount.commodity(); found_commodity = &new_post->amount.commodity();
@ -475,6 +476,7 @@ xact_t * draft_t::insert(journal_t& journal)
} }
added->add_post(new_post.release()); added->add_post(new_post.release());
added->posts.back()->account->add_post(added->posts.back());
added->posts.back()->set_state(item_t::UNCLEARED); added->posts.back()->set_state(item_t::UNCLEARED);
DEBUG("derive.xact", "Added new posting to derived entry"); DEBUG("derive.xact", "Added new posting to derived entry");

View file

@ -33,7 +33,6 @@
#include "format.h" #include "format.h"
#include "scope.h" #include "scope.h"
#include "unistring.h"
#include "pstream.h" #include "pstream.h"
namespace ledger { namespace ledger {

View file

@ -43,6 +43,7 @@
#define _FORMAT_H #define _FORMAT_H
#include "expr.h" #include "expr.h"
#include "unistring.h"
namespace ledger { namespace ledger {

View file

@ -70,9 +70,9 @@ public:
list.remove(func); list.remove(func);
} }
bool run_hooks(Data& item, bool post) { bool run_hooks(Data& item) {
foreach (T * func, list) foreach (T * func, list)
if (! (*func)(item, post)) if (! (*func)(item))
return false; return false;
return true; return true;
} }

View file

@ -126,9 +126,7 @@ bool journal_t::add_xact(xact_t * xact)
{ {
xact->journal = this; xact->journal = this;
if (! xact_finalize_hooks.run_hooks(*xact, false) || if (! xact->finalize() || ! xact_finalize_hooks.run_hooks(*xact)) {
! xact->finalize() ||
! xact_finalize_hooks.run_hooks(*xact, true)) {
xact->journal = NULL; xact->journal = NULL;
return false; return false;
} }

View file

@ -42,7 +42,8 @@ namespace ledger {
format_posts::format_posts(report_t& _report, format_posts::format_posts(report_t& _report,
const string& format, const string& format,
bool _print_raw) bool _print_raw,
const optional<string>& _prepend_format)
: report(_report), last_xact(NULL), last_post(NULL), : report(_report), last_xact(NULL), last_post(NULL),
print_raw(_print_raw) print_raw(_print_raw)
{ {
@ -65,6 +66,9 @@ format_posts::format_posts(report_t& _report,
first_line_format.parse_format(format); first_line_format.parse_format(format);
next_lines_format.parse_format(format); next_lines_format.parse_format(format);
} }
if (_prepend_format)
prepend_format.parse_format(*_prepend_format);
} }
void format_posts::flush() void format_posts::flush()
@ -95,6 +99,10 @@ void format_posts::operator()(post_t& post)
else if (! post.has_xdata() || else if (! post.has_xdata() ||
! post.xdata().has_flags(POST_EXT_DISPLAYED)) { ! post.xdata().has_flags(POST_EXT_DISPLAYED)) {
bind_scope_t bound_scope(report, post); bind_scope_t bound_scope(report, post);
if (prepend_format)
out << prepend_format(bound_scope);
if (last_xact != post.xact) { if (last_xact != post.xact) {
if (last_xact) { if (last_xact) {
bind_scope_t xact_scope(report, *last_xact); bind_scope_t xact_scope(report, *last_xact);
@ -116,7 +124,8 @@ void format_posts::operator()(post_t& post)
} }
format_accounts::format_accounts(report_t& _report, format_accounts::format_accounts(report_t& _report,
const string& format) const string& format,
const optional<string>& _prepend_format)
: report(_report), disp_pred() : report(_report), disp_pred()
{ {
TRACE_CTOR(format_accounts, "report&, const string&"); TRACE_CTOR(format_accounts, "report&, const string&");
@ -136,6 +145,9 @@ format_accounts::format_accounts(report_t& _report,
account_line_format.parse_format(format); account_line_format.parse_format(format);
total_line_format.parse_format(format, account_line_format); total_line_format.parse_format(format, account_line_format);
} }
if (_prepend_format)
prepend_format.parse_format(*_prepend_format);
} }
std::size_t format_accounts::post_account(account_t& account, const bool flat) std::size_t format_accounts::post_account(account_t& account, const bool flat)
@ -150,6 +162,11 @@ std::size_t format_accounts::post_account(account_t& account, const bool flat)
account.xdata().add_flags(ACCOUNT_EXT_DISPLAYED); account.xdata().add_flags(ACCOUNT_EXT_DISPLAYED);
bind_scope_t bound_scope(report, account); bind_scope_t bound_scope(report, account);
if (prepend_format)
static_cast<std::ostream&>(report.output_stream)
<< prepend_format(bound_scope);
static_cast<std::ostream&>(report.output_stream) static_cast<std::ostream&>(report.output_stream)
<< account_line_format(bound_scope); << account_line_format(bound_scope);
@ -217,6 +234,11 @@ void format_accounts::flush()
! report.HANDLED(no_total) && ! report.HANDLED(percent)) { ! report.HANDLED(no_total) && ! report.HANDLED(percent)) {
bind_scope_t bound_scope(report, *report.session.journal->master); bind_scope_t bound_scope(report, *report.session.journal->master);
out << separator_format(bound_scope); out << separator_format(bound_scope);
if (prepend_format)
static_cast<std::ostream&>(report.output_stream)
<< prepend_format(bound_scope);
out << total_line_format(bound_scope); out << total_line_format(bound_scope);
} }

View file

@ -60,13 +60,15 @@ protected:
format_t first_line_format; format_t first_line_format;
format_t next_lines_format; format_t next_lines_format;
format_t between_format; format_t between_format;
format_t prepend_format;
xact_t * last_xact; xact_t * last_xact;
post_t * last_post; post_t * last_post;
bool print_raw; bool print_raw;
public: public:
format_posts(report_t& _report, const string& format, format_posts(report_t& _report, const string& format,
bool _print_raw = false); bool _print_raw = false,
const optional<string>& _prepend_format = none);
virtual ~format_posts() { virtual ~format_posts() {
TRACE_DTOR(format_posts); TRACE_DTOR(format_posts);
} }
@ -82,12 +84,14 @@ protected:
format_t account_line_format; format_t account_line_format;
format_t total_line_format; format_t total_line_format;
format_t separator_format; format_t separator_format;
format_t prepend_format;
predicate_t disp_pred; predicate_t disp_pred;
std::list<account_t *> posted_accounts; std::list<account_t *> posted_accounts;
public: public:
format_accounts(report_t& _report, const string& _format); format_accounts(report_t& _report, const string& _format,
const optional<string>& _prepend_format = none);
virtual ~format_accounts() { virtual ~format_accounts() {
TRACE_DTOR(format_accounts); TRACE_DTOR(format_accounts);
} }

View file

@ -36,7 +36,6 @@
#include "account.h" #include "account.h"
#include "journal.h" #include "journal.h"
#include "interactive.h" #include "interactive.h"
#include "unistring.h"
#include "format.h" #include "format.h"
namespace ledger { namespace ledger {

View file

@ -61,6 +61,7 @@ public:
account_t * account; account_t * account;
amount_t amount; // can be null until finalization amount_t amount; // can be null until finalization
optional<expr_t> amount_expr;
optional<amount_t> cost; optional<amount_t> cost;
optional<amount_t> assigned_amount; optional<amount_t> assigned_amount;
@ -212,6 +213,7 @@ private:
ar & xact; ar & xact;
ar & account; ar & account;
ar & amount; ar & amount;
ar & amount_expr;
ar & cost; ar & cost;
ar & assigned_amount; ar & assigned_amount;
} }

View file

@ -220,6 +220,10 @@ internal precision."))
.def("in_place_truncate", &amount_t::in_place_truncate, .def("in_place_truncate", &amount_t::in_place_truncate,
return_internal_reference<>()) return_internal_reference<>())
.def("floored", &amount_t::floored)
.def("in_place_floor", &amount_t::in_place_floor,
return_internal_reference<>())
.def("unrounded", &amount_t::unrounded) .def("unrounded", &amount_t::unrounded)
.def("in_place_unround", &amount_t::in_place_unround, .def("in_place_unround", &amount_t::in_place_unround,
return_internal_reference<>()) return_internal_reference<>())

View file

@ -176,6 +176,10 @@ void export_balance()
.def("in_place_truncate", &balance_t::in_place_truncate, .def("in_place_truncate", &balance_t::in_place_truncate,
return_internal_reference<>()) return_internal_reference<>())
.def("floored", &balance_t::floored)
.def("in_place_floor", &balance_t::in_place_floor,
return_internal_reference<>())
.def("unrounded", &balance_t::unrounded) .def("unrounded", &balance_t::unrounded)
.def("in_place_unround", &balance_t::in_place_unround, .def("in_place_unround", &balance_t::in_place_unround,
return_internal_reference<>()) return_internal_reference<>())

View file

@ -136,8 +136,8 @@ namespace {
py_xact_finalizer_t(object obj) : pyobj(obj) {} py_xact_finalizer_t(object obj) : pyobj(obj) {}
py_xact_finalizer_t(const py_xact_finalizer_t& other) py_xact_finalizer_t(const py_xact_finalizer_t& other)
: pyobj(other.pyobj) {} : pyobj(other.pyobj) {}
virtual bool operator()(xact_t& xact, bool post) { virtual bool operator()(xact_t& xact) {
return call<bool>(pyobj.ptr(), xact, post); return call<bool>(pyobj.ptr(), xact);
} }
}; };
@ -161,9 +161,9 @@ namespace {
} }
} }
void py_run_xact_finalizers(journal_t& journal, xact_t& xact, bool post) void py_run_xact_finalizers(journal_t& journal, xact_t& xact)
{ {
journal.xact_finalize_hooks.run_hooks(xact, post); journal.xact_finalize_hooks.run_hooks(xact);
} }
std::size_t py_read(journal_t& journal, const string& pathname) std::size_t py_read(journal_t& journal, const string& pathname)

View file

@ -33,6 +33,7 @@
#include "pyinterp.h" #include "pyinterp.h"
#include "pyutils.h" #include "pyutils.h"
#include "times.h"
// jww (2007-05-04): Convert time duration objects to PyDelta // jww (2007-05-04): Convert time duration objects to PyDelta
@ -40,8 +41,6 @@ namespace ledger {
using namespace boost::python; using namespace boost::python;
typedef boost::gregorian::date date;
#define MY_PyDateTime_IMPORT \ #define MY_PyDateTime_IMPORT \
PyDateTimeAPI = (PyDateTime_CAPI*) \ PyDateTimeAPI = (PyDateTime_CAPI*) \
PyCObject_Import(const_cast<char *>("datetime"), \ PyCObject_Import(const_cast<char *>("datetime"), \
@ -49,7 +48,7 @@ typedef boost::gregorian::date date;
struct date_to_python struct date_to_python
{ {
static PyObject* convert(const date& dte) static PyObject* convert(const date_t& dte)
{ {
MY_PyDateTime_IMPORT; MY_PyDateTime_IMPORT;
return PyDate_FromDate(dte.year(), dte.month(), dte.day()); return PyDate_FromDate(dte.year(), dte.month(), dte.day());
@ -77,13 +76,13 @@ struct date_from_python
date::day_type d = date::day_type d =
static_cast<date::day_type>(PyDateTime_GET_DAY(obj_ptr)); static_cast<date::day_type>(PyDateTime_GET_DAY(obj_ptr));
date * dte = new date(y, m, d); date_t * dte = new date_t(y, m, d);
data->convertible = (void *) dte; data->convertible = (void *) dte;
} }
}; };
typedef register_python_conversion<date, date_to_python, date_from_python> typedef register_python_conversion<date_t, date_to_python, date_from_python>
date_python_conversion; date_python_conversion;
@ -93,7 +92,7 @@ struct datetime_to_python
{ {
MY_PyDateTime_IMPORT; MY_PyDateTime_IMPORT;
date dte = moment.date(); date_t dte = moment.date();
datetime_t::time_duration_type tod = moment.time_of_day(); datetime_t::time_duration_type tod = moment.time_of_day();
return PyDateTime_FromDateAndTime return PyDateTime_FromDateAndTime
@ -139,16 +138,90 @@ struct datetime_from_python
(PyDateTime_DATE_GET_MICROSECOND(obj_ptr)) * 1000000; (PyDateTime_DATE_GET_MICROSECOND(obj_ptr)) * 1000000;
datetime_t * moment datetime_t * moment
= new datetime_t(date(y, m, d), = new datetime_t(date_t(y, m, d),
datetime_t::time_duration_type(h, min, s, ms)); datetime_t::time_duration_type(h, min, s, ms));
data->convertible = (void *) moment; data->convertible = (void *) moment;
} }
}; };
typedef register_python_conversion<datetime_t, datetime_to_python, datetime_from_python> typedef register_python_conversion<datetime_t,
datetime_to_python, datetime_from_python>
datetime_python_conversion; datetime_python_conversion;
/* Convert time_duration to/from python */
struct duration_to_python
{
static int get_usecs(boost::posix_time::time_duration const& d)
{
static int64_t resolution =
boost::posix_time::time_duration::ticks_per_second();
int64_t fracsecs = d.fractional_seconds();
if (resolution > 1000000)
return static_cast<int>(fracsecs / (resolution / 1000000));
else
return static_cast<int>(fracsecs * (1000000 / resolution));
}
static PyObject * convert(posix_time::time_duration d)
{
int days = d.hours() / 24;
if (days < 0)
days --;
int seconds = d.total_seconds() - days*(24*3600);
int usecs = get_usecs(d);
if (days < 0)
usecs = 1000000-1 - usecs;
return PyDelta_FromDSU(days, seconds, usecs);
}
};
/* Should support the negative values, but not the special boost time
durations */
struct duration_from_python
{
static void* convertible(PyObject * obj_ptr)
{
if ( ! PyDelta_Check(obj_ptr))
return 0;
return obj_ptr;
}
static void construct(PyObject* obj_ptr,
python::converter::rvalue_from_python_stage1_data * data)
{
PyDateTime_Delta const* pydelta
= reinterpret_cast<PyDateTime_Delta*>(obj_ptr);
int days = pydelta->days;
bool is_negative = (days < 0);
if (is_negative)
days = -days;
// Create time duration object
posix_time::time_duration
duration = (posix_time::hours(24) * days +
posix_time::seconds(pydelta->seconds) +
posix_time::microseconds(pydelta->microseconds));
if (is_negative)
duration = duration.invert_sign();
void * storage =
reinterpret_cast<converter::rvalue_from_python_storage
<posix_time::time_duration> *>
(data)->storage.bytes;
new (storage) posix_time::time_duration(duration);
data->convertible = storage;
}
};
typedef register_python_conversion<time_duration_t,
duration_to_python, duration_from_python>
duration_python_conversion;
datetime_t py_parse_datetime(const string& str) { datetime_t py_parse_datetime(const string& str) {
return parse_datetime(str); return parse_datetime(str);
} }
@ -161,6 +234,7 @@ void export_times()
{ {
datetime_python_conversion(); datetime_python_conversion();
date_python_conversion(); date_python_conversion();
duration_python_conversion();
register_optional_to_python<datetime_t>(); register_optional_to_python<datetime_t>();
register_optional_to_python<date_t>(); register_optional_to_python<date_t>();

View file

@ -61,7 +61,8 @@ struct bool_from_python
static void construct(PyObject* obj_ptr, static void construct(PyObject* obj_ptr,
converter::rvalue_from_python_stage1_data* data) converter::rvalue_from_python_stage1_data* data)
{ {
void* storage = ((converter::rvalue_from_python_storage<bool>*) data)->storage.bytes; void * storage =
((converter::rvalue_from_python_storage<bool>*) data)->storage.bytes;
if (obj_ptr == Py_True) if (obj_ptr == Py_True)
new (storage) bool(true); new (storage) bool(true);
else else
@ -74,13 +75,19 @@ typedef register_python_conversion<bool, bool_to_python, bool_from_python>
bool_python_conversion; bool_python_conversion;
#if defined(STRING_VERIFY_ON)
struct string_to_python struct string_to_python
{ {
static PyObject* convert(const ledger::string& str) static PyObject* convert(const string& str)
{ {
#if 1
// Return a Unicode object
PyObject * pstr = PyString_FromString(str.c_str());
PyObject * uni = PyUnicode_FromEncodedObject(pstr, "UTF-8", NULL);
return object(handle<>(borrowed(uni))).ptr();
#else
// Return a 7-bit ASCII string
return incref(object(static_cast<const std::string&>(str)).ptr()); return incref(object(static_cast<const std::string&>(str)).ptr());
#endif
} }
}; };
@ -88,26 +95,49 @@ struct string_from_python
{ {
static void* convertible(PyObject* obj_ptr) static void* convertible(PyObject* obj_ptr)
{ {
if (!PyString_Check(obj_ptr)) return 0; if (!PyUnicode_Check(obj_ptr) &&
!PyString_Check(obj_ptr)) return 0;
return obj_ptr; return obj_ptr;
} }
static void construct(PyObject* obj_ptr, converter::rvalue_from_python_stage1_data* data) static void construct(PyObject* obj_ptr,
converter::rvalue_from_python_stage1_data* data)
{ {
if (PyString_Check(obj_ptr)) {
const char* value = PyString_AsString(obj_ptr); const char* value = PyString_AsString(obj_ptr);
if (value == 0) throw_error_already_set(); if (value == 0) throw_error_already_set();
void* storage = void* storage =
reinterpret_cast<converter::rvalue_from_python_storage<ledger::string> *>(data)->storage.bytes; reinterpret_cast<converter::rvalue_from_python_storage<string> *>
new (storage) ledger::string(value); (data)->storage.bytes;
new (storage) string(value);
data->convertible = storage; data->convertible = storage;
} else {
VERIFY(PyUnicode_Check(obj_ptr));
Py_ssize_t size = PyUnicode_GET_SIZE(obj_ptr);
const Py_UNICODE* value = PyUnicode_AS_UNICODE(obj_ptr);
string str;
if (sizeof(Py_UNICODE) == 2) // UTF-16
utf8::unchecked::utf16to8(value, value + size, std::back_inserter(str));
else if (sizeof(Py_UNICODE) == 4) // UTF-32
utf8::unchecked::utf32to8(value, value + size, std::back_inserter(str));
else
assert(! "Py_UNICODE has an unexpected size");
if (value == 0) throw_error_already_set();
void* storage =
reinterpret_cast<converter::rvalue_from_python_storage<string> *>
(data)->storage.bytes;
new (storage) string(str);
data->convertible = storage;
}
} }
}; };
typedef register_python_conversion<ledger::string, string_to_python, string_from_python> typedef register_python_conversion<string, string_to_python, string_from_python>
string_python_conversion; string_python_conversion;
#endif // STRING_VERIFY_ON
struct istream_to_python struct istream_to_python
{ {
@ -125,16 +155,19 @@ struct istream_from_python
return obj_ptr; return obj_ptr;
} }
static void construct(PyObject* obj_ptr, converter::rvalue_from_python_stage1_data* data) static void construct(PyObject* obj_ptr,
converter::rvalue_from_python_stage1_data* data)
{ {
void* storage = void* storage =
reinterpret_cast<converter::rvalue_from_python_storage<pyifstream> *>(data)->storage.bytes; reinterpret_cast<converter::rvalue_from_python_storage<pyifstream> *>
(data)->storage.bytes;
new (storage) pyifstream(reinterpret_cast<PyFileObject *>(obj_ptr)); new (storage) pyifstream(reinterpret_cast<PyFileObject *>(obj_ptr));
data->convertible = storage; data->convertible = storage;
} }
}; };
typedef register_python_conversion<std::istream, istream_to_python, istream_from_python> typedef register_python_conversion<std::istream,
istream_to_python, istream_from_python>
istream_python_conversion; istream_python_conversion;
@ -154,15 +187,19 @@ struct ostream_from_python
return obj_ptr; return obj_ptr;
} }
static void construct(PyObject* obj_ptr, converter::rvalue_from_python_stage1_data* data) static void construct(PyObject* obj_ptr,
converter::rvalue_from_python_stage1_data* data)
{ {
void* storage = reinterpret_cast<converter::rvalue_from_python_storage<pyofstream> *>(data)->storage.bytes; void* storage =
reinterpret_cast<converter::rvalue_from_python_storage<pyofstream> *>
(data)->storage.bytes;
new (storage) pyofstream(reinterpret_cast<PyFileObject *>(obj_ptr)); new (storage) pyofstream(reinterpret_cast<PyFileObject *>(obj_ptr));
data->convertible = storage; data->convertible = storage;
} }
}; };
typedef register_python_conversion<std::ostream, ostream_to_python, ostream_from_python> typedef register_python_conversion<std::ostream,
ostream_to_python, ostream_from_python>
ostream_python_conversion; ostream_python_conversion;
@ -216,9 +253,7 @@ void export_utils()
; ;
bool_python_conversion(); bool_python_conversion();
#if defined(STRING_VERIFY_ON)
string_python_conversion(); string_python_conversion();
#endif
istream_python_conversion(); istream_python_conversion();
ostream_python_conversion(); ostream_python_conversion();
} }

View file

@ -47,6 +47,22 @@ BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(set_string_overloads, set_string, 0, 2)
namespace { namespace {
PyObject * py_base_type(value_t& value) {
if (value.is_boolean()) {
return (PyObject *)&PyBool_Type;
}
else if (value.is_long()) {
return (PyObject *)&PyInt_Type;
}
else if (value.is_string()) {
return (PyObject *)&PyUnicode_Type;
}
else {
object typeobj(object(value).attr("__class__"));
return typeobj.ptr();
}
}
expr_t py_value_getattr(const value_t& value, const string& name) expr_t py_value_getattr(const value_t& value, const string& name)
{ {
if (value.is_scope()) { if (value.is_scope()) {
@ -218,6 +234,8 @@ void export_value()
.def("in_place_round", &value_t::in_place_round) .def("in_place_round", &value_t::in_place_round)
.def("truncated", &value_t::truncated) .def("truncated", &value_t::truncated)
.def("in_place_truncate", &value_t::in_place_truncate) .def("in_place_truncate", &value_t::in_place_truncate)
.def("floored", &value_t::floored)
.def("in_place_floor", &value_t::in_place_floor)
.def("unrounded", &value_t::unrounded) .def("unrounded", &value_t::unrounded)
.def("in_place_unround", &value_t::in_place_unround) .def("in_place_unround", &value_t::in_place_unround)
.def("reduced", &value_t::reduced) .def("reduced", &value_t::reduced)
@ -305,6 +323,12 @@ void export_value()
.def("label", &value_t::label) .def("label", &value_t::label)
.def("valid", &value_t::valid) .def("valid", &value_t::valid)
.def("basetype", py_base_type)
;
class_< value_t::sequence_t > ("ValueSequence")
.def(vector_indexing_suite< value_t::sequence_t >());
; ;
scope().attr("NULL_VALUE") = NULL_VALUE; scope().attr("NULL_VALUE") = NULL_VALUE;

View file

@ -99,27 +99,41 @@ void python_interpreter_t::initialize()
Py_Initialize(); Py_Initialize();
assert(Py_IsInitialized()); assert(Py_IsInitialized());
hack_system_paths();
object main_module = python::import("__main__"); object main_module = python::import("__main__");
if (! main_module) if (! main_module)
throw_(std::logic_error, throw_(std::runtime_error,
_("Python failed to initialize (couldn't find __main__)")); _("Python failed to initialize (couldn't find __main__)"));
main_nspace = extract<dict>(main_module.attr("__dict__")); main_nspace = extract<dict>(main_module.attr("__dict__"));
if (! main_nspace) if (! main_nspace)
throw_(std::logic_error, throw_(std::runtime_error,
_("Python failed to initialize (couldn't find __dict__)")); _("Python failed to initialize (couldn't find __dict__)"));
python::detail::init_module("ledger", &initialize_for_python); python::detail::init_module("ledger", &initialize_for_python);
is_initialized = true; is_initialized = true;
}
catch (const error_already_set&) {
PyErr_Print();
throw_(std::runtime_error, _("Python failed to initialize"));
}
TRACE_FINISH(python_init, 1);
}
void python_interpreter_t::hack_system_paths()
{
// Hack ledger.__path__ so it points to a real location // Hack ledger.__path__ so it points to a real location
python::object module_sys = import("sys"); python::object sys_module = python::import("sys");
python::object sys_dict = module_sys.attr("__dict__"); python::object sys_dict = sys_module.attr("__dict__");
python::list paths(sys_dict["path"]); python::list paths(sys_dict["path"]);
#if defined(DEBUG_ON)
bool path_initialized = false; bool path_initialized = false;
#endif
int n = python::extract<int>(paths.attr("__len__")()); int n = python::extract<int>(paths.attr("__len__")());
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
python::extract<std::string> str(paths[i]); python::extract<std::string> str(paths[i]);
@ -127,7 +141,7 @@ void python_interpreter_t::initialize()
DEBUG("python.interp", "sys.path = " << pathname); DEBUG("python.interp", "sys.path = " << pathname);
if (exists(pathname / "ledger" / "__init__.py")) { if (exists(pathname / "ledger" / "__init__.py")) {
if (python::object module_ledger = import("ledger")) { if (python::object module_ledger = python::import("ledger")) {
DEBUG("python.interp", DEBUG("python.interp",
"Setting ledger.__path__ = " << (pathname / "ledger")); "Setting ledger.__path__ = " << (pathname / "ledger"));
@ -137,10 +151,12 @@ void python_interpreter_t::initialize()
ledger_dict["__path__"] = temp_list; ledger_dict["__path__"] = temp_list;
} else { } else {
throw_(std::logic_error, throw_(std::runtime_error,
_("Python failed to initialize (couldn't find ledger)")); _("Python failed to initialize (couldn't find ledger)"));
} }
#if defined(DEBUG_ON)
path_initialized = true; path_initialized = true;
#endif
break; break;
} }
} }
@ -149,16 +165,9 @@ void python_interpreter_t::initialize()
DEBUG("python.init", DEBUG("python.init",
"Ledger failed to find 'ledger/__init__.py' on the PYTHONPATH"); "Ledger failed to find 'ledger/__init__.py' on the PYTHONPATH");
#endif #endif
}
catch (const error_already_set&) {
PyErr_Print();
throw_(std::logic_error, _("Python failed to initialize"));
}
TRACE_FINISH(python_init, 1);
} }
object python_interpreter_t::import(const string& str) object python_interpreter_t::import_into_main(const string& str)
{ {
if (! is_initialized) if (! is_initialized)
initialize(); initialize();
@ -166,7 +175,8 @@ object python_interpreter_t::import(const string& str)
try { try {
object mod = python::import(str.c_str()); object mod = python::import(str.c_str());
if (! mod) if (! mod)
throw_(std::logic_error, _("Failed to import Python module %1") << str); throw_(std::runtime_error,
_("Failed to import Python module %1") << str);
// Import all top-level entries directly into the main namespace // Import all top-level entries directly into the main namespace
main_nspace.update(mod.attr("__dict__")); main_nspace.update(mod.attr("__dict__"));
@ -179,6 +189,32 @@ object python_interpreter_t::import(const string& str)
return object(); return object();
} }
object python_interpreter_t::import_option(const string& str)
{
path file(str);
python::object sys_module = python::import("sys");
python::object sys_dict = sys_module.attr("__dict__");
python::list paths(sys_dict["path"]);
#if BOOST_VERSION >= 103700
paths.insert(0, file.parent_path().string());
sys_dict["path"] = paths;
string name = file.filename();
if (contains(name, ".py"))
name = file.stem();
#else // BOOST_VERSION >= 103700
paths.insert(0, file.branch_path().string());
sys_dict["path"] = paths;
string name = file.leaf();
#endif // BOOST_VERSION >= 103700
return python::import(python::str(name.c_str()));
}
object python_interpreter_t::eval(std::istream& in, py_eval_mode_t mode) object python_interpreter_t::eval(std::istream& in, py_eval_mode_t mode)
{ {
bool first = true; bool first = true;
@ -212,7 +248,7 @@ object python_interpreter_t::eval(std::istream& in, py_eval_mode_t mode)
} }
catch (const error_already_set&) { catch (const error_already_set&) {
PyErr_Print(); PyErr_Print();
throw_(std::logic_error, _("Failed to evaluate Python code")); throw_(std::runtime_error, _("Failed to evaluate Python code"));
} }
return object(); return object();
} }
@ -234,7 +270,7 @@ object python_interpreter_t::eval(const string& str, py_eval_mode_t mode)
} }
catch (const error_already_set&) { catch (const error_already_set&) {
PyErr_Print(); PyErr_Print();
throw_(std::logic_error, _("Failed to evaluate Python code")); throw_(std::runtime_error, _("Failed to evaluate Python code"));
} }
return object(); return object();
} }
@ -255,7 +291,7 @@ value_t python_interpreter_t::python_command(call_scope_t& args)
std::strcpy(argv[i + 1], arg.c_str()); std::strcpy(argv[i + 1], arg.c_str());
} }
int status; int status = 1;
try { try {
status = Py_Main(static_cast<int>(args.size()) + 1, argv); status = Py_Main(static_cast<int>(args.size()) + 1, argv);
@ -277,6 +313,44 @@ value_t python_interpreter_t::python_command(call_scope_t& args)
return NULL_VALUE; return NULL_VALUE;
} }
value_t python_interpreter_t::server_command(call_scope_t& args)
{
if (! is_initialized)
initialize();
python::object server_module;
try {
server_module = python::import("ledger.server");
if (! server_module)
throw_(std::runtime_error,
_("Could not import ledger.server; please check your PYTHONPATH"));
}
catch (const error_already_set&) {
PyErr_Print();
throw_(std::runtime_error,
_("Could not import ledger.server; please check your PYTHONPATH"));
}
if (python::object main_function = server_module.attr("main")) {
functor_t func(main_function, "main");
try {
func(args);
}
catch (const error_already_set&) {
PyErr_Print();
throw_(std::runtime_error,
_("Error while invoking ledger.server's main() function"));
}
return true;
} else {
throw_(std::runtime_error,
_("The ledger.server module is missing its main() function!"));
}
return false;
}
option_t<python_interpreter_t> * option_t<python_interpreter_t> *
python_interpreter_t::lookup_option(const char * p) python_interpreter_t::lookup_option(const char * p)
{ {
@ -304,7 +378,7 @@ expr_t::ptr_op_t python_interpreter_t::lookup(const symbol_t::kind_t kind,
DEBUG("python.interp", "Python lookup: " << name); DEBUG("python.interp", "Python lookup: " << name);
if (python::object obj = main_nspace.get(name.c_str())) if (python::object obj = main_nspace.get(name.c_str()))
return WRAP_FUNCTOR(functor_t(name, obj)); return WRAP_FUNCTOR(functor_t(obj, name));
} }
break; break;
@ -320,6 +394,11 @@ expr_t::ptr_op_t python_interpreter_t::lookup(const symbol_t::kind_t kind,
if (is_eq(p, "python")) if (is_eq(p, "python"))
return MAKE_FUNCTOR(python_interpreter_t::python_command); return MAKE_FUNCTOR(python_interpreter_t::python_command);
break; break;
case 's':
if (is_eq(p, "server"))
return MAKE_FUNCTOR(python_interpreter_t::server_command);
break;
} }
} }
@ -349,7 +428,7 @@ namespace {
dynamic_cast<const auto_xact_t *>(scope)) dynamic_cast<const auto_xact_t *>(scope))
lst.append(ptr(auto_xact)); lst.append(ptr(auto_xact));
else else
throw_(std::runtime_error, throw_(std::logic_error,
_("Cannot downcast scoped object to specific type")); _("Cannot downcast scoped object to specific type"));
} else { } else {
lst.append(value); lst.append(value);

View file

@ -57,8 +57,10 @@ public:
} }
void initialize(); void initialize();
void hack_system_paths();
python::object import(const string& name); python::object import_into_main(const string& name);
python::object import_option(const string& name);
enum py_eval_mode_t { enum py_eval_mode_t {
PY_EVAL_EXPR, PY_EVAL_EXPR,
@ -77,6 +79,7 @@ public:
} }
value_t python_command(call_scope_t& scope); value_t python_command(call_scope_t& scope);
value_t server_command(call_scope_t& args);
class functor_t { class functor_t {
functor_t(); functor_t();
@ -87,9 +90,9 @@ public:
public: public:
string name; string name;
functor_t(const string& _name, python::object _func) functor_t(python::object _func, const string& _name)
: func(_func), name(_name) { : func(_func), name(_name) {
TRACE_CTOR(functor_t, "const string&, python::object"); TRACE_CTOR(functor_t, "python::object, const string&");
} }
functor_t(const functor_t& other) functor_t(const functor_t& other)
: func(other.func), name(other.name) { : func(other.func), name(other.name) {
@ -106,41 +109,10 @@ public:
virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind, virtual expr_t::ptr_op_t lookup(const symbol_t::kind_t kind,
const string& name); const string& name);
#if BOOST_VERSION >= 103700
OPTION_(python_interpreter_t, import_, DO_(scope) { OPTION_(python_interpreter_t, import_, DO_(scope) {
interactive_t args(scope, "s"); interactive_t args(scope, "ss");
parent->import_option(args.get<string>(1));
path file(args.get<string>(0));
python::object module_sys = parent->import("sys");
python::object sys_dict = module_sys.attr("__dict__");
python::list paths(sys_dict["path"]);
paths.insert(0, file.parent_path().string());
sys_dict["path"] = paths;
string name = file.filename();
if (contains(name, ".py"))
parent->import(file.stem());
else
parent->import(name);
}); });
#else // BOOST_VERSION >= 103700
OPTION_(python_interpreter_t, import_, DO_(scope) {
interactive_t args(scope, "s");
path file(args.get<string>(0));
python::object module_sys = parent->import("sys");
python::object sys_dict = module_sys.attr("__dict__");
python::list paths(sys_dict["path"]);
paths.insert(0, file.branch_path().string());
sys_dict["path"] = paths;
parent->import(file.leaf());
});
#endif // BOOST_VERSION >= 103700
}; };
extern shared_ptr<python_interpreter_t> python_session; extern shared_ptr<python_interpreter_t> python_session;

View file

@ -36,7 +36,7 @@ template <typename T, typename TfromPy>
struct object_from_python struct object_from_python
{ {
object_from_python() { object_from_python() {
boost::python::converter::registry::push_back boost::python::converter::registry::insert
(&TfromPy::convertible, &TfromPy::construct, (&TfromPy::convertible, &TfromPy::construct,
boost::python::type_id<T>()); boost::python::type_id<T>());
} }
@ -106,6 +106,55 @@ struct register_optional_to_python : public boost::noncopyable
} }
}; };
namespace boost { namespace python {
// Use expr to create the PyObject corresponding to x
# define BOOST_PYTHON_RETURN_TO_PYTHON_BY_VALUE(T, expr, pytype)\
template <> struct to_python_value<T&> \
: detail::builtin_to_python \
{ \
inline PyObject* operator()(T const& x) const \
{ \
return (expr); \
} \
inline PyTypeObject const* get_pytype() const \
{ \
return (pytype); \
} \
}; \
template <> struct to_python_value<T const&> \
: detail::builtin_to_python \
{ \
inline PyObject* operator()(T const& x) const \
{ \
return (expr); \
} \
inline PyTypeObject const* get_pytype() const \
{ \
return (pytype); \
} \
};
# define BOOST_PYTHON_ARG_TO_PYTHON_BY_VALUE(T, expr) \
namespace converter \
{ \
template <> struct arg_to_python< T > \
: handle<> \
{ \
arg_to_python(T const& x) \
: python::handle<>(expr) {} \
}; \
}
// Specialize argument and return value converters for T using expr
# define BOOST_PYTHON_TO_PYTHON_BY_VALUE(T, expr, pytype) \
BOOST_PYTHON_RETURN_TO_PYTHON_BY_VALUE(T,expr, pytype) \
BOOST_PYTHON_ARG_TO_PYTHON_BY_VALUE(T,expr)
BOOST_PYTHON_TO_PYTHON_BY_VALUE(ledger::string, ::PyUnicode_FromEncodedObject(::PyString_FromString(x.c_str()), "UTF-8", NULL), &PyUnicode_Type)
} } // namespace boost::python
//boost::python::register_ptr_to_python< boost::shared_ptr<Base> >(); //boost::python::register_ptr_to_python< boost::shared_ptr<Base> >();
#endif // _PY_UTILS_H #endif // _PY_UTILS_H

View file

@ -155,8 +155,6 @@ query_t::lexer_t::token_t query_t::lexer_t::next_token()
return token_t(token_t::TOK_PAYEE); return token_t(token_t::TOK_PAYEE);
else if (ident == "note") else if (ident == "note")
return token_t(token_t::TOK_NOTE); return token_t(token_t::TOK_NOTE);
else if (ident == "account")
return token_t(token_t::TOK_ACCOUNT);
else if (ident == "tag") else if (ident == "tag")
return token_t(token_t::TOK_META); return token_t(token_t::TOK_META);
else if (ident == "meta") else if (ident == "meta")

View file

@ -33,7 +33,6 @@
#include "report.h" #include "report.h"
#include "session.h" #include "session.h"
#include "unistring.h"
#include "format.h" #include "format.h"
#include "query.h" #include "query.h"
#include "output.h" #include "output.h"
@ -208,6 +207,12 @@ value_t report_t::fn_quantity(call_scope_t& scope)
return args.get<amount_t>(0).number(); return args.get<amount_t>(0).number();
} }
value_t report_t::fn_floor(call_scope_t& scope)
{
interactive_t args(scope, "v");
return args.value_at(0).floored();
}
value_t report_t::fn_abs(call_scope_t& scope) value_t report_t::fn_abs(call_scope_t& scope)
{ {
interactive_t args(scope, "v"); interactive_t args(scope, "v");
@ -453,15 +458,6 @@ value_t report_t::echo_command(call_scope_t& scope)
return true; return true;
} }
bool report_t::maybe_import(const string& module)
{
if (lookup(symbol_t::OPTION, "import_")) {
expr_t(string("import_(\"") + module + "\")").calc(*this);
return true;
}
return false;
}
option_t<report_t> * report_t::lookup_option(const char * p) option_t<report_t> * report_t::lookup_option(const char * p)
{ {
switch (*p) { switch (*p) {
@ -641,6 +637,7 @@ option_t<report_t> * report_t::lookup_option(const char * p)
else OPT(pricesdb_format_); else OPT(pricesdb_format_);
else OPT(print_format_); else OPT(print_format_);
else OPT(payee_width_); else OPT(payee_width_);
else OPT(prepend_format_);
break; break;
case 'q': case 'q':
OPT(quantity); OPT(quantity);
@ -744,6 +741,8 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind,
case 'f': case 'f':
if (is_eq(p, "format_date")) if (is_eq(p, "format_date"))
return MAKE_FUNCTOR(report_t::fn_format_date); return MAKE_FUNCTOR(report_t::fn_format_date);
else if (is_eq(p, "floor"))
return MAKE_FUNCTOR(report_t::fn_floor);
break; break;
case 'g': case 'g':
@ -864,7 +863,8 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind,
if (*(p + 1) == '\0' || is_eq(p, "bal") || is_eq(p, "balance")) { if (*(p + 1) == '\0' || is_eq(p, "bal") || is_eq(p, "balance")) {
return expr_t::op_t::wrap_functor return expr_t::op_t::wrap_functor
(reporter<account_t, acct_handler_ptr, &report_t::accounts_report> (reporter<account_t, acct_handler_ptr, &report_t::accounts_report>
(new format_accounts(*this, report_format(HANDLER(balance_format_))), (new format_accounts(*this, report_format(HANDLER(balance_format_)),
maybe_format(HANDLER(prepend_format_))),
*this, "#balance")); *this, "#balance"));
} }
else if (is_eq(p, "budget")) { else if (is_eq(p, "budget")) {
@ -876,7 +876,8 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind,
return expr_t::op_t::wrap_functor return expr_t::op_t::wrap_functor
(reporter<account_t, acct_handler_ptr, &report_t::accounts_report> (reporter<account_t, acct_handler_ptr, &report_t::accounts_report>
(new format_accounts(*this, report_format(HANDLER(budget_format_))), (new format_accounts(*this, report_format(HANDLER(budget_format_)),
maybe_format(HANDLER(prepend_format_))),
*this, "#budget")); *this, "#budget"));
} }
break; break;
@ -885,7 +886,8 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind,
if (is_eq(p, "csv")) { if (is_eq(p, "csv")) {
return WRAP_FUNCTOR return WRAP_FUNCTOR
(reporter<> (reporter<>
(new format_posts(*this, report_format(HANDLER(csv_format_))), (new format_posts(*this, report_format(HANDLER(csv_format_)),
maybe_format(HANDLER(prepend_format_))),
*this, "#csv")); *this, "#csv"));
} }
else if (is_eq(p, "cleared")) { else if (is_eq(p, "cleared")) {
@ -894,7 +896,8 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind,
return expr_t::op_t::wrap_functor return expr_t::op_t::wrap_functor
(reporter<account_t, acct_handler_ptr, &report_t::accounts_report> (reporter<account_t, acct_handler_ptr, &report_t::accounts_report>
(new format_accounts(*this, report_format(HANDLER(cleared_format_))), (new format_accounts(*this, report_format(HANDLER(cleared_format_)),
maybe_format(HANDLER(prepend_format_))),
*this, "#cleared")); *this, "#cleared"));
} }
break; break;
@ -923,22 +926,23 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind,
else if (is_eq(p, "prices")) else if (is_eq(p, "prices"))
return expr_t::op_t::wrap_functor return expr_t::op_t::wrap_functor
(reporter<post_t, post_handler_ptr, &report_t::commodities_report> (reporter<post_t, post_handler_ptr, &report_t::commodities_report>
(new format_posts(*this, report_format(HANDLER(prices_format_))), (new format_posts(*this, report_format(HANDLER(prices_format_)),
maybe_format(HANDLER(prepend_format_))),
*this, "#prices")); *this, "#prices"));
else if (is_eq(p, "pricesdb")) else if (is_eq(p, "pricesdb"))
return expr_t::op_t::wrap_functor return expr_t::op_t::wrap_functor
(reporter<post_t, post_handler_ptr, &report_t::commodities_report> (reporter<post_t, post_handler_ptr, &report_t::commodities_report>
(new format_posts(*this, report_format(HANDLER(pricesdb_format_))), (new format_posts(*this, report_format(HANDLER(pricesdb_format_)),
maybe_format(HANDLER(prepend_format_))),
*this, "#pricesdb")); *this, "#pricesdb"));
else if (is_eq(p, "python") && maybe_import("ledger.interp"))
return session.lookup(symbol_t::COMMAND, "python");
break; break;
case 'r': case 'r':
if (*(p + 1) == '\0' || is_eq(p, "reg") || is_eq(p, "register")) if (*(p + 1) == '\0' || is_eq(p, "reg") || is_eq(p, "register"))
return WRAP_FUNCTOR return WRAP_FUNCTOR
(reporter<> (reporter<>
(new format_posts(*this, report_format(HANDLER(register_format_))), (new format_posts(*this, report_format(HANDLER(register_format_)),
false, maybe_format(HANDLER(prepend_format_))),
*this, "#register")); *this, "#register"));
else if (is_eq(p, "reload")) else if (is_eq(p, "reload"))
return MAKE_FUNCTOR(report_t::reload_command); return MAKE_FUNCTOR(report_t::reload_command);
@ -947,8 +951,7 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind,
case 's': case 's':
if (is_eq(p, "stats") || is_eq(p, "stat")) if (is_eq(p, "stats") || is_eq(p, "stat"))
return WRAP_FUNCTOR(report_statistics); return WRAP_FUNCTOR(report_statistics);
else else if (is_eq(p, "server"))
if (is_eq(p, "server") && maybe_import("ledger.server"))
return session.lookup(symbol_t::COMMAND, "server"); return session.lookup(symbol_t::COMMAND, "server");
break; break;
@ -981,10 +984,6 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind,
(reporter<post_t, post_handler_ptr, &report_t::generate_report> (reporter<post_t, post_handler_ptr, &report_t::generate_report>
(new format_posts(*this, report_format(HANDLER(print_format_)), (new format_posts(*this, report_format(HANDLER(print_format_)),
false), *this, "#generate")); false), *this, "#generate"));
case 'h':
if (is_eq(p, "hello") && maybe_import("ledger.hello"))
return session.lookup(symbol_t::PRECOMMAND, "hello");
break;
case 'p': case 'p':
if (is_eq(p, "parse")) if (is_eq(p, "parse"))
return WRAP_FUNCTOR(parse_command); return WRAP_FUNCTOR(parse_command);

View file

@ -143,6 +143,7 @@ public:
value_t fn_rounded(call_scope_t& scope); value_t fn_rounded(call_scope_t& scope);
value_t fn_unrounded(call_scope_t& scope); value_t fn_unrounded(call_scope_t& scope);
value_t fn_truncated(call_scope_t& scope); value_t fn_truncated(call_scope_t& scope);
value_t fn_floor(call_scope_t& scope);
value_t fn_abs(call_scope_t& scope); value_t fn_abs(call_scope_t& scope);
value_t fn_justify(call_scope_t& scope); value_t fn_justify(call_scope_t& scope);
value_t fn_quoted(call_scope_t& scope); value_t fn_quoted(call_scope_t& scope);
@ -172,6 +173,12 @@ public:
return option.str(); return option.str();
} }
optional<string> maybe_format(option_t<report_t>& option) {
if (option)
return option.str();
return none;
}
value_t reload_command(call_scope_t&); value_t reload_command(call_scope_t&);
value_t echo_command(call_scope_t& scope); value_t echo_command(call_scope_t& scope);
@ -183,8 +190,6 @@ public:
HANDLED(lots_actual)); HANDLED(lots_actual));
} }
bool maybe_import(const string& module);
void report_options(std::ostream& out) void report_options(std::ostream& out)
{ {
HANDLER(abbrev_len_).report(out); HANDLER(abbrev_len_).report(out);
@ -254,6 +259,7 @@ public:
HANDLER(period_).report(out); HANDLER(period_).report(out);
HANDLER(plot_amount_format_).report(out); HANDLER(plot_amount_format_).report(out);
HANDLER(plot_total_format_).report(out); HANDLER(plot_total_format_).report(out);
HANDLER(prepend_format_).report(out);
HANDLER(price).report(out); HANDLER(price).report(out);
HANDLER(prices_format_).report(out); HANDLER(prices_format_).report(out);
HANDLER(pricesdb_format_).report(out); HANDLER(pricesdb_format_).report(out);
@ -695,6 +701,8 @@ public:
"%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_total)))\n"); "%(format_date(date, \"%Y-%m-%d\")) %(quantity(scrub(display_total)))\n");
}); });
OPTION(report_t, prepend_format_);
OPTION_(report_t, price, DO() { // -I OPTION_(report_t, price, DO() { // -I
parent->HANDLER(display_amount_) parent->HANDLER(display_amount_)
.set_expr(string("--price"), "price(amount_expr)"); .set_expr(string("--price"), "price(amount_expr)");

View file

@ -246,8 +246,10 @@ void serialize(Archive& ar, istream_pos_type& pos, const unsigned int)
#include <boost/python/detail/wrap_python.hpp> #include <boost/python/detail/wrap_python.hpp>
#include <datetime.h> #include <datetime.h>
#include <unicodeobject.h>
#include <boost/python/module_init.hpp> #include <boost/python/module_init.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
#endif // HAVE_BOOST_PYTHON #endif // HAVE_BOOST_PYTHON

View file

@ -132,10 +132,12 @@ namespace {
std::streamsize len, std::streamsize len,
account_t * account, account_t * account,
xact_t * xact, xact_t * xact,
bool honor_strict = true); bool honor_strict = true,
bool defer_expr = false);
bool parse_posts(account_t * account, bool parse_posts(account_t * account,
xact_base_t& xact); xact_base_t& xact,
const bool defer_expr = false);
xact_t * parse_xact(char * line, xact_t * parse_xact(char * line,
std::streamsize len, std::streamsize len,
@ -148,8 +150,10 @@ namespace {
void parse_amount_expr(scope_t& scope, void parse_amount_expr(scope_t& scope,
std::istream& in, std::istream& in,
amount_t& amount, amount_t& amount,
optional<expr_t> * amount_expr,
post_t * post, post_t * post,
const parse_flags_t& flags = PARSE_DEFAULT) const parse_flags_t& flags = PARSE_DEFAULT,
const bool defer_expr = false)
{ {
expr_t expr(in, flags.plus_flags(PARSE_PARTIAL)); expr_t expr(in, flags.plus_flags(PARSE_PARTIAL));
@ -166,19 +170,24 @@ namespace {
if (expr) { if (expr) {
bind_scope_t bound_scope(scope, *post); bind_scope_t bound_scope(scope, *post);
if (defer_expr) {
assert(amount_expr);
*amount_expr = expr;
(*amount_expr)->compile(bound_scope);
} else {
value_t result(expr.calc(bound_scope)); value_t result(expr.calc(bound_scope));
if (result.is_long()) { if (result.is_long()) {
amount = result.to_amount(); amount = result.to_amount();
} else { } else {
if (! result.is_amount()) if (! result.is_amount())
throw_(parse_error, _("Postings may only specify simple amounts")); throw_(amount_error,
_("Amount expressions must result in a simple amount"));
amount = result.as_amount(); amount = result.as_amount();
} }
DEBUG("textual.parse", "The posting amount is " << amount); DEBUG("textual.parse", "The posting amount is " << amount);
} }
} }
}
} }
instance_t::instance_t(std::list<account_t *>& _account_stack, instance_t::instance_t(std::list<account_t *>& _account_stack,
@ -415,9 +424,26 @@ void instance_t::clock_in_directive(char * line,
char * p = skip_ws(line + 22); char * p = skip_ws(line + 22);
char * n = next_element(p, true); char * n = next_element(p, true);
char * end = n ? next_element(n, true) : NULL;
timelog.clock_in(parse_datetime(datetime, current_year), if (end && *end == ';')
account_stack.front()->find_account(p), n ? n : ""); end = skip_ws(end + 1);
else
end = NULL;
position_t position;
position.pathname = pathname;
position.beg_pos = line_beg_pos;
position.beg_line = linenum;
position.end_pos = curr_pos;
position.end_line = linenum;
time_xact_t event(position, parse_datetime(datetime, current_year),
p ? account_stack.front()->find_account(p) : NULL,
n ? n : "",
end ? end : "");
timelog.clock_in(event);
} }
void instance_t::clock_out_directive(char * line, void instance_t::clock_out_directive(char * line,
@ -427,9 +453,26 @@ void instance_t::clock_out_directive(char * line,
char * p = skip_ws(line + 22); char * p = skip_ws(line + 22);
char * n = next_element(p, true); char * n = next_element(p, true);
char * end = n ? next_element(n, true) : NULL;
timelog.clock_out(parse_datetime(datetime, current_year), if (end && *end == ';')
p ? account_stack.front()->find_account(p) : NULL, n ? n : ""); end = skip_ws(end + 1);
else
end = NULL;
position_t position;
position.pathname = pathname;
position.beg_pos = line_beg_pos;
position.beg_line = linenum;
position.end_pos = curr_pos;
position.end_line = linenum;
time_xact_t event(position, parse_datetime(datetime, current_year),
p ? account_stack.front()->find_account(p) : NULL,
n ? n : "",
end ? end : "");
timelog.clock_out(event);
count++; count++;
} }
@ -514,7 +557,7 @@ void instance_t::automated_xact_directive(char * line)
reveal_context = false; reveal_context = false;
if (parse_posts(account_stack.front(), *ae.get())) { if (parse_posts(account_stack.front(), *ae.get(), true)) {
reveal_context = true; reveal_context = true;
journal.auto_xacts.push_back(ae.get()); journal.auto_xacts.push_back(ae.get());
@ -558,7 +601,7 @@ void instance_t::period_xact_directive(char * line)
pe->journal = &journal; pe->journal = &journal;
if (pe->finalize()) { if (pe->finalize()) {
extend_xact_base(&journal, *pe.get(), true); extend_xact_base(&journal, *pe.get());
journal.period_xacts.push_back(pe.get()); journal.period_xacts.push_back(pe.get());
@ -628,7 +671,7 @@ void instance_t::include_directive(char * line)
if (! exists(filename)) if (! exists(filename))
throw_(std::runtime_error, throw_(std::runtime_error,
_("File to include was not found: '%1'" << filename)); _("File to include was not found: '%1'") << filename);
ifstream stream(filename); ifstream stream(filename);
@ -783,7 +826,8 @@ post_t * instance_t::parse_post(char * line,
std::streamsize len, std::streamsize len,
account_t * account, account_t * account,
xact_t * xact, xact_t * xact,
bool honor_strict) bool honor_strict,
bool defer_expr)
{ {
TRACE_START(post_details, 1, "Time spent parsing postings:"); TRACE_START(post_details, 1, "Time spent parsing postings:");
@ -885,8 +929,9 @@ post_t * instance_t::parse_post(char * line,
if (*next != '(') // indicates a value expression if (*next != '(') // indicates a value expression
post->amount.parse(stream, PARSE_NO_REDUCE); post->amount.parse(stream, PARSE_NO_REDUCE);
else else
parse_amount_expr(scope, stream, post->amount, post.get(), parse_amount_expr(scope, stream, post->amount, &post->amount_expr,
PARSE_NO_REDUCE | PARSE_SINGLE | PARSE_NO_ASSIGN); post.get(), PARSE_NO_REDUCE | PARSE_SINGLE |
PARSE_NO_ASSIGN, defer_expr);
if (! post->amount.is_null() && honor_strict && strict && if (! post->amount.is_null() && honor_strict && strict &&
post->amount.has_commodity() && post->amount.has_commodity() &&
@ -931,9 +976,9 @@ post_t * instance_t::parse_post(char * line,
if (*p != '(') // indicates a value expression if (*p != '(') // indicates a value expression
post->cost->parse(cstream, PARSE_NO_MIGRATE); post->cost->parse(cstream, PARSE_NO_MIGRATE);
else else
parse_amount_expr(scope, cstream, *post->cost, post.get(), parse_amount_expr(scope, cstream, *post->cost, NULL, post.get(),
PARSE_NO_MIGRATE | PARSE_SINGLE | PARSE_NO_MIGRATE | PARSE_SINGLE | PARSE_NO_ASSIGN,
PARSE_NO_ASSIGN); defer_expr);
if (post->cost->sign() < 0) if (post->cost->sign() < 0)
throw parse_error(_("A posting's cost may not be negative")); throw parse_error(_("A posting's cost may not be negative"));
@ -983,8 +1028,9 @@ post_t * instance_t::parse_post(char * line,
if (*p != '(') // indicates a value expression if (*p != '(') // indicates a value expression
post->assigned_amount->parse(stream, PARSE_NO_MIGRATE); post->assigned_amount->parse(stream, PARSE_NO_MIGRATE);
else else
parse_amount_expr(scope, stream, *post->assigned_amount, post.get(), parse_amount_expr(scope, stream, *post->assigned_amount, NULL,
PARSE_SINGLE | PARSE_NO_MIGRATE); post.get(), PARSE_SINGLE | PARSE_NO_MIGRATE,
defer_expr);
if (post->assigned_amount->is_null()) { if (post->assigned_amount->is_null()) {
if (post->amount.is_null()) if (post->amount.is_null())
@ -1085,7 +1131,8 @@ post_t * instance_t::parse_post(char * line,
} }
bool instance_t::parse_posts(account_t * account, bool instance_t::parse_posts(account_t * account,
xact_base_t& xact) xact_base_t& xact,
const bool defer_expr)
{ {
TRACE_START(xact_posts, 1, "Time spent parsing postings:"); TRACE_START(xact_posts, 1, "Time spent parsing postings:");
@ -1096,7 +1143,9 @@ bool instance_t::parse_posts(account_t * account,
std::streamsize len = read_line(line); std::streamsize len = read_line(line);
assert(len > 0); assert(len > 0);
if (post_t * post = parse_post(line, len, account, NULL, false)) { if (post_t * post =
parse_post(line, len, account, NULL, /* honor_strict= */ false,
defer_expr)) {
xact.add_post(post); xact.add_post(post);
added = true; added = true;
} }

View file

@ -41,9 +41,7 @@ namespace ledger {
namespace { namespace {
void clock_out_from_timelog(std::list<time_xact_t>& time_xacts, void clock_out_from_timelog(std::list<time_xact_t>& time_xacts,
const datetime_t& when, time_xact_t out_event,
account_t * account,
const char * desc,
journal_t& journal) journal_t& journal)
{ {
time_xact_t event; time_xact_t event;
@ -55,7 +53,7 @@ namespace {
else if (time_xacts.empty()) { else if (time_xacts.empty()) {
throw parse_error(_("Timelog check-out event without a check-in")); throw parse_error(_("Timelog check-out event without a check-in"));
} }
else if (! account) { else if (! out_event.account) {
throw parse_error throw parse_error
(_("When multiple check-ins are active, checking out requires an account")); (_("When multiple check-ins are active, checking out requires an account"));
} }
@ -65,7 +63,7 @@ namespace {
for (std::list<time_xact_t>::iterator i = time_xacts.begin(); for (std::list<time_xact_t>::iterator i = time_xacts.begin();
i != time_xacts.end(); i != time_xacts.end();
i++) i++)
if (account == (*i).account) { if (out_event.account == (*i).account) {
event = *i; event = *i;
found = true; found = true;
time_xacts.erase(i); time_xacts.erase(i);
@ -77,29 +75,39 @@ namespace {
(_("Timelog check-out event does not match any current check-ins")); (_("Timelog check-out event does not match any current check-ins"));
} }
if (desc && event.desc.empty()) { if (out_event.checkin < event.checkin)
event.desc = desc;
desc = NULL;
}
std::auto_ptr<xact_t> curr(new xact_t);
curr->_date = when.date();
curr->code = desc ? desc : "";
curr->payee = event.desc;
if (when < event.checkin)
throw parse_error throw parse_error
(_("Timelog check-out date less than corresponding check-in")); (_("Timelog check-out date less than corresponding check-in"));
if (! out_event.desc.empty() && event.desc.empty()) {
event.desc = out_event.desc;
out_event.desc = empty_string;
}
if (! out_event.note.empty() && event.note.empty())
event.note = out_event.note;
std::auto_ptr<xact_t> curr(new xact_t);
curr->_date = out_event.checkin.date();
curr->code = out_event.desc; // if it wasn't used above
curr->payee = event.desc;
curr->pos = event.position;
if (! event.note.empty())
curr->append_note(event.note.c_str());
char buf[32]; char buf[32];
std::sprintf(buf, "%lds", long((when - event.checkin).total_seconds())); std::sprintf(buf, "%lds", long((out_event.checkin - event.checkin)
.total_seconds()));
amount_t amt; amount_t amt;
amt.parse(buf); amt.parse(buf);
VERIFY(amt.valid()); VERIFY(amt.valid());
post_t * post = new post_t(event.account, amt, POST_VIRTUAL); post_t * post = new post_t(event.account, amt, POST_VIRTUAL);
post->set_state(item_t::CLEARED); post->set_state(item_t::CLEARED);
post->pos = event.position;
curr->add_post(post); curr->add_post(post);
event.account->add_post(post);
if (! journal.add_xact(curr.get())) if (! journal.add_xact(curr.get()))
throw parse_error(_("Failed to record 'out' timelog transaction")); throw parse_error(_("Failed to record 'out' timelog transaction"));
@ -119,19 +127,16 @@ time_log_t::~time_log_t()
accounts.push_back(time_xact.account); accounts.push_back(time_xact.account);
foreach (account_t * account, accounts) foreach (account_t * account, accounts)
clock_out_from_timelog(time_xacts, CURRENT_TIME(), account, NULL, clock_out_from_timelog(time_xacts,
time_xact_t(none, CURRENT_TIME(), account),
journal); journal);
assert(time_xacts.empty()); assert(time_xacts.empty());
} }
} }
void time_log_t::clock_in(const datetime_t& checkin, void time_log_t::clock_in(time_xact_t event)
account_t * account,
const string& desc)
{ {
time_xact_t event(checkin, account, desc);
if (! time_xacts.empty()) { if (! time_xacts.empty()) {
foreach (time_xact_t& time_xact, time_xacts) { foreach (time_xact_t& time_xact, time_xacts) {
if (event.account == time_xact.account) if (event.account == time_xact.account)
@ -142,15 +147,12 @@ void time_log_t::clock_in(const datetime_t& checkin,
time_xacts.push_back(event); time_xacts.push_back(event);
} }
void time_log_t::clock_out(const datetime_t& checkin, void time_log_t::clock_out(time_xact_t event)
account_t * account,
const string& desc)
{ {
if (time_xacts.empty()) if (time_xacts.empty())
throw std::logic_error(_("Timelog check-out event without a check-in")); throw std::logic_error(_("Timelog check-out event without a check-in"));
clock_out_from_timelog(time_xacts, checkin, account, desc.c_str(), clock_out_from_timelog(time_xacts, event, journal);
journal);
} }
} // namespace ledger } // namespace ledger

View file

@ -44,6 +44,7 @@
#include "utils.h" #include "utils.h"
#include "times.h" #include "times.h"
#include "item.h"
namespace ledger { namespace ledger {
@ -56,19 +57,25 @@ public:
datetime_t checkin; datetime_t checkin;
account_t * account; account_t * account;
string desc; string desc;
string note;
position_t position;
time_xact_t() : account(NULL) { time_xact_t() : account(NULL) {
TRACE_CTOR(time_xact_t, ""); TRACE_CTOR(time_xact_t, "");
} }
time_xact_t(const datetime_t& _checkin, time_xact_t(const optional<position_t>& _position,
const datetime_t& _checkin,
account_t * _account = NULL, account_t * _account = NULL,
const string& _desc = "") const string& _desc = "",
: checkin(_checkin), account(_account), desc(_desc) { const string& _note = "")
TRACE_CTOR(time_xact_t, "const datetime_t&, account_t *, const string&"); : checkin(_checkin), account(_account), desc(_desc), note(_note),
position(_position ? *_position : position_t()) {
TRACE_CTOR(time_xact_t,
"position_t, datetime_t, account_t *, string, string");
} }
time_xact_t(const time_xact_t& xact) time_xact_t(const time_xact_t& xact)
: checkin(xact.checkin), account(xact.account), : checkin(xact.checkin), account(xact.account),
desc(xact.desc) { desc(xact.desc), note(xact.note), position(xact.position) {
TRACE_CTOR(time_xact_t, "copy"); TRACE_CTOR(time_xact_t, "copy");
} }
~time_xact_t() throw() { ~time_xact_t() throw() {
@ -87,13 +94,8 @@ public:
} }
~time_log_t(); ~time_log_t();
void clock_in(const datetime_t& checkin, void clock_in(time_xact_t event);
account_t * account = NULL, void clock_out(time_xact_t event);
const string& desc = "");
void clock_out(const datetime_t& checkin,
account_t * account = NULL,
const string& desc = "");
}; };
} // namespace ledger } // namespace ledger

View file

@ -406,8 +406,16 @@ void report_memory(std::ostream& out, bool report_all)
} }
} }
} // namespace ledger
#if defined(STRING_VERIFY_ON) #endif // VERIFY_ON
/**********************************************************************
*
* String wrapper
*/
namespace ledger {
string::string() : std::string() { string::string() : std::string() {
TRACE_CTOR(string, ""); TRACE_CTOR(string, "");
@ -445,18 +453,10 @@ string::~string() throw() {
TRACE_DTOR(string); TRACE_DTOR(string);
} }
#endif // STRING_VERIFY_ON string empty_string("");
} // namespace ledger strings_list split_arguments(const char * line)
#endif // VERIFY_ON
ledger::string empty_string("");
ledger::strings_list split_arguments(const char * line)
{ {
using namespace ledger;
strings_list args; strings_list args;
char buf[4096]; char buf[4096];
@ -506,6 +506,8 @@ ledger::strings_list split_arguments(const char * line)
return args; return args;
} }
} // namespace ledger
/********************************************************************** /**********************************************************************
* *
* Logging * Logging

View file

@ -62,10 +62,6 @@
#define TIMERS_ON 1 #define TIMERS_ON 1
#endif #endif
#if defined(VERIFY_ON)
//#define STRING_VERIFY_ON 1
#endif
/*@}*/ /*@}*/
/** /**
@ -76,11 +72,7 @@
namespace ledger { namespace ledger {
using namespace boost; using namespace boost;
#if defined(STRING_VERIFY_ON)
class string; class string;
#else
typedef std::string string;
#endif
typedef std::list<string> strings_list; typedef std::list<string> strings_list;
@ -162,12 +154,33 @@ void trace_dtor_func(void * ptr, const char * cls_name, std::size_t cls_size);
void report_memory(std::ostream& out, bool report_all = false); void report_memory(std::ostream& out, bool report_all = false);
#if defined(STRING_VERIFY_ON) } // namespace ledger
#else // ! VERIFY_ON
#define VERIFY(x)
#define DO_VERIFY() true
#define TRACE_CTOR(cls, args)
#define TRACE_DTOR(cls)
#endif // VERIFY_ON
#define IF_VERIFY() if (DO_VERIFY())
/*@}*/
/** /**
* This string type is a wrapper around std::string that allows us to * @name String wrapper
* trace constructor and destructor calls. *
* This string type is a wrapper around std::string that allows us to trace
* constructor and destructor calls. It also makes ledger's use of strings a
* unique type, that the Boost.Python code can use as the basis for
* transparent Unicode conversions.
*/ */
/*@{*/
namespace ledger {
class string : public std::string class string : public std::string
{ {
public: public:
@ -240,25 +253,12 @@ inline bool operator!=(const char* __lhs, const string& __rhs)
inline bool operator!=(const string& __lhs, const char* __rhs) inline bool operator!=(const string& __lhs, const char* __rhs)
{ return __lhs.compare(__rhs) != 0; } { return __lhs.compare(__rhs) != 0; }
#endif // STRING_VERIFY_ON extern string empty_string;
strings_list split_arguments(const char * line);
} // namespace ledger } // namespace ledger
#else // ! VERIFY_ON
#define VERIFY(x)
#define DO_VERIFY() true
#define TRACE_CTOR(cls, args)
#define TRACE_DTOR(cls)
#endif // VERIFY_ON
extern ledger::string empty_string;
ledger::strings_list split_arguments(const char * line);
#define IF_VERIFY() if (DO_VERIFY())
/*@}*/ /*@}*/
/** /**

View file

@ -35,7 +35,7 @@
#include "commodity.h" #include "commodity.h"
#include "annotate.h" #include "annotate.h"
#include "pool.h" #include "pool.h"
#include "unistring.h" #include "unistring.h" // for justify()
namespace ledger { namespace ledger {
@ -1439,6 +1439,31 @@ void value_t::in_place_truncate()
throw_(value_error, _("Cannot truncate %1") << label()); throw_(value_error, _("Cannot truncate %1") << label());
} }
void value_t::in_place_floor()
{
switch (type()) {
case INTEGER:
return;
case AMOUNT:
as_amount_lval().in_place_floor();
return;
case BALANCE:
as_balance_lval().in_place_floor();
return;
case SEQUENCE: {
value_t temp;
foreach (const value_t& value, as_sequence())
temp.push_back(value.floored());
*this = temp;
return;
}
default:
break;
}
throw_(value_error, _("Cannot floor %1") << label());
}
void value_t::in_place_unround() void value_t::in_place_unround()
{ {
switch (type()) { switch (type()) {

View file

@ -440,6 +440,13 @@ public:
} }
void in_place_truncate(); void in_place_truncate();
value_t floored() const {
value_t temp(*this);
temp.in_place_floor();
return temp;
}
void in_place_floor();
value_t unrounded() const { value_t unrounded() const {
value_t temp(*this); value_t temp(*this);
temp.in_place_unround(); temp.in_place_unround();

View file

@ -480,7 +480,7 @@ bool xact_t::valid() const
return true; return true;
} }
void auto_xact_t::extend_xact(xact_base_t& xact, bool post_handler) void auto_xact_t::extend_xact(xact_base_t& xact)
{ {
posts_list initial_posts(xact.posts.begin(), xact.posts.end()); posts_list initial_posts(xact.posts.begin(), xact.posts.end());
@ -490,19 +490,31 @@ void auto_xact_t::extend_xact(xact_base_t& xact, bool post_handler)
if (! initial_post->has_flags(ITEM_GENERATED) && if (! initial_post->has_flags(ITEM_GENERATED) &&
predicate(*initial_post)) { predicate(*initial_post)) {
foreach (post_t * post, posts) { foreach (post_t * post, posts) {
amount_t amt; amount_t post_amount;
assert(post->amount); if (post->amount.is_null()) {
if (! post->amount.commodity()) { if (! post->amount_expr)
if ((post_handler && throw_(amount_error,
! initial_post->has_flags(POST_CALCULATED)) || _("Automated transaction's posting has no amount"));
initial_post->amount.is_null())
continue; bind_scope_t bound_scope(*scope_t::default_scope, *initial_post);
amt = initial_post->amount * post->amount; value_t result(post->amount_expr->calc(bound_scope));
if (result.is_long()) {
post_amount = result.to_amount();
} else { } else {
if (post_handler) if (! result.is_amount())
continue; throw_(amount_error,
amt = post->amount; _("Amount expressions must result in a simple amount"));
post_amount = result.as_amount();
} }
} else {
post_amount = post->amount;
}
amount_t amt;
if (! post_amount.commodity())
amt = initial_post->amount * post_amount;
else
amt = post_amount;
IF_DEBUG("xact.extend") { IF_DEBUG("xact.extend") {
DEBUG("xact.extend", DEBUG("xact.extend",
@ -517,12 +529,12 @@ void auto_xact_t::extend_xact(xact_base_t& xact, bool post_handler)
DEBUG("xact.extend", DEBUG("xact.extend",
"Posting on line " << post->pos->beg_line << ": " "Posting on line " << post->pos->beg_line << ": "
<< "amount " << post->amount << ", amt " << amt << "amount " << post_amount << ", amt " << amt
<< " (precision " << post->amount.precision() << " (precision " << post_amount.precision()
<< " != " << amt.precision() << ")"); << " != " << amt.precision() << ")");
#if defined(DEBUG_ON) #if defined(DEBUG_ON)
if (post->amount.keep_precision()) if (post_amount.keep_precision())
DEBUG("xact.extend", " precision is kept"); DEBUG("xact.extend", " precision is kept");
if (amt.keep_precision()) if (amt.keep_precision())
DEBUG("xact.extend", " amt precision is kept"); DEBUG("xact.extend", " amt precision is kept");
@ -542,6 +554,7 @@ void auto_xact_t::extend_xact(xact_base_t& xact, bool post_handler)
new_post->add_flags(ITEM_GENERATED); new_post->add_flags(ITEM_GENERATED);
xact.add_post(new_post); xact.add_post(new_post);
new_post->account->add_post(new_post);
} }
} }
} }
@ -555,11 +568,10 @@ void auto_xact_t::extend_xact(xact_base_t& xact, bool post_handler)
} }
void extend_xact_base(journal_t * journal, void extend_xact_base(journal_t * journal,
xact_base_t& base, xact_base_t& base)
bool post_handler)
{ {
foreach (auto_xact_t * xact, journal->auto_xacts) foreach (auto_xact_t * xact, journal->auto_xacts)
xact->extend_xact(base, post_handler); xact->extend_xact(base);
} }
void to_xml(std::ostream& out, const xact_t& xact) void to_xml(std::ostream& out, const xact_t& xact)

View file

@ -142,7 +142,7 @@ private:
struct xact_finalizer_t { struct xact_finalizer_t {
virtual ~xact_finalizer_t() {} virtual ~xact_finalizer_t() {}
virtual bool operator()(xact_t& xact, bool post) = 0; virtual bool operator()(xact_t& xact) = 0;
}; };
class auto_xact_t : public xact_base_t class auto_xact_t : public xact_base_t
@ -167,7 +167,7 @@ public:
TRACE_DTOR(auto_xact_t); TRACE_DTOR(auto_xact_t);
} }
virtual void extend_xact(xact_base_t& xact, bool post); virtual void extend_xact(xact_base_t& xact);
#if defined(HAVE_BOOST_SERIALIZATION) #if defined(HAVE_BOOST_SERIALIZATION)
private: private:
@ -201,7 +201,7 @@ struct auto_xact_finalizer_t : public xact_finalizer_t
TRACE_DTOR(auto_xact_finalizer_t); TRACE_DTOR(auto_xact_finalizer_t);
} }
virtual bool operator()(xact_t& xact, bool post); virtual bool operator()(xact_t& xact);
#if defined(HAVE_BOOST_SERIALIZATION) #if defined(HAVE_BOOST_SERIALIZATION)
private: private:
@ -258,7 +258,7 @@ class func_finalizer_t : public xact_finalizer_t
func_finalizer_t(); func_finalizer_t();
public: public:
typedef function<bool (xact_t& xact, bool post)> func_t; typedef function<bool (xact_t& xact)> func_t;
func_t func; func_t func;
@ -273,15 +273,15 @@ public:
TRACE_DTOR(func_finalizer_t); TRACE_DTOR(func_finalizer_t);
} }
virtual bool operator()(xact_t& xact, bool post) { virtual bool operator()(xact_t& xact) {
return func(xact, post); return func(xact);
} }
}; };
void extend_xact_base(journal_t * journal, xact_base_t& xact, bool post); void extend_xact_base(journal_t * journal, xact_base_t& xact);
inline bool auto_xact_finalizer_t::operator()(xact_t& xact, bool post) { inline bool auto_xact_finalizer_t::operator()(xact_t& xact) {
extend_xact_base(journal, xact, post); extend_xact_base(journal, xact);
return true; return true;
} }

View file

@ -150,6 +150,7 @@ for line in fd.readlines():
line = re.sub('set_session_context\(\)', line = re.sub('set_session_context\(\)',
'set_session_context()\n self.testSession = None', line) 'set_session_context()\n self.testSession = None', line)
line = re.sub('([a-z_]+?)_t\b', '\\1', line) line = re.sub('([a-z_]+?)_t\b', '\\1', line)
line = re.sub('("[^"]+")', 'u\\1', line)
line = re.sub('std::string\(([^)]+?)\)', '\\1', line) line = re.sub('std::string\(([^)]+?)\)', '\\1', line)
line = re.sub('string\(([^)]+?)\)', '\\1', line) line = re.sub('string\(([^)]+?)\)', '\\1', line)
line = re.sub('\.print\(([^)]+?)\)', '.print_(\\1)', line) line = re.sub('\.print\(([^)]+?)\)', '.print_(\\1)', line)

View file

@ -4,16 +4,16 @@
>>>2 >>>2
While parsing file "$sourcepath/src/amount.h", line 67: While parsing file "$sourcepath/src/amount.h", line 67:
Error: No quantity specified for amount Error: No quantity specified for amount
While parsing file "$sourcepath/src/amount.h", line 712: While parsing file "$sourcepath/src/amount.h", line 721:
Error: Invalid date/time: line amount_t amoun Error: Invalid date/time: line amount_t amoun
While parsing file "$sourcepath/src/amount.h", line 718: While parsing file "$sourcepath/src/amount.h", line 727:
Error: Invalid date/time: line string amount_ Error: Invalid date/time: line string amount_
While parsing file "$sourcepath/src/amount.h", line 724: While parsing file "$sourcepath/src/amount.h", line 733:
Error: Invalid date/time: line string amount_ Error: Invalid date/time: line string amount_
While parsing file "$sourcepath/src/amount.h", line 730: While parsing file "$sourcepath/src/amount.h", line 739:
Error: Invalid date/time: line string amount_ Error: Invalid date/time: line string amount_
While parsing file "$sourcepath/src/amount.h", line 736: While parsing file "$sourcepath/src/amount.h", line 745:
Error: Invalid date/time: line std::ostream& Error: Invalid date/time: line std::ostream&
While parsing file "$sourcepath/src/amount.h", line 743: While parsing file "$sourcepath/src/amount.h", line 752:
Error: Invalid date/time: line std::istream& Error: Invalid date/time: line std::istream&
=== 7 === 7

View file

@ -235,9 +235,7 @@ ledger_la_DEPENDENCIES = $(lib_LTLIBRARIES)
ledger_la_LDFLAGS = -avoid-version -module ledger_la_LDFLAGS = -avoid-version -module
ledger_la_LIBADD = $(LIBOBJS) $(lib_LTLIBRARIES) $(INTLLIBS) ledger_la_LIBADD = $(LIBOBJS) $(lib_LTLIBRARIES) $(INTLLIBS)
pkgpython_PYTHON = python/__init__.py \ pkgpython_PYTHON = python/__init__.py python/server.py
python/hello.py \
python/server.py
endif endif