3

I'm packaging a python application with "pyinstaller --onefile myapp.py" which creates a single executable file, and the application works great. Now I would like to be able to import a module from the system if it exists, and otherwise use the bundled module from the single executable file. I'm specifically wanting to import PySide2 from the system if it's installed, and ideally with a configurable flag that indicates if the application should load from the system or from the single executable. I'm already using a configuration file that is read by the single executable, so I could put a flag in there to indicate how to load the module. Is there a way to accomplish this?

The pseudo code would be something like this:

read import_external from configuration file
if import_external is true:
    try:
        import PySide2 from system
    except:
        import PySide2 from executable

EDIT I'm getting close to the solution. I can import a single Python file dynamically from a pyinstaller onefile executable, as in the below code where I created a dummy "PySide2.py" file with a simple print statement to validate it imported.

import os
print("Import test")
if os.path.isfile('./PySide2.py'):
    print('Importing local PySide2')
    import importlib
    import importlib.util
    spec = importlib.util.spec_from_file_location('PySide2', './PySide2.py')
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)
else:
    print('Importing system PySide2')
    import PySide2
    print(PySide2.__version__)
print("Import complete")

But PySide2 is not a single file, so now I need to understand how to import from a directory. Any advice is appreciated!

EDIT 2 I found the solution here: Python use importlib to import a module from a package directory

Rusty Lemur
  • 1,697
  • 1
  • 21
  • 54
  • 1
    PyInstaller installs its own Python interpreter, which is separate from the already installed one, if any. I don't think you can (or you should) try to import packages from another Python installation. – Selcuk Jun 13 '19 at 03:32
  • Thanks for pointing out the separate interpreter. I hadn't thought of that. How about searching the local directory first to see if it is importable from there? The user would have to take care that the version is compatible. – Rusty Lemur Jun 13 '19 at 03:39
  • 1
    If you have to do that, you should have a look at [`importlib`](https://docs.python.org/3/library/importlib.html#module-importlib) – Selcuk Jun 13 '19 at 03:44
  • I'm not sure why you are trying to do this because the purpose of pyinstaller is to convert a py file into exe. But if you want to import external modules on runtime (that points out Python is installed on the target machine) why you want to use pyinstaller in the first place? Just use your script. – Masoud Rahimi Jun 13 '19 at 07:09
  • The reason I am after this is for compliance with the LGPL license of PySide2. As I understand, a work that uses a LGPL'd library must either distribute the source code or make it possible for the end user to be able to replace the LGPL'd library with a different library (it's their responsibility to ensure parity with the API/function calls/etc.). If I create a closed package with pyinstaller, then the end user doesn't have a way to swap out PySide2, which seems like a violation of the license. – Rusty Lemur Jun 13 '19 at 17:26

1 Answers1

2

After some more research and posting, I got the following solution (from this post).

import os, sys
if os.path.isdir('./PySide2/'):
    print('Importing local PySide2')
    import importlib.util

    MODULE_PATH = "./PySide2/__init__.py"
    MODULE_NAME = "PySide2"

    spec = importlib.util.spec_from_file_location(MODULE_NAME, MODULE_PATH)
    PySide2 = importlib.util.module_from_spec(spec)
    sys.modules[spec.name] = PySide2
    spec.loader.exec_module(PySide2)

    print(PySide2.__version__)
else:
    print('Importing system PySide2')
    import PySide2
    print(PySide2.__version__)
Rusty Lemur
  • 1,697
  • 1
  • 21
  • 54
  • A followup question to your solution - how do you use the submodules (QtWidgets etc) in your myapp.py? – tobkum Jan 02 '20 at 04:02