Added a new 'python' directive
This commit is contained in:
parent
ff89cb9c4d
commit
f6c087cfe4
10 changed files with 190 additions and 33 deletions
|
|
@ -147,7 +147,7 @@ void export_value()
|
|||
.def(init<balance_t>())
|
||||
.def(init<mask_t>())
|
||||
.def(init<std::string>())
|
||||
// jww (2009-11-02): Need to support conversion eof value_t::sequence_t
|
||||
// jww (2009-11-02): Need to support conversion of value_t::sequence_t
|
||||
//.def(init<value_t::sequence_t>())
|
||||
.def(init<value_t>())
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include <system.hh>
|
||||
|
||||
#include "pyinterp.h"
|
||||
#include "pyutils.h"
|
||||
#include "account.h"
|
||||
#include "xact.h"
|
||||
#include "post.h"
|
||||
|
|
@ -103,7 +104,7 @@ void python_interpreter_t::initialize()
|
|||
|
||||
hack_system_paths();
|
||||
|
||||
object main_module = python::import("__main__");
|
||||
main_module = python::import("__main__");
|
||||
if (! main_module)
|
||||
throw_(std::runtime_error,
|
||||
_("Python failed to initialize (couldn't find __main__)"));
|
||||
|
|
@ -446,28 +447,56 @@ expr_t::ptr_op_t python_interpreter_t::lookup(const symbol_t::kind_t kind,
|
|||
}
|
||||
|
||||
namespace {
|
||||
void append_value(list& lst, const value_t& value)
|
||||
object convert_value_to_python(const value_t& val)
|
||||
{
|
||||
if (value.is_scope()) {
|
||||
const scope_t * scope = value.as_scope();
|
||||
if (const post_t * post = dynamic_cast<const post_t *>(scope))
|
||||
lst.append(ptr(post));
|
||||
else if (const xact_t * xact = dynamic_cast<const xact_t *>(scope))
|
||||
lst.append(ptr(xact));
|
||||
else if (const account_t * account =
|
||||
dynamic_cast<const account_t *>(scope))
|
||||
lst.append(ptr(account));
|
||||
else if (const period_xact_t * period_xact =
|
||||
dynamic_cast<const period_xact_t *>(scope))
|
||||
lst.append(ptr(period_xact));
|
||||
else if (const auto_xact_t * auto_xact =
|
||||
dynamic_cast<const auto_xact_t *>(scope))
|
||||
lst.append(ptr(auto_xact));
|
||||
else
|
||||
throw_(std::logic_error,
|
||||
_("Cannot downcast scoped object to specific type"));
|
||||
} else {
|
||||
lst.append(value);
|
||||
switch (val.type()) {
|
||||
case value_t::VOID: // a null value (i.e., uninitialized)
|
||||
return object();
|
||||
case value_t::BOOLEAN: // a boolean
|
||||
return object(val.to_boolean());
|
||||
case value_t::DATETIME: // a date and time (Boost posix_time)
|
||||
return object(val.to_datetime());
|
||||
case value_t::DATE: // a date (Boost gregorian::date)
|
||||
return object(val.to_date());
|
||||
case value_t::INTEGER: // a signed integer value
|
||||
return object(val.to_long());
|
||||
case value_t::AMOUNT: // a ledger::amount_t
|
||||
return object(val.as_amount());
|
||||
case value_t::BALANCE: // a ledger::balance_t
|
||||
return object(val.as_balance());
|
||||
case value_t::STRING: // a string object
|
||||
return object(handle<>(borrowed(str_to_py_unicode(val.as_string()))));
|
||||
case value_t::MASK: // a regular expression mask
|
||||
return object(handle<>(borrowed(str_to_py_unicode(val.as_mask().str()))));
|
||||
case value_t::SEQUENCE: { // a vector of value_t objects
|
||||
list arglist;
|
||||
foreach (const value_t& elem, val.as_sequence())
|
||||
arglist.append(elem);
|
||||
return arglist;
|
||||
}
|
||||
case value_t::SCOPE: // a pointer to a scope
|
||||
if (const scope_t * scope = val.as_scope()) {
|
||||
if (const post_t * post = dynamic_cast<const post_t *>(scope))
|
||||
return object(ptr(post));
|
||||
else if (const xact_t * xact = dynamic_cast<const xact_t *>(scope))
|
||||
return object(ptr(xact));
|
||||
else if (const account_t * account =
|
||||
dynamic_cast<const account_t *>(scope))
|
||||
return object(ptr(account));
|
||||
else if (const period_xact_t * period_xact =
|
||||
dynamic_cast<const period_xact_t *>(scope))
|
||||
return object(ptr(period_xact));
|
||||
else if (const auto_xact_t * auto_xact =
|
||||
dynamic_cast<const auto_xact_t *>(scope))
|
||||
return object(ptr(auto_xact));
|
||||
else
|
||||
throw_(std::logic_error,
|
||||
_("Cannot downcast scoped object to specific type"));
|
||||
}
|
||||
return object();
|
||||
case value_t::ANY: // a pointer to an arbitrary object
|
||||
assert("Attempted to convert an Value.ANY object to Python" == NULL);
|
||||
return object();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -491,9 +520,9 @@ value_t python_interpreter_t::functor_t::operator()(call_scope_t& args)
|
|||
// rather than a sequence of arguments?
|
||||
if (args.value().is_sequence())
|
||||
foreach (const value_t& value, args.value().as_sequence())
|
||||
append_value(arglist, value);
|
||||
arglist.append(convert_value_to_python(value));
|
||||
else
|
||||
append_value(arglist, args.value());
|
||||
arglist.append(convert_value_to_python(args.value()));
|
||||
|
||||
if (PyObject * val =
|
||||
PyObject_CallObject(func.ptr(), python::tuple(arglist).ptr())) {
|
||||
|
|
|
|||
|
|
@ -41,8 +41,9 @@ namespace ledger {
|
|||
class python_interpreter_t : public session_t
|
||||
{
|
||||
public:
|
||||
python::dict main_nspace;
|
||||
bool is_initialized;
|
||||
python::object main_module;
|
||||
python::dict main_nspace;
|
||||
bool is_initialized;
|
||||
|
||||
python_interpreter_t()
|
||||
: session_t(), main_nspace(), is_initialized(false) {
|
||||
|
|
|
|||
|
|
@ -40,6 +40,9 @@
|
|||
#include "query.h"
|
||||
#include "pstream.h"
|
||||
#include "pool.h"
|
||||
#if defined(HAVE_BOOST_PYTHON)
|
||||
#include "pyinterp.h"
|
||||
#endif
|
||||
|
||||
#define TIMELOG_SUPPORT 1
|
||||
#if defined(TIMELOG_SUPPORT)
|
||||
|
|
@ -104,6 +107,10 @@ namespace {
|
|||
return (in.good() && ! in.eof() &&
|
||||
(in.peek() == ' ' || in.peek() == '\t'));
|
||||
}
|
||||
bool peek_blank_line() {
|
||||
return (in.good() && ! in.eof() &&
|
||||
(in.peek() == '\n' || in.peek() == '\r'));
|
||||
}
|
||||
|
||||
void read_next_directive();
|
||||
|
||||
|
|
@ -157,6 +164,10 @@ namespace {
|
|||
void assert_directive(char * line);
|
||||
void check_directive(char * line);
|
||||
|
||||
#if defined(HAVE_BOOST_PYTHON)
|
||||
void python_directive(char * line);
|
||||
#endif
|
||||
|
||||
post_t * parse_post(char * line,
|
||||
std::streamsize len,
|
||||
account_t * account,
|
||||
|
|
@ -1094,6 +1105,48 @@ void instance_t::comment_directive(char * line)
|
|||
}
|
||||
}
|
||||
|
||||
#if defined(HAVE_BOOST_PYTHON)
|
||||
void instance_t::python_directive(char * line)
|
||||
{
|
||||
std::ostringstream script;
|
||||
|
||||
if (line)
|
||||
script << skip_ws(line) << '\n';
|
||||
|
||||
std::size_t indent = 0;
|
||||
|
||||
while (peek_whitespace_line() || peek_blank_line()) {
|
||||
if (read_line(line) > 0) {
|
||||
if (! indent) {
|
||||
const char * p = line;
|
||||
while (*p && std::isspace(*p)) {
|
||||
++indent;
|
||||
++p;
|
||||
}
|
||||
}
|
||||
|
||||
const char * p = line;
|
||||
for (std::size_t i = 0; i < indent; i++) {
|
||||
if (std::isspace(*p))
|
||||
++p;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (*p)
|
||||
script << p << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
if (! python_session->is_initialized)
|
||||
python_session->initialize();
|
||||
|
||||
python_session->main_nspace["journal"] =
|
||||
python::object(python::ptr(context.journal));
|
||||
python_session->eval(script.str(), python_interpreter_t::PY_EVAL_MULTI);
|
||||
}
|
||||
#endif // HAVE_BOOST_PYTHON
|
||||
|
||||
bool instance_t::general_directive(char * line)
|
||||
{
|
||||
char buf[8192];
|
||||
|
|
@ -1178,6 +1231,15 @@ bool instance_t::general_directive(char * line)
|
|||
payee_directive(arg);
|
||||
return true;
|
||||
}
|
||||
else if (std::strcmp(p, "python") == 0) {
|
||||
#if defined(HAVE_BOOST_PYTHON)
|
||||
python_directive(arg);
|
||||
#else
|
||||
throw_(parse_error,
|
||||
_("'python' directive seen, but Python support is missing"));
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case 't':
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ class LedgerHarness:
|
|||
failed = 0
|
||||
verify = False
|
||||
gmalloc = False
|
||||
python = False
|
||||
|
||||
def __init__(self, argv):
|
||||
if not os.path.isfile(argv[1]):
|
||||
|
|
@ -49,6 +50,9 @@ class LedgerHarness:
|
|||
self.failed = 0
|
||||
self.verify = '--verify' in argv
|
||||
self.gmalloc = '--gmalloc' in argv
|
||||
self.python = '--python' in argv
|
||||
|
||||
os.chdir(self.sourcepath)
|
||||
|
||||
def run(self, command, verify=None, gmalloc=None, columns=True):
|
||||
env = os.environ.copy()
|
||||
|
|
|
|||
|
|
@ -179,7 +179,9 @@ if __name__ == '__main__':
|
|||
|
||||
if os.path.isdir(tests):
|
||||
tests = [os.path.join(tests, x)
|
||||
for x in os.listdir(tests) if x.endswith('.test')]
|
||||
for x in os.listdir(tests)
|
||||
if (x.endswith('.test') and
|
||||
(not '_py.test' in x or harness.python))]
|
||||
if pool:
|
||||
pool.map(do_test, tests, 1)
|
||||
else:
|
||||
|
|
|
|||
26
test/baseline/dir-python_py.test
Normal file
26
test/baseline/dir-python_py.test
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
python
|
||||
import os
|
||||
def check_path(path_value):
|
||||
return os.path.isfile(path_value)
|
||||
|
||||
tag PATH
|
||||
check check_path(value)
|
||||
|
||||
2012-02-29 KFC
|
||||
; PATH: test/baseline/feat-import_py.test
|
||||
Expenses:Food $20
|
||||
Assets:Cash
|
||||
|
||||
2012-02-29 KFC
|
||||
; PATH: test/baseline/feat-import_noexist.test
|
||||
Expenses:Food $20
|
||||
Assets:Cash
|
||||
|
||||
test reg
|
||||
12-Feb-29 KFC Expenses:Food $20 $20
|
||||
Assets:Cash $-20 0
|
||||
12-Feb-29 KFC Expenses:Food $20 $20
|
||||
Assets:Cash $-20 0
|
||||
__ERROR__
|
||||
Warning: "$sourcepath/test/baseline/dir-python_py.test", line 17: Metadata check failed for (PATH: test/baseline/feat-import_noexist.test): check_path(value)
|
||||
end test
|
||||
23
test/baseline/feat-import_py.test
Normal file
23
test/baseline/feat-import_py.test
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
--import featimport.py
|
||||
|
||||
tag PATH
|
||||
check check_path(value)
|
||||
|
||||
2012-02-29 KFC
|
||||
; PATH: test/baseline/feat-import_py.test
|
||||
Expenses:Food $20
|
||||
Assets:Cash
|
||||
|
||||
2012-02-29 KFC
|
||||
; PATH: test/baseline/feat-import_noexist.test
|
||||
Expenses:Food $20
|
||||
Assets:Cash
|
||||
|
||||
test reg
|
||||
12-Feb-29 KFC Expenses:Food $20 $20
|
||||
Assets:Cash $-20 0
|
||||
12-Feb-29 KFC Expenses:Food $20 $20
|
||||
Assets:Cash $-20 0
|
||||
__ERROR__
|
||||
Warning: "$sourcepath/test/baseline/feat-import_py.test", line 14: Metadata check failed for (PATH: test/baseline/feat-import_noexist.test): check_path(value)
|
||||
end test
|
||||
4
test/baseline/featimport.py
Normal file
4
test/baseline/featimport.py
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
import os
|
||||
|
||||
def check_path(path_value):
|
||||
return os.path.isfile(str(path_value))
|
||||
|
|
@ -358,8 +358,14 @@ RegressTests_SOURCES = test/RegressTests.py
|
|||
|
||||
EXTRA_DIST += test/regress test/convert.py test/LedgerHarness.py
|
||||
|
||||
if HAVE_BOOST_PYTHON
|
||||
TEST_PYTHON_FLAGS = --python
|
||||
else
|
||||
TEST_PYTHON_FLAGS =
|
||||
endif
|
||||
|
||||
RegressTests: $(srcdir)/test/RegressTests.py
|
||||
echo "$(PYTHON) $(srcdir)/test/RegressTests.py -j$(JOBS) $(top_builddir)/ledger$(EXEEXT) $(srcdir) $(srcdir)/test/regress \"\$$@\"" > $@
|
||||
echo "$(PYTHON) $(srcdir)/test/RegressTests.py -j$(JOBS) $(top_builddir)/ledger$(EXEEXT) $(srcdir) $(srcdir)/test/regress $(TEST_PYTHON_FLAGS) \"\$$@\"" > $@
|
||||
chmod 755 $@
|
||||
|
||||
BaselineTests_SOURCES = test/RegressTests.py
|
||||
|
|
@ -367,7 +373,7 @@ BaselineTests_SOURCES = test/RegressTests.py
|
|||
EXTRA_DIST += test/baseline
|
||||
|
||||
BaselineTests: $(srcdir)/test/RegressTests.py
|
||||
echo "$(PYTHON) $(srcdir)/test/RegressTests.py -j$(JOBS) $(top_builddir)/ledger$(EXEEXT) $(srcdir) $(srcdir)/test/baseline \"\$$@\"" > $@
|
||||
echo "$(PYTHON) $(srcdir)/test/RegressTests.py -j$(JOBS) $(top_builddir)/ledger$(EXEEXT) $(srcdir) $(srcdir)/test/baseline $(TEST_PYTHON_FLAGS) \"\$$@\"" > $@
|
||||
chmod 755 $@
|
||||
|
||||
ManualTests_SOURCES = test/RegressTests.py
|
||||
|
|
@ -375,7 +381,7 @@ ManualTests_SOURCES = test/RegressTests.py
|
|||
EXTRA_DIST += test/manual
|
||||
|
||||
ManualTests: $(srcdir)/test/RegressTests.py
|
||||
echo "$(PYTHON) $(srcdir)/test/RegressTests.py -j$(JOBS) $(top_builddir)/ledger$(EXEEXT) $(srcdir) $(srcdir)/test/manual \"\$$@\"" > $@
|
||||
echo "$(PYTHON) $(srcdir)/test/RegressTests.py -j$(JOBS) $(top_builddir)/ledger$(EXEEXT) $(srcdir) $(srcdir)/test/manual $(TEST_PYTHON_FLAGS) \"\$$@\"" > $@
|
||||
chmod 755 $@
|
||||
|
||||
ConfirmTests_SOURCES = test/ConfirmTests.py
|
||||
|
|
@ -390,13 +396,13 @@ test/input/mondo.dat: test/input/standard.dat
|
|||
done
|
||||
|
||||
ConfirmTests: $(srcdir)/test/ConfirmTests.py
|
||||
echo "$(PYTHON) $(srcdir)/test/ConfirmTests.py $(top_builddir)/ledger$(EXEEXT) $(srcdir) $(srcdir)/test/input \"\$$@\"" > $@
|
||||
echo "$(PYTHON) $(srcdir)/test/ConfirmTests.py $(top_builddir)/ledger$(EXEEXT) $(srcdir) $(srcdir)/test/input $(TEST_PYTHON_FLAGS) \"\$$@\"" > $@
|
||||
chmod 755 $@
|
||||
|
||||
GenerateTests_SOURCES = test/GenerateTests.py
|
||||
|
||||
GenerateTests: $(srcdir)/test/GenerateTests.py
|
||||
echo "$(PYTHON) $(srcdir)/test/GenerateTests.py -j$(JOBS) $(top_builddir)/ledger$(EXEEXT) $(srcdir) 1 ${1:-20} \"\$$@\"" > $@
|
||||
echo "$(PYTHON) $(srcdir)/test/GenerateTests.py -j$(JOBS) $(top_builddir)/ledger$(EXEEXT) $(srcdir) 1 ${1:-20} $(TEST_PYTHON_FLAGS) \"\$$@\"" > $@
|
||||
chmod 755 $@
|
||||
|
||||
CheckTests_SOURCES = test/CheckTests.py
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue