Question or issue on macOS:
Can you create an Mac OS X Service with Python? How ?
What I want to do is to know hook my Python-fu to the service system provided by Mac OS X.
Anyone knows how? If yes any working code snippet? Will work only on text or also on a given mimetype – defined object?
How to solve this problem?
Solution no. 1:
-
Open Automator.app and create a new service.
-
Select “Utilities” from the left-hand actions list, then drag the “Run Shell Script” action into the workflow.
-
Choose
/usr/bin/python
as your shell. -
Type some python. For example:
:
import sys for f in sys.stdin: print "Hello World: " + f,
-
Save the service as, say, “Test”
-
Try it out in TextEdit.app. Type some text, select the text, then choose TextEdit -> Services -> Test from the menu. It should prepend “Hello World: ” to each line of the text (as per the python code
for f in sys.stdin
)
The above example works with text. Presumably, it could be modified to work with other data types provided through the OS X Services system.
Solution no. 2:
How to implement – I would say that there are three possibilities:
- You could use the Automator and create a service from a run shell script action.
- You could write an Automator action yourself using Xcode and use this in the Automator and create a new service out of it. The benefit would be that you could write an UI for it. You use bindings to bind it to the file’s owner object. The binding keys are then reflected in the script as environment variables.
- Create a stand-alone service or a service going along with an application. I have implemented one included in the application. There you can also define a separate UI just for the service.
Input types – If you want to define the service to only show up if certain things are selected, such as text or URLs, you would define this in the Info.plist of that service. I think it should be possible to edit the Info.plist that was generated by the Automator if you want to further customize it.
There is a great way of debugging a service using TextEdit and it is described in the docs mentioned below. It gives great help to debug why a service is not showing up in the menu, for example.
More information can be found in Apple’s service implementation guide
Solution no. 3:
One way to build an OS X service using Python is to bundle your Python app with py2app and edit the Info.plist file to provide the menu entries you need in the Service menu.
A sample plist file can be found at http://pythonhosted.org/pyobjc/examples/Cocoa/AppKit/SimpleService/index.html
You need to build the functionality to accept the commands you defined in the Info.plist’s NSMessage section (see link mentioned above). You can use PyObjC to do this.
This is a little bit trickier than using Automator, but has the advantage that you can bundle the whole functionality into a single App.
Solution no. 4:
I found this page when I was trying to make python service. I wanted to share my complete solution to save others a little time and maybe get some feedback from more experienced coders. I used System Preference>Keyboard:Keybindings to set an “Application Sortcut:All Apps” for this service so I can execute the selected text anywhere in the system using command-shift-return. You will notice that any Exceptions will cause the selection to be replaced with the original text. Works with UNDO if you didn’t get the result you were after. So far, I like it a lot!
Create an automator workflow as a service, service receives selected text in any application, input is entire selection, output replaces selected text
Add a run shell script action, shell: /usr/bin/python pass input: as arguments
This code goes in the box:
import sys #, random try: print eval(' '.join(sys.argv[1:])) except: try: #This is a compound statement using variable assignment code = compile(' '.join(sys.argv[1:]), '', 'exec') exec code except: print ' '.join(sys.argv[1:])
These are a few samples of how I might use this service. Again, this works in Mail, word processors and other apps.
Sample 0: Basic Math
>>> 42*24*60*60 3628800
Sample 1: Single line, compound statement
>>> import random; foo = range(17); random.shuffle(foo); print(foo) [11, 14, 12, 3, 1, 4, 15, 7, 8, 13, 2, 16, 0, 10, 9, 6, 5]
Sample 2: Multiline
>>> import random >>> foo = range(17) >>> random.shuffle(foo) >>> print(foo) [5, 16, 14, 10, 3, 11, 7, 15, 12, 0, 13, 2, 4, 1, 6, 9, 8]
Sample 3: Loops
>>> for x in range(5): ... print x ... 0 1 2 3 4