Skip to content

Commit c654c83

Browse files
committed
Added new support unit PythonVersions.pas
1 parent a4fd2b4 commit c654c83

File tree

1 file changed

+322
-0
lines changed

1 file changed

+322
-0
lines changed
Lines changed: 322 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,322 @@
1+
{-----------------------------------------------------------------------------
2+
Unit Name: PythonVersions
3+
Author: Kiriakos
4+
Date: PyScripter
5+
Purpose: Discover and get info about Python versions
6+
Part of the Python for Delphi library
7+
8+
History:
9+
-----------------------------------------------------------------------------}
10+
11+
unit PythonVersions;
12+
13+
interface
14+
Uses
15+
Classes;
16+
17+
type
18+
19+
TPythonVersion = record
20+
private
21+
FDisplayName: string;
22+
FHelpFile: string;
23+
function GetDLLName: string;
24+
function GetSysArchitecture:string;
25+
function GetIsPython3K: Boolean;
26+
function GetHelpFile: string;
27+
function GetDisplayName: string;
28+
public
29+
IsRegistered: Boolean;
30+
IsAllUsers: Boolean;
31+
SysVersion: string;
32+
Version: string;
33+
DLLPath: string;
34+
InstallPath: string;
35+
PythonPath: string;
36+
procedure AssignTo(PythonEngine: TPersistent);
37+
property DLLName: string read GetDLLName;
38+
property SysArchitecture: string read GetSysArchitecture;
39+
property IsPython3K: Boolean read GetIsPython3K;
40+
property HelpFile: string read GetHelpFile write FHelpFile;
41+
property DisplayName: string read GetDisplayName write FDisplayName;
42+
end;
43+
44+
TPythonVersions = array of TPythonVersion;
45+
46+
(* Compares two Version strings and returns -1, 0, 1 depending on result *)
47+
function CompareVersions(const A, B : String) : Integer;
48+
49+
50+
{$IFDEF MSWINDOWS}
51+
(* Checks whether a Python version is registered and returns the related info *)
52+
function GetRegisterPythonVersion(SysVersion: string;
53+
out PythonVersion: TPythonVersion): Boolean;
54+
(* Returns all registered Python versions *)
55+
function GetRegisteredPythonVersions : TPythonVersions;
56+
(* Returns the highest numbered registered Python version *)
57+
function GetLatestRegisteredPythonVersion(PythonVersion: TPythonVersion): Boolean;
58+
{$ENDIF}
59+
60+
implementation
61+
62+
Uses
63+
{$IFDEF MSWINDOWS}
64+
Windows,
65+
{$ENDIF}
66+
SysUtils,
67+
Math,
68+
Registry,
69+
PythonEngine;
70+
71+
{ TPythonVersion }
72+
73+
function TPythonVersion.GetDLLName: string;
74+
begin
75+
{$IFDEF MSWINDOWS}
76+
Result := 'python' + SysVersion[1] + SysVersion[3] + '.dll';
77+
{$ELSE}
78+
Result := 'libpython' + SysVersion + '.so';
79+
{$ENDIF}
80+
end;
81+
82+
function TPythonVersion.GetSysArchitecture: string;
83+
begin
84+
Result := '';
85+
{$IFDEF CPUX64}
86+
Result := '64bit';
87+
{$ENDIF}
88+
{$IFDEF CPU64}
89+
Result := '64bit';
90+
{$ENDIF}
91+
{$IFDEF CPU64bits}
92+
Result := '64bit';
93+
{$ENDIF}
94+
if Result = '' then
95+
Result := '32bit';
96+
end;
97+
98+
procedure TPythonVersion.AssignTo(PythonEngine: TPersistent);
99+
begin
100+
if PythonEngine is TPythonEngine then begin
101+
TPythonEngine(PythonEngine).UseLastKnownVersion := False;
102+
TPythonEngine(PythonEngine).RegVersion := SysVersion;
103+
TPythonEngine(PythonEngine).DllName := DLLName;
104+
TPythonEngine(PythonEngine).DllPath := DLLPath;
105+
end;
106+
end;
107+
108+
function TPythonVersion.GetDisplayName: string;
109+
begin
110+
Result := FDisplayName;
111+
if Result = '' then
112+
Result := Format('Python %s (%s)', [SysVersion, SysArchitecture]);
113+
end;
114+
115+
function TPythonVersion.GetHelpFile: string;
116+
var
117+
PythonHelpFilePath: string;
118+
Res: Integer;
119+
SR: TSearchRec;
120+
begin
121+
Result := FHelpFile;
122+
// for unregistered Python
123+
if (Result = '') and (InstallPath <> '') then
124+
begin
125+
PythonHelpFilePath := InstallPath + '\Doc\python*.chm';
126+
Res := FindFirst(PythonHelpFilePath, faAnyFile, SR);
127+
if Res = 0 then
128+
Result := InstallPath + '\Doc\' + SR.Name;
129+
FindClose(SR);
130+
end;
131+
end;
132+
133+
function TPythonVersion.GetIsPython3K: Boolean;
134+
begin
135+
try
136+
Result := StrToInt(SysVersion[1]) >= 3;
137+
except
138+
Result := False;
139+
end;
140+
end;
141+
142+
function CompareVersions(const A, B : String) : Integer;
143+
var
144+
i : Integer;
145+
_delta : Integer;
146+
_version1 : TStringList;
147+
_version2 : TStringList;
148+
_version : TStringList;
149+
begin
150+
Result := 0;
151+
_version1 := TStringList.Create;
152+
try
153+
_version1.Delimiter := '.';
154+
_version1.DelimitedText := A;
155+
_version2 := TStringList.Create;
156+
try
157+
_version2.Delimiter := '.';
158+
_version2.DelimitedText := B;
159+
for i := 0 to Min(_version1.Count, _version2.Count)-1 do
160+
begin
161+
try
162+
_delta := StrToInt(_version1[i]) - StrToInt(_version2[i]);
163+
except
164+
_delta := CompareText(_version1[i], _version2[i]);
165+
end;
166+
if _delta <> 0 then
167+
begin
168+
if _delta > 0 then
169+
Result := 1
170+
else
171+
Result := -1;
172+
Break;
173+
end;
174+
end;
175+
// if we have an equality but the 2 versions don't have the same number of parts
176+
// then check the remaining parts of the stronger version, and if it contains
177+
// something different from 0, it will win.
178+
if Result = 0 then
179+
if _version1.Count <> _version2.Count then
180+
begin
181+
if _version1.Count > _version2.Count then
182+
_version := _version1
183+
else
184+
_version := _version2;
185+
for i := Min(_version1.Count, _version2.Count) to _version.Count-1 do
186+
begin
187+
if StrToIntDef(_version[i], -1) <> 0 then
188+
begin
189+
if _version1.Count > _version2.Count then
190+
Result := 1
191+
else
192+
Result := -1;
193+
Break;
194+
end;
195+
end;
196+
end;
197+
finally
198+
_version2.Free;
199+
end;
200+
finally
201+
_version1.Free;
202+
end;
203+
end;
204+
205+
{$IFDEF MSWINDOWS}
206+
function GetRegisterPythonVersion(SysVersion: string;
207+
out PythonVersion: TPythonVersion): Boolean;
208+
// Python provides for All user and Current user installations
209+
// All User installations place the Python DLL in the Windows System directory
210+
// and write registry info to HKEY_LOCAL_MACHINE
211+
// Current User installations place the DLL in the install path and
212+
// the registry info in HKEY_CURRENT_USER.
213+
// Hence, for Current user installations we need to try and find the install path
214+
// since it may not be on the system path.
215+
216+
// The above convension was changed in Python 3.5. Now even for all user
217+
// installations the dll is located at the InstallPath.
218+
// Also from version 3.5 onwards 32 bit version have a suffix -32 e.g. "3.6-32"
219+
// See also PEP 514
220+
221+
function ReadFromRegistry(Root: HKEY; key: string): Boolean;
222+
begin
223+
Result := False;
224+
try
225+
with TRegistry.Create(KEY_READ and not KEY_NOTIFY) do
226+
try
227+
RootKey := Root;
228+
if OpenKey(Key + '\InstallPath', False) then begin
229+
PythonVersion.InstallPath := ReadString('');
230+
if PythonVersion.IsAllUsers and (CompareVersions(SysVersion, '3.5') < 0) then
231+
PythonVersion.DLLPath := ''
232+
else
233+
PythonVersion.DLLPath := PythonVersion.InstallPath;
234+
CloseKey;
235+
end;
236+
Result := PythonVersion.InstallPath <> '';
237+
if not Result then Exit;
238+
239+
if OpenKey(Key, False) then begin
240+
PythonVersion.DisplayName := ReadString('DisplayName');
241+
PythonVersion.Version := ReadString('Version');
242+
CloseKey;
243+
end;
244+
if OpenKey(Key + '\PythonPath', False) then begin
245+
PythonVersion.PythonPath := ReadString('');
246+
CloseKey;
247+
end;
248+
if OpenKey(Key + '\Help\Main Python Documentation', False) then begin
249+
PythonVersion.HelpFile := ReadString('');
250+
CloseKey;
251+
end;
252+
finally
253+
Free;
254+
end;
255+
except
256+
end;
257+
258+
end;
259+
260+
var
261+
key: string;
262+
VersionSuffix: string;
263+
begin
264+
// Initialize PythohVersion
265+
Finalize(PythonVersion);
266+
FillChar(PythonVersion, SizeOf(TPythonVersion), 0);
267+
268+
VersionSuffix := '';
269+
{$IFDEF CPUX86}
270+
if CompareVersions(SysVersion, '3.5') >= 0 then
271+
VersionSuffix := '-32';
272+
{$ENDIF}
273+
key := Format('\Software\Python\PythonCore\%s%s', [SysVersion, VersionSuffix]);
274+
275+
276+
PythonVersion.SysVersion := SysVersion;
277+
// First try HKEY_CURRENT_USER as per PEP514
278+
PythonVersion.IsAllUsers := False;
279+
Result := ReadFromRegistry(HKEY_CURRENT_USER, key);
280+
281+
//Then try for an all user installation
282+
if not Result then begin
283+
PythonVersion.IsAllUsers := True;
284+
Result := ReadFromRegistry(HKEY_LOCAL_MACHINE, key);
285+
end;
286+
287+
PythonVersion.IsRegistered := Result;
288+
end;
289+
290+
function GetRegisteredPythonVersions : TPythonVersions;
291+
Var
292+
Count: Integer;
293+
I: Integer;
294+
PythonVersion : TPythonVersion;
295+
begin
296+
Count := 0;
297+
SetLength(Result, High(PYTHON_KNOWN_VERSIONS) - COMPILED_FOR_PYTHON_VERSION_INDEX + 1);
298+
for I := High(PYTHON_KNOWN_VERSIONS) downto COMPILED_FOR_PYTHON_VERSION_INDEX do
299+
if GetRegisterPythonVersion(PYTHON_KNOWN_VERSIONS[I].RegVersion, PythonVersion) then
300+
begin
301+
Result[Count] := PythonVersion;
302+
Inc(Count);
303+
end;
304+
SetLength(Result, Count);
305+
end;
306+
307+
function GetLatestRegisteredPythonVersion(PythonVersion: TPythonVersion): Boolean;
308+
Var
309+
I: Integer;
310+
begin
311+
Result := False;
312+
for I := High(PYTHON_KNOWN_VERSIONS) downto COMPILED_FOR_PYTHON_VERSION_INDEX do
313+
begin
314+
Result := GetRegisterPythonVersion(PYTHON_KNOWN_VERSIONS[I].RegVersion, PythonVersion);
315+
if Result then break;
316+
end;
317+
end;
318+
319+
{$ENDIF}
320+
321+
322+
end.

0 commit comments

Comments
 (0)