Flask 应用程序无法从 PyInstaller 可执行文件运行

问题描述 投票:0回答:1

我使用单个 API 路由构建了一个非常简单的 Flask 服务器。如果我只使用

python order_input_api.py
,它会运行得很好。但是,我正在尝试使用 PyInstaller 构建可执行文件。当我运行可执行文件时,如果我向 API 端点发送完全相同的请求,它将不起作用。

这是

order_input_api.py
文件的简化版本:


    import sys
    from flask import Flask, request, jsonify
    from flask_cors import CORS
    import subprocess
    import socket
    import json
    import os
    
    app = Flask(__name__)
    CORS(app)
    
    
    def find_free_port():
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.bind(('localhost', 0))
            return s.getsockname()[1]
    
    
    def parse_output_data(output):
        # Returns json data
    
    
    @app.route('/process_orders', methods=['POST'])
    def process_orders():
        data = request.json
        packages_input = '\n'.join(data['packages']) + '\n' + 'done'
        python_executable = sys.executable
        process = subprocess.Popen([python_executable, 'order_counter.py'],
                                   stdin=subprocess.PIPE,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE,
                                   text=True)
        stdout, stderr = process.communicate(input=packages_input)
    
        if process.returncode == 0:
            return parse_output_data(stdout)
        else:
            return jsonify({'success': False, 'error': stderr}), 500
    
    
    os.chdir(app.root_path)
    
    if __name__ == '__main__':
        port = find_free_port()
        with open("port_info.json", "w") as f:
            json.dump({"port": port}, f)
        print(port)
        from waitress import serve
        serve(app, host='127.0.0.1', port=port)
    

按照现在的设置方式,运行可执行文件后,如果我向“/process_orders”端点发送 POST 请求,我永远不会收到响应。但是,我尝试了几种不同的方法,但都收到了错误:

  • 在实现waitress之前,我使用的是flask开发服务器:

    if __name__ == '__main__':
        port = find_free_port()
        with open("port_info.json", "w") as f:
            json.dump({"port": port}, f)
        print(port)
        app.run(debug=True, host='127.0.0.1', port=port)

这导致了以下响应:


    {
        "error": " * Debugger is active!
                   * Debugger PIN: 454-500-849
                  Traceback (most recent call last):
                    File "order_input_api.py", line 63, in <module>
                    File "flask/app.py", line 615, in run
                    File "werkzeug/serving.py", line 1077, in run_simple
                    File "werkzeug/serving.py", line 917, in make_server
                    File "werkzeug/serving.py", line 784, in __init__
                  IndexError: string index out of range
                  [26517] Failed to execute script 'order_input_api' due to unhandled exception!",
        "success": false
    }

  • 然后我将端口从
    find_free_port()
    更改为
    5000
    :

    "...
        Traceback (most recent call last):
          File \"order_input_api.py\", line 63, in <module>
          File \"flask/app.py\", line 615, in run
          File \"werkzeug/serving.py\", line 1077, in run_simple
          File \"werkzeug/serving.py\", line 917, in make_server
          File \"werkzeug/serving.py\", line 779, in __init__
          File \"socket.py\", line 544, in fromfd
        OSError: [Errno 9] Bad file descriptor
        [25777] Failed to execute script 'order_input_api' due to unhandled exception!
    ..."

  • 我尝试更新
    .spec
    文件并根据此
    链接
    添加os.chdir(app.root_path)
  • 我尝试将
    'werkzeung'
    添加到
    hiddenimports
    文件中的
    .spec
  • 我尝试从虚拟环境内部和外部构建可执行文件,并在 venv 内部/外部运行可执行文件
  • 我检查了所有其他有关 Flask/PyInstaller 问题的 StackOverflow 帖子;大多数人没有答案或有不同的解决方案对我不起作用

每次我进行更改时我尝试运行脚本本身(

python order_input_api.py
),向端点发送POST请求,并且工作正常。然后运行可执行文件并发送相同的请求失败。

对于如何调试或问题可能是什么,我已经没有想法了。此时非常感谢任何帮助。

python flask pyinstaller
1个回答
0
投票

我确定问题出在使用

subprocess.Popen
并尝试调用
python
命令(或
sys.executable
):

    python_executable = sys.executable
    process = subprocess.Popen([python_executable, 'order_counter.py'],

由于这不起作用,我只是将

order_counter.py
main
作为函数导入并直接调用它,并使用
contextlib
redirect_stdout
捕获输出:


    import io
    from contextlib import redirect_stdout
    from order_counter import main as order_counter
    
    @app.route('/process_orders', methods=['POST'])
    def process_orders():
        try:
            data = request.json
            packages_input = '\n'.join(data['packages']) + '\n' + 'done'
            input_stream = io.StringIO(packages_input)
            output_stream = io.StringIO()
            with redirect_stdout(output_stream):
                sys.stdin = input_stream
                order_counter()
                sys.stdin = sys.__stdin__
            return parse_output_data(output_stream.getvalue())
        except Exception as e:
            return jsonify({'success': False, 'error': str(e)}), 500

© www.soinside.com 2019 - 2024. All rights reserved.