Jump to content

RSG

Members
  • Content Count

    13
  • Joined

  • Last visited

Community Reputation

0 Neutral
  1. RSG

    Debug issue with Numpy loading

    Everything checks out but numpy still won't load. Here is the complete error message it gives me: I checked the mkl-service "warning", and found it weird; when I attempted to install it, conda said it already was installed, and the warning (and the numpy error) persist. The Python version is correct, though it seems weird to show my app as the "from" output. However, the only other Python on the machine is definitely different; also, if I check the version first before attempting to import from Oct2Py, I see the expected 3.9.12. Numpy version similarly checks out. Any other ideas? I'm back to Google as I'm blocked on it now...
  2. I tried to recreate the problem, but the sample project works differently, and it seems my app is now working (on my machine). I don't know what I changed, or maybe my understanding has changed. I may revisit this if things change, but right now this is a mystery that I cannot recreate, simplify, or even explain correctly - not to mention understand it! - so I'm going to have to let this idle for now. Thanks regardless!
  3. RSG

    Debug issue with Numpy loading

    Thanks for the reply! Yes, and I've taken them into account. As I stated above, my app works on my Windows 11 machine, but not on my client's Windows 10 machine. Obviously, there are nearly infinite differences, but I don't think most are relevant. There is one final location where I could have made a mistake (the config file where my code reads the location of the Python installation), but I don't think that is the issue; I'll know soon enough. Regarding your second link, here is the most relevant text: All of those items have been addressed, with one exception; RegVersion is not set in my code, and seems to default to 3.11. The Anaconda installation we are using (same in both locations) is 3.9.12, so that could be a problem - and I will fix that! - but it doesn't seem to explain why it doesn't fail to work on my machine! To be clear, both installations use the same (incorrect) 3.11 for this setting. Tomorrow morning I will check the path and add this fix for RegVersion, but I don't expect it to make I difference - of course, I hope I'm wrong! One little detail that could be an issue: we had installed Anaconda using admin rights to make it available to all users, which the wiki calls a "registered" version. As indicated above, I am following the "specific unregistered version" (SUV) instructions. I don't think this is the cause, as it appears to me that SUV is a superset of the "specific registered version" (SRV) approach. IIRC I had some trouble using SRV when I got started with P4D and Anaconda (I had both an existing Python version and added Anaconda), which I resolved by switching to the SUV approach. If you think using SUV is wrong with a registered version, I can revisit that.
  4. Of course, I understood the indentation requirement - sorry if I didn't make that clear. That said, your other paragraph is much more to the point - thanks for the insight!
  5. I don't know if it is or not, but I already use Python scripting elsewhere... Maybe, but it isn't possible to go straight from Delphi to Matlab without a C "wrapper" DLL, so I wouldn't bet on it!! True, but I haven't been able to convince my client of that! They are scientists that use Matlab heavily, and are willing to go as far as Octave, but they refuse Python!
  6. The app I am working on needs to support running Octave (GNU answer to Matlab) scripts. My approach is to use Python4Delphi to enable Python scripting in my Delphi app, which works fine. (Note: I have other uses for Python scripting in other areas of the app.) In the Python scripts, I am using Oct2Py to connect to Octave. Because Oct2Py requires NumPy and SciPy, my Python installation is recent Anaconda, which comes with those packages pre-installed. After much trial and error, with great help from here and Stack Overflow, I got it all working. But... Yesterday I tried to set up the app on my client's machine and I couldn't get it to work. The problem is shown with the following two-line Python script: from oct2py import Oct2Py octave = Oct2Py() I'm not sure, but I believe the first line is the problem; if not that, then the second. The following message is displayed: IMPORTANT: PLEASE READ THIS FOR ADVICE ON HOW TO SOLVE THIS ISSUE! Importing the numpy c-extensions failed. This error can happen for different reasons, often due to issues with your setup. If I run the exact same script from an Anaconda shell (i.e. from the Python shell), it works correctly, so Anaconda/Oct2Py/Octave is working correctly. When run from my Delphi app, it fails with that error. Other Python can run correctly from my Delphi app, so the basic Python4Delphi mechanism seems to be working. It is the whole Delphi/Python4Delphi/Oct2Py/Octave chain that fails, on that machine (Windows 10; mine is Windows 11 - I have not been able to find any differences of significance yet!). I think the key question is why Numpy appears broken to scripts running via TPythonEngine vs. the Anaconda shell? Any ideas? For reference, this is the procedure I followed when installing everything on my client's computer: 1. Install Anaconda (https://repo.anaconda.com/archive/Anaconda3-2022.05-Windows-x86.exe) for All Users as administrator 2. Install Octave (https://ftpmirror.gnu.org/octave/windows/octave-8.2.0-w32-installer.exe) the same way. 3. In Anaconda admin shell, install oct2py (conda install -c conda-forge oct2py) 4. Make sure PATH is set to point to Anaconda correctly 5. Add system variable OCTAVE_EXECUTABLE to point to octave-cli.exe
  7. I see that is possible, but if you look at the code, it will use the GlobalVars member if those parameters are nil, right? From PythonEngine.pas: procedure TPythonEngine.ExecString(const command : AnsiString); begin Py_XDecRef( Run_CommandAsObject( command, file_input ) ); end; function TPythonEngine.Run_CommandAsString(const command : AnsiString; mode : Integer) : string; var v : PPyObject; begin Result := ''; v := Run_CommandAsObject( command, mode ); Result := PyObjectAsString( v ); Py_XDECREF(v); end; function TPythonEngine.Run_CommandAsObject(const command : AnsiString; mode : Integer) : PPyObject; begin Result := Run_CommandAsObjectWithDict(command, mode, nil, nil); end; function TPythonEngine.Run_CommandAsObjectWithDict(const command : AnsiString; mode : Integer; locals, globals : PPyObject) : PPyObject; var m : PPyObject; _locals, _globals : PPyObject; begin CheckPython; Result := nil; Traceback.Clear; CheckError(False); m := GetMainModule; if m = nil then raise EPythonError.Create('Run_CommandAsObject: can''t create __main__'); if Assigned(locals) then _locals := locals else if Assigned(FLocalVars) then _locals := LocalVars else _locals := PyModule_GetDict(m); if Assigned(globals) then _globals := globals else if Assigned(FGlobalVars) then _globals := GlobalVars else _globals := _locals; try Result := PyRun_String(PAnsiChar(CleanString(command)), mode, _globals, _locals); if Result = nil then CheckError(False); except if PyErr_Occurred <> nil then CheckError(False) else raise; end; end; Thinking a bit more, I'm wondering if any globals that are defined in a Python script get returned to the PythonEngine object; is that what I'm missing here? It seems that could be the issue I'm having now - thoughts? Thanks for the great work, by the way!
  8. Good to know, pyscripter, and thanks! I just got your reply after my last! However, it is the call to Oct2Py() that is expensive, not the import, so that doesn't help. Fortunately, as I just discovered, that call is both expensive only during the first call, and can be called by each of my modules! So it appears my "octave" Python variable can be initialized separately by each module. I do have some concerns about what Octave preserves between calls to its scripts, but this is off-topic here. I'm hoping (and will be investigating) that it maintains state between calls to its scripts! For completeness, I may still want to share globals across multiple Python modules and script calls, so I'm still interested in my original questions...
  9. Maybe getting closer; TPythonEngine has a GlobalVars property that isn't initialized to anything; this seems to set it: GlobalEngine.GlobalVars := GlobalEngine.ArrayToPyDict([]); But it doesn't seem to work, in that a global variable defined in one Python script is not available in another script. I've also confused things a bit in my earlier post; here are my two Python scripts. Script 1: print('1a. p4d-oct2py-bpl/Win32/Debug/octaveTest.py') if 'octave' in globals(): print('1a. Oct2Py already set up - reusing') else: print('1a. Setting up Oct2Py') from oct2py import Oct2Py octave = Oct2Py() def addMax(fam): print('1b. Hello Max') return octave.addMax(fam) Script 2: print('2a. p4d-oct2py-bpl/Win32/Debug/emilyTest.py') if 'octave' in globals(): print('2a. Oct2Py already set up for Emily- reusing') else: print('2a. Setting up Oct2Py for Emily') from oct2py import Oct2Py octave = Oct2Py() def addEmily(fam): print('2b. Hello Emily') return octave.addEmily(fam) Here is the output from Python (I use pyIOconnector to send standard output to a TMemo): 1a: p4d-oct2py-bpl/Win32/Debug/octaveTest.py 1a: Setting up Oct2Py 2a: p4d-oct2py-bpl/Win32/Debug/emilyTest.py 2a: Setting up Oct2Py for Emily 1b: Hello Max 2b: Hello Emily 1b: Hello Max The 1a and 2a are output when I load the two modules; clearly, both modules create their own `octave` variables. When I run the functions, we see from the 1b and 2b that the def functions are called, and the global code (the code outside of the def functions) is not. So what appears to be going on here is that the variable 'octave' in each module is really global to that module. The only problem I was worried about is that I thought each module was going to take the 5 second loading of Octave, but that doesn't appear to be the case. At this point, I suppose I don't need globals (in the sense of the single Python DLL) after all! Does that make sense? For what it's worth, the expensive call here is the first `octave = Oct2Py()` call; the second one returns immediately.
  10. I am using Python4Delphi, Oct2Py, and Octave to add Octave scripts to my Delphi app. I've got it all working beautifully in a test setup using one Python4Delphi "module" (here, a Python module that is imported via VarPyth.Import, as in Demo25's code after the comment "importing an external module and using it"). However, my production app will have several modules. The problem I am facing now is how to get them all to work together. Note that by nature of this application, only one module will ever be "running" at any point in time; the nature of the problem is that exactly one is chosen and then used, then another chosen and used, etc.; in other words, strictly sequential usage. One approach which would normally work if these were simple Python scripts is just to load the modules then run them sequentially; however, each script must call a global instance of an Oct2Py object, which is essentially the Octave interpreter (fully analogous to the Python engine!). Since we can only have one Python engine, we can only have one Octave interpreter; besides, it can take 5-10 seconds to load the Octave interpreter, so we only want to do that once. So the real issue is how to share the single Oct2Py object among the various Python modules. PythonEngine does have a GlobalVars dictionary property that appears to do what I need; if a Python script is run that loads a global variable with the Oct2Py object, then it could be provided to the other modules when they are called. However, there are no examples of this usage and I haven't been able to figure out how to do this. Is it possible? If so, how do I set up the GlobalVars property so that it contains the Oct2Py global, and then how do I provide it to the Python scripts? For reference, here is some Python-only code that works; imagine each of the two functions being separate modules. if 'octave' in globals(): print('Oct2Py/Octave already set up - reusing') else: print('Setting up Oct2Py/Octave') from oct2py import Oct2Py octave = Oct2Py() def addMembers(fam): return octave.addMembers(fam) def removeMembers(fam): return octave.removeMembers(fam)
  11. I am using Python4Delphi in my app to allow users to incorporate Octave (open-source Matlab) scripts via Oct2Py - very cool capabilities here! While not central to what I am doing, I'm hoping to provide a sort of Python console similar to running an interactive Python session from a shell/command line. This is rather trivial for simple one-line code; simply pass the user-provided string to ExecString() and it works. However, if you try to do something like the following trivial code, passing each line to ExecString, the first two lines work, but the third throws an exception as shown below. pi = 3.1415926 print(pi) if pi > 0.0: print('pi is positive') else: print('pi is negative') It appears that ExecString really expects a single Python statement in one string. I suppose this isn't terribly surprising, so maybe my goal is not realizable without a lot of work, unless I am missing something that can make this possible. Any ideas?
  12. Thanks, David, for the confirmation - that is exactly what I've done since posting the above, and it seems to work as expected. I believe you are right about "sharing the same Python interpreter. For my purposes, this is actually a good thing, as my app essentially serializes access to Python, and I need to maintain some state there - specifically, an Octave environment! So even though this solution might at first seem a pain, it is actually a good thing - for me, anyway!
  13. My app consists of a main VCL-based app and a number of plugins implemented as .bpl packages. I'd like to incorporate Python using P4D, but I'm having problems. My first attempts work fine in the main app, but I cannot get the bpl's to work. In effect, my main app loads the Python .dll, runs some Python code, then loads the .bpl packages; these packages, however, can't seem to access the Python .dll, as GetPythonEngine thinks the global variable gPythonEngine is nil. My guess is that this is explained because gPythonEngine belongs to the app, and is not visible in the .bpl. I tried loading the Python .dll in both the app and the .bpl, but that doesn't seem to work in a different way when I try to load a Python module. I first try to add a folder to the Python path using the following, abd I get a EPyAttributeError with the message "module 'sys' has no attribute 'Path'. if not Boolean(SysModule.Path.Contains(folder)) then begin SysModule.Path.Insert(0, folder); end; My guess is that I am not going to be able to use P4D from both the main app and several .bpl (i.e. DLLs) simultaneously. It seems there are a few reasons why, but I'm not certain if I understand exactly the limitations. 1. It appears that the Python DLL really only supports one instantiation per process. It isn't entirely clear if this is a DLL restriction or due to the Python DLL's design itself. Is this true absolutely? 2. Even if I could "share" the Python environment created when loading the DLL, I'm likely to run into problems because some functions use that global; for example, the SysModule functions don't provide a means to provide the pointer to the Python environment, but rather access that global directly. Is this correct? 3. Because I use certain .bpls as plug-ins, I can't readily get rid of the .bpls and go to a monolithic architecture, so it seems my only option might be to write another .bpl that encapsulates all the P4D/Python.dll stuff in it, and provides routines for other .bpls and the main app to give access to the Python environment that way. Other than being a bit more work, this does have some nice characteristics, such as hiding all the P4D details in one place, and it formalizes the notion of the Python environment (e.g. when it starts, who "owns" it, and its global nature). This could really work well for my application, as its use of Python is, by domain design, serialized naturally, and benefits from the persistent state from call to call. But before I go down that road, does this even make sense?
×