From c26aec892f26ee21067a0beb9dd79a8c834e6dba Mon Sep 17 00:00:00 2001 From: Omer Zak Date: Sun, 22 May 2016 11:03:43 +0300 Subject: Added all old files of interest to version control. --- AUTHORS | 4 + INSTALL | 33 + Makefile | 146 ++++ THANKS | 4 + TODO | 37 ++ extract_conversion_functions.py | 177 +++++ g2p2g_smob.c | 164 +++++ g2p2g_smob.h | 79 +++ guiletap.scm | 115 ++++ guiletopy.c | 1141 ++++++++++++++++++++++++++++++++ guiletopy.h | 104 +++ pyguile.c | 497 ++++++++++++++ pyguile.h | 87 +++ pyguile.scm.in | 49 ++ pyscm.c | 577 ++++++++++++++++ pyscm.h | 55 ++ pysmob.c | 342 ++++++++++ pysmob.h | 53 ++ pytoguile.c | 747 +++++++++++++++++++++ pytoguile.h | 50 ++ t/01_basic.t | 34 + t/02_pyguile.t | 49 ++ t/03_guile2python.t | 58 ++ t/04_python_apply.t | 66 ++ t/05_pysmobs.t | 128 ++++ t/06_guile2python.t | 143 ++++ t/07_apply.t | 129 ++++ t/08_eval.t | 69 ++ t/09_import.t | 59 ++ t/10_python2guile.t | 56 ++ t/11_g2p2g.t | 49 ++ t/12_g2p_templated.t | 225 +++++++ t/13_hashes.t | 167 +++++ t/14_p2g_templated.t | 293 ++++++++ t/15_p2g_errors.t | 126 ++++ t/16_verbose.t | 50 ++ t/17_verbose.t | 161 +++++ t/18_bignum.t | 55 ++ t/19_verbose_always.t | 733 ++++++++++++++++++++ t/20_pyscm.t | 247 +++++++ t/__init__.py | 2 + t/scripts/RunGuileTests.pl | 36 + t/scripts/__init__.py | 0 t/scripts/t2conv.py | 67 ++ t/scripts/t4apply.py | 46 ++ t/scripts/t5smobs.py | 51 ++ t/scripts/t7except.py | 32 + t/scripts/test_auxiliary_functions.scm | 177 +++++ verbose.c | 90 +++ verbose.h | 66 ++ 50 files changed, 7925 insertions(+) create mode 100644 AUTHORS create mode 100644 INSTALL create mode 100644 Makefile create mode 100644 THANKS create mode 100644 TODO create mode 100755 extract_conversion_functions.py create mode 100644 g2p2g_smob.c create mode 100644 g2p2g_smob.h create mode 100644 guiletap.scm create mode 100644 guiletopy.c create mode 100644 guiletopy.h create mode 100644 pyguile.c create mode 100644 pyguile.h create mode 100644 pyguile.scm.in create mode 100644 pyscm.c create mode 100644 pyscm.h create mode 100644 pysmob.c create mode 100644 pysmob.h create mode 100644 pytoguile.c create mode 100644 pytoguile.h create mode 100755 t/01_basic.t create mode 100644 t/02_pyguile.t create mode 100644 t/03_guile2python.t create mode 100644 t/04_python_apply.t create mode 100644 t/05_pysmobs.t create mode 100644 t/06_guile2python.t create mode 100644 t/07_apply.t create mode 100644 t/08_eval.t create mode 100644 t/09_import.t create mode 100644 t/10_python2guile.t create mode 100644 t/11_g2p2g.t create mode 100644 t/12_g2p_templated.t create mode 100644 t/13_hashes.t create mode 100644 t/14_p2g_templated.t create mode 100644 t/15_p2g_errors.t create mode 100755 t/16_verbose.t create mode 100644 t/17_verbose.t create mode 100644 t/18_bignum.t create mode 100644 t/19_verbose_always.t create mode 100644 t/20_pyscm.t create mode 100644 t/__init__.py create mode 100755 t/scripts/RunGuileTests.pl create mode 100644 t/scripts/__init__.py create mode 100644 t/scripts/t2conv.py create mode 100644 t/scripts/t4apply.py create mode 100644 t/scripts/t5smobs.py create mode 100644 t/scripts/t7except.py create mode 100644 t/scripts/test_auxiliary_functions.scm create mode 100644 verbose.c create mode 100644 verbose.h diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..e842878 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,4 @@ +tddpirate: Omer Zak +# +# 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 . +# +######################################################################## +# +# 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 + * 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 . +# +######################################################################## +# $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 . +// +//////////////////////////////////////////////////////////////////////// +// +// 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 = ""; // 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 = ""; // 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 . +// +//////////////////////////////////////////////////////////////////////// +// +// 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 // Must be first header file +#include + + +// 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 . +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; +;;; 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 . +// +//////////////////////////////////////////////////////////////////////// +//#include // included in guiletopy.h +//#include // included in guiletopy.h +#include "guiletopy.h" +#include "pysmob.h" +#include +#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 . +// +//////////////////////////////////////////////////////////////////////// + +#include // Must be first header file +#include + +//////////////////////////////////////////////////////////////////////// +// 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 . +// +//////////////////////////////////////////////////////////////////////// + +//#include // included in pyguile.h +//#include // included in pyguile.h +#include "pyguile.h" +#include "pytoguile.h" +#include "guiletopy.h" +#include "pysmob.h" +#include +#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 \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 . +// +//////////////////////////////////////////////////////////////////////// + +#include // Must be first header file +#include + +//////////////////////////////////////////////////////////////////////// +// 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 . +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; 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 . +// +//////////////////////////////////////////////////////////////////////// +// +// 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("")); + } + 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 . +// +//////////////////////////////////////////////////////////////////////// + +#include // Must be first header file +#include + +//////////////////////////////////////////////////////////////////////// +// 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 . +// +//////////////////////////////////////////////////////////////////////// +// +// 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 . +// +//////////////////////////////////////////////////////////////////////// +// +// 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 // Must be first header file +#include + +// 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 . +// +//////////////////////////////////////////////////////////////////////// +//#include // included in pytoguile.h +//#include // 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 . +// +//////////////////////////////////////////////////////////////////////// + +#include // Must be first header file +#include + +//////////////////////////////////////////////////////////////////////// +// 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 . +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +(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 . +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +(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 . +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +(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 . +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +(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 . +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +(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 . +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +(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 #]*>\\) #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 . +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +(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\" \\(\"\"\\) #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 #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\" \\(\"\"\\) #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 . +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +(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\" \\(\"\"\\) #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\" \\(\"\"\\) #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\" \\(\"\"\\) #f\\)\\)$" + (catch-test-eval "print '# no value was returned'\n" #t)) + +(is-ok 7 "code returns unsolicited result" + "#" + (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 . +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +(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 #t\\)$" + (object->string (catch-test-import "math"))) + +(like 2 "nonexistent module" + "^\\(misc-error \\(\"python-import\" \"Python exception during module ~A import: ~A\" \\(\"mathalternate\" \"\"\\) #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 . +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +(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 . +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +(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 . +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +(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 #) #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 #) #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 . +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +(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 . +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +(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" "#" + (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" "#" + (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" "#" + (object->string (python-eval "None" p2g_Int2num))) + +(is-ok 12 "p2g_Int2num long" "#" + (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" "#" + (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" "#" + (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" "#" + (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" "#" + (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" "#" + (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" "#" + (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" "#" + (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" "#" + (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" "#" + (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" "#" + (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" "#" + (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 . +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +(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" + "#" + (object->string (catch-test-eval "'a-42'" (list p2g_leaf p2g_Int2num p2g_Long2bignum)))) + +(is-ok 4 "too short string for keyword" + "#" + (object->string (catch-test-eval "''" p2g_String2keyword))) + +(is-ok 5 "too long string for char" + "#" + (object->string (catch-test-eval "'lg'" p2g_1String2char))) + +(is-ok 6 "too short string for char" + "#" + (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" "#" + (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" "#" + (object->string (python-eval "(1,)" (cons p2g_2Tuple2pair (cons p2g_Int2num p2g_Int2num))))) + +(is-ok 10 "2-tuple to pair, wrong 2nd item datatype" "#" + (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" "#" + (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" "#" + (object->string (python-eval "[1]" (cons p2g_2List2pair (cons p2g_Int2num p2g_Int2num))))) + +(is-ok 14 "2-list to pair, wrong 1st item datatype" "#" + (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" "#" + (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 . +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +(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 . +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +(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 #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 \"\\\"[, {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 . +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +(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 . +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +(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=#\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=#\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=#\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=#\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=#\n" + "# guileassoc2pythondict: successful conversion of ()\n" + "# p2g_apply: pobj=\"'True'\" smob-stemplate='python2guile\n" + "# python2guile: trying to convert pobj=\"'True'\" using stemplate=#\n" + "# p2g_None2SCM_EOL: pobj=\"'True'\" stemplate=#\n" + "# p2g_Bool2SCM_BOOL: failed to convert pobj=\"'True'\" using stemplate=#\n" + "# p2g_Int2num: failed to convert pobj=\"'True'\" using stemplate=#\n" + "# p2g_Long2bignum: failed to convert pobj=\"'True'\" using stemplate=#\n" + "# p2g_Float2real: failed to convert pobj=\"'True'\" using stemplate=#\n" + "# p2g_Complex2complex: failed to convert pobj=\"'True'\" using stemplate=#\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=#\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=#\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=#\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=#\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=#\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=#\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=#\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=#\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=#\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=#\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=#\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=#\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=#\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=#\n" + "# p2g_None2SCM_EOL: pobj=\"\\\"[None, True, False, 1, -5, 'P', ['quote', 'symba'], (1-1j), 3.125]\\\"\" stemplate=#\n" + "# p2g_Bool2SCM_BOOL: failed to convert pobj=\"\\\"[None, True, False, 1, -5, 'P', ['quote', 'symba'], (1-1j), 3.125]\\\"\" using stemplate=#\n" + "# p2g_Int2num: failed to convert pobj=\"\\\"[None, True, False, 1, -5, 'P', ['quote', 'symba'], (1-1j), 3.125]\\\"\" using stemplate=#\n" + "# p2g_Long2bignum: failed to convert pobj=\"\\\"[None, True, False, 1, -5, 'P', ['quote', 'symba'], (1-1j), 3.125]\\\"\" using stemplate=#\n" + "# p2g_Float2real: failed to convert pobj=\"\\\"[None, True, False, 1, -5, 'P', ['quote', 'symba'], (1-1j), 3.125]\\\"\" using stemplate=#\n" + "# p2g_Complex2complex: failed to convert pobj=\"\\\"[None, True, False, 1, -5, 'P', ['quote', 'symba'], (1-1j), 3.125]\\\"\" using stemplate=#\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=#\n" + "# guileassoc2pythondict: successful conversion of ()\n" + "# p2g_apply: pobj=\"'((), [], {})'\" smob-stemplate='python2guile\n" + "# python2guile: trying to convert pobj=\"'((), [], {})'\" using stemplate=#\n" + "# p2g_None2SCM_EOL: pobj=\"'((), [], {})'\" stemplate=#\n" + "# p2g_Bool2SCM_BOOL: failed to convert pobj=\"'((), [], {})'\" using stemplate=#\n" + "# p2g_Int2num: failed to convert pobj=\"'((), [], {})'\" using stemplate=#\n" + "# p2g_Long2bignum: failed to convert pobj=\"'((), [], {})'\" using stemplate=#\n" + "# p2g_Float2real: failed to convert pobj=\"'((), [], {})'\" using stemplate=#\n" + "# p2g_Complex2complex: failed to convert pobj=\"'((), [], {})'\" using stemplate=#\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=#\n" + "# guileassoc2pythondict: successful conversion of ()\n" + "# p2g_apply: pobj=\"'[1000000, 1000000000000L]'\" smob-stemplate='python2guile\n" + "# python2guile: trying to convert pobj=\"'[1000000, 1000000000000L]'\" using stemplate=#\n" + "# p2g_None2SCM_EOL: pobj=\"'[1000000, 1000000000000L]'\" stemplate=#\n" + "# p2g_Bool2SCM_BOOL: failed to convert pobj=\"'[1000000, 1000000000000L]'\" using stemplate=#\n" + "# p2g_Int2num: failed to convert pobj=\"'[1000000, 1000000000000L]'\" using stemplate=#\n" + "# p2g_Long2bignum: failed to convert pobj=\"'[1000000, 1000000000000L]'\" using stemplate=#\n" + "# p2g_Float2real: failed to convert pobj=\"'[1000000, 1000000000000L]'\" using stemplate=#\n" + "# p2g_Complex2complex: failed to convert pobj=\"'[1000000, 1000000000000L]'\" using stemplate=#\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=#\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=#\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=#\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=#\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=#\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=#\n" + "# p2g_None2SCM_EOL: pobj=\"\\\"(('p1at', 'p1bt'), ['p2al', 'p2bl'])\\\"\" stemplate=#\n" + "# p2g_Bool2SCM_BOOL: failed to convert pobj=\"\\\"(('p1at', 'p1bt'), ['p2al', 'p2bl'])\\\"\" using stemplate=#\n" + "# p2g_Int2num: failed to convert pobj=\"\\\"(('p1at', 'p1bt'), ['p2al', 'p2bl'])\\\"\" using stemplate=#\n" + "# p2g_Long2bignum: failed to convert pobj=\"\\\"(('p1at', 'p1bt'), ['p2al', 'p2bl'])\\\"\" using stemplate=#\n" + "# p2g_Float2real: failed to convert pobj=\"\\\"(('p1at', 'p1bt'), ['p2al', 'p2bl'])\\\"\" using stemplate=#\n" + "# p2g_Complex2complex: failed to convert pobj=\"\\\"(('p1at', 'p1bt'), ['p2al', 'p2bl'])\\\"\" using stemplate=#\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 #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 #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 #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 #t) CAR(stemp)='g2p_opaque2Object\n" + "# Entered g2p_apply: sobj=(python-eval #t) stemplate='g2p_opaque2Object\n" + "# g2p_opaque2Object: the Python object inside opaque pysmob (python-eval #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=#\n" + "# guileassoc2pythondict: successful conversion of ()\n" + "# p2g_apply: pobj=\"\\\"[, {1: 'one', 2: 'two', 3: 'three'}]\\\"\" smob-stemplate='python2guile\n" + "# python2guile: trying to convert pobj=\"\\\"[, {1: 'one', 2: 'two', 3: 'three'}]\\\"\" using stemplate=#\n" + "# p2g_None2SCM_EOL: pobj=\"\\\"[, {1: 'one', 2: 'two', 3: 'three'}]\\\"\" stemplate=#\n" + "# p2g_Bool2SCM_BOOL: failed to convert pobj=\"\\\"[, {1: 'one', 2: 'two', 3: 'three'}]\\\"\" using stemplate=#\n" + "# p2g_Int2num: failed to convert pobj=\"\\\"[, {1: 'one', 2: 'two', 3: 'three'}]\\\"\" using stemplate=#\n" + "# p2g_Long2bignum: failed to convert pobj=\"\\\"[, {1: 'one', 2: 'two', 3: 'three'}]\\\"\" using stemplate=#\n" + "# p2g_Float2real: failed to convert pobj=\"\\\"[, {1: 'one', 2: 'two', 3: 'three'}]\\\"\" using stemplate=#\n" + "# p2g_Complex2complex: failed to convert pobj=\"\\\"[, {1: 'one', 2: 'two', 3: 'three'}]\\\"\" using stemplate=#\n" + "# p2g_String2string: successful conversion of \"\\\"[, {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=#\n" + "# guileassoc2pythondict: successful conversion of ()\n" + "# p2g_apply: pobj=\"'[True, 5]'\" smob-stemplate='python2guile\n" + "# python2guile: trying to convert pobj=\"'[True, 5]'\" using stemplate=#\n" + "# p2g_None2SCM_EOL: pobj=\"'[True, 5]'\" stemplate=#\n" + "# p2g_Bool2SCM_BOOL: failed to convert pobj=\"'[True, 5]'\" using stemplate=#\n" + "# p2g_Int2num: failed to convert pobj=\"'[True, 5]'\" using stemplate=#\n" + "# p2g_Long2bignum: failed to convert pobj=\"'[True, 5]'\" using stemplate=#\n" + "# p2g_Float2real: failed to convert pobj=\"'[True, 5]'\" using stemplate=#\n" + "# p2g_Complex2complex: failed to convert pobj=\"'[True, 5]'\" using stemplate=#\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=#\n" + "# guileassoc2pythondict: successful conversion of ()\n" + "# p2g_apply: pobj=\"'()'\" smob-stemplate='python2guile\n" + "# python2guile: trying to convert pobj=\"'()'\" using stemplate=#\n" + "# p2g_None2SCM_EOL: pobj=\"'()'\" stemplate=#\n" + "# p2g_Bool2SCM_BOOL: failed to convert pobj=\"'()'\" using stemplate=#\n" + "# p2g_Int2num: failed to convert pobj=\"'()'\" using stemplate=#\n" + "# p2g_Long2bignum: failed to convert pobj=\"'()'\" using stemplate=#\n" + "# p2g_Float2real: failed to convert pobj=\"'()'\" using stemplate=#\n" + "# p2g_Complex2complex: failed to convert pobj=\"'()'\" using stemplate=#\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 . +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +(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" + "#\n(python-eval #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 #t); unused stemplate=#\n" + "# g2p_null2PyNone: unsuccessful conversion: (python-eval #t) is not null\n" + "# g2p_list2List: unsuccessful conversion: (python-eval #t) is not a list\n" + "# g2p_pair2Tuple: unsuccessful conversion: (python-eval #t) is not a pair\n" + "# g2p_bool2Bool: unsuccessful conversion: (python-eval #t) is not a bool value\n" + "# g2p_num2Int: unsuccessful conversion: (python-eval #t) is not a num value\n" + "# g2p_bignum2Long: unsuccessful conversion: (python-eval #t) is not a bignum value\n" + "# g2p_real2Float: unsuccessful conversion: (python-eval #t) is not a real value\n" + "# g2p_complex2Complex: unsuccessful conversion: (python-eval #t) is not a complex value\n" + "# g2p_string2String: unsuccessful conversion: (python-eval #t) is not a string value\n" + "# g2p_symbol2String: unsuccessful conversion: (python-eval #t) is not a symbol value\n" + "# g2p_keyword2String: unsuccessful conversion: (python-eval #t) is not a keyword value\n" + "# g2p_procedure2PySCMObject: unsuccessful conversion: (python-eval #t) is not a procedure\n" + "# g2p_opaque2Object: the Python object inside opaque pysmob (python-eval #t) is unwrapped\n" + "# python_apply: decoded pfunc \"\"\n" + "# python_apply: decoded function actually to be invoked: \"\"\n" + "# Entered g2p_apply: sobj=(#) stemplate=('g2p_list2Tuple ('g2p_procedure2PySCMObject . #('python2guile 'python2guile 'guile2python # #f)))\n" + "# g2p_list2Tuple - processing item CAR(sobj)=# CAR(stemp)=('g2p_procedure2PySCMObject . #('python2guile 'python2guile 'guile2python # #f))\n" + "# Entered g2p_apply: sobj=# stemplate=('g2p_procedure2PySCMObject . #('python2guile 'python2guile 'guile2python # #f))\n" + "# wrap_scm: was called to wrap #\n" + "# g2p_procedure2PySCMObject: successful conversion: # 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 \"(,)\"\n" + "# guileassoc2pythondict: entry: seeking to convert sobj=() using stemplate=#\n" + "# guileassoc2pythondict: successful conversion of ()\n" + "# python_apply: decoded keyword arguments \"{}\"\n" + "# pyscm_PySCM_call: calling # with args=\"()\" and keywords=\"(null PyObject)\"; stemplate=#('python2guile 'python2guile 'guile2python # #f)\n" + "# p2g_apply: pobj=\"()\" smob-stemplate='python2guile\n" + "# python2guile: trying to convert pobj=\"()\" using stemplate=#\n" + "# p2g_None2SCM_EOL: pobj=\"()\" stemplate=#\n" + "# p2g_Bool2SCM_BOOL: failed to convert pobj=\"()\" using stemplate=#\n" + "# p2g_Int2num: failed to convert pobj=\"()\" using stemplate=#\n" + "# p2g_Long2bignum: failed to convert pobj=\"()\" using stemplate=#\n" + "# p2g_Float2real: failed to convert pobj=\"()\" using stemplate=#\n" + "# p2g_Complex2complex: failed to convert pobj=\"()\" using stemplate=#\n" + "# p2g_String2string: failed to convert pobj=\"()\" using stemplate=#\n" + "# Entered g2p_apply: sobj=2.73 stemplate='guile2python\n" + "# guile2python: entry: seeking to convert sobj=2.73; unused stemplate=#\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=#\n" + "# p2g_None2SCM_EOL: pobj=\"6.8300000000000001\" stemplate=#\n" + "# p2g_Bool2SCM_BOOL: failed to convert pobj=\"6.8300000000000001\" using stemplate=#\n" + "# p2g_Int2num: failed to convert pobj=\"6.8300000000000001\" using stemplate=#\n" + "# p2g_Long2bignum: failed to convert pobj=\"6.8300000000000001\" using stemplate=#\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" + "#\n(python-eval #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 #t); unused stemplate=#\n" + "# g2p_null2PyNone: unsuccessful conversion: (python-eval #t) is not null\n" + "# g2p_list2List: unsuccessful conversion: (python-eval #t) is not a list\n" + "# g2p_pair2Tuple: unsuccessful conversion: (python-eval #t) is not a pair\n" + "# g2p_bool2Bool: unsuccessful conversion: (python-eval #t) is not a bool value\n" + "# g2p_num2Int: unsuccessful conversion: (python-eval #t) is not a num value\n" + "# g2p_bignum2Long: unsuccessful conversion: (python-eval #t) is not a bignum value\n" + "# g2p_real2Float: unsuccessful conversion: (python-eval #t) is not a real value\n" + "# g2p_complex2Complex: unsuccessful conversion: (python-eval #t) is not a complex value\n" + "# g2p_string2String: unsuccessful conversion: (python-eval #t) is not a string value\n" + "# g2p_symbol2String: unsuccessful conversion: (python-eval #t) is not a symbol value\n" + "# g2p_keyword2String: unsuccessful conversion: (python-eval #t) is not a keyword value\n" + "# g2p_procedure2PySCMObject: unsuccessful conversion: (python-eval #t) is not a procedure\n" + "# g2p_opaque2Object: the Python object inside opaque pysmob (python-eval #t) is unwrapped\n" + "# python_apply: decoded pfunc \"\"\n" + "# python_apply: decoded function actually to be invoked: \"\"\n" + "# Entered g2p_apply: sobj=(60.0 #) stemplate=('g2p_list2Tuple 'g2p_real2Float ('g2p_procedure2PySCMObject . #('python2guile 'python2guile 'guile2python # #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)=# CAR(stemp)=('g2p_procedure2PySCMObject . #('python2guile 'python2guile 'guile2python # #f))\n" + "# Entered g2p_apply: sobj=# stemplate=('g2p_procedure2PySCMObject . #('python2guile 'python2guile 'guile2python # #f))\n" + "# wrap_scm: was called to wrap #\n" + "# g2p_procedure2PySCMObject: successful conversion: # 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, )\"\n" + "# guileassoc2pythondict: entry: seeking to convert sobj=() using stemplate=#\n" + "# guileassoc2pythondict: successful conversion of ()\n" + "# python_apply: decoded keyword arguments \"{}\"\n" + "# pyscm_PySCM_call: calling # with args=\"(60.0,)\" and keywords=\"(null PyObject)\"; stemplate=#('python2guile 'python2guile 'guile2python # #f)\n" + "# p2g_apply: pobj=\"(60.0,)\" smob-stemplate='python2guile\n" + "# python2guile: trying to convert pobj=\"(60.0,)\" using stemplate=#\n" + "# p2g_None2SCM_EOL: pobj=\"(60.0,)\" stemplate=#\n" + "# p2g_Bool2SCM_BOOL: failed to convert pobj=\"(60.0,)\" using stemplate=#\n" + "# p2g_Int2num: failed to convert pobj=\"(60.0,)\" using stemplate=#\n" + "# p2g_Long2bignum: failed to convert pobj=\"(60.0,)\" using stemplate=#\n" + "# p2g_Float2real: failed to convert pobj=\"(60.0,)\" using stemplate=#\n" + "# p2g_Complex2complex: failed to convert pobj=\"(60.0,)\" using stemplate=#\n" + "# p2g_String2string: failed to convert pobj=\"(60.0,)\" using stemplate=#\n" + "# p2g_apply: pobj=\"60.0\" smob-stemplate='python2guile\n" + "# python2guile: trying to convert pobj=\"60.0\" using stemplate=#\n" + "# p2g_None2SCM_EOL: pobj=\"60.0\" stemplate=#\n" + "# p2g_Bool2SCM_BOOL: failed to convert pobj=\"60.0\" using stemplate=#\n" + "# p2g_Int2num: failed to convert pobj=\"60.0\" using stemplate=#\n" + "# p2g_Long2bignum: failed to convert pobj=\"60.0\" using stemplate=#\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=#\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=#\n" + "# p2g_None2SCM_EOL: pobj=\"67.700000000000003\" stemplate=#\n" + "# p2g_Bool2SCM_BOOL: failed to convert pobj=\"67.700000000000003\" using stemplate=#\n" + "# p2g_Int2num: failed to convert pobj=\"67.700000000000003\" using stemplate=#\n" + "# p2g_Long2bignum: failed to convert pobj=\"67.700000000000003\" using stemplate=#\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" + "^#$" + (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 . +# +######################################################################## + +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 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 . +# +######################################################################## + +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 . +# +######################################################################## + +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 . +# +######################################################################## + +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 . +# +######################################################################## + +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 . +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +(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 . +// +//////////////////////////////////////////////////////////////////////// +// +// 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 . +// +//////////////////////////////////////////////////////////////////////// + +#include // Must be first header file +#include + +//////////////////////////////////////////////////////////////////////// +// 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 -- cgit v1.2.3