3

My Cocoa app requires a Python command line tool to be installed on the user's system using easy_install. Ideally, I'd want to bundle one bash file with my app which I can then run. But as far as I know this is not possible, because packages are installed in the "site-packages" directory of Python.

Is there a way to create a "package" of those files? If not, how should I run the easy_install installation? I wanted to bundle a .pkg file with my app which I can then open if necessary, but I wasn't able to let this installation package run a script only.

If you have ideas on how to fix this, I'd be glad.

Kind regards, Fabian

Eric O. Lebigot
  • 91,433
  • 48
  • 218
  • 260
Fabian Kreiser
  • 8,307
  • 1
  • 34
  • 60
  • Could you ship the command line tool with your application? Who is going to use the command line tool: your application only, or possibly the user by himself? – Eric O. Lebigot Jan 14 '12 at 10:54

2 Answers2

4

If you can ship the command line tool with your application, and if only your application will be using it (instead of the tool being used directly by the user), you can directly include and use the command line tool with your application like so:

  • Store the command line tool somewhere in your application directory.
  • Set the Python module search path so that it looks for the module you need.

Setting the Python module search path relative to a Python program can be done with

import sys
import os.path as path

sys.path.append(path.join(path.dirname(__file__),
    '<relative path between this program and the command line tool module>'))

import <command line tool module>

The relative path can be written with the .. parent directory convention: this works both on Unix (including Mac OS X) and Windows.

PS: If many programs need to access the command line tool module, you can:

  • Either put the above code in each of your programs,

  • or, if you want something easier to maintain, you can create your own module my_runner and use it as a proxy: your module my_runner would import all the original runner functions like so:

    import sys
    import os.path as path
    
    sys.path.append(path.join(path.dirname(__file__),
        '<relative path between this program and the original ino module>'))
    
    from ino.runner import *
    

    You can then use the original runner module in all your programs through your proxy, by simply doing "normal" imports of your own my_runnermodule:

    from my_runner import main
    

PPS: If you want a more robust path that works even if the working directory later changes, it is possible to convert a potentially local path to an absolute path with: path.join(path.abspath(path.dirname(__file__)),….

Eric O. Lebigot
  • 91,433
  • 48
  • 218
  • 260
  • I think that's what I need. Just for clarification: Where do I need to change the import paths? I have about 10 .py files and one command line interface with this import statement: "from ino.runner import main" What exactly do I need to change? Thank you very much! – Fabian Kreiser Jan 14 '12 at 11:25
  • There are also some dependencies, I'd need to ship with the app. Does the same thing you described work here, too? If it helps you, the python tool can be downloaded here: http://pypi.python.org/packages/source/i/ino/ino-0.3.1.tar.gz#md5=ed26bba45c71528f7efd3b0998caafaa – Fabian Kreiser Jan 14 '12 at 11:41
  • Thank you for your feedback. I added a PS that gives you a solution that's simple to maintain. – Eric O. Lebigot Jan 14 '12 at 11:51
  • Sorry, but I don't see anything. :-/ – Fabian Kreiser Jan 14 '12 at 11:59
  • @FabianKreiser: Yeah, I wrote the comment first and abusively used the past tense in it. :) – Eric O. Lebigot Jan 14 '12 at 12:02
  • I will try to do what you said. In my command line tool I need to change the import to a relative path and in all other .py files I need to adjust the imports, too. What about the external dependencies? How do I reference the .egg files? Sorry for being so annoying. ;) – Fabian Kreiser Jan 14 '12 at 12:19
  • @FabianKreiser: I'm glad that you keep asking questions, actually. :) I'm not sure about the external dependencies. My guess would be that you could put them in a directory inside your project (through easy_install or, better, pip), and then simply adjust the Python library path accordingly (by doing as in the answer for each of your programs, if you want to allow your application to be put anywhere on the disk by the user). – Eric O. Lebigot Jan 14 '12 at 12:59
  • @FabianKreiser: PS: I realize that you want all your dependencies to know about each other. Modifying the PYTHONPATH variable within your main program, before imports (`os.environ` module) might be the simplest solution. I don't have much experience with this, so you might want to experiment. :) – Eric O. Lebigot Jan 14 '12 at 13:02
2

Thanks to EOL I got it working now. The solution involved several steps listed below.

  1. Remove dependencies by packaging the .egg files and referencing them like this:

    import sys
    import os.path as path
    sys.path.append(path.join(path.dirname(__file__), '<relative path to .egg file>'))
    
    from dependency import something
    
  2. Build a single .egg file from the now independent python module using setup.py

  3. Reference the resulting .egg file in the command line tool like in 1.
  4. Bundle the single .egg file and the command line tool in the Cocoa app

I hope this will be useful to someone.

Eric O. Lebigot
  • 91,433
  • 48
  • 218
  • 260
Fabian Kreiser
  • 8,307
  • 1
  • 34
  • 60