python: a thin ffi to Python, everything mostly works except for the cursed reference counting
							parent
							
								
									3bbdd067be
								
							
						
					
					
						commit
						446498c67a
					
				| 
						 | 
				
			
			@ -0,0 +1,101 @@
 | 
			
		|||
USING:
 | 
			
		||||
    alien
 | 
			
		||||
    alien.c-types
 | 
			
		||||
    alien.destructors
 | 
			
		||||
    alien.libraries alien.libraries.finder
 | 
			
		||||
    alien.syntax
 | 
			
		||||
    kernel
 | 
			
		||||
    sequences ;
 | 
			
		||||
IN: python.ffi
 | 
			
		||||
 | 
			
		||||
<< "python" { "3.0" "2.6" "2.7" } [
 | 
			
		||||
    "python" prepend find-library
 | 
			
		||||
] map-find drop cdecl add-library >>
 | 
			
		||||
 | 
			
		||||
LIBRARY: python
 | 
			
		||||
 | 
			
		||||
C-TYPE: PyObject
 | 
			
		||||
 | 
			
		||||
! Top-level
 | 
			
		||||
FUNCTION: c-string Py_GetVersion ( ) ;
 | 
			
		||||
FUNCTION: void Py_Initialize ( ) ;
 | 
			
		||||
FUNCTION: bool Py_IsInitialized ( ) ;
 | 
			
		||||
FUNCTION: void Py_Finalize ( ) ;
 | 
			
		||||
 | 
			
		||||
! Misc
 | 
			
		||||
FUNCTION: int PyRun_SimpleString ( c-string command ) ;
 | 
			
		||||
 | 
			
		||||
! Importing
 | 
			
		||||
FUNCTION: PyObject* PyImport_AddModule ( c-string name ) ;
 | 
			
		||||
FUNCTION: long PyImport_GetMagicNumber ( ) ;
 | 
			
		||||
FUNCTION: PyObject* PyImport_ImportModule ( c-string name ) ;
 | 
			
		||||
 | 
			
		||||
! Dicts
 | 
			
		||||
FUNCTION: PyObject* PyDict_GetItemString ( PyObject* d, c-string key ) ;
 | 
			
		||||
FUNCTION: PyObject* PyDict_New ( ) ;
 | 
			
		||||
FUNCTION: int PyDict_Size ( PyObject* d ) ;
 | 
			
		||||
FUNCTION: int PyDict_SetItemString ( PyObject* d,
 | 
			
		||||
                                     c-string key,
 | 
			
		||||
                                     PyObject* val ) ;
 | 
			
		||||
FUNCTION: int PyDict_SetItem ( PyObject* d, PyObject* k, PyObject* o ) ;
 | 
			
		||||
FUNCTION: PyObject* PyDict_Items ( PyObject *d ) ;
 | 
			
		||||
 | 
			
		||||
! Tuples
 | 
			
		||||
FUNCTION: PyObject* PyTuple_GetItem ( PyObject* t, int pos ) ;
 | 
			
		||||
FUNCTION: PyObject* PyTuple_New ( int len ) ;
 | 
			
		||||
FUNCTION: int PyTuple_SetItem ( PyObject* t, int pos, PyObject* o ) ;
 | 
			
		||||
FUNCTION: int PyTuple_Size ( PyObject* t ) ;
 | 
			
		||||
 | 
			
		||||
! Lists (sequences)
 | 
			
		||||
FUNCTION: PyObject* PyList_GetItem ( PyObject* l, int pos ) ;
 | 
			
		||||
FUNCTION: int PyList_Size ( PyObject* t ) ;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
! Modules
 | 
			
		||||
FUNCTION: c-string PyModule_GetName ( PyObject* module ) ;
 | 
			
		||||
FUNCTION: PyObject* PyModule_GetDict ( PyObject* module ) ;
 | 
			
		||||
 | 
			
		||||
! Objects
 | 
			
		||||
FUNCTION: PyObject* PyObject_CallObject ( PyObject* callable,
 | 
			
		||||
                                          PyObject* args ) ;
 | 
			
		||||
FUNCTION: PyObject* PyObject_Call ( PyObject* callable,
 | 
			
		||||
                                    PyObject* args,
 | 
			
		||||
                                    PyObject* kw ) ;
 | 
			
		||||
FUNCTION: PyObject* PyObject_GetAttrString ( PyObject* callable,
 | 
			
		||||
                                             c-string attr_name ) ;
 | 
			
		||||
FUNCTION: PyObject* PyObject_Str ( PyObject* o ) ;
 | 
			
		||||
 | 
			
		||||
! Strings
 | 
			
		||||
FUNCTION: c-string PyString_AsString ( PyObject* string ) ;
 | 
			
		||||
FUNCTION: PyObject* PyString_FromString ( c-string v ) ;
 | 
			
		||||
 | 
			
		||||
! Unicode
 | 
			
		||||
FUNCTION: PyObject* PyUnicode_DecodeUTF8 ( c-string s,
 | 
			
		||||
                                           int size,
 | 
			
		||||
                                           void* errors ) ;
 | 
			
		||||
FUNCTION: PyObject* PyUnicodeUCS4_FromString ( c-string s ) ;
 | 
			
		||||
FUNCTION: PyObject* PyUnicodeUCS4_AsUTF8String ( PyObject* unicode ) ;
 | 
			
		||||
 | 
			
		||||
! Ints
 | 
			
		||||
FUNCTION: long PyInt_AsLong ( PyObject* io ) ;
 | 
			
		||||
 | 
			
		||||
! Longs
 | 
			
		||||
FUNCTION: PyObject* PyLong_FromLong ( long v ) ;
 | 
			
		||||
FUNCTION: long PyLong_AsLong ( PyObject* o ) ;
 | 
			
		||||
 | 
			
		||||
! Floats
 | 
			
		||||
FUNCTION: PyObject* PyFloat_FromDouble ( double d ) ;
 | 
			
		||||
 | 
			
		||||
! Reference counting
 | 
			
		||||
FUNCTION: void Py_IncRef ( PyObject* o ) ;
 | 
			
		||||
FUNCTION: void Py_DecRef ( PyObject* o ) ;
 | 
			
		||||
DESTRUCTOR: Py_DecRef
 | 
			
		||||
 | 
			
		||||
! Reflection
 | 
			
		||||
FUNCTION: c-string PyEval_GetFuncName ( PyObject* func ) ;
 | 
			
		||||
 | 
			
		||||
! Errors
 | 
			
		||||
FUNCTION: void PyErr_Print ( ) ;
 | 
			
		||||
FUNCTION: void PyErr_Fetch ( PyObject** ptype,
 | 
			
		||||
                             PyObject** pvalue,
 | 
			
		||||
                             PyObject** *ptraceback ) ;
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,113 @@
 | 
			
		|||
USING:
 | 
			
		||||
    accessors arrays assocs
 | 
			
		||||
    calendar
 | 
			
		||||
    continuations
 | 
			
		||||
    fry kernel
 | 
			
		||||
    math
 | 
			
		||||
    namespaces
 | 
			
		||||
    python python.ffi
 | 
			
		||||
    sequences
 | 
			
		||||
    strings tools.test ;
 | 
			
		||||
IN: python.tests
 | 
			
		||||
 | 
			
		||||
: py-test ( result quot -- )
 | 
			
		||||
    '[ _ with-py ] unit-test ; inline
 | 
			
		||||
 | 
			
		||||
[ t ] [ Py_GetVersion string? ] unit-test
 | 
			
		||||
 | 
			
		||||
[ "os" ] [ "os" PyImport_ImportModule PyModule_GetName ] py-test
 | 
			
		||||
 | 
			
		||||
[ t ] [ "os" import "getpid" getattr { } py-call 0 > ] py-test
 | 
			
		||||
 | 
			
		||||
[ t ] [ Py_IsInitialized ] py-test
 | 
			
		||||
 | 
			
		||||
! Importing
 | 
			
		||||
[ { "ImportError" "No module named kolobi" } ] [
 | 
			
		||||
    [ "kolobi" import ] [ [ type>> ] [ message>> ] bi 2array ] recover
 | 
			
		||||
] py-test
 | 
			
		||||
 | 
			
		||||
! Tuples
 | 
			
		||||
[ 2 ] [ 2 <py-tuple> py-tuple-size ] py-test
 | 
			
		||||
 | 
			
		||||
: py-date>factor ( py-obj -- timestamp )
 | 
			
		||||
    { "year" "month" "day" } [ getattr >factor ] with map
 | 
			
		||||
    first3 0 0 0 instant <timestamp> ;
 | 
			
		||||
 | 
			
		||||
! Datetimes
 | 
			
		||||
[ t ] [
 | 
			
		||||
    [ py-date>factor ] "date" py-type-dispatch get set-at
 | 
			
		||||
    "datetime" import
 | 
			
		||||
    "date" getattr "today" getattr
 | 
			
		||||
    { } py-call
 | 
			
		||||
    today instant >>gmt-offset =
 | 
			
		||||
] py-test
 | 
			
		||||
 | 
			
		||||
! Unicode
 | 
			
		||||
[ "غثههح" ] [
 | 
			
		||||
    "os.path" import "basename" getattr { "غثههح" } py-call
 | 
			
		||||
] py-test
 | 
			
		||||
 | 
			
		||||
! Instance variables
 | 
			
		||||
[ 7 ] [
 | 
			
		||||
    "datetime" import "timedelta" getattr
 | 
			
		||||
    { 7 } >py call-object "days" getattr >factor
 | 
			
		||||
] py-test
 | 
			
		||||
 | 
			
		||||
! Create a dictonary
 | 
			
		||||
[ 0 ] [ <py-dict> py-dict-size ] py-test
 | 
			
		||||
 | 
			
		||||
! Dictionary with object keys
 | 
			
		||||
[ 1 ] [
 | 
			
		||||
    <py-dict> dup 0 >py 33 >py py-dict-set-item py-dict-size
 | 
			
		||||
] py-test
 | 
			
		||||
 | 
			
		||||
! Dictionary with string keys
 | 
			
		||||
[ 1 ] [
 | 
			
		||||
    <py-dict> [ "foo" 33 >py py-dict-set-item-string ] [ py-dict-size ] bi
 | 
			
		||||
] py-test
 | 
			
		||||
 | 
			
		||||
! Get dictionary items
 | 
			
		||||
[ 33 ] [
 | 
			
		||||
    <py-dict> "tjaba"
 | 
			
		||||
    [ 33 >py  py-dict-set-item-string ]
 | 
			
		||||
    [ py-dict-get-item-string >factor ] 2bi
 | 
			
		||||
] py-test
 | 
			
		||||
 | 
			
		||||
! Nest dicts
 | 
			
		||||
[ 0 ] [
 | 
			
		||||
    <py-dict> "foo"
 | 
			
		||||
    [ <py-dict> py-dict-set-item-string ]
 | 
			
		||||
    [ py-dict-get-item-string ] 2bi
 | 
			
		||||
    py-dict-size
 | 
			
		||||
] py-test
 | 
			
		||||
 | 
			
		||||
! Nested tuples
 | 
			
		||||
[ 3 ] [
 | 
			
		||||
    1 <py-tuple> dup 0 3 <py-tuple> py-tuple-set-item
 | 
			
		||||
    0 py-tuple-get-item py-tuple-size
 | 
			
		||||
] py-test
 | 
			
		||||
 | 
			
		||||
! Round tripping!
 | 
			
		||||
[ { "foo" { 99 77 } } ] [ { "foo" { 99 77 } } >py >factor ] py-test
 | 
			
		||||
 | 
			
		||||
[ H{ { "foo" "bar" } { 3 4 } } ] [
 | 
			
		||||
    H{ { "foo" "bar" } { 3 4 } } >py >factor
 | 
			
		||||
] py-test
 | 
			
		||||
 | 
			
		||||
! Kwargs
 | 
			
		||||
[ 2014 10 22 ] [
 | 
			
		||||
    "datetime" import "date" getattr
 | 
			
		||||
    { } { "year" 2014 "month" 10 "day" 22 } py-call2
 | 
			
		||||
    [ year>> ] [ month>> ] [ day>> ] tri
 | 
			
		||||
] py-test
 | 
			
		||||
 | 
			
		||||
SYMBOLS: year month day ;
 | 
			
		||||
 | 
			
		||||
[ 2014 10 22 ] [
 | 
			
		||||
    "datetime" import "date" getattr
 | 
			
		||||
    { } { year 2014 month 10 day 22 } py-call2
 | 
			
		||||
    [ year>> ] [ month>> ] [ day>> ] tri
 | 
			
		||||
] py-test
 | 
			
		||||
 | 
			
		||||
! Modules
 | 
			
		||||
[ t ] [ "os" import PyModule_GetDict py-dict-size 200 > ] py-test
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,146 @@
 | 
			
		|||
USING:
 | 
			
		||||
    accessors
 | 
			
		||||
    alien alien.c-types alien.data
 | 
			
		||||
    arrays
 | 
			
		||||
    assocs
 | 
			
		||||
    destructors
 | 
			
		||||
    fry
 | 
			
		||||
    grouping
 | 
			
		||||
    hashtables
 | 
			
		||||
    kernel
 | 
			
		||||
    namespaces
 | 
			
		||||
    python.ffi
 | 
			
		||||
    sequences
 | 
			
		||||
    strings
 | 
			
		||||
    words ;
 | 
			
		||||
IN: python
 | 
			
		||||
QUALIFIED: math
 | 
			
		||||
 | 
			
		||||
! Error handling
 | 
			
		||||
ERROR: python-error type message ;
 | 
			
		||||
 | 
			
		||||
: get-error ( -- ptype pvalue )
 | 
			
		||||
    { void* void* void* } [ PyErr_Fetch ] with-out-parameters drop ;
 | 
			
		||||
 | 
			
		||||
: throw-error ( ptype pvalue -- )
 | 
			
		||||
    [ "__name__" PyObject_GetAttrString ] [ PyObject_Str ] bi* [ &Py_DecRef ] bi@
 | 
			
		||||
    [ PyString_AsString ] bi@ python-error ;
 | 
			
		||||
 | 
			
		||||
: (check-return) ( value/f -- value' )
 | 
			
		||||
    [ get-error throw-error f ] unless* ;
 | 
			
		||||
 | 
			
		||||
: check-return ( value/f -- value' )
 | 
			
		||||
    (check-return) ; ! &Py_DecRef ;
 | 
			
		||||
 | 
			
		||||
: check-return-code ( return -- )
 | 
			
		||||
    0 = [ get-error throw-error ] unless ;
 | 
			
		||||
 | 
			
		||||
! Importing
 | 
			
		||||
: import ( str -- module )
 | 
			
		||||
    PyImport_ImportModule check-return ;
 | 
			
		||||
 | 
			
		||||
! Objects
 | 
			
		||||
: getattr ( obj str -- value )
 | 
			
		||||
    PyObject_GetAttrString check-return ;
 | 
			
		||||
 | 
			
		||||
: call-object ( obj args -- value )
 | 
			
		||||
    PyObject_CallObject check-return ;
 | 
			
		||||
 | 
			
		||||
! Context
 | 
			
		||||
: with-py ( quot -- )
 | 
			
		||||
    '[ Py_Initialize _ call Py_Finalize ] with-destructors ; inline
 | 
			
		||||
 | 
			
		||||
! Types and their methods
 | 
			
		||||
: <py-tuple> ( length -- tuple )
 | 
			
		||||
    PyTuple_New check-return ;
 | 
			
		||||
 | 
			
		||||
: py-tuple-set-item ( obj pos val -- )
 | 
			
		||||
    PyTuple_SetItem check-return-code ;
 | 
			
		||||
 | 
			
		||||
: py-tuple-get-item ( obj pos -- val )
 | 
			
		||||
    PyTuple_GetItem check-return ;
 | 
			
		||||
 | 
			
		||||
: py-tuple-size ( obj -- len )
 | 
			
		||||
    PyTuple_Size ;
 | 
			
		||||
 | 
			
		||||
: <py-dict> ( -- dict )
 | 
			
		||||
    PyDict_New check-return ;
 | 
			
		||||
 | 
			
		||||
: py-dict-set-item ( obj key val -- )
 | 
			
		||||
    PyDict_SetItem check-return-code ;
 | 
			
		||||
 | 
			
		||||
: py-dict-set-item-string ( dict key val -- )
 | 
			
		||||
    PyDict_SetItemString check-return-code ;
 | 
			
		||||
 | 
			
		||||
: py-dict-get-item-string ( obj key -- val )
 | 
			
		||||
    PyDict_GetItemString check-return ;
 | 
			
		||||
 | 
			
		||||
: py-dict-size ( obj -- len )
 | 
			
		||||
    PyDict_Size ;
 | 
			
		||||
 | 
			
		||||
: py-list-size ( list -- len )
 | 
			
		||||
    PyList_Size ;
 | 
			
		||||
 | 
			
		||||
: py-list-get-item ( obj pos -- val )
 | 
			
		||||
    PyList_GetItem check-return ;
 | 
			
		||||
 | 
			
		||||
! Data marshalling to Python
 | 
			
		||||
GENERIC: (>py) ( obj -- obj' )
 | 
			
		||||
M: string (>py) PyUnicodeUCS4_FromString ;
 | 
			
		||||
M: math:fixnum (>py) PyLong_FromLong ;
 | 
			
		||||
M: math:float (>py) PyFloat_FromDouble ;
 | 
			
		||||
 | 
			
		||||
M: array (>py)
 | 
			
		||||
    [ length <py-tuple> dup ] [ [ (>py) ] map ] bi
 | 
			
		||||
    [ rot py-tuple-set-item ] with each-index ;
 | 
			
		||||
 | 
			
		||||
M: hashtable (>py)
 | 
			
		||||
    <py-dict> swap dupd [
 | 
			
		||||
        swapd [ (>py) ] [ (>py) ] bi* py-dict-set-item
 | 
			
		||||
    ] with assoc-each ;
 | 
			
		||||
 | 
			
		||||
! I'll make a fast-path for this
 | 
			
		||||
M: word (>py) name>> (>py) ;
 | 
			
		||||
 | 
			
		||||
: >py ( obj -- py-obj )
 | 
			
		||||
    (>py) ; ! &Py_DecRef ;
 | 
			
		||||
 | 
			
		||||
! Data marshalling to Factor
 | 
			
		||||
SYMBOL: py-type-dispatch
 | 
			
		||||
 | 
			
		||||
DEFER: >factor
 | 
			
		||||
 | 
			
		||||
: init-py-type-dispatch ( -- table )
 | 
			
		||||
    H{
 | 
			
		||||
        { "NoneType" [ drop f ] }
 | 
			
		||||
        { "dict" [ PyDict_Items (check-return) >factor >hashtable ] }
 | 
			
		||||
        { "int" [ PyInt_AsLong ] }
 | 
			
		||||
 | 
			
		||||
        { "list" [
 | 
			
		||||
            dup py-list-size iota [ py-list-get-item >factor ] with map
 | 
			
		||||
        ] }
 | 
			
		||||
        { "long" [ PyLong_AsLong ] }
 | 
			
		||||
        { "str" [ PyString_AsString (check-return) ] }
 | 
			
		||||
        { "tuple" [
 | 
			
		||||
            dup py-tuple-size iota [ py-tuple-get-item >factor ] with map
 | 
			
		||||
        ] }
 | 
			
		||||
        { "unicode" [
 | 
			
		||||
            PyUnicodeUCS4_AsUTF8String (check-return)
 | 
			
		||||
            PyString_AsString (check-return)
 | 
			
		||||
        ] }
 | 
			
		||||
    } clone ;
 | 
			
		||||
 | 
			
		||||
py-type-dispatch [ init-py-type-dispatch ] initialize
 | 
			
		||||
 | 
			
		||||
ERROR: missing-type type ;
 | 
			
		||||
 | 
			
		||||
: >factor ( py-obj -- obj )
 | 
			
		||||
    dup "__class__" getattr "__name__" getattr PyString_AsString
 | 
			
		||||
    py-type-dispatch get ?at [ call( x -- x ) ] [ missing-type ] if ;
 | 
			
		||||
 | 
			
		||||
! Utility
 | 
			
		||||
: py-call ( obj args -- value )
 | 
			
		||||
    >py call-object >factor ;
 | 
			
		||||
 | 
			
		||||
: py-call2 ( obj args kwargs -- value )
 | 
			
		||||
    [ >py ] [ 2 group >hashtable >py ] bi* PyObject_Call >factor ;
 | 
			
		||||
		Loading…
	
		Reference in New Issue