|
Related 'Python for Delphi' Links on this site:
return to main Andy Patterns home page Deploying Python for Delphi applicationsWhen deploying delphi application that uses the python for delphi components, you need to supply python21.dll (or whatever version you are working with) plus any .py files. Simple advice, but the devil is in the detail - as I found out!! Nevertheless, I managed to successfully deploy a delphi app to hundreds of users and they didn't even know there was python inside ! -Andy Bulka Quick tipsTips:
Does the python for delphi demo25 rely on python 2.1?Andy: It seems that Demo25 does indeed rely on both delphi 6 and python 2.1 Morgan: Demo25 relies on Python2.1 indirectly, as it calls the function VarIsInstanceOf, which relies on version 2.1, but if you comment out this call (and VarIsSubclassOf), it should work without any problem! Andy: It seems that to use, say python21.dll on a system that only has the python2.0 development environment installed, one need not fully install python 2.1, all you need to do is put python21.dll into winnt\system32 or into the folder you are developing the delphi application in. Morgan: Yes, that's right! Andy: You can put the Lib folder anywhere you like, and have Delphi point to it e.g. SysModule.path.append('..blah..\Lib'). Morgan: That's a solution, but the Python Dll gets its path from the Windows registry: HKEY_LOCAL_MACHINE\Software\Python\PythonCore\2.1\PythonPath Of course, each version of Python has its own entry.
NOTE: When supplying the python classes with your delphi exe, you can put the Lib folder anywhere you like, and point to it using the python list variant SysModule.path (accessible and modifiable from Delphi). The current folder of the Delphi EXE is automatically added to the SysModule.path (by PythonEngine, I think), so you can put Strings.py or whatever in the current folder of your Delphi application, if you like, though its probably better to follow the python standards and keep this stuff in a Lib folder. The 'DllName' property of the pythonEngineAndy: Changing the DllName property of the pythonEngine doesn't seem to affect things - even if I put python15.dll in the demo25 example, things still run ok! So how do I control / verify which python dll is being used and accessed? What is the DllName property really doing, if anything? Morgan: You forgot to disable the UseLastKnownVersion property in the TPythonEngine!!! If it's true, it tries each version of Python, starting from the greatest version number, and uses what it finds. If nothing is found, then DllName is used! So, if you have several versions of Python installed in your system, you can't rely on this property, and you should set it to False and define the DllName corresponding to the expected Python version, but don't forget to specify the properties APIVersion and RegVersion. If you have a doubt, look at the beginning of PythonEngine.pas, that defines all known values in an array (PYTHON_KNOWN_VERSIONS). [ Note: You don't even need to set UseLastKnownVersion to false now, because the latest strategy is to find first the dll for which your code was compiled, and then try to upgrade if it's missing. ] Compiling for different versions of pythonRunning python 2.2 from delphi 6Andy: I am getting an error when I start my dephi app 'Error
127: could not map symbol "PyUnicode_FromWideChar" This is the
first time I've tried running a python 2.2.1 app. I was using 2.1 ok, then to
change to 2.2 I changed the AtomEngine component properties: Morgan: You don't need to do this for selecting a Python
version! Keep UseLastKnownVersion to True, and adjust your defines: Andy: Ok it works now. :-) These two steps seem to be the key to getting it working and all I have to do. When I turn UseLastKnownVersion to false, I get into trouble I was having. Furthermore, playing with
Cleaning up my python path from within DelphiAndy Bulka I want to ship (with my .exe) a few python units in the 'MyLib' folder underneath where my delphi .EXE lives. The destination machine doesn't have python, so I need to supply all the relevant classes (by the way are there any dependency utils for ensuring I identify all the dependent class files?) Anyway, so I want to set the sys.path appropriately to make sure my python class files are found. Of course I could put all the classes in the same folder as my EXE and this would solve the problem, cos I know the current folder of my EXE is automatically added to the sys.path. But I want to keep my python classes in the 'MyLib' folder underneath where my delphi .EXE lives. I know that when a python engine starts up inside delphi, the sys.path is set
via the Windows registry: If I'm using Delphi 6 and python 2.1 then I can use the following cool delphi code to trim the path and set it to something I want. SysModule.path := NewPythonList; SysModule.path.append(ExtractFilePath( Application.ExeName ) + 'MyLib' ); SysModule.path.append('some other folder'); NOTE: Above relies on Jan 2002 version of python for Delphi
components. MY QUESTION: Under delphi 4, I don't have access to the variant unit and the nice SysModule variable. Is the only alternative to send some python script text at the python interpreter to set sys.path ? e.g. Memo1.Lines.Text := 'import sys' ; PythonEngine1.ExecStrings( Memo1.Lines ); apath := ExtractFilePath( Application.ExeName ) + 'MyLib' ; Memo1.Lines.Text := 'sys.path.append("' + apath + '")' ; PythonEngine1.ExecStrings( Memo1.Lines ); **** above code works, ok by the way
Morgans' reply:You have another solution by using Python APIs: Here are some sys APIs: function PySys_GetObject(s:PChar):PPyObject; cdecl; function PySys_SetObject(s:PChar;ob:PPyObject):integer; cdecl; procedure PySys_SetPath(path:PChar); cdecl; procedure PySys_SetArgv( argc: Integer; argv: PPChar); cdecl; PySys_SetPath('c:\mylib'); it will be the same than doing in Python: import sys sys.path = 'c:\\mylib'; if you want to know the path content, simply use PySys_GetObject('path') and it will return a borrowed reference to the Python path string object. If you want to replace it, you can do: PySys_SetObject('path',MyNewPathStringObject); But don't forget to decrement the reference count of MyNewPathStringObject. void PySys_SetPath(char *path) { PyObject *v; if ((v = makepathobject(path, DELIM)) == NULL) Py_FatalError("can't create sys.path"); if (PySys_SetObject("path", v) != 0) Py_FatalError("can't assign sys.path"); Py_DECREF(v); } Compiling delphi app to use specific versions of python dll.I've was playing with using different versions of python from delphi, using the latest release of your components. Here are some scenarios:
Note: Compiling issues. Set the DEFINE inside the project you are using. setting it in the python for delphi package options does not help since whilst the package is fine, the .exe you build uses .dcu's which are recompiled on demand, using your project options. Thus you need to set the actual project options. here is a viewlet demo of this technique. -Andy Bulka. How to get python for Delphi to stop looking at the PYTHONPATH environment variable and at any existing python path in the registryQuestion: Do you know any way to stop python21 from If I want a truly independent distibution, then supplying my own libs and python21.dll
can be thwarted by the user's machine happening to have a I tried clearing the PYTHONPATH dos environment variable in delphi, prior to creating a
form on which the atomengine lived - but it seems that python is I soon intend to write an article about deploying with python for delphi, including the
issues & gotchas I have encountered, so any thoughts about this cheers, P.S. Can you suggest how to avoid picking up pythonpath? I scanned through the components source code and you only look in the registry for PythonPath, so python itself must be looking at the dos environment. I have set the engine dll name manually in the property editor. I have set UseLastKnown to false, and verified the correct dll is being loaded. The python21.dll is in the same directory as the exe, and the Lib and DLLs folders underneath the folder with the exe. Even the solution of clearing this environment variable BEFORE the form/datamodule with the python engine is even created.helps in some apps, but for others doesn't work either. Initial reply from Morgan> Whilst I've got your attention - do you know any way to stop python21 from looking at PYTHONPATH environment variable, when loaded from delphi? Morgan: Not especially! > If I want a truly independent distibution, then supplying my own libs and
python21.dll can be thwarted by the user's machine happening to have a PYTHONPATH defined,
and pointing to an old set of python libs (e.g. python20). The wrong os.py gets loaded
etc. etc. Even if the first thing I do in a Morgan: Ok. > I tried clearing the PYTHONPATH dos environment variable in delphi, prior to
creating a form on which the atomengine lived - but it seems that python is loaded into a
fresh process space with a fresh set of environment variables (including the nasty
PYTHONPATH ). Note that sometimes this solution DID work e.g. on a simple sample app (thus
looked like a promising solution) - but didn't work for my big serous delphi app - not
sure why not, perhaps cos of the Morgan: Ok. Anyway, you have to clear the registry also. A Dll shares the same address space of the process it runs into. >It may apply to registry paths too, if you want to override any existing libs and supply your own. I noticed in python 22 there was something about a flag to ignore the environment - perhaps that is also relevant? Morgan: It doesn't work with Windows, only Unix. I looked at Python source code and found out a way: Simply add the following line of code in the OnBeforeLoad event of SetEnvironmentVariable('PYTHONHOME', 'c:\myHome'); And it will generate the following path: ['D:\\Users\\Morgan\\PythonForDelphi\\Demos\\Demo25', 'c:\\myHome\\DLLs', 'c:\\myHome\\lib', 'c:\\myHome\\lib\\plat-win', 'c:\\myHome\\lib\\lib-tk', 'D:\\Users\\Morgan\\PythonForDelphi\\Demos\\Demo25'] In fact, I should even add a special event for this... Here are some comments extracted from Python source code: All PC ports use this scheme to try to set up a module search path: from PC\readme.txt 1) The script location; the current directory without script. 2) The PYTHONPATH variable, if set. 3) For Win32 platforms (NT/95), paths specified in the Registry. 4) Default directories lib, lib/win, lib/test, lib/tkinter; these are searched relative to the environment variable PYTHONHOME, if set, or relative to the executable and its ancestors, if a landmark file (Lib/string.py) is found , or the current directory (not useful). 5) The directory containing the executable. from PC\getpathp.c /* We need to construct a path from the following parts. (1) the PYTHONPATH environment variable, if set; (2) for Win32, the machinepath and userpath, if set; (3) the PYTHONPATH config macro, with the leading "." of each component replaced with pythonhome, if set; (4) the directory containing the executable (argv0_path). The length calculation calculates #3 first. Extra rules: - If PYTHONHOME is set (in any way) item (2) is ignored. - If registry values are used, (3) and (4) are ignored. */ Afterthought....From: Morgan Martinet [morgan.martinet@altavista.net] Hi again! In my last mail I omitted a point: I had removed the registry entries of Python, and it worked fine. But have proper registry settings override the PYTHONHOME path, as they are placed first in the path list. Anyway, I discovered that you can define your own Python settings in the current user registry, and they will override the default settings of local machine! So, with regedit I defined the following entries: Windows Registry Editor Version 5.00 [HKEY_CURRENT_USER\Software\Python] [HKEY_CURRENT_USER\Software\Python\PythonCore] [HKEY_CURRENT_USER\Software\Python\PythonCore\2.1] [HKEY_CURRENT_USER\Software\Python\PythonCore\2.1\PythonPath] @="d:\\default" [HKEY_CURRENT_USER\Software\Python\PythonCore\2.1\PythonPath\PythonWin] @="d:\\pythonwin" [HKEY_CURRENT_USER\Software\Python\PythonCore\2.1\PythonPath\Win32] @="d:\\win32com" [HKEY_CURRENT_USER\Software\Python\PythonCore\2.1\PythonPath\win32com] And I get the following path from Python: ['D:\\Users\\Morgan\\PythonForDelphi\\Demos\\Demo25', 'd:\\pythonwin', 'd:\\win32com', 'D:\\Users\\Morgan\\PythonForDelphi\\Demos\\Demo25', 'd:\\default', 'C:\\Python21\\Pythonwin', 'C:\\Python21\\win32', 'C:\\Python21\\win32\\lib', 'C:\\Python21', 'C:\\Python21\\Lib\\plat-win', 'C:\\Python21\\Lib', 'C:\\Python21\\DLLs', 'C:\\Python21\\Lib\\lib-tk'] Hope this helps, Andy still is not clear:So you are saying that inserting d:\\default into PythonPath is also needed? in conjunction with SetEnvironmentVariable('PYTHONHOME', 'd:\default'); So in summary, the combination: SetEnvironmentVariable('PYTHONHOME', 'c:\myHome'); and [HKEY_CURRENT_USER\Software\Python\PythonCore\2.1\PythonPath]@="d:\\default" ( the latter hopefully should appear before any other refs to any other \lib.) will fix the problem? I'll play around some more with the new PYTHONHOME idea. But try point your PYTHONPATH to a python20 lib folder and load python21.dll via delphi, and load os and urllib etc. Weird things happen unless the correct versions are loaded, that's for sure. I'll see how the PYTHONHOME works... Morgan's final word on this issue:You don't even need to use: SetEnvironmentVariable('PYTHONHOME', 'c:\myHome'); Simply override the Python registry settings for the current user, as you can't do it for the local machine if the logged user has not admin rights. Andy's final Solution:I never did resolve this. So I just added some code to the delphi app which checked to see if PYTHONPATH was defined. If it was, the app refuses to run. I just couldn't afford to take chances and didn't have the time to resolve this issue to my satisfaction. Anybody else is welcome to email me so I can do a final summary. Where does python for delphi look for the DLL at run time?>Is there any way I can control WHERE it looks for the DLL at run time? Yes, simply set the property UseLastKnownVersion to False, and set the property DllName to the fully qualified path of your python21.dll. Don't forget to set the properties APIVersion to 1010 and RegVersion to 2.1 If you want to do it by code, you must also set the property AutoLoad to False, and set the previous properties in the OnCreate event of your form or datamodule, and finally call the method LoadDll. Note that in the next release, I will add a property to specify the Dll's folder. Hope this helps, Morgan Related 'Python for Delphi' Links on this site:
return to main Andy Patterns home page |