From 81494453b0d6aae1355075e9b7528d541162b156 Mon Sep 17 00:00:00 2001 From: Yaser Amiri Date: Tue, 26 Dec 2017 19:17:13 +0330 Subject: [PATCH] Remove dependency on requests library. Change auto reloader enviroment varible name to SANIC_SERVER_RUNNING Fix some typo mistakes, flake uncompatibilities and such problems. Raise NotImplementedError for operating systems except posix systems for auto reloading. --- sanic/app.py | 13 +++++-- sanic/reloader_helpers.py | 15 ++++---- tests/test_auto_reload.py | 65 +++++++++++++++++++++----------- tests/test_keep_alive_timeout.py | 3 +- tests/test_middleware.py | 4 +- tests/test_request_data.py | 3 +- tests/test_request_stream.py | 3 +- tox.ini | 1 - 8 files changed, 68 insertions(+), 39 deletions(-) diff --git a/sanic/app.py b/sanic/app.py index 90a305f9..bb012d33 100644 --- a/sanic/app.py +++ b/sanic/app.py @@ -645,8 +645,13 @@ class Sanic: try: self.is_running = True if workers == 1: - if os.name == 'posix' and auto_reload and \ - os.environ.get('MAIN_PROCESS_RUNNED') != 'true': + if auto_reload and os.name != 'posix': + # This condition must be removed after implementing + # auto reloader for other operating systems. + raise NotImplementedError + + if auto_reload and \ + os.environ.get('SANIC_SERVER_RUNNING') != 'true': reloader_helpers.watchdog(2) else: serve(**server_settings) @@ -808,14 +813,14 @@ class Sanic: logger.setLevel(logging.DEBUG) if self.config.LOGO is not None and \ - os.environ.get('MAIN_PROCESS_RUNNED') != 'true': + os.environ.get('SANIC_SERVER_RUNNING') != 'true': logger.debug(self.config.LOGO) if run_async: server_settings['run_async'] = True # Serve - if host and port and os.environ.get('MAIN_PROCESS_RUNNED') != 'true': + if host and port and os.environ.get('SANIC_SERVER_RUNNING') != 'true': proto = "http" if ssl is not None: proto = "https" diff --git a/sanic/reloader_helpers.py b/sanic/reloader_helpers.py index ced62921..e1349089 100644 --- a/sanic/reloader_helpers.py +++ b/sanic/reloader_helpers.py @@ -7,7 +7,9 @@ from multiprocessing import Process def _iter_module_files(): - """This iterates over all relevant Python files. It goes through all + """This iterates over all relevant Python files. + + It goes through all loaded files from modules, all files in folders of already loaded modules as well as all files reachable through a package. """ @@ -38,12 +40,12 @@ def _get_args_for_reloading(): def restart_with_reloader(): - """Create a new process and a subprocess in it - with the same arguments as this one. + """Create a new process and a subprocess in it with the same arguments as + this one. """ args = _get_args_for_reloading() new_environ = os.environ.copy() - new_environ['MAIN_PROCESS_RUNNED'] = 'true' + new_environ['SANIC_SERVER_RUNNING'] = 'true' cmd = ' '.join(args) worker_process = Process( target=subprocess.call, args=(cmd,), @@ -58,7 +60,7 @@ def kill_process_children_unix(pid): :param pid: PID of process (process ID) :return: Nothing """ - root_process_path = "/proc/%s/task/%s/children" % (pid, pid) + root_process_path = "/proc/{pid}/task/{pid}/children".format(pid=pid) if not os.path.isfile(root_process_path): return with open(root_process_path) as children_list_file: @@ -87,12 +89,11 @@ def kill_program_completly(proc): def watchdog(sleep_interval): - """Whatch project files, restart worker process if a change happened. + """Watch project files, restart worker process if a change happened. :param sleep_interval: interval in second. :return: Nothing """ - mtimes = {} worker_process = restart_with_reloader() signal.signal( diff --git a/tests/test_auto_reload.py b/tests/test_auto_reload.py index de193042..104f823b 100644 --- a/tests/test_auto_reload.py +++ b/tests/test_auto_reload.py @@ -3,10 +3,13 @@ import sys import subprocess import signal from threading import Thread -import requests from time import sleep +from json.decoder import JSONDecodeError +import aiohttp +import asyncio +import async_timeout -sanic_project_content_one = ''' +sanic_project_content_one = ''' from sanic import Sanic from sanic import response @@ -22,7 +25,7 @@ if __name__ == '__main__': app.run(host="127.0.0.1", port=8000, auto_reload=True) ''' -sanic_project_content_two = ''' +sanic_project_content_two = ''' from sanic import Sanic from sanic import response @@ -39,6 +42,8 @@ if __name__ == '__main__': ''' process_id = None + + def execute_cmd(command): process = subprocess.Popen(command, shell=True) global process_id @@ -48,38 +53,56 @@ def execute_cmd(command): class TestAutoReloading: - def test_reloading_after_change_file(self,capsys): + def check_response(self, url, response): + """Send http request and tries to take it's response as json. + Returns a dictionary. + """ + async def req(url, excepted_response): + async with aiohttp.ClientSession() as session: + with async_timeout.timeout(10): + async with session.get(url) as response: + try: + result = await response.json() + except JSONDecodeError: + result = {} + return result == excepted_response + + loop = asyncio.get_event_loop() + return loop.run_until_complete(req(url, response)) + + def test_reloading_after_change_file(self, capsys): if os.name != 'posix': return - with capsys.disabled(): pass + with capsys.disabled(): + pass sanic_app_file_path = "simple_sanic_app.py" - with open (sanic_app_file_path, "w") as _file: + with open(sanic_app_file_path, "w") as _file: _file.write(sanic_project_content_one) cmd = ' '.join([sys.executable, sanic_app_file_path]) thread = Thread(target=execute_cmd, args=(cmd,)) thread.start() - sleep(2) # wait for completing server start process - response = requests.get("http://127.0.0.1:8000/").json() - assert response == {"test": 1} + sleep(2) # wait for completing server start process + assert self.check_response("http://127.0.0.1:8000/", {"test": 1}) - with open (sanic_app_file_path, "w") as _file: + with open(sanic_app_file_path, "w") as _file: _file.write(sanic_project_content_two) - sleep(2) # wait for completing server start process - response = requests.get("http://127.0.0.1:8000/").json() - assert response == {"test": 2} + + sleep(2) # wait for completing server start process + assert self.check_response("http://127.0.0.1:8000/", {"test": 2}) thread.join(1) os.remove(sanic_app_file_path) def teardown_method(self, method): - if process_id: - root_proc_path = "/proc/%s/task/%s/children" % (process_id, process_id) - if not os.path.isfile(root_proc_path): - return - with open(root_proc_path) as children_list_file: - children_list_pid = children_list_file.read().split() - for child_pid in children_list_pid: - os.kill(int(child_pid), signal.SIGTERM) + if process_id: + root_proc_path = \ + "/proc/{pid}/task/{pid}/children".format(pid=process_id) + if not os.path.isfile(root_proc_path): + return + with open(root_proc_path) as children_list_file: + children_list_pid = children_list_file.read().split() + for child_pid in children_list_pid: + os.kill(int(child_pid), signal.SIGTERM) diff --git a/tests/test_keep_alive_timeout.py b/tests/test_keep_alive_timeout.py index 15f6d705..3cb98771 100644 --- a/tests/test_keep_alive_timeout.py +++ b/tests/test_keep_alive_timeout.py @@ -68,7 +68,7 @@ class ReuseableSanicTestClient(SanicTestClient): import traceback traceback.print_tb(e2.__traceback__) exceptions.append(e2) - #Don't stop here! self.app.stop() + # Don't stop here! self.app.stop() if self._server is not None: _server = self._server @@ -266,4 +266,3 @@ def test_keep_alive_server_timeout(): assert isinstance(exception, ValueError) assert "Connection reset" in exception.args[0] or \ "got a new connection" in exception.args[0] - diff --git a/tests/test_middleware.py b/tests/test_middleware.py index 4d4d6901..a879834d 100644 --- a/tests/test_middleware.py +++ b/tests/test_middleware.py @@ -75,6 +75,7 @@ def test_middleware_response_exception(): assert response.text == 'OK' assert result['status_code'] == 404 + def test_middleware_override_request(): app = Sanic('test_middleware_override_request') @@ -109,7 +110,6 @@ def test_middleware_override_response(): assert response.text == 'OK' - def test_middleware_order(): app = Sanic('test_middleware_order') @@ -146,4 +146,4 @@ def test_middleware_order(): request, response = app.test_client.get('/') assert response.status == 200 - assert order == [1,2,3,4,5,6] + assert order == [1, 2, 3, 4, 5, 6] diff --git a/tests/test_request_data.py b/tests/test_request_data.py index f795ff1f..f9349427 100644 --- a/tests/test_request_data.py +++ b/tests/test_request_data.py @@ -20,7 +20,8 @@ def test_storage(): @app.route('/') def handler(request): - return json({'user': request.get('user'), 'sidekick': request.get('sidekick')}) + return json({'user': request.get('user'), + 'sidekick': request.get('sidekick')}) request, response = app.test_client.get('/') diff --git a/tests/test_request_stream.py b/tests/test_request_stream.py index 4ca4e44e..8941c53e 100644 --- a/tests/test_request_stream.py +++ b/tests/test_request_stream.py @@ -188,7 +188,8 @@ def test_request_stream_handle_exception(): # 405 request, response = app.test_client.get('/post/random_id', data=data) assert response.status == 405 - assert response.text == 'Error: Method GET not allowed for URL /post/random_id' + assert response.text == \ + 'Error: Method GET not allowed for URL /post/random_id' def test_request_stream_blueprint(): diff --git a/tox.ini b/tox.ini index 6bd2c72d..ff43a139 100644 --- a/tox.ini +++ b/tox.ini @@ -16,7 +16,6 @@ deps = chardet<=2.3.0 beautifulsoup4 gunicorn - requests commands = pytest tests --cov sanic --cov-report= {posargs} - coverage combine --append