Darkart Python Interface User's Guide
Build
I haven't prepared the makefile yet, so it will need some manual settings.
Compile ./c/py.c
file:
cc -fPIC -shared -L/Library/Frameworks/Python.framework/Versions/3.7/lib/ -lpython3.7 -o ../py.so py.c
Note that depending on the system, you may need to manually specify the path to python.h
.
Definition
*po
representing a pointer to a Python Object reference.
Most of Py-call procedure accept or return a *po
, in Scheme level, it's a memory address which is pointer a specific Python Object.
In the programme Py-call, the Class, the Method, the Number, all of it is *po
. In other words, we just pass the memory address in Scheme level.
This fits the philosophy of Scheme: "Parameter passing is by value, but the values are references."
This is also why the python library we call via the chez scheme is efficient than via the python:We just pass some pointer via binary interface, no extra overheadr.
When you write a code with Py-call, keep all the values in type of *po, don't convert it into scheme data until get the final result.
In this document, po may be followed by the type of Python object it points to, like: po etc.
And po{value} means a `po` point to this value.
Py-call
Library (darkart py call)
Py-init and Py-fin
(py-init)
and (py-fin)
is the alias of (py-initialize)
and (py-finalize)
.
The codes which manipulating the python library must between (py-init)
and (py-fin)
.
Note that you can use it only on time, or you risk to get a
Exception: invalid memory reference. Some debugging context lost
If you want to write a library which wrap some Python Library, you don't have to use (py-init)
and (py-fin)
in the library code. like: https://github.com/guenchi/numpy/blob/master/numpy.sc
Python Library
The library has to install, for exemple via Pip, before darkart call it.
You can go to python envirement do something like import numpy
to test it.
procedure: (py-import libraryName)
symbol -> *po
Don't forget store the memory addres that procedure return, like:
(define np (py-import 'numpy))
To repackage a Library to Scheme, there is a Exemple: https://github.com/guenchi/numpy/blob/master/numpy.sc
Point
procedure: (py-get object object)
*po -> symbol -> *po
This is the point syntax of Python. Like:
(define array (py-get np 'array)) = numpy.array
(define pi (py-get np 'pi)) = numpy.pi
Function
procedure: (py-call callable args ...)
*po -> any -> ... -> *po
Exemple:
(py-call array (list->plist int '(1 2 3 4 5)))
=> *op{[1, 2, 3, 4, 5]}
(define np-array
(lambda (x)
(py-call array x)))
(np-array (list->plist int '(1 2 3 4 5)))
=> *op{[1, 2, 3, 4, 5]}
There is a helper to generate a procedure with *po
procedure: (py-func object method)
*po -> symbol -> func
Exemple:
(define np-array (py-func np 'array))
(np-array (list->plist int '(1 2 3 4 5)))
=> *op{[1, 2, 3, 4, 5]}
Some python function need named arguments, use:
procedure: ((py-call* callable args ...) namedArgs)
*po -> any -> ... -> alist -> *po
The alist is like: '((symbol<Name> . *po) ...)
Exemple:
((py-call* array (list->plist int '(1 2 3 4 5)))
`(('dtype . ,(str "float"))))
=> *op{[1.0, 2.0, 3.0, 4.0, 5.0]}
(define-syntax np-array
(syntax-rules ()
((_ e)(py-call *array e))
((_ e (k v) ...)
((py-call* *array e)
(list (cons k v) ...)))))
(np-array (list->plist int '(1 2 3 4 5)))
=> *op{[1, 2, 3, 4, 5]}
(np-array (list->plist int '(1 2 3 4 5)) ('dtype (str "float")))
=> *op{[1.0, 2.0, 3.0, 4.0, 5.0]}
There is also a helper to generate a procedure which need named argument with *po
procedure: (py-func* object method)
*po -> symbol -> func
Exemple:
(define np-array (py-func* np 'array))
((np-array
(list->plist int '(1 2 3 4 5)))
`(('dtype . ,(str "float"))))
=> *op{[1.0, 2.0, 3.0, 4.0, 5.0]}
Args helpers:
procedure: (py-args *po ...)
procedure: (py-args* '(*po ...))
*po -> ... -> *po
This procedure is use to prepare a tuple of arguments for Python's function.
It usually won't be used directly because it's included in (py-call)
,(py-call*)
,(py-func)
and(py-func*)
.
Number and String
procedure: (pint? pythonData)
alias: (*int?)
procedure: (pflt? pythonData)
alias: (*flt?)
procedure: (pcplx? pythonData)
alias: (*cplx?)
procedure: (pstr? pythonData)
alias: (*str?)
*po -> boolean
Type Check for Python's number and string.
procedure: (s->pint int)
alias: (int)
int -> *po
procedure: (s->pflt float)
alias: (flt)
float -> *po
procedure: (s->pcplx cflonum)
alias: (cplx)
cflonum -> *po
procedure: (s->pstr string)
alias: (str)
string -> *po
Covert a Scheme data to Python data.
Exemple:
(int 8) => *po{8}
(flt 3.1415926) => *po{3.1415926}
(str "foo") => *po{"foo"}
procedure: (p->sint pyInt)
alias: (*int)
*po -> number
procedure: (p->sflt pyFloat)
alias: (*flt)
*po -> number
procedure: (p->scplx pyComplex)
alias: (*cplx)
*po -> cflonum
procedure: (p->sstr pyStr)
alias: (*str)
*po -> string
Covert a Python data to Scheme data.
Exemple:
(*int (int 8)) => 8
(*flt (flt 3.1415926)) => 3.1415926
(*str (str "foo")) => "foo"
procedure: (s->ptype schemeData)
alias: (auto)
any -> *po
procedure: (p->stype pythonData)
alias: (*auto)
*po -> any
Automatic simple type detection and conversion.
Exemple:
(*auto (int 8)) => 8
(*auto (flt 3.1415926)) => 3.1415926
(*auto (str "foo")) => "foo"
List and Tuple
procedure: (list->plist list)
procedure: (list->ptuple list)
list -> *po
procedure: (vector->plist vector)
procedure: (vector->ptuple vector)
vector -> *po
procedure: (list->plist type list)
procedure: (list->ptuple type list)
func -> list -> *po
procedure: (vector->plist type vector)
procedure: (vector->ptuple type vector)
func -> vector -> *po
procedure: (list->plist* '(*po ...))
procedure: (list->ptuple* '(*po ...))
list (*po) -> *po
procedure: (vector->plist* #(*po ...))
procedure: (vector->ptuple* #(*po ...))
vector (*po) -> *po
Covert a Scheme's List and Vector to Python's List and Tuple.
The type covert procedure will be: int
flt
cplx
str
or nothing.
If there is no specific type transfer function, the program will automatically check the type, but it is more efficient when specifying the type.
Exemple:
(list->plist int '(1 2 3 4 5 6 7 8))
(vector->plist* `#(,(int 1) ,(flt 3.14159) ,(str "foo")))
Attention that if don't specific list / vector's type, you have to covert data to *po before make list / vector.
procedure: (plist->list pyList)
procedure: (ptuple->list pyTuple)
*po -> list
procedure: (plist->vector pyList)
procedure: (ptuple->vector pyTuple)
*po -> vector
procedure: (plist->list type pyList)
procedure: (ptuple->list type pyTuple)
func -> *po -> list
procedure: (plist->vector type pyList)
procedure: (ptuple->vector type pyTuple)
func -> *po -> vector
procedure: (plist->list* pyList)
procedure: (ptuple->list* pyTuple)
*po -> list (*po)
procedure: (plist->vector* pyList)
procedure: (ptuple->vector* pyTuple)
*po -> vector (*po)
The type procedure will be: *int
*flt
*cplx
*str
or nothing.
If there is no specific type transfer function, the program will automatically check the type, but it is more efficient when specifying the type.
Exemple:
(plist->list *int (list->plist int '(1 2 3 4 5 6 7 8)))
=> (1 2 3 4 5 6 7 8)
(plist->list (list->plist* `(,(int 1) ,(flt 3.14159) ,(str "foo"))))
=> (1 3.14159 "foo")
(plist->list* (list->plist* `(,(int 1) ,(flt 3.14159) ,(str "foo"))))
=> (*po{1} *po{3.14159} *po{"foo"})
In last case it return a list of Memory Adresse of po. You can use (int), (flt) or (str) to convert it to Scheme Data.
Like:
(*int (car (plist->list* (list->plist* `(,(int 1) ,(flt 3.14159) ,(str "foo"))))))
=> 1
(*flt (cadr (plist->list* (list->plist* `(,(int 1) ,(flt 3.14159) ,(str "foo"))))))
=> 3.14159
(*str (caddr (plist->list* (list->plist* `(,(int 1) ,(flt 3.14159) ,(str "foo"))))))
=> "foo"
Numeric Operations
procedure: (py-add pyNum pyNum)
procedure: (py-sub pyNum pyNum)
procedure: (py-mul pyNum pyNum)
procedure: (py-div pyNum pyNum)
procedure: (py-fdiv pyNum pyNum)
procedure: (py-mod pyNum pyNum)
procedure: (py-lsh pyNum pyNum)
procedure: (py-rsh pyNum pyNum)
procedure: (py-and pyNum pyNum)
procedure: (py-or pyNum pyNum)
procedure: (py-xor pyNum pyNum)
*po -> *po -> *po
procedure: (py-inv pyNum)
procedure: (py-abs pyNum)
procedure: (py-neg pyNum)
*po -> *po
*po in here may be a number or a list, tuple, array(numpy) of number.
Exemple:
(py-add (int 3) (int 5)) => *po{8}
(py-mul (int 8) (list->plist int' '(1 2 3 4 5))) => *po{[8, 16, 24, 32, 40]}
(py-abs (int -5)) => *po{5}
Python's list
procedure: (plist? pyObject)
*po -> boolean
procedure: (make-plist size)
int -> *po
procedure: (plist-length pyList)
*po -> int
procedure: (plist-ref pyList index ...)
*po -> int -> ... -> *po
procedure: (plist-set! pyList index ... pyObject)
*po -> int -> ... -> *po -> 0 on success or -1 on failure.
procedure: (plist-sref pyList begin end)
*po -> int -> int -> *po
procedure: (plist-sref pyList index ... (begin end))
*po -> int -> ... -> (int int) -> *po
procedure: (plist-sset! pyList begin end pyList)
*po -> int -> int -> *po -> 0 on success or -1 on failure.
procedure: (plist-sset! pyList index ... (begin end) pyList)
*po -> int -> ... -> (int int) -> *po -> 0 on success or -1 on failure.
procedure: (plist-insert! pyList index pyObject)
*po -> int -> *po -> 0 on success or -1 on failure.
procedure: (plist-append! targetPyList pyList)
*po -> *po -> 0 on success or -1 on failure.
procedure: (plist-sort!)
0 on success or -1 on failure.
procedure: (plist-reverse!)
0 on success or -1 on failure.
Python's tuple
procedure: (ptuple? *po)
return: boolean
procedure: (make-ptuple int<index>)
return: *po<tuple>
procedure: (ptuple-length *po<tuple>)
return: int<index>
procedure: (ptuple-ref *po<tuple> int<index> ...)
return: *po
procedure: (ptuple-set! *po<tuple> int<index> ... *po)
return: 0 on success or -1 on failure.
procedure: (ptuple-sref *po<tuple> int<begin> int<end>)
procedure: (ptuple-sref *po<tuple> int<index> ... (int<begin> int<end>))
return: *po<tuple>{slice}
Python's set
procedure: (pset? *po)
return: boolean
procedure: (make-pset *po<iterable>)
return: *po<set>
procedure: (pset-length *po<set>)
return: int<size>
procedure: (pset-contains *po<set>)
return: 1 if found, 0 if not found, and -1 if an error is encountered.
procedure: (pset-add! *po<set> *po{key})
return: 0 on success or -1 on failure.
procedure: (pset-del! *po<set> *po{key})
return: 1 if found and removed, 0 if not found and no action taken, and -1 if an error is encountered.
procedure: (pset-pop! *po<set>)
return: *po{arbitrary_key}
procedure: (pset-clear! *po<set>)
Python's sequence
procedure: (psequ? *po)
return: boolean
procedure: (psequ->plist *po<sequence>)
return: *po<list>
procedure: (psequ->ptuple *po<sequence>)
return: *po<tuple>
procedure: (psequ-length *po<sequence>)
return: int<size>
procedure: (psequ-append *po<sequence> *po<sequence>)
return: *po<sequence>
procedure: (psequ-repeat *po<sequence> int)
return: *po<sequence>
procedure: (psequ-ref *po<sequence> int)
return: *po
procedure: (psequ-set! *po<sequence> int *po)
return: 0 on success or -1 on failure.
procedure: (psequ-del! *po<sequence> int)
return: 0 on success or -1 on failure.
procedure: (psequ-sref *po<sequence> int int)
return: *po<sequence>{slice}
procedure: (psequ-sset! *po<sequence> int int *po<sequence>)
return: 0 on success or -1 on failure.
procedure: (psequ-sdel! *po<sequence> int int)
return: 0 on success or -1 on failure.
procedure: (psequ-count *po<sequence> *po<value>)
return: int
procedure: (psequ-contains *po<sequence> *po<value>)
return: 1 for true 0 for false -1 for fail
procedure: (psequ-index *po<sequence> *po<value>)
return: int
Python's dict
procedure: (pdict? *po)
return: boolean
procedure: (make-pdict)
return: *po<dict>
procedure: (pdict-length *po<dict>)
return: int<size>
procedure: (pdict-ref *po<dict> string<key)
return: *po
procedure: (pdict-ref* *po<dict> *po{key})
return: *po
procedure: (pdict-set! *po<dict> string<key> *po<value>)
return: 0 on success or -1 on failure.
procedure: (pdict-set*! *po<dict> *po{key} *po<value>)
return: 0 on success or -1 on failure.
procedure: (pdict-del! *po<dict> string<key>)
return: 0 on success or -1 on failure.
procedure: (pdict-del*! *po<dict> *po{key})
return: 0 on success or -1 on failure.
procedure: (pdict-clear! *po<dict>)
return: <void>
procedure: (pdict-copy *po<dict>)
return: new *po<dict>
procedure: (pdict-keys *po<dict>)
return: *po<list>{key...}
procedure: (pdict-values)
return: *po<list>{value...}
procedure: (pdict-items)
return: *po<list>{key...values...}
Python's mapping
procedure: (pmap? *po)
return: boolean
procedure: (pmap-size *po<mapping>)
return: int
procedure: (pmap-has? *po<mapping> string<key>)
return: boolean
procedure: (pmap-has*? *po<mapping> *po{key})
return: boolean
procedure: (pmap-ref *po<mapping> string<key>)
return: *po
procedure: (pmap-set! *po<mapping> string<key> *po{value})
return: 0 on success or -1 on failure.
Display
procedure: (py-display *po)
Display anything of Python Object.
Py-FFI
Library (darkart py ffi)
This file contains the underlying interface to Python. Usually you won't use it.
Interpreter
procedure: (py-initialize)
procedure: (py-finalize)
See (py-init)
and (py-fin)
in (darkart py call)
procedure: (py-incref)
procedure: (py-decref)
procedure: (py-new-interpreter)
procedure: (py-end-interpreter)
Math
procedure: (py/number-add *po *po)
procedure: (py/number-subtract *po *po)
procedure: (py/number-multiply *po *po)
procedure: (py/number-divide *po *po)
procedure: (py/number-floor-divide *po *po)
procedure: (py/number-divmod *po *po)
procedure: (py/number-lshift *po *po)
procedure: (py/number-rshift *po *po)
procedure: (py/number-and *po *po)
procedure: (py/number-or *po *po)
procedure: (py/number-xor *po *po)
procedure: (py/number-invert *po)
procedure: (py/number-absolute *po)
procedure: (py/number-negative *po)
return: *po
*po in here may be a number or a list, tuple, array(numpy) of number.
Number
procedure: (py/long-from-long number)
procedure: (py/long-from-unsigned-long number)
procedure: (py/long-from-longlong number)
procedure: (py/long-from-unsigned-longlong number)
procedure: (py/long-from-double number)
procedure: (py/long-from-size_t size_t)
procedure: (py/long-from-ssize_t ssize_t)
return: *po<long>
procedure: (py/float-from-double number)
return: *po<float>
Pass Scheme number to Python.
procedure: (py/int-as-long *po<int>)
procedure: (py/int-as-ssize_t *po<int>)
procedure: (py/long-as-long *po<long>)
procedure: (py/long-as-unsigned-long *po<long>)
procedure: (py/long-as-longlong *po<long>)
procedure: (py/long-as-unsigned-longlong *po<long>)
procedure: (py/long-as-double *po<float>)
procedure: (py/long-as-ssize_t *po<float>)
procedure: (py/float-as-double *po<float>)
return: number
Pass Python number to Scheme.
String
procedure: (py/bytes-check? *po)
return: boolean
procedure: (py/bytes-from-string string)
return: *po<string>
Pass Scheme string to Python.
procedure: (py/bytes-as-string *po<string>)
return: string
Pass Python string to Scheme.
List
procedure: (py/list-check? *po)
return: boolean
procedure: (py/list-new int<size>)
return: *po<list>
procedure: (py/list-size *po<list>)
return: int<size>
procedure: (py/list-get-item *po<list> int<index>)
return: *po
procedure: (py/list-set-item! *po<list> int<index> *po)
return: 0 on success or -1 on failure.
procedure: (py/list-get-slice *po<list> int int)
return: *po<list>{slice}
procedure: (py/list-set-slice! *po<list> int int *po<list>)
return: 0 on success or -1 on failure.
procedure: (py/list-insert! *po<list> int<index> *po)
return: 0 on success or -1 on failure.
procedure: (py/list-append! *po<list-target> *po<list>)
return: 0 on success or -1 on failure.
procedure: (py/list-sort!)
return: 0 on success or -1 on failure.
procedure: (py/list-reverse!)
return: 0 on success or -1 on failure.
Tuple
procedure: (py/tuple-check? *po)
return: boolean
procedure: (py/tuple-new int<index>)
return: *po<tuple>
procedure: (py/tuple-size *po<tuple>)
return: int<index>
procedure: (py/tuple-get-item *po<tuple> int<index>)
return: *po
procedure: (py/tuple-set-item! *po<tuple> int<index> *po)
return: 0 on success or -1 on failure.
procedure: (py/tuple-get-slice *po<tuple> int int)
return: *po<tuple>{slice}
Set
procedure: (py/set-check? *po)
return: boolean
procedure: (py/set-new *po<iterable>)
return: *po<set>
procedure: (py/set-size *po<set>)
return: int<size>
procedure: (py/set-contains *po<set>)
return: 1 if found, 0 if not found, and -1 if an error is encountered.
procedure: (py/set-add! *po<set> *po{key})
return: 0 on success or -1 on failure.
procedure: (py/set-discard! *po<set> *po{key})
return: 1 if found and removed, 0 if not found and no action taken, and -1 if an error is encountered.
procedure: (py/set-pop! *po<set>)
return: *po{arbitrary_key}
procedure: (py/set-clear! *po<set>)
Sequence
procedure: (py/sequence-check? *po)
return: boolean
procedure: (py/sequence-list *po<sequence>)
return: *po<list>
procedure: (py/sequence-tuple *po<sequence>)
return: *po<tuple>
procedure: (py/sequence-size *po<sequence>)
return: int<size>
procedure: (py/sequence-concat *po<sequence> *po<sequence>)
return: *po<sequence>
procedure: (py/sequence-repeat *po<sequence> int)
return: *po<sequence>
procedure: (py/sequence-get-item *po<sequence> int)
return: *po
procedure: (py/sequence-set-item! *po<sequence> int *po)
return: 0 on success or -1 on failure.
procedure: (py/sequence-del-item! *po<sequence> int)
return: 0 on success or -1 on failure.
procedure: (py/sequence-get-slice *po<sequence> int int)
return: *po<sequence>{slice}
procedure: (py/sequence-set-slice! *po<sequence> int int *po<sequence>)
return: 0 on success or -1 on failure.
procedure: (py/sequence-del-slice! *po<sequence> int int)
return: 0 on success or -1 on failure.
procedure: (py/sequence-count *po<sequence> *po<value>)
return: int
procedure: (py/sequence-contains *po<sequence> *po<value>)
return: 1 for true 0 for false -1 for fail
procedure: (py/sequence-index *po<sequence> *po<value>)
return: int
Dictionary
procedure: (py/dict-check? *po)
return: boolean
procedure: (py/dict-new)
return: *po<dict>
procedure: (py/dict-size *po<dict>)
return: int<size>
procedure: (py/dict-get-item *po<dict> *po{key})
return: *po
procedure: (py/dict-get-item-string *po<dict> string<key>)
return: *po
procedure: (py/dict-set-item! *po<dict> *po{key} *po<value>)
return: 0 on success or -1 on failure.
procedure: (py/dict-set-item-string! *po<dict> string<key> *po<value>)
return: 0 on success or -1 on failure.
procedure: (py/dict-del-item! *po<dict> *po{key})
return: 0 on success or -1 on failure.
procedure: (py/dict-del-item-string! *po<dict> string<key>)
return: 0 on success or -1 on failure.
procedure: (py/dict-clear! *po<dict>)
return: <void>
procedure: (py/dict-copy *po<dict>)
return: new *po<dict>
procedure: (py/dict-keys *po<dict>)
return: *po<list>{key...}
procedure: (py/dict-values)
return: *po<list>{value...}
procedure: (py/dict-items)
return: *po<list>{key...values...}
Mapping
procedure: (py/mapping-check? *po)
return: boolean
procedure: (py/mapping-size *po<mapping>)
return: int
procedure: (py/mapping-has-key-string? *po<mapping> string<key>)
return: boolean
procedure: (py/mapping-has-key? *po<mapping> *po{key})
return: boolean
procedure: (py/mapping-get-item-string *po<mapping> string<key>)
return: *po
procedure: (py/mapping-set-item-string! *po<mapping> string<key> *po{value})
return: 0 on success or -1 on failure.
Run
procedure: (py/run-simple-file)
procedure: (py/run-file)
procedure: (py/run-file-exflags)
procedure: (py/run-simple-string)
procedure: (py/run-string)
procedure: (py/run-string-flags)
Library
procedure: (py/import-import)
procedure: (py/import-import-module string<library>)
return: *po<library>
procedure: (py/import-exec-code-module)
Module
procedure: (py/module-new)
procedure: (py/module-get-dict)
procedure: (py/module-get-name)
procedure: (py/module-get-filename)
Object
procedure: (py/object-get-attr-string *po string<keyword>)
return: *po<object,method>
procedure: (py/object-call *po<callable> *po<tulpe>{argument...} *po<dict>{name:argument...})
return: *po
procedure: (py/object-call-object *po<callable> *po<tuple>{argument...)
return: *po
procedure: (py/object-str *po)
return: *po<string>
procedure: (py/callable-check? *po)
return: boolean
Compile
procedure: (py-compile-string)