Re: [Distutils] VBScript puzzle
On Mon, 22 Mar 2010 at 21:25:55, "Martin v. L?wis" <martin@v.loewis.de> wrote:
As this might be an interesting puzzle to solve, I'd like to pose it to the entire distutils-sig readership. So here is the problem again:
As a batch file, my attempt at having a batch file that is also a Python script was to write it as
rem=""" %1 %0 exit """ <python code here>
This should have been run with argv[1] being the path to the Python interpreter. As a batch file, the first line is a comment, the second line runs Python, anbd the third line terminates the script (so it doesn't consider the following lines). As a Python script, the first block is a variable assignment, followed by the regular Python script.
Now (as Installer won't run batch files) we need the same as VBScript (or, failing that, as JScript). The script would be computed at the time of MSI generation.
I couldn't get this to work as VBScript (that requires too many line continuation characters), but I think the following JScript should work. The call to WShell.Run() pops another window, so I included a time.sleep() call to make that slow enough to notice. Fortunately JScript and python both will happily ignore integer and string literals at the beginning of the file, and JScript comments look like python floor division. I ran this at the command line like so: C:\>cscript bdist.js "Python26\python.exe" Here's the script: 4 // 3; ''' ; if (WScript.Arguments.Count() < 1) WScript.Echo("usage: " + WScript.ScriptName + " <path to python>"); else WScript.CreateObject("WScript.Shell").Run(WScript.Arguments.Item(0) + " " + WScript.ScriptFullName, 1, true); /* ''' # start of python script from time import sleep print("hello from python") sleep(5) # end of python script */ HTH, Jess
On Tue, Mar 23, 2010 at 5:21 PM, "Martin v. Löwis" <martin@v.loewis.de> wrote:
That's really cute!
Glad you like it! I wrote another version, this time using WshShell.Exec() rather than WshShell.Run(). This way everything happens in the same terminal, although from random googling it seems that Exec() may be rather racy (the fact that you can't just hook WshScriptExec.StdOut to WScript.StdOut is kind of pathetic). I've also added some argument handling: C:\>cscript bdist.js Python26\python.exe foo bar Microsoft (R) Windows Script Host Version 5.7 Copyright (C) Microsoft Corporation. All rights reserved. hello from python arguments: foo, bar C:\> Script: 4 // 3; ''' ; if (WScript.Arguments.length < 1) { WScript.Echo("usage: " + WScript.ScriptName + " <path to python> [args for" + " python]"); WScript.Quit(); } var args = ""; for (i=1; i<WScript.Arguments.length; i++) { args = args + " " + WScript.Arguments.Item(i); } var oExec = WScript.CreateObject("WScript.Shell") .Exec(WScript.Arguments.Item(0) + " " + WScript.ScriptFullName + args); while (oExec.Status == 0) { while (!oExec.StdOut.AtEndOfStream) { WScript.StdOut.WriteLine(oExec.StdOut.ReadLine()); } while (!oExec.StdErr.AtEndOfStream) { WScript.StdErr.WriteLine(oExec.StdErr.ReadLine()); } WScript.Sleep(100); } /* ''' # start of python script from sys import argv print("hello from python") print("arguments: " + ", ".join(argv[1:])) # end of python script */
On 23 March 2010 20:57, Jess Austin <jess.austin@gmail.com> wrote:
I couldn't get this to work as VBScript (that requires too many line continuation characters), but I think the following JScript should work.
You are a deeply disturbed person :-) That's seriously neat, in a scary sort of way... Paul.
Jess Austin <jess.austin@gmail.com> wrote:
On Mon, 22 Mar 2010 at 21:25:55, "Martin v. L?wis" <martin@v.loewis.de> wrote:
As this might be an interesting puzzle to solve, I'd like to pose it to the entire distutils-sig readership. So here is the problem again:
As a batch file, my attempt at having a batch file that is also a Python script was to write it as
rem=""" %1 %0 exit """ <python code here>
This should have been run with argv[1] being the path to the Python interpreter. As a batch file, the first line is a comment, the second line runs Python, anbd the third line terminates the script (so it doesn't consider the following lines). As a Python script, the first block is a variable assignment, followed by the regular Python script.
Now (as Installer won't run batch files) we need the same as VBScript (or, failing that, as JScript). The script would be computed at the time of MSI generation.
I couldn't get this to work as VBScript (that requires too many line continuation characters), but I think the following JScript should work. The call to WShell.Run() pops another window, so I included a time.sleep() call to make that slow enough to notice. Fortunately JScript and python both will happily ignore integer and string literals at the beginning of the file, and JScript comments look like python floor division. I ran this at the command line like so:
C:\>cscript bdist.js "Python26\python.exe"
Here's the script:
4 // 3; ''' ; if (WScript.Arguments.Count() < 1) WScript.Echo("usage: " + WScript.ScriptName + " <path to python>"); else WScript.CreateObject("WScript.Shell").Run(WScript.Arguments.Item(0) + " " + WScript.ScriptFullName, 1, true); /* ''' # start of python script from time import sleep print("hello from python") sleep(5) # end of python script */
Note that JScript run under the MSI context (custom action type 5) doesn't have access to the WScript object. It's restricted to the set of objects accessible under the installer. http://msdn.microsoft.com/en-us/library/aa367810(VS.85).aspx Might be able to invoke "wscript.exe" to run the script, making it a type 50 custom action. But apparently that ability is frequently disabled by a group policy. Bill
Jess Austin <jess.austin@gmail.com> wrote:
C:\>cscript bdist.js "Python26\python.exe"
Here's the script:
4 // 3; ''' ; if (WScript.Arguments.Count() < 1) WScript.Echo("usage: " + WScript.ScriptName + " <path to python>"); else WScript.CreateObject("WScript.Shell").Run(WScript.Arguments.Item(0) + " " + WScript.ScriptFullName, 1, true); /* ''' # start of python script from time import sleep print("hello from python") sleep(5) # end of python script */
The killer here is WScript.ScriptFullName. The MSI framework does not want to give that to you. The only way to do this (I think) is to include the full Python program as a literal in the JScript program. It can then create a temporary file in InstallTemp, write the Python to it, find Python in the registry, and invoke Python on the Python script. To make it more general, you could use a property for the location of Python, and use another custom action to find and/or set that property. What seems to work is to base64-encode the Python script andadd it as a literal to the JScript. Then the JScript saves it to a file, and invokes Python on it with "import base64; exec(base64.decodestring(open(TEMPFILE).read().strip()))": Here's my JScript: var pythonscript_base64 = 'cHJpbnQgJ2hlbGxvLCB3b3JsZCEn\n'; var WshShell = new ActiveXObject ("WScript.Shell"); var filesystem = new ActiveXObject("Scripting.FileSystemObject"); var logfile = filesystem.CreateTextFile("C:\\install-script.log"); var installtemp = Session.Property("TempFolder"); logfile.WriteLine("TempFolder is " + installtemp); var scriptfile = filesystem.CreateTextFile(installtemp + "iscript.py"); scriptfile.Write(pythonscript_base64); scriptfile.Close(); var pythonDir = WshShell.RegRead ("HKEY_LOCAL_MACHINE\\Software\\Python\\PythonCore\\2.6\\InstallPath\\"); var pythonExe = pythonDir + "pythonw.exe"; if (! filesystem.FileExists(pythonExe)) { logfile.WriteLine("Python not found!"); exit(1); // can we really just use exit? } else { logfile.WriteLine("python is " + pythonExe); }; var cmd = pythonExe + ' -c "import base64; exec(base64.decodestring(open(' + "r'" + installtemp + "iscript.py').read()))" + '"'; logfile.WriteLine("cmd is " + cmd); var oExec = WshShell.Exec(cmd); while (oExec.Status == 0) { while (!oExec.StdOut.AtEndOfStream) { logfile.WriteLine(oExec.StdOut.ReadLine()); } while (!oExec.StdErr.AtEndOfStream) { logfile.WriteLine(oExec.StdErr.ReadLine()); } // WScript.Sleep(100); } /* ''' Bill
participants (4)
-
"Martin v. Löwis"
-
Bill Janssen
-
Jess Austin
-
Paul Moore