Extend the functionality of a compiled Python script - python

I'm not a programmer, so I don't even know in what terms should I ask this. Let's say I've compiled a Python script to have an .exe (I use py2exe to do this). This is the major program. Now, I want to add some extra functionality to it, but I don't want to recompile the entire script with the added functionality. I tried to search something on the web, and I found examples of extending a C++ or other application with Python scripts (like a sort of plugin). But I can't figure out how to do it with an application already written in Python.
I tried this: I wrote major.py (this is the script from where I build the executable) and stuff.py. In major I wrote this:
def generic():
import stuff
while True:
param=input('what did you say? ')
stuff.speak(param)
generic()
And in stuff I wrote this:
def speak(param):
print(param)
Then I created a .exe with py2exe. It works as expected, when I run the program in the command line says "what did you say?" and waits until I type something, then it prints what I typed.
Then, I changed stuff.py with this:
def speak(param):
print('I said '+param)
Hoping that now upon the execution of the .exe created earlier it would print "I said.." plus whatever I typed. Obviously, it didn't work, the program continued to behave like before. So I'm guessing that once I imported stuff and created the .exe file, that import is permanent, not allowing me to change whatever is in stuff. What should I do?

py2exe packs the compiled scripts in the executable.
You need to recreate the executable (which will recompile any changed script) to see the changes take effect.
EDIT following the comments:
You can do it if you dynamically import/reimport the module from inside the executable.
In your main script you do (see code below)
mod, error = loadmodule('mystuff.py')
if mod is not None:
# loading succeeded you can now proceed and do things with it
pass
Of course you have to leave mystuff.py out of the scripts that py2exe packs into the executable. In the above example mystuff.py would be in the same directory as the executable.
The loading code:
def loadmodule(modpath, modname=''):
if not modpath.endswith('.py'):
modpath += '.py'
# generate a random name for the module
if not modname:
modpathbase = os.path.basename(modpath)
modname, _ = os.path.splitext(modpathbase)
version = (sys.version_info[0], sys.version_info[1])
if version < (3, 3):
mod, e = loadmodule2(modpath, modname)
else:
mod, e = loadmodule3(modpath, modname)
return mod, e
def loadmodule2(modpath, modname):
import imp
try:
mod = imp.load_source(modname, modpath)
except Exception as e:
return (None, e)
return (mod, None)
def loadmodule3(modpath, modname):
import importlib.machinery
try:
loader = importlib.machinery.SourceFileLoader(modname, modpath)
mod = loader.load_module()
except Exception as e:
return (None, e)
return (mod, None)

If you run your script from Python, instead of compiling it as an executable, you can make changes and run them without having to recompile. The py2exe is mostly for allowing you to distribute your application to other Windows computers that don't have Python installed. After you have finished developing it, then compile it as an executable so you can run it on other computers.

There is no way to do what you want. py2exe builds a standalone python interpreter (In the file named python.dll) with just the dependencies of your project. Then the .exe file runs your script using that interpreter
I suggest you that if you really need to provide regular upgrades then you should recompile it, or install python in the target machine, or make an updating routine that checks for updates and compiles it in the target machine (With py2exe)

Related

How do I start a COM server? Code is in Python

I want to run Python code as a COM server. Eventually I want to run an RTD server available here. But first I want to know what exactly you have to do to getting any COM server running. So let's focus on this example.
class HelloWorld:
_reg_clsid_ = "{7CC9F362-486D-11D1-BB48-0000E838A65F}"
_reg_desc_ = "Python Test COM Server"
_reg_progid_ = "Python.TestServer"
_public_methods_ = ['Hello']
_public_attrs_ = ['softspace', 'noCalls']
_readonly_attrs_ = ['noCalls']
def __init__(self):
self.softspace = 1
self.noCalls = 0
def Hello(self, who):
self.noCalls = self.noCalls + 1
# insert "softspace" number of spaces
return "Hello" + " " * self.softspace + who
if __name__=='__main__':
import win32com.server.register
win32com.server.register.UseCommandLine(HelloWorld)
Ok, this works in the way that there were no errors and server is registered, hence it is available in the HKEY_CLASSES_ROOT registry. But what can I do with this? Some say you have to compile a instance and have a .dll or .exe file. WHat else do I have to do?
Well, I ran your example. The registry key for the server is at:
HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{7CC9F362-486D-11D1-BB48-0000E838A65F}
It has two subkeys... one for LocalServer32 and one for InProcServer32
I created a simple VBA macro in Excel:
Sub d()
Set obj = CreateObject("Python.TestServer")
MsgBox obj.Hello("joe")
End Sub
Macro ran just fine. My version of Excel is 64-bit. I ran the macro and then fired up Task Manager while the message box was being displayed. I could see pythonw.exe running in the background.
The only difference between my python script and yours is probably the name and also that I added a line to print to make sure I was executing the function:
if __name__=='__main__':
import win32com.server.register
print("Going to register...")
win32com.server.register.UseCommandLine(HelloWorld)
When I ran the 64-bit csript.exe test, it worked... as expected... when I ran the 32-bit version it failed.
I know why...sort of...
The registry entry for InProcServer32 is pythoncom36.dll
That's no good. It is an incomplete path. I tried modifying the path variable on my shell to add to one of the 3 places where the DLL existed on my system, but it didn't work. Also, tried coding the path in the InProcServer32. That didn't work.. kept saying it couldn't find the file.
I ran procmon, and then I observerved that it couldn't load vcruntime140.dll. Found the directory under python where those files were, and added to my path. It got further along. If I cared enough, I might try more. Eventually using procmon, I could find all the problems. But you can do that.
My simple solution was to rename the key InProcServer32 for the CLSID to be _InProcServer32. How does that work? Well, the system can't find InProcServer32 so it always uses LocalServer32--for 32-bit and 64-bit processes. If you need the speed of in process then you'd need to fix the problem by using procmon and being relentless until you solved all the File Not Found errors and such. But, if you don't need the speed of in process, then just using the LocalServer32 might solve the problem.
Caveats I'm using an Anaconda distro that my employer limits access to and I can only install it from the employee store. YMMV.

What does it mean to "initialize the Julia runtime" when exporting compiled .dll or .so files for use in other langauges?

I'm trying to compile a usable .dll file from Julia to be used in Python as I've already written a large GUI in Python and need some fast optimization work done. Normally I would just call PyJulia or some "live" call, however this program needs to be compiled to distribute within my research team, so whatever solution I end up with needs to be able to run on its own (without Julia or Python actually installed).
Right now I'm able to create .dll files via PackageCompiler.jl, something I learned from previous posts on StackOverflow, however when trying to run these files in Python via the following code
Julia mock package
module JuliaFunctions
# Pkg.add("BlackBoxOptim")
Base.#ccallable function my_main_function(x::Cfloat,y::Cfloat)::Cfloat
z = 0
for i in 1:x
z += i ^ y
end
return z
end
# function julia_main()
# print("Hello from a compiled executable!")
# end
export my_main_function
end # module
Julia script to use PackageCompiler
# using PackageCompiler
using Pkg
# Pkg.develop(path="JuliaFunctions") # This is how you add a local package
# include("JuliaFunctions/src/JuliaFunctions.jl") # this is how you add a local module
using PackageCompiler
# Pkg.add(path="JuliaFunctions")
#time create_sysimage(:JuliaFunctions, sysimage_path="JuliaFunctions.dll")
Trying to use the resulting .dll in CTypes in Python
import ctypes
from ctypes.util import find_library
from ctypes import *
path = os.path.dirname(os.path.realpath(__file__)) + '\\JuliaFunctions.dll'
# _lib = cdll.LoadLibrary(ctypes.util.find_library(path)) # same error
# hllDll = ctypes.WinDLL(path, winmode=0) # same error
with os.add_dll_directory(os.path.dirname(os.path.realpath(__file__))):
_lib = ctypes.CDLL(path, winmode=0)
I get
OSError: [WinError 127] The specified procedure could not be found
With my current understanding, this means that CTypes found the dll and imported it, but didn't find.. something? I've yet to fully grasp how this behaves.
I've verified the function my_main_function is exported in the .dll file via Nirsoft's DLL Export Viewer. Users from previous similar issues have noted that this sysimage is already callable and should work, but they always add at the end something along the lines of "Note that you will also in general need to initialize the Julia runtime."
What does this mean? Is this even something that can be done independently from the Julia installation? The dev docs in PackageCompiler mention this, however they just mention that julia_main is automatically included in the .dll file and gets called as a sort of launch point. This function is also being exported correctly into the .dll file the above code creates. Below is an image of the Nirsoft export viewer output for reference.
Edit 1
Inexplicably, I've rebuilt this .dll on another machine and made progress. Now, the dll is imported correctly. I'm not sure yet why this worked on a fresh Julia install + Python venv, but I'm going to reinstall them on the other one and update this if anything changes. For anyone encountering this, also note you need to specify the expected output, whatever it may be. In my case this is done by adding (after the import):
_lib.testmethod1.restype = c_double # switched from Cfloat earlier, a lot has changed.
_lib.testmethod1.argtypes = [c_double, c_double] # (defined by ctypes)
The current error is now OSError: exception: access violation writing 0x0000000000000024 when trying to actually use the function, which is specific to Python. Any help on this would also be appreciated.

run maya from python shell

So I have hundreds of maya files that have to be run with one script. So I was thinking why do I even have to bother opening maya, I should be able to do it from python shell (not the python shell in maya, python shell in windows)
So the idea is:
fileList = ["....my huge list of files...."]
for f in fileList:
openMaya
runMyAwesomeScript
I found this:
C:\Program Files\Autodesk\Maya201x\bin\mayapy.exe
maya.standalone.initialize()
And it looks like it loads sth, because I can see my scripts loading from custom paths. However it does not make the maya.exe run.
Any help is welcome since I never did this kind of maya python external things.
P.S. Using maya 2015 and python 2.7.3
You are on the right track. Maya.standalone runs a headless, non-gui versions of Maya so it's ideal for batching, but it is essentially a command line app. Apart from lacking GUI it is the same as regular session, so you'll have the same python path and
You'll want to design your batch process so it doesn't need any UI interactions (so, for example, you want to make sure you are saving or exporting things in a way that does not throw dialogs at the user).
If you just want a commandline-only maya, this will let you run an session interactively:
mayapy.exe -i -c "import maya.standalone; maya.standalone.initialize()"
If you have a script to run instead, include import maya.standalone and maya.standalone.initialize() at the top and then whatever work you want to do. Then run it from the command line like this:
mayapy.exe "path/to/script.py"
Presumably you'd want to include a list of files to process in that script and have it just chew through them one at a time. Something like this:
import maya.standalone
maya.standalone.initialize()
import maya.cmds as cmds
import traceback
files = ['path/to/file1.ma'. '/path/to/file2.ma'.....]
succeeded, failed = {}
for eachfile in files:
cmds.file(eachfile, open=True, force=True)
try:
# real work goes here, this is dummy
cmds.polyCube()
cmds.file(save=True)
succeeded[eachfile] = True
except:
failed[eachfile] = traceback.format_exc()
print "Processed %i files" % len(files)
print "succeeded:"
for item in succeeded:
print "\t", item
print "failed:"
for item, reason in failed.items():
print "\t", item
print "\t", reason
which should do some operation on a bunch of files and report which ones succeed and which fail for what reason

IDLE subprocess startup error

I have the code below in a file called code.py. I am using IDLE to edit the file. When I click Run>Run Module I get the error:
"IDLE's subprocess didn't make connection. Either IDLE can't start a
subprocess of personal firewall software is blocking the connection."
I am using Windows 7 Ultimate 64bit, but I have the 32bit version of Python 2.7 installed.
I have looked for a solution on this site as well as others but all of them seem to recommend deleting something called tkinter.py (I have no idea what this is) or to turn off my firewalls (I have none enabled aside from Microsoft Security Essentials which isn't a firewall.)
#Globals
#-------------------
x_pad = 476
y_pad = 444
import ImageGrab
import os
import time
import win32api, win32con
def screenGrab():
box = (x_pad+1,y_pad+1,x_pad+641,y_pad+480)
im = ImageGrab.grab(box)
im.save(os.getcwd() + '\\full_snap__' + str(int(time.time())) +
'.png', 'PNG')
def main():
pass
if __name__ == '__main__':
main()
def leftClick():
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN,0,0)
time.sleep(.1)
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP,0,0)
print 'Click.' #completely optional. But nice for debugging purposes.
def leftDown():
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN,0,0)
time.sleep(.1)
print 'left Down'
def leftUp():
win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP,0,0)
time.sleep(.1)
print 'left release'
def mousePos(cord):
win32api.SetCursorPos((x_pad + cord[0], y_pad + cord[1])
def get_cords():
x,y = win32api.GetCursorPos()
x = x - x_pad
y = y - y_pad
print x,y
The thing is "python.exe" is being obstructed by "tkinter.py") that you created(i.e., you have written a program with Tk() and named it as tkinter.py and saved that in the root folder of python). And that's it just make sure that you don't save any program file directly in the root folder of python.
Another fix!!! Hopefully this will help someone.
I had the same problem and noticed something quite interesting. I had accidentally named a file (inside the desktop folder I was working in) "tkinter" (it will cause the same problem if you rename a file by any reserved keyword, I assume). Everytime I ran or attempted to run this file, it created a pycache folder, and the error you mention above came up. Deleting the erroneously named python file solved the problem.
So - look for ANY files (in the folder you are working with or indeed the root folder) that are named after any reserved words. Delete them. Hopefully it'll work!
I had the same problem. what i did that solved it, was to move every .py file that i had created in "C:\Python33" folder, to a sub-folder that i named "Examples". seems like one of my files was the cause of this problem.
I also had the following problem. My file was named code.py, and was working fine untill I installed Canopy, and numpy.
I tried reinstalling python, but what solved the problem for me was simply renaming the file. I called my file myCode.py, everything started working fine. Strange problem...
I made a python file and named it "socket.py" so then python IDLE showing an error on startup that 'startup failure'
so the problem is that if we are using python reserved keywords or module names as our python file name that it conflicts with built-in modules.
the solution is: go to path C:\Users\sony\AppData\Local\Programs\Python\Python38 where your python files are saved and just renamed that file.
then start IDLE.
Happily using IDLE continously under python36 and windows10, it has suddenly given this error on all the programs I'm working on, with no new files created.
I terminated IDLE and tried to restart it with idle.bat but that no longer works.
Happily I have been able to restart it successfully with Lib\idlelib\idle.pyw.
All my recent programs are there and they can again be run without problems.
No need to reinstall python.

How can I add a command to the Python interactive shell?

I'm trying to save myself just a few keystrokes for a command I type fairly regularly in Python.
In my python startup script, I define a function called load which is similar to import, but adds some functionality. It takes a single string:
def load(s):
# Do some stuff
return something
In order to call this function I have to type
>>> load('something')
I would rather be able to simply type:
>>> load something
I am running Python with readline support, so I know there exists some programmability there, but I don't know if this sort of thing is possible using it.
I attempted to get around this by using the InteractivConsole and creating an instance of it in my startup file, like so:
import code, re, traceback
class LoadingInteractiveConsole(code.InteractiveConsole):
def raw_input(self, prompt = ""):
s = raw_input(prompt)
match = re.match('^load\s+(.+)', s)
if match:
module = match.group(1)
try:
load(module)
print "Loaded " + module
except ImportError:
traceback.print_exc()
return ''
else:
return s
console = LoadingInteractiveConsole()
console.interact("")
This works with the caveat that I have to hit Ctrl-D twice to exit the python interpreter: once to get out of my custom console, once to get out of the real one.
Is there a way to do this without writing a custom C program and embedding the interpreter into it?
Edit
Out of channel, I had the suggestion of appending this to the end of my startup file:
import sys
sys.exit()
It works well enough, but I'm still interested in alternative solutions.
You could try ipython - which gives a python shell which does allow many things including automatic parentheses which gives you the function call as you requested.
I think you want the cmd module.
See a tutorial here:
http://wiki.python.org/moin/CmdModule
Hate to answer my own question, but there hasn't been an answer that works for all the versions of Python I use. Aside from the solution I posted in my question edit (which is what I'm now using), here's another:
Edit .bashrc to contain the following lines:
alias python3='python3 ~/py/shellreplace.py'
alias python='python ~/py/shellreplace.py'
alias python27='python27 ~/py/shellreplace.py'
Then simply move all of the LoadingInteractiveConsole code into the file ~/py/shellreplace.py Once the script finishes executing, python will cease executing, and the improved interactive session will be seamless.

Categories

Resources