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
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 ( ) ;
! 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 ) ;
! 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 ) ;
-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
: 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 ;
+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" } ;
}
{ $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." } ;
"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:"
{ $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." } ;
-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 ;
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
-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 -- )
[ 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 )
[ [ 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