16
16
17
17
unit PythonEngine;
18
18
19
- { TODO -oMMM : implement tp_as_buffer slot }
20
19
{ TODO -oMMM : implement Attribute descriptor and subclassing stuff }
21
20
22
21
{ $IFNDEF FPC}
@@ -189,6 +188,7 @@ TPythonVersionProp = record
189
188
WCharTString = UnicodeString;
190
189
{ $ENDIF}
191
190
191
+ PPy_ssize_t = PNativeInt;
192
192
Py_ssize_t = NativeInt;
193
193
194
194
const
@@ -307,7 +307,7 @@ TPythonVersionProp = record
307
307
T_UINT = 11 ;
308
308
T_ULONG = 12 ;
309
309
310
- // * Added by Jack: strings contained in the structure */
310
+ // * strings contained in the structure */
311
311
T_STRING_INPLACE= 13 ;
312
312
313
313
T_OBJECT_EX = 16 ;{ * Like T_OBJECT, but raises AttributeError
@@ -326,6 +326,32 @@ TPythonVersionProp = record
326
326
mtStringInplace, mtObjectEx);
327
327
TPyMemberFlag = (mfDefault, mfReadOnly, mfReadRestricted, mfWriteRestricted, mfRestricted);
328
328
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
+
329
355
// #######################################################
330
356
// ## ##
331
357
// ## Non-Python specific constants ##
@@ -610,7 +636,35 @@ TPythonVersionProp = record
610
636
m_free : inquiry;
611
637
end ;
612
638
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
+
613
666
// object.h
667
+
614
668
PyTypeObject = { $IFDEF CPUX86} packed { $ENDIF} record
615
669
ob_refcnt: NativeInt;
616
670
ob_type: PPyTypeObject;
@@ -643,7 +697,7 @@ TPythonVersionProp = record
643
697
tp_setattro: setattrofunc;
644
698
645
699
// Functions to access object as input/output buffer
646
- tp_as_buffer: Pointer; // PPyBufferProcs - not implemented
700
+ tp_as_buffer: PPyBufferProcs;
647
701
// Flags to define presence of optional/expanded features
648
702
tp_flags: C_ULong;
649
703
@@ -1075,6 +1129,7 @@ FutureWarning = class (EPyWarning);
1075
1129
EPySyntaxWarning = class (EPyWarning);
1076
1130
EPyRuntimeWarning = class (EPyWarning);
1077
1131
EPyReferenceError = class (EPyStandardError);
1132
+ EPyBufferError = class (EPyException);
1078
1133
{ $IFDEF MSWINDOWS}
1079
1134
EPyWindowsError = class (EPyOSError);
1080
1135
{ $ENDIF}
@@ -1326,6 +1381,7 @@ TPythonInterface=class(TDynamicDll)
1326
1381
PyExc_UnicodeDecodeError: PPPyObject;
1327
1382
PyExc_UnicodeEncodeError: PPPyObject;
1328
1383
PyExc_UnicodeTranslateError: PPPyObject;
1384
+ PyExc_BufferError: PPPyObject;
1329
1385
1330
1386
PyCode_Type: PPyTypeObject;
1331
1387
PyType_Type: PPyTypeObject;
@@ -1427,8 +1483,20 @@ TPythonInterface=class(TDynamicDll)
1427
1483
PySys_SetArgv: procedure( argc: Integer; argv: PPWCharT); cdecl;
1428
1484
1429
1485
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;
1432
1500
PyEval_CallObjectWithKeywords:function (callable_obj, args, kw:PPyObject):PPyObject; cdecl;
1433
1501
PyEval_GetFrame:function :PPyObject; cdecl;
1434
1502
PyEval_GetGlobals:function :PPyObject; cdecl;
@@ -1546,6 +1614,9 @@ TPythonInterface=class(TDynamicDll)
1546
1614
PyObject_GC_Del:procedure (ob:PPyObject); cdecl;
1547
1615
PyObject_GC_Track:procedure (ob:PPyObject); cdecl;
1548
1616
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;
1549
1620
PySequence_Check:function (ob:PPyObject):integer; cdecl;
1550
1621
PySequence_Concat:function (ob1,ob2:PPyObject):PPyObject; cdecl;
1551
1622
PySequence_Count:function (ob1,ob2:PPyObject):integer; cdecl;
@@ -2427,6 +2498,8 @@ TPyObject = class
2427
2498
function Iter : PPyObject; virtual ;
2428
2499
function IterNext : PPyObject; virtual ;
2429
2500
function Init ( args, kwds : PPyObject ) : Integer; virtual ;
2501
+ function GetBuffer (view : PPy_buffer; flags: Integer): Integer; virtual ;
2502
+ procedure ReleaseBuffer (view : PPy_buffer); virtual ;
2430
2503
2431
2504
// Number services
2432
2505
function NbAdd ( obj : PPyObject) : PPyObject; virtual ;
@@ -2495,7 +2568,8 @@ TPyObjectClass = class of TPyObject;
2495
2568
// since version 2.1
2496
2569
bsRichCompare,
2497
2570
// since version 2.2
2498
- bsIter, bsIterNext);
2571
+ bsIter, bsIterNext,
2572
+ bsBuffer);
2499
2573
TNumberServices = set of (nsAdd, nsSubtract, nsMultiply,
2500
2574
nsRemainder, nsDivmod,
2501
2575
nsPower, nsNegative, nsPositive,
@@ -2571,6 +2645,7 @@ TPythonType = class(TGetSetContainer)
2571
2645
FTypeFlags : TPFlags;
2572
2646
FCreateFunc : PPyObject;
2573
2647
FCreateFuncDef : PyMethodDef;
2648
+ FBufferProcs: PyBufferProcs;
2574
2649
FGenerateCreateFunction: Boolean;
2575
2650
2576
2651
procedure Notification ( AComponent: TComponent;
@@ -3550,6 +3625,8 @@ procedure TPythonInterface.MapDll;
3550
3625
PyExc_UnicodeDecodeError := Import (' PyExc_UnicodeDecodeError' );
3551
3626
PyExc_UnicodeEncodeError := Import (' PyExc_UnicodeEncodeError' );
3552
3627
PyExc_UnicodeTranslateError:= Import (' PyExc_UnicodeTranslateError' );
3628
+ PyExc_BufferError := Import (' PyExc_BufferError' );
3629
+
3553
3630
PyType_Type := Import (' PyType_Type' );
3554
3631
PyCFunction_Type := Import (' PyCFunction_Type' );
3555
3632
PyCode_Type := Import (' PyCode_Type' );
@@ -3639,7 +3716,17 @@ procedure TPythonInterface.MapDll;
3639
3716
PySys_SetArgv := Import (' PySys_SetArgv' );
3640
3717
Py_Exit := Import (' Py_Exit' );
3641
3718
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' );
3643
3730
3644
3731
PyEval_CallObjectWithKeywords:= Import (' PyEval_CallObjectWithKeywords' );
3645
3732
PyEval_GetFrame := Import (' PyEval_GetFrame' );
@@ -3757,6 +3844,9 @@ procedure TPythonInterface.MapDll;
3757
3844
PyObject_GC_Del := Import (' PyObject_GC_Del' );
3758
3845
PyObject_GC_Track := Import (' PyObject_GC_Track' );
3759
3846
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' );
3760
3850
PySequence_Check := Import (' PySequence_Check' );
3761
3851
PySequence_Concat := Import (' PySequence_Concat' );
3762
3852
PySequence_Count := Import (' PySequence_Count' );
@@ -5263,6 +5353,8 @@ procedure TPythonEngine.RaiseError;
5263
5353
raise Define( EPyValueError.Create(' ' ), s_type, s_value )
5264
5354
else if (PyErr_GivenExceptionMatches(err_type, PyExc_ReferenceError^) <> 0 ) then
5265
5355
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 )
5266
5358
else if (PyErr_GivenExceptionMatches(err_type, PyExc_SystemError^) <> 0 ) then
5267
5359
raise Define( EPySystemError.Create(' ' ), s_type, s_value )
5268
5360
else if (PyErr_GivenExceptionMatches(err_type, PyExc_MemoryError^) <> 0 ) then
@@ -7589,6 +7681,16 @@ function TPyObject.GetAttrO( key: PPyObject) : PPyObject;
7589
7681
Result := GetPythonEngine.PyObject_GenericGetAttr(GetSelf, key);
7590
7682
end ;
7591
7683
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
+
7592
7694
function TPyObject.SetAttrO ( key, value : PPyObject) : Integer;
7593
7695
begin
7594
7696
Result := GetPythonEngine.PyObject_GenericSetAttr(GetSelf, key, value );
@@ -7884,6 +7986,11 @@ class procedure TPyObject.RegisterMethods( APythonType : TPythonType );
7884
7986
begin
7885
7987
end ;
7886
7988
7989
+ procedure TPyObject.ReleaseBuffer (view : PPy_buffer);
7990
+ begin
7991
+ // Do nothing. Subclasses may provide an implementation.
7992
+ end ;
7993
+
7887
7994
class procedure TPyObject.RegisterMembers ( APythonType : TPythonType );
7888
7995
begin
7889
7996
end ;
@@ -8183,6 +8290,16 @@ function TPythonType_InitSubtype( pSelf, args, kwds : PPyObject) : Integer; cde
8183
8290
Result := PythonToDelphi(pSelf).Init(args, kwds);
8184
8291
end ;
8185
8292
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
+
8186
8303
function TPythonType.NewSubtypeInst ( aType: PPyTypeObject; args, kwds : PPyObject) : PPyObject;
8187
8304
var
8188
8305
obj : TPyObject;
@@ -8483,6 +8600,12 @@ procedure TPythonType.InitServices;
8483
8600
tp_iter := TPythonType_Iter;
8484
8601
if bsIterNext in Services.Basic then
8485
8602
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 ;
8486
8609
if tpfBaseType in TypeFlags then
8487
8610
begin
8488
8611
tp_init := TPythonType_InitSubtype;
0 commit comments