pallets / quart

An async Python micro framework for building web applications.
https://quart.palletsprojects.com
MIT License
3k stars 163 forks source link

Hot reload cause a permission denied error in docker container #350

Open cooolinx opened 4 months ago

cooolinx commented 4 months ago

Hot reload cause a permission denied error in docker container:

Traceback (most recent call last):
  File "/app/z.py", line 9, in <module>
    app.run()
  File "/usr/local/lib/python3.11/site-packages/quart/app.py", line 864, in run
    restart()
  File "/usr/local/lib/python3.11/site-packages/quart/utils.py", line 164, in restart
    os.execv(executable, [executable] + args)
PermissionError: [Errno 13] Permission denied

How to replicate the bug:

  1. Into a docker container docker run --rm -it -v $PWD:/app python:3.11-alpine ash (mount current directory)
  2. Install pip install quart
  3. Create a app.py, code copy from https://pgjones.gitlab.io/quart/tutorials/quickstart.html
  4. Run python /app/app.py
  5. Try to change app.py and you will see the error

When I try to replicate it outside of docker container, hot-reload works fine.

Environment:

cooolinx commented 4 months ago

And then when I tried to look into https://github.com/pallets/quart/blob/2fc6d4fa6e3df017e8eef1411ec80b5a6dce25a5/src/quart/utils.py#L133

The executable was /usr/local/bin/python at the beginning and became /app/app.py at the end.

I noticed that hypercorn condition was incorrectly set to true, which cause the executable variable to change.

def restart() -> None:                                                               
    # Restart  this process (only safe for dev/debug)                                
    executable = sys.executable                                                      
    script_path = Path(sys.argv[0]).resolve()                                        
    args = sys.argv[1:]                                                              
    main_package = sys.modules["__main__"].__package__                               

    if main_package is None:                                                         
        ...              
        else:                                                            
            if script_path.is_file() and os.access(script_path, os.X_OK):  # <-- HERE!
                # hypercorn run:app --reload                             
                executable = str(script_path)       
            else:                                        
                # python run.py                           
                args = [str(script_path), *args]          
    else:                                                 
        ...

    os.execv(executable, [executable] + args)               
tomfleming commented 4 months ago

I hit this as well, wonder if it may be related to this open docker issue: https://github.com/docker/for-mac/issues/5029