1616
1717unit PythonEngine;
1818
19- { TODO -oMMM : implement tp_as_buffer slot }
2019{ TODO -oMMM : implement Attribute descriptor and subclassing stuff }
2120
2221{ $IFNDEF FPC}
@@ -189,6 +188,7 @@ TPythonVersionProp = record
189188 WCharTString = UnicodeString;
190189{ $ENDIF}
191190
191+ PPy_ssize_t = PNativeInt;
192192 Py_ssize_t = NativeInt;
193193
194194 const
@@ -307,7 +307,7 @@ TPythonVersionProp = record
307307 T_UINT = 11 ;
308308 T_ULONG = 12 ;
309309
310- // * Added by Jack: strings contained in the structure */
310+ // * strings contained in the structure */
311311 T_STRING_INPLACE= 13 ;
312312
313313 T_OBJECT_EX = 16 ;{ * Like T_OBJECT, but raises AttributeError
@@ -326,6 +326,32 @@ TPythonVersionProp = record
326326 mtStringInplace, mtObjectEx);
327327 TPyMemberFlag = (mfDefault, mfReadOnly, mfReadRestricted, mfWriteRestricted, mfRestricted);
328328
329+ // Constants from pybuffer.h
330+ const
331+ PyBUF_MAX_NDIM = 64 ; // Maximum number of dimensions
332+ // Flags for getting buffers. Keep these in sync with inspect.BufferFlags.
333+ PyBUF_SIMPLE = 0 ;
334+ PyBUF_WRITABLE = 1 ;
335+
336+ PyBUF_FORMAT = $0004 ;
337+ PyBUF_ND = $0008 ;
338+ PyBUF_STRIDES = $0010 or PyBUF_ND;
339+ PyBUF_C_CONTIGUOUS = $0020 or PyBUF_STRIDES;
340+ PyBUF_F_CONTIGUOUS = $0040 or PyBUF_STRIDES;
341+ PyBUF_ANY_CONTIGUOUS = $0080 or PyBUF_STRIDES;
342+ PyBUF_INDIRECT = $0100 or PyBUF_STRIDES;
343+ PyBUF_CONTIG = PyBUF_ND or PyBUF_WRITABLE;
344+ PyBUF_CONTIG_RO = PyBUF_ND;
345+ PyBUF_STRIDED = PyBUF_STRIDES or PyBUF_WRITABLE;
346+ PyBUF_STRIDED_RO = PyBUF_STRIDES;
347+ PyBUF_RECORDS = PyBUF_STRIDES or PyBUF_WRITABLE or PyBUF_FORMAT;
348+ PyBUF_RECORDS_RO = PyBUF_STRIDES or PyBUF_FORMAT;
349+ PyBUF_FULL = PyBUF_INDIRECT or PyBUF_WRITABLE or PyBUF_FORMAT;
350+ PyBUF_FULL_RO = PyBUF_INDIRECT or PyBUF_FORMAT;
351+
352+ PyBUF_READ = $100 ;
353+ PyBUF_WRITE = $200 ;
354+
329355// #######################################################
330356// ## ##
331357// ## Non-Python specific constants ##
@@ -610,7 +636,35 @@ TPythonVersionProp = record
610636 m_free : inquiry;
611637 end ;
612638
639+ // pybuffer.h
640+
641+ PPy_buffer = ^Py_Buffer;
642+ Py_buffer = record
643+ buf: Pointer;
644+ obj: PPyObject; (* owned reference *)
645+ len: Py_ssize_t;
646+ itemsize: Py_ssize_t; (* This is Py_ssize_t so it can be
647+ pointed to by strides in simple case.*)
648+ readonly: Integer;
649+ ndim: Integer;
650+ format: PAnsiChar;
651+ shape: PPy_ssize_t ;
652+ strides: PPy_ssize_t;
653+ suboffsets: PPy_ssize_t;
654+ internal: Pointer;
655+ end ;
656+
657+ getbufferproc = function(exporter: PPyObject; view : PPy_buffer; flags: Integer): Integer; cdecl;
658+ releasebufferproc = procedure(exporter: PPyObject; view : PPy_buffer); cdecl;
659+
660+ PPyBufferProcs = ^PyBufferProcs;
661+ PyBufferProcs = record
662+ bf_getbuffer: getbufferproc;
663+ bf_releasebuffer: releasebufferproc;
664+ end ;
665+
613666 // object.h
667+
614668 PyTypeObject = { $IFDEF CPUX86} packed { $ENDIF} record
615669 ob_refcnt: NativeInt;
616670 ob_type: PPyTypeObject;
@@ -643,7 +697,7 @@ TPythonVersionProp = record
643697 tp_setattro: setattrofunc;
644698
645699 // Functions to access object as input/output buffer
646- tp_as_buffer: Pointer; // PPyBufferProcs - not implemented
700+ tp_as_buffer: PPyBufferProcs;
647701 // Flags to define presence of optional/expanded features
648702 tp_flags: C_ULong;
649703
@@ -1075,6 +1129,7 @@ FutureWarning = class (EPyWarning);
10751129 EPySyntaxWarning = class (EPyWarning);
10761130 EPyRuntimeWarning = class (EPyWarning);
10771131 EPyReferenceError = class (EPyStandardError);
1132+ EPyBufferError = class (EPyException);
10781133 { $IFDEF MSWINDOWS}
10791134 EPyWindowsError = class (EPyOSError);
10801135 { $ENDIF}
@@ -1326,6 +1381,7 @@ TPythonInterface=class(TDynamicDll)
13261381 PyExc_UnicodeDecodeError: PPPyObject;
13271382 PyExc_UnicodeEncodeError: PPPyObject;
13281383 PyExc_UnicodeTranslateError: PPPyObject;
1384+ PyExc_BufferError: PPPyObject;
13291385
13301386 PyCode_Type: PPyTypeObject;
13311387 PyType_Type: PPyTypeObject;
@@ -1427,8 +1483,20 @@ TPythonInterface=class(TDynamicDll)
14271483 PySys_SetArgv: procedure( argc: Integer; argv: PPWCharT); cdecl;
14281484
14291485 PyCFunction_NewEx: function(md:PPyMethodDef;self, ob:PPyObject):PPyObject; cdecl;
1430- // Removed. Use PyEval_CallObjectWithKeywords with third argument nil
1431- // PyEval_CallObject: function(callable_obj, args:PPyObject):PPyObject; cdecl;
1486+
1487+ PyBuffer_GetPointer: function(view : PPy_buffer; indices: PPy_ssize_t): Pointer; cdecl;
1488+ PyBuffer_SizeFromFormat: function(format: PAnsiChar): Py_ssize_t; cdecl; // New in Python 3.9
1489+ PyBuffer_ToContiguous: function(buf: Pointer; view : PPy_buffer; len: Py_ssize_t; order: AnsiChar): Integer; cdecl;
1490+ PyBuffer_FromContiguous: function(view : PPy_buffer; buf: Pointer; len: Py_ssize_t; order: AnsiChar): Integer; cdecl;
1491+ PyBuffer_IsContiguous: function(view : PPy_buffer; fort: AnsiChar): Integer; cdecl;
1492+ PyBuffer_FillContiguousStrides: procedure(ndims: Integer; shape: Py_ssize_t;
1493+ strides: PPy_ssize_t; itemsize: Integer; fort: AnsiChar); cdecl;
1494+ PyBuffer_FillInfo: function(view : PPy_buffer; o: PPyObject; buf: Pointer;
1495+ len: Py_ssize_t; readonly: Integer; flags: Integer): Integer; cdecl;
1496+ PyBuffer_Release: procedure(view : PPy_buffer); cdecl;
1497+
1498+ // Removed. Use PyEval_CallObjectWithKeywords with third argument nil
1499+ // PyEval_CallObject: function(callable_obj, args:PPyObject):PPyObject; cdecl;
14321500 PyEval_CallObjectWithKeywords:function (callable_obj, args, kw:PPyObject):PPyObject; cdecl;
14331501 PyEval_GetFrame:function :PPyObject; cdecl;
14341502 PyEval_GetGlobals:function :PPyObject; cdecl;
@@ -1546,6 +1614,9 @@ TPythonInterface=class(TDynamicDll)
15461614 PyObject_GC_Del:procedure (ob:PPyObject); cdecl;
15471615 PyObject_GC_Track:procedure (ob:PPyObject); cdecl;
15481616 PyObject_GC_UnTrack:procedure (ob:PPyObject); cdecl;
1617+ PyObject_CheckBuffer: function(obj: PPyObject): Integer; cdecl;
1618+ PyObject_GetBuffer: function(obj: PPyObject; view : PPy_buffer; flags: Integer): Integer; cdecl;
1619+ PyObject_CopyData: function (dest: PPyObject; src: PPyObject): Integer; cdecl;
15491620 PySequence_Check:function (ob:PPyObject):integer; cdecl;
15501621 PySequence_Concat:function (ob1,ob2:PPyObject):PPyObject; cdecl;
15511622 PySequence_Count:function (ob1,ob2:PPyObject):integer; cdecl;
@@ -2427,6 +2498,8 @@ TPyObject = class
24272498 function Iter : PPyObject; virtual ;
24282499 function IterNext : PPyObject; virtual ;
24292500 function Init ( args, kwds : PPyObject ) : Integer; virtual ;
2501+ function GetBuffer (view : PPy_buffer; flags: Integer): Integer; virtual ;
2502+ procedure ReleaseBuffer (view : PPy_buffer); virtual ;
24302503
24312504 // Number services
24322505 function NbAdd ( obj : PPyObject) : PPyObject; virtual ;
@@ -2495,7 +2568,8 @@ TPyObjectClass = class of TPyObject;
24952568 // since version 2.1
24962569 bsRichCompare,
24972570 // since version 2.2
2498- bsIter, bsIterNext);
2571+ bsIter, bsIterNext,
2572+ bsBuffer);
24992573 TNumberServices = set of (nsAdd, nsSubtract, nsMultiply,
25002574 nsRemainder, nsDivmod,
25012575 nsPower, nsNegative, nsPositive,
@@ -2571,6 +2645,7 @@ TPythonType = class(TGetSetContainer)
25712645 FTypeFlags : TPFlags;
25722646 FCreateFunc : PPyObject;
25732647 FCreateFuncDef : PyMethodDef;
2648+ FBufferProcs: PyBufferProcs;
25742649 FGenerateCreateFunction: Boolean;
25752650
25762651 procedure Notification ( AComponent: TComponent;
@@ -3550,6 +3625,8 @@ procedure TPythonInterface.MapDll;
35503625 PyExc_UnicodeDecodeError := Import (' PyExc_UnicodeDecodeError' );
35513626 PyExc_UnicodeEncodeError := Import (' PyExc_UnicodeEncodeError' );
35523627 PyExc_UnicodeTranslateError:= Import (' PyExc_UnicodeTranslateError' );
3628+ PyExc_BufferError := Import (' PyExc_BufferError' );
3629+
35533630 PyType_Type := Import (' PyType_Type' );
35543631 PyCFunction_Type := Import (' PyCFunction_Type' );
35553632 PyCode_Type := Import (' PyCode_Type' );
@@ -3639,7 +3716,17 @@ procedure TPythonInterface.MapDll;
36393716 PySys_SetArgv := Import (' PySys_SetArgv' );
36403717 Py_Exit := Import (' Py_Exit' );
36413718
3642- PyCFunction_NewEx := Import (' PyCFunction_NewEx' );
3719+ PyCFunction_NewEx := Import (' PyCFunction_NewEx' );
3720+
3721+ PyBuffer_GetPointer := Import (' PyBuffer_GetPointer' );
3722+ PyBuffer_ToContiguous := Import (' PyBuffer_ToContiguous' );
3723+ PyBuffer_FromContiguous := Import (' PyBuffer_FromContiguous' );
3724+ PyBuffer_IsContiguous := Import (' PyBuffer_IsContiguous' );
3725+ PyBuffer_FillContiguousStrides := Import (' PyBuffer_FillContiguousStrides' );
3726+ PyBuffer_FillInfo := Import (' PyBuffer_FillInfo' );
3727+ PyBuffer_Release := Import (' PyBuffer_Release' );
3728+ if (FMajorVersion > 3 ) or (FMinorVersion > 9 ) then
3729+ PyBuffer_SizeFromFormat := Import (' PyBuffer_SizeFromFormat' );
36433730
36443731 PyEval_CallObjectWithKeywords:= Import (' PyEval_CallObjectWithKeywords' );
36453732 PyEval_GetFrame := Import (' PyEval_GetFrame' );
@@ -3757,6 +3844,9 @@ procedure TPythonInterface.MapDll;
37573844 PyObject_GC_Del := Import (' PyObject_GC_Del' );
37583845 PyObject_GC_Track := Import (' PyObject_GC_Track' );
37593846 PyObject_GC_UnTrack := Import (' PyObject_GC_UnTrack' );
3847+ PyObject_CheckBuffer := Import (' PyObject_CheckBuffer' );
3848+ PyObject_GetBuffer := Import (' PyObject_GetBuffer' );
3849+ PyObject_CopyData := Import (' PyObject_CopyData' );
37603850 PySequence_Check := Import (' PySequence_Check' );
37613851 PySequence_Concat := Import (' PySequence_Concat' );
37623852 PySequence_Count := Import (' PySequence_Count' );
@@ -5263,6 +5353,8 @@ procedure TPythonEngine.RaiseError;
52635353 raise Define( EPyValueError.Create(' ' ), s_type, s_value )
52645354 else if (PyErr_GivenExceptionMatches(err_type, PyExc_ReferenceError^) <> 0 ) then
52655355 raise Define( EPyReferenceError.Create(' ' ), s_type, s_value )
5356+ else if (PyErr_GivenExceptionMatches(err_type, PyExc_BufferError^) <> 0 ) then
5357+ raise Define( EPyBufferError.Create(' ' ), s_type, s_value )
52665358 else if (PyErr_GivenExceptionMatches(err_type, PyExc_SystemError^) <> 0 ) then
52675359 raise Define( EPySystemError.Create(' ' ), s_type, s_value )
52685360 else if (PyErr_GivenExceptionMatches(err_type, PyExc_MemoryError^) <> 0 ) then
@@ -7589,6 +7681,16 @@ function TPyObject.GetAttrO( key: PPyObject) : PPyObject;
75897681 Result := GetPythonEngine.PyObject_GenericGetAttr(GetSelf, key);
75907682end ;
75917683
7684+ function TPyObject.GetBuffer (view : PPy_buffer; flags: Integer): Integer;
7685+ // Default implementation that raises an exception
7686+ // Subclass implementing the buffer protocol will need to override this
7687+ begin
7688+ view ^.obj := nil ;
7689+ with GetPythonEngine do
7690+ PyErr_SetObject(PyExc_BufferError^, PyUnicodeFromString(' ' ));
7691+ Result := -1 ;
7692+ end ;
7693+
75927694function TPyObject.SetAttrO ( key, value : PPyObject) : Integer;
75937695begin
75947696 Result := GetPythonEngine.PyObject_GenericSetAttr(GetSelf, key, value );
@@ -7884,6 +7986,11 @@ class procedure TPyObject.RegisterMethods( APythonType : TPythonType );
78847986begin
78857987end ;
78867988
7989+ procedure TPyObject.ReleaseBuffer (view : PPy_buffer);
7990+ begin
7991+ // Do nothing. Subclasses may provide an implementation.
7992+ end ;
7993+
78877994class procedure TPyObject.RegisterMembers ( APythonType : TPythonType );
78887995begin
78897996end ;
@@ -8183,6 +8290,16 @@ function TPythonType_InitSubtype( pSelf, args, kwds : PPyObject) : Integer; cde
81838290 Result := PythonToDelphi(pSelf).Init(args, kwds);
81848291end ;
81858292
8293+ function TPythonType_GetBuffer (exporter: PPyObject; view : PPy_buffer; flags: Integer): Integer; cdecl;
8294+ begin
8295+ Result := PythonToDelphi(exporter).GetBuffer(view , flags);
8296+ end ;
8297+
8298+ procedure TPythonType_ReleaseBuffer (exporter: PPyObject; view : PPy_buffer); cdecl;
8299+ begin
8300+ PythonToDelphi(exporter).ReleaseBuffer(view );
8301+ end ;
8302+
81868303function TPythonType.NewSubtypeInst ( aType: PPyTypeObject; args, kwds : PPyObject) : PPyObject;
81878304var
81888305 obj : TPyObject;
@@ -8483,6 +8600,12 @@ procedure TPythonType.InitServices;
84838600 tp_iter := TPythonType_Iter;
84848601 if bsIterNext in Services.Basic then
84858602 tp_iternext := TPythonType_IterNext;
8603+ if bsBuffer in Services.Basic then
8604+ begin
8605+ FBufferProcs.bf_getbuffer := TPythonType_GetBuffer;
8606+ FBufferProcs.bf_releasebuffer := TPythonType_ReleaseBuffer;
8607+ tp_as_buffer := @FBufferProcs;
8608+ end ;
84868609 if tpfBaseType in TypeFlags then
84878610 begin
84888611 tp_init := TPythonType_InitSubtype;
0 commit comments