python: feature to create python callbacks/functions, now you can call hofs like map and reduce

db4
Björn Lindqvist 2014-10-24 01:01:15 +02:00 committed by John Benediktsson
parent ba564d1b78
commit 4029bf7a17
5 changed files with 120 additions and 9 deletions

View File

@ -1,5 +1,6 @@
USING: alien alien.c-types alien.destructors alien.libraries
alien.libraries.finder alien.syntax assocs kernel sequences system ;
alien.libraries.finder alien.syntax assocs classes.struct kernel sequences
system ;
IN: python.ffi
! << "python" { "3.0" "3" "2.7" "2.6" } ! Python 3 has a different api, enable someday
@ -14,6 +15,32 @@ LIBRARY: python
C-TYPE: PyObject
! Methods
CONSTANT: METH_OLDARGS 0x0000
CONSTANT: METH_VARARGS 0x0001
CONSTANT: METH_KEYWORDS 0x0002
CONSTANT: METH_NOARGS 0x0004
CONSTANT: METH_O 0x0008
CONSTANT: METH_CLASS 0x0010
CONSTANT: METH_STATIC 0x0020
CONSTANT: METH_COEXIST 0x0040
C-TYPE: PyCFunction
STRUCT: PyMethodDef
{ ml_name void* }
{ ml_meth PyCFunction* }
{ ml_flags int }
{ ml_doc c-string } ;
FUNCTION: PyObject* PyCFunction_NewEx ( PyMethodDef* ml,
PyObject* self,
PyObject* module ) ;
CALLBACK: PyObject* PyCallback ( PyObject* self,
PyObject* args,
PyObject* kw ) ;
! Top-level
FUNCTION: c-string Py_GetVersion ( ) ;
FUNCTION: void Py_Initialize ( ) ;
@ -61,6 +88,8 @@ FUNCTION: int PyList_Size ( PyObject* l ) ;
! Steals the reference
FUNCTION: int PyList_SetItem ( PyObject* l, int pos, PyObject* o ) ;
! Sequences
FUNCTION: int PySequence_Check ( PyObject* o ) ;
! Modules
FUNCTION: c-string PyModule_GetName ( PyObject* module ) ;
@ -108,6 +137,9 @@ FUNCTION: long PyLong_AsLong ( PyObject* o ) ;
! Floats
FUNCTION: PyObject* PyFloat_FromDouble ( double d ) ;
! Types
FUNCTION: int PyType_Check ( PyObject* obj ) ;
! Reference counting
FUNCTION: void Py_IncRef ( PyObject* o ) ;
FUNCTION: void Py_DecRef ( PyObject* o ) ;

View File

@ -1,4 +1,5 @@
USING: alien.c-types alien.data kernel python.errors python.ffi ;
USING: alien.c-types alien.data classes.struct kernel python.errors
python.ffi ;
IN: python.objects
! Objects
@ -58,3 +59,10 @@ IN: python.objects
: py-list-set-item ( obj pos val -- )
unsteal-ref PyList_SetItem check-zero ;
! Functions
: <py-cfunction> ( alien -- cfunction )
f swap METH_VARARGS f PyMethodDef <struct-boa> f f
! It's not clear from the docs whether &Py_DecRef is right for
! PyCFunction_NewEx, but I'm betting on it.
PyCFunction_NewEx check-new-ref ;

View File

@ -1,5 +1,6 @@
USING: alien destructors help.markup help.syntax python python.throwing
quotations ;
IN: python
USING: python python.throwing help.markup help.syntax ;
HELP: py-initialize
{ $description "Initializes the python binding. This word must be called before any other words in the api can be used" } ;
@ -19,6 +20,21 @@ HELP: >py
}
{ $see-also py> } ;
HELP: quot>py-callback
{ $values { "quot" { $quotation ( args kw -- ret ) } } { "alien" alien } }
{ $description "Creates a python-compatible alien callback from a quotation." }
{ $examples
"This is how you create a callback which returns the double of its first positional parameter:"
{ $unchecked-example
"USING: python ;"
": double-fun ( -- alien ) [ drop first 2 * ] quot>py-callback ;"
}
} ;
HELP: with-quot>py-cfunction
{ $values { "alien" alien } { "quot" quotation } }
{ $description "Wrapper for " { $link with-callback } " to be used when passing functions as arguments to Python functions. It should be used in conjunction with " { $link quot>py-callback } " which creates the callbacks this word consumes." } ;
HELP: python-error
{ $error-description "When Python throws an exception, it is translated to this Factor error. " { $slot "type" } " is the class name of the python exception object, " { $slot "message" } " its string and " { $slot "traceback" } " a sequence of traceback lines, if the error has one, or " { $link f } " otherwise." } ;
@ -26,7 +42,7 @@ ARTICLE: "python" "Python binding"
"The " { $vocab-link "python" } " vocab and its subvocabs implements a simple binding for libpython, allowing factor code to call native python."
$nl
"Converting to and from Python:"
{ $subsections >py py> }
{ $subsections >py py> quot>py-callback }
"Error handling:"
{ $subsections python-error }
"Initialization and finalization:"
@ -35,4 +51,5 @@ $nl
{ $subsections py-import }
"The vocab " { $vocab-link "python.syntax" } " implements a higher level factorific interface on top of the lower-level constructs in this vocab. Prefer to use that vocab most of the time."
{ $notes "Sometimes the embedded python interpreter can't find or finds the wrong load path to it's module library. To counteract that problem it is recommended that the " { $snippet "PYTHONHOME" } " environment variable is set before " { $link py-initialize } " is called. E.g:" }
{ $code "\"C:/python27-64bit/\" \"PYTHONHOME\" set-os-env" } ;
{ $code "\"C:/python27-64bit/\" \"PYTHONHOME\" set-os-env" }
{ $warning "All code that calls Python words should always be wrapped in a " { $link with-destructors } " context. The reason is that the words add references to Pythons internal memory heap which are removed when the destructors trigger." } ;

View File

@ -1,4 +1,4 @@
USING: alien.c-types alien.data arrays assocs command-line fry
USING: alien alien.c-types alien.data arrays assocs command-line fry
hashtables init io.encodings.utf8 kernel namespaces
python.errors python.ffi python.objects sequences
specialized-arrays strings vectors ;
@ -100,5 +100,15 @@ ERROR: missing-type type ;
dup "__class__" getattr "__name__" getattr PyString_AsString
py-type-dispatch get ?at [ call( x -- x ) ] [ missing-type ] if ;
! Callbacks
: quot>py-callback ( quot: ( args kw -- ret ) -- alien )
'[
[ nip ] dip
[ [ py> ] [ { } ] if* ] bi@ @ >py
] PyCallback ; inline
: with-quot>py-cfunction ( alien quot -- )
'[ <py-cfunction> @ ] with-callback ; inline
[ py-initialize ] "py-initialize" add-startup-hook
[ py-finalize ] "py-finalize" add-shutdown-hook

View File

@ -1,6 +1,6 @@
USING: accessors arrays assocs continuations destructors fry io.files.temp
kernel math namespaces python python.ffi python.objects python.syntax
sequences sets splitting tools.test unicode.categories ;
USING: accessors arrays assocs continuations destructors destructors.private
fry io.files.temp kernel math namespaces python python.ffi python.objects
python.syntax sequences sets splitting tools.test unicode.categories ;
IN: python.syntax.tests
: py-test ( result quot -- )
@ -77,6 +77,12 @@ PY-FROM: sys => getrefcount ( obj -- n ) ;
[ 0 py-tuple-get-item getrefcount py> ] tri -
] py-test
{ t } [
6 <py-tuple>
[ getrefcount py> 1 - ]
[ always-destructors get [ alien>> = ] with count ] bi =
] py-test
PY-METHODS: file =>
close ( self -- )
fileno ( self -- n )
@ -166,3 +172,41 @@ PY-FROM: wsgiref.simple_server => make_server ( iface port callback -- httpd ) ;
[ [ 987 >py basename drop ] ignore-errors ] with-destructors
] times
] unit-test
! Working with types
PY-METHODS: obj =>
__name__ ( self -- n ) ;
PY-QUALIFIED-FROM: types => UnicodeType ( -- ) ;
{ "unicode" } [
types:$UnicodeType $__name__ py>
] py-test
! Make callbacks
PY-QUALIFIED-FROM: __builtin__ =>
None ( -- )
map ( func seq -- seq' )
reduce ( func seq -- seq' ) ;
{ V{ 1 2 3 } } [
__builtin__:$None { 1 2 3 } >py __builtin__:map py>
] py-test
: double-fun ( -- alien )
[ drop first 2 * ] quot>py-callback ;
{ V{ 2 4 16 2 4 68 } } [
double-fun [ { 1 2 8 1 2 34 } >py __builtin__:map py> ] with-quot>py-cfunction
] py-test
: reduce-func ( -- alien )
[ drop first2 + ] quot>py-callback ;
{ 48 } [
reduce-func [
{ 1 2 8 1 2 34 } >py __builtin__:reduce py>
] with-quot>py-cfunction
] py-test