3

Short version:

How do I use PyInstaller from within a Python script, instead of from the terminal?

What would I need to write inside a Python script to get the equivalent of writing this in the terminal:

>python -m PyInstaller --noconsole --name WorkLogger ../WorkLogger/main.py

Long version:

I'm using a library that requires using PyInstaller to distribute an executable. But I have to run PyInstaller once, then change the spec files, then run the spec file through PyInstaller.

So in the terminal I would've done this:

>python -m PyInstaller --noconsole --name WorkLogger ../WorkLogger/main.py

After this is done running, I manually change the spec file. Then I run:

>python -m PyInstaller WorkLogger.spec

I've written a script that does the manual labor for me, by running

>change_spec.py

But I ultimately want to do all of this in one Python script. I want to be able to type something like this:

>distribute_python_project.py ./Worklogger

This means my Python script would need to look something like this:

#Psuedocode:
#python -m PyInstaller --noconsole --name WorkLogger ../WorkLogger/main.py
#Code from change_spec.py
#python -m PyInstaller WorkLogger.spec

But I can't figure out how I use PyInstaller from a python script, instead of from the terminal. Is this possible? (The library I use is Kivy, for those interested).

eyllanesc
  • 190,383
  • 15
  • 87
  • 142
Einar
  • 896
  • 2
  • 9
  • 27
  • Could you use a subprocess to do the job? – Employee Oct 03 '18 at 22:56
  • You could call terminal commands in python script like [this](https://stackoverflow.com/questions/89228/calling-an-external-command-in-python) – Canh Oct 03 '18 at 23:00
  • I'm able to run the commands with subprocess. But I see now that for me to run the command, I have to be in a specific folder (the terminal has to be cd'd into the folder). Are there any ways you know of to achieve that through subprocess? – Einar Oct 03 '18 at 23:10
  • I figured it out. Thanks for the tip! – Einar Oct 03 '18 at 23:16

3 Answers3

5

Thanks to Employee and Canh! Working proof of concept:

Terminal:

>python -m PyInstaller --noconsole --name WorkLogger ../WorkLogger/main.py

Python script:

subprocess.call(r"python -m PyInstaller --noconsole --name WorkLogger F:\KivyApps\WorkLogger\main.py")

If needed, you can start the subprocess from a specific working directory:

subprocess.call(r"python -m PyInstaller --noconsole --name WorkLogger F:\KivyApps\WorkLogger\main.py", cwd=r"F:\KivyApps\WorkLogger_Dist")
Einar
  • 896
  • 2
  • 9
  • 27
4

You can even access directly to PyInstaller's module using spec file if you want. In this example this with different locations of spec-file, dist-dir and build-dir.

import PyInstaller

# my spec file in "dev\config" dir
workdir = os.getcwd()
fn_msi_spec = os.path.join(workdir, 'main_msi.spec')

# define the "dev\dist" and "dev\build" dirs
os.chdir("..")
devdir = os.getcwd()
distdir = os.path.join(devdir, 'dist')
builddir = os.path.join(devdir, 'build')

# call pyinstaller directly
PyInstaller.__main__.run(['--distpath', distdir, '--workpath', builddir, fn_msi_spec])
Berniiiii
  • 81
  • 3
  • Note: this works but it affects any loggers you have set up. So for example, logging before PyInstaller.__main__ works fine. Then after the call, any line you add logger.info(xx) will be printed twice. Once by your logger, once by PyInstaller's logger. No idea why... – JohnA May 20 '21 at 15:14
1

Berniiiii's answer was correct but not straight to the point and I personally found it a bit confusing.

this is an answer from the official docs: running-pyinstaller-from-python-code

import PyInstaller.__main__

PyInstaller.__main__.run([
    'my_script.py',
    '--onefile',
    '--windowed'
])

Is equivalent to:

pyinstaller my_script.py --onefile --windowed
coder
  • 309
  • 3
  • 7
  • using this approach, how would command look like if I want to add extra search paths, binaries, etc etc? I know this is example from docs and docs dont expalin any complex commands so I'm not sure what to do. – Dariusz Jan 16 '21 at 11:48
  • Basically any string you would have on the command line, you make an entry in the array: PyInstaller.__main__.run([ 'some_script.py', '--name', 'my_exe_name', '--specpath', 'some_dir', '--paths', 'some_other_dir', '--onefile', '--windowed' ]) which means: write the full command line as you normally would, then break it into strings and then add a entry in the array, one for every string – JohnA May 20 '21 at 15:17