I'm having a problem embedding the python 3 engine for an app that need to run custom scripts in python. Since the scripts might be completely different, and sometimes user provided, I am trying to make each execution isolated and there is not need to preserve any data between execution of the different scripts.
So, my solution is to wrap each execution between Py_Initialize and Py_Finalize. It looks something like that:
void ExecuteScript(const char* script)
{
Py_Initialize();
PyRun_SimpleString( script );
Py_Finalize();
}
However, this fails for a particular python script the second time a script is executed with:
done!
Traceback (most recent call last):
File "<string>", line 8, in <module>
File "\Python33Test\Output\Debug\Python33\Lib\copy.py", line 89, in copy
rv = reductor(2)
TypeError: attribute of type 'NoneType' is not callable
The python script looks like this:
class Data:
value1 = 'hello'
value2 = 0
import copy
d = Data()
dd = copy.copy( d )
print ( 'done!' )
As you can see, the first time around the script was executed the 'done!' was printed out. But the second time it rises an exception inside the copy function.
It looks like the python engine was left in some weird state after the first initialize-finalize. Note, this is python 3.
Also, it is very interesting to note that Python 2.7 did not have this problem.
I guess there might be other examples that could reveal better what's going, but i haven't had the time to find yet.
Full sources of the test project can be found here:
https://docs.google.com/file/d/0B86-G0mwwxZvNGpoM1Jia3E2Wmc/edit?usp=sharing
Note, the file is 8MB because it includes the python distribution.
Any ideas of how to solve this are appreciated.
EDIT: I also put a copy of the project containing flag to switch between Python 3 and Python 2.7 (the file is 31 MB): https://docs.google.com/file/d/0B86-G0mwwxZvbWRldTd5b2NNMWM/edit?usp=sharing
EDIT: Well, I tested with Python3.2 and it worked fine. So it seems to be bug in Python3.3 only. Adding as an issue: http://bugs.python.org/issue17408#
Well, this was fast.
They actually found the issue being a problem in Python 3.3 code.
http://bugs.python.org/issue17408#
Related
I'm facing some problems trying to load a full python script from my pastebin/github pages.
I followed this link, trying to convert the raw into a temp file and use it like a module: How to load a python script from a raw link (such as Pastebin)?
And this is my test (Using a really simple python script as raw, my main program is not so simple unfortunately): https://trinket.io/python/0e95ba50c8
When I run the script (that now is creating a temp file in the current directory of the .py file) I get this error:
PermissionError: [Errno 13] Permission denied: 'C:\\Users\\BOT\\Images\\tempxm4xpwpz.py'
Otherwise I also treid the exec() function... No better results unfortunately.
With this code:
import requests as rq
import urllib.request
def main():
code = "https://pastebin.com/raw/MJmYEKqh"
response = urllib.request.urlopen(code)
data = response.read()
exec(data)
I get this error:
File "<string>", line 10, in <module>
File "<string>", line 5, in hola
NameError: name 'printest' is not defined
Since my program is more complex compared to this simple test, I don't know how to proceed...
Basically What I want to achieve is to write the full script of my program on GitHub and connect it to a .exe so if I upgrade the raw also my program is updated. Avoiding to generate and share (only with my friends) a new .exe everytime...
Do you think is possible? If so.. what am I doing wrong?
PS: I'm also open to other possibilities to let my friends update the program without downloading everytime the .exe, as soon as they don't have to install anything (that's why I'm using .exe).
Disclaimer: it is really not a good idea to run an unverified (let alone untrusted) code. That being said if you really want to do it...
Probably the easiest and "least-dirty" way would be to run whole new process. This can be done directly in python. Something like this should work (inspiration from the answer you linked in your question):
import urllib.request
import tempfile
import subprocess
code = "https://pastebin.com/raw/MJmYEKqh"
response = urllib.request.urlopen(code)
data = response.read()
with tempfile.NamedTemporaryFile(suffix='.py') as source_code_file:
source_code_file.write(data)
source_code_file.flush()
subprocess.run(['python3', source_code_file.name])
You can also make your code with exec run correctly:
What may work:
exec(data, {}) -- All you need to do, is to supply {} as second argument (that is use exec(data, {})). Function exec may receive two additional optional arguments -- globals and locals. If you supply just one, it will use the same directory for locals. That is the code within the exec would behave like sort-of "clean" environment, at the top-level. Which is something you aim for.
exec(data, globals()) -- Second option is to supply the globals from your current scope. This will also work, though you probably has no need to give the execucted code access to your globals, given that that code will set-up everything inside anyway
What does not work:
exec(data, {}, {}) -- In this case the executed code will have two different dictionaries (albeit both empty) for locals and globals. As such it will behavie "as-in" (I'm not really sure about this part, but as I tested it, it seams as such) the function. Meaning that it will add the printest and hola functions to the local scope instead of global scope. Regardless, I expected it to work -- I expected it will just query the printest in the hola function from the local scope instead of global. However, for some reason the hola function in this case gets compiled in such a way it expects printest to be in global scope and not local, which is not there. I really did not figured out why. So this will result in the NameError
exec(data, globals(), locals()) -- This will provide access to the state from the caller function. Nevertheless, it will crash for the very same reason as in the previous case
exec(data) -- This is just a shorthand for exec(data, globals(), locals()
I want to have an object, generated by an existing MATLAB script, modelled in FreeCAD. See bolded titles for important content of Intro, Matlab code, python code, Testing via terminal, Matlab error, Notes (OS, versions)
Intro
I have each side of the process worked out, but I am having issues with the calling of the python script through MATLAB.
I have both object constructor, and a script that works in building things in FreeCAD; so essentially, I have either end. What I need now is to interface them.
My test is basic, but should have been enough, or so I assumed.
Matlab:
pyfile = '~/Desktop/FreeCADworkspace/testvec2.py';
filename = 'atestname';
call = "python " + pyfile + " " + filename;
[status,result] = system("sh ~/Desktop/FreeCADworkspace/freecad.sh")
Python:
# Import python roots
import sys
import os
sys.path.append('/usr/lib/freecad-python2/lib')
sys.path.append('/usr/lib/freecad/lib')
# Other imports
import numpy as np
import math
# Import FreeCAD and parts
import FreeCAD
from FreeCAD import Base
import Part, Sketcher, Draft
try:
filename = sys.argv[1]
except: # this should never be thrown, exists for testing and other integration
print('No arguments called into script.')
print('Please use format: python thisScript.py arg_filename')
quit() # exit script
print(filename) # checkmeplz
So, essentially what should happen is that I should get a print-out of the definition of filename in MATLAB. In this case, I should see 'atestname'
Testing
If I call this through terminal:
python ~/Desktop/FreeCADworkspace/testvec2.py atestname
I get:
FreeCAD 0.18.1, Libs: 0.18.1R
atestname
Which is exactly as expected. The same cannot be said of my MATLAB
MATLAB ERROR
Traceback (most recent call last):
File "/home/ashaiden/Desktop/FreeCADworkspace/testvec2.py", line 11, in <module>
import FreeCAD # no FreeCADGui??
ImportError: /usr/lib/freecad-python2/lib/libFreeCADBase.so: undefined symbol: _ZN11xercesc_3_111InputSource11setEncodingEPKt
I have also tried to execute the python script from MATLAB via a bash script. Same error.
What is confusing me: why would some command that executes perfectly through the terminal, be failing when calling via MATLAB?
I feel like I am misunderstanding how MATLAB system calls are performed. I assumed that the call was sent to, and processed by, the OS itself. But where that the case, it would not make sense to be getting an error. Does MATLAB do some interpreting of files that it is handling?
Notes
Operating system: Ubuntu 16.04
MATLAB 2018b
Python 2.7 -> is this potentially the issue? Python 2 seemed to be the default language for my particular FreeCAD install.
FreeCAD 0.18.1
Edit
After much searching by myself and my supervisor, it appears that this might be able to be put down to a conflict between binary files.
MATLAB has libxerces-c.so files defined inside /bin/glnxa64/ and another toolbox folder.
These may be conflicting with the linux binary /usr/lib/x86_64-linux-gnu/libxerces-c.so such that when the terminal is called via matlab, it uses the matlab binary instead of the system binary.
I will continue to investigate further.
Answering my own questions!
Edit 30/7 : This does not actually adequately solve the problem for linux, given that paths may have some slight difference between machines. It also does not solve the problem for mac or windows (the issue does occur on mac, but I have no data if it occurs on windows.
After digging and searching, I edited the question:
MATLAB has libxerces-c.so files defined inside /bin/glnxa64/ and
another toolbox folder.
These may be conflicting with the linux binary
/usr/lib/x86_64-linux-gnu/libxerces-c.so such that when terminal is
called via matlab, it uses the matlab binary instead of the system
binary.
I was correct that this was due to a conflict between MATLAB's bin libxerces-c.so and the linux native libxerces-c.so. I have found at least a partial fix for this. I do not know of its robustness. At worst, the path will be edited and then returned to original format each time FreeCAD will be called within my code (gross).
SOLUTION:
Important first step: oldpath = getenv("LD_LIBRARY_PATH") to get and then SAVE some record of the current path. This, for me, is in case some
issues or conflicts arise later.
I then removed the section of the path '/usr/local/MATLAB/R2018b/bin/glnxa64/' and saved the string to newpath
I then made this the new path: setenv("LD_LIBRARY_PATH", newpath),
where newpath no longer has the reference to the conflicting
folder
And now I am getting the expected result in my MATLAB command window:
FreeCAD 0.18.1, Libs: 0.18.1R
atestname
The comparative paths:
Newpath = '/usr/local/MATLAB/R2018b/sys/opengl/lib/glnxa64:/usr/local/MATLAB/R2018b/sys/os/glnxa64:/usr/local/MATLAB/R2018b/extern/lib/glnxa64:/usr/local/MATLAB/R2018b/runtime/glnxa64:/usr/local/MATLAB/R2018b/sys/java/jre/glnxa64/jre/lib/amd64/native_threads:/usr/local/MATLAB/R2018b/sys/java/jre/glnxa64/jre/lib/amd64/server:/usr/lib/x86_64-linux-gnu/';
Oldpath = '/usr/local/MATLAB/R2018b/sys/opengl/lib/glnxa64:/usr/local/MATLAB/R2018b/sys/os/glnxa64:/usr/local/MATLAB/R2018b/bin/glnxa64:/usr/local/MATLAB/R2018b/extern/lib/glnxa64:/usr/local/MATLAB/R2018b/runtime/glnxa64:/usr/local/MATLAB/R2018b/sys/java/jre/glnxa64/jre/lib/amd64/native_threads:/usr/local/MATLAB/R2018b/sys/java/jre/glnxa64/jre/lib/amd64/server:/usr/lib/x86_64-linux-gnu/';
I tried to call python code from c, the example runs ok for sample code on my environment(python3.6), but when I integrate it into my program, I got following error when I call Py_Initialize();:
...
sem_init: Success
Fatal Python error: Can't initialize threads for interpreter
Could you provide some clues to solve this problem?
It seems the error comes from here, but I am still not sure how to avoid this.
The failing code is
if (head_mutex == NULL)
Py_FatalError("Can't initialize threads for interpreter");
Searching the code back for head_mutex references finds
#define HEAD_INIT() (void)(head_mutex || (head_mutex = PyThread_allocate_lock()))
which is called right before the failing code.
So, the reason is that PyThread_allocate_lock returns NULL. There are a few different implementations for it in Python codebase depending on the OS and build flags, so you need to debug it or otherwise figure out which one is used in your case to track the error further to an OS call.
There is a function named sem_init in my program, which may conflict with the system library, the program runs ok after I modify the name of this function(but still not sure the reason).
I am new to python and have looked at similar questions but none seems to offer a solution to my simple case so I suspect I have made a basic error. I am using Python 2.7 on a Mac and on Ubuntu running on a Chromebook. When the project is complete I shall transfer it to a Raspberry Pi.
This snippet of code runs without a problem if I invoke the interpreter with the python call and type in the code.
switch = (int(time.strftime("%M"))%2
WhichOne = "Right","Left"
usbname = WhichOne[switch]
However, when I run the script containing this code fragment by typing ./project20160218.py or
python project20160218.py
I obtain
user#chrubuntu:~/Documents/Degree day project$ python project20160218.py
File "project20160218.py", line 23
WhichOne = "Right","Left"
^
SyntaxError: invalid syntax
I would be very grateful for some guidance here.
Thanks.
You're missing a closing parenthesis in the first line just before %2:
switch = (int(time.strftime("%M")))%2
WhichOne = "Right","Left"
usbname = WhichOne[switch]
If you close that it works. Also I assume you're importing time elsewhere, otherwise that would be undefined and also cause issues.
I am following this guide:
http://docs.python.org/3/extending/embedding.html
everything works as described there, until the moment when I want to load more than 1 python script.
My source code is here: https://github.com/huggle/huggle3-qt-lx/blob/ea6e7cafa0b7c57081368ee17a66aaee919d0176/huggle/pythonengine.cpp
Basically what I am trying to do is to create some kind of hooks, that could be optionally "overriden" in the python script that are extending my applications. So that in case of several events that happen in my C++ application, a python hook in extension may be called (if it exist).
So far I am registering hook called hook_main_window_is_loaded that is to be called when main window finish loading, so that python extensions can do something. I am doing it using
// load symbols for hooks now
this->ptr_Hook_MainLoaded = PyObject_GetAttrString(this->object, "hook_main_window_is_loaded");
if (this->ptr_Hook_MainLoaded != NULL && !PyCallable_Check(this->ptr_Hook_MainLoaded))
{ ...
(line 171)
it works great as long as I am loading only 1 python script. It doesn't matter if this script has this function (let's call it MainWindowHook):
def hook_main_window_is_loaded():
print ("called hook_main_window_is_loaded");
or not. However, if I load more than 1 script, it only works when both of them have this MainWindowHook implemented. If first one does not have it, then for unknown reasons fail to load the second script o.O with following error:
AttributeError: 'module' object has no attribute 'hook_main_window_is_loaded'
Thu Feb 20 10:22:02 2014 Failed to load a python script: extensions/ext2.py
This would make sense, if the first one failed to load (because that is one which doesn't contain the MainWindowHook), but if I have only first python script (even with no definition of MainWindowHook) it still works.
In simple words:
When I have only 1 python script to load, it works no matter if MainWindowHook is there or not (This is normal and expected)
When I have more than 1, it works only if all of them contain MainWindowHook (not expected)
When I have 2 of them, it works even if first which is loaded contains MainWindowHook but second not (really weird)
Why is that? How can I load more than 1 script so that I don't need to have this definition in all scripts?
EDIT: I figured out there is a workaround for this:
When I read the entire source code file (.py) and use regex to check if the function definition is there and call PyObject_GetAttrString only if regex find the definition there, it works. However it's a nasty thing which may not work well and creates extra overhead, while according to python documentation, the function PyObject_GetAttrString should return NULL in case there is no such a function (it doesn't do that).