diff --git a/extra/python/errors/errors-docs.factor b/extra/python/errors/errors-docs.factor new file mode 100644 index 0000000000..cb80e66aa0 --- /dev/null +++ b/extra/python/errors/errors-docs.factor @@ -0,0 +1,27 @@ +IN: python.errors +USING: python.errors help.markup help.syntax ; + +HELP: check-zero +{ $description + "Verifies that the return code is 0 and throws an error otherwise." +} ; + +HELP: (check-ref) +{ $description + "Verifies that the reference is not f and throws an error if it is." +} ; + +HELP: check-new-ref +{ $description + "Adds reference counting to the returned python object which is assumed to be a new reference. An error is thrown if the object is f. This word is used to wrap Python functions that return new references." +} ; + +HELP: check-borrowed-ref +{ $description + "Adds reference counting to the returned python object which is assumed to be a borrowed reference. An error is thrown if the object is f. This word is used to wrap Python functions that return borrowed references." +} ; + +HELP: unsteal-ref +{ $description + "Increases the objects reference count. Used by wrappers that call Python functions that steal references." +} ; diff --git a/extra/python/errors/errors.factor b/extra/python/errors/errors.factor new file mode 100644 index 0000000000..4107a635ec --- /dev/null +++ b/extra/python/errors/errors.factor @@ -0,0 +1,30 @@ +USING: alien.c-types alien.data kernel python.ffi ; +IN: python.errors + +ERROR: python-error type message ; + + + +: (check-ref) ( ref -- ref' ) + [ get-error throw-error f ] unless* ; + +: check-new-ref ( ref -- ref' ) + &Py_DecRef (check-ref) ; + +: check-borrowed-ref ( ref -- ref' ) + dup Py_IncRef &Py_DecRef (check-ref) ; + +: check-zero ( code -- ) + 0 = [ get-error throw-error ] unless ; + +: unsteal-ref ( ref -- ref' ) + dup Py_IncRef ; diff --git a/extra/python/errors/summary.txt b/extra/python/errors/summary.txt new file mode 100644 index 0000000000..cab5e91d7f --- /dev/null +++ b/extra/python/errors/summary.txt @@ -0,0 +1 @@ +Error handling and reference counting diff --git a/extra/python/objects/objects.factor b/extra/python/objects/objects.factor new file mode 100644 index 0000000000..9172493030 --- /dev/null +++ b/extra/python/objects/objects.factor @@ -0,0 +1,60 @@ +USING: alien.c-types alien.data kernel python.errors python.ffi ; +IN: python.objects + +! Objects +: getattr ( obj str -- value ) + PyObject_GetAttrString check-new-ref ; + +: setattr ( obj str value -- ) + PyObject_SetAttrString check-zero ; + +: call-object ( obj args -- value ) + PyObject_CallObject check-new-ref ; + +: call-object-full ( obj args kwargs -- value ) + PyObject_Call check-new-ref ; + +! Tuples +: ( length -- tuple ) + PyTuple_New check-new-ref ; + +: py-tuple-set-item ( obj pos val -- ) + unsteal-ref PyTuple_SetItem check-zero ; + +: py-tuple-get-item ( obj pos -- val ) + PyTuple_GetItem dup Py_IncRef check-new-ref ; + +: py-tuple-size ( obj -- len ) + PyTuple_Size ; + +: <1py-tuple> ( alien -- tuple ) + 1 [ 0 rot py-tuple-set-item ] keep ; + +! Dicts +: ( -- dict ) + PyDict_New check-new-ref ; + +: py-dict-set-item ( obj key val -- ) + PyDict_SetItem check-zero ; + +: py-dict-set-item-string ( dict key val -- ) + PyDict_SetItemString check-zero ; + +: py-dict-get-item-string ( obj key -- val ) + PyDict_GetItemString check-borrowed-ref ; + +: py-dict-size ( obj -- len ) + PyDict_Size ; + +! Lists +: ( length -- list ) + PyList_New check-new-ref ; + +: py-list-size ( list -- len ) + PyList_Size ; + +: py-list-get-item ( obj pos -- val ) + PyList_GetItem check-borrowed-ref ; + +: py-list-set-item ( obj pos val -- ) + unsteal-ref PyList_SetItem check-zero ; diff --git a/extra/python/python-docs.factor b/extra/python/python-docs.factor new file mode 100644 index 0000000000..00ffb8041f --- /dev/null +++ b/extra/python/python-docs.factor @@ -0,0 +1,28 @@ +IN: python +USING: python help.markup help.syntax ; + +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 +"Initialization and finalization:" +{ $subsections py-initialize py-finalize } +"Module management:" +{ $subsections import } ; + +HELP: py-initialize +{ $description "Initializes the python binding. This word must be called before any other words in the api can be used" } ; + +HELP: py-finalize +{ $description "Finalizes the python binding. After this word is called the api must not be used anymore." } ; + +HELP: >py +{ $values { "obj" "a factor object" } { "py-obj" "a python object" } } +{ $description "Converts a factor objects to its most fitting python representation." } +{ $examples + { $example + "USING: python ;" + "10 iota >array >py >factor ." + "{ 0 1 2 3 4 5 6 7 8 9 }" + } +} +{ $see-also >factor } ; diff --git a/extra/python/python-tests.factor b/extra/python/python-tests.factor index 81aba275b5..7e52e5b11e 100644 --- a/extra/python/python-tests.factor +++ b/extra/python/python-tests.factor @@ -1,5 +1,5 @@ USING: accessors arrays assocs calendar continuations destructors fry kernel -math namespaces python python.ffi sequences strings tools.test ; +math namespaces python python.ffi python.objects sequences strings tools.test ; IN: python.tests py-initialize diff --git a/extra/python/python.factor b/extra/python/python.factor index 7370d73155..f6e5e95d87 100644 --- a/extra/python/python.factor +++ b/extra/python/python.factor @@ -1,5 +1,6 @@ -USING: accessors alien alien.c-types alien.data arrays assocs fry -hashtables kernel namespaces python.ffi sequences strings vectors ; +USING: accessors alien alien.c-types alien.data arrays assocs fry hashtables +kernel namespaces python.errors python.ffi python.objects sequences strings +vectors ; IN: python QUALIFIED: math @@ -10,86 +11,9 @@ QUALIFIED: math : py-finalize ( -- ) Py_IsInitialized [ Py_Finalize ] when ; -! 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 ; - -: setattr ( obj str value -- ) - PyObject_SetAttrString check-return-code ; - -: call-object ( obj args -- value ) - PyObject_CallObject check-return ; - -: call-object-full ( obj args kwargs -- value ) - PyObject_Call check-return ; - -! Types -: ( length -- tuple ) - PyTuple_New check-return ; - -: py-tuple-set-item ( obj pos val -- ) - dup Py_IncRef PyTuple_SetItem check-return-code ; - -: py-tuple-get-item ( obj pos -- val ) - PyTuple_GetItem dup Py_IncRef check-return ; - -: py-tuple-size ( obj -- len ) - PyTuple_Size ; - -: <1py-tuple> ( alien -- tuple ) - 1 [ 0 rot py-tuple-set-item ] keep ; - -! Dicts -: ( -- 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 dup Py_IncRef check-return ; - -: py-dict-size ( obj -- len ) - PyDict_Size ; - -! Lists -: ( length -- list ) - PyList_New check-return ; - -: py-list-size ( list -- len ) - PyList_Size ; - -: py-list-get-item ( obj pos -- val ) - PyList_GetItem dup Py_IncRef check-return ; - -: py-list-set-item ( obj pos val -- ) - dup Py_IncRef PyList_SetItem check-return-code ; + PyImport_ImportModule check-new-ref ; ! Unicodes : py-ucs-size ( -- n ) @@ -98,8 +22,8 @@ ERROR: python-error type message ; : py-unicode>utf8 ( uni -- str ) py-ucs-size 4 = [ PyUnicodeUCS4_AsUTF8String ] - [ PyUnicodeUCS2_AsUTF8String ] if (check-return) - PyString_AsString (check-return) ; + [ PyUnicodeUCS2_AsUTF8String ] if (check-ref) + PyString_AsString (check-ref) ; : utf8>py-unicode ( str -- uni ) py-ucs-size 4 = @@ -145,11 +69,11 @@ DEFER: >factor H{ { "NoneType" [ drop f ] } { "bool" [ PyObject_IsTrue 1 = ] } - { "dict" [ PyDict_Items (check-return) >factor >hashtable ] } + { "dict" [ PyDict_Items (check-ref) >factor >hashtable ] } { "int" [ PyInt_AsLong ] } { "list" [ py-list>vector [ >factor ] map ] } { "long" [ PyLong_AsLong ] } - { "str" [ PyString_AsString (check-return) ] } + { "str" [ PyString_AsString (check-ref) ] } { "tuple" [ py-tuple>array [ >factor ] map ] } { "unicode" [ py-unicode>utf8 ] } } clone ; diff --git a/extra/python/syntax/syntax-tests.factor b/extra/python/syntax/syntax-tests.factor index 71ce136050..1231401e58 100644 --- a/extra/python/syntax/syntax-tests.factor +++ b/extra/python/syntax/syntax-tests.factor @@ -1,5 +1,5 @@ USING: arrays assocs destructors fry kernel math namespaces python python.ffi -python.syntax python.tests sequences sets tools.test ; +python.objects python.syntax python.tests sequences sets tools.test ; IN: python.syntax.tests ! Importing functions diff --git a/extra/python/syntax/syntax.factor b/extra/python/syntax/syntax.factor index cfa593ecbe..e4486b947d 100644 --- a/extra/python/syntax/syntax.factor +++ b/extra/python/syntax/syntax.factor @@ -1,5 +1,5 @@ USING: accessors arrays effects effects.parser fry generalizations -kernel lexer math namespaces parser python python.ffi sequences +kernel lexer math namespaces parser python python.ffi python.objects sequences sequences.generalizations vocabs.parser words ; IN: python.syntax