Playing with Python
iNMR contains an embedded Lua interpreter. You can write powerful and fast programs with this
lightweight component. If you prefer, however, you can use other languages, like Bash, AppleScript,
Perl, etc.. In this article we give simple demonstrations that you can drive iNMR from an external
Python program and that you can run Python programs from iNMR. At the end of the tutorial your Mac will return
to its original state. For this reason we are not going to use the most efficient and elegant solutions.
If you know unix and Python you will be able to find them.
You need a text editor to create your scripts (I have used Smultron, you may prefer TextWrangler or TextEdit). To start with, create a folder called “sandbox” right in your home directory. We are going to put there all our files. Eventually you can delete the whole folder and clean up your computer.
Driving iNMR from a Python wrapper
Our first script will open iNMR and put some text into it. The script is made of the following three lines.
#!/usr/bin/python import os os.system("""osascript -e 'tell app "iNMR" to set message to "Hello from Python"'""")
Copy and paste them into your file, save it with the name “pynmr” into the sandbox folder. We'll run the script from a unix shell. Open the Terminal application and type:
PATH=$PATH:~/sandbox cd sandbox chmod +x pynmr pynmr
Switch to iNMR and verify the result:
We have actually inserted an AppleScript into a Python program. With the same mechanism we can set and get other iNMR variables. At this point you might study the companion AppleScript tutorial to discover the full capabilities of this approach. You can find extensions to Python to send Apple Events in a different way.
- Scripting Bridge
- Using Scripting Bridge in PyObjC
- Python on the Mac
- Using Python and AppleScript Together
Calling Python from iNMR
The standard Lua library contains the command os.execute to execute a generic unix command. Try, for example: os.execute "say I love you". The results are visible only into the Console (an Apple application you can find into the Utility subfolder). iNMR version 3.6.1 introduced the alternative function shell, that captures the standard output of what you call. Try, for example, the following statement directly from the iNMR console:
print( shell "ls /" )
After clicking the Run button you will see this:
You can build, upon the shell command, a function that executes python code. Close the iNMR console and add the following lines into your Lua.init file. (If you haven't this file yet, create it as an empty file among your Lua scripts).
function python( statement ) return shell( "python -c \""..statement.."\"" ) end
Open the console again and try the commands:
zen = python "import this" print( zen )
Both os.execute and shell share the risk of making iNMR unresponsive. Let's insert a space between the minus and the c. This is a dangerous error.
seven = shell "python - c 'print 7'"
It is interpreted by the shell as "python", the command that starts the Python interpreter. iNMR will remains frozen until you exit from Python. The simplest way of doing it is to open the Activity Monitor utility and quit the process called Python. A similar thing happens with a command like:
Calling an external program from iNMR
Create another Python script, called pyreplace, still inside the sandbox folder. Here is the code to paste:
#!/usr/bin/python import sys,re [sys.stdout.write(re.sub('e', '*@*', line)) for line in sys.stdin]
The script simply changes any “e” in the standard input into “*@*”. First test the script into the Terminal.
PATH=$PATH:~/sandbox cd sandbox chmod +x pyreplace pyreplace cheese ctrl-D
Now we are going to call it from iNMR, passing some text, and we'll examine the result with iNMR itself. This example is apparently useless (Lua contains all the tools to perform the same operations on literal strings and much more). I simply want to demonstrate that, if you already have a program written in Python, you can recycle it inside iNMR. If you don't want to rewrite it in Lua, you can export data from iNMR, as a file, operate on the data with your old Python program, then reimport the processed data into iNMR. There is still some work to do, but you can save a lot of time. An example of Lua code that runs the example is:
Text = "If you believe that dreams can come true be prepared for the occasional nightmare." sandbox = string.sub( shell "echo $HOME", 1, -2 ).."/sandbox/" -- path to the sandbox file1 = sandbox.."test.txt" -- will contain the text to modify io.output( file1 ) -- sets the std output to ~/sandbox/test.txt io.write( Text ) io.close() -- saves the file file2 = sandbox.."result.txt" -- will contain the modified text os.execute( "PATH=$PATH:~/sandbox; pyreplace < "..file1.." > "..file2 ) io.input( file2 ) -- sets the std input to ~/sandbox/result.txt NewText = io.read "*all" -- the last 4 lines can be substituted with the single instruction: -- NewText = shell( "PATH=$PATH:~/sandbox; pyreplace < "..file1 ) print( NewText )
Note how we have changed the PATH and invoked pyreplace into the same call to shell (or execute). This is necessary, because iNMR opens (and closes) a new shell every time you use the command shell (or execute). These shells ignore the configuration file .bash_profile of your home folder. The best alternative is to change the path at the system level.
Do not try to call our first script (pynmr) from iNMR itself. iNMR will start pynmr and can resume its operations only after pynmr has finished its job. iNMR remains unreachable and unresponsive until then. pynmr can never perform its job of putting some text into iNMR because the latter is unresponsive. You are forced to use the combination Cmd-Alt-Esc.