aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS4
-rw-r--r--INSTALL33
-rw-r--r--Makefile146
-rw-r--r--THANKS4
-rw-r--r--TODO37
-rwxr-xr-xextract_conversion_functions.py177
-rw-r--r--g2p2g_smob.c164
-rw-r--r--g2p2g_smob.h79
-rw-r--r--guiletap.scm115
-rw-r--r--guiletopy.c1141
-rw-r--r--guiletopy.h104
-rw-r--r--pyguile.c497
-rw-r--r--pyguile.h87
-rw-r--r--pyguile.scm.in49
-rw-r--r--pyscm.c577
-rw-r--r--pyscm.h55
-rw-r--r--pysmob.c342
-rw-r--r--pysmob.h53
-rw-r--r--pytoguile.c747
-rw-r--r--pytoguile.h50
-rwxr-xr-xt/01_basic.t34
-rw-r--r--t/02_pyguile.t49
-rw-r--r--t/03_guile2python.t58
-rw-r--r--t/04_python_apply.t66
-rw-r--r--t/05_pysmobs.t128
-rw-r--r--t/06_guile2python.t143
-rw-r--r--t/07_apply.t129
-rw-r--r--t/08_eval.t69
-rw-r--r--t/09_import.t59
-rw-r--r--t/10_python2guile.t56
-rw-r--r--t/11_g2p2g.t49
-rw-r--r--t/12_g2p_templated.t225
-rw-r--r--t/13_hashes.t167
-rw-r--r--t/14_p2g_templated.t293
-rw-r--r--t/15_p2g_errors.t126
-rwxr-xr-xt/16_verbose.t50
-rw-r--r--t/17_verbose.t161
-rw-r--r--t/18_bignum.t55
-rw-r--r--t/19_verbose_always.t733
-rw-r--r--t/20_pyscm.t247
-rw-r--r--t/__init__.py2
-rwxr-xr-xt/scripts/RunGuileTests.pl36
-rw-r--r--t/scripts/__init__.py0
-rw-r--r--t/scripts/t2conv.py67
-rw-r--r--t/scripts/t4apply.py46
-rw-r--r--t/scripts/t5smobs.py51
-rw-r--r--t/scripts/t7except.py32
-rw-r--r--t/scripts/test_auxiliary_functions.scm177
-rw-r--r--verbose.c90
-rw-r--r--verbose.h66
50 files changed, 7925 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..e842878
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,4 @@
+tddpirate: Omer Zak <w1@zak.co.il>
+#
+# Created the PyGuile package.
+#
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..c8c0613
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,33 @@
+Installation Instructions
+*************************
+
+Copyright (C) 2008 Omer Zak.
+
+This file is free documentation; Omer Zak gives unlimited permission to
+copy, distribute and modify it.
+
+Dependencies
+============
+Before installing the package, ensure that you have the following
+dependencies installed:
+1. Python 2.4.x (if you use a different version of Python, edit the
+ values assigned to PYINC and PYLIB in the Makefile).
+2. The CPAN module TAP::Harness (only if you intend to run tests).
+ If necessary, edit the value assigned to TEST_LIBDIRS to point
+ at the lib directory where this package is installed.
+3. Guile 1.6.x (if you use a different version of Guile and encounter
+ problems, let me know about this).
+
+Basic Installation
+==================
+1. Gunzip and untar the archive file into a directory.
+ You will see a subdirectory having name of the form pyguile-x.y.z.
+2. `cd' into this subdirectory.
+3. Type `make' to build the library file `libpyguile.so'.
+4. Optionally, type `make check' to run the package's self tests.
+5. Currently, there is no `make install'.
+ You'll have to manually copy the library file `libpyguile.so' and
+ the module definition file `pyguile.scm' to a location where they
+ will be found by Guile.
+ You can obtain the locations searched by Guile by invoking the
+ command `(display %load-path)(newline)'.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..2cef720
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,146 @@
+# Infrastructure for invoking Python from Guile, via extension
+# functions.
+########################################################################
+#
+# Copyright (C) 2008 Omer Zak.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library, in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+#
+# For licensing issues, contact <w1@zak.co.il>.
+#
+########################################################################
+#
+# make
+# Build the software
+#
+# make coverage
+# Build a special version which includes coverage analysis code
+#
+# make clean
+# Clean intermediate files
+#
+# make distclean
+# Clean also targets
+#
+# make check
+# Run all tests in the test suite
+#
+# make gcov.out
+# Summarize coverage analysis files into gcov.out
+#
+########################################################################
+
+#PYVERSION = `python -V 2>&1 | cut -d\ -f2 | cut -d. -f1-2`
+PYVERSION = `python -c "import sys;sys.stdout.write(sys.version[:3])"`
+
+PYINC = -I/usr/include/python$(PYVERSION)
+PYLIB = -lpython$(PYVERSION)
+PERL = /usr/bin/perl
+TEST_VERBOSE = 0
+TEST_FILES = t/*.t
+TEST_LIBDIRS =
+RUN_GUILE_TESTS = ./t/scripts/RunGuileTests.pl
+EXTRACT_CONVERSION_FUNCTIONS_PY = ./extract_conversion_functions.py
+EXTRACT_CONVERSION_FUNCTIONS = $(EXTRACT_CONVERSION_FUNCTIONS_PY) --inc
+EXTRACT_CONVERSION_EXPORTS = $(EXTRACT_CONVERSION_FUNCTIONS_PY) --scm pyguile.scm.in
+
+CDEBUG = -g -Wall
+CFLAGS = $(CDEBUG) `guile-config compile` $(PYINC) $(GCOVFLAGS)
+CPPFLAGS = `guile-config compile` $(PYINC)
+LDFLAGS = `guile-config link` $(GCOVFLAGS)
+RM = rm -v
+
+
+SRCS = pyguile.c pysmob.c pytoguile.c guiletopy.c g2p2g_smob.c verbose.c pyscm.c
+TARGETS = libpyguile.so pyguile.scm
+
+all: $(TARGETS)
+all: GCOVFLAGS =
+coverage: $(TARGETS)
+coverage: GCOVFLAGS = -fprofile-arcs -ftest-coverage
+
+libpyguile.so: $(SRCS:.c=.o)
+ $(CC) -shared $(LDFLAGS) -o $@ $^ $(PYLIB) $(LIBS)
+
+%.o: %.c
+ $(CC) $(CFLAGS) -c -o $@ -fPIC $<
+
+guiletopy.inc: guiletopy.c $(EXTRACT_CONVERSION_FUNCTIONS_PY)
+ $(EXTRACT_CONVERSION_FUNCTIONS) < $< > $@
+
+pytoguile.inc: pytoguile.c $(EXTRACT_CONVERSION_FUNCTIONS_PY)
+ $(EXTRACT_CONVERSION_FUNCTIONS) < $< > $@
+
+pyguile.scm: pyguile.scm.in guiletopy.c pytoguile.c $(EXTRACT_CONVERSION_FUNCTIONS_PY)
+ cat guiletopy.c pytoguile.c | $(EXTRACT_CONVERSION_EXPORTS) > $@
+
+version.h: BUILD
+ touch BUILD
+ mv BUILD BUILD~
+ echo $$(( 1 + `cat BUILD~` )) > BUILD
+ echo "#define PYGUILE_VERSION \"0.3.1\"" > $@
+ echo "#define PYGUILE_BUILD \""`cat $<`"\"" >> $@
+
+.build:
+ echo 1 > $@
+ echo Initial build number file was created
+
+########################################################################
+# Clean up
+
+FILES_TO_CLEAN = *~ *.d *.inc *.o core *.pyc \
+ version.h \
+ *.gcda *.gcno *.gcov gcov.out
+clean:
+ -$(RM) $(FILES_TO_CLEAN)
+ -cd t; pwd; $(RM) $(FILES_TO_CLEAN)
+ -cd t/scripts; pwd; $(RM) $(FILES_TO_CLEAN)
+
+distclean: clean
+ -$(RM) $(TARGETS)
+
+########################################################################
+# Test and coverage analysis
+
+check: $(TARGETS)
+ $(PERL) $(TEST_LIBDIRS) $(RUN_GUILE_TESTS) $(TEST_FILES)
+
+gcov.out:
+ gcov -a -l -p *.c
+ -$(RM) *usr*include*.h.gcov
+ cat *.gcov > gcov.out
+
+manualcheck: $(TARGETS)
+ for testfile in $(TEST_FILES); do guile -s $$testfile; done
+
+########################################################################
+# Build dependencies
+
+-include $(SRCS:.c=.d)
+
+%.d: %.c
+ @set -e; rm -f $@; \
+ $(CC) -M $(CPPFLAGS) $< > $@.$$$$; \
+ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
+ rm -f $@.$$$$
+
+# We want to force those *.inc files to be created before building
+# the *.d files.
+pyguile.d: version.h
+pytoguile.d: pytoguile.inc
+guiletopy.d: guiletopy.inc
+
+########################################################################
+# End of PyGuile Makefile
diff --git a/THANKS b/THANKS
new file mode 100644
index 0000000..3c77a74
--- /dev/null
+++ b/THANKS
@@ -0,0 +1,4 @@
+Shlomi Fish <shlomif@iglu.org.il>
+ * For the initial impetus for actually working on the project.
+ * For testing an initial version of PyGuile on a system with
+ Python2.5 and Guile 1.8.5.
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..d9e5c9e
--- /dev/null
+++ b/TODO
@@ -0,0 +1,37 @@
+Roadmap for future PyGuile versions:
+
+* Make PyGuile work with Python 2.7, Python 3, guile 1.8, and guile 2.0.
+
+* Option for turning on/off verbosity.
+ Infrastructure: DONE
+ Modules: DONE (except for two functions in pytoguile.c)
+ Testing: partially done
+
+* Allow Python objects (PyObject *) to own Guile objects (SCM).
+ Function objects: DONE
+ Objects with attributes: not done (implemented but not tested)
+
+* Allow Python code to invoke Guile functions.
+ DONE.
+
+* Macros which will allow Python code to be invoked from Guile without
+ special keywords or data formats. Introspection of imported Python
+ modules to allow Guile to recognize Python code without having to do
+ anything special.
+ - Also, natural way to set/fetch values of Python variables.
+
+* Support for lazy conversion between heavy Python and Guile values
+
+Roadmap for the build process:
+
+* libtool/Autoconf/Automake
+
+* Deb packaging auxiliary files
+
+* RPM packaging auxiliary files
+
+Roadmap for other similar libraries:
+
+* Support for invoking Perl code from Guile.
+
+* Support for other Scheme implementations.
diff --git a/extract_conversion_functions.py b/extract_conversion_functions.py
new file mode 100755
index 0000000..c20f1e1
--- /dev/null
+++ b/extract_conversion_functions.py
@@ -0,0 +1,177 @@
+#!/usr/bin/python
+#
+# extract_conversion_functions.py
+#
+# Extract pytoguile and guiletopy conversion functions, and create
+# calls to bind_g2p_function/bind_p2g_function as needed.
+#
+# This script is an auxiliary script for building PyGuile.
+#
+########################################################################
+#
+# Copyright (C) 2008 Omer Zak.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library, in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+#
+# For licensing issues, contact <w1@zak.co.il>.
+#
+########################################################################
+# $Id: $
+########################################################################
+
+import re
+import sys
+import exceptions
+import time
+from optparse import OptionParser
+
+########################################################################
+
+class error(exceptions.Exception):
+ def __init__(self,*args):
+ self.args = args
+
+########################################################################
+# Common code
+########################################################################
+
+def extract_fnames(inptext=None):
+ """Extract g2p and p2g function names from the input text.
+ The return value is 2-tuple of (g2p_funcs,p2g_funcs), each
+ element being list of the names of the corresponding
+ functions.
+ """
+ g2p_pattern = re.compile(r'PyObject\s+\*\s+(g\w+)\s*\(\s*SCM\s+[^)]+\)',re.S)
+ p2g_pattern = re.compile(r'SCM\s+(p\w+)\s*\(\s*PyObject\s*\*\s*[^)]+\)',re.S)
+
+ g2p_funcs = g2p_pattern.findall(inptext)
+ p2g_funcs = p2g_pattern.findall(inptext)
+
+ return((g2p_funcs,p2g_funcs))
+
+########################################################################
+
+def extract_converters(stdin=sys.stdin,stdout=sys.stdout,stderr=sys.stderr):
+ """Extract function declarations for *.inc files, as needed
+ by the PyGuile project.
+ """
+ (g2p_funcs,p2g_funcs) = extract_fnames(inptext=stdin.read())
+ tstamp = time.strftime("%Y%b%d-%H:%M:%S")
+
+ if (len(g2p_funcs) > 0):
+ stdout.write("// Registration of guiletopy functions\n")
+ stdout.write("// Autogenerated at date %s\n" % tstamp)
+ stdout.write("void\nbind_g2p_functions(void)\n{\n")
+ for g2p_func in g2p_funcs:
+ if (g2p_func == "guile2python"):
+ stdout.write(""" guile2python_smob = scm_permanent_object(bind_g2p_function(%(fname)s,"%(fname)s"));\n"""
+ % { "fname" : g2p_func})
+ stdout.write(""" guile2python_template_default = scm_permanent_object(scm_list_1(guile2python_smob));\n""")
+ stdout.write(""" guile2python_pair_template_default = scm_permanent_object(scm_cons(guile2python_template_default,guile2python_template_default));\n""")
+ stdout.write(""" guileassoc2pythondict_default = scm_permanent_object(scm_c_make_hash_table (2));\n""")
+ stdout.write(""" g2p_alist_template_default = scm_permanent_object(scm_list_1(scm_cons(guile2python_smob,guile2python_smob)));\n""")
+ else:
+ stdout.write(""" bind_g2p_function(%(fname)s,"%(fname)s");\n"""
+ % { "fname" : g2p_func})
+ stdout.write("}\n")
+ stdout.write("// End of guiletopy functions\n")
+ if (len(p2g_funcs) > 0):
+ stdout.write("// Registration of pytoguile functions\n")
+ stdout.write("// Autogenerated at date %s\n" % tstamp)
+ stdout.write("void\nbind_p2g_functions(void)\n{\n")
+ for p2g_func in p2g_funcs:
+ if (p2g_func == "python2guile"):
+ stdout.write(""" python2guile_smob = scm_permanent_object(bind_p2g_function(%(fname)s,"%(fname)s"));\n"""
+ % { "fname" : p2g_func})
+ stdout.write(""" python2guile_dict_default = scm_permanent_object(scm_cons(python2guile_smob,python2guile_smob));\n""")
+ else:
+ stdout.write(""" bind_p2g_function(%(fname)s,"%(fname)s");\n"""
+ % { "fname" : p2g_func})
+ stdout.write("}\n")
+ stdout.write("// End of pytoguile functions\n")
+
+########################################################################
+
+def extract_exports(template=None,stdin=sys.stdin,stdout=sys.stdout,stderr=sys.stderr):
+ """Prepare list of exports for pyguile.scm.
+ """
+ (g2p_funcs,p2g_funcs) = extract_fnames(inptext=stdin.read())
+ tstamp = time.strftime("%Y%b%d-%H:%M:%S")
+
+ exports = "(export " + " ".join(g2p_funcs) + " " + " ".join(p2g_funcs) + ")\n"
+
+ stdout.write(template % {"timestamp" : tstamp, "autogeneratedexports" : exports})
+
+########################################################################
+# Main Program
+########################################################################
+
+def main(argv=None):
+ """Main program of the script - used to test the actual script code.
+ """
+ op = OptionParser()
+ op.add_option("-i","--input",action="store",
+ dest="input",default=None,
+ help="Name of input file. Default is sys.stdin.")
+ op.add_option("-o","--output",action="store",
+ dest="output",default=None,
+ help="Name of output file. Default is sys.stdout.")
+ op.add_option("-e","--error",action="store",
+ dest="error",default=None,
+ help="Name of error output file. Default is sys.stderr.")
+
+ op.add_option("--inc",action="store_true",
+ dest="inc",default=False,
+ help="Create *.inc file.")
+ op.add_option("--scm",action="store",
+ dest="scm",default=None,
+ help="Create *.scm file. The argument is the corresponding .scm.in file to be used as template.")
+
+ (options,args) = op.parse_args(argv[1:])
+
+ # Process input/output/error files' parameters.
+
+ if (options.input == None):
+ stdin = sys.stdin
+ else:
+ stdin = open(options.input)
+ if (options.output == None):
+ stdout = sys.stdout
+ else:
+ stdout = open(options.output,'w')
+ if (options.error == None):
+ stderr = sys.stderr
+ else:
+ stderr = open(options.error,'w')
+
+ if (options.inc):
+ extract_converters(stdin=stdin,stdout=stdout,stderr=stderr)
+ elif (options.scm != None):
+ scminfile = open(options.scm)
+ scmintxt = scminfile.read()
+ scminfile.close()
+ extract_exports(template=scmintxt,stdin=stdin,stdout=stdout,stderr=stderr)
+ return(0)
+
+########################################################################
+
+if (__name__ == '__main__'):
+ sys.exit(main(sys.argv))
+
+else:
+ # I have been imported as a module.
+ pass
+
+# End of extract_conversion_functions.py
diff --git a/g2p2g_smob.c b/g2p2g_smob.c
new file mode 100644
index 0000000..14a50c7
--- /dev/null
+++ b/g2p2g_smob.c
@@ -0,0 +1,164 @@
+// g2p2g_smob implementation
+//
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2008 Omer Zak.
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this library, in a file named COPYING; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+// Boston, MA 02111-1307 USA
+//
+// For licensing issues, contact <w1@zak.co.il>.
+//
+////////////////////////////////////////////////////////////////////////
+//
+// Implements the G2P2G_SMOB type, which encapsulates g2p and p2g
+// conversion routines, for use in defining templates for converting
+// arguments and results of Python functions.
+
+#include "g2p2g_smob.h"
+
+static scm_t_bits tag_g2p2g_smob;
+
+////////////////////////////////////////////////////////////////////////
+// Private functions
+////////////////////////////////////////////////////////////////////////
+
+// static SCM mark_g2p2g_smob(SCM g2p2g_smob)
+// not needed, as the default will be used.
+
+// static size_t free_g2p2g_smob(SCM g2p2g_smob)
+// not needed, as the default will be used.
+
+static int
+print_g2p2g_smob(SCM smob, SCM port, scm_print_state* prstate)
+{
+ if (!SCM_SMOB_PREDICATE(tag_g2p2g_smob,smob)) {
+ scm_wrong_type_arg("print-g2p2g-smob",SCM_ARG1,smob); // NOT COVERED BY TESTS
+ }
+ if (!SCM_PORTP(port)) {
+ scm_wrong_type_arg("print-g2p2g-smob",SCM_ARG2,port); // NOT COVERED BY TESTS
+ }
+ // I don't know how to validate the 3rd argument.
+
+ const char *name = (const char *) SCM_CELL_WORD_2(smob);
+ scm_puts("'",port);
+ scm_puts(name,port);
+ return(1); // Nonzero means success.
+}
+
+// static SCM equalp_g2p2g_smob(SCM smob1, SCM smob2)
+// not needed, as the default will be used.
+
+////////////////////////////////////////////////////////////////////////
+// Public functions
+////////////////////////////////////////////////////////////////////////
+
+// Return nonzero if sobj is of type g2p2g_smob.
+//extern int IS_G2P2G_SMOBP(SCM sobj);
+#define IS_G2P2G_SMOBP(sobj) SCM_SMOB_PREDICATE(tag_g2p2g_smob,sobj)
+
+// Return nonzero if sobj is of type pysmob.
+//int
+//IS_G2P2G_SMOBP(SCM sobj)
+//{
+// return(SCM_SMOB_PREDICATE(tag_g2p2g_smob,sobj));
+//}
+
+int
+IS_G2P_SMOBP(SCM sobj)
+{
+ if (!SCM_SMOB_PREDICATE(tag_g2p2g_smob,sobj)) {
+ return (0);
+ }
+ const char *name = (const char *) SCM_CELL_WORD_2(sobj);
+ return('g' == name[0]);
+}
+
+int
+IS_P2G_SMOBP(SCM sobj)
+{
+ if (!SCM_SMOB_PREDICATE(tag_g2p2g_smob,sobj)) {
+ return (0);
+ }
+ const char *name = (const char *) SCM_CELL_WORD_2(sobj);
+ return('p' == name[0]);
+}
+
+g2p_func_ptr
+get_g2p_function(SCM smob)
+{
+ return((g2p_func_ptr) SCM_CELL_WORD_1(smob));
+}
+
+p2g_func_ptr
+get_p2g_function(SCM smob)
+{
+ return((p2g_func_ptr) SCM_CELL_WORD_1(smob));
+}
+
+SCM
+bind_g2p_function(PyObject *(*g2p_func)(SCM,SCM), const char* name)
+{
+ if (NULL == g2p_func) {
+ if (NULL == name) { // NOT COVERED BY TESTS
+ name = "<null>"; // NOT COVERED BY TESTS
+ }
+ scm_misc_error("bind_g2p_function","NULL g2p_func pointer for ~S",scm_list_1(scm_mem2string(name,strlen(name)))); // NOT COVERED BY TESTS
+ }
+ if (NULL == name) {
+ scm_misc_error("bind_g2p_function","no name was specified",SCM_UNSPECIFIED); // NOT COVERED BY TESTS
+ }
+ if ('g' != name[0]) {
+ scm_misc_error("bind_g2p_function","name does not start with 'g': ~S",scm_list_1(scm_mem2string(name,strlen(name)))); // NOT COVERED BY TESTS
+ }
+ SCM scm_g2p;
+ SCM_NEWSMOB2(scm_g2p,tag_g2p2g_smob,g2p_func,name);
+ scm_c_define(name,scm_g2p);
+ return(scm_g2p);
+}
+
+SCM
+bind_p2g_function(SCM(*p2g_func)(PyObject *,SCM), const char* name)
+{
+ if (NULL == p2g_func) {
+ if (NULL == name) { // NOT COVERED BY TESTS
+ name = "<null>"; // NOT COVERED BY TESTS
+ }
+ scm_misc_error("bind_p2g_function","NULL p2g_func pointer for ~S",scm_list_1(scm_mem2string(name,strlen(name)))); // NOT COVERED BY TESTS
+ }
+ if (NULL == name) {
+ scm_misc_error("bind_p2g_function","no name was specified",SCM_UNDEFINED); // NOT COVERED BY TESTS
+ }
+ if ('p' != name[0]) {
+ scm_misc_error("bind_p2g_function","name does not start with 'p': ~S",scm_list_1(scm_mem2string(name,strlen(name)))); // NOT COVERED BY TESTS
+ }
+ SCM scm_p2g;
+ SCM_NEWSMOB2(scm_p2g,tag_g2p2g_smob,p2g_func,name);
+ scm_c_define(name,scm_p2g);
+ return(scm_p2g);
+}
+
+
+////////////////////////////////////////////////////////////////////////
+
+void
+init_g2p2g_smob_type(void)
+{
+ tag_g2p2g_smob = scm_make_smob_type("g2p2g_smob",0);
+ scm_set_smob_print(tag_g2p2g_smob,print_g2p2g_smob);
+ bind_g2p_functions(); // defined in guiletopy.inc
+ bind_p2g_functions(); // defined in pytoguile.inc
+}
+
+// End of g2p2g_smob.c
diff --git a/g2p2g_smob.h b/g2p2g_smob.h
new file mode 100644
index 0000000..4c7fa1e
--- /dev/null
+++ b/g2p2g_smob.h
@@ -0,0 +1,79 @@
+// g2p2g_smob header file
+//
+////////////////////////////////////////////////////////////////////////
+
+#ifndef G2P2G_SMOB_H
+#define G2P2G_SMOB_H
+
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2008 Omer Zak.
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this library, in a file named COPYING; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+// Boston, MA 02111-1307 USA
+//
+// For licensing issues, contact <w1@zak.co.il>.
+//
+////////////////////////////////////////////////////////////////////////
+//
+// Declares the G2P2G_SMOB type, which encapsulates g2p and p2g conversion
+// routines, for use in defining templates for converting arguments and
+// results of Python functions.
+
+#include <Python.h> // Must be first header file
+#include <libguile.h>
+
+
+// The following are the ones to be used in validating conversion
+// templates.
+extern int IS_G2P_SMOBP(SCM sobj);
+extern int IS_P2G_SMOBP(SCM sobj);
+
+typedef PyObject *(*g2p_func_ptr)(SCM,SCM);
+extern g2p_func_ptr get_g2p_function(SCM smob);
+
+// The name must start with 'g' which is used as part of the
+// real name (which by convention always starts with 'g2p_').
+// The function does NOT take over ownership of the name, so the
+// caller must ensure it continues to exist.
+//
+// This function creates a g2p2g_smob pointing at the g2p function,
+// and also defines (in the current environment) a symbol
+// whose value is the g2p2g_smob.
+extern SCM bind_g2p_function(g2p_func_ptr, const char* name);
+
+typedef SCM (*p2g_func_ptr)(PyObject *,SCM);
+extern p2g_func_ptr get_p2g_function(SCM smob);
+
+// The name must start with 'p' which is used as part of the
+// real name (which by convention always starts with 'p2g_').
+// The function does NOT take over ownership of the name, so the
+// caller must ensure it continues to exist.
+//
+// This function creates a g2p2g_smob pointing at the p2g function,
+// and also defines (in the current environment) a symbol
+// whose value is the g2p2g_smob.
+extern SCM bind_p2g_function(p2g_func_ptr, const char* name);
+
+extern void init_g2p2g_smob_type(void);
+
+////////////////////////////////////////////////////////////////////////
+// The following functions are defined in autogenerated *.inc files.
+
+extern void bind_g2p_functions(void);
+extern void bind_p2g_functions(void);
+
+#endif /* G2P2G_SMOB_H */
+////////////////////////////////////////////////////////////////////////
+// End of g2p2g_smob.h
diff --git a/guiletap.scm b/guiletap.scm
new file mode 100644
index 0000000..514b9bd
--- /dev/null
+++ b/guiletap.scm
@@ -0,0 +1,115 @@
+; Define functions for running Guile-written tests under the TAP protocol.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Copyright (C) 2008 Omer Zak.
+; This library is free software; you can redistribute it and/or
+; modify it under the terms of the GNU Lesser General Public
+; License as published by the Free Software Foundation; either
+; version 2.1 of the License, or (at your option) any later version.
+;
+; This library is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; Lesser General Public License for more details.
+;
+; You should have received a copy of the GNU Lesser General Public License
+; along with this library, in a file named COPYING; if not, write to the
+; Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+; Boston, MA 02111-1307 USA
+;
+; For licensing issues, contact <w1@zak.co.il>.
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;
+;;; To invoke it:
+;;; (use-modules (guiletap))
+;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(define-module (guiletap))
+(export plan)
+(export ok)
+(export bail-out)
+(export diag)
+(export is-ok)
+(export like)
+(export diagprint)
+
+(use-modules (ice-9 format))
+(use-modules (ice-9 regex))
+
+; n is the number of tests.
+(define plan
+ (lambda (n) (display (format "1..~d~%" n))))
+
+; n - test number
+; testdesc - test descriptor
+; res - result which is #f at failure, other at success.
+(define ok
+ (lambda (n testdesc res)
+ (if (not res)(display "not "))
+ (display (format "ok ~d - ~a~%" n testdesc))))
+
+; testdesc - test descriptor
+(define bail-out
+ (lambda (testdesc)
+ (display (format "Bail out! - ~a~%" testdesc))))
+
+; diagmsg - diagnostic message
+(define diag
+ (lambda (diagmsg)
+ (display (format "# ~a~%" diagmsg))))
+
+; n - test number
+; testdesc - test descriptor
+; expres - expected test result
+; actres - actual test result
+; Does not print expected+actual results even when they differ.
+(define is-ok-silent
+ (lambda (n testdesc expres actres)
+ (ok n testdesc (equal? expres actres))))
+
+; Has the same arguments as is-ok and like, but
+; instead of performing comparisons, it just prints
+; the information.
+(define diagprint
+ (lambda (n testdesc exp actres)
+ (display (format "# Test ~d - ~a:~%" n testdesc))
+ (display (format "# Exp: ~a~%# Act: ~a~%" exp actres))))
+
+; Match the actual result to a POSIX extended regular expression
+; (which is supported by Guile, by default).
+; n - test number
+; testdesc - test descriptor
+; exppatt - pattern to match expected test result
+; actres - actual test result
+(define like
+ (lambda (n testdesc exppatt actres)
+ (ok n testdesc (string-match exppatt actres))
+ (if (not (string-match exppatt actres))
+ (diagprint n testdesc exppatt actres))))
+
+; Same as is-ok-silent except that it prints expected and
+; actual results if they differ.
+(define is-ok
+ (lambda (n testdesc expres actres)
+ (is-ok-silent n testdesc expres actres)
+ (if (not (equal? expres actres))
+ (diagprint n testdesc expres actres))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; !!! TODO:
+; !!! To be implemented also:
+; plan-no-plan
+; plan-skip-all [REASON]
+;
+; is RESULT EXPECTED [NAME]
+; isnt RESULT EXPECTED [NAME]
+; unlike RESULT PATTERN [NAME]
+; pass [NAME]
+; fail [NAME]
+;
+; skip CONDITION [REASON] [NB_TESTS=1]
+; Specify TODO mode by setting $TODO:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; End of guiletap.scm
diff --git a/guiletopy.c b/guiletopy.c
new file mode 100644
index 0000000..967dffc
--- /dev/null
+++ b/guiletopy.c
@@ -0,0 +1,1141 @@
+// guiletopy functions
+// Functions for conversion from Guile SCMs into PyObjects.
+//
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2008 Omer Zak.
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this library, in a file named COPYING; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+// Boston, MA 02111-1307 USA
+//
+// For licensing issues, contact <w1@zak.co.il>.
+//
+////////////////////////////////////////////////////////////////////////
+//#include <Python.h> // included in guiletopy.h
+//#include <libguile.h> // included in guiletopy.h
+#include "guiletopy.h"
+#include "pysmob.h"
+#include <guile/gh.h>
+#include "g2p2g_smob.h" // used in guiletopy.inc
+#include "verbose.h"
+#include "pyscm.h"
+
+////////////////////////////////////////////////////////////////////////
+//
+// VERBOSITY HANDLING CONVENTIONS:
+//
+// PYGUILE_VERBOSE_G2P2G_SUCCESSFUL is tested whenever conversion in
+// a primitive data type conversion function (g2p*) is successful.
+// This condition is intended to capture all successful conversions
+// of primitive values.
+//
+// PYGUILE_VERBOSE_G2P2G_ALWAYS is tested in all conversion failures
+// and also when successfully converting aggregate data structures
+// (such as Tuples, Lists and Dicts). It is intended to provide
+// detailed trace of operation of the conversion functions.
+//
+////////////////////////////////////////////////////////////////////////
+// Convert data from Guile (SCM) representation into Python
+// representation
+////////////////////////////////////////////////////////////////////////
+
+// The following functions convert each a single data type.
+// They have to be efficient.
+// The interface conventions are:
+// 1. The function gets a single SCM argument, and upon
+// success - returns a single PyObject, which has already
+// been Py_INCREF()-ed.
+// 2. The function is responsible for checking the data type of
+// and whether the value of its argument is in range.
+// If any of them fails, the function returns NULL.
+// NOTE: no error is raised with the NULL return (unlike the
+// usual convention in Python code).
+// 3. If there is any error not associated with wrong data type
+// of its argument, the function throws a scm exception.
+// 4. Naming convention:
+// g2p_{Guile datatype name}2{Python datatype name}
+// The reason for (2),(3) above is that those functions are
+// intended to be called one after one, until one of them
+// succeeds in converting a data item.
+
+//////////////////////// general template handling /////////////////////
+
+// Apply a template to sobj.
+// The template consists of pair of g2p* token and data structure
+// which serves as the stemplate argument when the token's function
+// is invoked.
+// Alternatively, the template may consist of a single g2p* token.
+// In that case, the corresponding function gets SCM_UNSPECIFIED
+// as its stemplate argument.
+// The invoked function is responsible for ensuring that the stemplate
+// which it received is appropriate to sobj. Inappropriateness
+// means that NULL is returned, which allows g2p_leaf (see below)
+// to backtrace and try another template.
+PyObject *
+g2p_apply(SCM sobj,SCM stemplate)
+{
+ PyObject *pobj;
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# Entered g2p_apply: sobj=~S stemplate=~S\n"),scm_list_2(sobj,stemplate));
+ }
+ if (IS_G2P_SMOBP(stemplate)) {
+ pobj = (get_g2p_function(stemplate))(sobj,SCM_UNSPECIFIED);
+ }
+ else if (!SCM_EQ_P(SCM_BOOL_F,scm_pair_p(stemplate))) {
+ if (IS_G2P_SMOBP(SCM_CAR(stemplate))) {
+ pobj = (get_g2p_function(SCM_CAR(stemplate)))(sobj,SCM_CDR(stemplate));
+ }
+ else {
+ scm_misc_error("g2p_apply","bad template CAR item ~S",
+ scm_list_1(SCM_CAR(stemplate)));
+ }
+ }
+ else {
+ scm_misc_error("g2p_apply","bad template item ~S",
+ scm_list_1(stemplate));
+ }
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),
+ scm_makfrom0str("# Leaving g2p_apply: with ~A result\n"),
+ scm_list_1(NULL == pobj ? scm_makfrom0str("null")
+ : scm_makfrom0str("non-null")));
+ }
+ return(pobj); // is NULL if conversion failed, otherwise a new
+ // reference to a PyObject (Py_INCREF has been invoked on it).
+}
+
+////////////////////////// leaf ////////////////////////////////////////
+
+// Perform 'leaf' data conversion.
+// Normally, stemplate is a list of templates, to be tried one by one
+// until one of them succeeds.
+PyObject *
+g2p_leaf(SCM sobj,SCM stemplate)
+{
+ PyObject *pobj;
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# Entered g2p_leaf: sobj=~S stemplate=~S\n"),scm_list_2(sobj,stemplate));
+ }
+ if (IS_G2P_SMOBP(stemplate)) {
+ pobj = (get_g2p_function(stemplate))(sobj,SCM_UNSPECIFIED);
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),
+ scm_makfrom0str("# Leaving g2p_leaf, after G2P_SMOBP conversion, with ~A result\n"),
+ scm_list_1(NULL == pobj ? scm_makfrom0str("null")
+ : scm_makfrom0str("non-null")));
+ }
+ return(pobj); // Will be NULL if the conversion failed.
+ }
+ if (!SCM_EQ_P(SCM_BOOL_F,scm_list_p(stemplate))) {
+ // Each template list item is examined.
+ // If the template list item is itself a list, then its CAR is invoked
+ // and gets its CDR as template, and the whole sobj (which is
+ // expected to be a list, too) as the argument.
+ // If the template list item is a G2P_SMOB, then it is invoked with
+ // sobj.
+ //
+ // At any case, template list items are invoked one by one until
+ // one of them succeeds.
+ SCM slist;
+ for (slist = stemplate; (!SCM_EQ_P(slist,SCM_EOL));
+ slist = SCM_CDR(slist)) {
+ SCM scandidate = SCM_CAR(slist);
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_leaf: trying another stemplate ~S on sobj\n"),scm_list_1(scandidate));
+ }
+ pobj = g2p_apply(sobj,scandidate);
+ if (NULL != pobj) {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_leaf: successful conversion\n"),SCM_EOL);
+ }
+ return(pobj);
+ }
+ }
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_leaf: unsuccessful conversion, no stemplate fit the sobj\n"),SCM_EOL);
+ }
+ return(NULL); // None of the templates in the list fit the sobj.
+ // NULL return supports backtracking if the template is so designed.
+ }
+ scm_wrong_type_arg("g2p_leaf",SCM_ARG2,stemplate); // Bad template
+}
+
+////////////////////////// null ////////////////////////////////////////
+
+PyObject *
+g2p_null2PyNone(SCM sobj,SCM stemplate)
+{
+ if (SCM_NULLP(sobj)) { //(SCM_BOOL_F != scm_null_p(sobj))
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_null2PyNone: successful conversion of ~S into Python None\n"),scm_list_1(sobj));
+ }
+ Py_INCREF(Py_None);
+ return(Py_None);
+ }
+ else {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_null2PyNone: unsuccessful conversion: ~S is not null\n"),scm_list_1(sobj));
+ }
+ return(NULL);
+ }
+}
+
+PyObject *
+g2p_null2Tuple0(SCM sobj,SCM stemplate)
+{
+ if (SCM_NULLP(sobj)) { //(SCM_BOOL_F != scm_null_p(sobj))
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_null2Tuple0: successful conversion of ~S into Python ()\n"),scm_list_1(sobj));
+ }
+ PyObject *pres = PyTuple_New(0);
+ if (NULL == pres) {
+ scm_memory_error("g2p-null2Tuple0"); // NOT COVERED BY TESTS
+ }
+ return(pres);
+ }
+ else {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_null2Tuple0: unsuccessful conversion: ~S is not null\n"),scm_list_1(sobj));
+ }
+ return(NULL);
+ }
+}
+
+PyObject *
+g2p_null2List0(SCM sobj,SCM stemplate)
+{
+ if (SCM_NULLP(sobj)) { //(SCM_BOOL_F != scm_null_p(sobj))
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_null2List0: successful conversion of ~S into Python []\n"),scm_list_1(sobj));
+ }
+ PyObject *pres = PyList_New(0);
+ if (NULL == pres) {
+ scm_memory_error("g2p-null2List0"); // NOT COVERED BY TESTS
+ }
+ return(pres);
+ }
+ else {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_null2List0: unsuccessful conversion: ~S is not null\n"),scm_list_1(sobj));
+ }
+ return(NULL);
+ }
+}
+
+PyObject *
+g2p_null2DictEmpty(SCM sobj,SCM stemplate)
+{
+ if (SCM_NULLP(sobj)) { //(SCM_BOOL_F != scm_null_p(sobj))
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_null2DictEmpty: successful conversion of ~S into Python {}\n"),scm_list_1(sobj));
+ }
+ PyObject *pres = PyDict_New();
+ if (NULL == pres) {
+ scm_memory_error("g2p-null2DictEmpty"); // NOT COVERED BY TESTS
+ }
+ return(pres);
+ }
+ else {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_null2DictEmpty: unsuccessful conversion: ~S is not null\n"),scm_list_1(sobj));
+ }
+ return(NULL);
+ }
+}
+
+// Python's None is different from Python's () or Python's []
+// !!! Also, check for token such as '*none* or '*None* for
+// !!! conversion into Python None instead of Python string.
+
+///////////////////////// Numeric //////////////////////////////////////
+
+PyObject *
+g2p_bool2Bool(SCM sobj,SCM stemplate)
+{
+ if (SCM_BOOLP(sobj)) { //(SCM_BOOL_F != scm_boolean_p(sobj))
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_bool2Bool: successful conversion of ~S into a Python Bool value\n"),scm_list_1(sobj));
+ }
+ if (SCM_EQ_P(SCM_BOOL_T,sobj)) {
+ Py_INCREF(Py_True);
+ return(Py_True);
+ }
+ else {
+ Py_INCREF(Py_False);
+ return(Py_False);
+ }
+ }
+ else {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_bool2Bool: unsuccessful conversion: ~S is not a bool value\n"),scm_list_1(sobj));
+ }
+ return(NULL);
+ }
+}
+
+PyObject *
+g2p_num2Int(SCM sobj,SCM stemplate)
+{
+ if (SCM_INUMP(sobj)) {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_num2Int: successful conversion of ~S into a Python Int value\n"),scm_list_1(sobj));
+ }
+ return(PyInt_FromLong(scm_num2long(sobj,0,"g2p_long2Int")));
+ }
+ else {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_num2Int: unsuccessful conversion: ~S is not a num value\n"),scm_list_1(sobj));
+ }
+ return(NULL);
+ }
+}
+
+PyObject *
+g2p_real2Float(SCM sobj,SCM stemplate)
+{
+ if (SCM_INUMP(sobj) || SCM_REALP(sobj)) { //(SCM_BOOL_F != scm_real_p(sobj))
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_real2Float: successful conversion of ~S into a Python Float value\n"),scm_list_1(sobj));
+ }
+ return(PyFloat_FromDouble(scm_num2double(sobj,0,"g2p_real2Float")));
+ }
+ else {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_real2Float: unsuccessful conversion: ~S is not a real value\n"),scm_list_1(sobj));
+ }
+ return(NULL);
+ }
+}
+
+PyObject *
+g2p_complex2Complex(SCM sobj,SCM stemplate)
+{
+ if (SCM_COMPLEXP(sobj)) {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_complex2Complex: successful conversion of ~S into a Python Complex value\n"),scm_list_1(sobj));
+ }
+ double re = SCM_COMPLEX_REAL(sobj);
+ double im = SCM_COMPLEX_IMAG(sobj);
+ return(PyComplex_FromDoubles(re,im));
+ }
+ else {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_complex2Complex: unsuccessful conversion: ~S is not a complex value\n"),scm_list_1(sobj));
+ }
+ return(NULL);
+ }
+}
+
+PyObject *
+g2p_bignum2Long(SCM sobj,SCM stemplate)
+{
+ // Like schemepy, we accomplish this conversion by first
+ // converting into string and then evaluating the string.
+ if (SCM_BIGP(sobj)) {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_bignum2Long: successful conversion of ~S into a Python Long value\n"),scm_list_1(sobj));
+ }
+ SCM swrite_proc = scm_variable_ref(scm_c_lookup("write"));
+ SCM sbignumstr = scm_object_to_string(sobj,swrite_proc);
+ char *pstr = gh_scm2newstr(sbignumstr,NULL);
+ if (NULL == pstr) {
+ scm_memory_error("g2p_bignum2Long"); // NOT COVERED BY TESTS
+ }
+
+ PyObject *pres = PyInt_FromString(pstr, NULL, 10); // Will return PyLong if the value does not fit into PyInt.
+ free(pstr);
+ PyObject *pexception = PyErr_Occurred();
+ if (pexception) {
+ Py_XDECREF(pres); // NOT COVERED BY TESTS
+ PyErr_Clear(); // NOT COVERED BY TESTS
+ scm_misc_error("g2p_bignum2Long","internal conversion error of bignum ~S", // NOT COVERED BY TESTS
+ sobj);
+ }
+ return(pres);
+ }
+ else {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_bignum2Long: unsuccessful conversion: ~S is not a bignum value\n"),scm_list_1(sobj));
+ }
+ return(NULL);
+ }
+}
+
+///////////////////// Pairs and Lists //////////////////////////////////
+
+PyObject *
+g2p_pair2Tuple(SCM sobj,SCM stemplate)
+{
+ // We expect the template to be a pair.
+ // SCM_CAR(stemplate) is used to convert SCM_CAR(sobj), and similarly
+ // for SCM_CDR.
+ if (SCM_EQ_P(SCM_BOOL_F,scm_pair_p(sobj))) {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_pair2Tuple: unsuccessful conversion: ~S is not a pair\n"),scm_list_1(sobj));
+ }
+ return(NULL);
+ }
+ if (SCM_EQ_P(SCM_BOOL_F,scm_pair_p(stemplate))) {
+ scm_misc_error("g2p_pair2Tuple","bad template ~S",
+ scm_list_1(stemplate));
+ }
+
+ // Transform it into Python tuple.
+ PyObject *ppair = PyTuple_New(2);
+ if (NULL == ppair) {
+ scm_memory_error("g2p_pair2Tuple"); // NOT COVERED BY TESTS
+ }
+
+ // CAR
+ PyObject *pitem = g2p_apply(SCM_CAR(sobj),SCM_CAR(stemplate));
+ if (NULL == pitem) {
+ Py_DECREF(ppair);
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_pair2Tuple: unsuccessful conversion: CAR conversion failure\n"),SCM_EOL);
+ }
+ return(NULL); // Conversion failure.
+ }
+ if (-1 == PyTuple_SetItem(ppair, 0, pitem)) {
+ Py_DECREF(ppair); // NOT COVERED BY TESTS
+ // Py_DECREF(pitem); // already performed by PyTuple_SetItem
+ scm_misc_error("g2p_pair2Tuple","PyTuple_SetItem car failure (~S)", // NOT COVERED BY TESTS
+ scm_list_1(SCM_CAR(sobj)));
+ }
+
+ // CDR
+ pitem = g2p_apply(SCM_CDR(sobj),SCM_CDR(stemplate));
+ if (NULL == pitem) {
+ Py_DECREF(ppair);
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_pair2Tuple: unsuccessful conversion: CDR conversion failure\n"),SCM_EOL);
+ }
+ return(NULL); // Conversion failure.
+ }
+ if (-1 == PyTuple_SetItem(ppair, 1, pitem)) {
+ Py_DECREF(ppair); // NOT COVERED BY TESTS
+ // Py_DECREF(pitem); // already performed by PyTuple_SetItem
+ scm_misc_error("g2p_pair2Tuple","PyTuple_SetItem cdr failure (~S)", // NOT COVERED BY TESTS
+ scm_list_1(SCM_CDR(sobj)));
+ }
+
+ // Done
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_pair2Tuple: successful conversion of ~S into a Python 2-Tuple\n"),scm_list_1(sobj));
+ }
+ return(ppair);
+}
+
+// Very similar to g2p_pair2Tuple() above.
+PyObject *
+g2p_pair2List(SCM sobj,SCM stemplate)
+{
+ // We expect the template to be a pair.
+ // SCM_CAR(stemplate) is used to convert SCM_CAR(sobj), and similarly
+ // for SCM_CDR.
+ if (SCM_EQ_P(SCM_BOOL_F,scm_pair_p(sobj))) {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_pair2List: unsuccessful conversion: ~S is not a pair\n"),scm_list_1(sobj));
+ }
+ return(NULL);
+ }
+ if (SCM_EQ_P(SCM_BOOL_F,scm_pair_p(stemplate))) {
+ scm_misc_error("g2p_pair2List","bad template ~S",
+ scm_list_1(stemplate));
+ }
+
+ // Transform it into Python tuple.
+ PyObject *ppair = PyList_New(2);
+ if (NULL == ppair) {
+ scm_memory_error("g2p_pair2List"); // NOT COVERED BY TESTS
+ }
+
+ // CAR
+ PyObject *pitem = g2p_apply(SCM_CAR(sobj),SCM_CAR(stemplate));
+ if (NULL == pitem) {
+ Py_DECREF(ppair);
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_pair2List: unsuccessful conversion: CAR conversion failure\n"),SCM_EOL);
+ }
+ return(NULL);
+ }
+ if (-1 == PyList_SetItem(ppair, 0, pitem)) {
+ Py_DECREF(ppair); // NOT COVERED BY TESTS
+ // Py_DECREF(pitem); // already performed by PyList_SetItem
+ scm_misc_error("g2p_pair2List","PyList_SetItem car failure (~S)", // NOT COVERED BY TESTS
+ scm_list_1(SCM_CAR(sobj)));
+ }
+
+ // CDR
+ pitem = g2p_apply(SCM_CDR(sobj),SCM_CDR(stemplate));
+ if (NULL == pitem) {
+ Py_DECREF(ppair);
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_pair2List: unsuccessful conversion: CDR conversion failure\n"),SCM_EOL);
+ }
+ return(NULL);
+ }
+ if (-1 == PyList_SetItem(ppair, 1, pitem)) {
+ Py_DECREF(ppair); // NOT COVERED BY TESTS
+ // Py_DECREF(pitem); // already performed by PyList_SetItem
+ scm_misc_error("g2p_pair2List","PyList_SetItem cdr failure (~S)", // NOT COVERED BY TESTS
+ scm_list_1(SCM_CDR(sobj)));
+ }
+
+ // Done
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_pair2List: successful conversion of ~S into a Python 2-List\n"),scm_list_1(sobj));
+ }
+ return(ppair);
+}
+
+PyObject *
+g2p_list2Tuple(SCM sobj,SCM stemplate)
+{
+ // sobj is expected to be a list.
+
+ if (SCM_EQ_P(SCM_BOOL_F,scm_list_p(sobj))) {
+ // sobj is not a list, so this is the wrong conversion function for it.
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_list2Tuple: unsuccessful conversion: ~S is not a list\n"),scm_list_1(sobj));
+ }
+ return(NULL);
+ }
+
+ // The template may be either a G2P_SMOB (to be used for converting
+ // all list items) or a list of templates.
+ long listlen = scm_num2long(scm_length(sobj),0,"g2p_list2Tuple");
+ PyObject *plist = PyTuple_New(listlen);
+ if (NULL == plist) {
+ scm_memory_error("g2p_list2Tuple"); // NOT COVERED BY TESTS
+ }
+
+ // Conversion loop for the case in which the template is a single G2P_SMOB
+ if (IS_G2P_SMOBP(stemplate)) {
+ long ind1;
+ for (ind1 = 0; ind1 < listlen; sobj = SCM_CDR(sobj),++ind1) {
+ SCM sitem = SCM_CAR(sobj);
+ PyObject *pobj1 = g2p_apply(sitem,stemplate);
+ if (NULL == pobj1) {
+ Py_DECREF(plist);
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_list2Tuple: unsuccessful conversion of element ~A: ~S does not match template\n"),scm_list_2(scm_long2num(ind1),sitem));
+ }
+ return(NULL); // Conversion failure.
+ }
+ if (-1 == PyTuple_SetItem(plist, ind1, pobj1)) {
+ Py_DECREF(plist); // NOT COVERED BY TESTS
+ //Py_DECREF(pobj1);
+ scm_misc_error("g2p_list2Tuple","PyTuple_SetItem ~S failure (~S)", // NOT COVERED BY TESTS
+ scm_list_2(scm_long2num(ind1),sitem));
+ }
+ }
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_list2Tuple: successful conversion of list ~S\n"),scm_list_1(sobj));
+ }
+ return(plist);
+ }
+
+ if (SCM_EQ_P(SCM_BOOL_F,scm_list_p(stemplate))) {
+ // Bad template.
+ scm_wrong_type_arg("g2p_list2Tuple",SCM_ARG2,stemplate);
+ }
+
+ // Conversion loop for the case in which the template is a list.
+ long ind2;
+ SCM stemp = SCM_EOL; // We loop over stemplate again and again as needed.
+ for (ind2 = 0; ind2 < listlen;
+ sobj = SCM_CDR(sobj), stemp=SCM_CDR(stemp), ++ind2) {
+ if (SCM_EQ_P(stemp,SCM_EOL)) {
+ stemp = stemplate; // Loop back to stemplate's beginning.
+ }
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_list2Tuple - processing item CAR(sobj)=~S CAR(stemp)=~S\n"),scm_list_2(SCM_CAR(sobj),SCM_CAR(stemp)));
+ }
+ PyObject *pobj2 = g2p_apply(SCM_CAR(sobj),SCM_CAR(stemp));
+ if (NULL == pobj2) {
+ Py_DECREF(plist);
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_list2Tuple: unsuccessful conversion of element ~A: ~S does not match personalized template\n"),scm_list_2(scm_long2num(ind2),SCM_CAR(sobj)));
+ }
+ return(NULL); // Conversion failure.
+ }
+ if (-1 == PyTuple_SetItem(plist, ind2, pobj2)) {
+ Py_DECREF(plist); // NOT COVERED BY TESTS
+ //Py_DECREF(pobj2);
+ scm_misc_error("g2p_list2Tuple","PyTuple_SetItem ~S failure (~S)", // NOT COVERED BY TESTS
+ scm_list_2(scm_long2num(ind2),SCM_CAR(stemp)));
+ }
+ }
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_list2Tuple: successful conversion of list ~S by template\n"),scm_list_1(sobj));
+ }
+ return(plist);
+}
+
+// Very similar to g2p_list2Tuple() above.
+PyObject *
+g2p_list2List(SCM sobj,SCM stemplate)
+{
+ // sobj is expected to be a list.
+
+ if (SCM_EQ_P(SCM_BOOL_F,scm_list_p(sobj))) {
+ // sobj is not a list, so this is the wrong conversion function for it.
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_list2List: unsuccessful conversion: ~S is not a list\n"),scm_list_1(sobj));
+ }
+ return(NULL);
+ }
+
+ // The template may be either a G2P_SMOB (to be used for converting
+ // all list items) or a list of templates.
+ long listlen = scm_num2long(scm_length(sobj),0,"g2p_list2List");
+ PyObject *plist = PyList_New(listlen);
+ if (NULL == plist) {
+ scm_memory_error("g2p_list2List"); // NOT COVERED BY TESTS
+ }
+
+ // Conversion loop for the case in which the template is a single G2P_SMOB
+ if (IS_G2P_SMOBP(stemplate)) {
+ long ind1;
+ for (ind1 = 0; ind1 < listlen; sobj = SCM_CDR(sobj),++ind1) {
+ SCM sitem = SCM_CAR(sobj);
+ PyObject *pobj1 = g2p_apply(sitem,stemplate);
+ if (NULL == pobj1) {
+ Py_DECREF(plist);
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_list2List: unsuccessful conversion of element ~A: ~S does not match template\n"),scm_list_2(scm_long2num(ind1),sitem));
+ }
+ return(NULL); // Conversion failure.
+ }
+ if (-1 == PyList_SetItem(plist, ind1, pobj1)) {
+ Py_DECREF(plist); // NOT COVERED BY TESTS
+ //Py_DECREF(pobj1);
+ scm_misc_error("g2p_list2List","PyList_SetItem ~S failure (~S)", // NOT COVERED BY TESTS
+ scm_list_2(scm_long2num(ind1),sitem));
+ }
+ }
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_list2List: successful conversion of list ~S\n"),scm_list_1(sobj));
+ }
+ return(plist);
+ }
+
+ if (SCM_EQ_P(SCM_BOOL_F,scm_list_p(stemplate))) {
+ // Bad template.
+ scm_wrong_type_arg("g2p_list2List",SCM_ARG2,stemplate);
+ }
+
+ // Conversion loop for the case in which the template is a list.
+ long ind2;
+ SCM stemp = SCM_EOL; // We loop over stemplate again and again as needed.
+ for (ind2 = 0; ind2 < listlen;
+ sobj = SCM_CDR(sobj), stemp=SCM_CDR(stemp), ++ind2) {
+ if (SCM_EQ_P(stemp,SCM_EOL)) {
+ stemp = stemplate; // Loop back to stemplate's beginning.
+ }
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# DEBUG: g2p_list2List - processing item CAR(sobj)=~S CAR(stemp)=~S\n"),scm_list_2(SCM_CAR(sobj),SCM_CAR(stemp)));
+ }
+ PyObject *pobj2 = g2p_apply(SCM_CAR(sobj),SCM_CAR(stemp));
+ if (NULL == pobj2) {
+ Py_DECREF(plist);
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_list2List: unsuccessful conversion of element ~A: ~S does not match personalized template\n"),scm_list_2(scm_long2num(ind2),SCM_CAR(sobj)));
+ }
+ return(NULL); // Conversion failure
+ }
+ if (-1 == PyList_SetItem(plist, ind2, pobj2)) {
+ Py_DECREF(plist); // NOT COVERED BY TESTS
+ //Py_DECREF(pobj2);
+ scm_misc_error("g2p_list2List","PyList_SetItem ~S failure (~S)", // NOT COVERED BY TESTS
+ scm_list_2(scm_long2num(ind2),SCM_CAR(stemp)));
+ }
+ }
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_list2List: successful conversion of list ~S by template\n"),scm_list_1(sobj));
+ }
+ return(plist);
+}
+
+///////////////////////// strings and symbols //////////////////////////
+
+PyObject *
+g2p_char2String(SCM sobj,SCM stemplate)
+{
+ if (SCM_CHARP(sobj)) {
+ return(PyString_FromFormat("%c",(int) SCM_CHAR(sobj)));
+ }
+ else {
+ return(NULL);
+ }
+}
+
+PyObject *
+g2p_string2String(SCM sobj,SCM stemplate)
+{
+ if (SCM_STRINGP(sobj)) {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_string2String: successful conversion of ~S into a Python String value\n"),scm_list_1(sobj));
+ }
+ PyObject *pstr = PyString_FromStringAndSize(SCM_STRING_CHARS(sobj),SCM_STRING_LENGTH(sobj));
+ if (NULL == pstr) {
+ scm_memory_error("g2p_string2String"); // NOT COVERED BY TESTS
+ }
+ return(pstr);
+ }
+ else {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_string2String: unsuccessful conversion: ~S is not a string value\n"),scm_list_1(sobj));
+ }
+ return(NULL);
+ }
+}
+
+// Guile is case-sensitive by default, so we don't have to
+// ensure that symbols always lowercase their characters.
+PyObject *
+g2p_symbol2String(SCM sobj,SCM stemplate)
+{
+ if (SCM_SYMBOLP(sobj)) {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_symbol2String: successful conversion of ~S into a Python String value\n"),scm_list_1(sobj));
+ }
+ PyObject *pstr = PyString_FromStringAndSize(SCM_SYMBOL_CHARS(sobj),SCM_SYMBOL_LENGTH(sobj));
+ if (NULL == pstr) {
+ scm_memory_error("g2p_symbol2String"); // NOT COVERED BY TESTS
+ }
+ return(pstr);
+ }
+ else {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_symbol2String: unsuccessful conversion: ~S is not a symbol value\n"),scm_list_1(sobj));
+ }
+ return(NULL);
+ }
+}
+
+PyObject *
+g2p_keyword2String(SCM sobj,SCM stemplate)
+{
+ if (SCM_KEYWORDP(sobj)) { //(SCM_BOOL_F != scm_keyword_p(sobj))
+ SCM symb = scm_keyword_dash_symbol(sobj);
+ // We want to remove the leading '-' from the keyword, so that
+ // it can be used in keyword function arguments in a natural way.
+ const char *symchars = SCM_SYMBOL_CHARS(symb);
+ int symlength = SCM_SYMBOL_LENGTH(symb);
+ if (symlength < 1) {
+ scm_out_of_range("g2p_keyword2String",sobj); // NOT COVERED BY TESTS
+ // The symbol is too short to have dash.
+ // We allow symbols like '#:', which are converted here into empty
+ // strings. Guile does not accept them for version 1.8.x and later,
+ // but this does not matter here.
+ }
+ PyObject *pstr = PyString_FromStringAndSize(symchars+1,symlength-1);
+ if (NULL == pstr) {
+ scm_memory_error("g2p_keyword2String"); // NOT COVERED BY TESTS
+ }
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_keyword2String: successful conversion of ~S into a Python String value\n"),scm_list_1(sobj));
+ }
+ return(pstr);
+ }
+ else {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_keyword2String: unsuccessful conversion: ~S is not a keyword value\n"),scm_list_1(sobj));
+ }
+ return(NULL);
+ }
+}
+
+///////////////////////// opaque datatypes /////////////////////////////
+
+// scm_procedure_p
+// scm_procedure_with_setter_p - not implemented yet.
+
+// Reasonable default template for procedure objects
+static SCM g2p_procedure2PySCMObject_template_default;
+// The above variable is to be assigned the value:
+// scm_permanent_object(scm_vector(scm_list_5(...)))
+// where the arguments of scm_list_5() are:
+// scm_variable_ref(scm_c_lookup("python2guile"))
+// scm_variable_ref(scm_c_lookup("python2guile"))
+// guile2python_smob
+// scm_variable_ref(scm_c_lookup("apply"))
+// SCM_BOOL_F
+//
+// To clone the above and set other templates, use
+// Scheme procedure: (set! cloned (copy-tree obj))
+// and C function: SCM scopy = scm_copy_tree(SCM sobj)
+
+PyObject *
+g2p_procedure2PySCMObject(SCM sobj,SCM stemplate)
+{
+ if (SCM_EQ_P(stemplate,SCM_UNSPECIFIED)) {
+ stemplate = g2p_procedure2PySCMObject_template_default;
+ }
+ if (SCM_EQ_P(SCM_BOOL_F,scm_vector_p(stemplate))) {
+ // Bad template
+ scm_wrong_type_arg("g2p_procedure2PySCMObject",SCM_ARG2,stemplate);
+ }
+ if (SCM_EQ_P(SCM_BOOL_F,scm_procedure_p(sobj))) {
+ // Not a procedure
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_procedure2PySCMObject: unsuccessful conversion: ~S is not a procedure\n"),scm_list_1(sobj));
+ }
+ return(NULL);
+ }
+ PyObject *pobj = wrap_scm(sobj,stemplate);
+ if (NULL != pobj) {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_procedure2PySCMObject: successful conversion: ~S has been wrapped\n"),scm_list_1(sobj));
+ }
+ }
+ return(pobj);
+}
+
+
+PyObject *
+g2p_opaque2Object(SCM sobj,SCM stemplate)
+{
+ if (IS_PYSMOBP(sobj)) {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_opaque2Object: the Python object inside opaque pysmob ~S is unwrapped\n"),scm_list_1(sobj));
+ }
+ PyObject *pobj = unwrap_pysmob(sobj);
+ Py_INCREF(pobj);
+ return(pobj);
+ }
+ else {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_opaque2Object: unsuccessful conversion: ~S is not an opaque pysmob value\n"),scm_list_1(sobj));
+ }
+ return(NULL);
+ }
+}
+
+
+////////////////////////////////////////////////////////////////////////
+// Big default conversion function
+////////////////////////////////////////////////////////////////////////
+// The guile2python function chooses reasonable defaults, whenever
+// there is a possibility for ambiguity concerning the desired
+// Python datatype.
+
+static SCM guile2python_smob; // Used when we need it as hash default value.
+static SCM guile2python_template_default; // used by guile2python().
+static SCM guile2python_pair_template_default; // used by guile2python().
+static SCM guileassoc2pythondict_default; // used by guileassoc2pythondict.
+ // It is set to an empty hash table.
+static SCM g2p_alist_template_default; // used by g2p_alist2Dict.
+
+// This function does Py_INCREF() to its return values.
+PyObject *
+guile2python(SCM sobj,SCM stemplate)
+{
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# guile2python: entry: seeking to convert sobj=~S; unused stemplate=~S\n"),scm_list_2(sobj,stemplate));
+ }
+ PyObject *pres = NULL;
+
+ //////////// Scheme sequences
+
+ // SCM '() is converted into Python None.
+ pres = g2p_null2PyNone(sobj,SCM_UNSPECIFIED);
+ if (NULL != pres) return(pres);
+
+ // We check lists before pairs because every list in Scheme is
+ // also a pair.
+ //printf("# DEBUG: guile2python, before 1st call to g2p_list2List\n");
+ pres = g2p_list2List(sobj,guile2python_template_default);
+ if (NULL != pres) return(pres);
+
+ pres = g2p_pair2Tuple(sobj,guile2python_pair_template_default);
+ if (NULL != pres) return(pres);
+
+ //////////// Scheme numbers
+
+ pres = g2p_bool2Bool(sobj,SCM_UNSPECIFIED);
+ if (NULL != pres) return(pres);
+
+ pres = g2p_num2Int(sobj,SCM_UNSPECIFIED);
+ if (NULL != pres) return(pres);
+
+ pres = g2p_bignum2Long(sobj,SCM_UNSPECIFIED);
+ if (NULL != pres) return(pres);
+
+ pres = g2p_real2Float(sobj,SCM_UNSPECIFIED);
+ if (NULL != pres) return(pres);
+ // NOTE: in Guile 1.6, rational numbers and real numbers are
+ // internally represented the same way.
+
+ pres = g2p_complex2Complex(sobj,SCM_UNSPECIFIED);
+ if (NULL != pres) return(pres);
+
+ //////////// Scheme strings
+
+ pres = g2p_char2String(sobj,SCM_UNSPECIFIED);
+ if (NULL != pres) return(pres);
+
+ pres = g2p_string2String(sobj,SCM_UNSPECIFIED);
+ if (NULL != pres) return(pres);
+
+ pres = g2p_symbol2String(sobj,SCM_UNSPECIFIED);
+ if (NULL != pres) return(pres);
+
+ pres = g2p_keyword2String(sobj,SCM_UNSPECIFIED);
+ if (NULL != pres) return(pres);
+
+ //////////// Other Scheme data types
+
+ pres = g2p_procedure2PySCMObject(sobj,SCM_UNSPECIFIED);
+ if (NULL != pres) return(pres);
+
+ pres = g2p_opaque2Object(sobj,SCM_UNSPECIFIED);
+ if (NULL != pres) return(pres);
+
+ // !!! More simple data types: regexp
+ // !!! To wrap: procedures, macros(opaque), variables,
+ // !!! opaque: asyncs, dynamic roots, fluids, hooks, ports
+
+ // !!! Scheme objects
+
+ // !!! More complex data types: vectors,records,structures,arrays,
+
+
+ else {
+ scm_wrong_type_arg("guile2python",SCM_ARG1,sobj);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+// Quick conversion from alist into Dict
+////////////////////////////////////////////////////////////////////////
+// Treat the SCM object as an association list and convert it into
+// Python hash, whose keys are strings corresponding to the
+// association list's keys (which must be keywords).
+//
+// This version is meant for quick conversion of keyword arguments,
+// and it allows only keywords as keys.
+//
+// Checks:
+// 1. Validity of keys.
+// 2. No duplicate keys.
+//
+// The stemplate argument must be an hash table (keys are checked
+// using eq?).
+
+PyObject *
+guileassoc2pythondict(SCM sobj,SCM stemplate)
+{
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# guileassoc2pythondict: entry: seeking to convert sobj=~S using stemplate=~S\n"),scm_list_2(sobj,stemplate));
+ }
+
+ if (SCM_UNBNDP(stemplate)) {
+ stemplate = guileassoc2pythondict_default;
+ }
+ PyObject *pdict = NULL;
+ PyObject *pkey = NULL;
+ PyObject *pval = NULL;
+
+ if (SCM_EQ_P(SCM_BOOL_F,scm_list_p(sobj))) {
+ scm_wrong_type_arg("guileassoc2pythondict",SCM_ARG1,sobj);
+ }
+ // TODO: add here a call to SCM_HASHP(stemplate) to validate that the
+ // template is hashtable.
+
+ long listlen = scm_num2long(scm_length(sobj),0,"guileassoc2pythondict");
+ pdict = PyDict_New();
+ if (NULL == pdict) {
+ scm_memory_error("guileassoc2pythondict"); // NOT COVERED BY TESTS
+ }
+ long ind;
+ for (ind = 0,pkey = NULL,pval = NULL; ind < listlen; ++ind) {
+ SCM spair = SCM_CAR(sobj);
+ if (SCM_EQ_P(SCM_BOOL_F,scm_pair_p(spair))) {
+ // Not a pair.
+ Py_DECREF(pdict);
+ scm_wrong_type_arg("guileassoc2pythondict",ind+1,spair);
+ }
+
+ pkey = g2p_keyword2String(SCM_CAR(spair),SCM_UNSPECIFIED);
+ if (NULL == pkey) {
+ // Illegal key - it must be a keyword.
+ scm_wrong_type_arg("guileassoc2pythondict",ind+1,SCM_CAR(spair));
+ }
+ if (0 != PyDict_Contains(pdict,pkey)) {
+ // Duplicate key or some error
+ Py_DECREF(pdict);
+ Py_DECREF(pkey);
+ scm_misc_error("guileassoc2pythondict","duplicate key (~S)",
+ scm_list_1(SCM_CAR(spair)));
+ }
+
+ SCM sitem_template = scm_hashq_ref(stemplate,SCM_CAR(spair),guile2python_smob);
+ pval = g2p_apply(SCM_CDR(spair),sitem_template);
+ if (NULL == pval) {
+ Py_DECREF(pdict);
+ Py_DECREF(pkey);
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# guileassoc2pythondict: unsuccessful conversion of element ~A: ~S does not match template\n"),scm_list_2(SCM_CAR(spair),SCM_CDR(spair)));
+ }
+ return(NULL); // Conversion failure.
+ }
+
+ if (-1 == PyDict_SetItem(pdict,pkey,pval)) {
+ Py_XDECREF(pdict); // NOT COVERED BY TESTS
+ //Py_XDECREF(pkey);
+ //Py_XDECREF(pval);
+ scm_misc_error("guileassoc2pythondict","PyDict_SetItem failure (~S : ~S)", // NOT COVERED BY TESTS
+ scm_list_2(SCM_CAR(spair),SCM_CDR(spair)));
+ }
+ sobj = SCM_CDR(sobj);
+ }
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# guileassoc2pythondict: successful conversion of ~S\n"),scm_list_1(sobj));
+ }
+ return(pdict);
+}
+
+////////////////////////////////////////////////////////////////////////
+// Generalized conversion from alist into Dict
+////////////////////////////////////////////////////////////////////////
+// The following function is meant for conversion of alists into
+// general Python Dicts.
+// It allows any immutable object as key.
+// NOTE: no check for immutability is made here!
+//
+// The stemplate argument must be a list of pairs (i.e. structured
+// like an alist).
+
+PyObject *
+g2p_alist2Dict(SCM sobj,SCM stemplate)
+{
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_alist2Dict sobj=~S stemplate=~S\n"),scm_list_2(sobj,stemplate));
+ }
+ if (SCM_UNBNDP(stemplate) || SCM_EQ_P(stemplate,SCM_EOL)) {
+ stemplate = g2p_alist_template_default;
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_alist2Dict - default template was chosen: ~S\n"),scm_list_1(stemplate));
+ }
+ }
+
+ if (SCM_EQ_P(SCM_BOOL_F,scm_list_p(sobj))) {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_alist2Dict: unsuccessful conversion: argument is not list, let alone alist\n"),SCM_EOL);
+ }
+ return(NULL); // Conversion failure.
+ }
+ if (SCM_EQ_P(SCM_BOOL_F,scm_list_p(stemplate))) {
+ scm_wrong_type_arg("g2p_alist2Dict",SCM_ARG2,sobj); // Bad template.
+ }
+
+ long listlen = scm_num2long(scm_length(sobj),0,"g2p_alist2Dict");
+ PyObject *pdict = PyDict_New();
+ if (NULL == pdict) {
+ scm_memory_error("g2p_alist2Dict"); // NOT COVERED BY TESTS
+ }
+
+ long ind;
+ SCM stemp = SCM_EOL; // We loop over stemplate again and again as needed.
+ for (ind = 0; ind < listlen;
+ sobj = SCM_CDR(sobj),stemp=SCM_CDR(stemp), ++ind) {
+ if (SCM_EQ_P(stemp,SCM_EOL)) {
+ stemp = stemplate; // Loop back to stemplate's beginning.
+ }
+
+ if (SCM_EQ_P(SCM_BOOL_F,scm_pair_p(SCM_CAR(stemp)))) {
+ // Not a template pair - bad template.
+ Py_DECREF(pdict);
+ scm_wrong_type_arg("g2p_alist2Dict",SCM_ARG2,SCM_CAR(stemp));
+ }
+ SCM spair = SCM_CAR(sobj);
+ if (SCM_EQ_P(SCM_BOOL_F,scm_pair_p(spair))) {
+ // Not a data pair - conversion failure.
+ Py_DECREF(pdict);
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_alist2Dict: unsuccessful conversion: alist item is not pair: ~S\n"),scm_list_1(spair));
+ }
+ return(NULL);
+ }
+
+ PyObject *pkey = g2p_apply(SCM_CAR(spair),SCM_CAAR(stemp));
+ if (NULL == pkey) {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_alist2Dict: unsuccessful conversion: key ~S, template ~S\n"),scm_list_2(SCM_CAR(spair),SCM_CAAR(stemp)));
+ }
+ return(NULL); // Conversion failure.
+ }
+ if (0 != PyDict_Contains(pdict,pkey)) {
+ // Duplicate key or some error.
+ Py_DECREF(pdict);
+ Py_DECREF(pkey);
+ scm_misc_error("g2p_alist2Dict","duplicate key (~S)",
+ scm_list_1(SCM_CAR(spair)));
+ }
+
+ PyObject *pval = g2p_apply(SCM_CDR(spair),SCM_CDAR(stemp));
+ if (NULL == pval) {
+ Py_DECREF(pdict);
+ Py_DECREF(pkey);
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_alist2Dict: unsuccessful conversion: value ~S, template ~S\n"),scm_list_2(SCM_CDR(spair),SCM_CDAR(stemp)));
+ }
+ return(NULL); // Conversion failure.
+ }
+
+ if (-1 == PyDict_SetItem(pdict,pkey,pval)) {
+ Py_XDECREF(pdict); // NOT COVERED BY TESTS
+ //Py_XDECREF(pkey);
+ //Py_XDECREF(pval);
+ scm_misc_error("g2p_alist2Dict","PyDict_SetItem failure (~S : ~S)", // NOT COVERED BY TESTS
+ scm_list_2(SCM_CAR(spair),SCM_CDR(spair)));
+ }
+ }
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# g2p_alist2Dict: successful conversion\n"),SCM_EOL);
+ }
+ return(pdict);
+}
+
+// NOTE:
+// After all, we'll not support hash tables.
+// This is because I found no function to identify an hash table.
+// Hash table can be converted into alist using:
+// (hash-fold acons '() hashtable)
+
+////////////////////////////////////////////////////////////////////////
+// Register all g2p_* functions
+
+#include "guiletopy.inc"
+
+// The following must happen after registration of all g2p_* and p2g_*
+// functions.
+void
+init_default_guiletopy_templates(void)
+{
+ SCM s_python2guile = scm_variable_ref(scm_c_lookup("python2guile"));
+ g2p_procedure2PySCMObject_template_default
+ = scm_permanent_object(scm_vector(scm_list_5(
+ s_python2guile,
+ s_python2guile,
+ guile2python_smob,
+ scm_variable_ref(scm_c_lookup("apply")),
+ SCM_BOOL_F)));
+ scm_c_define("pyscm-default-template",g2p_procedure2PySCMObject_template_default);
+}
+
+////////////////////////////////////////////////////////////////////////
+// End of guiletopy.c
diff --git a/guiletopy.h b/guiletopy.h
new file mode 100644
index 0000000..00c3369
--- /dev/null
+++ b/guiletopy.h
@@ -0,0 +1,104 @@
+// guiletopy header file
+// Functions for conversion from Guile SCMs into PyObjects.
+
+#ifndef GUILETOPY_H
+#define GUILETOPY_H
+
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2008 Omer Zak.
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this library, in a file named COPYING; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+// Boston, MA 02111-1307 USA
+//
+// For licensing issues, contact <w1@zak.co.il>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#include <Python.h> // Must be first header file
+#include <libguile.h>
+
+////////////////////////////////////////////////////////////////////////
+// The format of stemplate for SCM->PyObject conversions
+////////////////////////////////////////////////////////////////////////
+//
+// The following cases are to be considered:
+// -----------------------------------------
+// All g2p* functions have a template argument.
+// However, those which are genuine leaf conversion functions
+// ignore it.
+//
+// 1. "Leaf" data item (such as a number or a string):
+// We want to try few g2p* functions until one succeeds.
+// The template in this case consists of 'g2p_leaf followed by
+// a list of one or more g2p* symbols.
+// It is possible for the template corresponding to a "leaf"
+// data item to consist of a single g2p* function. In this case,
+// the function is directly applied to the data item.
+// 2. Data item which is pair:
+// The template consists of list of g2p_pair2Tuple or g2p_pair2List,
+// and pair of two templates for processing the CAR and CDR of the
+// data item. Example:
+// (list g2p_pair2Tuple
+// ((list g2p_leaf g2p_real2Float) . (list g2p_leaf g2p_num2Int)))
+// 3. Data item which is a list:
+// The template data structure is a list of g2p_list2List or
+// g2p_list2Tuple followed by a list of one or more templates, which
+// are used to convert the list members.
+// It is possible for the data list to have more items than the
+// corresponding template list. In this case, the template list
+// items are cyclically processed.
+// This way it is possible to use a single template to convert all
+// list items, if they are to be converted in the same way.
+// 4. Data item which is an alist:
+// The data structure is TBD.
+// (need to consider handling of keys)
+//
+////////////////////////////////////////////////////////////////////////
+// SCM -> PyObject
+////////////////////////////////////////////////////////////////////////
+
+// Basic conversion of a SCM object according to template.
+extern PyObject *g2p_apply(SCM sobj,SCM stemplate);
+
+// Convert a SCM object into a Python object.
+// If cannot convert, abort.
+extern PyObject *guile2python(SCM sobj,SCM stemplate);
+
+// Treat the SCM object as an association list and convert it into
+// Python hash, whose keys are strings corresponding to the
+// association list's keys (which must be either strings or symbols).
+//
+// Checks:
+// 1. Validity of keys.
+// 2. No duplicate keys.
+PyObject *guileassoc2pythondict(SCM sobj,SCM stemplate);
+
+// Convert from Guile list into Python tuple.
+// Need separate function because the default behavior is to convert
+// it into Python list.
+extern PyObject *g2p_list2Tuple(SCM sobj,SCM stemplate);
+
+////////////////////////////////////////////////////////////////////////
+// Extra initialization after registration of all g2p_* and p2g_*
+// functions.
+
+extern void init_default_guiletopy_templates(void);
+
+////////////////////////////////////////////////////////////////////////
+
+#endif /* GUILETOPY_H */
+
+////////////////////////////////////////////////////////////////////////
+// End of guiletopy.h
diff --git a/pyguile.c b/pyguile.c
new file mode 100644
index 0000000..ebf9d83
--- /dev/null
+++ b/pyguile.c
@@ -0,0 +1,497 @@
+// pyguile functions
+//
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2008 Omer Zak.
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this library, in a file named COPYING; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+// Boston, MA 02111-1307 USA
+//
+// For licensing issues, contact <w1@zak.co.il>.
+//
+////////////////////////////////////////////////////////////////////////
+
+//#include <Python.h> // included in pyguile.h
+//#include <libguile.h> // included in pyguile.h
+#include "pyguile.h"
+#include "pytoguile.h"
+#include "guiletopy.h"
+#include "pysmob.h"
+#include <guile/gh.h>
+#include "g2p2g_smob.h"
+#include "verbose.h"
+#include "pyscm.h"
+#include "version.h"
+
+////////////////////////////////////////////////////////////////////////
+// Invoke Python function
+////////////////////////////////////////////////////////////////////////
+
+// !!! TODO:
+// Problems:
+// 1. There is a general problem that exceptions seem not to be properly
+// harvested!
+
+
+static SCM sargtemplate_default;
+static SCM skwtemplate_default;
+static SCM srestemplate_default;
+
+// python_apply implements the function call:
+// (python-apply ("module.submodule" 'obj 'func)
+// (arg1 arg2 arg3)
+// (('keyword1 . val4) ('keyword2 . val5))
+// sargtemplate
+// skwtemplate)
+// which is the basic way to invoke a Python function.
+//
+// sfunc specifies the function to be invoked. The possibilities
+// are:
+// String - denotes a top level function ("func" means __main__.func).
+// pysmob - assumed to be a callable object.
+// ("module.submodule" ...) - a List of strings/symbols/keywords
+// in which the first item must be a string denotes:
+// Module "module.submodule" (which should have already been imported
+// using python-import)
+// followed by name of object in that module, followed by attribute,...,
+// until the final callable attribute.
+// (pysmob ...) - a List starting with pysmob followed by
+// strings/symbols/keywords - processed similarly, except that the
+// pysmob stands for the module.
+// sarg is a list of arguments (in Python it's *arg)
+// skw is an alist (in Python it's **kw).
+// sargtemplate - specifies how to convert sarg - optional argument.
+// skwtemplate - specifies how to convert skw - optional argument.
+// srestemplate - specifies how to convert the result back into
+// SCM - optional argument.
+SCM
+python_apply(SCM sfunc, SCM sarg, SCM skw,
+ SCM sargtemplate, SCM skwtemplate, SCM srestemplate)
+{
+ PyObject *pfunc = NULL;
+ PyObject *parg = NULL;
+ PyObject *pkw = NULL;
+
+ PyObject *pfuncobj = NULL;
+ PyObject *pres = NULL;
+ SCM sres = SCM_UNDEFINED;
+
+ if (SCM_UNBNDP(sargtemplate)) { //(sargtemplate == SCM_UNDEFINED) // SCM_UNSPECIFIED
+ sargtemplate = sargtemplate_default;
+ }
+ if (SCM_UNBNDP(skwtemplate)) {
+ skwtemplate = skwtemplate_default;
+ }
+ if (SCM_UNBNDP(srestemplate)) {
+ srestemplate = srestemplate_default;
+ }
+
+ // Retrieve the function object.
+
+ pfunc = guile2python(sfunc,SCM_UNSPECIFIED);
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_PYTHON_APPLY)) {
+ char *preprfunc = PyString_AsString(PyObject_Repr(pfunc));
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# python_apply: decoded pfunc ~S\n"),scm_list_1(scm_makfrom0str(preprfunc)));
+ }
+ if (NULL == pfunc) {
+ scm_misc_error("python-apply","conversion failure (~S)",
+ scm_list_1(SCM_CDR(sfunc)));
+ }
+ // If it is a string, prepend it with "__main__".
+ if (PyString_CheckExact(pfunc)) {
+ // Convert it into a List of two items, to unify
+ // subsequent treatment.
+ PyObject *plist = PyList_New(2);
+ if (NULL == plist) {
+ Py_DECREF(pfunc); // NOT COVERED BY TESTS
+ scm_memory_error("python-apply"); // NOT COVERED BY TESTS
+ }
+ if (-1 == PyList_SetItem(plist,0,PyString_FromString("__main__"))) {
+ Py_DECREF(pfunc); // NOT COVERED BY TESTS
+ Py_DECREF(plist); // NOT COVERED BY TESTS
+ scm_misc_error("python-apply","PyList_SetItem 0 failure (~S)", // NOT COVERED BY TESTS
+ scm_list_1(SCM_CAR(sfunc)));
+ }
+ if (-1 == PyList_SetItem(plist,1,pfunc)) {
+ Py_DECREF(pfunc); // NOT COVERED BY TESTS
+ Py_DECREF(plist); // NOT COVERED BY TESTS
+ scm_misc_error("python-apply","PyList_SetItem 1 failure (~S)", // NOT COVERED BY TESTS
+ scm_list_1(SCM_CAR(sfunc)));
+ }
+ pfunc = plist; // plist stole previous pfunc's value's reference.
+ }
+ else if (IS_PYSMOBP(sfunc)) {
+ // We check the SCM object because guile2python() destroys
+ // the indication whether the SCM was originally a pysmob, when it
+ // converts it into PyObject.
+ PyObject *plist1 = PyList_New(1);
+ if (NULL == plist1) {
+ Py_DECREF(pfunc); // NOT COVERED BY TESTS
+ scm_memory_error("python-apply"); // NOT COVERED BY TESTS
+ }
+ if (-1 == PyList_SetItem(plist1,0,pfunc)) {
+ Py_DECREF(pfunc); // NOT COVERED BY TESTS
+ Py_DECREF(plist1); // NOT COVERED BY TESTS
+ scm_misc_error("python-apply","PyList_SetItem 0 failure (~S)", // NOT COVERED BY TESTS
+ scm_list_1(SCM_CAR(sfunc)));
+ }
+ pfunc = plist1; // plist1 stole previous pfunc's value's reference.
+ // Now pfunc is an 1-member list, and this member is
+ // expected to be callable.
+ }
+ else if (!PyList_CheckExact(pfunc)) {
+ // Now, the qualified function name must be a proper list.
+ scm_wrong_type_arg("python-apply",SCM_ARG1,sfunc);
+ }
+
+ if (1 > PyList_Size(pfunc)) {
+ // The list must consist of at least one callable module name/object.
+ scm_misc_error("python-apply",
+ "first argument must contain at least one callable object (~S)",
+ scm_list_1(SCM_CAR(sfunc)));
+ }
+
+ if (PyString_CheckExact(PyList_GetItem(pfunc,0))) {
+ // If it is a string, we assume it to be the name of a module
+ // which has already been imported.
+ // Due to the existence of dots,
+ // we don't allow it to be symbol or keyword.
+
+ pfuncobj = PyImport_AddModule(PyString_AsString(PyList_GetItem(pfunc,0)));
+ if (NULL == pfuncobj) {
+ Py_DECREF(pfunc);
+ scm_misc_error("python-apply",
+ "module ~S could not be accessed - probably not imported",
+ scm_list_1(SCM_CAR(sfunc)));
+ }
+ Py_INCREF(pfuncobj);
+ }
+ else {
+ // We assume that it is a callable or object with attributes.
+ pfuncobj = PyList_GetItem(pfunc,0);
+ if (NULL == pfuncobj) {
+ Py_DECREF(pfunc);
+ scm_misc_error("python-apply",
+ "could not access object starting ~S",
+ scm_list_1(sfunc));
+ }
+ Py_INCREF(pfuncobj);
+ }
+
+ // Here we dereference attributes (if any).
+ int listsize = PyList_Size(pfunc);
+ int ind;
+
+ for (ind = 1; ind < listsize; ++ind) {
+ PyObject *pnextobj = PyObject_GetAttr(pfuncobj,PyList_GetItem(pfunc,ind));
+ if (NULL == pnextobj) {
+ PyObject *pexception = PyErr_Occurred();
+ Py_DECREF(pfunc);
+ Py_DECREF(pfuncobj);
+ if (pexception) {
+ PyErr_Clear();
+ // An AttributeError exception is expected here.
+ if (!PyErr_GivenExceptionMatches(pexception,PyExc_AttributeError)) {
+ PyObject *prepr = PyObject_Repr(pexception);
+ if (NULL == prepr) {
+ scm_misc_error("python-apply",
+ "python exception - could not be identified",
+ SCM_UNSPECIFIED);
+ }
+ else {
+ int strlength = PyString_Size(prepr);
+ char *pstr = PyString_AsString(prepr);
+ SCM srepr = scm_list_1(scm_mem2string(pstr,strlength));
+ Py_DECREF(prepr);
+ scm_misc_error("python-apply",
+ "Python exception (~A) while dereferencing object attribute",
+ srepr);
+ }
+ }
+ // else we got the expected AttributeError exception.
+ }
+ // else we got NULL==pnextobj without Python exception.
+ scm_misc_error("python-apply",
+ "could not dereference ~Ath level attribute in ~S",
+ scm_list_2(scm_long2num(ind),sfunc));
+ }
+ Py_INCREF(pnextobj);
+ Py_DECREF(pfuncobj);
+ pfuncobj = pnextobj;
+ }
+ Py_DECREF(pfunc); // We do not need it anymore. pfuncobj points at
+ // the function actually to be invoked.
+ if (!PyCallable_Check(pfuncobj)) {
+ Py_DECREF(pfuncobj);
+ scm_misc_error("python-apply","function denoted by ~S is not callable",scm_list_1(sfunc));
+ }
+
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_PYTHON_APPLY)) {
+ char *preprfuncobj = PyString_AsString(PyObject_Repr(pfuncobj));
+ scm_simple_format(scm_current_output_port(),
+ scm_makfrom0str("# python_apply: decoded function actually to be invoked: ~S\n"),
+ scm_list_1(scm_makfrom0str(preprfuncobj)));
+ }
+
+ // Retrieve positional arguments
+
+ parg = g2p_apply(sarg,sargtemplate);
+ if (NULL == parg) {
+ Py_DECREF(pfuncobj);
+ scm_misc_error("python-apply","positional arguments conversion failure (~S)",
+ scm_list_1(sarg));
+ }
+ // Validate that it is indeed a tuple.
+ if (!PyTuple_CheckExact(parg)) {
+ Py_DECREF(pfuncobj);
+ Py_DECREF(parg);
+ scm_wrong_type_arg("python-apply",SCM_ARG2,sarg);
+ }
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_PYTHON_APPLY)) {
+ char *pposarg = PyString_AsString(PyObject_Repr(parg));
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# python_apply: decoded positional arguments ~S\n"),scm_list_1(scm_makfrom0str(pposarg)));
+ }
+
+ // Retrieve keyword arguments.
+
+ pkw = guileassoc2pythondict(skw,skwtemplate);
+ if (NULL == pkw) {
+ // Seems that PyDict_CheckExact() does not handle NULL argument gracefully.
+ Py_DECREF(pfuncobj);
+ Py_DECREF(parg);
+ scm_misc_error("python-apply","keyword arguments conversion failure (~S)",
+ scm_list_1(skw));
+ }
+ if (!PyDict_CheckExact(pkw)) {
+ Py_DECREF(pfuncobj);
+ Py_DECREF(parg);
+ Py_DECREF(pkw);
+ scm_misc_error("python-apply",
+ "keyword arguments (~S) not properly converted into Python Dict",
+ scm_list_1(skw));
+ }
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_PYTHON_APPLY)) {
+ char *pkwarg = PyString_AsString(PyObject_Repr(pkw));
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# python_apply: decoded keyword arguments ~S\n"),scm_list_1(scm_makfrom0str(pkwarg)));
+ }
+
+ // Ready to invoke the function.
+
+ pres = PyEval_CallObjectWithKeywords(pfuncobj,parg,pkw);
+ PyObject *pexception = PyErr_Occurred();
+ if (pexception) {
+ PyObject *prepr = PyObject_Repr(pexception);
+ Py_DECREF(pfuncobj);
+ Py_DECREF(parg);
+ Py_DECREF(pkw);
+ Py_XDECREF(pres);
+ PyErr_Clear();
+ if (NULL == prepr) {
+ scm_misc_error("python-apply",
+ "python exception - could not be identified",
+ SCM_UNSPECIFIED);
+ }
+ else {
+ int strlength = PyString_Size(prepr);
+ char *pstr = PyString_AsString(prepr);
+ SCM srepr = scm_list_1(scm_mem2string(pstr,strlength));
+ Py_DECREF(prepr);
+ scm_misc_error("python-apply","Python exception: ~A",
+ srepr);
+ }
+ }
+ if (NULL != pres) {
+ sres = p2g_apply(pres,srestemplate);
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_PYTHON_APPLY)) {
+ char *presstr = PyString_AsString(PyObject_Repr(pres));
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# python_apply: decoded results:\n# Python: ~S\n# Scheme: ~S\n"),scm_list_2(scm_makfrom0str(presstr),sres));
+ }
+ }
+ else {
+ // else sres remains SCM_UNDEFINED.
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_PYTHON_APPLY)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# python_apply: Python code returned <NULL>\n"),SCM_EOL);
+ }
+ }
+ return(sres);
+}
+
+////////////////////////////////////////////////////////////////////////
+// Run Python code
+////////////////////////////////////////////////////////////////////////
+
+SCM
+python_eval(SCM sobj,SCM smode)
+{
+ if (!SCM_STRINGP(sobj)) {
+ scm_wrong_type_arg("python-eval",SCM_ARG1,sobj);
+ }
+
+ int start = (SCM_UNBNDP(smode)) || (SCM_EQ_P(SCM_BOOL_F,smode))
+ ? Py_file_input
+ : Py_eval_input;
+
+ char *pstr = gh_scm2newstr(sobj,NULL);
+ if (NULL == pstr) {
+ scm_memory_error("python-eval"); // NOT COVERED BY TESTS
+ //return(SCM_UNSPECIFIED);
+ }
+
+ PyObject *pmaindict =
+ PyModule_GetDict(PyImport_AddModule("__main__"));
+ if (NULL == pmaindict) {
+ scm_misc_error("python-eval","could not get __main__ for (~S), mode ~A", // NOT COVERED BY TESTS
+ scm_list_2(sobj,smode));
+ }
+ Py_INCREF(pmaindict);
+ PyObject *pres = PyRun_String(pstr, start, pmaindict, pmaindict);
+ Py_DECREF(pmaindict);
+ free(pstr);
+
+ PyObject *pexception = PyErr_Occurred();
+ if (pexception) {
+ PyObject *prepr = PyObject_Repr(pexception);
+ Py_XDECREF(pres);
+ PyErr_Clear();
+ if (NULL == prepr) {
+ scm_misc_error("python-eval", // NOT COVERED BY TESTS
+ "python exception - could not be identified",
+ SCM_UNSPECIFIED);
+ }
+ else {
+ int strlength = PyString_Size(prepr);
+ char *pstr = PyString_AsString(prepr);
+ SCM slist = scm_list_1(scm_mem2string(pstr,strlength));
+ Py_DECREF(prepr);
+ scm_misc_error("python-eval","Python exception: ~A",
+ slist);
+ }
+ }
+
+ switch(start) {
+ case Py_eval_input:
+ {
+ if (NULL != pres) {
+ SCM sres = p2g_apply(pres,
+ SCM_EQ_P(SCM_BOOL_T,smode)
+ ? srestemplate_default : smode);
+ Py_DECREF(pres);
+ return(sres);
+ }
+ else {
+ scm_misc_error("python-eval","could not return result of evaluation",
+ SCM_UNSPECIFIED);
+ return(SCM_UNSPECIFIED);
+ }
+ }
+ case Py_file_input:
+ default:
+ {
+ Py_XDECREF(pres);
+ return(SCM_UNSPECIFIED);
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+// Import a Python module and return a wrapped pointer to it.
+////////////////////////////////////////////////////////////////////////
+
+SCM
+python_import(SCM smodulename)
+{
+ if (!SCM_STRINGP(smodulename)) {
+ scm_wrong_type_arg("python-import",SCM_ARG1,smodulename);
+ }
+ else {
+ char *mname = gh_scm2newstr(smodulename,NULL);
+ PyObject *pmodule = PyImport_ImportModule(mname);
+ PyObject *pexception = PyErr_Occurred();
+ if (pexception) {
+ PyObject *prepr = PyObject_Repr(pexception);
+ Py_XDECREF(pmodule);
+ PyErr_Clear();
+
+ SCM smname = scm_list_1(scm_mem2string(mname,strlen(mname)));
+ free(mname);
+ if (NULL == prepr) {
+ scm_misc_error("python-import", // NOT COVERED BY TESTS
+ "Python exception during module ~A import - could not be identified",
+ smname);
+ }
+ else {
+ int strlength = PyString_Size(prepr);
+ char *pstr = PyString_AsString(prepr);
+ SCM slist = scm_list_2(SCM_CAR(smname),scm_mem2string(pstr,strlength));
+ Py_DECREF(prepr);
+ scm_misc_error("python-import",
+ "Python exception during module ~A import: ~A",
+ slist);
+ }
+ }
+ // OK, exception did not occur. Do we have a module?
+ if (NULL == pmodule) {
+ SCM slist = scm_list_1(scm_mem2string(mname,strlen(mname)));
+ free(mname);
+ scm_misc_error("python-eval","could not import module ~S",
+ slist);
+ }
+ free(mname);
+ SCM smodule = wrap_pyobject(pmodule);
+ Py_DECREF(pmodule); // wrap_pyobject did Py_INCREF
+ return(smodule);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+// Version and build information
+////////////////////////////////////////////////////////////////////////
+
+// The macros used below are defined in version.h.
+SCM
+pyguile_version(void)
+{
+ return(scm_makfrom0str("PyGuile Version " PYGUILE_VERSION ", Build " PYGUILE_BUILD));
+}
+
+void
+init_wrapper (void)
+{
+ Py_Initialize();
+ if (atexit(Py_Finalize)) {
+ fprintf(stderr,"cannot set Python finalization function\n"); // NOT COVERED BY TESTS
+ exit(1); // NOT COVERED BY TESTS
+ }
+ initpyscm();
+
+ init_pysmob_type();
+ init_g2p2g_smob_type();
+
+ // The following must happen after init_g2p2g_smob_type().
+ init_default_guiletopy_templates();
+
+ SCM s_default_g2p = scm_variable_ref(scm_c_lookup("guile2python"));
+ sargtemplate_default = scm_permanent_object(scm_list_2(scm_variable_ref(scm_c_lookup("g2p_list2Tuple")),s_default_g2p));
+ skwtemplate_default = SCM_UNDEFINED; // guileassoc2pythondict will choose the right default.
+ srestemplate_default = scm_permanent_object(scm_variable_ref(scm_c_lookup("python2guile")));
+
+ scm_c_define_gsubr ("python-eval",1,1,0,python_eval);
+ scm_c_define_gsubr ("python-apply",3,3,0,python_apply);
+ scm_c_define_gsubr ("python-import",1,0,0,python_import);
+ scm_c_define_gsubr ("pyguile-verbosity-set!",1,0,0,pyguile_verbosity_set);
+ scm_c_define_gsubr ("pyguile-version",0,0,0,pyguile_version);
+}
+
+////////////////////////////////////////////////////////////////////////
+// End of pyguile.c
diff --git a/pyguile.h b/pyguile.h
new file mode 100644
index 0000000..f524232
--- /dev/null
+++ b/pyguile.h
@@ -0,0 +1,87 @@
+// pyguile header file
+#ifndef PYGUILE_H
+#define PYGUILE_H
+
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2008 Omer Zak.
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this library, in a file named COPYING; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+// Boston, MA 02111-1307 USA
+//
+// For licensing issues, contact <w1@zak.co.il>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#include <Python.h> // Must be first header file
+#include <libguile.h>
+
+////////////////////////////////////////////////////////////////////////
+// Functions exported by the library to Guile
+////////////////////////////////////////////////////////////////////////
+
+
+// Import Python module whose name is given in the SCM.
+// The returned value is a pysmob pointing at the module PyObject.
+//
+// NOTE: support for representing nested module names (like "os.path")
+// by a list like ("os" "path") will be implemented by means
+// of Scheme level macros.
+// NOTE: we support only strings - not symbols or keywords - due to
+// potential existence of dots.
+extern SCM python_import(SCM smodulename);
+
+////////////////////////////////////////////////////////////////////////
+// Final pyguile interface functions
+////////////////////////////////////////////////////////////////////////
+
+// Evaluate a string, which is a Python script.
+// sobj - the string to be evaluated.
+// smode - #f or unspecified - if the caller does not expect a return value.
+// The string is evaluated under Py_file_input.
+// #t if the caller expects a return value and wants to
+// convert it using the default template (python2guile).
+// The string is evaluated under Py_eval_input.
+// Any other value - is caller-supplied template for
+// converting the return value.
+// PyRun_String() 2nd argument value is
+// smode ? Py_eval_input : Py_file_input
+// This is an optional argument, defaulting to #f.
+//
+// Typical use:
+// import sys; sys.path = ['']+sys.path
+// in order to be able to load modules from home directory.
+extern SCM python_eval(SCM sobj,SCM smode);
+
+
+// The Python call is func(*args,**kwargs)
+extern SCM python_apply(SCM sfunc, SCM sarg, SCM skw,
+ SCM sargtemplate, SCM skwtemplate,
+ SCM srestemplate);
+// sfunc can either be a function object or a list of strings, the first
+// of which names a module and the others select attributes of the
+// module's attributes.
+// sarg is a list of values, which are to serve as positional
+// arguments for the function.
+// skw is a list of pairs, each pair consisting of keyword (a string)
+// and a value.
+// sargtemplate - specifies how to convert sarg - optional argument.
+// skwtemplate - specifies how to convert skw - optional argument.
+// srestemplate - specifies how to convert the result back into
+// SCM - optional argument.
+
+#endif /* PYGUILE_H */
+
+////////////////////////////////////////////////////////////////////////
+// End of pyguile.h
diff --git a/pyguile.scm.in b/pyguile.scm.in
new file mode 100644
index 0000000..7782c50
--- /dev/null
+++ b/pyguile.scm.in
@@ -0,0 +1,49 @@
+; Load extension library pyguile.so
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Copyright (C) 2008 Omer Zak.
+; This library is free software; you can redistribute it and/or
+; modify it under the terms of the GNU Lesser General Public
+; License as published by the Free Software Foundation; either
+; version 2.1 of the License, or (at your option) any later version.
+;
+; This library is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; Lesser General Public License for more details.
+;
+; You should have received a copy of the GNU Lesser General Public License
+; along with this library, in a file named COPYING; if not, write to the
+; Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+; Boston, MA 02111-1307 USA
+;
+; For licensing issues, contact <w1@zak.co.il>.
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; Autogenerated at time %(timestamp)s
+;;;
+;;; To invoke it:
+;;; (use-modules (pyguile))
+;;; Then, can use: (python-import "mname")
+;;; (python-apply '("mname" "fname") '(#t) '())
+;;;
+;;; Note: before importing modules from your working directory,
+;;; you have to invoke:
+;;; (exec-python-str "import sys;sys.path = ['']+sys.path\n")
+;;; before trying to import anything.
+;;;
+(define-module (pyguile))
+(export python-eval)
+(export python-apply)
+(export python-import)
+(export pyguile-verbosity-set!)
+(export python-version)
+;To obtain guile version, use (version)
+(export pyguile-version)
+%(autogeneratedexports)s
+(export pyscm-default-template) ; Default template for wrapping procedures
+ ; into PyObjects.
+(load-extension "./libpyguile.so" "init_wrapper")
+(define python-version
+ (lambda () (python-eval "__import__('sys').version" #t)))
+; End of pyguile.scm
diff --git a/pyscm.c b/pyscm.c
new file mode 100644
index 0000000..1f36fb7
--- /dev/null
+++ b/pyscm.c
@@ -0,0 +1,577 @@
+// pyscm implementation file
+// Python data type for wrapping Guile SCM objects
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2008 Omer Zak.
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this library, in a file named COPYING; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+// Boston, MA 02111-1307 USA
+//
+// For licensing issues, contact <w1@zak.co.il>.
+//
+////////////////////////////////////////////////////////////////////////
+//
+// Implements PySCM data type - used for wrapping SCM objects in Python
+// and making it possible to call them and access/manipulate their
+// attributes.
+
+#include "pyscm.h"
+#include "guiletopy.h"
+#include "pytoguile.h"
+#include "verbose.h"
+
+////////////////////////////////////////////////////////////////////////
+// Guile Data Structures
+////////////////////////////////////////////////////////////////////////
+
+static SCM pyscm_registration_hash; // Hash table for preservation of SCMs referred to by PySCM instances. We use *_hashv_* functions for the keys. !!!to ensure that eqv? comparisons are OK here
+static long pyscm_registration_index; // Used for building keys for the above hash table.
+
+
+////////////////////////////////////////////////////////////////////////
+// Python Data Structures
+////////////////////////////////////////////////////////////////////////
+
+//static PyObject *ErrorObject;
+
+typedef struct {
+ PyObject_HEAD
+ long ob_scm_index; // Index into the SCM registration hash table.
+} pyscm_PySCMObject;
+
+
+//static struct PyMethodDef pyscm_PySCM_methods[] = {
+// {NULL, NULL} /* sentinel */
+//};
+
+
+static char pyscm_PySCMtype__doc__[] =
+"PyGuile SCM wrapper object"
+;
+
+
+static int pyscm_PySCM_print(pyscm_PySCMObject *self, FILE *fp, int flags);
+static PyObject *pyscm_PySCM_getattr(pyscm_PySCMObject *self, char *name);
+static int pyscm_PySCM_setattr(pyscm_PySCMObject *self,
+ char *name, PyObject *v);
+static long pyscm_PySCM_hash(pyscm_PySCMObject *self);
+static PyObject *pyscm_PySCM_call(pyscm_PySCMObject *self,
+ PyObject *args, PyObject *kwargs);
+static PyObject *pyscm_PySCM_str(pyscm_PySCMObject *self);
+static void pyscm_PySCM_dealloc(pyscm_PySCMObject *self);
+static PyObject *pyscm_PySCM_new(PyTypeObject *type,
+ PyObject *args, PyObject *kwds);
+
+
+static PyTypeObject pyscm_PySCMType = {
+ PyObject_HEAD_INIT(&PyType_Type)
+ 0, /*ob_size*/
+ "pyscm.PySCM", /*tp_name*/
+ sizeof(pyscm_PySCMObject), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ (destructor)pyscm_PySCM_dealloc, /*tp_dealloc*/
+ (printfunc)pyscm_PySCM_print, /*tp_print*/
+ (getattrfunc)pyscm_PySCM_getattr, /*tp_getattr*/
+ (setattrfunc)pyscm_PySCM_setattr, /*tp_setattr*/
+ (cmpfunc)0, /*tp_compare*/
+ (reprfunc)0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ (hashfunc)pyscm_PySCM_hash, /*tp_hash */
+ (ternaryfunc)pyscm_PySCM_call, /*tp_call*/
+ (reprfunc)pyscm_PySCM_str, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT /*| Py_TPFLAGS_BASETYPE*/ , /*tp_flags*/ // We don't expect to subclass this class.
+ pyscm_PySCMtype__doc__, /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)0, /* tp_init */
+ 0, /* tp_alloc */
+ (newfunc)pyscm_PySCM_new, /* tp_new */
+};
+
+
+////////////////////////////////////////////////////////////////////////
+// Functions
+////////////////////////////////////////////////////////////////////////
+
+static int
+pyscm_PySCM_print(pyscm_PySCMObject *self, FILE *fp, int flags)
+{
+ PyObject *pstr = pyscm_PySCM_str(self);
+ if (NULL == pstr) {
+ scm_memory_error("pyscm_PySCM_print");
+ }
+ int retval = PyObject_Print(pstr,fp,flags);
+ Py_DECREF(pstr);
+ return(retval);
+}
+
+
+// Documentation of the stemplate data structure, which is paired
+// with the actual SCM wrapped by an PySCM instance.
+// It is a 5-element vector with the following elements.
+// The first 4 elements deal with the SCM being wrapped by a callable
+// PySCM.
+// If any of the three templates is SCM_EOL, then the corresponding type
+// of arguments/result is not expected to exist (if the SCM object
+// returns a value to template of SCM_EOL, then the value is discarded
+// and None is returned to Python).
+// If GET_APPLY_FUNC() has the value SCM_EOL instead of a function,
+// it means that the object is not callable.
+//
+// If the relevant template value is #t, then a default is used. (CANCELLED - when building the template, can use a macro to fill in defaults.)
+// 0. p2g template for positional arguments
+#define GET_P2G_POSITIONAL_ARGS_TEMPLATE(stemplate) scm_vector_ref(stemplate,scm_long2num(0))
+// 1. p2g template for keyword arguments
+#define GET_P2G_KEYWORD_ARGS_TEMPLATE(stemplate) scm_vector_ref(stemplate,scm_long2num(1))
+// 2. g2p template for result
+#define GET_G2P_RESULT_TEMPLATE(stemplate) scm_vector_ref(stemplate,scm_long2num(2))
+// 3. function for actually applying the SCM object on the arguments
+// (default being 'apply').
+#define GET_APPLY_FUNC(stemplate) scm_vector_ref(stemplate,scm_long2num(3))
+// 4. Either #f or a hash (_hashv_ type) whose keys are described below.
+#define GET_ATTRS_HASH(stemplate) scm_vector_ref(stemplate,scm_long2num(4))
+//
+// If the 5th element is #f, then the SCM has no attributes.
+// Otherwise, the SCM has attributes (which can be either data or methods),
+// and the 5th element is supposed to be an hash.
+//
+// The hash keys are as follows:
+// #t - for default values (CANCELLED - when building the template, can use
+// a macro to fill in defaults.)
+// #f - how to deal with a missing attribute - value can be either
+// another #f (throw an attribute error exception to Python) or a
+// 4-element vector as described below.
+// #:-keyword - refers to attribute 'keyword'
+// The values are either #t (to use defaults for everything) (CANCELLED - when
+// building the template, can use a macro to fill
+// in defaults.)
+// or 4-element vectors:
+// 0. p2g template for converting __setattr__ value
+#define GET_H_P2G_SETATTR_TEMPLATE(shashvalue) scm_vector_ref(shashvalue,scm_long2num(0))
+// 1. g2p template for converting __getattr__ value
+#define GET_H_G2P_GETATTR_TEMPLATE(shashvalue) scm_vector_ref(shashvalue,scm_long2num(1))
+// 2. function (func sobj #:-keyword . value) for doing the real
+// setattr work; if the value is missing, do delattr.
+// It is expected to return #f if it failed, or any other value (including
+// SCM_UNDEFINED) if it succeeded.
+#define GET_H_SETATTR_FUNC(shashvalue) scm_vector_ref(shashvalue,scm_long2num(2))
+// 3. function (func sobj #:-keyword) for doing the real getattr
+// work.
+#define GET_H_GETATTR_FUNC(shashvalue) scm_vector_ref(shashvalue,scm_long2num(3))
+// If any of the above is #t then get the corresponding element from
+// the default vector. (CANCELLED)
+// If any of the GET_H_{GET,SET}ATTR_FUNC values is SCM_EOL, then the
+// corresponding function is suppressed. The *_TEMPLATE values must be
+// valid whenever the corresponding function exists.
+// The value corresponding to the key #f can also be another #f, which
+// would cause Python attribute error to be raised. This
+// mechanism allows objects to decide how they wish currently-nonexistent
+// attributes to be handled.
+// In the case of an attribute which is recognized by Python as a method,
+// the g2p template for __getattr__ would be a pair of g2p_opaque2PySCM
+// and a whole stemplate as described above.
+//
+// Signatures of SCM functions to be invoked by Python:
+// Callable SCM objects wrapped by PySCM - always have two arguments.
+// When default templates are used, the first argument's value is a list,
+// and the second argument's value is an alist.
+// Apply procedure (obtained by GET_APPLY_FUNC()) - has the same signature
+// as Scheme's apply procedure i.e. (apply func . args)
+
+
+// Functions for manipulating vectors:
+// SCM_VECTORP()
+// SCM_VECTOR_LENGTH()
+// scm_vector(scm_list_2(sobj1,sobj2))
+// scm_vector_ref(vector,scm_long2num(index_zero_based))
+
+// PROBLEM: need to prepend "-" to name before converting it into
+// #:-keyword - inefficient! How to eliminate this?
+
+// Common code for pyscm_PySCM_getattr() and pyscm_PySCM_setattr():
+// Retrieve and return the 4-element vector corresponding to desired
+// attribute of the pyscm_PySCMObject.
+// Perform also validity checking and raise Python exception if
+// invalid.
+// Since it is needed later, also the SCM object, corresponding to the
+// pyscm_PySCMObject, is returned to the caller, put into 2-element
+// list together with the #:-keyword corresponding to name.
+static SCM
+retrieve_sattr_vector(pyscm_PySCMObject *self, char *name, SCM *sobj_keyword)
+{
+ SCM shandle = scm_hashv_get_handle(pyscm_registration_hash,scm_long2num(self->ob_scm_index));
+ if (SCM_BOOLP(shandle) && SCM_EQ_P(SCM_BOOL_F,shandle)) {
+ Py_FatalError("PySCM object lost its associated SCM object");
+ }
+ // Now:
+ // SCM_CADR(shandle) is the SCM object itself
+ // SCM_CDDR(shandle) is the stemplate.
+ SCM sattrshash = GET_ATTRS_HASH(SCM_CDDR(shandle));
+
+ if (SCM_EQ_P(SCM_BOOL_F,sattrshash)) {
+ PyErr_SetString(PyExc_AttributeError, name);
+ return(SCM_UNDEFINED); // Error return
+ }
+
+ // The object has attributes. Build the hash key (a keyword).
+
+ size_t nlength = strlen(name);
+ char *dashstr = malloc(nlength+2);
+ dashstr[0] = '-';
+ dashstr[1] = '\0';
+ strncat(dashstr,name,nlength);
+ SCM skeyword = scm_make_keyword_from_dash_symbol(scm_str2symbol(dashstr));
+ // !!! Do we have to free dashstr?
+ // !!! Similar code is used also in pytoguile.c - review it.
+
+ SCM sattr_vector_handle = scm_hashv_get_handle(sattrshash,skeyword);
+ if (SCM_EQ_P(SCM_BOOL_F,sattr_vector_handle)) {
+ // Missing attribute. How to deal with it?
+ sattr_vector_handle = scm_hashv_get_handle(sattrshash,SCM_BOOL_F);
+ if (SCM_EQ_P(SCM_BOOL_F,sattr_vector_handle)) {
+ // Hash value corresponding to key #f is itself another #f, which
+ // means that the object does not wish to exhibit to Python
+ // unknown attributes.
+ PyErr_SetString(PyExc_AttributeError, name);
+ return(SCM_UNDEFINED); // Error return
+ }
+ // Otherwise, we'll use the hash value corresponding to #f as
+ // a catch-all for all attributes not otherwise defined.
+ }
+ *sobj_keyword = scm_list_2(SCM_CADR(shandle),skeyword);
+ return(SCM_CDR(sattr_vector_handle));
+}
+
+static PyObject *
+pyscm_PySCM_getattr(pyscm_PySCMObject *self, char *name)
+{
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_PYSCM)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# pyscm_PySCM_getattr: trying to get attribute=~S from pobj=~S\n"),scm_list_2(scm_makfrom0str(name),verbosity_repr((PyObject *)self)));
+ }
+ SCM sobj_keyword;
+ SCM sattr_vector = retrieve_sattr_vector(self,name,&sobj_keyword);
+ if (SCM_UNBNDP(sattr_vector)) {
+ // Attribute error exception was raised by retrieve_sattr_vector().
+ return(NULL);
+ }
+
+ SCM sgetattr_func = GET_H_GETATTR_FUNC(sattr_vector);
+ if (SCM_EQ_P(SCM_EOL,sgetattr_func)) {
+ PyErr_SetString(PyExc_AttributeError, name);
+ return(NULL);
+ }
+ SCM stemplate = GET_H_G2P_GETATTR_TEMPLATE(sattr_vector);
+
+ SCM sresult = scm_apply(sgetattr_func,sobj_keyword,SCM_EOL);
+ return(g2p_apply(sresult,stemplate));
+}
+
+static int
+pyscm_PySCM_setattr(pyscm_PySCMObject *self, char *name, PyObject *v)
+{
+ /* Set attribute 'name' to value 'v'. v==NULL means delete */
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_PYSCM)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# pyscm_PySCM_setattr: trying to set attribute=~S from pobj=~S to value ~S\n"),scm_list_3(scm_makfrom0str(name),verbosity_repr((PyObject *)self),verbosity_repr(v)));
+ }
+ SCM sobj_keyword;
+ SCM sattr_vector = retrieve_sattr_vector(self,name,&sobj_keyword);
+ if (SCM_UNBNDP(sattr_vector)) {
+ // Attribute error exception was raised by retrieve_sattr_vector().
+ return(-1);
+ }
+
+ SCM ssetattr_func = GET_H_SETATTR_FUNC(sattr_vector);
+ if (SCM_EQ_P(SCM_EOL,ssetattr_func)) {
+ PyErr_SetString(PyExc_AttributeError, name);
+ return(-1);
+ }
+
+ if (NULL != v) {
+ SCM sval = p2g_apply(v,
+ GET_H_P2G_SETATTR_TEMPLATE(sattr_vector));
+ scm_append_x(scm_list_2(sobj_keyword,sval));
+ }
+
+ SCM sresult = scm_apply(ssetattr_func,sobj_keyword,SCM_EOL);
+ return(SCM_EQ_P(SCM_BOOL_F,sresult) ? (-1) : 0);
+}
+
+static long
+pyscm_PySCM_hash(pyscm_PySCMObject *self)
+{
+ /* Return a hash of self (or -1) */
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_PYSCM)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# pyscm_PySCM_hash: hash is ~S\n"),scm_list_1(scm_long2num(self->ob_scm_index)));
+ }
+ return(self->ob_scm_index);
+}
+
+// Compute logical XOR of a and b
+int logical_xor(int a,int b)
+{
+ return((a == 0)
+ ? (b != 0)
+ : (b == 0));
+}
+// Compute logical equivalence of a and b (logical inverse of XOR)
+int logical_equiv(int a,int b)
+{
+ return((a != 0)
+ ? (b != 0)
+ : (b == 0));
+}
+
+static PyObject *
+pyscm_PySCM_call(pyscm_PySCMObject *self, PyObject *args, PyObject *kwargs)
+{
+ /* Return the result of calling self with argument args */
+
+ SCM shandle = scm_hashv_get_handle(pyscm_registration_hash,scm_long2num(self->ob_scm_index));
+ if (SCM_BOOLP(shandle) && SCM_EQ_P(SCM_BOOL_F,shandle)) {
+ Py_FatalError("PySCM object lost its associated SCM object"); // NOT COVERED BY TESTS
+ }
+ // Now:
+ // SCM_CADR(shandle) is the SCM object itself
+ // SCM_CDDR(shandle) is the stemplate.
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_PYSCM)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# pyscm_PySCM_call: calling ~S with args=~S and keywords=~S; stemplate=~S\n"),scm_list_4(SCM_CADR(shandle),verbosity_repr(args),verbosity_repr(kwargs),SCM_CDDR(shandle)));
+ }
+
+ SCM sapply_func = GET_APPLY_FUNC(SCM_CDDR(shandle));
+ if (SCM_EQ_P(SCM_EOL,sapply_func)) {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_PYSCM)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# pyscm_PySCM_call: raising exceptions.TypeError due to \"PySCM wraps a non-callable SCM\"\n"),SCM_EOL);
+ }
+ PyErr_SetString(PyExc_TypeError, "PySCM wraps a non-callable SCM");
+ return(NULL);
+ }
+
+ // Process arguments.
+ SCM sargs_template = GET_P2G_POSITIONAL_ARGS_TEMPLATE(SCM_CDDR(shandle));
+ SCM skwargs_template = GET_P2G_KEYWORD_ARGS_TEMPLATE(SCM_CDDR(shandle));
+ /*if (logical_xor(SCM_EQ_P(SCM_EOL,sargs_template),(NULL==args))
+ || logical_xor(SCM_EQ_P(SCM_EOL,skwargs_template),(NULL==kwargs)))*/
+ // The following allows template to exist without actual arguments.
+ if ((SCM_EQ_P(SCM_EOL,sargs_template) && (NULL != args))
+ || (SCM_EQ_P(SCM_EOL,skwargs_template) && (NULL != kwargs))) {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_PYSCM)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# pyscm_PySCM_call: raising exceptions.TypeError due to \"wrapped SCM does not take some of the provided arguments\"\n"),SCM_EOL);
+ }
+ PyErr_SetString(PyExc_TypeError, "wrapped SCM does not take some of the provided arguments");
+ return(NULL);
+ }
+
+ SCM sargs = SCM_EQ_P(SCM_EOL,sargs_template) || (NULL == args)
+ ? SCM_EOL : p2g_apply(args,sargs_template);
+ SCM skwargs = SCM_EQ_P(SCM_EOL,skwargs_template) || (NULL == kwargs)
+ ? SCM_EOL : p2g_apply(kwargs,skwargs_template);
+
+ SCM sresult = scm_apply(sapply_func,scm_list_2(SCM_CADR(shandle),scm_list_2(sargs,skwargs)),SCM_EOL);
+ SCM sresult_template = GET_G2P_RESULT_TEMPLATE(SCM_CDDR(shandle));
+ if (SCM_EQ_P(SCM_EOL,sresult_template)) {
+ Py_RETURN_NONE;
+ }
+ else {
+ return(g2p_apply(sresult,sresult_template));
+ }
+}
+
+// Does not include the template object in the string representation.
+static PyObject *
+pyscm_PySCM_str(pyscm_PySCMObject *self)
+{
+ if (0 == self->ob_scm_index) {
+ return(PyString_FromString("<no SCM association>"));
+ }
+ SCM shandle = scm_hashv_get_handle(pyscm_registration_hash,scm_long2num(self->ob_scm_index));
+ if (SCM_BOOLP(shandle) && SCM_EQ_P(SCM_BOOL_F,shandle)) {
+ Py_FatalError("PySCM object lost its associated SCM object");
+ }
+ SCM sstr = scm_object_to_string(SCM_CADR(shandle),scm_variable_ref(scm_c_lookup("write")));
+
+ PyObject *pstr = PyString_FromStringAndSize(SCM_STRING_CHARS(sstr),SCM_STRING_LENGTH(sstr));
+ return(pstr); // possibly NULL.
+}
+
+static void
+pyscm_PySCM_dealloc(pyscm_PySCMObject *self)
+{
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_GC_PYSCM)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# pyscm_PySCM_dealloc: deallocating PySCMObject with hash ~S\n"),scm_list_1(scm_long2num(self->ob_scm_index)));
+ }
+ if (0 != self->ob_scm_index) {
+ // Unregister the associated SCM from the hash table.
+ SCM shashkey = scm_long2num(self->ob_scm_index);
+ scm_hashv_remove_x(pyscm_registration_hash,shashkey);
+ // If ob_scm_index is zero, no SCM was associated yet with
+ // this PySCM instance.
+ }
+ self->ob_type->tp_free((PyObject*)self);
+}
+
+static PyObject *
+pyscm_PySCM_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ pyscm_PySCMObject *self;
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_GC_PYSCM)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# pyscm_PySCM_new: was called\n"),SCM_EOL);
+ }
+ self = (pyscm_PySCMObject *)type->tp_alloc(type,0);
+ if (NULL != self) {
+ self->ob_scm_index = 0;
+ }
+ return((PyObject *)self);
+}
+
+////////////////////////////////////////////////////////////////////////
+// Interface to the rest of PyGuile
+////////////////////////////////////////////////////////////////////////
+
+// Create a pyscm_PySCMObject instance, which wraps sobj and associates
+// with it with template for data conversions when python accesses data
+// and functions/methods associated with sobj.
+PyObject *
+wrap_scm(SCM sobj,SCM stemplate)
+{
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_PYSCM)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# wrap_scm: was called to wrap ~S\n"),scm_list_1(sobj));
+ }
+ pyscm_PySCMObject *pwrapper = PyObject_New(pyscm_PySCMObject,&pyscm_PySCMType);
+ if (NULL == pwrapper) {
+ scm_memory_error("wrap_scm"); // NOT COVERED BY TESTS
+ }
+ //PyObject_Init(pwrapper,&pyscm_PySCMType); // Is it needed or does PyObject_New() take care of it?
+ //if (NULL == pwrapper) {
+ // scm_misc_error("wrap_scm","could not wrap object ~S with PySCM when using conversion template ~S",
+ // scm_list_2(sobj,stemplate));
+ //}
+ else {
+ SCM sconsed = scm_cons(sobj,stemplate);
+ SCM shashkey = scm_long2num(++pyscm_registration_index);
+ scm_hashv_create_handle_x(pyscm_registration_hash,shashkey,sconsed);
+ pwrapper->ob_scm_index = pyscm_registration_index;
+ return((PyObject *)pwrapper);
+ }
+}
+
+// Return 0 if pobj is not of this type and/or does not wrap a SCM.
+// Otherwise, return a nonzero value.
+int
+PySCMObject_Check(PyObject *pobj)
+{
+ if (!PyObject_TypeCheck(pobj, &pyscm_PySCMType)) {
+ return(0);
+ }
+ return ((0 == ((pyscm_PySCMObject *)pobj)->ob_scm_index)
+ ? 0 // pobj does not actually wrap a SCM.
+ : 1);
+}
+
+// Unwrap a pyscm_PySCMObject instance and get from it the original
+// SCM object. If the object is not a pyscm_PySCMObject or does not
+// wrap a SCM object, raise an error.
+SCM
+unwrap_pyscm_object(PyObject *pobj)
+{
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_PYSCM)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# unwrap_pyscm_object: trying to unwrap pobj=~S\n"),scm_list_1(verbosity_repr(pobj)));
+ }
+
+ if (!PySCMObject_Check(pobj)) {
+ Py_FatalError("Trying to pyscm-unwrap a non-PySCM");
+ }
+ SCM shandle = scm_hashv_get_handle(pyscm_registration_hash,scm_long2num(((pyscm_PySCMObject *)pobj)->ob_scm_index));
+ return(SCM_CADR(shandle));
+}
+
+////////////////////////////////////////////////////////////////////////
+// Initializer
+////////////////////////////////////////////////////////////////////////
+
+static struct PyMethodDef pyscm_methods[] = {
+
+ {NULL, (PyCFunction)NULL, 0, NULL} /* sentinel */
+};
+
+
+/* Initialization function for the module (*must* be called initpyscm) */
+
+static char pyscm_module_documentation[] =
+"pyscm - defines the Custom Python datatype PySCM for wrapping SCM objects"
+;
+
+#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
+#define PyMODINIT_FUNC void
+#endif
+PyMODINIT_FUNC
+initpyscm(void)
+{
+ PyObject *m;
+
+ /*pyscm_PySCMType.tp_new = PyType_GenericNew;*/
+ if (PyType_Ready(&pyscm_PySCMType) < 0) {
+ return; // NOT COVERED BY TESTS
+ }
+
+ /* Create the module and add the functions */
+ m = Py_InitModule4("pyscm", pyscm_methods,
+ pyscm_module_documentation,
+ (PyObject*)NULL,PYTHON_API_VERSION);
+ if (NULL == m) {
+ return; // NOT COVERED BY TESTS
+ }
+
+ Py_INCREF(&pyscm_PySCMType);
+ PyModule_AddObject(m, "PySCM", (PyObject *)&pyscm_PySCMType);
+
+ /* Add some symbolic constants to the module */
+ //PyObject *d;
+ //d = PyModule_GetDict(m);
+ //ErrorObject = PyString_FromString("pyscm.error");
+ //PyDict_SetItemString(d, "error", ErrorObject);
+
+ /* Add constants here */
+ // Currently, none is needed.
+
+ /* Check for errors */
+ if (PyErr_Occurred()) {
+ Py_FatalError("can't initialize module pyscm"); // NOT COVERED BY TESTS
+ }
+
+ // This part initializes the Guile data structures needed
+ // by this module.
+ pyscm_registration_hash = scm_permanent_object(scm_c_make_hash_table(65537));
+ pyscm_registration_index = 0;
+}
+
+////////////////////////////////////////////////////////////////////////
+// End of pyscm.c
diff --git a/pyscm.h b/pyscm.h
new file mode 100644
index 0000000..baccc25
--- /dev/null
+++ b/pyscm.h
@@ -0,0 +1,55 @@
+// pyscm header file
+// Python data type for wrapping Guile SCM objects
+////////////////////////////////////////////////////////////////////////
+
+#ifndef PYSCM_H
+#define PYSCM_H
+
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2008 Omer Zak.
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this library, in a file named COPYING; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+// Boston, MA 02111-1307 USA
+//
+// For licensing issues, contact <w1@zak.co.il>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#include <Python.h> // Must be first header file
+#include <libguile.h>
+
+////////////////////////////////////////////////////////////////////////
+// pyscm.PySCM
+////////////////////////////////////////////////////////////////////////
+
+PyObject *wrap_scm(SCM sobj,SCM stemplate);
+
+// Return 0 if pobj is not of this type and/or does not wrap a SCM.
+// Otherwise, return a nonzero value.
+int PySCMObject_Check(PyObject *pobj);
+
+// Unwrap a pyscm_PySCMObject instance and get from it the original
+// SCM object. If the object is not a pyscm_PySCMObject or does not
+// wrap a SCM object, raise an error.
+SCM unwrap_pyscm_object(PyObject *pobj);
+
+void initpyscm(void);
+
+////////////////////////////////////////////////////////////////////////
+
+#endif /* PYSCM_H */
+
+////////////////////////////////////////////////////////////////////////
+// End of pyscm.h
diff --git a/pysmob.c b/pysmob.c
new file mode 100644
index 0000000..98c26f4
--- /dev/null
+++ b/pysmob.c
@@ -0,0 +1,342 @@
+// pysmob implementation
+//
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2008 Omer Zak.
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this library, in a file named COPYING; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+// Boston, MA 02111-1307 USA
+//
+// For licensing issues, contact <w1@zak.co.il>.
+//
+////////////////////////////////////////////////////////////////////////
+//
+// Implements the Guile SMOB type, which encapsulates Python objects.
+// While those SMOBs can be used to encapsulate any PyObject* object,
+// they are typically used to encapsulate only class instances.
+//
+////////////////////////////////////////////////////////////////////////
+//
+// Note about verbosity handling:
+//
+// This module uses printf() rather than scm_simple_format & Co. for
+// PYGUILE_VERBOSE_GC and PYGUILE_VERBOSE_GC_DETAILED.
+// The reason is that scm_* functions cannot be used during garbage
+// collection.
+//
+////////////////////////////////////////////////////////////////////////
+#include "pysmob.h"
+#include "verbose.h"
+
+#ifndef Py_ssize_t
+#define Py_ssize_t int
+#endif /* Py_ssize_t */
+
+static scm_t_bits tag_pysmob;
+
+////////////////////////////////////////////////////////////////////////
+// Wrapped PyObject memory management
+////////////////////////////////////////////////////////////////////////
+
+static PyObject *pdict_wrapped_pyobjects;
+// Pointer at Dict, whose keys are addresses of wrapped PyObjects.
+// (The keys have to be addresses, because the objects themselves
+// can be mutable and hence unsuitable for use as Dict keys.)
+
+//scm_t_c_hook_function clear_pdict_values;
+//scm_t_c_hook_function delete_unmarked_pdict_keys;
+
+void *
+clear_pdict_values(void *hook_data, void *func_data, void *data)
+{
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_GC)) {
+ //scm_simple_format(scm_current_output_port(),scm_makfrom0str("# Starting garbage collection (mark phase) - entered clear_pdict_values\n"),SCM_EOL);
+ printf("# Starting garbage collection (mark phase) - entered clear_pdict_values\n");
+ }
+ PyObject *pdict = (PyObject *)func_data;
+ if (!PyDict_CheckExact(pdict)) {
+ scm_misc_error("clear_pdict_values","invalid pysmobs Dict",SCM_EOL); // NOT COVERED BY TESTS
+ }
+
+ PyObject *key;
+ PyObject *value;
+ Py_ssize_t pos = 0;
+ while(PyDict_Next(pdict,&pos,&key,&value)) {
+ Py_INCREF(Py_False);
+ if (0 != PyDict_SetItem(pdict,
+ key,
+ Py_False)) {
+ Py_DECREF(Py_False); // NOT COVERED BY TESTS
+ scm_misc_error("clear_pdict_values","failed to clear pysmobs Dict",SCM_EOL); // NOT COVERED BY TESTS
+ }
+ }
+ return(NULL);
+}
+
+void *
+delete_unmarked_pdict_keys(void *hook_data, void *func_data, void *data)
+{
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_GC)) {
+ //scm_simple_format(scm_current_output_port(),scm_makfrom0str("# Going to finish garbage collection (sweep phase) - entered delete_unmarked_pdict_keys\n"),SCM_EOL);
+ printf("# Going to finish garbage collection (sweep phase) - entered delete_unmarked_pdict_keys\n");
+ }
+ PyObject *pdict = (PyObject *)func_data;
+ if (!PyDict_CheckExact(pdict)) {
+ scm_misc_error("delete_unmarked_pdict_keys","invalid pysmobs Dict",SCM_EOL); // NOT COVERED BY TESTS
+ }
+
+ PyObject *pdict_clone = PyDict_Copy(pdict);
+ if (NULL == pdict_clone) {
+ PyErr_Clear(); // NOT COVERED BY TESTS
+ scm_misc_error("delete_ummarked_pdict_keys","failed to prepare for deleting from Dict",SCM_EOL); // NOT COVERED BY TESTS
+ }
+
+ PyObject *key;
+ PyObject *value;
+ Py_ssize_t pos = 0;
+ while(PyDict_Next(pdict_clone,&pos,&key,&value)) {
+ if (Py_False == value) {
+ Py_INCREF(key);
+ if (0 != PyDict_DelItem(pdict,key)) {
+ PyErr_Clear(); // NOT COVERED BY TESTS
+ Py_DECREF(key); // NOT COVERED BY TESTS
+ Py_DECREF(pdict_clone); // NOT COVERED BY TESTS
+ scm_misc_error("delete_ummarked_pdict_keys","failed to delete item from Dict",SCM_EOL); // NOT COVERED BY TESTS
+ }
+ long pptr = PyLong_AsLong(key);
+ Py_DECREF((PyObject *)pptr); // Actually delete (if necessary) the formerly-wrapped PyObject.
+ Py_DECREF(key);
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_GC_DETAILED)) {
+ //scm_simple_format(scm_current_output_port(),scm_makfrom0str("# delete_unmarked_pdict_keys: deleting ~S\n"),scm_list_1(scm_long2num(pptr)));
+ printf("# delete_unmarked_pdict_keys: deleting 0x%08lX\n",pptr);
+ }
+ }
+ }
+ Py_DECREF(pdict_clone);
+ return(NULL);
+}
+
+
+////////////////////////////////////////////////////////////////////////
+// Private functions
+////////////////////////////////////////////////////////////////////////
+
+static SCM
+mark_pysmob(SCM psmob)
+{
+ // New system of garbage collection:
+ // Each time we wrap a PyObject, we add its address, as a key, to
+ // a Python Dict, which will serve as hash table.
+ //
+ // Python facilities will be used for this purpose.
+ //
+ // When starting the mark phase, we clear all values in the
+ // aforementioned Python Dict.
+ //
+ // For each pysmob to be marked, we mark the corresponding Python
+ // Dict value.
+ //
+ // At end of the sweep phase, we delete from the Python Dict any
+ // items not marked.
+ // The reference counting will be maintained by ownership by the
+ // Python Dict. Guile will not deal with reference counting
+ // related to pysmobs at all.
+
+ long key = (long) unwrap_pysmob(psmob);
+ PyObject *pkey = PyLong_FromLong(key);
+ if (NULL == pkey) {
+ PyErr_Clear(); // NOT COVERED BY TESTS
+ scm_memory_error("mark_pysmob"); // NOT COVERED BY TESTS
+ }
+ int ret = PyDict_Contains(pdict_wrapped_pyobjects,pkey);
+ if (-1 == ret) {
+ PyErr_Clear(); // NOT COVERED BY TESTS
+ Py_DECREF(pkey); // NOT COVERED BY TESTS
+ scm_misc_error("mark_pysmob","error when checking pysmobs Dict for ~S",scm_list_1(psmob)); // NOT COVERED BY TESTS
+ }
+ if (0 == ret) {
+ Py_DECREF(pkey);
+ scm_misc_error("mark_pysmob","smob not found in pysmobs Dict: ~S",scm_list_1(psmob));
+ }
+ Py_INCREF(Py_True);
+ if (0 != PyDict_SetItem(pdict_wrapped_pyobjects,
+ pkey,
+ Py_True)) {
+ Py_DECREF(pkey);
+ scm_misc_error("mark_pysmob","failed to mark pysmob ~S",scm_list_1(psmob));
+ }
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_GC_DETAILED)) {
+ //scm_simple_format(scm_current_output_port(),scm_makfrom0str("# mark_pysmob: marking ~S\n"),scm_list_1(scm_long2num(key)));
+ printf("# mark_pysmob: marking 0x%08lX\n",key);
+ }
+
+ // scm_gc_mark(any SCM object referred to by the Python object);
+ // One SCM object is returned to the caller (who will mark it).
+ return(SCM_UNSPECIFIED); // No need to mark any SCM object for now
+ // (until PyObjects learn to hold wrapped SCM objects).
+}
+
+
+//static size_t
+//free_pysmob(SCM psmob)
+//{
+// PyObject *pobj = unwrap_pysmob(psmob);
+// if (pyguile_verbosity_test(PYGUILE_VERBOSE_GC_DETAILED)) {
+// scm_simple_format(scm_current_output_port(),scm_makfrom0str("# free_pysmob: freeing object ~S\n"),scm_list_1(scm_long2num((long)pobj)));
+// }
+// Py_XDECREF(pobj);
+// return(0);
+//}
+
+static int
+print_pysmob(SCM smob, SCM port, scm_print_state* prstate)
+{
+ if (!SCM_SMOB_PREDICATE(tag_pysmob,smob)) {
+ scm_wrong_type_arg("print-pysmob",SCM_ARG1,smob); // NOT COVERED BY TESTS
+ }
+ if (!SCM_PORTP(port)) {
+ scm_wrong_type_arg("print-pysmob",SCM_ARG2,port); // NOT COVERED BY TESTS
+ }
+ // I don't know how to validate the 3rd argument.
+ PyObject *prepr = PyObject_Repr(unwrap_pysmob(smob));
+ if (NULL != prepr) {
+ char *pstr = PyString_AsString(prepr);
+ scm_puts("(python-eval ",port);
+ scm_puts(pstr, port);
+ scm_puts(" #t)",port);
+ Py_DECREF(prepr); // also invalidates pstr.
+ }
+ else {
+ scm_misc_error("print-pysmob","repr(~S) failure",scm_list_1(smob)); // NOT COVERED BY TESTS
+ //scm_puts("*nil*", port);
+ }
+ return(1); // Nonzero means success.
+}
+
+static SCM
+equalp_pysmob(SCM smob1, SCM smob2)
+{
+ if (!SCM_SMOB_PREDICATE(tag_pysmob,smob1)) {
+ scm_wrong_type_arg("equalp-pysmob",SCM_ARG1,smob1);
+ }
+ if (!SCM_SMOB_PREDICATE(tag_pysmob,smob2)) {
+ scm_wrong_type_arg("equalp-pysmob",SCM_ARG2,smob2);
+ }
+ PyObject *pobj1 = unwrap_pysmob(smob1);
+ if (NULL == pobj1) {
+ scm_misc_error("equalp-pysmob","argument 1 (~S) unwrapping failure", // NOT COVERED BY TESTS
+ scm_list_1(smob1));
+ }
+ PyObject *pobj2 = unwrap_pysmob(smob2);
+ if (NULL == pobj2) {
+ scm_misc_error("equalp-pysmob","argument 2 (~S) unwrapping failure", // NOT COVERED BY TESTS
+ scm_list_1(smob2));
+ }
+ switch (PyObject_RichCompareBool(pobj1,pobj2,Py_EQ)) {
+ case 0:
+ return(SCM_BOOL_F);
+ case 1:
+ return(SCM_BOOL_T);
+ case -1:
+ default:
+ // Error.
+ scm_misc_error("equalp-pysmob","comparison failure ~S vs. ~S",
+ scm_list_2(smob1,smob2));
+ return(SCM_UNDEFINED);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+// Public functions
+////////////////////////////////////////////////////////////////////////
+
+
+// Return nonzero if sobj is of type pysmob.
+int
+IS_PYSMOBP(SCM sobj)
+{
+ return(SCM_SMOB_PREDICATE(tag_pysmob,sobj));
+}
+
+// Create a pysmob corresponding to a PyObject.
+SCM
+wrap_pyobject(PyObject *pobj)
+{
+ if (NULL == pobj) {
+ scm_misc_error("wrap-pyobject","NULL PyObject",SCM_EOL); // NOT COVERED BY TESTS
+ //return(SCM_UNSPECIFIED);
+ }
+ else {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_NEW_PYSMOB)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# wrap_pyobject: new wrapped object ~S\n"),scm_list_1(scm_long2num((long)pobj)));
+ }
+ Py_INCREF(pobj);
+ Py_INCREF(Py_True);
+ PyDict_SetItem(pdict_wrapped_pyobjects,
+ PyLong_FromLong((long)pobj),
+ Py_True);
+ SCM_RETURN_NEWSMOB(tag_pysmob,pobj);
+ }
+}
+
+// Provide reference to PyObject embedded in a pysmob.
+// No ownership transfer is implied.
+PyObject *
+unwrap_pysmob(SCM sobj)
+{
+ if (!SCM_SMOB_PREDICATE(tag_pysmob,sobj)) {
+ scm_wrong_type_arg("unwrap-pysmob",SCM_ARG1,sobj); // NOT COVERED BY TESTS
+ }
+ PyObject *pobj = (PyObject *)SCM_SMOB_DATA(sobj);
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_UNWRAP_PYSMOB)) {
+ // The scm_simple_format code can safely be invoked if we know
+ // that we are not inside garbage collection.
+ // TODO: invoke a flag for this purpose.
+ //scm_simple_format(scm_current_output_port(),scm_makfrom0str("# unwrap_pysmob: accessing object ~S\n"),scm_list_1(scm_long2num((long)pobj)));
+ printf("# unwrap_pysmob: accessing object 0x%08lX\n",(long)pobj);
+ }
+ return(pobj);
+}
+
+////////////////////////////////////////////////////////////////////////
+
+void finalize_pysmob_type(void)
+{
+ Py_DECREF(pdict_wrapped_pyobjects);
+}
+
+void init_pysmob_type(void)
+{
+ tag_pysmob = scm_make_smob_type("pysmob",0);
+ scm_set_smob_mark (tag_pysmob, mark_pysmob);
+ //scm_set_smob_free(tag_pysmob,free_pysmob); // does nothing in the new scheme of managing PyObjects during garbage collection.
+ scm_set_smob_print(tag_pysmob,print_pysmob);
+ scm_set_smob_equalp(tag_pysmob,equalp_pysmob);
+
+ // Py_Initialize must have already been invoked
+ // by init_pysmob_type()'s caller.
+ pdict_wrapped_pyobjects = PyDict_New();
+ if (atexit(finalize_pysmob_type)) {
+ fprintf(stderr,"cannot set pysmob finalization function\n"); // NOT COVERED BY TESTS
+ exit(1); // NOT COVERED BY TESTS
+ }
+
+ // Register garbage collection hooks.
+ scm_c_hook_add(&scm_before_mark_c_hook,&clear_pdict_values,
+ (void *)pdict_wrapped_pyobjects,0);
+ scm_c_hook_add(&scm_after_sweep_c_hook,&delete_unmarked_pdict_keys,
+ (void *)pdict_wrapped_pyobjects,0);
+}
+
+// End of pysmob.c
diff --git a/pysmob.h b/pysmob.h
new file mode 100644
index 0000000..c39ebfb
--- /dev/null
+++ b/pysmob.h
@@ -0,0 +1,53 @@
+// pysmob header file
+//
+////////////////////////////////////////////////////////////////////////
+
+#ifndef PYSMOB_H
+#define PYSMOB_H
+
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2008 Omer Zak.
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this library, in a file named COPYING; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+// Boston, MA 02111-1307 USA
+//
+// For licensing issues, contact <w1@zak.co.il>.
+//
+////////////////////////////////////////////////////////////////////////
+//
+// Declares the Guile SMOB type, which encapsulates Python objects.
+// While those SMOBs can be used to encapsulate any PyObject* object,
+// they are typically used to encapsulate only class instances.
+
+#include <Python.h> // Must be first header file
+#include <libguile.h>
+
+// Return nonzero if sobj is of type pysmob.
+int IS_PYSMOBP(SCM sobj);
+
+void init_pysmob_type(void);
+// Naming convention: don't start names with "py", but use them
+// if they are not the first characters of a name.
+
+SCM wrap_pyobject(PyObject *pobj);
+// Create a pysmob corresponding to a PyObject.
+
+PyObject *unwrap_pysmob(SCM sobj);
+// Provide reference to PyObject embedded in a pysmob.
+// No ownership transfer is implied.
+
+#endif /* PYSMOB_H */
+////////////////////////////////////////////////////////////////////////
+// End of pysmob.h
diff --git a/pytoguile.c b/pytoguile.c
new file mode 100644
index 0000000..640ddaa
--- /dev/null
+++ b/pytoguile.c
@@ -0,0 +1,747 @@
+// pytoguile functions
+// Functions for conversion from PyObjects into Guile SCMs.
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2008 Omer Zak.
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this library, in a file named COPYING; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+// Boston, MA 02111-1307 USA
+//
+// For licensing issues, contact <w1@zak.co.il>.
+//
+////////////////////////////////////////////////////////////////////////
+//#include <Python.h> // included in pytoguile.h
+//#include <libguile.h> // included in pytoguile.h
+#include "pytoguile.h"
+#include "pysmob.h"
+#include "g2p2g_smob.h" // used in pytoguile.inc
+#include "verbose.h"
+#include "pyscm.h"
+
+////////////////////////////////////////////////////////////////////////
+// Convert data from Python (PyObject *) into Guile (SCM)
+// representation
+////////////////////////////////////////////////////////////////////////
+
+// The following functions convert each a single data type.
+// They have to be efficient.
+// The interface conventions are:
+// 1. The function gets a single PyObject * argument, and upon
+// success - returns a single SCM.
+// 2. The function is responsible for checking the data type of
+// and whether the value of its argument is in range.
+// If any of them fails, the function returns SCM_UNDEFINED.
+// 3. If there is any error not associated with wrong data type
+// of its argument, the function throws a scm exception.
+// 4. Naming convention:
+// p2g_{Python datatype name}2{Guile datatype name}
+
+
+////////////////////////////////////////////////////////////////////////
+// Apply a template to PyObject for converting it into a SCM
+////////////////////////////////////////////////////////////////////////
+
+SCM
+p2g_apply(PyObject *pobj,SCM stemplate)
+{
+ if (IS_P2G_SMOBP(stemplate)) {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_apply: pobj=~S smob-stemplate=~S\n"),scm_list_2(verbosity_repr(pobj),stemplate));
+ }
+ return((get_p2g_function(stemplate))(pobj,SCM_UNSPECIFIED));
+ }
+ else if (!SCM_EQ_P(SCM_BOOL_F,scm_pair_p(stemplate))) {
+ if (IS_P2G_SMOBP(SCM_CAR(stemplate))) {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_apply: pobj=~S pair-stemplate=~S\n"),scm_list_2(verbosity_repr(pobj),stemplate));
+ }
+ return((get_p2g_function(SCM_CAR(stemplate)))(pobj,SCM_CDR(stemplate)));
+ }
+ else {
+ scm_misc_error("p2g_apply","bad template CAR item ~S",
+ scm_list_1(SCM_CAR(stemplate)));
+ }
+ }
+ else {
+ scm_misc_error("p2g_apply","bad template item ~S",
+ scm_list_1(stemplate));
+ }
+}
+
+////////////////////////// leaf ////////////////////////////////////////
+
+// Perform 'leaf' data conversion.
+// Normally, stemplate is a list of templates, to be tried one by one
+// until one of them succeeds.
+SCM
+p2g_leaf(PyObject *pobj,SCM stemplate)
+{
+ if (IS_P2G_SMOBP(stemplate)) {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_leaf: pobj=~S smob-stemplate=~S\n"),scm_list_2(verbosity_repr(pobj),stemplate));
+ }
+ return((get_p2g_function(stemplate))(pobj,SCM_UNSPECIFIED));
+ }
+ if (!SCM_EQ_P(SCM_BOOL_F,scm_list_p(stemplate))) {
+ // Each template list item is examined.
+ // If the template list item is itself a list, then its CAR is invoked
+ // and gets its CDR as template, and the whole sobj (which is
+ // expected to be Template or List, too) as the argument.
+ // If the template list item is a P2G_SMOB, then it is invoked with
+ // pobj.
+ //
+ // At any case, template list items are invoked one by one until
+ // one of them succeeds.
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_leaf: pobj=~S list-stemplate=~S\n"),scm_list_2(verbosity_repr(pobj),stemplate));
+ }
+ SCM slist;
+ for (slist = stemplate; (!SCM_EQ_P(slist,SCM_EOL));
+ slist = SCM_CDR(slist)) {
+ SCM scandidate = SCM_CAR(slist);
+ SCM sobj = p2g_apply(pobj,scandidate);
+ if (!SCM_UNBNDP(sobj)) {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_leaf: successful conversion of ~S into SCM\n"),scm_list_1(verbosity_repr(pobj)));
+ }
+ return(sobj);
+ }
+ }
+ return(SCM_UNDEFINED); // None of the templates in the list fit the sobj.
+ // Supports backtracking if the template is so designed.
+ }
+ scm_wrong_type_arg("p2g_leaf",SCM_ARG2,stemplate); // Bad template
+}
+
+////////////////////////// None ////////////////////////////////////////
+
+// This function is somewhat unusual in that its stemplate argument,
+// if specified, does not have to be a P2G_SMOB.
+// This argument is the one which is returned if the Python value
+// is None.
+SCM
+p2g_None2SCM_EOL(PyObject *pobj,SCM stemplate)
+{
+ //return((Py_None != pobj)
+ // ? SCM_UNDEFINED
+ // : (SCM_EQ_P(stemplate,SCM_UNSPECIFIED) || SCM_UNBNDP(stemplate))
+ // ? SCM_EOL
+ // : stemplate);
+ if (Py_None != pobj) {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_None2SCM_EOL: pobj=~S stemplate=~S\n"),scm_list_2(verbosity_repr(pobj),stemplate));
+ }
+ return(SCM_UNDEFINED);
+ }
+ else {
+ SCM stemp = (SCM_EQ_P(stemplate,SCM_UNSPECIFIED) || SCM_UNBNDP(stemplate))
+ ? SCM_EOL : stemplate;
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_None2SCM_EOL: successful conversion of None into ~S\n"),scm_list_1(stemp));
+ }
+ return(stemp);
+ }
+}
+
+///////////////////////// Boolean //////////////////////////////////////
+
+SCM
+p2g_Bool2SCM_BOOL(PyObject *pobj,SCM stemplate)
+{
+ if (PyBool_Check(pobj)) {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_Bool2SCM_BOOL: successful conversion of ~S into SCM\n"),scm_list_1(verbosity_repr(pobj)));
+ }
+ return (PyInt_AsLong(pobj) ? SCM_BOOL_T : SCM_BOOL_F);
+ }
+ else {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_Bool2SCM_BOOL: failed to convert pobj=~S using stemplate=~S\n"),scm_list_2(verbosity_repr(pobj),stemplate));
+ }
+ return(SCM_UNDEFINED);
+ }
+}
+
+///////////////////////// Numeric //////////////////////////////////////
+
+SCM
+p2g_Int2num(PyObject *pobj,SCM stemplate)
+{
+ if (PyInt_Check(pobj)) {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_Int2num: successful conversion of ~S into SCM\n"),scm_list_1(verbosity_repr(pobj)));
+ }
+ return (scm_long2num(PyInt_AsLong(pobj)));
+ }
+ else {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_Int2num: failed to convert pobj=~S using stemplate=~S\n"),scm_list_2(verbosity_repr(pobj),stemplate));
+ }
+ return(SCM_UNDEFINED);
+ }
+}
+
+SCM
+p2g_Long2bignum(PyObject *pobj,SCM stemplate)
+{
+ if (PyLong_Check(pobj)) {
+ PyObject *pstr = PyObject_Repr(pobj);
+ if (NULL == pstr) {
+ PyObject *pexception = PyErr_Occurred(); // NOT COVERED BY TESTS
+ if (pexception) { // NOT COVERED BY TESTS
+ PyErr_Clear(); // NOT COVERED BY TESTS
+ scm_misc_error("p2g_Long2bignum","internal conversion error of bignum", // NOT COVERED BY TESTS
+ SCM_UNDEFINED);
+ }
+ else {
+ scm_misc_error("p2g_Long2bignum","unknown internal conversion error of bignum", // NOT COVERED BY TESTS
+ SCM_UNDEFINED);
+ }
+ }
+ char *cstr = PyString_AsString(pstr);
+ long cstrlen = strlen(cstr);
+ if (cstrlen < 1) {
+ scm_misc_error("p2g_Long2bignum","conversion error of bignum - too short result string", // NOT COVERED BY TESTS
+ SCM_UNDEFINED);
+ }
+ if ('L' == cstr[cstrlen-1]) {
+ --cstrlen;
+ }
+ //return(scm_istring2number(cstr,cstrlen,10)); // scm_istring2number seems to be deprecated in newer versions of Guile.
+ SCM sstr = scm_mem2string(cstr,cstrlen);
+ Py_DECREF(pstr);
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) {
+ // Note: here verbosity_repr is inefficient in that it converts again
+ // a PyObject into a SCM string - but it is not critical for performance
+ // and code clarity is more important.
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_Long2bignum: successful conversion of ~S into SCM\n"),scm_list_1(verbosity_repr(pobj)));
+ }
+ return(scm_string_to_number(sstr,scm_long2num((long)10)));
+ }
+ else {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_Long2bignum: failed to convert pobj=~S using stemplate=~S\n"),scm_list_2(verbosity_repr(pobj),stemplate));
+ }
+ return(SCM_UNDEFINED);
+ }
+}
+
+SCM
+p2g_Float2real(PyObject *pobj,SCM stemplate)
+{
+ if (PyFloat_Check(pobj)) {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_Float2real: successful conversion of ~S into SCM\n"),scm_list_1(verbosity_repr(pobj)));
+ }
+ return (scm_double2num(PyFloat_AsDouble(pobj)));
+ }
+ else {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_Float2real: failed to convert pobj=~S using stemplate=~S\n"),scm_list_2(verbosity_repr(pobj),stemplate));
+ }
+ return(SCM_UNDEFINED);
+ }
+}
+
+SCM
+p2g_Complex2complex(PyObject *pobj,SCM stemplate)
+{
+ if (PyComplex_Check(pobj)) {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_Complex2complex: successful conversion of ~S into SCM\n"),scm_list_1(verbosity_repr(pobj)));
+ }
+ double re = PyComplex_RealAsDouble(pobj);
+ double im = PyComplex_ImagAsDouble(pobj);
+ return(scm_make_complex(re,im));
+ }
+ else {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_Complex2complex: failed to convert pobj=~S using stemplate=~S\n"),scm_list_2(verbosity_repr(pobj),stemplate));
+ }
+ return(SCM_UNDEFINED);
+ }
+}
+
+///////////////////////// Strings //////////////////////////////////////
+
+SCM
+p2g_String2string(PyObject *pobj,SCM stemplate)
+{
+ if (PyString_CheckExact(pobj)) {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_String2string: successful conversion of ~S into SCM\n"),scm_list_1(verbosity_repr(pobj)));
+ }
+ int strlength = PyString_Size(pobj);
+ char *strtext = PyString_AsString(pobj);
+ //scm_makfrom0str - duplicates zero-terminated string
+ //scm_take0str - takes over ownership of the zero-terminated string
+ //scm_mem2string(const char*,len)
+ //scm_take_str(const char*,len)
+ return(scm_mem2string(strtext,strlength));
+ }
+ else {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_String2string: failed to convert pobj=~S using stemplate=~S\n"),scm_list_2(verbosity_repr(pobj),stemplate));
+ }
+ return(SCM_UNDEFINED);
+ }
+}
+
+SCM
+p2g_String2symbol(PyObject *pobj,SCM stemplate)
+{
+ if (PyString_CheckExact(pobj)) {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_String2symbol: successful conversion of ~S into SCM\n"),scm_list_1(verbosity_repr(pobj)));
+ }
+ //int strlength = PyString_Size(pobj);
+ char *strtext = PyString_AsString(pobj);
+ return(scm_str2symbol(strtext));
+ }
+ else {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_String2symbol: failed to convert pobj=~S using stemplate=~S\n"),scm_list_2(verbosity_repr(pobj),stemplate));
+ }
+ return(SCM_UNDEFINED);
+ }
+}
+
+SCM
+p2g_String2keyword(PyObject *pobj,SCM stemplate)
+{
+ if (PyString_CheckExact(pobj)) {
+ int strlength = PyString_Size(pobj);
+ if (strlength < 1) {
+ // Ensure that there is at least one character in the
+ // real string.
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_String2keyword: failed to convert pobj=~S using stemplate=~S - zero-length string\n"),scm_list_2(verbosity_repr(pobj),stemplate));
+ }
+ return(SCM_UNDEFINED);
+ }
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_String2keyword: successful conversion of ~S into SCM\n"),scm_list_1(verbosity_repr(pobj)));
+ }
+ char *strtext = PyString_AsString(pobj);
+ // Prefixing dash to the string.
+ char *dashstr = malloc(strlength+2);
+ dashstr[0] = '-';
+ dashstr[1] = '\0';
+ strncat(dashstr,strtext,strlength);
+ SCM ssymbol = scm_str2symbol(dashstr);
+ return(scm_make_keyword_from_dash_symbol(ssymbol));
+ }
+ else {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_String2keyword: failed to convert pobj=~S using stemplate=~S\n"),scm_list_2(verbosity_repr(pobj),stemplate));
+ }
+ return(SCM_UNDEFINED);
+ }
+}
+
+SCM
+p2g_1String2char(PyObject *pobj,SCM stemplate)
+{
+ if (PyString_CheckExact(pobj)) {
+ int strlength = PyString_Size(pobj);
+ if (1 != strlength) {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_1String2char: failed to convert pobj=~S using stemplate=~S - wrong-length string\n"),scm_list_2(verbosity_repr(pobj),stemplate));
+ }
+ return(SCM_UNDEFINED);
+ }
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_SUCCESSFUL)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_1String2char: successful conversion of ~S into SCM\n"),scm_list_1(verbosity_repr(pobj)));
+ }
+ char *strtext = PyString_AsString(pobj);
+ return(SCM_MAKE_CHAR(strtext[0]));
+ }
+ else {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_1String2char: failed to convert pobj=~S using stemplate=~S\n"),scm_list_2(verbosity_repr(pobj),stemplate));
+ }
+ return(SCM_UNDEFINED);
+ }
+}
+
+/////////////////////// Aggregates /////////////////////////////////////
+
+// Convert a 2-tuple into a pair. Fails if the object is not a
+// 2-tuple. The template must be a pair as well.
+SCM
+p2g_2Tuple2pair(PyObject *pobj,SCM stemplate)
+{
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_2Tuple2pair: trying to convert pobj=~S using stemplate=~S\n"),scm_list_2(verbosity_repr(pobj),stemplate));
+ }
+ if (SCM_EQ_P(SCM_BOOL_F,scm_pair_p(stemplate))) {
+ scm_misc_error("p2g_2Tuple2pair","bad template item ~S",
+ scm_list_1(stemplate));
+ }
+ if (!PyTuple_CheckExact(pobj)) {
+ return(SCM_UNDEFINED);
+ }
+ if (2 != PyTuple_GET_SIZE(pobj)) {
+ return(SCM_UNDEFINED);
+ }
+
+ SCM sobj_car = p2g_apply(PyTuple_GET_ITEM(pobj,0),SCM_CAR(stemplate));
+ if (SCM_UNBNDP(sobj_car)) {
+ return(SCM_UNDEFINED);
+ }
+ SCM sobj_cdr = p2g_apply(PyTuple_GET_ITEM(pobj,1),SCM_CDR(stemplate));
+ if (SCM_UNBNDP(sobj_cdr)) {
+ return(SCM_UNDEFINED);
+ }
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_2Tuple2pair: successful conversion\n"),SCM_EOL);
+ }
+ return(scm_cons(sobj_car,sobj_cdr));
+}
+
+// Very similar to p2g_2Tuple2pair.
+SCM
+p2g_2List2pair(PyObject *pobj,SCM stemplate)
+{
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_2List2pair: trying to convert pobj=~S using stemplate=~S\n"),scm_list_2(verbosity_repr(pobj),stemplate));
+ }
+ if (SCM_EQ_P(SCM_BOOL_F,scm_pair_p(stemplate))) {
+ scm_misc_error("p2g_2List2pair","bad template item ~S",
+ scm_list_1(stemplate));
+ }
+ if (!PyList_CheckExact(pobj)) {
+ return(SCM_UNDEFINED);
+ }
+ if (2 != PyList_GET_SIZE(pobj)) {
+ return(SCM_UNDEFINED);
+ }
+
+ SCM sobj_car = p2g_apply(PyList_GET_ITEM(pobj,0),SCM_CAR(stemplate));
+ if (SCM_UNBNDP(sobj_car)) {
+ return(SCM_UNDEFINED);
+ }
+ SCM sobj_cdr = p2g_apply(PyList_GET_ITEM(pobj,1),SCM_CDR(stemplate));
+ if (SCM_UNBNDP(sobj_cdr)) {
+ return(SCM_UNDEFINED);
+ }
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_2List2pair: successful conversion\n"),SCM_EOL);
+ }
+ return(scm_cons(sobj_car,sobj_cdr));
+}
+
+// Convert a Tuple (of any length) into a list.
+// The template may be either P2G_SMOB (to be used for converting
+// all Tuple items) or a list of templates.
+// The length of the list of templates may be shorter than the length
+// of the Tuple. If shorter, list items will be cyclically reused.
+SCM
+p2g_Tuple2list(PyObject *pobj,SCM stemplate)
+{
+ if (!PyTuple_CheckExact(pobj)) {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_Tuple2list: pobj=~S stemplate=~S\n"),scm_list_2(verbosity_repr(pobj),stemplate));
+ }
+ return(SCM_UNDEFINED);
+ }
+ int size = PyTuple_GET_SIZE(pobj);
+ if (IS_P2G_SMOBP(stemplate)) {
+ // Conversion loop for the case in which the template is
+ // a single P2G_SMOB
+ int ind1;
+ SCM slist1 = SCM_EOL;
+ for (ind1 = size-1; ind1 >= 0; --ind1) {
+ PyObject *pitem1 = PyTuple_GET_ITEM(pobj, ind1);
+ if (NULL == pitem1) {
+ PyObject *pexception = PyErr_Occurred(); // NOT COVERED BY TESTS
+ if (pexception) { // NOT COVERED BY TESTS
+ PyErr_Clear(); // NOT COVERED BY TESTS
+ }
+ scm_misc_error("p2g_Tuple2list","access error of Python Tuple", // NOT COVERED BY TESTS
+ SCM_UNSPECIFIED);
+ }
+ SCM sitem1 = p2g_apply(pitem1,stemplate);
+ if (SCM_UNBNDP(sitem1)) {
+ // Conversion failure
+ return(SCM_UNDEFINED);
+ }
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_Tuple2list: 1. converted item pobj=~S[~S], stemplate=~S\n"),scm_list_3(scm_long2num(ind1),verbosity_repr(pitem1),stemplate));
+ }
+ slist1 = scm_cons(sitem1,slist1);
+ }
+ return(slist1);
+ }
+
+ if (SCM_EQ_P(SCM_BOOL_F,scm_list_p(stemplate))) {
+ // Bad template.
+ scm_wrong_type_arg("p2g_Tuple2list",SCM_ARG2,stemplate);
+ }
+ // Conversion loop for the case in which the template is a list.
+ SCM slist2 = SCM_EOL;
+ SCM stemp = SCM_EOL; // We loop over stemplate again and again as needed.
+ int ind2;
+ for (ind2 = 0; ind2 < size;
+ stemp = SCM_CDR(stemp),++ind2) {
+ if (SCM_EQ_P(stemp,SCM_EOL)) {
+ stemp = stemplate; // Loop back to stemplate's beginning.
+ }
+ //scm_simple_format(scm_current_output_port(),scm_makfrom0str("# DEBUG: going to convert item according to template ~S\n"),scm_list_1(SCM_CAR(stemp)));
+ PyObject *pitem2 = PyTuple_GET_ITEM(pobj, ind2);
+ if (NULL == pitem2) {
+ PyObject *pexception = PyErr_Occurred(); // NOT COVERED BY TESTS
+ if (pexception) { // NOT COVERED BY TESTS
+ PyErr_Clear(); // NOT COVERED BY TESTS
+ }
+ scm_misc_error("p2g_Tuple2list","access error of Python Tuple", // NOT COVERED BY TESTS
+ SCM_UNSPECIFIED);
+ }
+ SCM sitem2 = p2g_apply(pitem2,SCM_CAR(stemp));
+ if (SCM_UNBNDP(sitem2)) {
+ // Conversion failure
+ return(SCM_UNDEFINED);
+ }
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_Tuple2list: 2. converted item pobj=~S[~S], stemplate=~S\n"),scm_list_3(scm_long2num(ind2),verbosity_repr(pitem2),SCM_CAR(stemp)));
+ }
+ slist2 = scm_cons(sitem2,slist2);
+ }
+ return(scm_reverse(slist2));
+}
+
+// Very similar to p2g_Tuple2list above.
+SCM
+p2g_List2list(PyObject *pobj,SCM stemplate)
+{
+ if (!PyList_CheckExact(pobj)) {
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_List2list: pobj=~S stemplate=~S\n"),scm_list_2(verbosity_repr(pobj),stemplate));
+ }
+ return(SCM_UNDEFINED);
+ }
+ int size = PyList_GET_SIZE(pobj);
+ if (IS_P2G_SMOBP(stemplate)) {
+ // Conversion loop for the case in which the template is
+ // a single P2G_SMOB
+ int ind1;
+ SCM slist1 = SCM_EOL;
+ for (ind1 = size-1; ind1 >= 0; --ind1) {
+ PyObject *pitem1 = PyList_GET_ITEM(pobj, ind1);
+ if (NULL == pitem1) {
+ PyObject *pexception = PyErr_Occurred(); // NOT COVERED BY TESTS
+ if (pexception) { // NOT COVERED BY TESTS
+ PyErr_Clear(); // NOT COVERED BY TESTS
+ }
+ scm_misc_error("p2g_List2list","access error of Python List", // NOT COVERED BY TESTS
+ SCM_UNSPECIFIED);
+ }
+ SCM sitem1 = p2g_apply(pitem1,stemplate);
+ if (SCM_UNBNDP(sitem1)) {
+ // Conversion failure
+ return(SCM_UNDEFINED);
+ }
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_List2list: converted item pobj[~S]=~S, stemplate=~S\n"),scm_list_3(scm_long2num(ind1),verbosity_repr(pitem1),stemplate));
+ }
+ slist1 = scm_cons(sitem1,slist1);
+ }
+ return(slist1);
+ }
+
+ if (SCM_EQ_P(SCM_BOOL_F,scm_list_p(stemplate))) {
+ // Bad template.
+ scm_wrong_type_arg("p2g_List2list",SCM_ARG2,stemplate);
+ }
+ // Conversion loop for the case in which the template is a list.
+ SCM slist2 = SCM_EOL;
+ SCM stemp = SCM_EOL; // We loop over stemplate again and again as needed.
+ int ind2;
+ for (ind2 = 0; ind2 < size;
+ stemp = SCM_CDR(stemp),++ind2) {
+ if (SCM_EQ_P(stemp,SCM_EOL)) {
+ stemp = stemplate; // Loop back to stemplate's beginning.
+ }
+ PyObject *pitem2 = PyList_GET_ITEM(pobj, ind2);
+ if (NULL == pitem2) {
+ PyObject *pexception = PyErr_Occurred(); // NOT COVERED BY TESTS
+ if (pexception) { // NOT COVERED BY TESTS
+ PyErr_Clear(); // NOT COVERED BY TESTS
+ }
+ scm_misc_error("p2g_List2list","access error of Python List", // NOT COVERED BY TESTS
+ SCM_UNSPECIFIED);
+ }
+ SCM sitem2 = p2g_apply(pitem2,SCM_CAR(stemp));
+ if (SCM_UNBNDP(sitem2)) {
+ // Conversion failure
+ return(SCM_UNDEFINED);
+ }
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_List2list: 2. converted item pobj=~S[~S], stemplate=~S\n"),scm_list_3(scm_long2num(ind2),verbosity_repr(pitem2),SCM_CAR(stemp)));
+ }
+ slist2 = scm_cons(sitem2,slist2);
+ }
+ return(scm_reverse(slist2));
+}
+
+// Conversion of Dict into alist.
+// All keys are convertible using a single template (SCM_CAR(stemplate)).
+//
+// In the most general case, the keys will be used as keys into
+// SCM_CDR(stemplate) which would be an hash table.
+// However, this most general case is not currently implemented.
+// There is a single template also in SCM_CDR(stemplate), which is
+// used for converting all values. Any flexibility needed is to be
+// obtained through wise p2g_leaf() usage.
+
+SCM
+p2g_Dict2alist(PyObject *pobj,SCM stemplate)
+{
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_Dict2alist: trying to convert pobj=~S using stemplate=~S\n"),scm_list_2(verbosity_repr(pobj),stemplate));
+ }
+
+ if (SCM_EQ_P(SCM_BOOL_F,scm_pair_p(stemplate))) {
+ scm_misc_error("p2g_Dict2alist","bad template ~S",
+ scm_list_1(stemplate));
+ }
+ //if (!IS_P2G_SMOBP(SCM_CAR(stemplate))) {
+ // scm_misc_error("p2g_Dict2alist","bad template CAR item ~S",
+ // scm_list_1(SCM_CAR(stemplate)));
+ //}
+ //if (!IS_P2G_SMOBP(SCM_CDR(stemplate))) {
+ // scm_misc_error("p2g_Dict2alist","bad template CDR item ~S",
+ // scm_list_1(SCM_CDR(stemplate)));
+ //}
+
+ if (!PyDict_CheckExact(pobj)) {
+ return(SCM_UNDEFINED);
+ }
+ int iterstate = 0;
+ PyObject *pkey = NULL;
+ PyObject *pval = NULL;
+ SCM salist = SCM_EOL;
+ while (PyDict_Next(pobj, &iterstate, &pkey, &pval)) {
+ SCM skey = p2g_apply(pkey,SCM_CAR(stemplate));
+ if (SCM_UNBNDP(skey)) {
+ // Conversion failure.
+ return(SCM_UNDEFINED);
+ }
+ SCM sval = p2g_apply(pval,SCM_CDR(stemplate));
+ if (SCM_UNBNDP(sval)) {
+ // Conversion failure.
+ return(SCM_UNDEFINED);
+ }
+ salist = scm_cons(scm_cons(skey,sval),salist);
+ }
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_Dict2alist: successful conversion of Dict into ~S\n"),scm_list_1(salist));
+ }
+ return(salist);
+}
+
+SCM
+p2g_PySCMObject2SCM(PyObject *pobj,SCM stemplate)
+{
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# p2g_PySCMObject2SCM: trying to convert pobj=~S using stemplate=~S\n"),scm_list_2(verbosity_repr(pobj),stemplate));
+ }
+
+ if (!PySCMObject_Check(pobj)) {
+ return(SCM_UNDEFINED);
+ }
+ return(unwrap_pyscm_object(pobj));
+}
+
+////////////////////////////////////////////////////////////////////////
+// Big default conversion function
+////////////////////////////////////////////////////////////////////////
+// The python2guile function chooses reasonable defaults, whenever
+// there is a possibility for ambiguity concerning the desired
+// Scheme datatype.
+// It ignores the stemplate argument.
+
+static SCM python2guile_smob; // for use by default templates
+static SCM python2guile_dict_default; // used by python2guile().
+
+SCM
+python2guile(PyObject *pobj,SCM stemplate)
+{
+ SCM sres = SCM_UNDEFINED;
+ if (pyguile_verbosity_test(PYGUILE_VERBOSE_G2P2G_ALWAYS)) {
+ scm_simple_format(scm_current_output_port(),scm_makfrom0str("# python2guile: trying to convert pobj=~S using stemplate=~S\n"),scm_list_2(verbosity_repr(pobj),stemplate));
+ }
+
+ if (NULL == pobj) {
+ // Regarded as an error.
+ scm_misc_error("python2guile","no python value to be converted",
+ SCM_UNSPECIFIED);
+ //return(SCM_UNDEFINED);
+ }
+
+ sres = p2g_None2SCM_EOL(pobj,SCM_UNSPECIFIED);
+ if (!SCM_UNBNDP(sres)) return(sres); // if (SCM_UNDEFINED != sres) return(sres);
+
+ sres = p2g_Bool2SCM_BOOL(pobj,SCM_UNSPECIFIED);
+ if (!SCM_UNBNDP(sres)) return(sres); // if (SCM_UNDEFINED != sres) return(sres);
+
+ // Numeric
+
+ sres = p2g_Int2num(pobj,SCM_UNSPECIFIED);
+ if (!SCM_UNBNDP(sres)) return(sres); // if (SCM_UNDEFINED != sres) return(sres);
+
+ sres = p2g_Long2bignum(pobj,SCM_UNSPECIFIED);
+ if (!SCM_UNBNDP(sres)) return(sres); // if (SCM_UNDEFINED != sres) return(sres);
+
+ sres = p2g_Float2real(pobj,SCM_UNSPECIFIED);
+ if (!SCM_UNBNDP(sres)) return(sres); // if (SCM_UNDEFINED != sres) return(sres);
+
+ sres = p2g_Complex2complex(pobj,SCM_UNSPECIFIED);
+ if (!SCM_UNBNDP(sres)) return(sres); // if (SCM_UNDEFINED != sres) return(sres);
+
+ // Strings
+
+ sres = p2g_String2string(pobj,SCM_UNSPECIFIED);
+ if (!SCM_UNBNDP(sres)) return(sres); // if (SCM_UNDEFINED != sres) return(sres);
+
+ // Aggregates
+
+ sres = p2g_Tuple2list(pobj,python2guile_smob);
+ if (!SCM_UNBNDP(sres)) return(sres); // if (SCM_UNDEFINED != sres) return(sres);
+
+ sres = p2g_List2list(pobj,python2guile_smob);
+ if (!SCM_UNBNDP(sres)) return(sres); // if (SCM_UNDEFINED != sres) return(sres);
+
+ sres = p2g_Dict2alist(pobj,python2guile_dict_default);
+ if (!SCM_UNBNDP(sres)) return(sres); // if (SCM_UNDEFINED != sres) return(sres);
+
+ // PySCMObjects
+
+ sres = p2g_PySCMObject2SCM(pobj,SCM_UNSPECIFIED);
+ if (!SCM_UNBNDP(sres)) return(sres);
+
+ // !!! Implement here hooks for decoding more data types.
+
+ // If none of the above decoded the data type, then just
+ // wrap the PyObject with pysmob and return the pysmob.
+ return(wrap_pyobject(pobj));
+}
+
+////////////////////////////////////////////////////////////////////////
+// Register all p2g_* functions
+
+#include "pytoguile.inc"
+
+////////////////////////////////////////////////////////////////////////
+// End of pytoguile.c
diff --git a/pytoguile.h b/pytoguile.h
new file mode 100644
index 0000000..a9ff4ec
--- /dev/null
+++ b/pytoguile.h
@@ -0,0 +1,50 @@
+// pytoguile header file
+// Functions for conversion from PyObjects into Guile SCMs.
+////////////////////////////////////////////////////////////////////////
+
+#ifndef PYTOGUILE_H
+#define PYTOGUILE_H
+
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2008 Omer Zak.
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this library, in a file named COPYING; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+// Boston, MA 02111-1307 USA
+//
+// For licensing issues, contact <w1@zak.co.il>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#include <Python.h> // Must be first header file
+#include <libguile.h>
+
+////////////////////////////////////////////////////////////////////////
+// PyObject -> SCM
+////////////////////////////////////////////////////////////////////////
+
+// Basic conversion of a PyObject into a SCM object according
+// to template.
+extern SCM p2g_apply(PyObject *pobj,SCM stemplate);
+
+// Convert a Python object into a SCM object.
+// If cannot convert, abort.
+extern SCM python2guile(PyObject *pobj,SCM stemplate);
+
+////////////////////////////////////////////////////////////////////////
+
+#endif /* PYTOGUILE_H */
+
+////////////////////////////////////////////////////////////////////////
+// End of pytoguile.h
diff --git a/t/01_basic.t b/t/01_basic.t
new file mode 100755
index 0000000..acce403
--- /dev/null
+++ b/t/01_basic.t
@@ -0,0 +1,34 @@
+#!/usr/bin/guile -s
+!#
+; Basic tests of the guiletap module (for TAP based test scripting).
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Copyright (C) 2008 Omer Zak.
+; This library is free software; you can redistribute it and/or
+; modify it under the terms of the GNU Lesser General Public
+; License as published by the Free Software Foundation; either
+; version 2.1 of the License, or (at your option) any later version.
+;
+; This library is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; Lesser General Public License for more details.
+;
+; You should have received a copy of the GNU Lesser General Public License
+; along with this library, in a file named COPYING; if not, write to the
+; Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+; Boston, MA 02111-1307 USA
+;
+; For licensing issues, contact <w1@zak.co.il>.
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+(use-modules (guiletap))
+
+(plan 4)
+(ok 1 "Should pass" #t)
+(ok 2 "Should fail # TODO deliberate failure" #f)
+(is-ok 3 "Should be okay" '(1 . "abc") (cons 1 (string-append "a" "bc")))
+(is-ok 4 "Should not be okay # TODO deliberate failure" '(3 . "def") '(3 "def"))
+
+; End of 01_basic.t
diff --git a/t/02_pyguile.t b/t/02_pyguile.t
new file mode 100644
index 0000000..5eea12f
--- /dev/null
+++ b/t/02_pyguile.t
@@ -0,0 +1,49 @@
+#!/usr/bin/guile -s
+!#
+; Pyguile tests - transferring data from Python to Guile
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Copyright (C) 2008 Omer Zak.
+; This library is free software; you can redistribute it and/or
+; modify it under the terms of the GNU Lesser General Public
+; License as published by the Free Software Foundation; either
+; version 2.1 of the License, or (at your option) any later version.
+;
+; This library is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; Lesser General Public License for more details.
+;
+; You should have received a copy of the GNU Lesser General Public License
+; along with this library, in a file named COPYING; if not, write to the
+; Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+; Boston, MA 02111-1307 USA
+;
+; For licensing issues, contact <w1@zak.co.il>.
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+(use-modules (guiletap))
+(use-modules (pyguile))
+
+(plan 10)
+(python-eval "import sys;sys.path = ['']+sys.path\n")
+(python-eval "from t.scripts.t2conv import *\n")
+
+;(if (python-import "t.scripts.t2conv") (ok 1 "Imported t2conv" #t)
+; (bail-out "Could not import t2conv"))
+
+(is-ok 1 "None" 0 (length (python-eval "return_None()" #t)))
+(is-ok 2 "True" #t (python-eval "return_True()" #t))
+(is-ok 3 "False" #f (python-eval "return_False()" #t))
+(is-ok 4 "1" 1 (python-eval "return_Int1()" #t))
+(is-ok 5 "-5" -5 (python-eval "return_Int_5()" #t))
+(is-ok 6 "2^65" 36893488147419103232 (python-eval "return_BigInt()" #t))
+(is-ok 7 "-2^65" -36893488147419103232 (python-eval "return_BigInt_neg()" #t))
+(is-ok 8 "string 1" "abcdefghi" (python-eval "return_String1()" #t))
+(is-ok 9 "string 2" "01abCD%^" (python-eval "return_String2()" #t))
+(is-ok 10 "string 3"
+ (string-append "bef" (list->string (list (integer->char 0) (integer->char 163))) "ore")
+ (python-eval "return_String_Zero()" #t))
+
+; End of 02_pyguile.t
diff --git a/t/03_guile2python.t b/t/03_guile2python.t
new file mode 100644
index 0000000..9ef7964
--- /dev/null
+++ b/t/03_guile2python.t
@@ -0,0 +1,58 @@
+#!/usr/bin/guile -s
+!#
+; Pyguile tests - transferring data from Guile to Python.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Copyright (C) 2008 Omer Zak.
+; This library is free software; you can redistribute it and/or
+; modify it under the terms of the GNU Lesser General Public
+; License as published by the Free Software Foundation; either
+; version 2.1 of the License, or (at your option) any later version.
+;
+; This library is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; Lesser General Public License for more details.
+;
+; You should have received a copy of the GNU Lesser General Public License
+; along with this library, in a file named COPYING; if not, write to the
+; Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+; Boston, MA 02111-1307 USA
+;
+; For licensing issues, contact <w1@zak.co.il>.
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+(use-modules (guiletap))
+(use-modules (pyguile))
+
+(plan 14)
+
+(define invoke-python-func
+ (lambda (module func arg)
+ (python-apply (list module func) (list arg) '())))
+
+(is-ok 1 "None" "None" (invoke-python-func "__builtin__" "repr" (list)))
+(is-ok 2 "True" "True" (invoke-python-func "__builtin__" "repr" #t))
+(is-ok 3 "False" "False" (invoke-python-func "__builtin__" "repr" #f))
+(is-ok 4 "int 1" "1" (invoke-python-func "__builtin__" "repr" 1))
+(is-ok 5 "int -5" "-5" (invoke-python-func "__builtin__" "repr" -5))
+(is-ok 6 "string 1" "'string 1'"
+ (invoke-python-func "__builtin__" "repr" "string 1"))
+(is-ok 7 "char P" "'P'" (invoke-python-func "__builtin__" "repr" #\P))
+(is-ok 8 "symbol symba" "'symba'"
+ (invoke-python-func "__builtin__" "repr" 'symba))
+(is-ok 9 "complex 1-1i" "(1-1j)" (invoke-python-func "__builtin__" "repr" 1-1i))
+(is-ok 10 "float 3.125" "3.125" (invoke-python-func "__builtin__" "repr" 3.125))
+(define big10to7th 10000000)
+(define big10to35th (* big10to7th big10to7th big10to7th big10to7th big10to7th))
+(is-ok 11 "bignum 10^35" "100000000000000000000000000000000000L"
+ (invoke-python-func "__builtin__" "repr" big10to35th))
+(is-ok 12 "pair" "('mycar', 42)"
+ (invoke-python-func "__builtin__" "repr" (cons "mycar" (* 21 2))))
+(is-ok 13 "list 1" "['item1', 'item2', 'item3']"
+ (invoke-python-func "__builtin__" "repr" '(item1 item2 "item3")))
+(is-ok 14 "list 2" "['lambda', ['arg1', 'arg2'], ['display', 'textA'], ['newline'], ['display', 'arg1'], ['newline'], ['display', 'arg2']]"
+ (invoke-python-func "__builtin__" "repr" '(lambda (arg1 arg2)(display "textA")(newline)(display arg1)(newline)(display arg2))))
+
+; End of 03_guile2python.t
diff --git a/t/04_python_apply.t b/t/04_python_apply.t
new file mode 100644
index 0000000..a22fd3a
--- /dev/null
+++ b/t/04_python_apply.t
@@ -0,0 +1,66 @@
+#!/usr/bin/guile -s
+!#
+; Pyguile tests - exercise python-apply
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Copyright (C) 2008 Omer Zak.
+; This library is free software; you can redistribute it and/or
+; modify it under the terms of the GNU Lesser General Public
+; License as published by the Free Software Foundation; either
+; version 2.1 of the License, or (at your option) any later version.
+;
+; This library is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; Lesser General Public License for more details.
+;
+; You should have received a copy of the GNU Lesser General Public License
+; along with this library, in a file named COPYING; if not, write to the
+; Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+; Boston, MA 02111-1307 USA
+;
+; For licensing issues, contact <w1@zak.co.il>.
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+(use-modules (guiletap))
+(use-modules (pyguile))
+
+(plan 10)
+(python-import "os.path")
+
+(is-ok 1 "exists" #t
+ (python-apply '("os.path" exists) '("t/04_python_apply.t") '()))
+(is-ok 2 "does not exist" #f
+ (python-apply '("os.path" exists) '("t/04_python_apply.tnone") '()))
+(is-ok 3 "join 3 arguments" "Pyguile/t/04_python_apply.t"
+ (python-apply '("os.path" "join") '("Pyguile" "t" "04_python_apply.t") '()))
+
+(python-eval "import sys;sys.path = ['']+sys.path\n")
+(python-import "t.scripts.t4apply")
+
+(is-ok 4 "object path without arguments" "33y"
+ (python-apply '("t.scripts.t4apply" mainobj "cl2func") '() '()))
+
+(is-ok 5 "object path with positional argument as symbol" "33pos"
+ (python-apply '("t.scripts.t4apply" mainobj "cl2func") '(pos) '()))
+
+(is-ok 6 "object path with positional argument as string" "33:str"
+ (python-apply '("t.scripts.t4apply" mainobj "cl2func") '(":str") '()))
+
+(is-ok 7 "object path with kw+argument as symbols" "33symkw"
+ (python-apply '("t.scripts.t4apply" mainobj "cl2func") '() '((#:argx . symkw ))))
+
+(is-ok 8 "object path with kw+argument as strings" "33=strkw"
+ (python-apply '("t.scripts.t4apply" mainobj "cl2func") '() '((#:argx . "=strkw" ))))
+
+(is-ok 9 "object path with kw symbol, argument as string" "33<><>"
+ (python-apply '("t.scripts.t4apply" mainobj "cl2func") '() '((#:argx . "<><>" ))))
+
+(is-ok 10 "Return arguments" "positional: (True, 1, -3, 'mystr', 'symbolic') keywords: {'kw4n': 65537, 'keyword1': 'symb1', 'kw3': 'trying3', 'KW_stri2': 'symb2'}"
+ (python-apply '("t.scripts.t4apply" return_args)
+ '(#t 1 -3 "mystr" symbolic)
+ '((#:keyword1 . symb1) (#:KW_stri2 . symb2)
+ (#:kw3 . #:trying3 ) (#:kw4n . 65537))))
+
+; End of 04_guile2python.t
diff --git a/t/05_pysmobs.t b/t/05_pysmobs.t
new file mode 100644
index 0000000..4ed2cca
--- /dev/null
+++ b/t/05_pysmobs.t
@@ -0,0 +1,128 @@
+#!/usr/bin/guile -s
+!#
+; PyGuile tests - exercise pysmob handling.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Copyright (C) 2008 Omer Zak.
+; This library is free software; you can redistribute it and/or
+; modify it under the terms of the GNU Lesser General Public
+; License as published by the Free Software Foundation; either
+; version 2.1 of the License, or (at your option) any later version.
+;
+; This library is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; Lesser General Public License for more details.
+;
+; You should have received a copy of the GNU Lesser General Public License
+; along with this library, in a file named COPYING; if not, write to the
+; Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+; Boston, MA 02111-1307 USA
+;
+; For licensing issues, contact <w1@zak.co.il>.
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+(use-modules (guiletap))
+(use-modules (pyguile))
+
+(plan 12)
+(python-eval "import sys;sys.path = ['']+sys.path\n")
+(define t5smobs (python-import "t.scripts.t5smobs"))
+
+(is-ok 1 "Creation and repr" "opaq('abc')"
+ (python-apply '("__builtin__" "repr")
+ (list (python-apply '("t.scripts.t5smobs" "opaq")
+ '("abc") '()))
+ '()))
+
+(is-ok 2 "Creation and object->string" "(python-eval opaq('abcd') #t)"
+ (object->string
+ (python-apply '("t.scripts.t5smobs" "opaq")
+ '("abcd")
+ '())))
+
+(define objt (python-apply '("t.scripts.t5smobs" "opaq")
+ '(37)
+ '()))
+(is-ok 3 "Created opaq with numeric value" "(python-eval opaq(37) #t)"
+ (object->string objt))
+
+(python-apply (list objt "transform") '() '())
+(is-ok 4 "After transforming opaq with numeric value"
+ "(python-eval opaq(74) #t)"
+ (object->string objt))
+
+(define objl (python-apply '("t.scripts.t5smobs" "opaq")
+ '(("el1" "el2"))
+ '()))
+(is-ok 5 "opaq with list value"
+ "(python-eval opaq(['el1', 'el2']) #t)"
+ (object->string objl))
+
+(python-apply (list objl "transform") '() '())
+(is-ok 6 "opaq with list value"
+ "(python-eval opaq(['el1', 'el2', 'el1', 'el2']) #t)"
+ (object->string objl))
+
+(define equalities3?
+ (lambda (obj1 obj2)
+ (list (eq? obj1 obj2) (eqv? obj1 obj2) (equal? obj1 obj2))))
+
+(is-ok 7 "verify equalities3?"
+ '(#f #f #t)
+ (equalities3? '("a" "b") '("a" "b")))
+
+(define nd1 (python-apply (list t5smobs 'noisydelete) '("nd1a") '()))
+(define nd2 (python-apply (list t5smobs 'noisydelete) '("nd2b") '()))
+(define nd1same nd1)
+(define nd1equal (python-apply (list t5smobs 'noisydelete) '("nd1a") '()))
+
+(is-ok 8 "two different pysmobs"
+ '(#f #f #f)
+ (equalities3? nd1 nd2))
+
+(is-ok 9 "two identical pysmobs"
+ '(#t #t #t)
+ (equalities3? nd1 nd1same))
+
+(is-ok 10 "two equal pysmobs"
+ '(#f #f #t)
+ (equalities3? nd1 nd1equal))
+
+(define nd-me (python-apply (list t5smobs 'noisydelete) '("me") '()))
+(define nd-41 (python-apply (list t5smobs 'noisydelete) '(41) '()))
+(define nd-42 (python-apply (list t5smobs 'noisydelete) '(42) '()))
+
+(is-ok 11 "'me'!=41 to validate t5smobs.noisydelete.__cmp__ test"
+ '(#f #f #f)
+ (equalities3? nd-me nd-41))
+
+(is-ok 12 "'me'==42 to prove t5smobs.noisydelete.__cmp__ is being executed"
+ '(#f #f #t)
+ (equalities3? nd-me nd-42))
+
+
+; The following tests do not work as expected and I do not have yet
+; a way to capture outputs.
+; !!! Check strports.h (object->string, scm_object_to_string).
+
+; Garbage collection behavior of pysmobs.
+(define noisydel (python-apply (list t5smobs 'noisydelete) '("BOO!") '()))
+(diagprint 1001 "verify noisydel object"
+ "'BOO!'"
+ (python-apply '("__builtin__" repr) (list noisydel) '()))
+(display "# Forcing garbage collection...")
+(gc)
+(display "Done")(newline)
+(diagprint 1002 "verify noisydel object after gc"
+ "'BOO!'"
+ (python-apply '("__builtin__" repr) (list noisydel) '()))
+(set! noisydel "losing reference")
+(display "# Forcing another garbage collection...")
+(gc)
+(display "Done - should show deletion of noisydel")
+(newline)
+(display "# ")(display noisydel)(newline)
+(python-eval "import gc\ngc.collect()\n")
+; End of 05_pysmobs.t
diff --git a/t/06_guile2python.t b/t/06_guile2python.t
new file mode 100644
index 0000000..346d478
--- /dev/null
+++ b/t/06_guile2python.t
@@ -0,0 +1,143 @@
+#!/usr/bin/guile -s
+!#
+; Additional Pyguile tests - transferring data from Guile to Python.
+; Tests added to complete code coverage.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Copyright (C) 2008 Omer Zak.
+; This library is free software; you can redistribute it and/or
+; modify it under the terms of the GNU Lesser General Public
+; License as published by the Free Software Foundation; either
+; version 2.1 of the License, or (at your option) any later version.
+;
+; This library is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; Lesser General Public License for more details.
+;
+; You should have received a copy of the GNU Lesser General Public License
+; along with this library, in a file named COPYING; if not, write to the
+; Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+; Boston, MA 02111-1307 USA
+;
+; For licensing issues, contact <w1@zak.co.il>.
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+(use-modules (guiletap))
+(use-modules (pyguile))
+
+(plan 13)
+
+(is-ok 1 "Float 1.5" "1.5"
+ (python-apply '("__builtin__" repr) '(1.5) '()))
+(is-ok 2 "Float 1000000000.5" "1000000000.5"
+ (python-apply '("__builtin__" repr) '(1000000000.5) '()))
+(is-ok 3 "Float 1e+20" "1e+20"
+ (python-apply '("__builtin__" repr) '(100000000000000000000.5) '()))
+
+(is-ok 4 "Complex" "[(1+1j), (2-3j), (-3-4j), (-4+5j)]"
+ (python-apply '("__builtin__" repr) '((1+1i 2-3i -3-4i -4+5i)) '()))
+
+;(if (equal? (effective-version) "1.6")
+; (is-ok 5 "Dash-only keyword" "''"
+; (python-apply '("__builtin__" repr) '(#:) '()))
+; (ok 5 "Dash-only keyword not supported in Guile 1.8" #t))
+(ok 5 "Dash-only keyword not supported in Guile 1.8" #t)
+
+;(if (equal? (effective-version) "1.6")
+; (is-ok 6 "String, symbol and keywords (version 1.6.x)"
+; "['abc', 'def', 'ghi', 'gh', 'g', '']"
+; (python-apply '("__builtin__" repr)
+; '(("abc" def #:ghi #:gh #:g #:))
+; '()))
+; (is-ok 6 "String, symbol and keywords (version 1.8.x)"
+; "['abc', 'def', 'ghi', 'gh', 'g']"
+; (python-apply '("__builtin__" repr)
+; '(("abc" def #:ghi #:gh #:g))
+; '())))
+(is-ok 6 "String, symbol and keywords (version 1.8 compatible)"
+ "['abc', 'def', 'ghi', 'gh', 'g']"
+ (python-apply '("__builtin__" repr)
+ '(("abc" def #:ghi #:gh #:g))
+ '()))
+
+; Illegal positional keyword arguments handling
+
+(is-ok 7 "Positional argument list is not legal list"
+ '(misc-error ("python-apply" "positional arguments conversion failure (~S)" (42) #f))
+ (catch #t
+ (lambda () (python-apply '("__builtin__" repr)
+ 42
+ '()))
+ (lambda (key . args) (list key args))))
+
+
+; Illegal keyword arguments handling
+
+(python-eval "import sys;sys.path = ['']+sys.path\n")
+(python-import "t.scripts.t4apply")
+
+(is-ok 8 "good alist" "positional: ('a',) keywords: {'arg1': 'b'}"
+ (catch #t
+ (lambda () (python-apply '("t.scripts.t4apply" return_args)
+ '("a")
+ '((#:arg1 . "b"))))
+ (lambda (key . args) (list key args))))
+
+(is-ok 9 "bad alist - not list"
+ '(wrong-type-arg ("guileassoc2pythondict"
+ "Wrong type argument in position ~A: ~S"
+ (1 "b")
+ #f))
+ (catch #t
+ (lambda () (python-apply '("t.scripts.t4apply" return_args)
+ '("a")
+ '("b")))
+ (lambda (key . args) (list key args))))
+
+(is-ok 10 "bad alist - item not pair"
+ '(wrong-type-arg ("guileassoc2pythondict"
+ "Wrong type argument in position ~A: ~S"
+ (2 "c")
+ #f))
+ (catch #t
+ (lambda () (python-apply '("t.scripts.t4apply" return_args)
+ '("a")
+ '((#:b . 3) "c" (#:d . 5))))
+ (lambda (key . args) (list key args))))
+
+(is-ok 11 "bad alist - key not string"
+ '(wrong-type-arg ("guileassoc2pythondict"
+ "Wrong type argument in position ~A: ~S"
+ (2 4)
+ #f))
+ (catch #t
+ (lambda () (python-apply '("t.scripts.t4apply" return_args)
+ '("a")
+ '((#:b . 3) (4 . "c") ("d" . 5))))
+ (lambda (key . args) (list key args))))
+
+
+(is-ok 12 "bad alist - duplicate key"
+ '(misc-error ("guileassoc2pythondict"
+ "duplicate key (~S)"
+ (#:b)
+ #f))
+ (catch #t
+ (lambda () (python-apply '("t.scripts.t4apply" return_args)
+ '("a")
+ '((#:b . 3) (#:c . 4) (#:b . 5))))
+ (lambda (key . args) (list key args))))
+
+(like 13 "bad alist - inconvertible data"
+ "^\\(wrong-type-arg \\(\"guile2python\" \"Wrong type argument in position ~A: ~S\" \\(1 #<input: [^>]*>\\) #f\\)\\)$"
+ (catch #t
+ (lambda () (python-apply '("t.scripts.t4apply" return_args)
+ '("a")
+ `((#:b . 3)
+ (#:c . 4)
+ (#:d . ,(current-input-port)))))
+ (lambda (key . args) (object->string (list key args)))))
+
+; End of 06_guile2python.t
diff --git a/t/07_apply.t b/t/07_apply.t
new file mode 100644
index 0000000..368bc3f
--- /dev/null
+++ b/t/07_apply.t
@@ -0,0 +1,129 @@
+#!/usr/bin/guile -s
+!#
+; Additional Pyguile tests - python-apply tests.
+; Tests added to complete code coverage.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Copyright (C) 2008 Omer Zak.
+; This library is free software; you can redistribute it and/or
+; modify it under the terms of the GNU Lesser General Public
+; License as published by the Free Software Foundation; either
+; version 2.1 of the License, or (at your option) any later version.
+;
+; This library is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; Lesser General Public License for more details.
+;
+; You should have received a copy of the GNU Lesser General Public License
+; along with this library, in a file named COPYING; if not, write to the
+; Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+; Boston, MA 02111-1307 USA
+;
+; For licensing issues, contact <w1@zak.co.il>.
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+(use-modules (guiletap))
+(use-modules (pyguile))
+
+(plan 18)
+
+(python-eval "import sys;sys.path = ['']+sys.path\n")
+(python-eval "from t.scripts.t4apply import return_args")
+(python-eval "def myfunc():\n return('this was myfunc')\n")
+
+(define myfunc-smob (python-eval "myfunc" #t))
+
+(is-ok 1 "string funcname" "positional: ('xyzzy',) keywords: {}"
+ (python-apply "return_args" '(xyzzy) '()))
+
+(is-ok 2 "symbol funcname" "positional: () keywords: {'arg': 7}"
+ (python-apply #:return_args '() '((#:arg . 7))))
+
+(is-ok 3 "pysmob func" "this was myfunc"
+ (python-apply myfunc-smob '() '()))
+
+(is-ok 4 "list consisting of pysmob func" "this was myfunc"
+ (python-apply (list myfunc-smob) '() '()))
+
+
+; python-apply funcname tests
+
+(is-ok 5 "funcname not in list and is illegal"
+ '(wrong-type-arg ("python-apply" "Wrong type argument in position ~A: ~S" (1 42) #f))
+ (catch #t
+ (lambda () (python-apply 42 '("a") '((#:arg1 . "b"))))
+ (lambda (key . args) (list key args))))
+
+(is-ok 6 "funcname in list and is illegal"
+ ;like 6: "^\\(misc-error \\(\"python-apply\" \"Python exception: ~A\" \\(\"<class exceptions.TypeError at 0x[0-9a-f]{8}>\"\\) #f\\)\\)$"
+ "(misc-error (\"python-apply\" \"function denoted by ~S is not callable\" ((45)) #f))"
+ (catch #t
+ (lambda () (python-apply '(45) '("a") '((#:arg1 . "b"))))
+ (lambda (key . args) (object->string (list key args)))))
+
+(is-ok 7 "non-imported module"
+ "(misc-error (\"python-apply\" \"could not dereference ~Ath level attribute in ~S\" (1 (\"math\" sin)) #f))"
+ (catch #t
+ (lambda () (python-apply '("math" sin) '(3.14159) '()))
+ (lambda (key . args) (object->string (list key args)))))
+
+(define t2conv (python-import "t.scripts.t2conv"))
+
+; Run python-apply under catch harness.
+(define catch-test
+ (lambda (func posargs kwargs)
+ (catch #t
+ (lambda () (python-apply func posargs kwargs))
+ (lambda (key . args) (object->string (list key args))))))
+
+(is-ok 8 "finding attribute in module by string"
+ -5
+ (catch-test '("t.scripts.t2conv" "return_Int_5") '() '()))
+
+(is-ok 9 "nonexistent attribute in module by string"
+ "(misc-error (\"python-apply\" \"could not dereference ~Ath level attribute in ~S\" (1 (\"t.scripts.t2conv\" \"return_jnt_5\")) #f))"
+ (catch-test '("t.scripts.t2conv" "return_jnt_5") '() '()))
+
+(is-ok 10 "finding attribute in module by symbol"
+ -5
+ (catch-test '("t.scripts.t2conv" return_Int_5) '() '()))
+
+(is-ok 11 "nonexistent attribute in module by symbol"
+ "(misc-error (\"python-apply\" \"could not dereference ~Ath level attribute in ~S\" (1 (\"t.scripts.t2conv\" return_jnt_5)) #f))"
+ (catch-test '("t.scripts.t2conv" return_jnt_5) '() '()))
+
+(is-ok 12 "finding attribute in module by pysmob"
+ -5
+ (catch-test (list t2conv "return_Int_5") '() '()))
+
+(is-ok 13 "nonexistent attribute in module by pysmob"
+ "(misc-error (\"python-apply\" \"could not dereference ~Ath level attribute in ~S\" (1 ((python-eval <module 't.scripts.t2conv' from 't/scripts/t2conv.pyc'> #t) \"return_jnt_5\")) #f))"
+ (catch-test (list t2conv "return_jnt_5") '() '()))
+
+; Python function raises uncaught exception during its work.
+
+(define t7except (python-import "t.scripts.t7except"))
+
+(like 14 "exception inside Python code"
+ "^\\(misc-error \\(\"python-apply\" \"Python exception: ~A\" \\(\"<class t.scripts.t7except.myexception at 0x[0-9a-f]{8}>\"\\) #f\\)\\)$"
+ (catch-test (list t7except 'raiser) '(script7) '()))
+
+(is-ok 15 "kw argument is datum rather than list"
+ "(wrong-type-arg (\"guileassoc2pythondict\" \"Wrong type argument in position ~A: ~S\" (1 \"shut up\") #f))"
+ (catch-test '("__builtin__" repr) '((3 4 5)) "shut up"))
+
+(is-ok 16 "kw argument is pair rather than list"
+ "(wrong-type-arg (\"guileassoc2pythondict\" \"Wrong type argument in position ~A: ~S\" (1 (\"shut\" . \"up\")) #f))"
+ (catch-test '("__builtin__" repr) '((3 4 5)) '("shut" . "up")))
+
+(is-ok 17 "no proc specified"
+ "(wrong-type-arg (\"python-apply\" \"Wrong type argument in position ~A: ~S\" (1 ()) #f))"
+ (catch-test '() '() '()))
+
+(is-ok 18 "nonexistent module"
+ "(misc-error (\"python-apply\" \"could not dereference ~Ath level attribute in ~S\" (1 (\"no.such.module\" repr)) #f))"
+ (catch-test '("no.such.module" repr) '((3 4 5)) '("shut" . "up")))
+
+; End of 07_apply.t
diff --git a/t/08_eval.t b/t/08_eval.t
new file mode 100644
index 0000000..96d1dcb
--- /dev/null
+++ b/t/08_eval.t
@@ -0,0 +1,69 @@
+#!/usr/bin/guile -s
+!#
+; python-eval tests.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Copyright (C) 2008 Omer Zak.
+; This library is free software; you can redistribute it and/or
+; modify it under the terms of the GNU Lesser General Public
+; License as published by the Free Software Foundation; either
+; version 2.1 of the License, or (at your option) any later version.
+;
+; This library is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; Lesser General Public License for more details.
+;
+; You should have received a copy of the GNU Lesser General Public License
+; along with this library, in a file named COPYING; if not, write to the
+; Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+; Boston, MA 02111-1307 USA
+;
+; For licensing issues, contact <w1@zak.co.il>.
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+(use-modules (guiletap))
+(use-modules (pyguile))
+
+(plan 7)
+
+(is-ok 1 "regular python-eval"
+ 42
+ (python-eval "7*3*2" #t))
+
+
+; Run python-eval under catch harness.
+(define catch-test-eval
+ (lambda (txt retval)
+ (catch #t
+ (lambda () (python-eval txt retval))
+ (lambda (key . args) (object->string (list key args))))))
+
+(is-ok 2 "trying to run python-eval on non-string"
+ "(wrong-type-arg (\"python-eval\" \"Wrong type argument in position ~A: ~S\" (1 -42) #f))"
+ (catch-test-eval -42 #t))
+
+(is-ok 3 "trying to run python-eval with non-boolean/non-P2G_SMOB argument"
+ "(misc-error (\"p2g_apply\" \"bad template item ~S\" (42) #f))"
+ (catch-test-eval "7*3*2" 42))
+
+(like 4 "Raising exception inside python-eval f"
+ "\\(misc-error \\(\"python-eval\" \"Python exception: ~A\" \\(\"<class exceptions.ImportError at 0x[0-9a-f]{8}>\"\\) #f\\)\\)"
+ (catch-test-eval "import t7except\nraiser('xyzzy')\n" #f))
+
+(python-eval "import sys;sys.path = ['']+sys.path\n")
+(python-eval "from t.scripts.t7except import raiser")
+(like 5 "Raising exception inside python-eval t"
+ "^\\(misc-error \\(\"python-eval\" \"Python exception: ~A\" \\(\"<class t.scripts.t7except.myexception at 0x[0-9a-f]{8}>\"\\) #f\\)\\)$"
+ (catch-test-eval "1+raiser('foo fee dom')" #t))
+
+(like 6 "code does not return requested result"
+ "^\\(misc-error \\(\"python-eval\" \"Python exception: ~A\" \\(\"<class exceptions.SyntaxError at 0x[0-9a-f]{8}>\"\\) #f\\)\\)$"
+ (catch-test-eval "print '# no value was returned'\n" #t))
+
+(is-ok 7 "code returns unsolicited result"
+ "#<unspecified>"
+ (object->string (python-eval "99+101" #f)))
+
+; End of 08_eval.t
diff --git a/t/09_import.t b/t/09_import.t
new file mode 100644
index 0000000..4d2c406
--- /dev/null
+++ b/t/09_import.t
@@ -0,0 +1,59 @@
+#!/usr/bin/guile -s
+!#
+; python-import tests.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Copyright (C) 2008 Omer Zak.
+; This library is free software; you can redistribute it and/or
+; modify it under the terms of the GNU Lesser General Public
+; License as published by the Free Software Foundation; either
+; version 2.1 of the License, or (at your option) any later version.
+;
+; This library is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; Lesser General Public License for more details.
+;
+; You should have received a copy of the GNU Lesser General Public License
+; along with this library, in a file named COPYING; if not, write to the
+; Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+; Boston, MA 02111-1307 USA
+;
+; For licensing issues, contact <w1@zak.co.il>.
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+(use-modules (guiletap))
+(use-modules (pyguile))
+
+(plan 5)
+
+; Run python-import under catch harness.
+(define catch-test-import
+ (lambda (arg)
+ (catch #t
+ (lambda () (python-import arg))
+ (lambda (key . args) (object->string (list key args))))))
+
+(like 1 "good python-import"
+ "^\\(python-eval <module 'math' from '[^']*'> #t\\)$"
+ (object->string (catch-test-import "math")))
+
+(like 2 "nonexistent module"
+ "^\\(misc-error \\(\"python-import\" \"Python exception during module ~A import: ~A\" \\(\"mathalternate\" \"<class exceptions.ImportError at 0x[0-9a-f]{8}>\"\\) #f\\)\\)$"
+ (catch-test-import "mathalternate"))
+
+(is-ok 3 "bad argument datatype"
+ "(wrong-type-arg (\"python-import\" \"Wrong type argument in position ~A: ~S\" (1 2.7818) #f))"
+ (catch-test-import 2.7818))
+
+(is-ok 4 "python-import with symbol"
+ "(wrong-type-arg (\"python-import\" \"Wrong type argument in position ~A: ~S\" (1 os.path) #f))"
+ (catch-test-import 'os.path))
+
+(is-ok 5 "python-import with keyword"
+ "(wrong-type-arg (\"python-import\" \"Wrong type argument in position ~A: ~S\" (1 #:re) #f))"
+ (catch-test-import #:re))
+
+
+; End of 09_import.t
diff --git a/t/10_python2guile.t b/t/10_python2guile.t
new file mode 100644
index 0000000..9c57829
--- /dev/null
+++ b/t/10_python2guile.t
@@ -0,0 +1,56 @@
+#!/usr/bin/guile -s
+!#
+; Additional PyGuile tests - transferring data from Python to Guile.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Copyright (C) 2008 Omer Zak.
+; This library is free software; you can redistribute it and/or
+; modify it under the terms of the GNU Lesser General Public
+; License as published by the Free Software Foundation; either
+; version 2.1 of the License, or (at your option) any later version.
+;
+; This library is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; Lesser General Public License for more details.
+;
+; You should have received a copy of the GNU Lesser General Public License
+; along with this library, in a file named COPYING; if not, write to the
+; Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+; Boston, MA 02111-1307 USA
+;
+; For licensing issues, contact <w1@zak.co.il>.
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+(use-modules (guiletap))
+(use-modules (pyguile))
+
+(plan 8)
+
+(is-ok 1 "Float 1.5" 1.5
+ (python-eval "1.5" #t))
+
+(is-ok 2 "Float 10000000.5" 10000000.5
+ (python-eval "10000000 + 0.5" #t))
+
+(is-ok 3 "Float 1e+20" 1e20
+ (python-eval "1e11*1e9" #t))
+
+(is-ok 4 "Complex" '(1.0+1.0i 2.0-3.0i -3.0-4.0i -4.0+5.0i)
+ (python-eval "[1+1j,2-3j,-3-4j,-4+5j]" #t))
+
+(is-ok 5 "bigint" 123456789101112131415
+ (python-eval "15 + 100*1234567891011121314" #t))
+
+(is-ok 6 "list" '("ab" "cd")
+ (python-eval "['ab','cd']" #t))
+
+(is-ok 7 "tuple" '("ab" "cd")
+ (python-eval "('ab','cd')" #t))
+
+; !!! Need to sort to ensure consistent test results.
+(is-ok 8 "dict" '((6 . (78 90)) (3 . "4.5") (1 . 2.2))
+ (python-eval "{1 : 2.2, 3 : '4.5', 6 : (78,90)}" #t))
+
+; End of 10_python2guile.t
diff --git a/t/11_g2p2g.t b/t/11_g2p2g.t
new file mode 100644
index 0000000..de7721f
--- /dev/null
+++ b/t/11_g2p2g.t
@@ -0,0 +1,49 @@
+#!/usr/bin/guile -s
+!#
+; Basic g2p2g_smob tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Copyright (C) 2008 Omer Zak.
+; This library is free software; you can redistribute it and/or
+; modify it under the terms of the GNU Lesser General Public
+; License as published by the Free Software Foundation; either
+; version 2.1 of the License, or (at your option) any later version.
+;
+; This library is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; Lesser General Public License for more details.
+;
+; You should have received a copy of the GNU Lesser General Public License
+; along with this library, in a file named COPYING; if not, write to the
+; Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+; Boston, MA 02111-1307 USA
+;
+; For licensing issues, contact <w1@zak.co.il>.
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+(use-modules (guiletap))
+(use-modules (pyguile))
+
+(plan 6)
+
+(is-ok 1 "g2p_null2PyNone" "'g2p_null2PyNone"
+ (with-output-to-string (lambda () (display g2p_null2PyNone))))
+
+(is-ok 2 "guile2python" "'guile2python"
+ (with-output-to-string (lambda () (display guile2python))))
+
+(is-ok 3 "guileassoc2pythondict" "'guileassoc2pythondict"
+ (with-output-to-string (lambda () (display guileassoc2pythondict))))
+
+(is-ok 4 "p2g_None2SCM_EOL" "'p2g_None2SCM_EOL"
+ (with-output-to-string (lambda () (display p2g_None2SCM_EOL))))
+
+(is-ok 5 "p2g_Dict2alist" "'p2g_Dict2alist"
+ (with-output-to-string (lambda () (display p2g_Dict2alist))))
+
+(is-ok 6 "python2guile" "'python2guile"
+ (with-output-to-string (lambda () (display python2guile))))
+
+; End of 11_g2p2g.t
diff --git a/t/12_g2p_templated.t b/t/12_g2p_templated.t
new file mode 100644
index 0000000..7956443
--- /dev/null
+++ b/t/12_g2p_templated.t
@@ -0,0 +1,225 @@
+#!/usr/bin/guile -s
+!#
+; Basic g2p template tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Copyright (C) 2008 Omer Zak.
+; This library is free software; you can redistribute it and/or
+; modify it under the terms of the GNU Lesser General Public
+; License as published by the Free Software Foundation; either
+; version 2.1 of the License, or (at your option) any later version.
+;
+; This library is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; Lesser General Public License for more details.
+;
+; You should have received a copy of the GNU Lesser General Public License
+; along with this library, in a file named COPYING; if not, write to the
+; Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+; Boston, MA 02111-1307 USA
+;
+; For licensing issues, contact <w1@zak.co.il>.
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+(use-modules (guiletap))
+(use-modules (pyguile))
+
+(plan 36)
+
+(is-ok 1 "real" "1.0"
+ (python-apply '("__builtin__" "repr") '(1.0) '() (list g2p_list2Tuple g2p_real2Float)))
+
+(is-ok 2 "int" "3"
+ (python-apply '("__builtin__" "repr") '(3) '() (list g2p_list2Tuple g2p_num2Int)))
+
+(is-ok 3 "mixed int-real" "[4, 5.0, 6, 7.0, 8]"
+ (python-apply '("__builtin__" "repr") '((4 5 6 7 8)) '()
+ (list g2p_list2Tuple (list g2p_list2List g2p_num2Int g2p_real2Float))))
+
+(is-ok 4 "mixed int-real(tuple)" "(4, 5.0, 6, 7.0, 8)"
+ (python-apply '("__builtin__" "repr") '((4 5 6 7 8)) '()
+ (list g2p_list2Tuple (list g2p_list2Tuple g2p_num2Int g2p_real2Float))))
+
+(is-ok 5 "list2Tuple" "(4.0, 5.0, 6.0, 7.0, 8.0)"
+ (python-apply '("__builtin__" "repr") '((4 5 6 7 8)) '()
+ (list g2p_list2Tuple (cons g2p_list2Tuple g2p_real2Float))))
+
+(is-ok 6 "bool vs. int -> List" "[True, 5, 6, 7, False]"
+ (python-apply '("__builtin__" "repr") '((#t 5 6 7 #f)) '()
+ (list g2p_list2Tuple (list g2p_list2List (list g2p_leaf g2p_num2Int g2p_bool2Bool)))))
+
+(is-ok 7 "pair2tuple" "('a', 5)"
+ (python-apply '("__builtin__" "repr") '(("a" . 5)) '()
+ (list g2p_list2Tuple (cons g2p_pair2Tuple (cons g2p_string2String g2p_num2Int)))))
+
+(is-ok 8 "pair2list" "['c', 55]"
+ (python-apply '("__builtin__" "repr") '(("c" . 55)) '()
+ (list g2p_list2Tuple (cons g2p_pair2List (cons g2p_string2String g2p_num2Int)))))
+
+; Run python-apply under catch harness.
+(define catch-test-apply
+ (lambda (func args kws . targ)
+ (catch #t
+ (lambda () (python-apply func args kws (car targ)))
+ (lambda (key . args2) (object->string (list key args2))))))
+
+(is-ok 9 "bad template in g2p_apply"
+ "(misc-error (\"g2p_apply\" \"bad template item ~S\" (\"pooh\") #f))"
+ (catch-test-apply '("__builtin__" "repr") '() '() "pooh"))
+
+(is-ok 10 "bad template 2 in g2p_apply"
+ "(misc-error (\"g2p_apply\" \"bad template CAR item ~S\" (\"pooh\") #f))"
+ (catch-test-apply '("__builtin__" "repr") '() '() '("pooh" . "bar")))
+
+(is-ok 11 "g2p_leaf in pair" "'pair'"
+ (python-apply '("__builtin__" "repr") '("pair") '() (list g2p_list2Tuple (cons g2p_leaf g2p_string2String))))
+
+(is-ok 12 "g2p_leaf missing conversion datatype"
+ "(misc-error (\"python-apply\" \"positional arguments conversion failure (~S)\" (((#t 5 \"rude\" 7 #f))) #f))"
+ (catch-test-apply '("__builtin__" "repr") '((#t 5 "rude" 7 #f)) '()
+ (list g2p_list2Tuple (list g2p_list2List (list g2p_leaf g2p_num2Int g2p_bool2Bool)))))
+
+; Exercise various g2p_* functions not otherwise covered.
+
+(is-ok 13 "null" "None"
+ (python-apply '("__builtin__" "repr") '(()) '()
+ (list g2p_list2Tuple (list g2p_null2PyNone))))
+
+(is-ok 14 "Other nulls" "[(), [], {}]"
+ (python-apply '("__builtin__" "repr") '((() () ())) '()
+ (list g2p_list2Tuple (list g2p_list2List g2p_null2Tuple0 g2p_null2List0 g2p_null2DictEmpty))))
+
+(is-ok 15 "g2p_copmlex"
+ "[1, 2, 3.5, (4+6j)]"
+ (python-apply '("__builtin__" "repr") '((1 2 3.5 4+6i)) '()
+ (list g2p_list2Tuple (list g2p_list2List (list g2p_leaf g2p_num2Int g2p_real2Float g2p_complex2Complex)))))
+
+(is-ok 16 "bignums"
+ "[1000000, 100000000, 10000000000L, 1000000000000L]"
+ (python-apply '("__builtin__" "repr") '((1000000 100000000 10000000000 1000000000000)) '()
+ (list g2p_list2Tuple (list g2p_list2List (list g2p_leaf g2p_bignum2Long g2p_num2Int)))))
+
+; Exercise g2p_pair2Tuple with bad templates and data
+
+(is-ok 17 "bad template for g2p_pair2Tuple"
+ "(misc-error (\"g2p_pair2Tuple\" \"bad template ~S\" (\"pooh\") #f))"
+ (catch-test-apply '("__builtin__" "repr") '((1 . 2)) '()
+ (list g2p_list2Tuple (cons g2p_pair2Tuple "pooh"))))
+
+(is-ok 18 "validate datatype tests for g2p_pair2Tuple"
+ "(1, 2)"
+ (catch-test-apply '("__builtin__" "repr") '((1 . 2)) '()
+ (list g2p_list2Tuple (cons g2p_pair2Tuple (cons g2p_num2Int g2p_num2Int)))))
+
+(is-ok 19 "bad CAR datatype for g2p_pair2Tuple"
+ "(misc-error (\"python-apply\" \"positional arguments conversion failure (~S)\" (((1 . 2))) #f))"
+ (catch-test-apply '("__builtin__" "repr") '((1 . 2)) '()
+ (list g2p_list2Tuple (cons g2p_pair2Tuple (cons g2p_bool2Bool g2p_num2Int)))))
+
+(is-ok 20 "bad CDR datatype for g2p_pair2Tuple"
+ "(misc-error (\"python-apply\" \"positional arguments conversion failure (~S)\" (((1 . 2))) #f))"
+ (catch-test-apply '("__builtin__" "repr") '((1 . 2)) '()
+ (list g2p_list2Tuple (cons g2p_pair2Tuple (cons g2p_num2Int g2p_bool2Bool)))))
+
+; Exercise g2p_pair2List with bad templates and data
+
+(is-ok 21 "bad template for g2p_pair2List"
+ "(misc-error (\"g2p_pair2List\" \"bad template ~S\" (\"pooh\") #f))"
+ (catch-test-apply '("__builtin__" "repr") '((1 . 2)) '()
+ (list g2p_list2Tuple (cons g2p_pair2List "pooh"))))
+
+(is-ok 22 "validate datatype tests for g2p_pair2List"
+ "[1, 2]"
+ (catch-test-apply '("__builtin__" "repr") '((1 . 2)) '()
+ (list g2p_list2Tuple (cons g2p_pair2List (cons g2p_num2Int g2p_num2Int)))))
+
+(is-ok 23 "bad CAR datatype for g2p_pair2List"
+ "(misc-error (\"python-apply\" \"positional arguments conversion failure (~S)\" (((1 . 2))) #f))"
+ (catch-test-apply '("__builtin__" "repr") '((1 . 2)) '()
+ (list g2p_list2Tuple (cons g2p_pair2List (cons g2p_bool2Bool g2p_num2Int)))))
+
+(is-ok 24 "bad CDR datatype for g2p_pair2List"
+ "(misc-error (\"python-apply\" \"positional arguments conversion failure (~S)\" (((1 . 2))) #f))"
+ (catch-test-apply '("__builtin__" "repr") '((1 . 2)) '()
+ (list g2p_list2Tuple (cons g2p_pair2List (cons g2p_num2Int g2p_bool2Bool)))))
+
+; Exercise g2p_list2Tuple and g2p_list2List with bad arguments
+
+(is-ok 25 "bad argument to g2p_list2Tuple"
+ "(misc-error (\"python-apply\" \"positional arguments conversion failure (~S)\" ((\"not a list\")) #f))"
+ (catch-test-apply '("__builtin__" "repr") '("not a list") '()
+ (list g2p_list2Tuple (list g2p_list2Tuple g2p_string2String))))
+
+(is-ok 26 "bad argument to g2p_list2List"
+ "(misc-error (\"python-apply\" \"positional arguments conversion failure (~S)\" ((\"not a list\")) #f))"
+ (catch-test-apply '("__builtin__" "repr") '("not a list") '()
+ (list g2p_list2Tuple (list g2p_list2List g2p_string2String))))
+
+
+(is-ok 27 "no bad argument datatype to g2p_list2Tuple (test validation)"
+ "(1, 2, 3, 4, 5)"
+ (catch-test-apply '("__builtin__" "repr") '((1 2 3 4 5)) '()
+ (list g2p_list2Tuple (list g2p_list2Tuple g2p_num2Int))))
+
+(is-ok 28 "bad argument datatype to g2p_list2Tuple"
+ "(misc-error (\"python-apply\" \"positional arguments conversion failure (~S)\" (((1 2 3 #f 4 5))) #f))"
+ (catch-test-apply '("__builtin__" "repr") '((1 2 3 #f 4 5)) '()
+ (list g2p_list2Tuple (list g2p_list2Tuple g2p_num2Int))))
+
+
+(is-ok 29 "bad argument datatype to g2p_list2List"
+ "(misc-error (\"python-apply\" \"positional arguments conversion failure (~S)\" (((6 7 #t 8 9 10))) #f))"
+ (catch-test-apply '("__builtin__" "repr") '((6 7 #t 8 9 10)) '()
+ (list g2p_list2Tuple (list g2p_list2List g2p_num2Int))))
+
+(is-ok 30 "bad template (not list) to g2p_list2Tuple"
+ "(wrong-type-arg (\"g2p_list2Tuple\" \"Wrong type argument in position ~A: ~S\" (2 #<unspecified>) #f))"
+ (catch-test-apply '("__builtin__" "repr") '((1 2 3 #f 4 5)) '()
+ (list g2p_list2Tuple g2p_list2Tuple "fadiha")))
+
+
+(is-ok 31 "bad template (not list) to g2p_list2List"
+ "(wrong-type-arg (\"g2p_list2List\" \"Wrong type argument in position ~A: ~S\" (2 #<unspecified>) #f))"
+ (catch-test-apply '("__builtin__" "repr") '((6 7 #t 8 9 10)) '()
+ (list g2p_list2Tuple g2p_list2List "fadiha")))
+
+; g2p_char2String
+
+(is-ok 32 "g2p_char2String"
+ "['a', 5, 6, 7, ' ', 'Q']"
+ (python-apply '("__builtin__" "repr") '((#\a 5 6 7 #\space #\Q)) '()
+ (list g2p_list2Tuple (list g2p_list2List (list g2p_leaf g2p_char2String g2p_num2Int g2p_bool2Bool)))))
+
+; g2p_symbol2String
+
+(is-ok 33 "g2p_symbol2String"
+ "['one', 'two', 'three', 'four', 'five', 'six']"
+ (python-apply '("__builtin__" "repr") '(("one" two #:three "four" five #:six)) '()
+ (list g2p_list2Tuple (list g2p_list2List (list g2p_leaf g2p_string2String g2p_symbol2String g2p_keyword2String)))))
+
+(python-eval "class opaq(object):\n def __init__(self,v):\n self.v=v\n def __repr__(self): return('*** opaque %s ***' % str(self.v))\n" #f)
+(define opaq1 (python-eval "opaq('o p a q 1')" #t))
+(define opaq2 (python-eval "opaq(['o p a q',2])" #t))
+
+(is-ok 34 "opaque data"
+ "[*** opaque o p a q 1 ***, 3, *** opaque ['o p a q', 2] ***]"
+ (python-apply '("__builtin__" "repr") (list (list opaq1 3 opaq2)) '()
+ (list g2p_list2Tuple (list g2p_list2List (list g2p_leaf g2p_opaque2Object g2p_num2Int)))))
+
+; Additional tests: g2p_list2Tuple and g2p_list2List getting data with
+; item inappropriate to the single G2P_SMOB template argument.
+
+(is-ok 35 "list2Tuple"
+ "(misc-error (\"python-apply\" \"positional arguments conversion failure (~S)\" (((4 \"Gorilla!\" 6 7 8))) #f))"
+ (catch-test-apply '("__builtin__" "repr") '((4 "Gorilla!" 6 7 8)) '()
+ (list g2p_list2Tuple (cons g2p_list2Tuple g2p_real2Float))))
+
+(is-ok 36 "list2Tuple"
+ "(misc-error (\"python-apply\" \"positional arguments conversion failure (~S)\" (((4 \"Chimpanzee!\" 6 7 8))) #f))"
+ (catch-test-apply '("__builtin__" "repr") '((4 "Chimpanzee!" 6 7 8)) '()
+ (list g2p_list2Tuple (cons g2p_list2List g2p_real2Float))))
+
+
+; End of 12_g2p_templated.t
diff --git a/t/13_hashes.t b/t/13_hashes.t
new file mode 100644
index 0000000..881039c
--- /dev/null
+++ b/t/13_hashes.t
@@ -0,0 +1,167 @@
+#!/usr/bin/guile -s
+!#
+; Pyguile hash tests - keyword arguments to functions and general Dicts.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Copyright (C) 2008 Omer Zak.
+; This library is free software; you can redistribute it and/or
+; modify it under the terms of the GNU Lesser General Public
+; License as published by the Free Software Foundation; either
+; version 2.1 of the License, or (at your option) any later version.
+;
+; This library is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; Lesser General Public License for more details.
+;
+; You should have received a copy of the GNU Lesser General Public License
+; along with this library, in a file named COPYING; if not, write to the
+; Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+; Boston, MA 02111-1307 USA
+;
+; For licensing issues, contact <w1@zak.co.il>.
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+(use-modules (guiletap))
+(use-modules (pyguile))
+
+(python-eval "import sys;sys.path = ['']+sys.path\n")
+(python-eval "from t.scripts.t4apply import return_args\n")
+
+(define check-kw-conversion
+ (lambda (keywords kwtemplate)
+ (python-apply '("__main__" "return_args") '() keywords
+ (list g2p_list2Tuple guile2python)
+ kwtemplate)))
+
+(define catch-test-kw-conversion
+ (lambda (keywords kwtemplate)
+ (catch #t
+ (lambda () (check-kw-conversion keywords kwtemplate))
+ (lambda (key . args2) (object->string (list key args2))))))
+
+(plan 20)
+
+(is-ok 1 "validate test"
+ "positional: () keywords: {'a': 'argument A'}"
+ (catch-test-kw-conversion '((#:a . "argument A"))
+ (make-hash-table 2)))
+
+(is-ok 2 "no kw template"
+ "positional: () keywords: {'bob': 'buba'}"
+ (python-apply '("__main__" "return_args")
+ '()
+ '((#:bob . "buba"))))
+
+(is-ok 3 "no kw template 2"
+ "positional: () keywords: {'n': None}"
+ (python-apply '("__main__" "return_args")
+ '()
+ '((#:n . ()))
+ (list g2p_list2Tuple guile2python)))
+
+(is-ok 4 "not a list of pairs"
+ "(wrong-type-arg (\"guileassoc2pythondict\" \"Wrong type argument in position ~A: ~S\" (1 not-a-list) #f))"
+ (catch-test-kw-conversion 'not-a-list
+ (make-hash-table 2)))
+
+(is-ok 5 "not a list of pairs 2"
+ "(wrong-type-arg (\"guileassoc2pythondict\" \"Wrong type argument in position ~A: ~S\" (1 not-list-either) #f))"
+ (catch-test-kw-conversion '(not-list-either)
+ (make-hash-table 2)))
+
+(is-ok 6 "bad key"
+ "(wrong-type-arg (\"guileassoc2pythondict\" \"Wrong type argument in position ~A: ~S\" (1 bad-key) #f))"
+ (catch-test-kw-conversion '((bad-key . 'val))
+ (make-hash-table 2)))
+
+; Our own hash table for keywords
+
+(define kwhash (make-hash-table 7))
+(hashq-set! kwhash #:inpstr (list g2p_apply g2p_string2String))
+(hashq-set! kwhash #:inpkw (list g2p_apply g2p_keyword2String))
+(hashq-set! kwhash #:mynum (list g2p_apply g2p_num2Int))
+
+(is-ok 7 "our own conversions"
+ "positional: () keywords: {'inpkw': 'must-be-kw', 'mynum': -3, 'inpstr': 'must be string', 'unverified': ['a', 'b', 'c']}"
+ (catch-test-kw-conversion '((#:unverified . (a b c)) (#:inpstr . "must be string") (#:inpkw . #:must-be-kw) (#:mynum . -3))
+ kwhash))
+
+(is-ok 8 "bad conversion"
+ "(misc-error (\"python-apply\" \"keyword arguments conversion failure (~S)\" (((#:unverified . -3) (#:inpstr . 7) (#:inpkw . #:must-be-kw) (#:mynum . -3))) #f))"
+ (catch-test-kw-conversion '((#:unverified . -3) (#:inpstr . 7) (#:inpkw . #:must-be-kw) (#:mynum . -3))
+ kwhash))
+
+; Passing a Dict argument to Python function
+
+(define catch-python-apply
+ (lambda (args keywords argtemplate)
+ (catch #t
+ (lambda () (python-apply '("__main__" "return_args") args keywords argtemplate))
+ (lambda (key . args2) (object->string (list key args2))))))
+
+(is-ok 9 "basic Dict"
+ "positional: ({1: 2},) keywords: {}"
+ (catch-python-apply '(((1 . 2))) '()
+ (list g2p_list2Tuple (list g2p_alist2Dict (cons (list g2p_apply guile2python) (list g2p_apply guile2python))))))
+
+(is-ok 10 "more complicated Dict"
+ "positional: ({1: 2, '3': 4.0},) keywords: {}"
+ (catch-python-apply '(((1 . 2) ("3" . 4.0))) '()
+ (list g2p_list2Tuple (list g2p_alist2Dict (cons (list g2p_apply guile2python) (list g2p_apply guile2python)) (cons (list g2p_apply g2p_string2String) (list g2p_apply g2p_real2Float))))))
+
+(is-ok 11 "bad conversion"
+ "(misc-error (\"python-apply\" \"positional arguments conversion failure (~S)\" ((((1 . 2) (\"3\" four-dot-zero)))) #f))"
+ (catch-python-apply '(((1 . 2) ("3" . (four-dot-zero)))) '()
+ (list g2p_list2Tuple (list g2p_alist2Dict (cons (list g2p_apply guile2python) (list g2p_apply guile2python)) (cons (list g2p_apply g2p_string2String) (list g2p_apply g2p_real2Float))))))
+
+(is-ok 12 "no alist template"
+ "positional: ([[1, 3, 1, 4], ['2', '6', '2', '8']],) keywords: {}"
+ (python-apply '("__main__" "return_args")
+ '(((1 . (3 1 4)) ("2" . ("6" "2" "8"))))
+ '()))
+
+(is-ok 13 "default template 2"
+ "positional: ({1: [3, 1, 4], '2': ['6', '2', '8']},) keywords: {}"
+ (python-apply '("__main__" "return_args")
+ '(((1 . (3 1 4)) ("2" . ("6" "2" "8"))))
+ '()
+ (list g2p_list2Tuple (list g2p_apply g2p_alist2Dict))))
+
+(is-ok 14 "Dict template with non-list argument"
+ "(misc-error (\"python-apply\" \"positional arguments conversion failure (~S)\" ((\"not-a-list\")) #f))"
+ (catch-python-apply '("not-a-list") '()
+ (list g2p_list2Tuple (list g2p_alist2Dict (cons (list g2p_apply guile2python) (list g2p_apply guile2python))))))
+
+(is-ok 15 "basic Dict with non-list template"
+ "(wrong-type-arg (\"g2p_alist2Dict\" \"Wrong type argument in position ~A: ~S\" (2 'guile2python) #f))"
+ (catch-python-apply '(((1 . 2))) '()
+ (list g2p_list2Tuple (list g2p_apply g2p_alist2Dict guile2python))))
+
+(is-ok 16 "Dict with malformed template"
+ "(wrong-type-arg (\"g2p_alist2Dict\" \"Wrong type argument in position ~A: ~S\" (2 \"notapair\") #f))"
+ (catch-python-apply '(((1 . 2) ("3" . 4.0))) '()
+ (list g2p_list2Tuple (list g2p_alist2Dict (cons (list g2p_apply guile2python) (list g2p_apply guile2python)) "notapair"))))
+
+(is-ok 17 "malformed alist"
+ "(misc-error (\"python-apply\" \"positional arguments conversion failure (~S)\" ((((1 . 2) conjugate (\"3\" . 4.0)))) #f))"
+ (catch-python-apply '(((1 . 2) conjugate ("3" . 4.0))) '()
+ (list g2p_list2Tuple (list g2p_alist2Dict (cons (list g2p_apply guile2python) (list g2p_apply guile2python)) (cons (list g2p_apply g2p_string2String) (list g2p_apply g2p_real2Float))))))
+
+(is-ok 18 "Dict with keys matching template"
+ "positional: ({1: '2', '3': 4.0},) keywords: {}"
+ (catch-python-apply '(((1 . "2") ("3" . 4.0))) '()
+ (list g2p_list2Tuple (list g2p_alist2Dict (cons (list g2p_apply g2p_num2Int) (list g2p_apply g2p_string2String)) (cons (list g2p_apply g2p_string2String) (list g2p_apply g2p_real2Float))))))
+
+(is-ok 19 "Dict with keys not matching template"
+ "(misc-error (\"python-apply\" \"positional arguments conversion failure (~S)\" ((((1.5 . 2) (\"3\" . 4.0)))) #f))"
+ (catch-python-apply '(((1.5 . 2) ("3" . 4.0))) '()
+ (list g2p_list2Tuple (list g2p_alist2Dict (cons (list g2p_apply g2p_num2Int) (list g2p_apply g2p_string2String)) (cons (list g2p_apply g2p_string2String) (list g2p_apply g2p_real2Float))))))
+
+(is-ok 20 "alist with duplicate key"
+ "(misc-error (\"g2p_alist2Dict\" \"duplicate key (~S)\" (1) #f))"
+ (catch-python-apply '(((1 . 2) ("3" . 4.0) (1 . a55))) '()
+ (list g2p_list2Tuple (list g2p_alist2Dict (cons (list g2p_apply guile2python) (list g2p_apply guile2python)) (cons (list g2p_apply guile2python) (list g2p_apply guile2python))))))
+
+; End of 13_hashes.t
diff --git a/t/14_p2g_templated.t b/t/14_p2g_templated.t
new file mode 100644
index 0000000..88611ae
--- /dev/null
+++ b/t/14_p2g_templated.t
@@ -0,0 +1,293 @@
+#!/usr/bin/guile -s
+!#
+; Basic p2g template tests
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Copyright (C) 2008 Omer Zak.
+; This library is free software; you can redistribute it and/or
+; modify it under the terms of the GNU Lesser General Public
+; License as published by the Free Software Foundation; either
+; version 2.1 of the License, or (at your option) any later version.
+;
+; This library is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; Lesser General Public License for more details.
+;
+; You should have received a copy of the GNU Lesser General Public License
+; along with this library, in a file named COPYING; if not, write to the
+; Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+; Boston, MA 02111-1307 USA
+;
+; For licensing issues, contact <w1@zak.co.il>.
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+(use-modules (guiletap))
+(use-modules (pyguile))
+
+(plan 57)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; p2g_None2SCM_EOL
+
+(is-ok 1 "default None" '()
+ (python-eval "None" #t))
+
+(is-ok 2 "default None 2" '()
+ (python-eval "None" python2guile))
+
+(is-ok 3 "default None 3" '()
+ (python-eval "None" p2g_None2SCM_EOL))
+
+(is-ok 4 "default None 4" '()
+ (python-eval "None" (cons p2g_None2SCM_EOL '())))
+
+(is-ok 5 "None becomes 'None'" "None"
+ (python-eval "None" (cons p2g_None2SCM_EOL "None")))
+
+(is-ok 6 "non-None" "#<undefined>"
+ (object->string (python-eval "True" p2g_None2SCM_EOL)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; p2g_Bool2SCM_BOOL
+
+(is-ok 7 "p2g_Bool2SCM_BOOL True" #t
+ (python-eval "True" p2g_Bool2SCM_BOOL))
+
+(is-ok 8 "p2g_Bool2SCM_BOOL False" #f
+ (python-eval "False" p2g_Bool2SCM_BOOL))
+
+(is-ok 9 "p2g_Bool2SCM_BOOL Other" "#<undefined>"
+ (object->string (python-eval "2239" p2g_Bool2SCM_BOOL)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; p2g_Int2num, p2g_Long2bignum
+
+(is-ok 10 "p2g_Int2num ok" 12
+ (python-eval "12" p2g_Int2num))
+
+(is-ok 11 "p2g_Int2num bad" "#<undefined>"
+ (object->string (python-eval "None" p2g_Int2num)))
+
+(is-ok 12 "p2g_Int2num long" "#<undefined>"
+ (object->string (python-eval "1000200030004" p2g_Int2num)))
+
+(is-ok 13 "p2g_Long2bignum ok" 12
+ (python-eval "12L" p2g_Long2bignum))
+
+(is-ok 14 "p2g_Long2bignum bad" "#<undefined>"
+ (object->string (python-eval "'q'" p2g_Long2bignum)))
+
+(is-ok 15 "p2g_Long2bignum long" "1000200030004"
+ (object->string (python-eval "1000200030004" p2g_Long2bignum)))
+
+; p2g_leaf
+
+(is-ok 16 "p2g_Int2num+p2g_Long2bignum" "5000600070008"
+ (object->string (python-eval "5000600070008" (list p2g_leaf p2g_Int2num p2g_Long2bignum))))
+
+(is-ok 17 "p2g_Int2num+p2g_Long2bignum/cons" "5100600070008"
+ (object->string (python-eval "5100600070008" (cons p2g_leaf p2g_Long2bignum))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; float and complex
+
+(is-ok 18 "p2g_Float2real" 2.718
+ (python-eval "2.7+0.018" p2g_Float2real))
+
+(is-ok 19 "p2g_Complex2complex" 1.5+3.6i
+ (python-eval "1.5+3.6j" p2g_Complex2complex))
+
+; Tuple2list of numbers
+
+(is-ok 20 "Numbers in tuple" '(1023 -445 4.75 -8.1e5 2e7+3.125i)
+ (python-eval "(1023,-445,4.75,-8.1e5,(2e7+3.125j))"
+ (list p2g_apply p2g_Tuple2list (list p2g_leaf p2g_Int2num p2g_Long2bignum p2g_Float2real p2g_Complex2complex))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; Strings
+
+(is-ok 21 "String" "a message"
+ (python-eval "'''a message'''" p2g_String2string))
+
+(is-ok 22 "Sumbol" 'a-message
+ (python-eval "'''a-message'''" p2g_String2symbol))
+
+(is-ok 23 "Keyword" #:another-message
+ (python-eval "'''another-message'''" p2g_String2keyword))
+
+; string to char
+
+(is-ok 24 "single char" #\space
+ (python-eval "' '" p2g_1String2char))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; 2-tuple/2-list to pairs
+
+(is-ok 25 "2-tuple to pair" '(12.0 . 34)
+ (python-eval "(12.0,34)" (cons p2g_2Tuple2pair (cons p2g_Float2real p2g_Int2num))))
+
+(is-ok 26 "2-tuple to pair, wrong template" "#<undefined>"
+ (object->string (python-eval "(12.0,34)" (cons p2g_2Tuple2pair (cons p2g_Int2num p2g_Float2real)))))
+
+(is-ok 27 "2-tuple to pair, wrong template due another reason" "#<undefined>"
+ (object->string (python-eval "[12.0,34]" (cons p2g_2Tuple2pair (cons p2g_Int2num p2g_Float2real)))))
+
+(is-ok 28 "2-list to pair" '("ab" . "cd")
+ (python-eval "['ab','cd']" (cons p2g_2List2pair (cons p2g_String2string p2g_String2string))))
+
+(is-ok 29 "2-tuple (or 2-list) to pair" '("ef" . 3)
+ (python-eval "('ef',3)"
+ (cons p2g_leaf
+ (list
+ (cons p2g_2Tuple2pair (cons p2g_String2string p2g_Int2num))
+ (cons p2g_2List2pair (cons p2g_String2string p2g_Int2num))))))
+
+
+(is-ok 30 "(2-tuple or) 2-list to pair" '("ef" . 3)
+ (python-eval "['ef',3]"
+ (cons p2g_leaf
+ (list
+ (cons p2g_2Tuple2pair (cons p2g_String2string p2g_Int2num))
+ (cons p2g_2List2pair (cons p2g_String2string p2g_Int2num))))))
+
+(is-ok 31 "2-list to pair, wrong template" "#<undefined>"
+ (object->string (python-eval "[12.0,34]" (cons p2g_2List2pair (cons p2g_Int2num p2g_Float2real)))))
+
+(is-ok 32 "2-list to pair, wrong template due another reason" "#<undefined>"
+ (object->string (python-eval "(12.0,34)" (cons p2g_2List2pair (cons p2g_Int2num p2g_Float2real)))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; Aggregates - N-Tuples/N-Lists to lists
+
+; from Tuples
+(is-ok 33 "tuple to list, mixed datatypes" "(11 12.0 13 34.5 38 780.25)"
+ (object->string (python-eval "(11,12.0,13,34.5,38,780.25)" (cons p2g_Tuple2list (list p2g_Int2num p2g_Float2real)))))
+
+(is-ok 34 "tuple to list, wrong data type" "#<undefined>"
+ (object->string (python-eval "[11,12.0,13,34.5,38,780.25]" (cons p2g_Tuple2list (list p2g_Int2num p2g_Float2real)))))
+
+(is-ok 35 "tuple to list, P2G_SMOB" "(11 12 13 34 38 780)"
+ (object->string (python-eval "(11,12,13,34,38,780)" (cons p2g_Tuple2list p2g_Int2num))))
+
+(is-ok 36 "tuple to list, P2G_SMOB, one bad value" "#<undefined>"
+ (object->string (python-eval "(11,12,'a13',34,38,780)" (cons p2g_Tuple2list p2g_Int2num))))
+
+; from Lists
+(is-ok 37 "List to list, mixed datatypes" "(11 12.0 13 34.5 38 780.25)"
+ (object->string (python-eval "[11,12.0,13,34.5,38,780.25]" (cons p2g_List2list (list p2g_Int2num p2g_Float2real)))))
+
+(is-ok 38 "List to list, wrong data type" "#<undefined>"
+ (object->string (python-eval "(11,12.0,13,34.5,38,780.25)" (cons p2g_List2list (list p2g_Int2num p2g_Float2real)))))
+
+(is-ok 39 "List to list, P2G_SMOB" "(11 12 13 34 38 780)"
+ (object->string (python-eval "[11,12,13,34,38,780]" (cons p2g_List2list p2g_Int2num))))
+
+(is-ok 40 "List to list, P2G_SMOB, one bad value" "#<undefined>"
+ (object->string (python-eval "[11,12,'a13',34,38,780]" (cons p2g_List2list p2g_Int2num))))
+
+; p2g_Dict2alist
+
+(define alist-properly-included?
+ (lambda (included includor)
+ (if (null? included) #t
+ (let ((key (caar included))
+ (value (cdar included))
+ (rest (cdr included)))
+ (let ((includor-ref (assoc key includor)))
+ (cond ((not includor-ref) #f)
+ ((not (equal? (cdr includor-ref) value)) #f)
+ (else (alist-properly-included? rest includor))))))))
+
+(define alist-equal?
+ (lambda (alista alistb)
+ (and (alist-properly-included? alista alistb)
+ (alist-properly-included? alistb alista))))
+
+; Quick tests of alist-properly-included?
+(ok 41 "should pass 1"
+ (alist-equal? '((1 . 2)("3" . "4"))
+ '(("3" . "4")(1 . 2))))
+
+(ok 42 "should pass 2"
+ (alist-equal? '((1 . 2)("3" . "4"))
+ '((1 . 2)("3" . "4"))))
+
+(ok 43 "should fail 1"
+ (not (alist-equal? '((1 . 2)("3" . "4"))
+ '((11 . 2)("3" . "4")))))
+
+(ok 44 "should fail 2"
+ (not (alist-equal? '((1 . 2)("3" . "4"))
+ '((1 . 21)("3" . "4")))))
+
+(ok 45 "should fail 3"
+ (not (alist-equal? '((1 . 2)("3" . "4"))
+ '((1 . 2)("3a" . "4")))))
+
+(ok 46 "should fail 4"
+ (not (alist-equal? '((1 . 2)("3" . "4"))
+ '((1 . 2)("3" . "4b")))))
+
+(ok 47 "should fail 1a"
+ (not (alist-equal? '(("3" . "4")(1 . 2))
+ '((11 . 2)("3" . "4")))))
+
+(ok 48 "should fail 2a"
+ (not (alist-equal? '(("3" . "4")(1 . 2))
+ '((1 . 21)("3" . "4")))))
+
+(ok 49 "should fail 3a"
+ (not (alist-equal? '(("3" . "4")(1 . 2))
+ '((1 . 2)("3a" . "4")))))
+
+(ok 50 "should fail 4a"
+ (not (alist-equal? '(("3" . "4")(1 . 2))
+ '((1 . 2)("3" . "4b")))))
+
+(ok 51 "should fail 5"
+ (not (alist-equal? '(("3" . "4")(1 . 2)(5 . 6))
+ '((1 . 2)("3" . "4")))))
+
+(ok 52 "should fail 5a"
+ (not (alist-equal? '(("3" . "4")(1 . 2)(5 . 6))
+ '((1 . 2)("3" . "4")(7 . 8)(5 . 6)))))
+
+
+; Proper p2g_Dict2alist tests
+
+
+(ok 53 "Default p2g_Dict2alist"
+ (alist-equal? '((1 . 2) ("3" . "4"))
+ (python-eval "{1 : 2, '3' : '4'}" #t)))
+
+(ok 54 "Explicit p2g_Dict2alist template"
+ (alist-equal? '((#\b . 42) (gg . 3))
+ (python-eval "{'b' : None, 'gg' : 3}"
+ (cons p2g_Dict2alist
+ (cons
+ (cons p2g_leaf (list p2g_1String2char p2g_String2symbol))
+ (cons p2g_leaf (list (cons p2g_None2SCM_EOL 42) p2g_Int2num)))))))
+
+(ok 55 "P2G_SMOBP based p2g_Dict2alist template"
+ (alist-equal? '(("key1" . val1) ("key2" . myval2) ("k3" . yourval3))
+ (python-eval "{'key1' : 'val1', 'key2' : 'myval2', 'k3' : 'yourval3'}"
+ (cons p2g_Dict2alist
+ (cons p2g_String2string p2g_String2symbol)))))
+
+(is-ok 56 "p2g_Dict2alist key conversion failure" "#<undefined>"
+ (object->string (python-eval "{'b' : None, 1.2 : 3}"
+ (cons p2g_Dict2alist
+ (cons
+ (cons p2g_leaf (list p2g_1String2char p2g_String2symbol))
+ (cons p2g_leaf (list (cons p2g_None2SCM_EOL 42) p2g_Int2num)))))))
+
+(is-ok 57 "p2g_Dict2alist value conversion failure" "#<undefined>"
+ (object->string (python-eval "{'b' : 'None', 'gg' : 3}"
+ (cons p2g_Dict2alist
+ (cons
+ (cons p2g_leaf (list p2g_1String2char p2g_String2symbol))
+ (cons p2g_leaf (list (cons p2g_None2SCM_EOL 42) p2g_Int2num)))))))
+
+; End of 14_p2g_templated.t
diff --git a/t/15_p2g_errors.t b/t/15_p2g_errors.t
new file mode 100644
index 0000000..13398bd
--- /dev/null
+++ b/t/15_p2g_errors.t
@@ -0,0 +1,126 @@
+#!/usr/bin/guile -s
+!#
+; p2g conversion error handling tests.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Copyright (C) 2008 Omer Zak.
+; This library is free software; you can redistribute it and/or
+; modify it under the terms of the GNU Lesser General Public
+; License as published by the Free Software Foundation; either
+; version 2.1 of the License, or (at your option) any later version.
+;
+; This library is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; Lesser General Public License for more details.
+;
+; You should have received a copy of the GNU Lesser General Public License
+; along with this library, in a file named COPYING; if not, write to the
+; Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+; Boston, MA 02111-1307 USA
+;
+; For licensing issues, contact <w1@zak.co.il>.
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+(use-modules (guiletap))
+(use-modules (pyguile))
+
+(plan 20)
+
+; Run python-eval under catch harness.
+(define catch-test-eval
+ (lambda (txt template)
+ (catch #t
+ (lambda () (python-eval txt template))
+ (lambda (key . args) (object->string (list key args))))))
+
+(is-ok 1 "template with bad CAR"
+ "(misc-error (\"p2g_apply\" \"bad template CAR item ~S\" (42) #f))"
+ (catch-test-eval "-42" (cons 42 "None")))
+
+(is-ok 2 "p2g_leaf template with bad CDR"
+ "(wrong-type-arg (\"p2g_leaf\" \"Wrong type argument in position ~A: ~S\" (2 \"foofoo\") #f))"
+ (catch-test-eval "-42" (cons p2g_leaf "foofoo")))
+
+(is-ok 3 "no p2g_leaf template matches the data"
+ "#<undefined>"
+ (object->string (catch-test-eval "'a-42'" (list p2g_leaf p2g_Int2num p2g_Long2bignum))))
+
+(is-ok 4 "too short string for keyword"
+ "#<undefined>"
+ (object->string (catch-test-eval "''" p2g_String2keyword)))
+
+(is-ok 5 "too long string for char"
+ "#<undefined>"
+ (object->string (catch-test-eval "'lg'" p2g_1String2char)))
+
+(is-ok 6 "too short string for char"
+ "#<undefined>"
+ (object->string (catch-test-eval "''''''" p2g_1String2char)))
+
+(is-ok 7 "2-tuple to pair, bad template"
+ "(misc-error (\"p2g_2Tuple2pair\" \"bad template item ~S\" (12.3) #f))"
+ (catch-test-eval "(12.0,34)" (cons p2g_2Tuple2pair 12.3)))
+
+(is-ok 8 "2-tuple to pair, wrong tuple length - too long" "#<undefined>"
+ (object->string (python-eval "(1,2,3)" (cons p2g_2Tuple2pair (cons p2g_Int2num p2g_Int2num)))))
+
+(is-ok 9 "2-tuple to pair, wrong tuple length - too short" "#<undefined>"
+ (object->string (python-eval "(1,)" (cons p2g_2Tuple2pair (cons p2g_Int2num p2g_Int2num)))))
+
+(is-ok 10 "2-tuple to pair, wrong 2nd item datatype" "#<undefined>"
+ (object->string (python-eval "(1,'zuzu')" (cons p2g_2Tuple2pair (cons p2g_Int2num p2g_Int2num)))))
+
+(is-ok 11 "2-list to pair, bad template"
+ "(misc-error (\"p2g_2List2pair\" \"bad template item ~S\" (12.3) #f))"
+ (catch-test-eval "[12.0,34]" (cons p2g_2List2pair 12.3)))
+
+(is-ok 12 "2-list to pair, wrong tuple length - too long" "#<undefined>"
+ (object->string (python-eval "[1,2,3]" (cons p2g_2List2pair (cons p2g_Int2num p2g_Int2num)))))
+
+(is-ok 13 "2-list to pair, wrong tuple length - too short" "#<undefined>"
+ (object->string (python-eval "[1]" (cons p2g_2List2pair (cons p2g_Int2num p2g_Int2num)))))
+
+(is-ok 14 "2-list to pair, wrong 1st item datatype" "#<undefined>"
+ (object->string (python-eval "['xyxy',11]" (cons p2g_2List2pair (cons p2g_Int2num p2g_Int2num)))))
+
+(is-ok 15 "2-list to pair, wrong 2nd item datatype" "#<undefined>"
+ (object->string (python-eval "[1,'zuzu']" (cons p2g_2List2pair (cons p2g_Int2num p2g_Int2num)))))
+
+
+; template not a proper list
+
+(is-ok 16 "Tuple to list, template improper list"
+ "(wrong-type-arg (\"p2g_Tuple2list\" \"Wrong type argument in position ~A: ~S\" (2 ('p2g_Int2num 'p2g_Float2real . 'p2g_Float2real)) #f))"
+ (catch-test-eval
+ "(11,12.0,13,34.5,38,780.25)"
+ (cons p2g_Tuple2list (cons p2g_Int2num (cons p2g_Float2real p2g_Float2real)))))
+
+(is-ok 17 "List to list, template improper list"
+ "(wrong-type-arg (\"p2g_List2list\" \"Wrong type argument in position ~A: ~S\" (2 ('p2g_Int2num 'p2g_Float2real . 'p2g_Float2real)) #f))"
+ (catch-test-eval
+ "[11,12.0,13,34.5,38,780.25]"
+ (cons p2g_List2list (cons p2g_Int2num (cons p2g_Float2real p2g_Float2real)))))
+
+; p2g_Dict2alist - bad templates
+
+(is-ok 18 "Dict to alist, template is not pair"
+ "(misc-error (\"p2g_Dict2alist\" \"bad template ~S\" ('p2g_Int2num) #f))"
+ (catch-test-eval
+ "{1 : 2, 3 : 4}"
+ (cons p2g_Dict2alist p2g_Int2num)))
+
+(is-ok 19 "Dict to alist, bad template CAR"
+ "(misc-error (\"p2g_apply\" \"bad template item ~S\" (12) #f))"
+ (catch-test-eval
+ "{1 : 2, 3 : 4}"
+ (cons p2g_Dict2alist (cons 12 p2g_Int2num))))
+
+(is-ok 20 "Dict to alist, bad template CDR"
+ "(misc-error (\"p2g_apply\" \"bad template item ~S\" (\"cde\") #f))"
+ (catch-test-eval
+ "{1 : 2, 3 : 4}"
+ (cons p2g_Dict2alist (cons p2g_Int2num "cde"))))
+
+; End of 15_p2g_errors.t
diff --git a/t/16_verbose.t b/t/16_verbose.t
new file mode 100755
index 0000000..58adb75
--- /dev/null
+++ b/t/16_verbose.t
@@ -0,0 +1,50 @@
+#!/usr/bin/guile -s
+!#
+; Basic tests of the PyGuile verbosity control
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Copyright (C) 2008 Omer Zak.
+; This library is free software; you can redistribute it and/or
+; modify it under the terms of the GNU Lesser General Public
+; License as published by the Free Software Foundation; either
+; version 2.1 of the License, or (at your option) any later version.
+;
+; This library is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; Lesser General Public License for more details.
+;
+; You should have received a copy of the GNU Lesser General Public License
+; along with this library, in a file named COPYING; if not, write to the
+; Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+; Boston, MA 02111-1307 USA
+;
+; For licensing issues, contact <w1@zak.co.il>.
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+(use-modules (guiletap))
+(use-modules (pyguile))
+
+(plan 4)
+
+(pyguile-verbosity-set! 43)
+(is-ok 1 "Verbosity value should be 43"
+ 43
+ (pyguile-verbosity-set! 10))
+
+(is-ok 2 "Verbosity value should be 10"
+ 10
+ (pyguile-verbosity-set! 0))
+
+(is-ok 3 "Verbosity value should be 0"
+ 0
+ (pyguile-verbosity-set! 0))
+
+(is-ok 4 "Trying to set verbosity value to a non-number"
+ "(wrong-type-arg (\"pyguile-verbosity-set!\" \"Wrong type argument in position ~A: ~S\" (1 \"NAN\") #f))"
+ (catch #t
+ (lambda () (pyguile-verbosity-set! "NAN"))
+ (lambda (key . args) (object->string (list key args)))))
+
+; End of 16_verbose.t
diff --git a/t/17_verbose.t b/t/17_verbose.t
new file mode 100644
index 0000000..c807ed8
--- /dev/null
+++ b/t/17_verbose.t
@@ -0,0 +1,161 @@
+#!/usr/bin/guile -s
+!#
+; Tests of PyGuile verbosity messages - for full source code coverage
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Copyright (C) 2008 Omer Zak.
+; This library is free software; you can redistribute it and/or
+; modify it under the terms of the GNU Lesser General Public
+; License as published by the Free Software Foundation; either
+; version 2.1 of the License, or (at your option) any later version.
+;
+; This library is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; Lesser General Public License for more details.
+;
+; You should have received a copy of the GNU Lesser General Public License
+; along with this library, in a file named COPYING; if not, write to the
+; Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+; Boston, MA 02111-1307 USA
+;
+; For licensing issues, contact <w1@zak.co.il>.
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+(use-modules (guiletap))
+(use-modules (pyguile))
+;(use-modules (srfi srfi-6)) ; string ports
+(use-modules (srfi srfi-13)) ; string-concatenate
+(use-modules (ice-9 regex)) ; regexp-substitute
+
+;(define get-output
+; (lambda (thunk)
+; (let ((my-output-port open-output-string))
+; (call-with-output-string (thunk))
+; (get-output-string my-output-port))))
+
+(define thunk-invoke-python-repr
+ (lambda (arg . template)
+ (lambda ()
+ (if (null? template)
+ (python-apply '("__builtin__" "repr") arg '())
+ (python-apply '("__builtin__" "repr") arg '() (car template))))))
+
+(plan 6)
+
+(pyguile-verbosity-set! 1)
+
+(define successful-verbose-builtin-repr-conversion-report
+ (string-concatenate
+ '("# g2p_string2String: successful conversion of \"__builtin__\" into a Python String value\n"
+ "# g2p_string2String: successful conversion of \"repr\" into a Python String value\n")))
+
+(define expres1
+ (string-concatenate
+ (list
+ successful-verbose-builtin-repr-conversion-report
+ "# g2p_bool2Bool: successful conversion of #t into a Python Bool value\n"
+ "# p2g_String2string: successful conversion of \"'True'\" into SCM\n")))
+(is-ok 1 "Verbosity with a single value"
+ expres1
+ (with-output-to-string
+ (thunk-invoke-python-repr '(#t))))
+
+(define expres2
+ (string-concatenate
+ (list
+ successful-verbose-builtin-repr-conversion-report
+ "# g2p_null2PyNone: successful conversion of () into Python None\n"
+ "# g2p_bool2Bool: successful conversion of #t into a Python Bool value\n"
+ "# g2p_bool2Bool: successful conversion of #f into a Python Bool value\n"
+ "# g2p_num2Int: successful conversion of 1 into a Python Int value\n"
+ "# g2p_num2Int: successful conversion of -5 into a Python Int value\n"
+ "# g2p_symbol2String: successful conversion of quote into a Python String value\n"
+ "# g2p_symbol2String: successful conversion of symba into a Python String value\n"
+ "# g2p_complex2Complex: successful conversion of 1.0-1.0i into a Python Complex value\n"
+ "# g2p_real2Float: successful conversion of 3.125 into a Python Float value\n"
+ "# p2g_String2string: successful conversion of \"\\\"[None, True, False, 1, -5, 'P', ['quote', 'symba'], (1-1j), 3.125]\\\"\" into SCM\n")))
+(is-ok 2 "Verbosity when successful"
+ expres2
+ (with-output-to-string
+ (thunk-invoke-python-repr '((() #t #f 1 -5 #\P 'symba 1-1i 3.125)))))
+
+(define expres3
+ (string-concatenate
+ (list
+ successful-verbose-builtin-repr-conversion-report
+ "# g2p_null2Tuple0: successful conversion of () into Python ()\n"
+ "# g2p_null2List0: successful conversion of () into Python []\n"
+ "# g2p_null2DictEmpty: successful conversion of () into Python {}\n"
+ "# p2g_String2string: successful conversion of \"'((), [], {})'\" into SCM\n")))
+(is-ok 3 "g2p* verbosity coverage"
+ expres3
+ (with-output-to-string
+ (thunk-invoke-python-repr
+ '((() () ()))
+ (list g2p_list2Tuple (list g2p_list2Tuple g2p_null2Tuple0 g2p_null2List0 g2p_null2DictEmpty)))))
+
+(define expres4
+ (string-concatenate
+ (list
+ successful-verbose-builtin-repr-conversion-report
+ "# g2p_num2Int: successful conversion of 1000000 into a Python Int value\n"
+ "# g2p_bignum2Long: successful conversion of 1000000000000 into a Python Long value\n"
+ "# p2g_String2string: successful conversion of \"'[1000000, 1000000000000L]'\" into SCM\n")))
+(is-ok 4 "g2p* verbosity coverage - bignums,list2Tuple,list2List"
+ expres4
+ (with-output-to-string
+ (thunk-invoke-python-repr
+ '((1000000 1000000000000))
+ (list g2p_list2Tuple (list g2p_list2List (list g2p_leaf g2p_bignum2Long g2p_num2Int))))))
+
+(define expres5
+ (string-concatenate
+ (list
+ successful-verbose-builtin-repr-conversion-report
+ "# g2p_symbol2String: successful conversion of p1at into a Python String value\n"
+ "# g2p_string2String: successful conversion of \"p1bt\" into a Python String value\n"
+ "# g2p_keyword2String: successful conversion of #:p2al into a Python String value\n"
+ "# g2p_symbol2String: successful conversion of p2bl into a Python String value\n"
+ "# p2g_String2string: successful conversion of \"\\\"(('p1at', 'p1bt'), ['p2al', 'p2bl'])\\\"\" into SCM\n")))
+(is-ok 5 "g2p* verbosity coverage - pair2Tuple,pair2List"
+ expres5
+ (with-output-to-string
+ (thunk-invoke-python-repr
+ '(((p1at . "p1bt") (#:p2al . p2bl)))
+ (list g2p_list2Tuple (list g2p_list2Tuple (cons g2p_pair2Tuple (cons guile2python guile2python)) (cons g2p_pair2List (cons guile2python guile2python)))))))
+
+(define substitute-hex-addresses-for-gggggggg
+ (lambda (strarg)
+ (regexp-substitute/global #f
+ "0x[0-9a-f]{8}"
+ strarg
+ 'pre "0xgggggggg" 'post)))
+
+(define expres6
+ (string-concatenate
+ (list
+ successful-verbose-builtin-repr-conversion-report
+ "# g2p_opaque2Object: the Python object inside opaque pysmob (python-eval <function func at 0xgggggggg> #t) is unwrapped\n"
+ "# g2p_num2Int: successful conversion of 1 into a Python Int value\n"
+ "# g2p_string2String: successful conversion of \"one\" into a Python String value\n"
+ "# g2p_num2Int: successful conversion of 2 into a Python Int value\n"
+ "# g2p_string2String: successful conversion of \"two\" into a Python String value\n"
+ "# g2p_num2Int: successful conversion of 3 into a Python Int value\n"
+ "# g2p_string2String: successful conversion of \"three\" into a Python String value\n"
+ "# p2g_String2string: successful conversion of \"\\\"[<function func at 0xgggggggg>, {1: 'one', 2: 'two', 3: 'three'}]\\\"\" into SCM\n")))
+(python-eval "def func(a):\n print a\n" #f)
+(define opaqueobj (python-eval "func" #t))
+(define run-test-opaque
+ (lambda ()
+ (with-output-to-string
+ (thunk-invoke-python-repr
+ (list (list opaqueobj '((1 . "one")(2 . "two")(3 . "three"))))
+ (list g2p_list2Tuple (list g2p_list2List g2p_opaque2Object (cons g2p_alist2Dict (list (cons g2p_num2Int g2p_string2String)))))))))
+(is-ok 6 "g2p* verbosity coverage - opaque2Object,g2p_alist2Dict"
+ expres6
+ (substitute-hex-addresses-for-gggggggg
+ (run-test-opaque)))
+
+; End of 17_verbose.t
diff --git a/t/18_bignum.t b/t/18_bignum.t
new file mode 100644
index 0000000..d30adb6
--- /dev/null
+++ b/t/18_bignum.t
@@ -0,0 +1,55 @@
+#!/usr/bin/guile -s
+!#
+; Miscellaneous tests of PyGuile data handling
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Copyright (C) 2008 Omer Zak.
+; This library is free software; you can redistribute it and/or
+; modify it under the terms of the GNU Lesser General Public
+; License as published by the Free Software Foundation; either
+; version 2.1 of the License, or (at your option) any later version.
+;
+; This library is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; Lesser General Public License for more details.
+;
+; You should have received a copy of the GNU Lesser General Public License
+; along with this library, in a file named COPYING; if not, write to the
+; Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+; Boston, MA 02111-1307 USA
+;
+; For licensing issues, contact <w1@zak.co.il>.
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+(use-modules (guiletap))
+(use-modules (pyguile))
+
+(python-eval "def func(val):\n global longint\n longint=val\n" #f)
+
+(plan 3)
+
+(python-apply "func" '("abc") '())
+; The following won't work because Guile's and Python's stdout are
+; separately controlled.
+;;(is-ok 1 "should yield 'abc'"
+;; "abc\n"
+;; (with-output-to-string
+;; (lambda ()
+;; (python-eval "print longint\n" #f))))
+(is-ok 1 "should yield 'abc'"
+ "abc"
+ (python-eval "longint" #t))
+
+(python-apply "func" '(3773) '())
+(is-ok 2 "should yield 3773"
+ 3773
+ (python-eval "longint" #t))
+
+(python-apply "func" '(36893488147419103232) '())
+(is-ok 3 "should yield the bignum"
+ 36893488147419103232
+ (python-eval "longint" #t))
+
+; End of 18_bignum.t
diff --git a/t/19_verbose_always.t b/t/19_verbose_always.t
new file mode 100644
index 0000000..f65f3df
--- /dev/null
+++ b/t/19_verbose_always.t
@@ -0,0 +1,733 @@
+#!/usr/bin/guile -s
+!#
+; Tests of PyGuile verbosity messages - for full source code coverage
+;
+; Same tests as 17_verbose.t, except that the verbosity level was set
+; to 3 (PYGUILE_VERBOSE_G2P2G_SUCCESSFUL+PYGUILE_VERBOSE_G2P2G_ALWAYS).
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Copyright (C) 2008 Omer Zak.
+; This library is free software; you can redistribute it and/or
+; modify it under the terms of the GNU Lesser General Public
+; License as published by the Free Software Foundation; either
+; version 2.1 of the License, or (at your option) any later version.
+;
+; This library is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; Lesser General Public License for more details.
+;
+; You should have received a copy of the GNU Lesser General Public License
+; along with this library, in a file named COPYING; if not, write to the
+; Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+; Boston, MA 02111-1307 USA
+;
+; For licensing issues, contact <w1@zak.co.il>.
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+(use-modules (guiletap))
+(use-modules (pyguile))
+(use-modules (srfi srfi-13)) ; string-concatenate
+(use-modules (ice-9 regex)) ; regexp-substitute
+
+(define thunk-invoke-python-repr
+ (lambda (arg . template)
+ (lambda ()
+ (if (null? template)
+ (python-apply '("__builtin__" "repr") arg '())
+ (python-apply '("__builtin__" "repr") arg '() (car template))))))
+
+(plan 13)
+
+(pyguile-verbosity-set! 3)
+
+(define successful-verbose-3-report
+ (string-concatenate
+ '("# guile2python: entry: seeking to convert sobj=(\"__builtin__\" \"repr\"); unused stemplate=#<unspecified>\n"
+ "# g2p_null2PyNone: unsuccessful conversion: (\"__builtin__\" \"repr\") is not null\n"
+ "# DEBUG: g2p_list2List - processing item CAR(sobj)=\"__builtin__\" CAR(stemp)='guile2python\n"
+ "# Entered g2p_apply: sobj=\"__builtin__\" stemplate='guile2python\n"
+ "# guile2python: entry: seeking to convert sobj=\"__builtin__\"; unused stemplate=#<unspecified>\n"
+ "# g2p_null2PyNone: unsuccessful conversion: \"__builtin__\" is not null\n"
+ "# g2p_list2List: unsuccessful conversion: \"__builtin__\" is not a list\n"
+ "# g2p_pair2Tuple: unsuccessful conversion: \"__builtin__\" is not a pair\n"
+ "# g2p_bool2Bool: unsuccessful conversion: \"__builtin__\" is not a bool value\n"
+ "# g2p_num2Int: unsuccessful conversion: \"__builtin__\" is not a num value\n"
+ "# g2p_bignum2Long: unsuccessful conversion: \"__builtin__\" is not a bignum value\n"
+ "# g2p_real2Float: unsuccessful conversion: \"__builtin__\" is not a real value\n"
+ "# g2p_complex2Complex: unsuccessful conversion: \"__builtin__\" is not a complex value\n"
+ "# g2p_string2String: successful conversion of \"__builtin__\" into a Python String value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# DEBUG: g2p_list2List - processing item CAR(sobj)=\"repr\" CAR(stemp)='guile2python\n"
+ "# Entered g2p_apply: sobj=\"repr\" stemplate='guile2python\n"
+ "# guile2python: entry: seeking to convert sobj=\"repr\"; unused stemplate=#<unspecified>\n"
+ "# g2p_null2PyNone: unsuccessful conversion: \"repr\" is not null\n"
+ "# g2p_list2List: unsuccessful conversion: \"repr\" is not a list\n"
+ "# g2p_pair2Tuple: unsuccessful conversion: \"repr\" is not a pair\n"
+ "# g2p_bool2Bool: unsuccessful conversion: \"repr\" is not a bool value\n"
+ "# g2p_num2Int: unsuccessful conversion: \"repr\" is not a num value\n"
+ "# g2p_bignum2Long: unsuccessful conversion: \"repr\" is not a bignum value\n"
+ "# g2p_real2Float: unsuccessful conversion: \"repr\" is not a real value\n"
+ "# g2p_complex2Complex: unsuccessful conversion: \"repr\" is not a complex value\n"
+ "# g2p_string2String: successful conversion of \"repr\" into a Python String value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_list2List: successful conversion of list () by template\n")))
+
+(define expres1
+ (string-concatenate
+ (list
+ successful-verbose-3-report
+ "# Entered g2p_apply: sobj=(#t) stemplate=('g2p_list2Tuple 'guile2python)\n"
+ "# g2p_list2Tuple - processing item CAR(sobj)=#t CAR(stemp)='guile2python\n"
+ "# Entered g2p_apply: sobj=#t stemplate='guile2python\n"
+ "# guile2python: entry: seeking to convert sobj=#t; unused stemplate=#<unspecified>\n"
+ "# g2p_null2PyNone: unsuccessful conversion: #t is not null\n"
+ "# g2p_list2List: unsuccessful conversion: #t is not a list\n"
+ "# g2p_pair2Tuple: unsuccessful conversion: #t is not a pair\n"
+ "# g2p_bool2Bool: successful conversion of #t into a Python Bool value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_list2Tuple: successful conversion of list () by template\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# guileassoc2pythondict: entry: seeking to convert sobj=() using stemplate=#<undefined>\n"
+ "# guileassoc2pythondict: successful conversion of ()\n"
+ "# p2g_apply: pobj=\"'True'\" smob-stemplate='python2guile\n"
+ "# python2guile: trying to convert pobj=\"'True'\" using stemplate=#<unspecified>\n"
+ "# p2g_None2SCM_EOL: pobj=\"'True'\" stemplate=#<unspecified>\n"
+ "# p2g_Bool2SCM_BOOL: failed to convert pobj=\"'True'\" using stemplate=#<unspecified>\n"
+ "# p2g_Int2num: failed to convert pobj=\"'True'\" using stemplate=#<unspecified>\n"
+ "# p2g_Long2bignum: failed to convert pobj=\"'True'\" using stemplate=#<unspecified>\n"
+ "# p2g_Float2real: failed to convert pobj=\"'True'\" using stemplate=#<unspecified>\n"
+ "# p2g_Complex2complex: failed to convert pobj=\"'True'\" using stemplate=#<unspecified>\n"
+ "# p2g_String2string: successful conversion of \"'True'\" into SCM\n")))
+(is-ok 1 "Verbosity/3 with a single value"
+ expres1
+ (with-output-to-string
+ (thunk-invoke-python-repr '(#t))))
+
+(define expres2
+ (string-concatenate
+ (list
+ successful-verbose-3-report
+ "# Entered g2p_apply: sobj=((() #t #f 1 -5 #\\P (quote symba) 1.0-1.0i 3.125)) stemplate=('g2p_list2Tuple 'guile2python)\n"
+ "# g2p_list2Tuple - processing item CAR(sobj)=(() #t #f 1 -5 #\\P (quote symba) 1.0-1.0i 3.125) CAR(stemp)='guile2python\n"
+ "# Entered g2p_apply: sobj=(() #t #f 1 -5 #\\P (quote symba) 1.0-1.0i 3.125) stemplate='guile2python\n"
+ "# guile2python: entry: seeking to convert sobj=(() #t #f 1 -5 #\\P (quote symba) 1.0-1.0i 3.125); unused stemplate=#<unspecified>\n"
+ "# g2p_null2PyNone: unsuccessful conversion: (() #t #f 1 -5 #\\P (quote symba) 1.0-1.0i 3.125) is not null\n"
+ "# DEBUG: g2p_list2List - processing item CAR(sobj)=() CAR(stemp)='guile2python\n"
+ "# Entered g2p_apply: sobj=() stemplate='guile2python\n"
+ "# guile2python: entry: seeking to convert sobj=(); unused stemplate=#<unspecified>\n"
+ "# g2p_null2PyNone: successful conversion of () into Python None\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# DEBUG: g2p_list2List - processing item CAR(sobj)=#t CAR(stemp)='guile2python\n"
+ "# Entered g2p_apply: sobj=#t stemplate='guile2python\n"
+ "# guile2python: entry: seeking to convert sobj=#t; unused stemplate=#<unspecified>\n"
+ "# g2p_null2PyNone: unsuccessful conversion: #t is not null\n"
+ "# g2p_list2List: unsuccessful conversion: #t is not a list\n"
+ "# g2p_pair2Tuple: unsuccessful conversion: #t is not a pair\n"
+ "# g2p_bool2Bool: successful conversion of #t into a Python Bool value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# DEBUG: g2p_list2List - processing item CAR(sobj)=#f CAR(stemp)='guile2python\n"
+ "# Entered g2p_apply: sobj=#f stemplate='guile2python\n"
+ "# guile2python: entry: seeking to convert sobj=#f; unused stemplate=#<unspecified>\n"
+ "# g2p_null2PyNone: unsuccessful conversion: #f is not null\n"
+ "# g2p_list2List: unsuccessful conversion: #f is not a list\n"
+ "# g2p_pair2Tuple: unsuccessful conversion: #f is not a pair\n"
+ "# g2p_bool2Bool: successful conversion of #f into a Python Bool value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# DEBUG: g2p_list2List - processing item CAR(sobj)=1 CAR(stemp)='guile2python\n"
+ "# Entered g2p_apply: sobj=1 stemplate='guile2python\n"
+ "# guile2python: entry: seeking to convert sobj=1; unused stemplate=#<unspecified>\n"
+ "# g2p_null2PyNone: unsuccessful conversion: 1 is not null\n"
+ "# g2p_list2List: unsuccessful conversion: 1 is not a list\n"
+ "# g2p_pair2Tuple: unsuccessful conversion: 1 is not a pair\n"
+ "# g2p_bool2Bool: unsuccessful conversion: 1 is not a bool value\n"
+ "# g2p_num2Int: successful conversion of 1 into a Python Int value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# DEBUG: g2p_list2List - processing item CAR(sobj)=-5 CAR(stemp)='guile2python\n"
+ "# Entered g2p_apply: sobj=-5 stemplate='guile2python\n"
+ "# guile2python: entry: seeking to convert sobj=-5; unused stemplate=#<unspecified>\n"
+ "# g2p_null2PyNone: unsuccessful conversion: -5 is not null\n"
+ "# g2p_list2List: unsuccessful conversion: -5 is not a list\n"
+ "# g2p_pair2Tuple: unsuccessful conversion: -5 is not a pair\n"
+ "# g2p_bool2Bool: unsuccessful conversion: -5 is not a bool value\n"
+ "# g2p_num2Int: successful conversion of -5 into a Python Int value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# DEBUG: g2p_list2List - processing item CAR(sobj)=#\\P CAR(stemp)='guile2python\n"
+ "# Entered g2p_apply: sobj=#\\P stemplate='guile2python\n"
+ "# guile2python: entry: seeking to convert sobj=#\\P; unused stemplate=#<unspecified>\n"
+ "# g2p_null2PyNone: unsuccessful conversion: #\\P is not null\n"
+ "# g2p_list2List: unsuccessful conversion: #\\P is not a list\n"
+ "# g2p_pair2Tuple: unsuccessful conversion: #\\P is not a pair\n"
+ "# g2p_bool2Bool: unsuccessful conversion: #\\P is not a bool value\n"
+ "# g2p_num2Int: unsuccessful conversion: #\\P is not a num value\n"
+ "# g2p_bignum2Long: unsuccessful conversion: #\\P is not a bignum value\n"
+ "# g2p_real2Float: unsuccessful conversion: #\\P is not a real value\n"
+ "# g2p_complex2Complex: unsuccessful conversion: #\\P is not a complex value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# DEBUG: g2p_list2List - processing item CAR(sobj)=(quote symba) CAR(stemp)='guile2python\n"
+ "# Entered g2p_apply: sobj=(quote symba) stemplate='guile2python\n"
+ "# guile2python: entry: seeking to convert sobj=(quote symba); unused stemplate=#<unspecified>\n"
+ "# g2p_null2PyNone: unsuccessful conversion: (quote symba) is not null\n"
+ "# DEBUG: g2p_list2List - processing item CAR(sobj)=quote CAR(stemp)='guile2python\n"
+ "# Entered g2p_apply: sobj=quote stemplate='guile2python\n"
+ "# guile2python: entry: seeking to convert sobj=quote; unused stemplate=#<unspecified>\n"
+ "# g2p_null2PyNone: unsuccessful conversion: quote is not null\n"
+ "# g2p_list2List: unsuccessful conversion: quote is not a list\n"
+ "# g2p_pair2Tuple: unsuccessful conversion: quote is not a pair\n"
+ "# g2p_bool2Bool: unsuccessful conversion: quote is not a bool value\n"
+ "# g2p_num2Int: unsuccessful conversion: quote is not a num value\n"
+ "# g2p_bignum2Long: unsuccessful conversion: quote is not a bignum value\n"
+ "# g2p_real2Float: unsuccessful conversion: quote is not a real value\n"
+ "# g2p_complex2Complex: unsuccessful conversion: quote is not a complex value\n"
+ "# g2p_string2String: unsuccessful conversion: quote is not a string value\n"
+ "# g2p_symbol2String: successful conversion of quote into a Python String value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# DEBUG: g2p_list2List - processing item CAR(sobj)=symba CAR(stemp)='guile2python\n"
+ "# Entered g2p_apply: sobj=symba stemplate='guile2python\n"
+ "# guile2python: entry: seeking to convert sobj=symba; unused stemplate=#<unspecified>\n"
+ "# g2p_null2PyNone: unsuccessful conversion: symba is not null\n"
+ "# g2p_list2List: unsuccessful conversion: symba is not a list\n"
+ "# g2p_pair2Tuple: unsuccessful conversion: symba is not a pair\n"
+ "# g2p_bool2Bool: unsuccessful conversion: symba is not a bool value\n"
+ "# g2p_num2Int: unsuccessful conversion: symba is not a num value\n"
+ "# g2p_bignum2Long: unsuccessful conversion: symba is not a bignum value\n"
+ "# g2p_real2Float: unsuccessful conversion: symba is not a real value\n"
+ "# g2p_complex2Complex: unsuccessful conversion: symba is not a complex value\n"
+ "# g2p_string2String: unsuccessful conversion: symba is not a string value\n"
+ "# g2p_symbol2String: successful conversion of symba into a Python String value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_list2List: successful conversion of list () by template\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# DEBUG: g2p_list2List - processing item CAR(sobj)=1.0-1.0i CAR(stemp)='guile2python\n"
+ "# Entered g2p_apply: sobj=1.0-1.0i stemplate='guile2python\n"
+ "# guile2python: entry: seeking to convert sobj=1.0-1.0i; unused stemplate=#<unspecified>\n"
+ "# g2p_null2PyNone: unsuccessful conversion: 1.0-1.0i is not null\n"
+ "# g2p_list2List: unsuccessful conversion: 1.0-1.0i is not a list\n"
+ "# g2p_pair2Tuple: unsuccessful conversion: 1.0-1.0i is not a pair\n"
+ "# g2p_bool2Bool: unsuccessful conversion: 1.0-1.0i is not a bool value\n"
+ "# g2p_num2Int: unsuccessful conversion: 1.0-1.0i is not a num value\n"
+ "# g2p_bignum2Long: unsuccessful conversion: 1.0-1.0i is not a bignum value\n"
+ "# g2p_real2Float: unsuccessful conversion: 1.0-1.0i is not a real value\n"
+ "# g2p_complex2Complex: successful conversion of 1.0-1.0i into a Python Complex value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# DEBUG: g2p_list2List - processing item CAR(sobj)=3.125 CAR(stemp)='guile2python\n"
+ "# Entered g2p_apply: sobj=3.125 stemplate='guile2python\n"
+ "# guile2python: entry: seeking to convert sobj=3.125; unused stemplate=#<unspecified>\n"
+ "# g2p_null2PyNone: unsuccessful conversion: 3.125 is not null\n"
+ "# g2p_list2List: unsuccessful conversion: 3.125 is not a list\n"
+ "# g2p_pair2Tuple: unsuccessful conversion: 3.125 is not a pair\n"
+ "# g2p_bool2Bool: unsuccessful conversion: 3.125 is not a bool value\n"
+ "# g2p_num2Int: unsuccessful conversion: 3.125 is not a num value\n"
+ "# g2p_bignum2Long: unsuccessful conversion: 3.125 is not a bignum value\n"
+ "# g2p_real2Float: successful conversion of 3.125 into a Python Float value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_list2List: successful conversion of list () by template\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_list2Tuple: successful conversion of list () by template\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# guileassoc2pythondict: entry: seeking to convert sobj=() using stemplate=#<undefined>\n"
+ "# guileassoc2pythondict: successful conversion of ()\n"
+ "# p2g_apply: pobj=\"\\\"[None, True, False, 1, -5, 'P', ['quote', 'symba'], (1-1j), 3.125]\\\"\" smob-stemplate='python2guile\n"
+ "# python2guile: trying to convert pobj=\"\\\"[None, True, False, 1, -5, 'P', ['quote', 'symba'], (1-1j), 3.125]\\\"\" using stemplate=#<unspecified>\n"
+ "# p2g_None2SCM_EOL: pobj=\"\\\"[None, True, False, 1, -5, 'P', ['quote', 'symba'], (1-1j), 3.125]\\\"\" stemplate=#<unspecified>\n"
+ "# p2g_Bool2SCM_BOOL: failed to convert pobj=\"\\\"[None, True, False, 1, -5, 'P', ['quote', 'symba'], (1-1j), 3.125]\\\"\" using stemplate=#<unspecified>\n"
+ "# p2g_Int2num: failed to convert pobj=\"\\\"[None, True, False, 1, -5, 'P', ['quote', 'symba'], (1-1j), 3.125]\\\"\" using stemplate=#<unspecified>\n"
+ "# p2g_Long2bignum: failed to convert pobj=\"\\\"[None, True, False, 1, -5, 'P', ['quote', 'symba'], (1-1j), 3.125]\\\"\" using stemplate=#<unspecified>\n"
+ "# p2g_Float2real: failed to convert pobj=\"\\\"[None, True, False, 1, -5, 'P', ['quote', 'symba'], (1-1j), 3.125]\\\"\" using stemplate=#<unspecified>\n"
+ "# p2g_Complex2complex: failed to convert pobj=\"\\\"[None, True, False, 1, -5, 'P', ['quote', 'symba'], (1-1j), 3.125]\\\"\" using stemplate=#<unspecified>\n"
+ "# p2g_String2string: successful conversion of \"\\\"[None, True, False, 1, -5, 'P', ['quote', 'symba'], (1-1j), 3.125]\\\"\" into SCM\n")))
+(is-ok 2 "Verbosity/3 when successful"
+ expres2
+ (with-output-to-string
+ (thunk-invoke-python-repr '((() #t #f 1 -5 #\P 'symba 1-1i 3.125)))))
+
+(define expres3
+ (string-concatenate
+ (list
+ successful-verbose-3-report
+ "# Entered g2p_apply: sobj=((() () ())) stemplate=('g2p_list2Tuple ('g2p_list2Tuple 'g2p_null2Tuple0 'g2p_null2List0 'g2p_null2DictEmpty))\n"
+ "# g2p_list2Tuple - processing item CAR(sobj)=(() () ()) CAR(stemp)=('g2p_list2Tuple 'g2p_null2Tuple0 'g2p_null2List0 'g2p_null2DictEmpty)\n"
+ "# Entered g2p_apply: sobj=(() () ()) stemplate=('g2p_list2Tuple 'g2p_null2Tuple0 'g2p_null2List0 'g2p_null2DictEmpty)\n"
+ "# g2p_list2Tuple - processing item CAR(sobj)=() CAR(stemp)='g2p_null2Tuple0\n"
+ "# Entered g2p_apply: sobj=() stemplate='g2p_null2Tuple0\n"
+ "# g2p_null2Tuple0: successful conversion of () into Python ()\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_list2Tuple - processing item CAR(sobj)=() CAR(stemp)='g2p_null2List0\n"
+ "# Entered g2p_apply: sobj=() stemplate='g2p_null2List0\n"
+ "# g2p_null2List0: successful conversion of () into Python []\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_list2Tuple - processing item CAR(sobj)=() CAR(stemp)='g2p_null2DictEmpty\n"
+ "# Entered g2p_apply: sobj=() stemplate='g2p_null2DictEmpty\n"
+ "# g2p_null2DictEmpty: successful conversion of () into Python {}\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_list2Tuple: successful conversion of list () by template\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_list2Tuple: successful conversion of list () by template\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# guileassoc2pythondict: entry: seeking to convert sobj=() using stemplate=#<undefined>\n"
+ "# guileassoc2pythondict: successful conversion of ()\n"
+ "# p2g_apply: pobj=\"'((), [], {})'\" smob-stemplate='python2guile\n"
+ "# python2guile: trying to convert pobj=\"'((), [], {})'\" using stemplate=#<unspecified>\n"
+ "# p2g_None2SCM_EOL: pobj=\"'((), [], {})'\" stemplate=#<unspecified>\n"
+ "# p2g_Bool2SCM_BOOL: failed to convert pobj=\"'((), [], {})'\" using stemplate=#<unspecified>\n"
+ "# p2g_Int2num: failed to convert pobj=\"'((), [], {})'\" using stemplate=#<unspecified>\n"
+ "# p2g_Long2bignum: failed to convert pobj=\"'((), [], {})'\" using stemplate=#<unspecified>\n"
+ "# p2g_Float2real: failed to convert pobj=\"'((), [], {})'\" using stemplate=#<unspecified>\n"
+ "# p2g_Complex2complex: failed to convert pobj=\"'((), [], {})'\" using stemplate=#<unspecified>\n"
+ "# p2g_String2string: successful conversion of \"'((), [], {})'\" into SCM\n")))
+(is-ok 3 "g2p* verbosity/3 coverage"
+ expres3
+ (with-output-to-string
+ (thunk-invoke-python-repr
+ '((() () ()))
+ (list g2p_list2Tuple (list g2p_list2Tuple g2p_null2Tuple0 g2p_null2List0 g2p_null2DictEmpty)))))
+
+(define expres4
+ (string-concatenate
+ (list
+ successful-verbose-3-report
+ "# Entered g2p_apply: sobj=((1000000 1000000000000)) stemplate=('g2p_list2Tuple ('g2p_list2List ('g2p_leaf 'g2p_bignum2Long 'g2p_num2Int)))\n"
+ "# g2p_list2Tuple - processing item CAR(sobj)=(1000000 1000000000000) CAR(stemp)=('g2p_list2List ('g2p_leaf 'g2p_bignum2Long 'g2p_num2Int))\n"
+ "# Entered g2p_apply: sobj=(1000000 1000000000000) stemplate=('g2p_list2List ('g2p_leaf 'g2p_bignum2Long 'g2p_num2Int))\n"
+ "# DEBUG: g2p_list2List - processing item CAR(sobj)=1000000 CAR(stemp)=('g2p_leaf 'g2p_bignum2Long 'g2p_num2Int)\n"
+ "# Entered g2p_apply: sobj=1000000 stemplate=('g2p_leaf 'g2p_bignum2Long 'g2p_num2Int)\n"
+ "# Entered g2p_leaf: sobj=1000000 stemplate=('g2p_bignum2Long 'g2p_num2Int)\n"
+ "# g2p_leaf: trying another stemplate 'g2p_bignum2Long on sobj\n"
+ "# Entered g2p_apply: sobj=1000000 stemplate='g2p_bignum2Long\n"
+ "# g2p_bignum2Long: unsuccessful conversion: 1000000 is not a bignum value\n"
+ "# Leaving g2p_apply: with null result\n"
+ "# g2p_leaf: trying another stemplate 'g2p_num2Int on sobj\n"
+ "# Entered g2p_apply: sobj=1000000 stemplate='g2p_num2Int\n"
+ "# g2p_num2Int: successful conversion of 1000000 into a Python Int value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_leaf: successful conversion\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# DEBUG: g2p_list2List - processing item CAR(sobj)=1000000000000 CAR(stemp)=('g2p_leaf 'g2p_bignum2Long 'g2p_num2Int)\n"
+ "# Entered g2p_apply: sobj=1000000000000 stemplate=('g2p_leaf 'g2p_bignum2Long 'g2p_num2Int)\n"
+ "# Entered g2p_leaf: sobj=1000000000000 stemplate=('g2p_bignum2Long 'g2p_num2Int)\n"
+ "# g2p_leaf: trying another stemplate 'g2p_bignum2Long on sobj\n"
+ "# Entered g2p_apply: sobj=1000000000000 stemplate='g2p_bignum2Long\n"
+ "# g2p_bignum2Long: successful conversion of 1000000000000 into a Python Long value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_leaf: successful conversion\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_list2List: successful conversion of list () by template\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_list2Tuple: successful conversion of list () by template\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# guileassoc2pythondict: entry: seeking to convert sobj=() using stemplate=#<undefined>\n"
+ "# guileassoc2pythondict: successful conversion of ()\n"
+ "# p2g_apply: pobj=\"'[1000000, 1000000000000L]'\" smob-stemplate='python2guile\n"
+ "# python2guile: trying to convert pobj=\"'[1000000, 1000000000000L]'\" using stemplate=#<unspecified>\n"
+ "# p2g_None2SCM_EOL: pobj=\"'[1000000, 1000000000000L]'\" stemplate=#<unspecified>\n"
+ "# p2g_Bool2SCM_BOOL: failed to convert pobj=\"'[1000000, 1000000000000L]'\" using stemplate=#<unspecified>\n"
+ "# p2g_Int2num: failed to convert pobj=\"'[1000000, 1000000000000L]'\" using stemplate=#<unspecified>\n"
+ "# p2g_Long2bignum: failed to convert pobj=\"'[1000000, 1000000000000L]'\" using stemplate=#<unspecified>\n"
+ "# p2g_Float2real: failed to convert pobj=\"'[1000000, 1000000000000L]'\" using stemplate=#<unspecified>\n"
+ "# p2g_Complex2complex: failed to convert pobj=\"'[1000000, 1000000000000L]'\" using stemplate=#<unspecified>\n"
+ "# p2g_String2string: successful conversion of \"'[1000000, 1000000000000L]'\" into SCM\n")))
+(is-ok 4 "g2p* verbosity/3 coverage - bignums,list2Tuple,list2List"
+ expres4
+ (with-output-to-string
+ (thunk-invoke-python-repr
+ '((1000000 1000000000000))
+ (list g2p_list2Tuple (list g2p_list2List (list g2p_leaf g2p_bignum2Long g2p_num2Int))))))
+
+(define expres5
+ (string-concatenate
+ (list
+ successful-verbose-3-report
+ "# Entered g2p_apply: sobj=(((p1at . \"p1bt\") (#:p2al . p2bl))) stemplate=('g2p_list2Tuple ('g2p_list2Tuple ('g2p_pair2Tuple 'guile2python . 'guile2python) ('g2p_pair2List 'guile2python . 'guile2python)))\n"
+ "# g2p_list2Tuple - processing item CAR(sobj)=((p1at . \"p1bt\") (#:p2al . p2bl)) CAR(stemp)=('g2p_list2Tuple ('g2p_pair2Tuple 'guile2python . 'guile2python) ('g2p_pair2List 'guile2python . 'guile2python))\n"
+ "# Entered g2p_apply: sobj=((p1at . \"p1bt\") (#:p2al . p2bl)) stemplate=('g2p_list2Tuple ('g2p_pair2Tuple 'guile2python . 'guile2python) ('g2p_pair2List 'guile2python . 'guile2python))\n"
+ "# g2p_list2Tuple - processing item CAR(sobj)=(p1at . \"p1bt\") CAR(stemp)=('g2p_pair2Tuple 'guile2python . 'guile2python)\n"
+ "# Entered g2p_apply: sobj=(p1at . \"p1bt\") stemplate=('g2p_pair2Tuple 'guile2python . 'guile2python)\n"
+ "# Entered g2p_apply: sobj=p1at stemplate='guile2python\n"
+ "# guile2python: entry: seeking to convert sobj=p1at; unused stemplate=#<unspecified>\n"
+ "# g2p_null2PyNone: unsuccessful conversion: p1at is not null\n"
+ "# g2p_list2List: unsuccessful conversion: p1at is not a list\n"
+ "# g2p_pair2Tuple: unsuccessful conversion: p1at is not a pair\n"
+ "# g2p_bool2Bool: unsuccessful conversion: p1at is not a bool value\n"
+ "# g2p_num2Int: unsuccessful conversion: p1at is not a num value\n"
+ "# g2p_bignum2Long: unsuccessful conversion: p1at is not a bignum value\n"
+ "# g2p_real2Float: unsuccessful conversion: p1at is not a real value\n"
+ "# g2p_complex2Complex: unsuccessful conversion: p1at is not a complex value\n"
+ "# g2p_string2String: unsuccessful conversion: p1at is not a string value\n"
+ "# g2p_symbol2String: successful conversion of p1at into a Python String value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# Entered g2p_apply: sobj=\"p1bt\" stemplate='guile2python\n"
+ "# guile2python: entry: seeking to convert sobj=\"p1bt\"; unused stemplate=#<unspecified>\n"
+ "# g2p_null2PyNone: unsuccessful conversion: \"p1bt\" is not null\n"
+ "# g2p_list2List: unsuccessful conversion: \"p1bt\" is not a list\n"
+ "# g2p_pair2Tuple: unsuccessful conversion: \"p1bt\" is not a pair\n"
+ "# g2p_bool2Bool: unsuccessful conversion: \"p1bt\" is not a bool value\n"
+ "# g2p_num2Int: unsuccessful conversion: \"p1bt\" is not a num value\n"
+ "# g2p_bignum2Long: unsuccessful conversion: \"p1bt\" is not a bignum value\n"
+ "# g2p_real2Float: unsuccessful conversion: \"p1bt\" is not a real value\n"
+ "# g2p_complex2Complex: unsuccessful conversion: \"p1bt\" is not a complex value\n"
+ "# g2p_string2String: successful conversion of \"p1bt\" into a Python String value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_pair2Tuple: successful conversion of (p1at . \"p1bt\") into a Python 2-Tuple\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_list2Tuple - processing item CAR(sobj)=(#:p2al . p2bl) CAR(stemp)=('g2p_pair2List 'guile2python . 'guile2python)\n"
+ "# Entered g2p_apply: sobj=(#:p2al . p2bl) stemplate=('g2p_pair2List 'guile2python . 'guile2python)\n"
+ "# Entered g2p_apply: sobj=#:p2al stemplate='guile2python\n"
+ "# guile2python: entry: seeking to convert sobj=#:p2al; unused stemplate=#<unspecified>\n"
+ "# g2p_null2PyNone: unsuccessful conversion: #:p2al is not null\n"
+ "# g2p_list2List: unsuccessful conversion: #:p2al is not a list\n"
+ "# g2p_pair2Tuple: unsuccessful conversion: #:p2al is not a pair\n"
+ "# g2p_bool2Bool: unsuccessful conversion: #:p2al is not a bool value\n"
+ "# g2p_num2Int: unsuccessful conversion: #:p2al is not a num value\n"
+ "# g2p_bignum2Long: unsuccessful conversion: #:p2al is not a bignum value\n"
+ "# g2p_real2Float: unsuccessful conversion: #:p2al is not a real value\n"
+ "# g2p_complex2Complex: unsuccessful conversion: #:p2al is not a complex value\n"
+ "# g2p_string2String: unsuccessful conversion: #:p2al is not a string value\n"
+ "# g2p_symbol2String: unsuccessful conversion: #:p2al is not a symbol value\n"
+ "# g2p_keyword2String: successful conversion of #:p2al into a Python String value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# Entered g2p_apply: sobj=p2bl stemplate='guile2python\n"
+ "# guile2python: entry: seeking to convert sobj=p2bl; unused stemplate=#<unspecified>\n"
+ "# g2p_null2PyNone: unsuccessful conversion: p2bl is not null\n"
+ "# g2p_list2List: unsuccessful conversion: p2bl is not a list\n"
+ "# g2p_pair2Tuple: unsuccessful conversion: p2bl is not a pair\n"
+ "# g2p_bool2Bool: unsuccessful conversion: p2bl is not a bool value\n"
+ "# g2p_num2Int: unsuccessful conversion: p2bl is not a num value\n"
+ "# g2p_bignum2Long: unsuccessful conversion: p2bl is not a bignum value\n"
+ "# g2p_real2Float: unsuccessful conversion: p2bl is not a real value\n"
+ "# g2p_complex2Complex: unsuccessful conversion: p2bl is not a complex value\n"
+ "# g2p_string2String: unsuccessful conversion: p2bl is not a string value\n"
+ "# g2p_symbol2String: successful conversion of p2bl into a Python String value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_pair2List: successful conversion of (#:p2al . p2bl) into a Python 2-List\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_list2Tuple: successful conversion of list () by template\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_list2Tuple: successful conversion of list () by template\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# guileassoc2pythondict: entry: seeking to convert sobj=() using stemplate=#<undefined>\n"
+ "# guileassoc2pythondict: successful conversion of ()\n"
+ "# p2g_apply: pobj=\"\\\"(('p1at', 'p1bt'), ['p2al', 'p2bl'])\\\"\" smob-stemplate='python2guile\n"
+ "# python2guile: trying to convert pobj=\"\\\"(('p1at', 'p1bt'), ['p2al', 'p2bl'])\\\"\" using stemplate=#<unspecified>\n"
+ "# p2g_None2SCM_EOL: pobj=\"\\\"(('p1at', 'p1bt'), ['p2al', 'p2bl'])\\\"\" stemplate=#<unspecified>\n"
+ "# p2g_Bool2SCM_BOOL: failed to convert pobj=\"\\\"(('p1at', 'p1bt'), ['p2al', 'p2bl'])\\\"\" using stemplate=#<unspecified>\n"
+ "# p2g_Int2num: failed to convert pobj=\"\\\"(('p1at', 'p1bt'), ['p2al', 'p2bl'])\\\"\" using stemplate=#<unspecified>\n"
+ "# p2g_Long2bignum: failed to convert pobj=\"\\\"(('p1at', 'p1bt'), ['p2al', 'p2bl'])\\\"\" using stemplate=#<unspecified>\n"
+ "# p2g_Float2real: failed to convert pobj=\"\\\"(('p1at', 'p1bt'), ['p2al', 'p2bl'])\\\"\" using stemplate=#<unspecified>\n"
+ "# p2g_Complex2complex: failed to convert pobj=\"\\\"(('p1at', 'p1bt'), ['p2al', 'p2bl'])\\\"\" using stemplate=#<unspecified>\n"
+ "# p2g_String2string: successful conversion of \"\\\"(('p1at', 'p1bt'), ['p2al', 'p2bl'])\\\"\" into SCM\n")))
+(is-ok 5 "g2p* verbosity/3 coverage - pair2Tuple,pair2List"
+ expres5
+ (with-output-to-string
+ (thunk-invoke-python-repr
+ '(((p1at . "p1bt") (#:p2al . p2bl)))
+ (list g2p_list2Tuple (list g2p_list2Tuple (cons g2p_pair2Tuple (cons guile2python guile2python)) (cons g2p_pair2List (cons guile2python guile2python)))))))
+
+(define substitute-hex-addresses-for-gggggggg
+ (lambda (strarg)
+ (regexp-substitute/global #f
+ "0x[0-9a-f]{8}"
+ strarg
+ 'pre "0xgggggggg" 'post)))
+
+(define expres6
+ (string-concatenate
+ (list
+ successful-verbose-3-report
+ "# Entered g2p_apply: sobj=(((python-eval <function func at 0xgggggggg> #t) ((1 . \"one\") (2 . \"two\") (3 . \"three\")))) stemplate=('g2p_list2Tuple ('g2p_list2List 'g2p_opaque2Object ('g2p_alist2Dict ('g2p_num2Int . 'g2p_string2String))))\n"
+ "# g2p_list2Tuple - processing item CAR(sobj)=((python-eval <function func at 0xgggggggg> #t) ((1 . \"one\") (2 . \"two\") (3 . \"three\"))) CAR(stemp)=('g2p_list2List 'g2p_opaque2Object ('g2p_alist2Dict ('g2p_num2Int . 'g2p_string2String)))\n"
+ "# Entered g2p_apply: sobj=((python-eval <function func at 0xgggggggg> #t) ((1 . \"one\") (2 . \"two\") (3 . \"three\"))) stemplate=('g2p_list2List 'g2p_opaque2Object ('g2p_alist2Dict ('g2p_num2Int . 'g2p_string2String)))\n"
+ "# DEBUG: g2p_list2List - processing item CAR(sobj)=(python-eval <function func at 0xgggggggg> #t) CAR(stemp)='g2p_opaque2Object\n"
+ "# Entered g2p_apply: sobj=(python-eval <function func at 0xgggggggg> #t) stemplate='g2p_opaque2Object\n"
+ "# g2p_opaque2Object: the Python object inside opaque pysmob (python-eval <function func at 0xgggggggg> #t) is unwrapped\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# DEBUG: g2p_list2List - processing item CAR(sobj)=((1 . \"one\") (2 . \"two\") (3 . \"three\")) CAR(stemp)=('g2p_alist2Dict ('g2p_num2Int . 'g2p_string2String))\n"
+ "# Entered g2p_apply: sobj=((1 . \"one\") (2 . \"two\") (3 . \"three\")) stemplate=('g2p_alist2Dict ('g2p_num2Int . 'g2p_string2String))\n"
+ "# g2p_alist2Dict sobj=((1 . \"one\") (2 . \"two\") (3 . \"three\")) stemplate=(('g2p_num2Int . 'g2p_string2String))\n"
+ "# Entered g2p_apply: sobj=1 stemplate='g2p_num2Int\n"
+ "# g2p_num2Int: successful conversion of 1 into a Python Int value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# Entered g2p_apply: sobj=\"one\" stemplate='g2p_string2String\n"
+ "# g2p_string2String: successful conversion of \"one\" into a Python String value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# Entered g2p_apply: sobj=2 stemplate='g2p_num2Int\n"
+ "# g2p_num2Int: successful conversion of 2 into a Python Int value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# Entered g2p_apply: sobj=\"two\" stemplate='g2p_string2String\n"
+ "# g2p_string2String: successful conversion of \"two\" into a Python String value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# Entered g2p_apply: sobj=3 stemplate='g2p_num2Int\n"
+ "# g2p_num2Int: successful conversion of 3 into a Python Int value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# Entered g2p_apply: sobj=\"three\" stemplate='g2p_string2String\n"
+ "# g2p_string2String: successful conversion of \"three\" into a Python String value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_alist2Dict: successful conversion\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_list2List: successful conversion of list () by template\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_list2Tuple: successful conversion of list () by template\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# guileassoc2pythondict: entry: seeking to convert sobj=() using stemplate=#<undefined>\n"
+ "# guileassoc2pythondict: successful conversion of ()\n"
+ "# p2g_apply: pobj=\"\\\"[<function func at 0xgggggggg>, {1: 'one', 2: 'two', 3: 'three'}]\\\"\" smob-stemplate='python2guile\n"
+ "# python2guile: trying to convert pobj=\"\\\"[<function func at 0xgggggggg>, {1: 'one', 2: 'two', 3: 'three'}]\\\"\" using stemplate=#<unspecified>\n"
+ "# p2g_None2SCM_EOL: pobj=\"\\\"[<function func at 0xgggggggg>, {1: 'one', 2: 'two', 3: 'three'}]\\\"\" stemplate=#<unspecified>\n"
+ "# p2g_Bool2SCM_BOOL: failed to convert pobj=\"\\\"[<function func at 0xgggggggg>, {1: 'one', 2: 'two', 3: 'three'}]\\\"\" using stemplate=#<unspecified>\n"
+ "# p2g_Int2num: failed to convert pobj=\"\\\"[<function func at 0xgggggggg>, {1: 'one', 2: 'two', 3: 'three'}]\\\"\" using stemplate=#<unspecified>\n"
+ "# p2g_Long2bignum: failed to convert pobj=\"\\\"[<function func at 0xgggggggg>, {1: 'one', 2: 'two', 3: 'three'}]\\\"\" using stemplate=#<unspecified>\n"
+ "# p2g_Float2real: failed to convert pobj=\"\\\"[<function func at 0xgggggggg>, {1: 'one', 2: 'two', 3: 'three'}]\\\"\" using stemplate=#<unspecified>\n"
+ "# p2g_Complex2complex: failed to convert pobj=\"\\\"[<function func at 0xgggggggg>, {1: 'one', 2: 'two', 3: 'three'}]\\\"\" using stemplate=#<unspecified>\n"
+ "# p2g_String2string: successful conversion of \"\\\"[<function func at 0xgggggggg>, {1: 'one', 2: 'two', 3: 'three'}]\\\"\" into SCM\n")))
+(python-eval "def func(a):\n print a\n" #f)
+(define opaqueobj (python-eval "func" #t))
+(define run-test-opaque
+ (lambda ()
+ (with-output-to-string
+ (thunk-invoke-python-repr
+ (list (list opaqueobj '((1 . "one")(2 . "two")(3 . "three"))))
+ (list g2p_list2Tuple (list g2p_list2List g2p_opaque2Object (cons g2p_alist2Dict (list (cons g2p_num2Int g2p_string2String)))))))))
+(is-ok 6 "g2p* verbosity/3 coverage - opaque2Object,g2p_alist2Dict"
+ expres6
+ (substitute-hex-addresses-for-gggggggg
+ (run-test-opaque)))
+
+; Additional tests
+
+(define expres7
+ (string-concatenate
+ (list
+ successful-verbose-3-report
+ "# Entered g2p_apply: sobj=((#t 5)) stemplate=('g2p_list2Tuple ('g2p_list2List ('g2p_leaf 'g2p_num2Int 'g2p_bool2Bool)))\n"
+ "# g2p_list2Tuple - processing item CAR(sobj)=(#t 5) CAR(stemp)=('g2p_list2List ('g2p_leaf 'g2p_num2Int 'g2p_bool2Bool))\n"
+ "# Entered g2p_apply: sobj=(#t 5) stemplate=('g2p_list2List ('g2p_leaf 'g2p_num2Int 'g2p_bool2Bool))\n"
+ "# DEBUG: g2p_list2List - processing item CAR(sobj)=#t CAR(stemp)=('g2p_leaf 'g2p_num2Int 'g2p_bool2Bool)\n"
+ "# Entered g2p_apply: sobj=#t stemplate=('g2p_leaf 'g2p_num2Int 'g2p_bool2Bool)\n"
+ "# Entered g2p_leaf: sobj=#t stemplate=('g2p_num2Int 'g2p_bool2Bool)\n"
+ "# g2p_leaf: trying another stemplate 'g2p_num2Int on sobj\n"
+ "# Entered g2p_apply: sobj=#t stemplate='g2p_num2Int\n"
+ "# g2p_num2Int: unsuccessful conversion: #t is not a num value\n"
+ "# Leaving g2p_apply: with null result\n"
+ "# g2p_leaf: trying another stemplate 'g2p_bool2Bool on sobj\n"
+ "# Entered g2p_apply: sobj=#t stemplate='g2p_bool2Bool\n"
+ "# g2p_bool2Bool: successful conversion of #t into a Python Bool value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_leaf: successful conversion\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# DEBUG: g2p_list2List - processing item CAR(sobj)=5 CAR(stemp)=('g2p_leaf 'g2p_num2Int 'g2p_bool2Bool)\n"
+ "# Entered g2p_apply: sobj=5 stemplate=('g2p_leaf 'g2p_num2Int 'g2p_bool2Bool)\n"
+ "# Entered g2p_leaf: sobj=5 stemplate=('g2p_num2Int 'g2p_bool2Bool)\n"
+ "# g2p_leaf: trying another stemplate 'g2p_num2Int on sobj\n"
+ "# Entered g2p_apply: sobj=5 stemplate='g2p_num2Int\n"
+ "# g2p_num2Int: successful conversion of 5 into a Python Int value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_leaf: successful conversion\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_list2List: successful conversion of list () by template\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_list2Tuple: successful conversion of list () by template\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# guileassoc2pythondict: entry: seeking to convert sobj=() using stemplate=#<undefined>\n"
+ "# guileassoc2pythondict: successful conversion of ()\n"
+ "# p2g_apply: pobj=\"'[True, 5]'\" smob-stemplate='python2guile\n"
+ "# python2guile: trying to convert pobj=\"'[True, 5]'\" using stemplate=#<unspecified>\n"
+ "# p2g_None2SCM_EOL: pobj=\"'[True, 5]'\" stemplate=#<unspecified>\n"
+ "# p2g_Bool2SCM_BOOL: failed to convert pobj=\"'[True, 5]'\" using stemplate=#<unspecified>\n"
+ "# p2g_Int2num: failed to convert pobj=\"'[True, 5]'\" using stemplate=#<unspecified>\n"
+ "# p2g_Long2bignum: failed to convert pobj=\"'[True, 5]'\" using stemplate=#<unspecified>\n"
+ "# p2g_Float2real: failed to convert pobj=\"'[True, 5]'\" using stemplate=#<unspecified>\n"
+ "# p2g_Complex2complex: failed to convert pobj=\"'[True, 5]'\" using stemplate=#<unspecified>\n"
+ "# p2g_String2string: successful conversion of \"'[True, 5]'\" into SCM\n")))
+(is-ok 7 "g2p_leaf verbosity/3 coverage - all values recognized by leaf"
+ expres7
+ (with-output-to-string
+ (thunk-invoke-python-repr
+ '((#t 5 ))
+ (list g2p_list2Tuple (list g2p_list2List (list g2p_leaf g2p_num2Int g2p_bool2Bool))))))
+
+; Run thunk-invoke-python-repr under catch harness.
+(define catch-thunk-invoke-python-repr
+ (lambda (arg . template)
+ (lambda ()
+ (catch
+ #t
+ (lambda ()
+ (if (null? template)
+ (python-apply '("__builtin__" "repr") arg '())
+ (python-apply '("__builtin__" "repr") arg '() (car template))))
+ (lambda (key . args2) (object->string (list key args2)))))))
+
+(define expres8
+ (string-concatenate
+ (list
+ successful-verbose-3-report
+ "# Entered g2p_apply: sobj=((3.1 5)) stemplate=('g2p_list2Tuple ('g2p_list2List ('g2p_leaf 'g2p_num2Int 'g2p_bool2Bool)))\n"
+ "# g2p_list2Tuple - processing item CAR(sobj)=(3.1 5) CAR(stemp)=('g2p_list2List ('g2p_leaf 'g2p_num2Int 'g2p_bool2Bool))\n"
+ "# Entered g2p_apply: sobj=(3.1 5) stemplate=('g2p_list2List ('g2p_leaf 'g2p_num2Int 'g2p_bool2Bool))\n"
+ "# DEBUG: g2p_list2List - processing item CAR(sobj)=3.1 CAR(stemp)=('g2p_leaf 'g2p_num2Int 'g2p_bool2Bool)\n"
+ "# Entered g2p_apply: sobj=3.1 stemplate=('g2p_leaf 'g2p_num2Int 'g2p_bool2Bool)\n"
+ "# Entered g2p_leaf: sobj=3.1 stemplate=('g2p_num2Int 'g2p_bool2Bool)\n"
+ "# g2p_leaf: trying another stemplate 'g2p_num2Int on sobj\n"
+ "# Entered g2p_apply: sobj=3.1 stemplate='g2p_num2Int\n"
+ "# g2p_num2Int: unsuccessful conversion: 3.1 is not a num value\n"
+ "# Leaving g2p_apply: with null result\n"
+ "# g2p_leaf: trying another stemplate 'g2p_bool2Bool on sobj\n"
+ "# Entered g2p_apply: sobj=3.1 stemplate='g2p_bool2Bool\n"
+ "# g2p_bool2Bool: unsuccessful conversion: 3.1 is not a bool value\n"
+ "# Leaving g2p_apply: with null result\n"
+ "# g2p_leaf: unsuccessful conversion, no stemplate fit the sobj\n"
+ "# Leaving g2p_apply: with null result\n"
+ "# g2p_list2List: unsuccessful conversion of element 0: 3.1 does not match personalized template\n"
+ "# Leaving g2p_apply: with null result\n"
+ "# g2p_list2Tuple: unsuccessful conversion of element 0: (3.1 5) does not match personalized template\n"
+ "# Leaving g2p_apply: with null result\n")))
+(is-ok 8 "g2p_leaf verbosity/3 coverage - first value not recognized by leaf"
+ expres8
+ (with-output-to-string
+ (catch-thunk-invoke-python-repr
+ '((3.1 5))
+ (list g2p_list2Tuple (list g2p_list2List (list g2p_leaf g2p_num2Int g2p_bool2Bool))))))
+
+(define expres9
+ (string-concatenate
+ (list
+ successful-verbose-3-report
+ "# Entered g2p_apply: sobj=((#t \"string\")) stemplate=('g2p_list2Tuple ('g2p_list2List ('g2p_leaf 'g2p_num2Int 'g2p_bool2Bool)))\n"
+ "# g2p_list2Tuple - processing item CAR(sobj)=(#t \"string\") CAR(stemp)=('g2p_list2List ('g2p_leaf 'g2p_num2Int 'g2p_bool2Bool))\n"
+ "# Entered g2p_apply: sobj=(#t \"string\") stemplate=('g2p_list2List ('g2p_leaf 'g2p_num2Int 'g2p_bool2Bool))\n"
+ "# DEBUG: g2p_list2List - processing item CAR(sobj)=#t CAR(stemp)=('g2p_leaf 'g2p_num2Int 'g2p_bool2Bool)\n"
+ "# Entered g2p_apply: sobj=#t stemplate=('g2p_leaf 'g2p_num2Int 'g2p_bool2Bool)\n"
+ "# Entered g2p_leaf: sobj=#t stemplate=('g2p_num2Int 'g2p_bool2Bool)\n"
+ "# g2p_leaf: trying another stemplate 'g2p_num2Int on sobj\n"
+ "# Entered g2p_apply: sobj=#t stemplate='g2p_num2Int\n"
+ "# g2p_num2Int: unsuccessful conversion: #t is not a num value\n"
+ "# Leaving g2p_apply: with null result\n"
+ "# g2p_leaf: trying another stemplate 'g2p_bool2Bool on sobj\n"
+ "# Entered g2p_apply: sobj=#t stemplate='g2p_bool2Bool\n"
+ "# g2p_bool2Bool: successful conversion of #t into a Python Bool value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_leaf: successful conversion\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# DEBUG: g2p_list2List - processing item CAR(sobj)=\"string\" CAR(stemp)=('g2p_leaf 'g2p_num2Int 'g2p_bool2Bool)\n"
+ "# Entered g2p_apply: sobj=\"string\" stemplate=('g2p_leaf 'g2p_num2Int 'g2p_bool2Bool)\n"
+ "# Entered g2p_leaf: sobj=\"string\" stemplate=('g2p_num2Int 'g2p_bool2Bool)\n"
+ "# g2p_leaf: trying another stemplate 'g2p_num2Int on sobj\n"
+ "# Entered g2p_apply: sobj=\"string\" stemplate='g2p_num2Int\n"
+ "# g2p_num2Int: unsuccessful conversion: \"string\" is not a num value\n"
+ "# Leaving g2p_apply: with null result\n"
+ "# g2p_leaf: trying another stemplate 'g2p_bool2Bool on sobj\n"
+ "# Entered g2p_apply: sobj=\"string\" stemplate='g2p_bool2Bool\n"
+ "# g2p_bool2Bool: unsuccessful conversion: \"string\" is not a bool value\n"
+ "# Leaving g2p_apply: with null result\n"
+ "# g2p_leaf: unsuccessful conversion, no stemplate fit the sobj\n"
+ "# Leaving g2p_apply: with null result\n"
+ "# g2p_list2List: unsuccessful conversion of element 1: \"string\" does not match personalized template\n"
+ "# Leaving g2p_apply: with null result\n"
+ "# g2p_list2Tuple: unsuccessful conversion of element 0: (#t \"string\") does not match personalized template\n"
+ "# Leaving g2p_apply: with null result\n")))
+(is-ok 9 "g2p_leaf verbosity/3 coverage - last value not recognized by leaf"
+ expres9
+ (with-output-to-string
+ (catch-thunk-invoke-python-repr
+ '((#t "string"))
+ (list g2p_list2Tuple (list g2p_list2List (list g2p_leaf g2p_num2Int g2p_bool2Bool))))))
+
+(define saved-verbosity (pyguile-verbosity-set! 0))
+
+(define catch-test-apply
+ (lambda (func args kws . targ)
+ (catch #t
+ (lambda () (python-apply func args kws (car targ)))
+ (lambda (key . args2) (object->string (list key args2))))))
+
+(is-ok 10 "g2p_null2* validate test - non-null values"
+ "(misc-error (\"python-apply\" \"positional arguments conversion failure (~S)\" ((\"string\")) #f))"
+ (catch-test-apply '("__builtin__" "repr") '("string") '()
+ (list g2p_list2Tuple (list g2p_leaf g2p_null2Tuple0 g2p_null2List0 g2p_null2DictEmpty))))
+
+(pyguile-verbosity-set! saved-verbosity)
+
+(define expres11
+ (string-concatenate
+ (list
+ successful-verbose-3-report
+ "# Entered g2p_apply: sobj=(\"string\") stemplate=('g2p_list2Tuple ('g2p_leaf 'g2p_null2Tuple0 'g2p_null2List0 'g2p_null2DictEmpty))\n"
+ "# g2p_list2Tuple - processing item CAR(sobj)=\"string\" CAR(stemp)=('g2p_leaf 'g2p_null2Tuple0 'g2p_null2List0 'g2p_null2DictEmpty)\n"
+ "# Entered g2p_apply: sobj=\"string\" stemplate=('g2p_leaf 'g2p_null2Tuple0 'g2p_null2List0 'g2p_null2DictEmpty)\n"
+ "# Entered g2p_leaf: sobj=\"string\" stemplate=('g2p_null2Tuple0 'g2p_null2List0 'g2p_null2DictEmpty)\n"
+ "# g2p_leaf: trying another stemplate 'g2p_null2Tuple0 on sobj\n"
+ "# Entered g2p_apply: sobj=\"string\" stemplate='g2p_null2Tuple0\n"
+ "# g2p_null2Tuple0: unsuccessful conversion: \"string\" is not null\n"
+ "# Leaving g2p_apply: with null result\n"
+ "# g2p_leaf: trying another stemplate 'g2p_null2List0 on sobj\n"
+ "# Entered g2p_apply: sobj=\"string\" stemplate='g2p_null2List0\n"
+ "# g2p_null2List0: unsuccessful conversion: \"string\" is not null\n"
+ "# Leaving g2p_apply: with null result\n"
+ "# g2p_leaf: trying another stemplate 'g2p_null2DictEmpty on sobj\n"
+ "# Entered g2p_apply: sobj=\"string\" stemplate='g2p_null2DictEmpty\n"
+ "# g2p_null2DictEmpty: unsuccessful conversion: \"string\" is not null\n"
+ "# Leaving g2p_apply: with null result\n"
+ "# g2p_leaf: unsuccessful conversion, no stemplate fit the sobj\n"
+ "# Leaving g2p_apply: with null result\n"
+ "# g2p_list2Tuple: unsuccessful conversion of element 0: \"string\" does not match personalized template\n"
+ "# Leaving g2p_apply: with null result\n")))
+(is-ok 11 "g2p_null2* verbosity/3 coverage - non-null values"
+ expres11
+ (with-output-to-string
+ (catch-thunk-invoke-python-repr
+ '("string")
+ (list g2p_list2Tuple (list g2p_leaf g2p_null2Tuple0 g2p_null2List0 g2p_null2DictEmpty)))))
+
+
+(define expres12
+ (string-concatenate
+ (list
+ successful-verbose-3-report
+ "# Entered g2p_apply: sobj=(()) stemplate=('g2p_list2Tuple ('g2p_leaf . 'g2p_null2Tuple0))\n"
+ "# g2p_list2Tuple - processing item CAR(sobj)=() CAR(stemp)=('g2p_leaf . 'g2p_null2Tuple0)\n"
+ "# Entered g2p_apply: sobj=() stemplate=('g2p_leaf . 'g2p_null2Tuple0)\n"
+ "# Entered g2p_leaf: sobj=() stemplate='g2p_null2Tuple0\n"
+ "# g2p_null2Tuple0: successful conversion of () into Python ()\n"
+ "# Leaving g2p_leaf, after G2P_SMOBP conversion, with non-null result\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_list2Tuple: successful conversion of list () by template\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# guileassoc2pythondict: entry: seeking to convert sobj=() using stemplate=#<undefined>\n"
+ "# guileassoc2pythondict: successful conversion of ()\n"
+ "# p2g_apply: pobj=\"'()'\" smob-stemplate='python2guile\n"
+ "# python2guile: trying to convert pobj=\"'()'\" using stemplate=#<unspecified>\n"
+ "# p2g_None2SCM_EOL: pobj=\"'()'\" stemplate=#<unspecified>\n"
+ "# p2g_Bool2SCM_BOOL: failed to convert pobj=\"'()'\" using stemplate=#<unspecified>\n"
+ "# p2g_Int2num: failed to convert pobj=\"'()'\" using stemplate=#<unspecified>\n"
+ "# p2g_Long2bignum: failed to convert pobj=\"'()'\" using stemplate=#<unspecified>\n"
+ "# p2g_Float2real: failed to convert pobj=\"'()'\" using stemplate=#<unspecified>\n"
+ "# p2g_Complex2complex: failed to convert pobj=\"'()'\" using stemplate=#<unspecified>\n"
+ "# p2g_String2string: successful conversion of \"'()'\" into SCM\n")))
+(is-ok 12 "g2p_leaf verbosity/3 coverage - G2P_SMOB, successful"
+ expres12
+ (with-output-to-string
+ (catch-thunk-invoke-python-repr
+ '(())
+ (list g2p_list2Tuple (cons g2p_leaf g2p_null2Tuple0)))))
+
+(define expres13
+ (string-concatenate
+ (list
+ successful-verbose-3-report
+ "# Entered g2p_apply: sobj=(#t) stemplate=('g2p_list2Tuple ('g2p_leaf . 'g2p_null2Tuple0))\n"
+ "# g2p_list2Tuple - processing item CAR(sobj)=#t CAR(stemp)=('g2p_leaf . 'g2p_null2Tuple0)\n"
+ "# Entered g2p_apply: sobj=#t stemplate=('g2p_leaf . 'g2p_null2Tuple0)\n"
+ "# Entered g2p_leaf: sobj=#t stemplate='g2p_null2Tuple0\n"
+ "# g2p_null2Tuple0: unsuccessful conversion: #t is not null\n"
+ "# Leaving g2p_leaf, after G2P_SMOBP conversion, with null result\n"
+ "# Leaving g2p_apply: with null result\n"
+ "# g2p_list2Tuple: unsuccessful conversion of element 0: #t does not match personalized template\n"
+ "# Leaving g2p_apply: with null result\n")))
+(is-ok 13 "g2p_leaf verbosity/3 coverage - G2P_SMOB, failed"
+ expres13
+ (with-output-to-string
+ (catch-thunk-invoke-python-repr
+ '(#t)
+ (list g2p_list2Tuple (cons g2p_leaf g2p_null2Tuple0)))))
+
+; End of 19_verbose_always.t
diff --git a/t/20_pyscm.t b/t/20_pyscm.t
new file mode 100644
index 0000000..ef19cea
--- /dev/null
+++ b/t/20_pyscm.t
@@ -0,0 +1,247 @@
+#!/usr/bin/guile -s
+!#
+; PySCM related tests.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Copyright (C) 2008 Omer Zak.
+; This library is free software; you can redistribute it and/or
+; modify it under the terms of the GNU Lesser General Public
+; License as published by the Free Software Foundation; either
+; version 2.1 of the License, or (at your option) any later version.
+;
+; This library is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; Lesser General Public License for more details.
+;
+; You should have received a copy of the GNU Lesser General Public License
+; along with this library, in a file named COPYING; if not, write to the
+; Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+; Boston, MA 02111-1307 USA
+;
+; For licensing issues, contact <w1@zak.co.il>.
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+(use-modules (guiletap))
+(use-modules (pyguile))
+(use-modules (srfi srfi-13)) ; string-concatenate
+(load "scripts/test_auxiliary_functions.scm")
+
+(plan 7)
+
+(define guilefunc0
+ (lambda (dummy1 dummy2) 2.73))
+(define pyfunc0code
+ (string-concatenate
+ '("def pyfunc0(func):\n"
+ " return(4.1 + func())\n")))
+(python-eval pyfunc0code)
+(define pyfunc0-smob (python-eval "pyfunc0" #t))
+
+
+(is-ok 1 "show argumentless guile procedure and Python function"
+ "#<procedure guilefunc0 (dummy1 dummy2)>\n(python-eval <function pyfunc0 at 0xgggggggg> #t)\n"
+ (substitute-hex-addresses-for-gggggggg
+ (cdr
+ (capture-result-output-catch
+ (lambda ()
+ (display guilefunc0)
+ (newline)
+ (display pyfunc0-smob)
+ (newline))))))
+
+(is-ok 2 "pysmob func, using argumentless guile procedure"
+ 6.83
+ (python-apply pyfunc0-smob (list guilefunc0) '()
+ (list g2p_list2Tuple (cons g2p_procedure2PySCMObject pyscm-default-template))))
+
+(define expres3_cdr
+ (string-concatenate
+ (list
+ "# guile2python: entry: seeking to convert sobj=(python-eval <function pyfunc0 at 0xgggggggg> #t); unused stemplate=#<unspecified>\n"
+ "# g2p_null2PyNone: unsuccessful conversion: (python-eval <function pyfunc0 at 0xgggggggg> #t) is not null\n"
+ "# g2p_list2List: unsuccessful conversion: (python-eval <function pyfunc0 at 0xgggggggg> #t) is not a list\n"
+ "# g2p_pair2Tuple: unsuccessful conversion: (python-eval <function pyfunc0 at 0xgggggggg> #t) is not a pair\n"
+ "# g2p_bool2Bool: unsuccessful conversion: (python-eval <function pyfunc0 at 0xgggggggg> #t) is not a bool value\n"
+ "# g2p_num2Int: unsuccessful conversion: (python-eval <function pyfunc0 at 0xgggggggg> #t) is not a num value\n"
+ "# g2p_bignum2Long: unsuccessful conversion: (python-eval <function pyfunc0 at 0xgggggggg> #t) is not a bignum value\n"
+ "# g2p_real2Float: unsuccessful conversion: (python-eval <function pyfunc0 at 0xgggggggg> #t) is not a real value\n"
+ "# g2p_complex2Complex: unsuccessful conversion: (python-eval <function pyfunc0 at 0xgggggggg> #t) is not a complex value\n"
+ "# g2p_string2String: unsuccessful conversion: (python-eval <function pyfunc0 at 0xgggggggg> #t) is not a string value\n"
+ "# g2p_symbol2String: unsuccessful conversion: (python-eval <function pyfunc0 at 0xgggggggg> #t) is not a symbol value\n"
+ "# g2p_keyword2String: unsuccessful conversion: (python-eval <function pyfunc0 at 0xgggggggg> #t) is not a keyword value\n"
+ "# g2p_procedure2PySCMObject: unsuccessful conversion: (python-eval <function pyfunc0 at 0xgggggggg> #t) is not a procedure\n"
+ "# g2p_opaque2Object: the Python object inside opaque pysmob (python-eval <function pyfunc0 at 0xgggggggg> #t) is unwrapped\n"
+ "# python_apply: decoded pfunc \"<function pyfunc0 at 0xgggggggg>\"\n"
+ "# python_apply: decoded function actually to be invoked: \"<function pyfunc0 at 0xgggggggg>\"\n"
+ "# Entered g2p_apply: sobj=(#<procedure guilefunc0 (dummy1 dummy2)>) stemplate=('g2p_list2Tuple ('g2p_procedure2PySCMObject . #('python2guile 'python2guile 'guile2python #<procedure apply (fun . args)> #f)))\n"
+ "# g2p_list2Tuple - processing item CAR(sobj)=#<procedure guilefunc0 (dummy1 dummy2)> CAR(stemp)=('g2p_procedure2PySCMObject . #('python2guile 'python2guile 'guile2python #<procedure apply (fun . args)> #f))\n"
+ "# Entered g2p_apply: sobj=#<procedure guilefunc0 (dummy1 dummy2)> stemplate=('g2p_procedure2PySCMObject . #('python2guile 'python2guile 'guile2python #<procedure apply (fun . args)> #f))\n"
+ "# wrap_scm: was called to wrap #<procedure guilefunc0 (dummy1 dummy2)>\n"
+ "# g2p_procedure2PySCMObject: successful conversion: #<procedure guilefunc0 (dummy1 dummy2)> has been wrapped\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_list2Tuple: successful conversion of list () by template\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# python_apply: decoded positional arguments \"(<pyscm.PySCM object at 0xgggggggg>,)\"\n"
+ "# guileassoc2pythondict: entry: seeking to convert sobj=() using stemplate=#<undefined>\n"
+ "# guileassoc2pythondict: successful conversion of ()\n"
+ "# python_apply: decoded keyword arguments \"{}\"\n"
+ "# pyscm_PySCM_call: calling #<procedure guilefunc0 (dummy1 dummy2)> with args=\"()\" and keywords=\"(null PyObject)\"; stemplate=#('python2guile 'python2guile 'guile2python #<procedure apply (fun . args)> #f)\n"
+ "# p2g_apply: pobj=\"()\" smob-stemplate='python2guile\n"
+ "# python2guile: trying to convert pobj=\"()\" using stemplate=#<unspecified>\n"
+ "# p2g_None2SCM_EOL: pobj=\"()\" stemplate=#<unspecified>\n"
+ "# p2g_Bool2SCM_BOOL: failed to convert pobj=\"()\" using stemplate=#<unspecified>\n"
+ "# p2g_Int2num: failed to convert pobj=\"()\" using stemplate=#<unspecified>\n"
+ "# p2g_Long2bignum: failed to convert pobj=\"()\" using stemplate=#<unspecified>\n"
+ "# p2g_Float2real: failed to convert pobj=\"()\" using stemplate=#<unspecified>\n"
+ "# p2g_Complex2complex: failed to convert pobj=\"()\" using stemplate=#<unspecified>\n"
+ "# p2g_String2string: failed to convert pobj=\"()\" using stemplate=#<unspecified>\n"
+ "# Entered g2p_apply: sobj=2.73 stemplate='guile2python\n"
+ "# guile2python: entry: seeking to convert sobj=2.73; unused stemplate=#<unspecified>\n"
+ "# g2p_null2PyNone: unsuccessful conversion: 2.73 is not null\n"
+ "# g2p_list2List: unsuccessful conversion: 2.73 is not a list\n"
+ "# g2p_pair2Tuple: unsuccessful conversion: 2.73 is not a pair\n"
+ "# g2p_bool2Bool: unsuccessful conversion: 2.73 is not a bool value\n"
+ "# g2p_num2Int: unsuccessful conversion: 2.73 is not a num value\n"
+ "# g2p_bignum2Long: unsuccessful conversion: 2.73 is not a bignum value\n"
+ "# g2p_real2Float: successful conversion of 2.73 into a Python Float value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# p2g_apply: pobj=\"6.8300000000000001\" smob-stemplate='python2guile\n"
+ "# python2guile: trying to convert pobj=\"6.8300000000000001\" using stemplate=#<unspecified>\n"
+ "# p2g_None2SCM_EOL: pobj=\"6.8300000000000001\" stemplate=#<unspecified>\n"
+ "# p2g_Bool2SCM_BOOL: failed to convert pobj=\"6.8300000000000001\" using stemplate=#<unspecified>\n"
+ "# p2g_Int2num: failed to convert pobj=\"6.8300000000000001\" using stemplate=#<unspecified>\n"
+ "# p2g_Long2bignum: failed to convert pobj=\"6.8300000000000001\" using stemplate=#<unspecified>\n"
+ "# p2g_Float2real: successful conversion of \"6.8300000000000001\" into SCM\n"
+ "# python_apply: decoded results:\n"
+ "# Python: \"6.8300000000000001\"\n"
+ "# Scheme: 6.83\n")))
+(define saved-verbosity (pyguile-verbosity-set! (+ 3 64 128 256)))
+(define gggggggg-transform-cdr ; perform substitute-hex-addresses-for-gggggggg only on cdr of the argument
+ (lambda (arg)
+ (cons (car arg)
+ (substitute-hex-addresses-for-gggggggg (cdr arg)))))
+(is-ok 3 "pysmob func, using argumentless guile procedure"
+ (cons 6.83 expres3_cdr)
+ (gggggggg-transform-cdr
+ (capture-result-output-catch
+ python-apply pyfunc0-smob (list guilefunc0) '()
+ (list g2p_list2Tuple (cons g2p_procedure2PySCMObject pyscm-default-template)))))
+(pyguile-verbosity-set! saved-verbosity)
+
+(define guilefunc
+ (lambda (arg dummy) (+ 3.5 (car arg))))
+(define pyfunccode
+ (string-concatenate
+ '("def pyfunc(argu,func):\n"
+ " return(4.2 + func(argu))\n")))
+(python-eval pyfunccode)
+(define pyfunc-smob (python-eval "pyfunc" #t))
+
+(is-ok 4 "show guile procedure with argument and Python function"
+ "#<procedure guilefunc (arg dummy)>\n(python-eval <function pyfunc at 0xgggggggg> #t)\n"
+ (substitute-hex-addresses-for-gggggggg
+ (cdr
+ (capture-result-output-catch
+ (lambda ()
+ (display guilefunc)
+ (newline)
+ (display pyfunc-smob)
+ (newline))))))
+
+
+(is-ok 5 "pysmob func, using guile procedure with argument"
+ 67.7
+ (python-apply pyfunc-smob (list 60.0 guilefunc) '()
+ (list g2p_list2Tuple g2p_real2Float (cons g2p_procedure2PySCMObject pyscm-default-template))))
+
+(define expres6_cdr
+ (string-concatenate
+ (list
+ "# guile2python: entry: seeking to convert sobj=(python-eval <function pyfunc at 0xgggggggg> #t); unused stemplate=#<unspecified>\n"
+ "# g2p_null2PyNone: unsuccessful conversion: (python-eval <function pyfunc at 0xgggggggg> #t) is not null\n"
+ "# g2p_list2List: unsuccessful conversion: (python-eval <function pyfunc at 0xgggggggg> #t) is not a list\n"
+ "# g2p_pair2Tuple: unsuccessful conversion: (python-eval <function pyfunc at 0xgggggggg> #t) is not a pair\n"
+ "# g2p_bool2Bool: unsuccessful conversion: (python-eval <function pyfunc at 0xgggggggg> #t) is not a bool value\n"
+ "# g2p_num2Int: unsuccessful conversion: (python-eval <function pyfunc at 0xgggggggg> #t) is not a num value\n"
+ "# g2p_bignum2Long: unsuccessful conversion: (python-eval <function pyfunc at 0xgggggggg> #t) is not a bignum value\n"
+ "# g2p_real2Float: unsuccessful conversion: (python-eval <function pyfunc at 0xgggggggg> #t) is not a real value\n"
+ "# g2p_complex2Complex: unsuccessful conversion: (python-eval <function pyfunc at 0xgggggggg> #t) is not a complex value\n"
+ "# g2p_string2String: unsuccessful conversion: (python-eval <function pyfunc at 0xgggggggg> #t) is not a string value\n"
+ "# g2p_symbol2String: unsuccessful conversion: (python-eval <function pyfunc at 0xgggggggg> #t) is not a symbol value\n"
+ "# g2p_keyword2String: unsuccessful conversion: (python-eval <function pyfunc at 0xgggggggg> #t) is not a keyword value\n"
+ "# g2p_procedure2PySCMObject: unsuccessful conversion: (python-eval <function pyfunc at 0xgggggggg> #t) is not a procedure\n"
+ "# g2p_opaque2Object: the Python object inside opaque pysmob (python-eval <function pyfunc at 0xgggggggg> #t) is unwrapped\n"
+ "# python_apply: decoded pfunc \"<function pyfunc at 0xgggggggg>\"\n"
+ "# python_apply: decoded function actually to be invoked: \"<function pyfunc at 0xgggggggg>\"\n"
+ "# Entered g2p_apply: sobj=(60.0 #<procedure guilefunc (arg dummy)>) stemplate=('g2p_list2Tuple 'g2p_real2Float ('g2p_procedure2PySCMObject . #('python2guile 'python2guile 'guile2python #<procedure apply (fun . args)> #f)))\n"
+ "# g2p_list2Tuple - processing item CAR(sobj)=60.0 CAR(stemp)='g2p_real2Float\n"
+ "# Entered g2p_apply: sobj=60.0 stemplate='g2p_real2Float\n"
+ "# g2p_real2Float: successful conversion of 60.0 into a Python Float value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_list2Tuple - processing item CAR(sobj)=#<procedure guilefunc (arg dummy)> CAR(stemp)=('g2p_procedure2PySCMObject . #('python2guile 'python2guile 'guile2python #<procedure apply (fun . args)> #f))\n"
+ "# Entered g2p_apply: sobj=#<procedure guilefunc (arg dummy)> stemplate=('g2p_procedure2PySCMObject . #('python2guile 'python2guile 'guile2python #<procedure apply (fun . args)> #f))\n"
+ "# wrap_scm: was called to wrap #<procedure guilefunc (arg dummy)>\n"
+ "# g2p_procedure2PySCMObject: successful conversion: #<procedure guilefunc (arg dummy)> has been wrapped\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# g2p_list2Tuple: successful conversion of list () by template\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# python_apply: decoded positional arguments \"(60.0, <pyscm.PySCM object at 0xgggggggg>)\"\n"
+ "# guileassoc2pythondict: entry: seeking to convert sobj=() using stemplate=#<undefined>\n"
+ "# guileassoc2pythondict: successful conversion of ()\n"
+ "# python_apply: decoded keyword arguments \"{}\"\n"
+ "# pyscm_PySCM_call: calling #<procedure guilefunc (arg dummy)> with args=\"(60.0,)\" and keywords=\"(null PyObject)\"; stemplate=#('python2guile 'python2guile 'guile2python #<procedure apply (fun . args)> #f)\n"
+ "# p2g_apply: pobj=\"(60.0,)\" smob-stemplate='python2guile\n"
+ "# python2guile: trying to convert pobj=\"(60.0,)\" using stemplate=#<unspecified>\n"
+ "# p2g_None2SCM_EOL: pobj=\"(60.0,)\" stemplate=#<unspecified>\n"
+ "# p2g_Bool2SCM_BOOL: failed to convert pobj=\"(60.0,)\" using stemplate=#<unspecified>\n"
+ "# p2g_Int2num: failed to convert pobj=\"(60.0,)\" using stemplate=#<unspecified>\n"
+ "# p2g_Long2bignum: failed to convert pobj=\"(60.0,)\" using stemplate=#<unspecified>\n"
+ "# p2g_Float2real: failed to convert pobj=\"(60.0,)\" using stemplate=#<unspecified>\n"
+ "# p2g_Complex2complex: failed to convert pobj=\"(60.0,)\" using stemplate=#<unspecified>\n"
+ "# p2g_String2string: failed to convert pobj=\"(60.0,)\" using stemplate=#<unspecified>\n"
+ "# p2g_apply: pobj=\"60.0\" smob-stemplate='python2guile\n"
+ "# python2guile: trying to convert pobj=\"60.0\" using stemplate=#<unspecified>\n"
+ "# p2g_None2SCM_EOL: pobj=\"60.0\" stemplate=#<unspecified>\n"
+ "# p2g_Bool2SCM_BOOL: failed to convert pobj=\"60.0\" using stemplate=#<unspecified>\n"
+ "# p2g_Int2num: failed to convert pobj=\"60.0\" using stemplate=#<unspecified>\n"
+ "# p2g_Long2bignum: failed to convert pobj=\"60.0\" using stemplate=#<unspecified>\n"
+ "# p2g_Float2real: successful conversion of \"60.0\" into SCM\n"
+ "# p2g_Tuple2list: 1. converted item pobj=0[\"60.0\"], stemplate='python2guile\n"
+ "# Entered g2p_apply: sobj=63.5 stemplate='guile2python\n"
+ "# guile2python: entry: seeking to convert sobj=63.5; unused stemplate=#<unspecified>\n"
+ "# g2p_null2PyNone: unsuccessful conversion: 63.5 is not null\n"
+ "# g2p_list2List: unsuccessful conversion: 63.5 is not a list\n"
+ "# g2p_pair2Tuple: unsuccessful conversion: 63.5 is not a pair\n"
+ "# g2p_bool2Bool: unsuccessful conversion: 63.5 is not a bool value\n"
+ "# g2p_num2Int: unsuccessful conversion: 63.5 is not a num value\n"
+ "# g2p_bignum2Long: unsuccessful conversion: 63.5 is not a bignum value\n"
+ "# g2p_real2Float: successful conversion of 63.5 into a Python Float value\n"
+ "# Leaving g2p_apply: with non-null result\n"
+ "# p2g_apply: pobj=\"67.700000000000003\" smob-stemplate='python2guile\n"
+ "# python2guile: trying to convert pobj=\"67.700000000000003\" using stemplate=#<unspecified>\n"
+ "# p2g_None2SCM_EOL: pobj=\"67.700000000000003\" stemplate=#<unspecified>\n"
+ "# p2g_Bool2SCM_BOOL: failed to convert pobj=\"67.700000000000003\" using stemplate=#<unspecified>\n"
+ "# p2g_Int2num: failed to convert pobj=\"67.700000000000003\" using stemplate=#<unspecified>\n"
+ "# p2g_Long2bignum: failed to convert pobj=\"67.700000000000003\" using stemplate=#<unspecified>\n"
+ "# p2g_Float2real: successful conversion of \"67.700000000000003\" into SCM\n"
+ "# python_apply: decoded results:\n"
+ "# Python: \"67.700000000000003\"\n"
+ "# Scheme: 67.7\n")))
+(set! saved-verbosity (pyguile-verbosity-set! (+ 3 64 128 256)))
+(is-ok 6 "pysmob func, using guile procedure with argument"
+ (cons 67.7 expres6_cdr)
+ (gggggggg-transform-cdr
+ (capture-result-output-catch
+ python-apply pyfunc-smob (list 60.0 guilefunc) '()
+ (list g2p_list2Tuple g2p_real2Float (cons g2p_procedure2PySCMObject pyscm-default-template)))))
+(pyguile-verbosity-set! saved-verbosity)
+
+(python-eval "def holdval(val):\n global heldval\n heldval=val\n")
+(python-apply '("__main__" "holdval") (list guilefunc0) '())
+(like 7 "obtain python representation of a procedure value"
+ "^#<procedure guilefunc0 \\([-a-zA-Z0-9_]+ [-a-zA-Z0-9_]+\\)>$"
+ (object->string (python-eval "heldval" #t)))
+
+; End of 20_pyscm.t
diff --git a/t/__init__.py b/t/__init__.py
new file mode 100644
index 0000000..5dff9f3
--- /dev/null
+++ b/t/__init__.py
@@ -0,0 +1,2 @@
+# Dummy module - needed to keep happy import commands
+# which import t/scripts/*.py modules
diff --git a/t/scripts/RunGuileTests.pl b/t/scripts/RunGuileTests.pl
new file mode 100755
index 0000000..a61a18a
--- /dev/null
+++ b/t/scripts/RunGuileTests.pl
@@ -0,0 +1,36 @@
+#!/usr/bin/perl -w
+# Run Guile tests - filenames are given as arguments to the script.
+########################################################################
+#
+# Copyright (C) 2008 Omer Zak.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library, in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+#
+# For licensing issues, contact <w1@zak.co.il>.
+#
+########################################################################
+
+use strict;
+use TAP::Harness;
+my @tests = @ARGV;
+my %args = (
+ verbosity => 0,
+ timer => 1,
+ exec => ['/usr/bin/guile', '-s'],
+ );
+my $harness = TAP::Harness->new( \%args );
+ $harness->runtests(@tests);
+
+# End of RunGuileTests.pl
diff --git a/t/scripts/__init__.py b/t/scripts/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/t/scripts/__init__.py
diff --git a/t/scripts/t2conv.py b/t/scripts/t2conv.py
new file mode 100644
index 0000000..160c3b6
--- /dev/null
+++ b/t/scripts/t2conv.py
@@ -0,0 +1,67 @@
+#!/usr/bin/python
+# Basic tests to validate conversions from Python to Guile and vice versa.
+########################################################################
+#
+# Copyright (C) 2008 Omer Zak.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library, in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+#
+# For licensing issues, contact <w1@zak.co.il>.
+#
+########################################################################
+
+def return_None():
+ return None
+
+def return_True():
+ return True
+
+def return_False():
+ return False
+
+def return_Int1():
+ return 1
+
+def return_Int_5():
+ return(-5)
+
+def return_BigInt():
+ return(1048576*1048576*1048576*32)
+
+def return_BigInt_neg():
+ return(-1048576*1048576*1048576*32)
+
+def return_String1():
+ return("abcdefghi")
+
+def return_String2():
+ return("01abCD%^")
+
+def return_String_Zero():
+ return("bef" + chr(0) + chr(163) + "ore")
+
+if (__name__ == '__main__'):
+ print "return_None: ",return_None()
+ print "return_True: ",return_True()
+ print "return_False: ",return_False()
+ print "return_Int1: ",return_Int1()
+ print "return_Int_5: ",return_Int_5()
+ print "return_BigInt: ",return_BigInt()
+ print "return_BigInt_neg: ",return_BigInt_neg()
+ print "String1: ",return_String1()
+ print "String2: ",return_String2()
+ print "String_Zero: ",return_String_Zero(),repr(return_String_Zero())
+
+# End of t2conv.py
diff --git a/t/scripts/t4apply.py b/t/scripts/t4apply.py
new file mode 100644
index 0000000..a716462
--- /dev/null
+++ b/t/scripts/t4apply.py
@@ -0,0 +1,46 @@
+#!/usr/bin/python
+# Auxiliary functions for exercising python-apply.
+########################################################################
+#
+# Copyright (C) 2008 Omer Zak.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library, in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+#
+# For licensing issues, contact <w1@zak.co.il>.
+#
+########################################################################
+
+def return_args(*args,**kw):
+ return("positional: %s keywords: %s" % (repr(args),repr(kw)))
+
+class cl1(object):
+ def __init__(self,num=1,str="2"):
+ self.num = num
+ self.str = str
+
+ def myfunc(self,arg="x"):
+ return(arg + self.str)
+
+class cl2(object):
+ def __init__(self,num=3):
+ self.num2 = num
+ self.mycl1 = cl1(10,str="Twenty")
+
+ def cl2func(self,argx="y"):
+ return(str(self.num2) + argx)
+
+mainobj = cl2(33)
+
+# End of t4apply.py
diff --git a/t/scripts/t5smobs.py b/t/scripts/t5smobs.py
new file mode 100644
index 0000000..2f4b4a5
--- /dev/null
+++ b/t/scripts/t5smobs.py
@@ -0,0 +1,51 @@
+#!/usr/bin/python
+# Auxiliary functions for exercising pysmobs.
+########################################################################
+#
+# Copyright (C) 2008 Omer Zak.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library, in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+#
+# For licensing issues, contact <w1@zak.co.il>.
+#
+########################################################################
+
+class opaq(object):
+ def __init__(self,arg1):
+ self.arg1 = arg1
+ def transform(self):
+ self.arg1 = self.arg1 + self.arg1
+ def __repr__(self):
+ return("opaq(%s)" % repr(self.arg1))
+
+# Work around temporary problem in PyGuile.
+def genopaq(arg):
+ return(opaq(arg))
+
+class noisydelete(object):
+ def __init__(self,id):
+ self.id = id
+ def __del__(self):
+ print "# Deleting class instance %s" % self.id
+ #object.__del__()
+ def __repr__(self):
+ return(repr(self.id))
+ def __cmp__(self,other):
+ if ((self.id == "me") and (other.id == 42)):
+ # Want to prove that this function has indeed been exercised.
+ return(0)
+ return(cmp(self.id,other.id))
+
+# End of t5smobs.py
diff --git a/t/scripts/t7except.py b/t/scripts/t7except.py
new file mode 100644
index 0000000..d2f6948
--- /dev/null
+++ b/t/scripts/t7except.py
@@ -0,0 +1,32 @@
+#!/usr/bin/python
+# Raising exception inside my own code
+########################################################################
+#
+# Copyright (C) 2008 Omer Zak.
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library, in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+# Boston, MA 02111-1307 USA
+#
+# For licensing issues, contact <w1@zak.co.il>.
+#
+########################################################################
+
+import exceptions
+class myexception(exceptions.Exception):
+ pass
+
+def raiser(arg="typical!"):
+ raise myexception("This is my exception",arg)
+
+# End of t7except.py
diff --git a/t/scripts/test_auxiliary_functions.scm b/t/scripts/test_auxiliary_functions.scm
new file mode 100644
index 0000000..f420fab
--- /dev/null
+++ b/t/scripts/test_auxiliary_functions.scm
@@ -0,0 +1,177 @@
+#!/usr/bin/guile -s
+!#
+; Auxiliary functions, for use by test scripts
+; Those functions are used by scripts from 20_* and on.
+; Earlier scripts usually define their own versions of the
+; functions.
+;
+; To use the functions, add
+; (load "scripts/test_auxliary_functions.scm")
+; at the beginning of your test script, after any (use-modules ...)
+; calls.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Copyright (C) 2008 Omer Zak.
+; This library is free software; you can redistribute it and/or
+; modify it under the terms of the GNU Lesser General Public
+; License as published by the Free Software Foundation; either
+; version 2.1 of the License, or (at your option) any later version.
+;
+; This library is distributed in the hope that it will be useful,
+; but WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; Lesser General Public License for more details.
+;
+; You should have received a copy of the GNU Lesser General Public License
+; along with this library, in a file named COPYING; if not, write to the
+; Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+; Boston, MA 02111-1307 USA
+;
+; For licensing issues, contact <w1@zak.co.il>.
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+(use-modules (ice-9 regex)) ; regexp-substitute
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; Functions from modules, unlikely to be generally useful
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+; from 03_guile2python.t
+(define invoke-python-func
+ (lambda (module func arg)
+ (python-apply (list module func) (list arg) '())))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; Functions likely to be generally useful
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; Miscellaneous data manipulation ;
+; functions ;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+; Returns a 3-element list of booleans.
+(define equalities3?
+ (lambda (obj1 obj2)
+ (list (eq? obj1 obj2) (eqv? obj1 obj2) (equal? obj1 obj2))))
+
+; Does one alist include another alist.
+; Inclusion means that all keys of the included alist are in the
+; including one, and the corresponding values are equal.
+; The equality criteria used here is equal? (for both key and value).
+(define alist-properly-included?
+ (lambda (included includor)
+ (if (null? included) #t
+ (let ((key (caar included))
+ (value (cdar included))
+ (rest (cdr included)))
+ (let ((includor-ref (assoc key includor)))
+ (cond ((not includor-ref) #f)
+ ((not (equal? (cdr includor-ref) value)) #f)
+ (else (alist-properly-included? rest includor))))))))
+
+; Are two alists equal?
+(define alist-equal?
+ (lambda (alista alistb)
+ (and (alist-properly-included? alista alistb)
+ (alist-properly-included? alistb alista))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; Modify actual results for easier ;
+; comparison to expected results ;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+; Replace all hex addresses appearing in a string
+; by a specific literal.
+(define substitute-hex-addresses-for-gggggggg
+ (lambda (strarg)
+ (regexp-substitute/global #f
+ "0x[0-9a-f]{8}"
+ strarg
+ 'pre "0xgggggggg" 'post)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; Running inside catch harness ;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+; Run a function, so that anything it writes to the current
+; output port is captured, together with its return value.
+; The return value to caller of capture-result-output is the
+; pair of (return-value . output-string).
+
+(define capture-result-output
+ (lambda (func . args)
+ (let ((stdoutstr #f)
+ (retval #f))
+ (set! stdoutstr
+ (with-output-to-string
+ (lambda () (set! retval
+ (apply func args)))))
+ (cons retval stdoutstr))))
+
+; Run a function in an environment, in which any exceptions
+; raised by it are caught; and anything it writes to the
+; current output port is captured as well.
+; The return value to caller of capture-result-output-catch
+; is the pair of (return-value . output-string).
+(define capture-result-output-catch
+ (lambda (func . args)
+ (let ((output-string #f)
+ (return-value #f))
+ (set! output-string
+ (with-output-to-string
+ (lambda () (set! return-value
+ (catch #t
+ (lambda () (apply func args))
+ (lambda (key . args2)
+ (object->string (list key args2))))))))
+ (cons return-value output-string))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; Functions specific to PyGuile ;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+; Run python-eval under catch harness.
+; template can be #f when no return value is expected, or #t when
+; the default template is to be used.
+(define catch-python-eval
+ (lambda (txt template)
+ (catch #t
+ (lambda () (python-eval txt template))
+ (lambda (key . args) (object->string (list key args))))))
+
+; Run python-import under catch harness.
+(define catch-python-import
+ (lambda (arg)
+ (catch #t
+ (lambda () (python-import arg))
+ (lambda (key . args) (object->string (list key args))))))
+
+; Run python-apply under catch harness.
+; The positional argument list must be supplied.
+; The keyword argument list and the templates are optional.
+(define catch-python-apply
+ (lambda (func posargs . kwargs-templates)
+ (catch #t
+ (lambda () (apply python-apply func posargs kwargs-templates))
+ (lambda (key . args) (object->string (list key args))))))
+
+; The following function is useful for checking how a SCM is
+; actually converted into a PyObject using a template.
+; The conversion is run under a catch harness.
+(define catch-thunk-invoke-python-repr
+ (lambda (arg . template)
+ (catch #t
+ (lambda ()
+ (if (null? template)
+ (python-apply '("__builtin__" "repr") arg '())
+ (python-apply '("__builtin__" "repr") arg '() (car template))))
+ (lambda (key . args2) (object->string (list key args2))))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+; End of test_axuliary_functions.scm
diff --git a/verbose.c b/verbose.c
new file mode 100644
index 0000000..e06b7d0
--- /dev/null
+++ b/verbose.c
@@ -0,0 +1,90 @@
+// verbosity control implementation
+//
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2008 Omer Zak.
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this library, in a file named COPYING; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+// Boston, MA 02111-1307 USA
+//
+// For licensing issues, contact <w1@zak.co.il>.
+//
+////////////////////////////////////////////////////////////////////////
+//
+// Implements verbosity control for PyGuile C code.
+
+#include "verbose.h"
+
+static int pyguile_verbosity_control = 0;
+
+////////////////////////////////////////////////////////////////////////
+// SCM side
+////////////////////////////////////////////////////////////////////////
+
+SCM
+pyguile_verbosity_set(SCM snewsetting)
+{
+ if (SCM_INUMP(snewsetting)) {
+ SCM sprev_value = scm_long2num(pyguile_verbosity_control);
+ pyguile_verbosity_control = (int)scm_num2long(snewsetting,0,"pyguile-verbosity-set!");
+ return(sprev_value);
+ }
+ else {
+ scm_wrong_type_arg("pyguile-verbosity-set!",SCM_ARG1,snewsetting);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+// C side
+////////////////////////////////////////////////////////////////////////
+
+int
+pyguile_verbosity_test(int mask)
+{
+ return(pyguile_verbosity_control & mask);
+}
+
+////////////////////////////////////////////////////////////////////////
+// Verbosity auxiliary functions
+////////////////////////////////////////////////////////////////////////
+
+// Create a SCM string whose contents are the Python-generated repr()
+// of a PyObject.
+// If PyObject==NULL, return "(null PyObject)".
+SCM
+verbosity_repr(PyObject *pobj)
+{
+ if (NULL == pobj) {
+ return(scm_makfrom0str("(null PyObject)"));
+ }
+ PyObject *pstr = PyObject_Repr(pobj);
+ if (NULL == pstr) {
+ PyObject *pexception = PyErr_Occurred(); // NOT COVERED BY TESTS
+ if (pexception) { // NOT COVERED BY TESTS
+ PyErr_Clear(); // NOT COVERED BY TESTS
+ scm_misc_error("verbosity_repr","cannot get repr of pobj", // NOT COVERED BY TESTS
+ SCM_UNDEFINED);
+ }
+ else {
+ scm_misc_error("verbosity_repr","unknown error during repr of pobj", // NOT COVERED BY TESTS
+ SCM_UNDEFINED);
+ }
+ }
+ SCM sstr = scm_makfrom0str(PyString_AsString(pstr));
+ Py_DECREF(pstr);
+ return(sstr);
+}
+
+////////////////////////////////////////////////////////////////////////
+// End of verbose.c
diff --git a/verbose.h b/verbose.h
new file mode 100644
index 0000000..55a48fa
--- /dev/null
+++ b/verbose.h
@@ -0,0 +1,66 @@
+// verbose header file
+// Functions for verbosity control of PyGuile functions.
+////////////////////////////////////////////////////////////////////////
+
+#ifndef VERBOSE_H
+#define VERBOSE_H
+
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2008 Omer Zak.
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this library, in a file named COPYING; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+// Boston, MA 02111-1307 USA
+//
+// For licensing issues, contact <w1@zak.co.il>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#include <Python.h> // Must be first header file
+#include <libguile.h>
+
+////////////////////////////////////////////////////////////////////////
+// Verbosity control
+////////////////////////////////////////////////////////////////////////
+
+// Function to be invoked from Scheme code.
+// The previous setting is returned to the caller.
+extern SCM pyguile_verbosity_set(SCM newsetting);
+
+// Function for accessing the verbosity control from C data.
+// The return value is 0 if the caller is not to print the
+// requested information.
+extern int pyguile_verbosity_test(int mask);
+
+// Auxiliary function, for use in several verbosity messages.
+extern SCM verbosity_repr(PyObject *pobj);
+
+// Bitmap settings for the aforementioned mask argument.
+#define PYGUILE_VERBOSE_NONE 0
+#define PYGUILE_VERBOSE_G2P2G_SUCCESSFUL 1
+#define PYGUILE_VERBOSE_G2P2G_ALWAYS 2 // You should use 3 = 1+2
+#define PYGUILE_VERBOSE_GC 4
+#define PYGUILE_VERBOSE_GC_DETAILED 8 // You should use 12 = 4+8
+#define PYGUILE_VERBOSE_NEW_PYSMOB 16
+#define PYGUILE_VERBOSE_UNWRAP_PYSMOB 32 // You should use 48 = 16+32
+#define PYGUILE_VERBOSE_PYTHON_APPLY 64
+#define PYGUILE_VERBOSE_PYSCM 128
+#define PYGUILE_VERBOSE_GC_PYSCM 256
+
+////////////////////////////////////////////////////////////////////////
+
+#endif /* VERBOSE_H */
+
+////////////////////////////////////////////////////////////////////////
+// End of verbose.h