Author Topic: Packaging a python script as an executable  (Read 21957 times)

KM

  • Newcomer
  • Posts: 13
  • Karma: +0/-0
    • View Profile
Packaging a python script as an executable
« on: January 17, 2017, 04:11:41 AM »
Hi; I'm trying to package my python script as an executable, but am finding it very difficult.  I'm following the directions at py2exe.org, and am using bearlibterminal and libtcod, and am getting dependencies that I don't understand how to fix.  When I try to click the .exe file I created, it just crashes with no errors, but it runs fine when I launch it out of powershell.

Here's what powershell tells me when I'm trying to run the .exe from there:

Code: [Select]
PS C:\Python27\Grit\dist> .\gritandsteel.exe
Traceback (most recent call last):
  File "gritandsteel.py", line 6, in <module>
  File "zipextimporter.pyc", line 82, in load_module
  File "PyBearLibTerminal.pyc", line 58, in <module>
  File "PyBearLibTerminal.pyc", line 56, in _load_library
RuntimeError: BearLibTerminal library cannot be loaded (looked for BearLibTerminal.dll in C:\Python27\Grit\dist\gritandsteel.exe)
PS C:\Python27\Grit\dist>

I don't know why it's looking for the .dll IN the .exe, or how I would fix it.  BearLibTerminal.dll is in the same file directory as my .exe.

I created the .exe using the tutorial code here:
Code: [Select]
from distutils.core import setup
import py2exe
import os
import sys
 
sys.argv.append('py2exe')
 
# The filename of the script you use to start your program.
target_file = 'gritandsteel.py'
 
# The root directory containing your assets, libraries, etc.
assets_dir = '.\\'
 
# Filetypes not to be included in the above.
excluded_file_types = ['py','pyc','project','pydevproject']
 
def get_data_files(base_dir, target_dir, list=[]):
    """
    " * get_data_files
    " *    base_dir:    The full path to the current working directory.
    " *    target_dir:  The directory of assets to include.
    " *    list:        Current list of assets. Used for recursion.
    " *
    " *    returns:     A list of relative and full path pairs. This is
    " *                 specified by distutils.
    """
    for file in os.listdir(base_dir + target_dir):
 
        full_path = base_dir + target_dir + file
        if os.path.isdir(full_path):
            get_data_files(base_dir, target_dir + file + '\\', list)
        elif os.path.isfile(full_path):
            if (len(file.split('.')) == 2 and file.split('.')[1] not in excluded_file_types):
                list.append((target_dir, [full_path]))
 
    return list
 
# The directory of assets to include.
my_files = get_data_files(sys.path[0] + '\\', assets_dir)
 
# Build a dictionary of the options we want.
opts = { 'py2exe': {
                    'ascii':'True',
                    'excludes':['_ssl','_hashlib'],
                    'includes' : ['anydbm', 'dbhash'],
                    'bundle_files':'1',
                    'compressed':'True'}}
 
# Run the setup utility.
setup(console=[target_file],
      data_files=my_files,
      zipfile=None,
      options=opts)

Any help is appreciated as to why this isn't launching.  Thank you.

AgingMinotaur

  • Rogueliker
  • ***
  • Posts: 805
  • Karma: +2/-0
  • Original Discriminating Buffalo Man
    • View Profile
    • Land of Strangers
Re: Packaging a python script as an executable
« Reply #1 on: January 17, 2017, 08:38:24 AM »
Hello. I used to pack Windows executables with Py2exe, but I switched to Pyinstaller a while back. Don't know if this may be a solution for you, but I thought I'd mention it. Pyinstaller is pretty nice overall, but the main draw for me is that I can use it in Linux to get Window exes. I wrote a short blot post about that. The short, not Linux-specific version is to install Pywin32, Setuptools and MS Visual C++. Then you get an exe by typing "pyinstaller --onefile main.py". Hope this helps.

As always,
Minotauros
This matir, as laborintus, Dedalus hous, hath many halkes and hurnes ... wyndynges and wrynkelynges.

javelinrl

  • Rogueliker
  • ***
  • Posts: 86
  • Karma: +0/-0
  • Creator of Javelin
    • View Profile
    • Javelin - party-based roguelike (open-source RPG / strategy game)
Re: Packaging a python script as an executable
« Reply #2 on: January 17, 2017, 06:55:07 PM »
If all you want is for the user to be able to click a file icon and run your game automatically, the usual way to do that with Python is to provide a .bat file for Windows and a .sh file for Linux, both of them being mostly just the "python3 myscript.py" command (and also a #!/usr/bin/python3 opening line for the .sh). If you go that route make sure your .sh file is packaged as +x (executable).

I think this is how most RLs written in Python achieve that and it's certainly a lot less complex than creating an actual binary file. If you want something more professional you can try an installer package as been said in the last post and maybe provide a shortcut that launches Python directly from a command (instead of creating an EXE).

Finally, I think most modern operating systems can handle the user double clicking a Python file to launch it directly. Haven't used Windows in a long while but Linux certainly does that if the file is executable (+x) and has the appropriate shebang line (#!).
« Last Edit: January 17, 2017, 06:57:13 PM by javelinrl »
Javelin, party-based roguelike (free RPG / strategy game for Win/Mac/Lin)
https://javelinrl.wordpress.com/

AgingMinotaur

  • Rogueliker
  • ***
  • Posts: 805
  • Karma: +2/-0
  • Original Discriminating Buffalo Man
    • View Profile
    • Land of Strangers
Re: Packaging a python script as an executable
« Reply #3 on: January 17, 2017, 08:17:23 PM »
A binary saves the player the hassle of installing dependencies, though. And I don't think Python is a standard component in Windows like it is in Linux (someone correct me if I'm wrong). So for many casual users, it's the difference between "click on the file" and "download and install Python and Pygame, then click on the file". For Linux users, of course, it's mostly hunky dory just to ship a Python script :) but I maintain a deb-file of my own project just for kicks.

As always,
Minotauros
This matir, as laborintus, Dedalus hous, hath many halkes and hurnes ... wyndynges and wrynkelynges.

javelinrl

  • Rogueliker
  • ***
  • Posts: 86
  • Karma: +0/-0
  • Creator of Javelin
    • View Profile
    • Javelin - party-based roguelike (open-source RPG / strategy game)
Re: Packaging a python script as an executable
« Reply #4 on: January 17, 2017, 08:29:05 PM »
You're absolutely right. My game requires Java but I don't provide an installer, I just ask people to download it from the website first and install it if they don't have it already. People still seem to download and play the game so I guess it's not something that bothers people too much. So I was saying this in regards to humble games like most RLs. If you want a game on Steam or to feel more professional (and potentially sold) you'd probably want an installer of some kind to do that for you.

As for dependencies, I've been successful in the past just distributing their source files with my own package. I've seen some big companies do the same. I'm not sure this would work with all libraries though, or if some of them need to be installed to work properly. Anyway, you can always ask your players to run a single pip command line as well to install any dependencies - the system is there and works fine so why not use it. Again, if you're trying to sell your game you shouldn't do this but for hobby projects like ours I feel it's not much to ask from the player, since we don't ask for anything else.
Javelin, party-based roguelike (free RPG / strategy game for Win/Mac/Lin)
https://javelinrl.wordpress.com/

AgingMinotaur

  • Rogueliker
  • ***
  • Posts: 805
  • Karma: +2/-0
  • Original Discriminating Buffalo Man
    • View Profile
    • Land of Strangers
Re: Packaging a python script as an executable
« Reply #5 on: January 17, 2017, 08:49:37 PM »
True that. It is a privilege of tinkering in all humility, as you say, to make whatever you like however you want.

As always,
Minotauros
This matir, as laborintus, Dedalus hous, hath many halkes and hurnes ... wyndynges and wrynkelynges.

Krice

  • (Banned)
  • Rogueliker
  • ***
  • Posts: 2316
  • Karma: +0/-2
    • View Profile
    • Email
Re: Packaging a python script as an executable
« Reply #6 on: January 17, 2017, 09:28:37 PM »
Java is super annoying dependency, because it's such a bloatware and security risk. Python is also kind of dumb, because it has no backwards compatibility whatsoever so many programs simply include the entire python with it, using the proper version for that software. Why can't we fix this? It's 2017 and still we don't have something that would turn EVERYTHING into a native .exe (binary) without dependencies. Go computer science.

KM

  • Newcomer
  • Posts: 13
  • Karma: +0/-0
    • View Profile
Re: Packaging a python script as an executable
« Reply #7 on: January 18, 2017, 02:21:59 AM »
Java is super annoying dependency, because it's such a bloatware and security risk. Python is also kind of dumb, because it has no backwards compatibility whatsoever so many programs simply include the entire python with it, using the proper version for that software. Why can't we fix this? It's 2017 and still we don't have something that would turn EVERYTHING into a native .exe (binary) without dependencies. Go computer science.

It's actually baffling to me how difficult this process is.  I figured I'd just hit compile in Python and BOOM, have an .exe.

Alas, it's a challenge.

javelinrl

  • Rogueliker
  • ***
  • Posts: 86
  • Karma: +0/-0
  • Creator of Javelin
    • View Profile
    • Javelin - party-based roguelike (open-source RPG / strategy game)
Re: Packaging a python script as an executable
« Reply #8 on: January 18, 2017, 05:20:30 AM »
If you thought you'd just compile Python into an EXE then you've fundamentally misunderstood the whole premise of it. I suggest you reconsider your way of thinking, as I've said before because you could be trying to hit a screw with a hammer (trying to solve a problem with a method that really doesn't apply to it).

As a last case scenario you can always create a compiled EXE in another language like C++ that does nothing but execute a system command that calls Python for you and runs your script. Unnecessary to say that'd be a very unpythonic way of doing things. It will work but you'd be just as better reconsidering your language of choice if it comes down to it, in my opinion. Or, as I've said before, create a system link (like a start menu entry) that does the same thing for you, or a BAT file.
Javelin, party-based roguelike (free RPG / strategy game for Win/Mac/Lin)
https://javelinrl.wordpress.com/

tuturto

  • Rogueliker
  • ***
  • Posts: 259
  • Karma: +0/-0
    • View Profile
    • pyherc
Re: Packaging a python script as an executable
« Reply #9 on: January 18, 2017, 10:36:35 AM »
It's been a while since I used py2exe, but my hunch is that you should explicitly include BearLibTerminal.dll in your package, as shown in http://stackoverflow.com/questions/220777/including-pyds-dlls-in-py2exe-builds
Everyone you will ever meet knows something you don't.
 - Bill Nye

Cfyz

  • Rogueliker
  • ***
  • Posts: 194
  • Karma: +0/-0
    • View Profile
    • Email
Re: Packaging a python script as an executable
« Reply #10 on: January 18, 2017, 02:37:13 PM »
As for BearLibTerminal the problem was in the wrapper. It looked for a binary in the module directory (if it exists) or the application directory (otherwise). Turns out, a packed module still has its __file__, though a wrong one like "C:\dist\app.exe\terminal.py" which looks like there is an app.exe directory.

I've made a small update (0.15.1), changing the behaviour to look in both directories. I've tested it with PyInstaller and py2exe and it seems to work as long as you copy a library .dll near the .exe.
I do not know how to correctly pack the .dll into the executable yet.

Avagart

  • 7DRL Reviewer
  • Rogueliker
  • *
  • Posts: 567
  • Karma: +0/-0
    • View Profile
Re: Packaging a python script as an executable
« Reply #11 on: January 18, 2017, 04:20:57 PM »
My game requires Java but I don't provide an installer, I just ask people to download it from the website first and install it if they don't have it already. People still seem to download and play the game so I guess it's not something that bothers people too much.

AgingMinotaurs is right - it's all about dependencies. People (I mean windows users now, due to py2exe case) still are downloading and playing your game, ok. But java is really common thing, most users have it installed already, usually there is no need to download any additional package. It's why java dependecy doesn't bother and doesn't affect your downloads. But python is not very popular on windows. Unlike linux, microsoft doesn't ship python together with its systems; also python is very rare dependency. So, most users wouldn't download and install python just for playing one game. (Omitting whole topic about py2 vs py3 mess and problems with using these two environments together). Packing python application into executable
is only sensible way to distribute python games for windows.
« Last Edit: January 18, 2017, 04:22:43 PM by Avagart »

KM

  • Newcomer
  • Posts: 13
  • Karma: +0/-0
    • View Profile
Re: Packaging a python script as an executable
« Reply #12 on: January 19, 2017, 02:25:38 AM »
If you thought you'd just compile Python into an EXE then you've fundamentally misunderstood the whole premise of it.

Elaborate?

Avagart

  • 7DRL Reviewer
  • Rogueliker
  • *
  • Posts: 567
  • Karma: +0/-0
    • View Profile
Re: Packaging a python script as an executable
« Reply #13 on: January 19, 2017, 09:31:02 AM »
I suppose that javelinrl had in mind that whole python compilation is sort of... fake. Output of py2exe and pyinstaller (and cx_freeze, if I'm not mistaken, but maybe it's linux exclusive?) is exe package that includes your scripts and a python interpreter. Your code is still interpreted by python environment. It implies few things you'd need to know: a) python exes are bloated b) your code will not be faster; if it's slow now, it will be slow after compilation 3) it's very easy to decompile python exes (so, no the best choice if you really need your sources closed)

But still, in my opinion, packing py files into exe is best choice for deploying your software to windows. But not due to speed up or something. It's a good choice because there will be no need to download python.
« Last Edit: January 19, 2017, 09:33:09 AM by Avagart »

javelinrl

  • Rogueliker
  • ***
  • Posts: 86
  • Karma: +0/-0
  • Creator of Javelin
    • View Profile
    • Javelin - party-based roguelike (open-source RPG / strategy game)
Re: Packaging a python script as an executable
« Reply #14 on: January 19, 2017, 05:37:19 PM »
Avagart got my meaning but just to make it clearer: Python is not a compiled language in the sense it compiles to an EXE so the fact you're trying to do that isn't even something the language creators had in mind. It is, in fact a compiled language but not in the same sense that C or C++ are compiled, you're not supposed to distribute the PYC binaries but the source PY files themselves. Py2exe is not an official part of the Python language but a hack and as such it's obviously not the recommended way of doing things (the pythonic way).

I have raised a few issues that you haven't addressed but my point is: if you have no reason against doing that, you should just distribute a BAT file that executes the Python command like all other Python games do. The player will need to install Python manually but that is the pythonic way of doing things, as far as I'm concerned - or you could ship an installer that also runs the Python installation and uses pip to grab any required dependencies.

I've got nothing against you creating an EXE for your game if it works. I just want you to understand that this is a hack and unofficial way of doing things (which is why you're running into trouble, probably) and not the way Python is designed to work.
Javelin, party-based roguelike (free RPG / strategy game for Win/Mac/Lin)
https://javelinrl.wordpress.com/