Skip to content

Commit 3e6a00a

Browse files
committed
Fix pyscripter#327 (Support for class references).
Added test
1 parent 96f95b3 commit 3e6a00a

File tree

3 files changed

+90
-23
lines changed

3 files changed

+90
-23
lines changed

Source/PythonEngine.pas

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1759,6 +1759,8 @@ TPythonTraceback = class
17591759
property Limit : Integer read FLimit write FLimit;
17601760
end;
17611761

1762+
TPythonType = class; //forward declaration
1763+
17621764
{$IF not Defined(FPC) and (CompilerVersion >= 23)}
17631765
[ComponentPlatformsAttribute(pidSupportedPlatforms)]
17641766
{$IFEND}
@@ -1859,6 +1861,7 @@ TPythonEngine = class(TPythonInterface)
18591861
procedure AddClient( client : TEngineClient );
18601862
procedure RemoveClient( client : TEngineClient );
18611863
function FindClient( const aName : string ) : TEngineClient;
1864+
function FindPythonType( const TypeName : AnsiString ) : TPythonType;
18621865
function TypeByName( const aTypeName : AnsiString ) : PPyTypeObject;
18631866
function ModuleByName( const aModuleName : AnsiString ) : PPyObject;
18641867
function MethodsByName( const aMethodsContainer: string ) : PPyMethodDef;
@@ -2286,9 +2289,6 @@ TPythonModule = class(TMethodsContainer)
22862289
//-- --
22872290
//-------------------------------------------------------
22882291

2289-
type
2290-
TPythonType = class; //forward declaration
2291-
22922292
{
22932293
A B C
22942294
+-------------------++------------------------------------------------------+
@@ -5768,6 +5768,19 @@ function TPythonEngine.FindModule( const ModuleName : AnsiString ) : PPyObject;
57685768
Result := nil;
57695769
end;
57705770

5771+
function TPythonEngine.FindPythonType(const TypeName: AnsiString): TPythonType;
5772+
var
5773+
i : Integer;
5774+
begin
5775+
Result := nil;
5776+
for i := 0 to ClientCount - 1 do
5777+
if (Clients[i] is TPythonType) and (TPythonType(Clients[i]).TypeName = TypeName) then
5778+
begin
5779+
Result := TPythonType(Clients[i]);
5780+
Break;
5781+
end;
5782+
end;
5783+
57715784
function TPythonEngine.FindFunction(const ModuleName,FuncName: AnsiString): PPyObject;
57725785
var
57735786
module,func: PPyObject;
@@ -8885,22 +8898,6 @@ function pyio_GetTypesStats(self, args : PPyObject) : PPyObject;
88858898
end;
88868899
end;
88878900

8888-
function FindType( const TName : AnsiString ) : TPythonType;
8889-
var
8890-
i : Integer;
8891-
begin
8892-
Result := nil;
8893-
with GetPythonEngine do
8894-
for i := 0 to ClientCount - 1 do
8895-
if Clients[i] is TPythonType then
8896-
with TPythonType(Clients[i]) do
8897-
if TypeName = TName then
8898-
begin
8899-
Result := TPythonType(Clients[i]);
8900-
Break;
8901-
end;
8902-
end;
8903-
89048901
var
89058902
i : Integer;
89068903
T : TPythonType;
@@ -8914,7 +8911,7 @@ function pyio_GetTypesStats(self, args : PPyObject) : PPyObject;
89148911
for i := 0 to PyTuple_Size(args)-1 do
89158912
begin
89168913
str := AnsiString(PyObjectAsString( PyTuple_GetItem(args, i) ));
8917-
T := FindType( str );
8914+
T := FindPythonType( str );
89188915
if Assigned(T) then
89198916
begin
89208917
obj := HandleType( T );

Source/WrapDelphi.pas

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -920,6 +920,7 @@ implementation
920920
rs_ExpectedObject = 'Expected a Pascal object';
921921
rs_ExpectedRecord = 'Expected a Pascal record';
922922
rs_ExpectedInterface = 'Expected a Pascal interface';
923+
rs_ExpectedClass = 'Expected a Pascal class';
923924
rs_InvalidClass = 'Invalid class';
924925
rs_ErrEventNotReg = 'No Registered EventHandler for events of type "%s';
925926
rs_ErrEventNoSuport = 'Class %s does not support events because it must '+
@@ -1131,6 +1132,46 @@ function ValidateInterfaceProperty(PyValue: PPyObject; RttiType: TRttiInterfaceT
11311132
ErrMsg := rs_ExpectedInterface;
11321133
end;
11331134

1135+
function ValidateClassRef(PyValue: PPyObject; TypeInfo: PTypeInfo;
1136+
out ClassRef: TClass; out ErrMsg: string): Boolean;
1137+
var
1138+
LTypeName: AnsiString;
1139+
LPythonType: TPythonType;
1140+
begin
1141+
ClassRef := nil;
1142+
if (PyValue = GetPythonEngine.Py_None) then begin
1143+
Result := True;
1144+
Exit;
1145+
end;
1146+
1147+
Result := False;
1148+
// Is PyValue a Python type?
1149+
if PyValue^.ob_type^.tp_name = 'type' then
1150+
LTypeName := PPyTypeObject(PyValue).tp_name
1151+
else
1152+
begin
1153+
ErrMsg := rs_ExpectedClass;
1154+
Exit;
1155+
end;
1156+
1157+
LPythonType := GetPythonEngine.FindPythonType(LTypeName);
1158+
if Assigned(LPythonType) then
1159+
begin
1160+
if Assigned(LPythonType) and LPythonType.PyObjectClass.InheritsFrom(TPyDelphiObject) then
1161+
begin
1162+
ClassRef := TPyDelphiObjectClass(LPythonType.PyObjectClass).DelphiObjectClass;
1163+
TypeInfo := TypeInfo^.TypeData^.InstanceType^;
1164+
if Assigned(TypeInfo) and (ClassRef.InheritsFrom(TypeInfo^.TypeData^.ClassType)) then
1165+
Result := True
1166+
else
1167+
ErrMsg := rs_IncompatibleClasses;
1168+
end
1169+
else
1170+
ErrMsg := rs_ExpectedClass;
1171+
end
1172+
else
1173+
ErrMsg := rs_ExpectedClass;
1174+
end;
11341175
{$ENDIF}
11351176

11361177
function ValidateClassProperty(PyValue: PPyObject; TypeInfo: PTypeInfo;
@@ -2831,6 +2872,7 @@ function TPyDelphiMethodObject.Call(ob1, ob2: PPyObject): PPyObject;
28312872
Index: Integer;
28322873
ErrMsg: string;
28332874
Obj: TObject;
2875+
ClassRef: TClass;
28342876
PyValue : PPyObject;
28352877
Param: TRttiParameter;
28362878
Params : TArray<TRttiParameter>;
@@ -2872,6 +2914,15 @@ function TPyDelphiMethodObject.Call(ob1, ob2: PPyObject): PPyObject;
28722914
Break
28732915
end
28742916
end
2917+
else if (Param.ParamType.TypeKind = tkClassRef) then
2918+
begin
2919+
if ValidateClassRef(PyValue, Param.ParamType.Handle, ClassRef, ErrMsg) then
2920+
Args[Index] := ClassRef
2921+
else begin
2922+
Result := nil;
2923+
Break
2924+
end
2925+
end
28752926
else if (Param.ParamType.TypeKind = tkDynArray) and PythonType.Engine.PyList_Check(PyValue) then
28762927
begin
28772928
if ParamAsDynArray(PyValue, Param, Args[Index]) then

Tests/WrapDelphiTest.pas

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
interface
77

88
uses
9-
Types,
9+
System.Types,
10+
System.Classes,
1011
DUnitX.TestFramework,
1112
PythonEngine,
1213
WrapDelphi;
@@ -35,6 +36,7 @@ TTestRecord = record
3536

3637
TFruitDynArray = TArray<TFruit>;
3738
TStaticArray = array[0..999] of Int64;
39+
3840
TTestRttiAccess = class
3941
private
4042
FFruit: TFruit;
@@ -58,6 +60,7 @@ TTestRttiAccess = class
5860
function SetStringField(var Value: Integer): string; overload;
5961
function SetStringField(const Value: string): string; overload;
6062
procedure PassVariantArray(const Value: Variant);
63+
function ClassRefParam(ClassRef: TPersistentClass): string;
6164
end;
6265

6366
TTestInterfaceImpl = class(TInterfacedObject, ITestInterface)
@@ -122,14 +125,15 @@ TTestWrapDelphi = class(TObject)
122125
procedure TestFreeReturnedObject;
123126
[Test]
124127
procedure TestPassVariantArray;
128+
[Test]
129+
procedure TestClassRefParam;
125130
end;
126131

127132
implementation
128133

129134
Uses
130135
System.SysUtils,
131136
System.Variants,
132-
System.Classes,
133137
System.Rtti,
134138
VarPyth,
135139
WrapDelphiClasses;
@@ -201,7 +205,7 @@ procedure TTestWrapDelphi.SetupFixture;
201205
Py := PyDelphiWrapper.WrapInterface(TValue.From(FTestInterface));
202206
DelphiModule.SetVar('rtti_interface', Py);
203207
PythonEngine.Py_DecRef(Py);
204-
PythonEngine.ExecString('from delphi import rtti_var, rtti_rec, rtti_interface');
208+
PythonEngine.ExecString('from delphi import rtti_var, rtti_rec, rtti_interface, Object, Collection, Strings');
205209
Rtti_Var := MainModule.rtti_var;
206210
Rtti_Rec := MainModule.rtti_rec;
207211
Rtti_Interface := MainModule.rtti_interface;
@@ -218,6 +222,16 @@ procedure TTestWrapDelphi.TearDownFixture;
218222
TestRttiAccess.Free;
219223
end;
220224

225+
procedure TTestWrapDelphi.TestClassRefParam;
226+
begin
227+
Assert.AreEqual<string>(Rtti_Var.ClassRefParam(MainModule.Collection), 'TCollection');
228+
Assert.AreEqual<string>(Rtti_Var.ClassRefParam(MainModule.Strings), 'TStrings');
229+
Assert.WillRaise(procedure
230+
begin
231+
Rtti_Var.ClassRefParam(MainModule.Object)
232+
end);
233+
end;
234+
221235
procedure TTestWrapDelphi.TestDoubleField;
222236
begin
223237
TestRttiAccess.DoubleField := 3.14;
@@ -442,6 +456,11 @@ function TTestRttiAccess.SetStringField(var Value: Integer): string;
442456
Result := StringField;
443457
end;
444458

459+
function TTestRttiAccess.ClassRefParam(ClassRef: TPersistentClass): string;
460+
begin
461+
Result := ClassRef.ClassName;
462+
end;
463+
445464
function TTestRttiAccess.GetData: TObject;
446465
begin
447466
Result := TStringList.Create;

0 commit comments

Comments
 (0)