Flask 热重载在 Python 3.10 中不起作用:“尝试对非套接字的对象进行操作”

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

我正在使用 Python 3.10.11、Flask 3.0.0 和 Werkzeug 3.0.1。 我在一个项目上工作了几个月,休息了一下,现在,几个月后,由于某种原因,热重载不起作用。相反,它会抛出此错误。此外,有时即使我重新启动应用程序(关闭并启动新终端),应用程序也不会更新。我尝试将这个问题作为环境问题和版本不匹配问题来解决,但这两种解决方案最终都不起作用。我的应用程序中也没有任何套接字。

Exception in thread Thread-2 (serve_forever):
Traceback (most recent call last):
  File "C:\Users\USER\AppData\Local\Programs\Python\Python310\lib\threading.py", line 1016, in _bootstrap_inner
    self.run()
  File "C:\Users\USER\AppData\Local\Programs\Python\Python310\lib\threading.py", line 953, in run
    self._target(*self._args, **self._kwargs)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python310\lib\site-packages\werkzeug\serving.py", line 806, in serve_forever
    super().serve_forever(poll_interval=poll_interval)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python310\lib\socketserver.py", line 232, in serve_forever
    ready = selector.select(poll_interval)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python310\lib\selectors.py", line 324, in select
    r, w, _ = self._select(self._readers, self._writers, [], timeout)
  File "C:\Users\USER\AppData\Local\Programs\Python\Python310\lib\selectors.py", line 315, in _select
    r, w, x = select.select(r, w, w, timeout)
OSError: [WinError 10038] An operation was attempted on something that is not a socket 

我正在使用pipenv,尝试以多种方式运行该应用程序。

flask --debug run
py ./app.py
pipenv run py ./app.py

我只是不明白为什么它停止工作。我检查了版本不匹配并尝试更改多个 python 版本以及多个 Flask & Werkzeug 版本,不幸的是没有运气。 我尝试重置 netsh:

netsh winsock reset
,如here所示,但也没有运气。 我尝试删除环境并创建一个新环境。我在全球和本地(在环境中)运行这个应用程序,但遇到了同样的问题。有时在环境中运行它只捕获第一次重新加载,然后停止工作(没有任何重新加载选项)

我一无所知。什么会导致这种情况? 我唯一的猜测是,当它尝试重新加载应用程序时,它会在我无法“Ctrl + C”退出的地方崩溃,这会导致循环损坏整个侦听端口。更改端口也不起作用。

python sockets flask werkzeug hot-reload
1个回答
0
投票

@JohnGordon 和@MarkTolonen 是对的。我花了几个小时试图解决这个问题,甚至不知道是什么线路造成的。我删除了所有逻辑,发现我的主 Flask 应用程序中的行

db = MyDBClass()
导致了错误。

导致错误的原因是什么

我的数据库类有一个函数,该函数在一个单独的、分离的线程中运行,该线程处理其请求队列。由于大量请求同时传入,因此该线程对于我的数据库来说是必须的,以免混合提供给请求的结果。热重载 Flask 应用程序创建了多个数据库实例,这些实例创建了连接到同一数据库的多个线程。两个线程无法访问同一数据库,因此我们收到此错误。

import logging
import threading
from flask_cors import CORS
from flask import Flask, request
# Other imports
        
app = Flask(__name__)

db = PostgreDB() 
# Causes an error because a hot-reload will create multiple threads using the same database here 

def restart_db():
    global db
    db = PostgreDB() # < Error


@app.route('/')
    def index():
        return "Hurray!"


if __name__ == '__main__':

    app.run(host='0.0.0.0', port=5500, debug=True)

修复:Flask 的全局命名空间
g

Flask 为我们提供了一个在应用程序上下文中使用的全局命名空间。根据 Flask 的文档:

通常,应用程序上下文与请求具有相同的生命周期。

因此,对于每个请求,数据库类都会重新启动。这意味着现在可以在创建新线程之前正确终止线程(当然它的终止必须在

YourDBClass.__del__()
中实现)。现在,热重载时不会创建多个线程,因此问题已解决。 代码:

import logging
import threading
from flask_cors import CORS
from flask import Flask, request
# Other imports

def get_db():
    '''
    Function inserts db into Flask global variable namespace
    which shutdowns after appcontext dies
    '''
    if 'db' not in g:
        g.db = MyDB()  # create a new database connection
    return g.db


# Create Flask app
def create_app():
    app = Flask(__name__)


    # Register FLASK ROUTES:
    @app.route('/')
    def index():

        name = g.db.get_name({"id": 123}) # < How to use db class in your code
        return f"Hurray {name}" 
    
    @app.before_request
    def before_request():
        g.db = get_db()

    @app.teardown_appcontext
    def teardown_db(exception):
        db: MyDB | None = g.pop('db', None)
        if db is not None:
            db.close()
            

        return app

if __name__ == '__main__':
    app = create_app()
    app.run(host='0.0.0.0', port=5500, debug=True)

请考虑

现在可能出现的一个问题是效率,因为为每个请求创建数据库类实例。这种开销可以通过 Postgresql 连接池(重用数据库连接)来减少。目前,我的数据库收到的连接数不足以导致此错误,因此我暂时保留它。

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