50

I have a simple python script, which imports various other modules I've written (and so on). Due to my environment, my PYTHONPATH is quite long. I'm also using Python 2.4.

What I need to do is somehow package up my script and all the dependencies that aren't part of the standard python, so that I can email a single file to another system where I want to execute it. I know the target version of python is the same, but it's on linux where I'm on Windows. Otherwise I'd just use py2exe.

Ideally I'd like to send a .py file that somehow embeds all the required modules, but I'd settle for automatically building a zip I can just unzip, with the required modules all in a single directory.

I've had a look at various packaging solutions, but I can't seem to find a suitable way of doing this. Have I missed something?

[edit] I appear to be quite unclear in what I'm after. I'm basically looking for something like py2exe that will produce a single file (or 2 files) from a given python script, automatically including all the imported modules.

For example, if I have the following two files:

[\foo\module.py]
def example():
    print "Hello"

[\bar\program.py]
import module
module.example()

And I run:

cd \bar
set PYTHONPATH=\foo
program.py

Then it will work. What I want is to be able to say:

magic program.py

and end up with a single file, or possibly a file and a zip, that I can then copy to linux and run. I don't want to be installing my modules on the target linux system.

Gringo Suave
  • 29,931
  • 6
  • 88
  • 75
xorsyst
  • 7,897
  • 5
  • 39
  • 58
  • I think that you are after a [package](http://docs.python.org/tutorial/modules.html#packages) (this is what Drahkar is describing in his answer). See [this](http://stackoverflow.com/questions/4155914/how-to-create-a-python-2-x-package-simple-case) question and the excellent accepted answer. – Chris Jan 25 '12 at 13:01
  • Possible duplicate of [Packing Python files into a single .py script](http://stackoverflow.com/questions/4368040/packing-python-files-into-a-single-py-script) – Jace Browning Oct 11 '16 at 22:34

8 Answers8

62

I found this useful:

http://blog.ablepear.com/2012/10/bundling-python-files-into-stand-alone.html

In short, you can .zip your modules and include a __main__.py file inside, which will enable you to run it like so:

python3 app.zip

Since my app is small I made a link from my main script to __main__.py.

Addendum:

You can also make the zip self-executable on UNIX-like systems by adding a single line at the top of the file. This may be important for scripts using Python3.

echo '#!/usr/bin/env python3' | cat - app.zip > app
chmod a+x app

Which can now be executed without specifying python

./app
Gringo Suave
  • 29,931
  • 6
  • 88
  • 75
17

Use stickytape module

stickytape scripts/blah --add-python-path . > /tmp/blah-standalone

This will result with a functioning script, but not necessarily human-readable.

Marek
  • 1,413
  • 2
  • 20
  • 36
  • 2
    I expected a readable concatination of my Python scripts, but for me it generated unreadable code full of special stickytape calls. – stefanbschneider Apr 28 '20 at 18:54
  • 2
    @CGFoX thanks for catching that, I updated the response text to reflect that to save time for people who look for human-readable output. – Marek Apr 30 '20 at 03:07
12

You can try converting the script into an executable file. First, use:

pip install pyinstaller

After installation type ( Be sure you are in your file of interest directory):

pyinstaller --onefile --windowed filename.py

This will create an executable version of your script containing all the necessary modules. You can then transfer (copy and paste) this executable to the PC or machine you want to run your script.

I hope this helps.

Community
  • 1
  • 1
Daachi
  • 173
  • 2
  • 10
6

You should create an egg file. This is an archive of python files.

See this question for guidance: How to create Python egg file

Update: Consider wheels in 2019

Marcin
  • 48,559
  • 18
  • 128
  • 201
  • Don't I need a package for that? – xorsyst Jan 26 '12 at 12:22
  • @xorsyst: (1) Your development setup should be completely separate from your deployment setup, so you are in fact free to organise your code how you like (2) Once you have your egg it is one file. If you can put two files somewhere, you can put one file there (3) You seem to be aggressively discounting workable solutions by throwing up new requirements. This is impolite to say the least. – Marcin Jan 26 '12 at 15:01
  • 1: My development setup is separate from my deployment setup. It's my development setup that is restricted. 2: I have no problems with having an egg. 3: I'm not throwing up new requirements, just clarifying the original badly-worded ones – xorsyst Jan 26 '12 at 15:12
  • @xorsyst: You can't create directories in your development environment? Complain that that is preventing you from working effectively. – Marcin Jan 26 '12 at 15:15
  • I can create directories to store temporary copies of files, what I cannot do is a fullscale reorganization of our codebase just to make this particular task easier. – xorsyst Jan 26 '12 at 15:29
  • @xorsyst: I'm not suggesting a fullscale reorganisation of your codebase. However, it sounds like there might be an issue with your code organisation if you need to deploy related files stored in wholly unrelated places. – Marcin Jan 26 '12 at 16:22
2

The only way to send a single .py is if the code from all of the various modules were moved into the single script and they your'd have to redo everything to reference the new locations.

A better way of doing it would be to move the modules in question into subdirectories under the same directory as your command. You can then make sure that the subdirectory containing the module has a __init__.py that imports the primary module file. At that point you can then reference things through it.

For example:

App Directory: /test

Module Directory: /test/hello

/test/hello/__init__.py contents:

import sayhello

/test/hello/sayhello.py contents:

def print_hello():
    print 'hello!'

/test/test.py contents:

#!/usr/bin/python2.7

import hello

hello.sayhello.print_hello()

If you run /test/test.py you will see that it runs the print_hello function from the module directory under the existing directory, no changes to your PYTHONPATH required.

Drahkar
  • 1,694
  • 13
  • 17
  • Unfortunately, moving source files around is not possible. – xorsyst Jan 26 '12 at 12:24
  • I'm a little confused. Now you are contradicting your previous statement in the question at the top. You stated that you wanted to move everything into a single directory/file. If you are unable to move any of the modules in question then you can't even do what it is you are asking due to your own internal policy, all technical limitations aside. – Drahkar Jan 26 '12 at 13:02
  • 1
    Sorry for the confusion. I can't permanently move files around on my system. What I want to do is collect all the necessary source files into a single file that I can put on the target system to run the python. I don't want to install the libraries there. Basically, I'm looking for py2exe, but that produces a source-based archive that is cross-platform. – xorsyst Jan 26 '12 at 13:59
1

I've come up with a solution involving modulefinder, the compiler, and the zip function that works well. Unfortunately I can't paste a working program here as it's intermingled with other irrelevant code, but here are some snippets:

zipfile = ZipFile(os.path.join(dest_dir, zip_name), 'w', ZIP_DEFLATED)
sys.path.insert(0, '.')
finder = ModuleFinder()
finder.run_script(source_name)

for name, mod in finder.modules.iteritems():
    filename = mod.__file__
    if filename is None:
        continue
    if "python" in filename.lower():
        continue

    subprocess.call('"%s" -OO -m py_compile "%s"' % (python_exe, filename))

    zipfile.write(filename, dest_path)
xorsyst
  • 7,897
  • 5
  • 39
  • 58
1

If you want to package your script with all its dependencies into a single file (it won't be a .py file) you should look into virtualenv. This is a tool that lets you build a sandbox environment to install Python packages into, and manages all the PATH, PYTHONPATH, and LD_LIBRARY_PATH issues to make sure that the sandbox is completely self-contained.

If you start with a virgin Python with no additional libraries installed, then easy_install your dependencies into the virtual environment, you will end up with a built project in the virtualenv that requires only Python to run.

The sandbox is a directory tree, not a single file, but for distribution you can tar/zip it. I have never tried distributing the env so there may be path dependencies, I'm not sure.

You may need to, instead, distribute a build script that builds out a virtual environment on the target machine. zc.buildout is a tool that helps automate that process, sort of like a "make install" that is tightly integrated with the Python package system and PyPI.

Bill Gribble
  • 1,797
  • 12
  • 15
  • You should use `pip` with virtualenv instead of `easy_install`. – Marcin Jan 26 '12 at 15:02
  • 1
    Interesting module - but I don't think I can use it to create an environment on Windows and then port it to Linux – xorsyst Jan 26 '12 at 15:18
  • No, definitely not. You might be able to make a buildout script that automatically creates and populates a virtual environment. – Bill Gribble Jan 26 '12 at 15:40
0

Have you taken into considerations Automatic script creation of distribute the official packaging solution.

What you do is create a setup.py for you program and provide entry points that will be turned into executables that you will be able run. This way you don't have to change your source layout while still having the possibility to easily distribute and run you program.

You will find an example on a real app of this system in gunicorn's setup.py

Community
  • 1
  • 1
amirouche
  • 7,682
  • 6
  • 40
  • 94