Я согласен с принятым ответом, что
Вы не можете легко добраться до сокета сервера, используемого Flask, так как он скрыт во внутренностях стандартной библиотеки (Flask использует Werkzeug, чья разработкасервер основан на BaseHTTPServer stdlib).
Однако я только что обнаружил, что Werkzeug предоставляет возможность передавать дескриптор файла для использования в качестве сокета при запуске werkzeug.serving.BaseWSGIServer
(что и есть в колбе * 1008).* функция в конечном итоге делает).Используя эту функциональность, можно выбрать произвольно доступный порт, а затем указать Werkzeug использовать его.
Вот запуск моего приложения:
import logging
import pprint
import sys
from flask import Flask
applog = logging.getLogger(__name__)
applog.setLevel(logging.INFO)
app = Flask(__name__)
if __name__ == '__main__':
app.config.from_object('myapp.default_settings')
try:
app.config.from_envvar('MYAPP_SETTINGS')
except RuntimeError:
applog.warning("MYAPP_SETTINGS environment variable not set or invalid. Using default settings.")
# SERVER_NAME should be in the form of localhost:5000, 127.0.0.1:0, etc...
# So, split on : and take the second element as the port
# If the port is 0, we need to pick a random port and then tell the server to use that socket
if app.config['SERVER_NAME'] and int(app.config['SERVER_NAME'].split(':')[1]) == 0:
import socket, os
# Chose a random available port by binding to port 0
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((app.config['SERVER_NAME'].split(':')[0], 0))
sock.listen()
# Tells the underlying WERKZEUG server to use the socket we just created
os.environ['WERKZEUG_SERVER_FD'] = str(sock.fileno())
# Update the configuration so it matches with the port we just chose
# (sock.getsockname will return the actual port being used, not 0)
app.config['SERVER_NAME'] = '%s:%d' % (sock.getsockname())
# Optionally write the current port to a file on the filesystem
if app.config['CREATE_PORT_FILE']:
with open(app.config['PORT_FILE'], 'w') as port_file:
port_file.write(app.config['SERVER_NAME'].split(':')[1])
applog.info("Running app on %s" % (app.config['SERVER_NAME']))
applog.info("Active Settings:%s" % pprint.pformat(app.config, compact=True))
app.run()
Содержимое myapp.default_settings
:
SERVER_NAME = '127.0.0.1:0'
PORT_FILE = 'current_port'
CREATE_PORT_FILE = True
Важным битом здесь является установка os.environ['WERKZEUG_SERVER_FD']
.Werkzeug просматривает эту переменную среды во время функции run_simple
и, если она определена, передает ее как параметр fd
в функцию make_server
.В конечном итоге это используется в качестве сокета для связи.
Хотя я не могу гарантировать стабильность этого подхода (я не знаю, насколько хорошо поддерживается переменная окружения WERKZEUG_SERVER_FD
), я предпочитаю его предложенномурешений пока что:
- Мне не нужно проходить через ряд портов, перехватывающих исключения, я просто получаю первый доступный порт прямо из ОС.
- Нетвероятность того, что выбранный мной случайный порт будет использован между моментом, когда я связываю случайный порт, и когда я запускаю приложение, потому что порт, который я связываю, является портом, который заканчивается моим приложением.
Приведенный выше код также регистрирует журналиспользуемый порт и дополнительно записывает текущий используемый порт в файл, указанный параметром конфигурации.