python-eel / Eel

A little Python library for making simple Electron-like HTML/JS GUI apps
MIT License
6.41k stars 585 forks source link

AttributeError: module 'eel' has no attribute 'receiveLogFromPython' #699

Open emilioh93 opened 1 year ago

emilioh93 commented 1 year ago

Describe the problem I am developing an app with React and eel. I am trying to import a JS function in Python. In development it works fine, but when running the debugger or build, I get the error: AttributeError: module 'eel' has no attribute 'receiveLogFromPython'.

Code snippet(s) I export the receiveLogFromPython function from my AppContext.js as follows:

function receiveLogFromPython(message) {
  if (shouldCancelRef.current) {
    return;
   }
   showLogAsNotification(message);
}
window.eel.expose(receiveLogFromPython);

This is the file utilities.py which is where I call the function in receiveLogFromPython:

import eel

@eel.expose
def separate_pdf(filepath, temp):
    delete_contents(temp)
    with Image(filename=filepath, resolution=300) as pdf:
        pdf_images = pdf.sequence
        for i, pdf_image in enumerate(pdf_images):
            with Image(image=pdf_image) as img:
                img.save(filename=os.path.join(temp, f"image{i:02d}.png"))
                msg = f"Saved image{i:02d}.png to temporary folder {temp}"
               # Here is the function
                eel.receiveLogFromPython(msg)
    images = [os.path.join(temp, f)
              for f in os.listdir(temp) if f.endswith(".png")]
    return images

Finally, my Main.py file:

# Use latest version of Eel from parent directory
sys.path.insert(1, '../../')

def start_eel(develop):
    """Start Eel with either production or development configuration."""

    if develop:
        directory = 'src'
        app = None
        page = {'port': 3000}
    else:
        directory = 'build'
        app = 'chrome'
        page = 'index.html'

    eel.init(directory, ['.tsx', '.ts', '.jsx', '.js', '.html'])

    print("Application running...")

    eel_kwargs = dict(
        host='localhost',
        port=8080,
        size=(1380, 900),
    )
    eel.start(page, mode=app, **eel_kwargs)

if __name__ == '__main__':
    import sys

    # Set the correct working directory
    if getattr(sys, 'frozen', False):
        # Set the working directory to the bundle resource path
        os.chdir(sys._MEIPASS)
    else:
        os.chdir(os.path.dirname(os.path.abspath(__file__)))

    # Pass any second argument to enable debugging
    start_eel(develop=len(sys.argv) == 2)

As I mentioned before, the function works correctly while I am developing. But when I do the build or activate the VSCode debugger, it returns the error I mentioned before. My theory is that when I do the build or debug, it doesn't get to read the JS function properly, and in eel.receiveLogFromPython(msg) it takes it as if it was native to eel. I have searched for the same problem in other forums and posts in this repository, but so far I did not find any satisfactory result. So I would greatly appreciate if you can give me a hand.

Desktop (please complete the following information):

ajmirsky commented 1 year ago

When eel creates your app using pyinstaller, avoid using sys.path.insert and os.chdir; once packaged, these will be problematic for running on computers other than the one used to build the app. Instead, respectively, (1) use a proper virtual environment with the right eel version and (2) use sys._MEIPASS / os.path.dirname to create the right absolute paths to your files (instead of changing the working directory and then using relative path names).

Also, Eel finds the exposed javascript functions by parsing the javascript files directly so, make sure that your .spec file has the correct paths passed to the datas keyword parameter when Analysis is instantiated as that directs pyinstaller where to put the webpacked bundle (it is much easier to get this all working by generating the .spec file once using the eel command line and then modify it and then run pyinstaller directly.

And, the webpack settings, by default, will "optimize" the eel package and function names using uglify. for example, if not directed correctly, webpack will turn it into something like this, which removes both the eel package name and the function name that the python-side of eel needs:

sl.expose((function(e){console.log("hello from python"+e)}))

( if you're using react, you'll have to "eject" your app and modify the webpack config directly or use react-app-rewired or craco to modify the webpack config so that you can disable uglify. in theory, setting the chunkFilter option to bypass the eel functions could work too, but i've never tried.)

the --onefile option for pyinstaller can cause issues. so start with --onedir so that you can look for the files and make sure they're in the place that eel expects (for mac, they would need to be in the myapp.app/Contents/MacOS/build directory. I'm not sure how pyinstaller works with windows)

webpack + eel + pyinstaller can be a tricky combination, but it is doable!