Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade Tornado to v6, and fix servers accordingly. #1181

Merged
merged 1 commit into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions openhtf/output/servers/station_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
aggregate info from multiple station servers with a single frontend.
"""

import asyncio
import contextlib
import itertools
import json
Expand Down Expand Up @@ -166,6 +167,7 @@ def __init__(self, update_callback):

def run(self):
"""Call self._poll_for_update() in a loop and handle errors."""
asyncio.set_event_loop(asyncio.new_event_loop())
while True:
try:
self._poll_for_update()
Expand Down
51 changes: 35 additions & 16 deletions openhtf/output/servers/web_gui_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
# limitations under the License.
"""Extensible HTTP server serving the OpenHTF Angular frontend."""

import asyncio
import os
import threading
import time

import tornado.httpclient
import tornado.httpserver
Expand Down Expand Up @@ -112,6 +112,8 @@ class WebGuiServer(threading.Thread):

def __init__(self, additional_routes, port, sockets=None):
super(WebGuiServer, self).__init__(name=type(self).__name__)
self.ts_event = threading.Event()
self._running = asyncio.Event()

# Set up routes.
routes = [
Expand All @@ -121,24 +123,17 @@ def __init__(self, additional_routes, port, sockets=None):
}),
]
routes.extend(additional_routes)

if sockets is None:
sockets, self.port = bind_port(port)
else:
if not port:
raise ValueError('When sockets are passed to the server, port must be '
'specified and nonzero.')
self.port = port
self._sockets = sockets
self.port = port
self._loop = None

# Configure the Tornado application.
application = tornado.web.Application(
self.application = tornado.web.Application(
routes,
default_handler_class=DefaultHandler,
template_loader=TemplateLoader(STATIC_FILES_ROOT),
static_path=STATIC_FILES_ROOT,
)
self.server = tornado.httpserver.HTTPServer(application)
self.server.add_sockets(sockets)

def __enter__(self):
self.start()
Expand All @@ -147,14 +142,38 @@ def __enter__(self):
def __exit__(self, *unused_args):
self.stop()

async def run_app(self):
"""Runs the station server application."""
self.ts_watchdog_task = asyncio.create_task(self._stop_threadsafe())
if self._sockets is None:
self._sockets, self.port = bind_port(self.port)
else:
if not self.port:
raise ValueError(
'When sockets are passed to the server, port must be '
'specified and nonzero.'
)
self.server = tornado.httpserver.HTTPServer(self.application)
self.server.add_sockets(self._sockets)
await self._running.wait()
await self.ts_watchdog_task
await self.server.close_all_connections()

async def _stop_threadsafe(self):
"""Handles stopping the server in a threadsafe manner."""
while not self.ts_event.is_set():
await asyncio.sleep(0.1)
self._running.set()

def _get_config(self):
"""Override this to configure the Angular app."""
return {}

def run(self):
tornado.ioloop.IOLoop.instance().start() # Blocking IO loop.
"""Runs the station server."""
asyncio.run(self.run_app())

def stop(self):
self.server.stop()
ioloop = tornado.ioloop.IOLoop.instance()
ioloop.add_timeout(time.time() + _SERVER_SHUTDOWN_BUFFER_S, ioloop.stop)
"""Stops the station server. Method is threadsafe."""
self.ts_event.set()

15 changes: 13 additions & 2 deletions openhtf/output/web_gui/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
# OpenHTF web GUI client

## Prerequisites

Follow the steps from [CONTRIBUTING.md](../../../CONTRIBUTING.md) to set up
the Python virtual environment. Make sure you have done an "editable" install.

## Development

First, start the dashboard server on the default port by running the following
Start the dashboard server on the default port by running the following
from the project root directory:

```
Expand All @@ -22,4 +27,10 @@ the dashboard server and should reload automatically when changes are made to
frontend files.

With the dashboard running, you can run OpenHTF tests separately, and they
should be picked up over multicast and appear on the dashboard.
should be picked up over multicast and appear on the dashboard. To test things
out, you can run the frontend example test:

```
python3 examples/frontend_example.py
```

4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "openhtf"
version = "1.6.0"
version = "1.6.1"
authors = [
{ name="The OpenHTF Authors"},
]
Expand All @@ -23,7 +23,7 @@ dependencies = [
"pyOpenSSL>=17.1.0",
"requests>=2.27.1",
"sockjs_tornado>=1.0.7",
"tornado>=4.3,<5.0",
"tornado>=6,<6.3",
"typing_extensions>=4.12.2",
]
license = {file = "LICENSE"}
Expand Down