kopia lustrzana https://github.com/inkstitch/inkstitch
111 wiersze
3.1 KiB
Python
111 wiersze
3.1 KiB
Python
|
import errno
|
||
|
import logging
|
||
|
import socket
|
||
|
from threading import Thread
|
||
|
import time
|
||
|
|
||
|
from flask import Flask, request, g
|
||
|
import requests
|
||
|
|
||
|
from ..utils.json import InkStitchJSONEncoder
|
||
|
from .simulator import simulator
|
||
|
from .stitch_plan import stitch_plan
|
||
|
|
||
|
|
||
|
class APIServer(Thread):
|
||
|
def __init__(self, *args, **kwargs):
|
||
|
self.extension = args[0]
|
||
|
Thread.__init__(self, *args[1:], **kwargs)
|
||
|
self.daemon = True
|
||
|
self.shutting_down = False
|
||
|
self.app = None
|
||
|
self.host = None
|
||
|
self.port = None
|
||
|
self.ready = False
|
||
|
|
||
|
self.__setup_app()
|
||
|
|
||
|
def __setup_app(self): # noqa: C901
|
||
|
self.app = Flask(__name__)
|
||
|
self.app.json_encoder = InkStitchJSONEncoder
|
||
|
|
||
|
self.app.register_blueprint(simulator, url_prefix="/simulator")
|
||
|
self.app.register_blueprint(stitch_plan, url_prefix="/stitch_plan")
|
||
|
|
||
|
@self.app.before_request
|
||
|
def store_extension():
|
||
|
# make the InkstitchExtension object available to the view handling
|
||
|
# this request
|
||
|
g.extension = self.extension
|
||
|
|
||
|
@self.app.route('/shutdown', methods=['POST'])
|
||
|
def shutdown():
|
||
|
self.shutting_down = True
|
||
|
request.environ.get('werkzeug.server.shutdown')()
|
||
|
return "shutting down"
|
||
|
|
||
|
@self.app.route('/ping')
|
||
|
def ping():
|
||
|
return "pong"
|
||
|
|
||
|
def stop(self):
|
||
|
# for whatever reason, shutting down only seems possible in
|
||
|
# the context of a flask request, so we'll just make one
|
||
|
requests.post("http://%s:%s/shutdown" % (self.host, self.port))
|
||
|
|
||
|
def disable_logging(self):
|
||
|
logging.getLogger('werkzeug').setLevel(logging.ERROR)
|
||
|
|
||
|
def run(self):
|
||
|
self.disable_logging()
|
||
|
|
||
|
self.host = "127.0.0.1"
|
||
|
self.port = 5000
|
||
|
|
||
|
while True:
|
||
|
try:
|
||
|
self.app.run(self.host, self.port, threaded=True)
|
||
|
except socket.error as e:
|
||
|
if e.errno == errno.EADDRINUSE:
|
||
|
self.port += 1
|
||
|
continue
|
||
|
else:
|
||
|
raise
|
||
|
else:
|
||
|
break
|
||
|
|
||
|
def ready_checker(self):
|
||
|
"""Wait until the server is started.
|
||
|
|
||
|
Annoyingly, there's no way to get a callback to be run when the Flask
|
||
|
server starts. Instead, we'll have to poll.
|
||
|
"""
|
||
|
|
||
|
while True:
|
||
|
if self.port:
|
||
|
try:
|
||
|
response = requests.get("http://%s:%s/ping" % (self.host, self.port))
|
||
|
if response.status_code == 200:
|
||
|
break
|
||
|
except socket.error, e:
|
||
|
if e.errno == errno.ECONNREFUSED:
|
||
|
pass
|
||
|
else:
|
||
|
raise
|
||
|
|
||
|
time.sleep(0.1)
|
||
|
|
||
|
def start_server(self):
|
||
|
"""Start the API server.
|
||
|
|
||
|
returns: port (int) -- the port that the server is listening on
|
||
|
(on localhost)
|
||
|
"""
|
||
|
|
||
|
checker = Thread(target=self.ready_checker)
|
||
|
checker.start()
|
||
|
self.start()
|
||
|
checker.join()
|
||
|
|
||
|
return self.port
|