From 839d513bb80cb601d7309cb831a009b812824845 Mon Sep 17 00:00:00 2001 From: creslinux Date: Sat, 23 Jun 2018 11:53:09 +0000 Subject: [PATCH 1/4] Added api server shutdown function, and exposed on HTTP as /stop_api url This will stop the running app gracefully - processing current api calls then shutting the werkzueg (run) listening server. Have also called this from the cleanup placeholder. I'm not sure this is what is intended by cleanup def. By which I mean there may be a thread left running with no app within - not sure how to check this just yet. tidied excessive logging. --- freqtrade/rpc/api_server.py | 49 +++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/freqtrade/rpc/api_server.py b/freqtrade/rpc/api_server.py index 3a8391b98..d89bee5f9 100644 --- a/freqtrade/rpc/api_server.py +++ b/freqtrade/rpc/api_server.py @@ -41,6 +41,7 @@ class ApiServerSuperWrap(RPC): """ app.register_error_handler(404, self.page_not_found) app.add_url_rule('/', 'hello', view_func=self.hello, methods=['GET']) + app.add_url_rule('/stop_api', 'stop_api', view_func=self.stop_api, methods=['GET']) def register_rest_rpc_urls(self): """ @@ -77,10 +78,6 @@ class ApiServerSuperWrap(RPC): except Exception: logger.exception("Api server failed to start, exception message is:") - def cleanup(self) -> None: - # TODO: implement me - raise NotImplementedError - @property def name(self) -> str: # TODO: implement me @@ -90,11 +87,43 @@ class ApiServerSuperWrap(RPC): # TODO: implement me raise NotImplementedError + def shutdown_api_server(self): + """ + Stop the running flask application + + Records the shutdown in logger.info + :return: + """ + func = request.environ.get('werkzeug.server.shutdown') + if func is None: + raise RuntimeError('Not running the Flask Werkzeug Server') + if func is not None: + logger.info('Stopping the Local Rest Server') + func() + return + + def cleanup(self) -> None: + """ + Stops the running application server + + Does not stop the thread,this may not be the desired outcome of cleanup. TBC + :return: + """ + self.shutdown_api_server() + # def cleanup(self) -> None: + # # TODO: implement me + # raise NotImplementedError + """ Define the application methods here, called by app.add_url_rule each Telegram command should have a like local substitute """ + def stop_api(self): + """ For calling shutdown_api_server over via api server HTTP""" + self.shutdown_api_server() + return 'Api Server shutting down... ' + def page_not_found(self, error): # Return "404 not found", 404. return jsonify({'status': 'error', @@ -110,11 +139,16 @@ class ApiServerSuperWrap(RPC): :return: index.html """ rest_cmds = 'Commands implemented:
' \ - '/daily?timescale=7' \ + 'Show 7 days of stats' \ '
' \ - '/stop' \ + 'Stop the Trade thread' \ '
' \ - '/start' + 'Start the Traded thread' \ + '
' \ + ' 404 page does not exist' \ + '
' \ + '
' \ + 'Shut down the api server - be sure' return rest_cmds def daily(self): @@ -125,7 +159,6 @@ class ApiServerSuperWrap(RPC): """ try: timescale = request.args.get('timescale') - logger.info("LocalRPC - Daily Command Called") timescale = int(timescale) stats = self._rpc_daily_profit(timescale, From eedcb649622b952e7a35720967c9e982e3bb8448 Mon Sep 17 00:00:00 2001 From: creslinux Date: Sat, 23 Jun 2018 12:17:00 +0000 Subject: [PATCH 2/4] Added api server shutdown function, and exposed on HTTP as /stop_api url This will stop the running app gracefully - processing current api calls then shutting the werkzueg (run) listening server. Have also called this from the cleanup placeholder. I'm not sure this is what is intended by cleanup def. By which I mean there may be a thread left running with no app within - not sure how to check this just yet. tidied excessive logging. --- freqtrade/rpc/api_server.py | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/freqtrade/rpc/api_server.py b/freqtrade/rpc/api_server.py index d89bee5f9..40eb39943 100644 --- a/freqtrade/rpc/api_server.py +++ b/freqtrade/rpc/api_server.py @@ -1,3 +1,4 @@ +import json import threading import logging # import json @@ -13,7 +14,7 @@ logger = logging.getLogger(__name__) app = Flask(__name__) -class ApiServerSuperWrap(RPC): +class ApiServer(RPC): """ This class is for REST calls across api server """ @@ -78,14 +79,8 @@ class ApiServerSuperWrap(RPC): except Exception: logger.exception("Api server failed to start, exception message is:") - @property - def name(self) -> str: - # TODO: implement me - raise NotImplementedError - def send_msg(self, msg: str) -> None: - # TODO: implement me - raise NotImplementedError + pass def shutdown_api_server(self): """ @@ -111,14 +106,12 @@ class ApiServerSuperWrap(RPC): """ self.shutdown_api_server() # def cleanup(self) -> None: - # # TODO: implement me - # raise NotImplementedError + # pass """ Define the application methods here, called by app.add_url_rule each Telegram command should have a like local substitute """ - def stop_api(self): """ For calling shutdown_api_server over via api server HTTP""" self.shutdown_api_server() @@ -178,7 +171,7 @@ class ApiServerSuperWrap(RPC): Starts TradeThread in bot if stopped. """ msg = self._rpc_start() - return jsonify(msg) + return json.dumps(msg) def stop(self): """ @@ -187,4 +180,4 @@ class ApiServerSuperWrap(RPC): Stops TradeThread in bot if running """ msg = self._rpc_stop() - return jsonify(msg) + return json.dumps(msg) From 3fb680b3b414d9f1ae698dcf31106bdc51f4472c Mon Sep 17 00:00:00 2001 From: creslinux Date: Sat, 23 Jun 2018 12:38:04 +0000 Subject: [PATCH 3/4] removed change to cleanup() in api_server.py --- freqtrade/rpc/api_server.py | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/freqtrade/rpc/api_server.py b/freqtrade/rpc/api_server.py index 40eb39943..5f82b307f 100644 --- a/freqtrade/rpc/api_server.py +++ b/freqtrade/rpc/api_server.py @@ -79,6 +79,9 @@ class ApiServer(RPC): except Exception: logger.exception("Api server failed to start, exception message is:") + def cleanup(self) -> None: + pass + def send_msg(self, msg: str) -> None: pass @@ -97,17 +100,6 @@ class ApiServer(RPC): func() return - def cleanup(self) -> None: - """ - Stops the running application server - - Does not stop the thread,this may not be the desired outcome of cleanup. TBC - :return: - """ - self.shutdown_api_server() - # def cleanup(self) -> None: - # pass - """ Define the application methods here, called by app.add_url_rule each Telegram command should have a like local substitute From c227bb3139a72c502b95704309dbc44703cecadd Mon Sep 17 00:00:00 2001 From: creslinux Date: Sat, 23 Jun 2018 14:04:15 +0000 Subject: [PATCH 4/4] Moved routes that do not need access to rpc.rpc self into their own common file. This is to reduce file size and separate api server routes with privilege to access rpc.rpc defs and those that do not need access, so should not. --- freqtrade/rpc/api_server.py | 72 ++++++----------------------- freqtrade/rpc/api_server_common.py | 74 ++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 57 deletions(-) create mode 100644 freqtrade/rpc/api_server_common.py diff --git a/freqtrade/rpc/api_server.py b/freqtrade/rpc/api_server.py index 5f82b307f..e1897ff3d 100644 --- a/freqtrade/rpc/api_server.py +++ b/freqtrade/rpc/api_server.py @@ -1,22 +1,32 @@ import json import threading import logging -# import json -from flask import Flask, request, jsonify -# from flask_restful import Resource, Api +from flask import request from json import dumps from freqtrade.rpc.rpc import RPC, RPCException from ipaddress import IPv4Address +from freqtrade.rpc.api_server_common import MyApiApp logger = logging.getLogger(__name__) -app = Flask(__name__) +""" +api server routes that do not need access to rpc.rpc +are held within api_server_common.api_server +""" +app = MyApiApp(__name__) class ApiServer(RPC): """ - This class is for REST calls across api server + This class runs api server and provides rpc.rpc functionality to it + + This class starts a none blocking thread the api server runs within + Any routes that require access to rpc.rpc defs are held within this + class. + + Any routes that do not require access to rpc.rcp should be registered + in api_server_common.MyApiApp """ def __init__(self, freqtrade) -> None: """ @@ -29,21 +39,11 @@ class ApiServer(RPC): self._config = freqtrade.config # Register application handling - self.register_rest_other() self.register_rest_rpc_urls() thread = threading.Thread(target=self.run, daemon=True) thread.start() - def register_rest_other(self): - """ - Registers flask app URLs that are not calls to functionality in rpc.rpc. - :return: - """ - app.register_error_handler(404, self.page_not_found) - app.add_url_rule('/', 'hello', view_func=self.hello, methods=['GET']) - app.add_url_rule('/stop_api', 'stop_api', view_func=self.stop_api, methods=['GET']) - def register_rest_rpc_urls(self): """ Registers flask app URLs that are calls to functonality in rpc.rpc. @@ -85,21 +85,6 @@ class ApiServer(RPC): def send_msg(self, msg: str) -> None: pass - def shutdown_api_server(self): - """ - Stop the running flask application - - Records the shutdown in logger.info - :return: - """ - func = request.environ.get('werkzeug.server.shutdown') - if func is None: - raise RuntimeError('Not running the Flask Werkzeug Server') - if func is not None: - logger.info('Stopping the Local Rest Server') - func() - return - """ Define the application methods here, called by app.add_url_rule each Telegram command should have a like local substitute @@ -109,33 +94,6 @@ class ApiServer(RPC): self.shutdown_api_server() return 'Api Server shutting down... ' - def page_not_found(self, error): - # Return "404 not found", 404. - return jsonify({'status': 'error', - 'reason': '''There's no API call for %s''' % request.base_url, - 'code': 404}), 404 - - def hello(self): - """ - None critical but helpful default index page. - - That lists URLs added to the flask server. - This may be deprecated at any time. - :return: index.html - """ - rest_cmds = 'Commands implemented:
' \ - 'Show 7 days of stats' \ - '
' \ - 'Stop the Trade thread' \ - '
' \ - 'Start the Traded thread' \ - '
' \ - ' 404 page does not exist' \ - '
' \ - '
' \ - 'Shut down the api server - be sure' - return rest_cmds - def daily(self): """ Returns the last X days trading stats summary. diff --git a/freqtrade/rpc/api_server_common.py b/freqtrade/rpc/api_server_common.py new file mode 100644 index 000000000..19338a825 --- /dev/null +++ b/freqtrade/rpc/api_server_common.py @@ -0,0 +1,74 @@ +import logging +import flask +from flask import request, jsonify + +logger = logging.getLogger(__name__) + + +class MyApiApp(flask.Flask): + def __init__(self, import_name): + """ + Contains common rest routes and resource that do not need + to access to rpc.rpc functionality + """ + super(MyApiApp, self).__init__(import_name) + + """ + Registers flask app URLs that are not calls to functionality in rpc.rpc. + :return: + """ + self.before_request(self.my_preprocessing) + self.register_error_handler(404, self.page_not_found) + self.add_url_rule('/', 'hello', view_func=self.hello, methods=['GET']) + self.add_url_rule('/stop_api', 'stop_api', view_func=self.stop_api, methods=['GET']) + + def my_preprocessing(self): + # Do stuff to flask.request + pass + + def page_not_found(self, error): + # Return "404 not found", 404. + return jsonify({'status': 'error', + 'reason': '''There's no API call for %s''' % request.base_url, + 'code': 404}), 404 + + def hello(self): + """ + None critical but helpful default index page. + + That lists URLs added to the flask server. + This may be deprecated at any time. + :return: index.html + """ + rest_cmds = 'Commands implemented:
' \ + 'Show 7 days of stats' \ + '
' \ + 'Stop the Trade thread' \ + '
' \ + 'Start the Traded thread' \ + '
' \ + ' 404 page does not exist' \ + '
' \ + '
' \ + 'Shut down the api server - be sure' + return rest_cmds + + def stop_api(self): + """ For calling shutdown_api_server over via api server HTTP""" + self.shutdown_api_server() + return 'Api Server shutting down... ' + + def shutdown_api_server(self): + """ + Stop the running flask application + + Records the shutdown in logger.info + :return: + """ + func = request.environ.get('werkzeug.server.shutdown') + if func is None: + raise RuntimeError('Not running the Flask Werkzeug Server') + if func is not None: + logger.info('Stopping the Local Rest Server') + func() + return