This repository has been archived by the owner on Sep 20, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 129
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1097 from pypeclub/feature/ws_tool_in_avalon
Websocket server tool in avalon
- Loading branch information
Showing
18 changed files
with
210 additions
and
1,611 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from .webserver_module import WebServerModule | ||
|
||
|
||
__all__ = ( | ||
"WebServerModule", | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
import threading | ||
import asyncio | ||
|
||
from aiohttp import web | ||
|
||
from pype.lib import PypeLogger | ||
|
||
log = PypeLogger.get_logger("WebServer") | ||
|
||
|
||
class WebServerManager: | ||
"""Manger that care about web server thread.""" | ||
def __init__(self, module): | ||
self.module = module | ||
|
||
self.client = None | ||
self.handlers = {} | ||
self.on_stop_callbacks = [] | ||
|
||
self.app = web.Application() | ||
|
||
# add route with multiple methods for single "external app" | ||
|
||
self.webserver_thread = WebServerThread(self, self.module.port) | ||
|
||
def add_route(self, *args, **kwargs): | ||
self.app.router.add_route(*args, **kwargs) | ||
|
||
def add_static(self, *args, **kwargs): | ||
self.app.router.add_static(*args, **kwargs) | ||
|
||
def start_server(self): | ||
if self.webserver_thread and not self.webserver_thread.is_alive(): | ||
self.webserver_thread.start() | ||
|
||
def stop_server(self): | ||
if not self.is_running: | ||
return | ||
try: | ||
log.debug("Stopping Web server") | ||
self.webserver_thread.is_running = False | ||
self.webserver_thread.stop() | ||
|
||
except Exception: | ||
log.warning( | ||
"Error has happened during Killing Web server", | ||
exc_info=True | ||
) | ||
|
||
@property | ||
def is_running(self): | ||
if not self.webserver_thread: | ||
return False | ||
return self.webserver_thread.is_running | ||
|
||
def thread_stopped(self): | ||
for callback in self.on_stop_callbacks: | ||
callback() | ||
|
||
|
||
class WebServerThread(threading.Thread): | ||
""" Listener for requests in thread.""" | ||
def __init__(self, manager, port): | ||
super(WebServerThread, self).__init__() | ||
|
||
self.is_running = False | ||
self.port = port | ||
self.manager = manager | ||
self.loop = None | ||
self.runner = None | ||
self.site = None | ||
self.tasks = [] | ||
|
||
def run(self): | ||
self.is_running = True | ||
|
||
try: | ||
log.info("Starting WebServer server") | ||
self.loop = asyncio.new_event_loop() # create new loop for thread | ||
asyncio.set_event_loop(self.loop) | ||
|
||
self.loop.run_until_complete(self.start_server()) | ||
|
||
log.debug( | ||
"Running Web server on URL: \"localhost:{}\"".format(self.port) | ||
) | ||
|
||
asyncio.ensure_future(self.check_shutdown(), loop=self.loop) | ||
self.loop.run_forever() | ||
|
||
except Exception: | ||
log.warning( | ||
"Web Server service has failed", exc_info=True | ||
) | ||
finally: | ||
self.loop.close() # optional | ||
|
||
self.is_running = False | ||
self.manager.thread_stopped() | ||
log.info("Web server stopped") | ||
|
||
async def start_server(self): | ||
""" Starts runner and TCPsite """ | ||
self.runner = web.AppRunner(self.manager.app) | ||
await self.runner.setup() | ||
self.site = web.TCPSite(self.runner, 'localhost', self.port) | ||
await self.site.start() | ||
|
||
def stop(self): | ||
"""Sets is_running flag to false, 'check_shutdown' shuts server down""" | ||
self.is_running = False | ||
|
||
async def check_shutdown(self): | ||
""" Future that is running and checks if server should be running | ||
periodically. | ||
""" | ||
while self.is_running: | ||
while self.tasks: | ||
task = self.tasks.pop(0) | ||
log.debug("waiting for task {}".format(task)) | ||
await task | ||
log.debug("returned value {}".format(task.result)) | ||
|
||
await asyncio.sleep(0.5) | ||
|
||
log.debug("Starting shutdown") | ||
await self.site.stop() | ||
log.debug("Site stopped") | ||
await self.runner.cleanup() | ||
log.debug("Runner stopped") | ||
tasks = [ | ||
task | ||
for task in asyncio.all_tasks() | ||
if task is not asyncio.current_task() | ||
] | ||
list(map(lambda task: task.cancel(), tasks)) # cancel all the tasks | ||
results = await asyncio.gather(*tasks, return_exceptions=True) | ||
log.debug(f'Finished awaiting cancelled tasks, results: {results}...') | ||
await self.loop.shutdown_asyncgens() | ||
# to really make sure everything else has time to stop | ||
await asyncio.sleep(0.07) | ||
self.loop.stop() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import os | ||
from pype import resources | ||
from .. import PypeModule, ITrayService | ||
|
||
|
||
class WebServerModule(PypeModule, ITrayService): | ||
name = "webserver" | ||
label = "WebServer" | ||
|
||
def initialize(self, module_settings): | ||
self.enabled = True | ||
self.server_manager = None | ||
|
||
# TODO find free port | ||
self.port = 8098 | ||
|
||
def connect_with_modules(self, *_a, **_kw): | ||
return | ||
|
||
def tray_init(self): | ||
self.create_server_manager() | ||
self._add_resources_statics() | ||
|
||
def tray_start(self): | ||
self.start_server() | ||
|
||
def tray_exit(self): | ||
self.stop_server() | ||
|
||
def _add_resources_statics(self): | ||
static_prefix = "/res" | ||
self.server_manager.add_static(static_prefix, resources.RESOURCES_DIR) | ||
|
||
os.environ["PYPE_STATICS_SERVER"] = "http://localhost:{}{}".format( | ||
self.port, static_prefix | ||
) | ||
|
||
def start_server(self): | ||
if self.server_manager: | ||
self.server_manager.start_server() | ||
|
||
def stop_server(self): | ||
if self.server_manager: | ||
self.server_manager.stop_server() | ||
|
||
def create_server_manager(self): | ||
if self.server_manager: | ||
return | ||
|
||
from .server import WebServerManager | ||
|
||
self.server_manager = WebServerManager(self) | ||
self.server_manager.on_stop_callbacks.append( | ||
self.set_service_failed_icon | ||
) |
This file was deleted.
Oops, something went wrong.
Empty file.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.