[clpython-devel] Calling Python functions from Lisp [Re Franz thread: spr35431]

Stuart C. Shapiro shapiro at cse.Buffalo.EDU
Fri Jan 9 20:48:46 UTC 2009


Willem,

Thanks a lot.  Your examples worked, and using them, I wrote a set of 
functions that seem to do what I want.  I'll include them below, but 
first, here's a demo.

The python file, called /projects/shapiro/clpython/test.py
-----------------------------
# A Python file to use to test clpython

def test():
    print "I am a Python function."
    return "I am done."

def pyplus(x,y):
    return x+y

def pyminus(x,y):
    return x-y

print("The Python test file has been run.")
-----------------------------

My use of it, after loading CLPython and my new utility:
-----------------------------
cl-user(3): :pwd
Lisp's current working directory is "/home/csefaculty/shapiro/"
*default-pathname-defaults* is #P"/home/csefaculty/shapiro/"

cl-user(4): (clpython:pypaths "/projects/shapiro/clpython/")
("/projects/shapiro/clpython/")

cl-user(5): (clpython:pyimport 'test)
; Fast loading /projects/shapiro/clpython/test.fasl
The Python test file has been run.
#<module `test'
   Src: /net/projects/shapiro/clpython/test.py
   Binary: /net/projects/shapiro/clpython/test.fasl @ #x723d675a>

cl-user(6): (* (clpython:pycall 'test 'pyplus 3 5)
                     (clpython:pycall 'test 'pyminus 7 4))
24
-----------------------------

My utility.  It could probably be made more sophisticated.
-----------------------------
;;; Utility for calling Python Functions from Common Lisp
;;; Stuart C. Shapiro
;;; Uses CLPython
;;; and additional suggestions from Willem Broekema
;;; January 9, 2009

(in-package :clpython)

(export '(pypaths pyimport pycall))

(setf *habitat* (make-habitat))

(defvar *pymodules* (make-hash-table)
  "A map from module names to module structures.")

(defun pypaths (&rest paths)
  "Adds the paths to the list of paths that are tried
       when locating a module in order to import it."
  (setf cl-user::*clpython-module-search-paths*
    (append cl-user::*clpython-module-search-paths* paths)))

(defun pyimport (module)
  ;; Imports the given Python module, and creates an entry for it in 
*pymodules*"
  (setf (gethash module *pymodules*)
    (py-import (list module))))

(defun pycall (module fn &rest args)
  "Calls the function named fn of the module named module on the given 
arguments,
       and returns what it returns."
  (unless (gethash module *pymodules*)
    (error "There is no loaded module named ~S." module))
  (apply (attr (gethash module *pymodules*) fn) args))
-----------------------------

Thanks, again.

stu

Willem Broekema wrote:
> Hello Stuart, welcome to the list.
>
> On Thu, Jan 8, 2009 at 4:56 PM, Stuart C. Shapiro
> <shapiro at cse.buffalo.edu> wrote:
>   
>> cl-user(3): (clpython:run "import test; test.test()")
>> Warning: *import-recompiled-files* = #<equal hash-table with 0 entries>
>> Warning: /net/projects/shapiro/clpython/test.fasl not in
>>         #<equal hash-table with 0 entries>
>> ; Fast loading /net/projects/shapiro/clpython/test.fasl
>> [...]
>> First question:  Why the Warning messages?  How can I get rid of them?
>>     
>
> Those were debug messages accidentally left in. They have been removed
> now. Sorry about that.
>
>   
>> Notice that I can make use of the values returned by the Python function:
>> -----------------------------
>> cl-user(4): (setf x (clpython:run "import test; test.test()"))
>> Warning: *import-recompiled-files* = #<equal hash-table with 0 entries>
>> Warning: /net/projects/shapiro/clpython/test.fasl not in
>>         #<equal hash-table with 0 entries>
>> ; Fast loading /net/projects/shapiro/clpython/test.fasl
>> The Python test file has been run.
>> Warning: *import-recompiled-files* = #<equal hash-table with 0 entries>
>> Warning: /net/projects/shapiro/clpython/test.fasl not in
>>         #<equal hash-table with 0 entries>
>> I am a Python function.
>> "I am done."
>>
>> cl-user(5): x
>> "I am done."
>>     
>
> Yes, the last value of the Python expression is returned.
>
>   
>> What I'd really like to do now is to call test.test() as much as
>> possible as though it were a Common Lisp function.  One possible way is:
>> -----------------------------
>> cl-user(6): (clpython:run "test.test()")
>> Error: NameError: Variable `test' is unbound.
>>  [condition type: NameError]
>>
>> Restart actions (select using :continue):
>>  0: Enter a Lisp value to use for `test'.
>>  1: Return to Top Level (an "abort" restart).
>>  2: Abort entirely from this (lisp) process.
>> -----------------------------
>> But, the name of the function does not seem to survive from one call of
>> clpython:run to another.
>>
>> Final question: How can I do this?
>>     
>
> There is not an elegent way for that yet (I'm working on it), but the
> following works:
>
> cl-user(6): :pa clpython
> clpython(7): (setf *habitat* (make-habitat)) ;; required for py-import
> #<habitat @ #x10fd5472>
> clpython(8): (setq m (py-import '(foo)))
> #<module `foo'
>    Src: /Users/willem/dev/lisp/tmp/git-clpython/foo.py
>    Binary: /Users/willem/.fasl/allegro-8.1m-macosx-x86/Users/willem/dev/lisp/tmp/git-clpython/foo.fasl
> @ #x10a5a5a2>
> clpython(9): (attr m 'f) ;; attribute lookup
> #<python-function f @ #x10a5a872>
> clpython(10): (funcall *)
> 24
> None
>
> Where foo.y is:
>  def f(): print 24
>
> - Willem
>   

-- 
Stuart C. Shapiro
Professor, Department of Computer Science and Engineering
University at Buffalo, The State University of New York
201 Bell Hall, Box 602000, Buffalo, NY 14260-2000, U.S.A.
PHONE:  716-645-3180x125    FAX:  716-645-3464
shapiro at cse.buffalo.edu     http://www.cse.buffalo.edu/~shapiro/





More information about the Clpython-devel mailing list