From 49a356f2d260ed0cc071ca1aa82027bd325b0b8b Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Thu, 12 Nov 2009 05:26:06 -0500 Subject: [PATCH] Added a Journal.collect method to Python This lets you run standard report queries against a Ledger journal and get back a collection of postings, for example: import ledger journal = ledger.Journal("sample.dat") posts = journal.collect("-M assets") for post in posts: print post.account However, this code is not really working yet for a large variety of reasons, having to do with object life-time, shallow copying, and lack of reference counting where it should be. For instance, calling ledger.Journal().collect() fails because the temporary journal object is destroyed and the collection now has a host of bad pointers. Using "for post in journal.collect()" fails too with other bad pointers. And the whole lot of it crashes on exit at the moment. --- src/py_journal.cc | 106 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/src/py_journal.cc b/src/py_journal.cc index a8dae5f7..bea14d66 100644 --- a/src/py_journal.cc +++ b/src/py_journal.cc @@ -35,6 +35,12 @@ #include "pyutils.h" #include "journal.h" #include "xact.h" +#include "post.h" +#include "chain.h" +#include "filters.h" +#include "iterators.h" +#include "scope.h" +#include "report.h" namespace ledger { @@ -134,10 +140,108 @@ namespace { return journal.read(pathname); } + struct collector_wrapper + { + journal_t& journal; + report_t report; + collect_posts * posts_collector; + post_handler_ptr chain; + + collector_wrapper(journal_t& _journal, report_t& base) + : journal(_journal), report(base), + posts_collector(new collect_posts) {} + ~collector_wrapper() { + journal.clear_xdata(); + } + + std::size_t length() const { + return posts_collector->length(); + } + + std::vector::iterator begin() { + return posts_collector->begin(); + } + std::vector::iterator end() { + return posts_collector->end(); + } + }; + + shared_ptr + py_collect(journal_t& journal, const string& query) + { + if (journal.has_xdata()) { + PyErr_SetString(PyExc_RuntimeError, + _("Cannot have multiple journal collections open at once")); + throw_error_already_set(); + } + + report_t& current_report(downcast(*scope_t::default_scope)); + shared_ptr coll(new collector_wrapper(journal, + current_report)); + std::auto_ptr save_journal + (current_report.session.journal.release()); + current_report.session.journal.reset(&journal); + + try { + strings_list remaining = + process_arguments(split_arguments(query.c_str()), coll->report); + coll->report.normalize_options("register"); + + value_t args; + foreach (const string& arg, remaining) + args.push_back(string_value(arg)); + coll->report.parse_query_args(args, "@Journal.collect"); + + journal_posts_iterator walker(coll->journal); + coll->chain = + chain_post_handlers(coll->report, + post_handler_ptr(coll->posts_collector)); + pass_down_posts(coll->chain, walker); + } + catch (...) { + current_report.session.journal.release(); + current_report.session.journal.reset(save_journal.release()); + throw; + } + current_report.session.journal.release(); + current_report.session.journal.reset(save_journal.release()); + + return coll; + } + + post_t * posts_getitem(collector_wrapper& collector, long i) + { + post_t * post = collector.posts_collector->posts[i]; + std::cerr << typeid(post).name() << std::endl; + std::cerr << typeid(*post).name() << std::endl; + std::cerr << typeid(post->account).name() << std::endl; + std::cerr << typeid(*post->account).name() << std::endl; + return post; + } + } // unnamed namespace void export_journal() { + class_< item_handler, shared_ptr >, + boost::noncopyable >("PostHandler") + ; + + class_< collect_posts, bases >, + shared_ptr, boost::noncopyable >("PostCollector") + .def("__len__", &collect_posts::length) + .def("__iter__", range > + (&collect_posts::begin, &collect_posts::end)) + ; + + class_< collector_wrapper, shared_ptr, + boost::noncopyable >("PostCollectorWrapper", no_init) + .def("__len__", &collector_wrapper::length) + .def("__getitem__", posts_getitem, return_internal_reference<>()) + .def("__iter__", range > + (&collector_wrapper::begin, &collector_wrapper::end)) + ; + class_< journal_t::fileinfo_t > ("FileInfo") .def(init()) @@ -200,6 +304,8 @@ void export_journal() .def("has_xdata", &journal_t::has_xdata) .def("clear_xdata", &journal_t::clear_xdata) + .def("collect", py_collect) + .def("valid", &journal_t::valid) ; }