17

I'm making a pygame program that is designed to be modular. I am building an exe with pygame2exe of the file main.py, which basically just imports the real main game and runs it. What I'm hoping for is a sort of launcher that will execute Python scripts from an EXE, rather than a single program containing all immutable files.

What is the best way to go about this? I've tried using imp to dynamically import all modules at runtime instead of implicitly importing them, but that seems to break object inheritance.

Matthew Fournier
  • 1,077
  • 2
  • 17
  • 32
  • how do you make exe? if you using py2exe, there is a syntax for that. – YOU Mar 10 '16 at 02:35
  • Yes, I'm using py2exe, what's the syntax for that? – Matthew Fournier Mar 10 '16 at 02:42
  • you know IDLE does this, the executable for idle just imports stuf from `idlelib` in the standard library and runs that, I'd imagine you would similarly need just a exe that forwards to the actual program – Tadhg McDonald-Jensen Mar 10 '16 at 03:36
  • bundle_files=3 - http://www.py2exe.org/index.cgi/ListOfOptions – YOU Mar 10 '16 at 04:52
  • bundle_files=3 is giving me this error: `AttributeError: Module instance has no attribute '__pydfile__'` – Matthew Fournier Mar 10 '16 at 05:51
  • I hope there is a good answer to this question :P I'll probably end up wanting to do this too! – David Jay Brady Mar 10 '16 at 19:41
  • you could try `skip_archive=True` which would put the compiled bytecode in the program directory, then if there is a python file with the same name alongside it, when it's imported if it's newer it should get overwritten. however you may have problems with UAC on newer versions of windows – James Kent Mar 23 '16 at 16:08

2 Answers2

9

After some experiments I've found a solution.

  1. Create a separate folder source in the main folder of the application. Here will be placed source files. Also place file __init__.py to the folder. Lets name a main file like main_module.py.

  2. Add all of its contents as a data files to the py2exe configuration setup.py. Now after compiling the program, these files will be placed in the dist folder.

    data_files += [('source', glob('source/*.py'),)]
    setup(
          data_files=data_files,
          ....  # other options
         windows=[
              {
                "script": "launcher.py",
                "icon_resources": [(0, "resources/favicon.ico")]
              }
         )
    
  3. Make launcher.py - it's task is to import all system and required libraries like pygame, pyqt and so on. Then run you program:

    import sys, time, os, hashlib, atexit  # std modules
    import PyQt5, ...   # foreign libraries
    sys.path.insert(0, 'source')
    exec('import main_module')
    
  4. Now main_module.py will be imported, if it imports your modules, they will be imported too in their places in hierarchy. For example head of the main_module.py can be like this:

     import user_tweaks
     from user_data import parser
    

    These files user_tweaks.py and user_data.py should be located in source folder at appropriate paths relative to main_module.py.

You may change contents of source folder without recompilation program itself. Any time program runs it uses fresh contents of source.

As a result you have an application folder with:

  • A separate launcher - simple .exe file
  • All required modules
  • Your application with all its modules.
Eugene Lisitsky
  • 12,113
  • 5
  • 38
  • 59
  • Could I potentially load _every_ file in this way? – Matthew Fournier Mar 21 '16 at 01:17
  • Yes, I've to updated the recipe to load all the files from the folder. – Eugene Lisitsky Mar 21 '16 at 10:57
  • 1
    No, that still doesn't seem to be what I'm looking for. I need a program that basically runs "python main.py" without needing Python and all of the libraries installed. I can't import from that, and I'd have to add the files to the setup.py. What if the user adds files, but modifies the code to import it? This isn't just some list of code that has to be executed once called for, it's modules that need to be imported, are part of an object hierarchy, and are used multiple times, in files that might not even exist when the exe is compiled. – Matthew Fournier Mar 22 '16 at 05:23
  • What if spread application in source code? Users can easily edit it, apply patches and so on? – Eugene Lisitsky Mar 22 '16 at 09:14
  • I'm almost there! I'm just having a bit of trouble importing from sub-packages. In my main.py, it imports source.menu.main_menu, but when I run it from the launcher, it gives me the error `ImportError: No module named source.menu.main_menu` – Matthew Fournier Mar 22 '16 at 16:58
  • In my solutions source is added as a folder-container, not a package itself. Start all paths from the `main_module`. If `another` module is in the same folder as `main_module`, import it with `import another`. If you need subfolders - add them to source folder, provide them with default __init__.py to turn into packages. Then you can `from some_package import some_module`. – Eugene Lisitsky Mar 23 '16 at 08:23
  • How are you? Was the comment useful? Pls, do not hesitate to ask details about it. – Eugene Lisitsky Mar 24 '16 at 13:08
  • Do I need to add all of the other packages as data_files? It still is giving me the same error with the direct imports like that – Matthew Fournier Mar 24 '16 at 19:47
  • You need to separate imports: all standard and foreign library code is imported in `launcer.py`. Your code and modules are imported in your files and located in `source` folder. – Eugene Lisitsky Mar 25 '16 at 07:47
1

Last summer I have been struggling with the same problem - build single .exe from python script. However there was no PyGame, but there was a PyQt5, which added some problems alike. None of the pure standard tools helped me.

Finally I'd solved the problem with a chaine: make + Py2exe + 7Zip + Resource Hacker. This set gave me single .exe with all the resources onboard, which don't require installaton - so you can put it everywhere on windows box and run.

Here's detailed article: exe built with cx_Freeze, PyQt5, Python3 can't import ExtensionLoader_PyQt5_QtWidgets.py and run

Please, feel free to ask any questions.

Community
  • 1
  • 1
Eugene Lisitsky
  • 12,113
  • 5
  • 38
  • 59
  • Will this EXE run any changes to the script files without needing to recompile? I'm not trying to get a single EXE that encapsulates all of the python files -- quite the opposite in fact. The program is meant to be modular, where users can tweak values inside scripts and run the program with new behavior even without needing to recompile or have any libraries installed. – Matthew Fournier Mar 19 '16 at 19:47
  • No, program is being built into single file. But if you with to tweak values, may be move them out to some sort of config-file or command line options? – Eugene Lisitsky Mar 20 '16 at 07:09
  • The program needs to be a lot more configurable than just a few options. I need code to be executed dynamically. It's working for most things, but if I try to dynamically import everything, it breaks multi-file inheritance. If I could just get a launcher that runs main.py and doesn't compile any of the .py files, running them as .py files instead of .pyo, it would work perfectly. – Matthew Fournier Mar 20 '16 at 16:27
  • I understand. Pls try my another solution below. It produces application folder with 1 source file. This file is read and executed at program's start. – Eugene Lisitsky Mar 20 '16 at 17:02