]> gitweb.factorcode.org Git - factor.git/commitdiff
python: feature to create python callbacks/functions, now you can call hofs like...
authorBjörn Lindqvist <bjourne@gmail.com>
Thu, 23 Oct 2014 23:01:15 +0000 (01:01 +0200)
committerJohn Benediktsson <mrjbq7@gmail.com>
Fri, 24 Oct 2014 00:01:46 +0000 (17:01 -0700)
extra/python/ffi/ffi.factor
extra/python/objects/objects.factor
extra/python/python-docs.factor
extra/python/python.factor
extra/python/syntax/syntax-tests.factor

index d73fea8f0b2d5725d51d095bfb27b3811a62461e..aa9283474f68bf36c074796a110e492db369f9ed 100644 (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 ) ;
index 9172493030e5fd4fa5f7b20baf0cbfb359223433..e605975d0d17fd3df8d5974410280853ba13280f 100644 (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 ;
index 2d78f8bd70ad0d99fc66106ae1e2622687f4900b..346f175214426fce440406f8a967435b71e3ec15 100644 (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." } ;
index a298fd196cfd5389bd09ee658f02452f21f99fcb..016749ebb5d1478bed178c381bd97db1cbd86a28 100644 (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
index c107dce225c312723ba5f04e9170f0983e4c4d33..eeaa91484e01fbf5e0332aba318d443b5577d88c 100644 (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