From fb38daa520190613ac9f0b4c1551c8dadaa23baa Mon Sep 17 00:00:00 2001 From: Garrett Michael Flynn Date: Thu, 14 Mar 2024 10:13:49 -0700 Subject: [PATCH 01/13] Split synchronous demos into two readable, separate subfolders --- src/tqdm_publisher/_demo/_client.css | 37 +++++++++ src/tqdm_publisher/_demo/_client.html | 75 ------------------- src/tqdm_publisher/_demo/_client.js | 72 ------------------ .../_demo/_demo_command_line_interface.py | 35 ++++++--- .../_demo/_multiple/_client.html | 36 +++++++++ src/tqdm_publisher/_demo/_multiple/_client.js | 21 ++++++ src/tqdm_publisher/_demo/_multiple/_server.py | 69 +++++++++++++++++ .../_demo/_single_bar/_client.html | 25 +++++++ .../_demo/_single_bar/_client.js | 25 +++++++ .../_demo/{ => _single_bar}/_server.py | 42 +++++++---- .../_demo/utils/WebSocketManager.js | 32 ++++++++ src/tqdm_publisher/_demo/utils/elements.js | 12 +++ src/tqdm_publisher/_publisher.py | 1 + 13 files changed, 309 insertions(+), 173 deletions(-) create mode 100644 src/tqdm_publisher/_demo/_client.css delete mode 100644 src/tqdm_publisher/_demo/_client.html delete mode 100644 src/tqdm_publisher/_demo/_client.js create mode 100644 src/tqdm_publisher/_demo/_multiple/_client.html create mode 100644 src/tqdm_publisher/_demo/_multiple/_client.js create mode 100644 src/tqdm_publisher/_demo/_multiple/_server.py create mode 100644 src/tqdm_publisher/_demo/_single_bar/_client.html create mode 100644 src/tqdm_publisher/_demo/_single_bar/_client.js rename src/tqdm_publisher/_demo/{ => _single_bar}/_server.py (62%) create mode 100644 src/tqdm_publisher/_demo/utils/WebSocketManager.js create mode 100644 src/tqdm_publisher/_demo/utils/elements.js diff --git a/src/tqdm_publisher/_demo/_client.css b/src/tqdm_publisher/_demo/_client.css new file mode 100644 index 0000000..35c818c --- /dev/null +++ b/src/tqdm_publisher/_demo/_client.css @@ -0,0 +1,37 @@ + +html, body { + font-family: sans-serif; +} + +h1 { + margin: 0; + padding: 0; + font-size: 1.5rem; +} + +header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 20px; +} + +#bars { + display: flex; + justify-content: center; + align-items: center; + gap: 20px; + flex-wrap: wrap; +} + +.progress { + width: 100%; + height: 20px; + background-color: #ddd; +} + +.progress div { + height: 100%; + background-color: #4caf50; + width: 0%; +} \ No newline at end of file diff --git a/src/tqdm_publisher/_demo/_client.html b/src/tqdm_publisher/_demo/_client.html deleted file mode 100644 index 0e514eb..0000000 --- a/src/tqdm_publisher/_demo/_client.html +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - Concurrent Client Demo - - - - - - - - - -
-
-

tqdm_progress

- Create multiple progress bars to test concurrent subscriptions -
- - - -
- - -
- - diff --git a/src/tqdm_publisher/_demo/_client.js b/src/tqdm_publisher/_demo/_client.js deleted file mode 100644 index edf3670..0000000 --- a/src/tqdm_publisher/_demo/_client.js +++ /dev/null @@ -1,72 +0,0 @@ - -// Grab bar container from HTML -const barContainer = document.querySelector('#bars'); - -// Create a progress bar and append it to the bar container -const createProgressBar = () => { - const element = document.createElement('div'); - element.classList.add('progress'); - const progress = document.createElement('div'); - element.appendChild(progress); - barContainer.appendChild(element); - return { element, progress }; -} - -// Create a simple WebSocket client wrapper class -class ProgressClient { - - #connect = (props = {}) => { - - const { - onopen = () => {}, - onclose = () => {}, - onmessage = () => {} - } = props; - - this.socket = new WebSocket('ws://localhost:8000'); - this.socket.addEventListener('open', onopen); - - // Attempt to reconnect every second if the connection is closed - this.socket.addEventListener('close', () => { - onclose(); - setTimeout(() => this.#connect(props), 1000); - }); - - this.socket.addEventListener('message', onmessage); - } - - constructor(props) { - this.#connect(props); - } - - close() { - this.socket.close(); - } - -} - - -const bars = {} // Track progress bars - - -// Update the specified progress bar when a message is received from the server -const onProgressUpdate = (event) => { - const { progress_bar_id, format_dict } = JSON.parse(event.data); - bars[progress_bar_id].style.width = 100 * (format_dict.n / format_dict.total) + '%'; -} - -// Create a new WebSocket client -const client = new ProgressClient({ onmessage: onProgressUpdate }); - -// Declare that the HTML Button should create a new progress bar when clicked -const button = document.querySelector('button'); -button.addEventListener('click', () => { - const { element, progress } = createProgressBar(); // Create a progress bar - - barContainer.appendChild(element); // Render the progress bar - - const progress_bar_id = Math.random().toString(36).substring(7); // Create a unique ID for the progress bar - bars[progress_bar_id] = progress; // Track the progress bar - - client.socket.send(JSON.stringify({ command: 'start', progress_bar_id })); // Send a message to the server to start the progress bar -}) diff --git a/src/tqdm_publisher/_demo/_demo_command_line_interface.py b/src/tqdm_publisher/_demo/_demo_command_line_interface.py index 2f1764c..2a6c0b7 100644 --- a/src/tqdm_publisher/_demo/_demo_command_line_interface.py +++ b/src/tqdm_publisher/_demo/_demo_command_line_interface.py @@ -3,12 +3,18 @@ import sys from pathlib import Path -from ._server import run_demo +import webbrowser + +DEMOS = { + "single": "_single", + "multiple": "_multiple", + # "parallel": "_parallel", +} DEMO_BASE_FOLDER_PATH = Path(__file__).parent -CLIENT_FILE_PATH = DEMO_BASE_FOLDER_PATH / "_client.html" -SERVER_FILE_PATH = DEMO_BASE_FOLDER_PATH / "_server.py" +CLIENT_PORT = 1234 +RELATIVE_DEMO_BASE_FOLDER_PATH = DEMO_BASE_FOLDER_PATH.relative_to(Path.cwd()) def _command_line_interface(): @@ -30,14 +36,23 @@ def _command_line_interface(): print(f"No flags are accepted at this time, but flags {flags_list} were received.") return - if command == "demo": - # For convenience - automatically pop-up a browser window on the locally hosted HTML page - if sys.platform == "win32": - os.system(f'start "" "{CLIENT_FILE_PATH}"') - else: - subprocess.run(["open", CLIENT_FILE_PATH]) + if command in DEMOS: + + subpath = DEMOS[command] + + # if command == "parallel": + # client_relative_path = Path(subpath) / "_client.py" + # subprocess.Popen(['python', str(DEMO_BASE_FOLDER_PATH / subpath / "_server.py")]) + # subprocess.Popen(['python', str(DEMO_BASE_FOLDER_PATH / subpath / "_client.py")]) + + # else: + + client_relative_path = Path(subpath) / "_client.html" + subprocess.Popen(['python', '-m', 'http.server', str(CLIENT_PORT), "-d", DEMO_BASE_FOLDER_PATH]) + + webbrowser.open_new_tab(f"http://localhost:{CLIENT_PORT}/{client_relative_path}") - run_demo() + subprocess.run(['python', str(DEMO_BASE_FOLDER_PATH / subpath / "_server.py")]) else: print(f"{command} is an invalid command.") diff --git a/src/tqdm_publisher/_demo/_multiple/_client.html b/src/tqdm_publisher/_demo/_multiple/_client.html new file mode 100644 index 0000000..a59ec1b --- /dev/null +++ b/src/tqdm_publisher/_demo/_multiple/_client.html @@ -0,0 +1,36 @@ + + + + + + + + + + + + + Multiple Bar Demo + + + + + + + + + +
+
+

tqdm_progress

+ Create multiple progress bars to test concurrent subscriptions +
+ + + +
+ + +
+ + diff --git a/src/tqdm_publisher/_demo/_multiple/_client.js b/src/tqdm_publisher/_demo/_multiple/_client.js new file mode 100644 index 0000000..29b5da9 --- /dev/null +++ b/src/tqdm_publisher/_demo/_multiple/_client.js @@ -0,0 +1,21 @@ +import { WebSocketManager } from '../utils/WebSocketManager.js'; +import { createProgressBar } from '../utils/elements.js'; + +const bars = {} // Track progress bars + +// Update the specified progress bar when a message is received from the server +const onProgressUpdate = (event) => { + const { request_id, format_dict } = JSON.parse(event.data); + bars[request_id].style.width = 100 * (format_dict.n / format_dict.total) + '%'; +} + +// Create a new WebSocket client +const client = new WebSocketManager({ onmessage: onProgressUpdate }); + +// Declare that the HTML Button should create a new progress bar when clicked +const button = document.querySelector('button'); +button.addEventListener('click', () => { + const request_id = Math.random().toString(36).substring(7); // Create a unique ID for the progress bar + bars[request_id] = createProgressBar(); // Create and render a progress bar + client.socket.send(JSON.stringify({ command: 'start', request_id })); // Send a message to the server to start the progress bar +}) diff --git a/src/tqdm_publisher/_demo/_multiple/_server.py b/src/tqdm_publisher/_demo/_multiple/_server.py new file mode 100644 index 0000000..856b679 --- /dev/null +++ b/src/tqdm_publisher/_demo/_multiple/_server.py @@ -0,0 +1,69 @@ +import asyncio +import json +import threading +import time + +import websockets + +import tqdm_publisher + + +async def handler(websocket: websockets.WebSocketServerProtocol) -> None: + """Handle messages from the client and manage the client connections.""" + + class WebSocketProgressBar(threading.Thread): + + def __init__(self, request_id: str): + super().__init__() + self.request_id = request_id + + def update(self, format_dict) -> None: + """ + This is the function that will run on every update of the TQDM object. + + It will forward the progress to the client. + """ + asyncio.run( + websocket.send(message=json.dumps(obj=dict(request_id=self.request_id, format_dict=format_dict))) + ) + + def run(self): + """ + Emulate running the specified number of tasks by sleeping the specified amount of time on each iteration. + + Defaults are chosen for a deterministic and regular update period of one second for a total time of one minute. + """ + all_task_durations_in_seconds = [.1 for _ in range(100)] # Ten seconds of one hundred tasks + progress_bar = self.progress_bar = tqdm_publisher.TQDMPublisher(iterable=all_task_durations_in_seconds) + progress_bar.subscribe(callback=self.update) + + for task_duration in progress_bar: + time.sleep(task_duration) + + # def start(self): + # thread = threading.Thread(target=self.run_progress_bar) + # thread.start() + + + # Wait for messages from the client + async for message in websocket: + message_from_client = json.loads(message) + + if message_from_client["command"] == "start": + progress_bar = WebSocketProgressBar(request_id=message_from_client["request_id"]) + progress_bar.start() + + +async def spawn_server() -> None: + """Spawn the server asynchronously.""" + async with websockets.serve(ws_handler=handler, host="", port=8000): + await asyncio.Future() + + +def run_demo() -> None: + """Trigger the execution of the asynchronous spawn.""" + asyncio.run(spawn_server()) + + +if __name__ == "__main__": + run_demo() \ No newline at end of file diff --git a/src/tqdm_publisher/_demo/_single_bar/_client.html b/src/tqdm_publisher/_demo/_single_bar/_client.html new file mode 100644 index 0000000..9ce3b3d --- /dev/null +++ b/src/tqdm_publisher/_demo/_single_bar/_client.html @@ -0,0 +1,25 @@ + + + + + + Single Bar Demo + + + + + +
+
+

tqdm_progress

+ Single Bar Demo +
+ + + +
+ + +
+ + diff --git a/src/tqdm_publisher/_demo/_single_bar/_client.js b/src/tqdm_publisher/_demo/_single_bar/_client.js new file mode 100644 index 0000000..1596925 --- /dev/null +++ b/src/tqdm_publisher/_demo/_single_bar/_client.js @@ -0,0 +1,25 @@ +import { WebSocketManager } from '../utils/WebSocketManager.js'; +import { createProgressBar } from '../utils/elements.js'; + + +const bar = createProgressBar(); // Create and render a progress bar + +// Update the specified progress bar when a message is received from the server +const onProgressUpdate = (event) => { + const { format_dict } = JSON.parse(event.data); + const ratio = format_dict.n / format_dict.total; + bar.style.width = `${100 * ratio}%`; + + if (ratio === 1) button.removeAttribute('disabled'); // Enable the button when the progress bar is complete +} + +// Create a new WebSocket client +const client = new WebSocketManager({ onmessage: onProgressUpdate }); + +// Declare that the HTML Button should create a new progress bar when clicked +const button = document.querySelector('button'); +button.addEventListener('click', () => { + button.setAttribute('disabled', true); // Disable the button to prevent multiple progress bars from being created + bar.style.width = 0; // Reset the progress bar + client.socket.send(JSON.stringify({ command: 'start' })); // Send a message to the server to start the progress bar +}) diff --git a/src/tqdm_publisher/_demo/_server.py b/src/tqdm_publisher/_demo/_single_bar/_server.py similarity index 62% rename from src/tqdm_publisher/_demo/_server.py rename to src/tqdm_publisher/_demo/_single_bar/_server.py index ac821dc..ff3da61 100644 --- a/src/tqdm_publisher/_demo/_server.py +++ b/src/tqdm_publisher/_demo/_single_bar/_server.py @@ -8,13 +8,13 @@ import tqdm_publisher -def start_progress_bar(*, progress_bar_id: str, client_callback: callable) -> None: +def start_progress_bar(*, progress_callback: callable) -> None: """ Emulate running the specified number of tasks by sleeping the specified amount of time on each iteration. Defaults are chosen for a deterministic and regular update period of one second for a total time of one minute. """ - all_task_durations_in_seconds = [1.0 for _ in range(60)] # One minute at one second per update + all_task_durations_in_seconds = [.1 for _ in range(100)] # Ten seconds of one hundred tasks progress_bar = tqdm_publisher.TQDMPublisher(iterable=all_task_durations_in_seconds) def run_function_on_progress_update(format_dict: dict) -> None: @@ -25,9 +25,11 @@ def run_function_on_progress_update(format_dict: dict) -> None: on outside parameters must be achieved by defining those fields at an outer scope and defining this server-specific callback inside the local scope. - In this demo, we will execute the `client_callback` whose protocol is known only to the WebSocketHandler. + In this demo, we will execute the `progress_callback` whose protocol is known only to the WebSocketHandler. + + This specifically requires the `id` of the progress bar and the `format_dict` of the TQDM instance. """ - client_callback(progress_bar_id=progress_bar_id, format_dict=format_dict) + progress_callback(id=progress_bar.id, format_dict=format_dict) progress_bar.subscribe(callback=run_function_on_progress_update) @@ -35,29 +37,33 @@ def run_function_on_progress_update(format_dict: dict) -> None: time.sleep(task_duration) -async def handler(websocket: websockets.WebSocketServerProtocol) -> None: - """Handle messages from the client and manage the client connections.""" +def send_message_to_client(*, websocket: websockets.WebSocketServerProtocol, message: dict) -> None: + """ + Send a message to a specific client. - def forward_progress_to_client(*, progress_bar_id: str, format_dict: dict) -> None: - """ - This is the function that will run on every update of the TQDM object. + This expects a WebSocket connection and a message (dict) to send. + """ - It will forward the progress to the client. - """ - asyncio.run( - websocket.send(message=json.dumps(obj=dict(progress_bar_id=progress_bar_id, format_dict=format_dict))) - ) + asyncio.run( + websocket.send(message=json.dumps(obj=message)) + ) + +async def handler(websocket: websockets.WebSocketServerProtocol) -> None: + """Handle messages from the client and manage the client connections.""" # Wait for messages from the client async for message in websocket: message_from_client = json.loads(message) if message_from_client["command"] == "start": + + # Start the progress bar in a separate thread thread = threading.Thread( target=start_progress_bar, + + # On each update of the progress bar, send this update to the requesting client kwargs=dict( - progress_bar_id=message_from_client["progress_bar_id"], - client_callback=forward_progress_to_client, + progress_callback=lambda id, format_dict: send_message_to_client(websocket, dict(id=id, format_dict=format_dict)) ), ) thread.start() @@ -72,3 +78,7 @@ async def spawn_server() -> None: def run_demo() -> None: """Trigger the execution of the asynchronous spawn.""" asyncio.run(spawn_server()) + + +if __name__ == "__main__": + run_demo() \ No newline at end of file diff --git a/src/tqdm_publisher/_demo/utils/WebSocketManager.js b/src/tqdm_publisher/_demo/utils/WebSocketManager.js new file mode 100644 index 0000000..50c845a --- /dev/null +++ b/src/tqdm_publisher/_demo/utils/WebSocketManager.js @@ -0,0 +1,32 @@ +// Create a simple WebSocket client wrapper class +export class WebSocketManager { + + #connect = (props = {}) => { + + const { + onopen = () => {}, + onclose = () => {}, + onmessage = () => {} + } = props; + + this.socket = new WebSocket('ws://localhost:8000'); + this.socket.addEventListener('open', onopen); + + // Attempt to reconnect every second if the connection is closed + this.socket.addEventListener('close', () => { + onclose(); + setTimeout(() => this.#connect(props), 1000); + }); + + this.socket.addEventListener('message', onmessage); + } + + constructor(props) { + this.#connect(props); + } + + close() { + this.socket.close(); + } + +} diff --git a/src/tqdm_publisher/_demo/utils/elements.js b/src/tqdm_publisher/_demo/utils/elements.js new file mode 100644 index 0000000..f316bb1 --- /dev/null +++ b/src/tqdm_publisher/_demo/utils/elements.js @@ -0,0 +1,12 @@ + +const barContainer = document.querySelector('#bars'); + +// Create a progress bar and append it to the bar container +export const createProgressBar = () => { + const element = document.createElement('div'); + element.classList.add('progress'); + const progress = document.createElement('div'); + element.appendChild(progress); + barContainer.appendChild(element); // Render the progress bar + return progress; +} diff --git a/src/tqdm_publisher/_publisher.py b/src/tqdm_publisher/_publisher.py index acd723d..d5cc3b3 100644 --- a/src/tqdm_publisher/_publisher.py +++ b/src/tqdm_publisher/_publisher.py @@ -7,6 +7,7 @@ class TQDMPublisher(base_tqdm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + self.id = str(uuid4()) self.callbacks = {} # Override the update method to run callbacks From 3806458aa7665d63e575d075d4e5561a5822452e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 17:15:40 +0000 Subject: [PATCH 02/13] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/tqdm_publisher/_demo/_client.css | 2 +- .../_demo/_demo_command_line_interface.py | 9 ++++----- src/tqdm_publisher/_demo/_multiple/_server.py | 5 ++--- src/tqdm_publisher/_demo/_single_bar/_server.py | 14 +++++++------- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/tqdm_publisher/_demo/_client.css b/src/tqdm_publisher/_demo/_client.css index 35c818c..5134a39 100644 --- a/src/tqdm_publisher/_demo/_client.css +++ b/src/tqdm_publisher/_demo/_client.css @@ -34,4 +34,4 @@ header { height: 100%; background-color: #4caf50; width: 0%; -} \ No newline at end of file +} diff --git a/src/tqdm_publisher/_demo/_demo_command_line_interface.py b/src/tqdm_publisher/_demo/_demo_command_line_interface.py index 2a6c0b7..22b5bdc 100644 --- a/src/tqdm_publisher/_demo/_demo_command_line_interface.py +++ b/src/tqdm_publisher/_demo/_demo_command_line_interface.py @@ -1,9 +1,8 @@ import os import subprocess import sys -from pathlib import Path - import webbrowser +from pathlib import Path DEMOS = { "single": "_single", @@ -44,15 +43,15 @@ def _command_line_interface(): # client_relative_path = Path(subpath) / "_client.py" # subprocess.Popen(['python', str(DEMO_BASE_FOLDER_PATH / subpath / "_server.py")]) # subprocess.Popen(['python', str(DEMO_BASE_FOLDER_PATH / subpath / "_client.py")]) - + # else: client_relative_path = Path(subpath) / "_client.html" - subprocess.Popen(['python', '-m', 'http.server', str(CLIENT_PORT), "-d", DEMO_BASE_FOLDER_PATH]) + subprocess.Popen(["python", "-m", "http.server", str(CLIENT_PORT), "-d", DEMO_BASE_FOLDER_PATH]) webbrowser.open_new_tab(f"http://localhost:{CLIENT_PORT}/{client_relative_path}") - subprocess.run(['python', str(DEMO_BASE_FOLDER_PATH / subpath / "_server.py")]) + subprocess.run(["python", str(DEMO_BASE_FOLDER_PATH / subpath / "_server.py")]) else: print(f"{command} is an invalid command.") diff --git a/src/tqdm_publisher/_demo/_multiple/_server.py b/src/tqdm_publisher/_demo/_multiple/_server.py index 856b679..d56b06f 100644 --- a/src/tqdm_publisher/_demo/_multiple/_server.py +++ b/src/tqdm_publisher/_demo/_multiple/_server.py @@ -33,7 +33,7 @@ def run(self): Defaults are chosen for a deterministic and regular update period of one second for a total time of one minute. """ - all_task_durations_in_seconds = [.1 for _ in range(100)] # Ten seconds of one hundred tasks + all_task_durations_in_seconds = [0.1 for _ in range(100)] # Ten seconds of one hundred tasks progress_bar = self.progress_bar = tqdm_publisher.TQDMPublisher(iterable=all_task_durations_in_seconds) progress_bar.subscribe(callback=self.update) @@ -44,7 +44,6 @@ def run(self): # thread = threading.Thread(target=self.run_progress_bar) # thread.start() - # Wait for messages from the client async for message in websocket: message_from_client = json.loads(message) @@ -66,4 +65,4 @@ def run_demo() -> None: if __name__ == "__main__": - run_demo() \ No newline at end of file + run_demo() diff --git a/src/tqdm_publisher/_demo/_single_bar/_server.py b/src/tqdm_publisher/_demo/_single_bar/_server.py index ff3da61..ca2b02d 100644 --- a/src/tqdm_publisher/_demo/_single_bar/_server.py +++ b/src/tqdm_publisher/_demo/_single_bar/_server.py @@ -14,7 +14,7 @@ def start_progress_bar(*, progress_callback: callable) -> None: Defaults are chosen for a deterministic and regular update period of one second for a total time of one minute. """ - all_task_durations_in_seconds = [.1 for _ in range(100)] # Ten seconds of one hundred tasks + all_task_durations_in_seconds = [0.1 for _ in range(100)] # Ten seconds of one hundred tasks progress_bar = tqdm_publisher.TQDMPublisher(iterable=all_task_durations_in_seconds) def run_function_on_progress_update(format_dict: dict) -> None: @@ -44,9 +44,8 @@ def send_message_to_client(*, websocket: websockets.WebSocketServerProtocol, mes This expects a WebSocket connection and a message (dict) to send. """ - asyncio.run( - websocket.send(message=json.dumps(obj=message)) - ) + asyncio.run(websocket.send(message=json.dumps(obj=message))) + async def handler(websocket: websockets.WebSocketServerProtocol) -> None: """Handle messages from the client and manage the client connections.""" @@ -60,10 +59,11 @@ async def handler(websocket: websockets.WebSocketServerProtocol) -> None: # Start the progress bar in a separate thread thread = threading.Thread( target=start_progress_bar, - # On each update of the progress bar, send this update to the requesting client kwargs=dict( - progress_callback=lambda id, format_dict: send_message_to_client(websocket, dict(id=id, format_dict=format_dict)) + progress_callback=lambda id, format_dict: send_message_to_client( + websocket, dict(id=id, format_dict=format_dict) + ) ), ) thread.start() @@ -81,4 +81,4 @@ def run_demo() -> None: if __name__ == "__main__": - run_demo() \ No newline at end of file + run_demo() From c258d18594879ec71c4b4ab494090b716fefe09d Mon Sep 17 00:00:00 2001 From: Garrett Michael Flynn Date: Thu, 14 Mar 2024 11:13:05 -0700 Subject: [PATCH 03/13] Apply suggestions from code review Co-authored-by: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> --- src/tqdm_publisher/_demo/_multiple/_server.py | 4 ++-- src/tqdm_publisher/_demo/_single_bar/_server.py | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/tqdm_publisher/_demo/_multiple/_server.py b/src/tqdm_publisher/_demo/_multiple/_server.py index d56b06f..f78dee1 100644 --- a/src/tqdm_publisher/_demo/_multiple/_server.py +++ b/src/tqdm_publisher/_demo/_multiple/_server.py @@ -33,7 +33,7 @@ def run(self): Defaults are chosen for a deterministic and regular update period of one second for a total time of one minute. """ - all_task_durations_in_seconds = [0.1 for _ in range(100)] # Ten seconds of one hundred tasks + all_task_durations_in_seconds = [1.0 for _ in range(10)] # Ten seconds at one task per second progress_bar = self.progress_bar = tqdm_publisher.TQDMPublisher(iterable=all_task_durations_in_seconds) progress_bar.subscribe(callback=self.update) @@ -59,7 +59,7 @@ async def spawn_server() -> None: await asyncio.Future() -def run_demo() -> None: +def run_multiple_bar_demo() -> None: """Trigger the execution of the asynchronous spawn.""" asyncio.run(spawn_server()) diff --git a/src/tqdm_publisher/_demo/_single_bar/_server.py b/src/tqdm_publisher/_demo/_single_bar/_server.py index ca2b02d..899a49f 100644 --- a/src/tqdm_publisher/_demo/_single_bar/_server.py +++ b/src/tqdm_publisher/_demo/_single_bar/_server.py @@ -14,7 +14,7 @@ def start_progress_bar(*, progress_callback: callable) -> None: Defaults are chosen for a deterministic and regular update period of one second for a total time of one minute. """ - all_task_durations_in_seconds = [0.1 for _ in range(100)] # Ten seconds of one hundred tasks + all_task_durations_in_seconds = [1.0 for _ in range(10)] # Ten seconds at one second per task progress_bar = tqdm_publisher.TQDMPublisher(iterable=all_task_durations_in_seconds) def run_function_on_progress_update(format_dict: dict) -> None: @@ -75,10 +75,8 @@ async def spawn_server() -> None: await asyncio.Future() -def run_demo() -> None: +def run_single_bar_demo() -> None: """Trigger the execution of the asynchronous spawn.""" asyncio.run(spawn_server()) -if __name__ == "__main__": - run_demo() From 49853f0c1c9aa205d21c8120f78d93ed0c0e2409 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 18:13:17 +0000 Subject: [PATCH 04/13] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/tqdm_publisher/_demo/_single_bar/_server.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/tqdm_publisher/_demo/_single_bar/_server.py b/src/tqdm_publisher/_demo/_single_bar/_server.py index 899a49f..2a85256 100644 --- a/src/tqdm_publisher/_demo/_single_bar/_server.py +++ b/src/tqdm_publisher/_demo/_single_bar/_server.py @@ -78,5 +78,3 @@ async def spawn_server() -> None: def run_single_bar_demo() -> None: """Trigger the execution of the asynchronous spawn.""" asyncio.run(spawn_server()) - - From 8160b8a1d4a0b5050ef493328be3aa3de4240f0d Mon Sep 17 00:00:00 2001 From: Garrett Michael Flynn Date: Thu, 14 Mar 2024 11:15:28 -0700 Subject: [PATCH 05/13] Run server functions directly --- .../_demo/_demo_command_line_interface.py | 20 ++++++++++++++----- src/tqdm_publisher/_demo/_multiple/_server.py | 6 +----- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/tqdm_publisher/_demo/_demo_command_line_interface.py b/src/tqdm_publisher/_demo/_demo_command_line_interface.py index 22b5bdc..3cec14a 100644 --- a/src/tqdm_publisher/_demo/_demo_command_line_interface.py +++ b/src/tqdm_publisher/_demo/_demo_command_line_interface.py @@ -4,9 +4,19 @@ import webbrowser from pathlib import Path +from tqdm_publisher._demo._single_bar._server import run_single_bar_demo +from tqdm_publisher._demo._multiple._server import run_multiple_bar_demo + + DEMOS = { - "single": "_single", - "multiple": "_multiple", + "single": dict( + subpath="_single_bar", + server=run_single_bar_demo + ), + "multiple": dict( + subpath="_multiple", + server=run_multiple_bar_demo + ) # "parallel": "_parallel", } @@ -37,7 +47,7 @@ def _command_line_interface(): if command in DEMOS: - subpath = DEMOS[command] + demo_info = DEMOS[command] # if command == "parallel": # client_relative_path = Path(subpath) / "_client.py" @@ -46,12 +56,12 @@ def _command_line_interface(): # else: - client_relative_path = Path(subpath) / "_client.html" + client_relative_path = Path(demo_info["subpath"]) / "_client.html" subprocess.Popen(["python", "-m", "http.server", str(CLIENT_PORT), "-d", DEMO_BASE_FOLDER_PATH]) webbrowser.open_new_tab(f"http://localhost:{CLIENT_PORT}/{client_relative_path}") - subprocess.run(["python", str(DEMO_BASE_FOLDER_PATH / subpath / "_server.py")]) + demo_info["server"]() else: print(f"{command} is an invalid command.") diff --git a/src/tqdm_publisher/_demo/_multiple/_server.py b/src/tqdm_publisher/_demo/_multiple/_server.py index f78dee1..60a8ff3 100644 --- a/src/tqdm_publisher/_demo/_multiple/_server.py +++ b/src/tqdm_publisher/_demo/_multiple/_server.py @@ -61,8 +61,4 @@ async def spawn_server() -> None: def run_multiple_bar_demo() -> None: """Trigger the execution of the asynchronous spawn.""" - asyncio.run(spawn_server()) - - -if __name__ == "__main__": - run_demo() + asyncio.run(spawn_server()) \ No newline at end of file From 713ec0f4576fb8a980e3db2b813d8dd232d36593 Mon Sep 17 00:00:00 2001 From: Garrett Michael Flynn Date: Thu, 14 Mar 2024 11:16:31 -0700 Subject: [PATCH 06/13] Update _demo_command_line_interface.py --- src/tqdm_publisher/_demo/_demo_command_line_interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tqdm_publisher/_demo/_demo_command_line_interface.py b/src/tqdm_publisher/_demo/_demo_command_line_interface.py index 3cec14a..f0586bf 100644 --- a/src/tqdm_publisher/_demo/_demo_command_line_interface.py +++ b/src/tqdm_publisher/_demo/_demo_command_line_interface.py @@ -7,6 +7,7 @@ from tqdm_publisher._demo._single_bar._server import run_single_bar_demo from tqdm_publisher._demo._multiple._server import run_multiple_bar_demo +CLIENT_PORT = 1234 DEMOS = { "single": dict( @@ -22,7 +23,6 @@ DEMO_BASE_FOLDER_PATH = Path(__file__).parent -CLIENT_PORT = 1234 RELATIVE_DEMO_BASE_FOLDER_PATH = DEMO_BASE_FOLDER_PATH.relative_to(Path.cwd()) From 56676d906b900c9dc71526aabaa272647b7b06c8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 18:17:00 +0000 Subject: [PATCH 07/13] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../_demo/_demo_command_line_interface.py | 12 +++--------- src/tqdm_publisher/_demo/_multiple/_server.py | 2 +- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/tqdm_publisher/_demo/_demo_command_line_interface.py b/src/tqdm_publisher/_demo/_demo_command_line_interface.py index f0586bf..287e0cf 100644 --- a/src/tqdm_publisher/_demo/_demo_command_line_interface.py +++ b/src/tqdm_publisher/_demo/_demo_command_line_interface.py @@ -4,20 +4,14 @@ import webbrowser from pathlib import Path -from tqdm_publisher._demo._single_bar._server import run_single_bar_demo from tqdm_publisher._demo._multiple._server import run_multiple_bar_demo +from tqdm_publisher._demo._single_bar._server import run_single_bar_demo CLIENT_PORT = 1234 DEMOS = { - "single": dict( - subpath="_single_bar", - server=run_single_bar_demo - ), - "multiple": dict( - subpath="_multiple", - server=run_multiple_bar_demo - ) + "single": dict(subpath="_single_bar", server=run_single_bar_demo), + "multiple": dict(subpath="_multiple", server=run_multiple_bar_demo), # "parallel": "_parallel", } diff --git a/src/tqdm_publisher/_demo/_multiple/_server.py b/src/tqdm_publisher/_demo/_multiple/_server.py index 60a8ff3..69f9a8c 100644 --- a/src/tqdm_publisher/_demo/_multiple/_server.py +++ b/src/tqdm_publisher/_demo/_multiple/_server.py @@ -61,4 +61,4 @@ async def spawn_server() -> None: def run_multiple_bar_demo() -> None: """Trigger the execution of the asynchronous spawn.""" - asyncio.run(spawn_server()) \ No newline at end of file + asyncio.run(spawn_server()) From c61a90eaf4d57b7c7e87d56dfcd943ed329b12fe Mon Sep 17 00:00:00 2001 From: Garrett Michael Flynn Date: Thu, 14 Mar 2024 11:27:29 -0700 Subject: [PATCH 08/13] Rename demos and fix single demo --- src/tqdm_publisher/_demo/_demo_command_line_interface.py | 4 ++-- src/tqdm_publisher/_demo/_single_bar/_server.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/tqdm_publisher/_demo/_demo_command_line_interface.py b/src/tqdm_publisher/_demo/_demo_command_line_interface.py index f0586bf..33f1c9b 100644 --- a/src/tqdm_publisher/_demo/_demo_command_line_interface.py +++ b/src/tqdm_publisher/_demo/_demo_command_line_interface.py @@ -10,11 +10,11 @@ CLIENT_PORT = 1234 DEMOS = { - "single": dict( + "single_demo": dict( subpath="_single_bar", server=run_single_bar_demo ), - "multiple": dict( + "multiple_demo": dict( subpath="_multiple", server=run_multiple_bar_demo ) diff --git a/src/tqdm_publisher/_demo/_single_bar/_server.py b/src/tqdm_publisher/_demo/_single_bar/_server.py index 2a85256..67a5f91 100644 --- a/src/tqdm_publisher/_demo/_single_bar/_server.py +++ b/src/tqdm_publisher/_demo/_single_bar/_server.py @@ -62,7 +62,8 @@ async def handler(websocket: websockets.WebSocketServerProtocol) -> None: # On each update of the progress bar, send this update to the requesting client kwargs=dict( progress_callback=lambda id, format_dict: send_message_to_client( - websocket, dict(id=id, format_dict=format_dict) + websocket=websocket, + message=dict(id=id, format_dict=format_dict) ) ), ) From 69801b79f67f764837c30a5ac1ebcee73316c802 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 18:28:55 +0000 Subject: [PATCH 09/13] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../_demo/_demo_command_line_interface.py | 10 ++-------- src/tqdm_publisher/_demo/_single_bar/_server.py | 3 +-- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/tqdm_publisher/_demo/_demo_command_line_interface.py b/src/tqdm_publisher/_demo/_demo_command_line_interface.py index ee0e1d1..bf8da52 100644 --- a/src/tqdm_publisher/_demo/_demo_command_line_interface.py +++ b/src/tqdm_publisher/_demo/_demo_command_line_interface.py @@ -10,14 +10,8 @@ CLIENT_PORT = 1234 DEMOS = { - "single_demo": dict( - subpath="_single_bar", - server=run_single_bar_demo - ), - "multiple_demo": dict( - subpath="_multiple", - server=run_multiple_bar_demo - ) + "single_demo": dict(subpath="_single_bar", server=run_single_bar_demo), + "multiple_demo": dict(subpath="_multiple", server=run_multiple_bar_demo), # "parallel": "_parallel", } diff --git a/src/tqdm_publisher/_demo/_single_bar/_server.py b/src/tqdm_publisher/_demo/_single_bar/_server.py index 67a5f91..c8e739b 100644 --- a/src/tqdm_publisher/_demo/_single_bar/_server.py +++ b/src/tqdm_publisher/_demo/_single_bar/_server.py @@ -62,8 +62,7 @@ async def handler(websocket: websockets.WebSocketServerProtocol) -> None: # On each update of the progress bar, send this update to the requesting client kwargs=dict( progress_callback=lambda id, format_dict: send_message_to_client( - websocket=websocket, - message=dict(id=id, format_dict=format_dict) + websocket=websocket, message=dict(id=id, format_dict=format_dict) ) ), ) From fd560dbfb9d1d293118147a4dfe9f47a85e6dcc7 Mon Sep 17 00:00:00 2001 From: Garrett Michael Flynn Date: Thu, 14 Mar 2024 11:47:56 -0700 Subject: [PATCH 10/13] Move demos and change commands --- pyproject.toml | 2 +- src/tqdm_publisher/{_demo => _demos}/__init__.py | 0 src/tqdm_publisher/{_demo => _demos}/_client.css | 0 .../{_demo => _demos}/_demo_command_line_interface.py | 10 +++++----- .../_multiple => _demos/_multiple_bars}/_client.html | 2 +- .../_multiple => _demos/_multiple_bars}/_client.js | 0 .../_multiple => _demos/_multiple_bars}/_server.py | 0 .../{_demo => _demos}/_single_bar/_client.html | 0 .../{_demo => _demos}/_single_bar/_client.js | 0 .../{_demo => _demos}/_single_bar/_server.py | 0 .../{_demo => _demos}/utils/WebSocketManager.js | 0 src/tqdm_publisher/{_demo => _demos}/utils/elements.js | 0 12 files changed, 7 insertions(+), 7 deletions(-) rename src/tqdm_publisher/{_demo => _demos}/__init__.py (100%) rename src/tqdm_publisher/{_demo => _demos}/_client.css (100%) rename src/tqdm_publisher/{_demo => _demos}/_demo_command_line_interface.py (88%) rename src/tqdm_publisher/{_demo/_multiple => _demos/_multiple_bars}/_client.html (88%) rename src/tqdm_publisher/{_demo/_multiple => _demos/_multiple_bars}/_client.js (100%) rename src/tqdm_publisher/{_demo/_multiple => _demos/_multiple_bars}/_server.py (100%) rename src/tqdm_publisher/{_demo => _demos}/_single_bar/_client.html (100%) rename src/tqdm_publisher/{_demo => _demos}/_single_bar/_client.js (100%) rename src/tqdm_publisher/{_demo => _demos}/_single_bar/_server.py (100%) rename src/tqdm_publisher/{_demo => _demos}/utils/WebSocketManager.js (100%) rename src/tqdm_publisher/{_demo => _demos}/utils/elements.js (100%) diff --git a/pyproject.toml b/pyproject.toml index 942e7fa..e0f9be2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,7 +52,7 @@ demo = [ "Bug Tracker" = "https://github.com/catalystneuro/tqdm_publisher/issues" [project.scripts] -tqdm_publisher = "tqdm_publisher._demo._demo_command_line_interface:_command_line_interface" +tqdm_publisher = "tqdm_publisher._demos._demo_command_line_interface:_command_line_interface" [tool.black] line-length = 120 diff --git a/src/tqdm_publisher/_demo/__init__.py b/src/tqdm_publisher/_demos/__init__.py similarity index 100% rename from src/tqdm_publisher/_demo/__init__.py rename to src/tqdm_publisher/_demos/__init__.py diff --git a/src/tqdm_publisher/_demo/_client.css b/src/tqdm_publisher/_demos/_client.css similarity index 100% rename from src/tqdm_publisher/_demo/_client.css rename to src/tqdm_publisher/_demos/_client.css diff --git a/src/tqdm_publisher/_demo/_demo_command_line_interface.py b/src/tqdm_publisher/_demos/_demo_command_line_interface.py similarity index 88% rename from src/tqdm_publisher/_demo/_demo_command_line_interface.py rename to src/tqdm_publisher/_demos/_demo_command_line_interface.py index ee0e1d1..a6bdf5d 100644 --- a/src/tqdm_publisher/_demo/_demo_command_line_interface.py +++ b/src/tqdm_publisher/_demos/_demo_command_line_interface.py @@ -4,18 +4,18 @@ import webbrowser from pathlib import Path -from tqdm_publisher._demo._multiple._server import run_multiple_bar_demo -from tqdm_publisher._demo._single_bar._server import run_single_bar_demo +from tqdm_publisher._demos._multiple_bars._server import run_multiple_bar_demo +from tqdm_publisher._demos._single_bar._server import run_single_bar_demo CLIENT_PORT = 1234 DEMOS = { - "single_demo": dict( + "demo_single": dict( subpath="_single_bar", server=run_single_bar_demo ), - "multiple_demo": dict( - subpath="_multiple", + "demo_multiple": dict( + subpath="_multiple_bars", server=run_multiple_bar_demo ) # "parallel": "_parallel", diff --git a/src/tqdm_publisher/_demo/_multiple/_client.html b/src/tqdm_publisher/_demos/_multiple_bars/_client.html similarity index 88% rename from src/tqdm_publisher/_demo/_multiple/_client.html rename to src/tqdm_publisher/_demos/_multiple_bars/_client.html index a59ec1b..e5fa184 100644 --- a/src/tqdm_publisher/_demo/_multiple/_client.html +++ b/src/tqdm_publisher/_demos/_multiple_bars/_client.html @@ -23,7 +23,7 @@

tqdm_progress

- Create multiple progress bars to test concurrent subscriptions + Click more than once to create multiple progress bars
diff --git a/src/tqdm_publisher/_demo/_multiple/_client.js b/src/tqdm_publisher/_demos/_multiple_bars/_client.js similarity index 100% rename from src/tqdm_publisher/_demo/_multiple/_client.js rename to src/tqdm_publisher/_demos/_multiple_bars/_client.js diff --git a/src/tqdm_publisher/_demo/_multiple/_server.py b/src/tqdm_publisher/_demos/_multiple_bars/_server.py similarity index 100% rename from src/tqdm_publisher/_demo/_multiple/_server.py rename to src/tqdm_publisher/_demos/_multiple_bars/_server.py diff --git a/src/tqdm_publisher/_demo/_single_bar/_client.html b/src/tqdm_publisher/_demos/_single_bar/_client.html similarity index 100% rename from src/tqdm_publisher/_demo/_single_bar/_client.html rename to src/tqdm_publisher/_demos/_single_bar/_client.html diff --git a/src/tqdm_publisher/_demo/_single_bar/_client.js b/src/tqdm_publisher/_demos/_single_bar/_client.js similarity index 100% rename from src/tqdm_publisher/_demo/_single_bar/_client.js rename to src/tqdm_publisher/_demos/_single_bar/_client.js diff --git a/src/tqdm_publisher/_demo/_single_bar/_server.py b/src/tqdm_publisher/_demos/_single_bar/_server.py similarity index 100% rename from src/tqdm_publisher/_demo/_single_bar/_server.py rename to src/tqdm_publisher/_demos/_single_bar/_server.py diff --git a/src/tqdm_publisher/_demo/utils/WebSocketManager.js b/src/tqdm_publisher/_demos/utils/WebSocketManager.js similarity index 100% rename from src/tqdm_publisher/_demo/utils/WebSocketManager.js rename to src/tqdm_publisher/_demos/utils/WebSocketManager.js diff --git a/src/tqdm_publisher/_demo/utils/elements.js b/src/tqdm_publisher/_demos/utils/elements.js similarity index 100% rename from src/tqdm_publisher/_demo/utils/elements.js rename to src/tqdm_publisher/_demos/utils/elements.js From 76f66cab834934a905153b018f19a36081a7bc0d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 18:49:23 +0000 Subject: [PATCH 11/13] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../_demos/_demo_command_line_interface.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/tqdm_publisher/_demos/_demo_command_line_interface.py b/src/tqdm_publisher/_demos/_demo_command_line_interface.py index a6bdf5d..c354ef7 100644 --- a/src/tqdm_publisher/_demos/_demo_command_line_interface.py +++ b/src/tqdm_publisher/_demos/_demo_command_line_interface.py @@ -10,14 +10,8 @@ CLIENT_PORT = 1234 DEMOS = { - "demo_single": dict( - subpath="_single_bar", - server=run_single_bar_demo - ), - "demo_multiple": dict( - subpath="_multiple_bars", - server=run_multiple_bar_demo - ) + "demo_single": dict(subpath="_single_bar", server=run_single_bar_demo), + "demo_multiple": dict(subpath="_multiple_bars", server=run_multiple_bar_demo), # "parallel": "_parallel", } From e56fc6a11a75d596751acc4d990b4e4d05555815 Mon Sep 17 00:00:00 2001 From: Garrett Michael Flynn Date: Thu, 14 Mar 2024 11:56:49 -0700 Subject: [PATCH 12/13] Apply suggestions from code review Co-authored-by: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> --- src/tqdm_publisher/_demos/_multiple_bars/_client.html | 4 ++-- src/tqdm_publisher/_demos/_single_bar/_client.html | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tqdm_publisher/_demos/_multiple_bars/_client.html b/src/tqdm_publisher/_demos/_multiple_bars/_client.html index e5fa184..b47be65 100644 --- a/src/tqdm_publisher/_demos/_multiple_bars/_client.html +++ b/src/tqdm_publisher/_demos/_multiple_bars/_client.html @@ -22,8 +22,8 @@
-

tqdm_progress

- Click more than once to create multiple progress bars +

TQDM Publisher Demo: Multiple Bars

+ Click the button more than once to create multiple progress bars
diff --git a/src/tqdm_publisher/_demos/_single_bar/_client.html b/src/tqdm_publisher/_demos/_single_bar/_client.html index 9ce3b3d..4f21f4b 100644 --- a/src/tqdm_publisher/_demos/_single_bar/_client.html +++ b/src/tqdm_publisher/_demos/_single_bar/_client.html @@ -11,8 +11,8 @@
-

tqdm_progress

- Single Bar Demo +

TQDM Publisher Demo: Single Bar

+ Click the button to start the progress bar
From 85e7a6f94be7489d9cc93f767d1e8c6c99323b2d Mon Sep 17 00:00:00 2001 From: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com> Date: Thu, 14 Mar 2024 16:35:38 -0400 Subject: [PATCH 13/13] Improvements to simplified demos (#44) * enhance documentation; remove remaining anonymous callables; better variable names * finish docstring --- .../_demos/_multiple_bars/_server.py | 34 ++++++++++++------- .../_demos/_single_bar/_server.py | 32 +++++++---------- src/tqdm_publisher/_publisher.py | 2 +- 3 files changed, 35 insertions(+), 33 deletions(-) diff --git a/src/tqdm_publisher/_demos/_multiple_bars/_server.py b/src/tqdm_publisher/_demos/_multiple_bars/_server.py index 69f9a8c..248a2ed 100644 --- a/src/tqdm_publisher/_demos/_multiple_bars/_server.py +++ b/src/tqdm_publisher/_demos/_multiple_bars/_server.py @@ -11,46 +11,56 @@ async def handler(websocket: websockets.WebSocketServerProtocol) -> None: """Handle messages from the client and manage the client connections.""" - class WebSocketProgressBar(threading.Thread): + class WebSocketProgressBar: + """ + This is similar to the `send_progress_update_to_client` function from the single bar demo server. + + The translation of this approach into a scoped class definition is merely to showcase an alternative approach + of the execution. + """ def __init__(self, request_id: str): super().__init__() self.request_id = request_id - def update(self, format_dict) -> None: + def update(self, format_dict: dict) -> None: """ This is the function that will run on every update of the TQDM object. It will forward the progress to the client. """ asyncio.run( - websocket.send(message=json.dumps(obj=dict(request_id=self.request_id, format_dict=format_dict))) + websocket.send(message=json.dumps(obj=dict(format_dict=format_dict, request_id=self.request_id))) ) def run(self): """ Emulate running the specified number of tasks by sleeping the specified amount of time on each iteration. - Defaults are chosen for a deterministic and regular update period of one second for a total time of one minute. + Defaults are chosen for a deterministic and regular update period of one second for a total time of + seconds per bar. + + This is similar to the `start_progress_bar` function from the single bar demo server. This is simply a + showcase of an alternative approach to defining and scoping the execution. """ all_task_durations_in_seconds = [1.0 for _ in range(10)] # Ten seconds at one task per second - progress_bar = self.progress_bar = tqdm_publisher.TQDMPublisher(iterable=all_task_durations_in_seconds) - progress_bar.subscribe(callback=self.update) + self.progress_bar = tqdm_publisher.TQDMPublisher(iterable=all_task_durations_in_seconds) + self.progress_bar.subscribe(callback=self.update) - for task_duration in progress_bar: + for task_duration in self.progress_bar: time.sleep(task_duration) - # def start(self): - # thread = threading.Thread(target=self.run_progress_bar) - # thread.start() + def start(self): + thread = threading.Thread(target=self.run) + thread.start() # Wait for messages from the client async for message in websocket: message_from_client = json.loads(message) if message_from_client["command"] == "start": - progress_bar = WebSocketProgressBar(request_id=message_from_client["request_id"]) - progress_bar.start() + web_socket_progress_bar = WebSocketProgressBar(request_id=message_from_client["request_id"]) + web_socket_progress_bar.start() async def spawn_server() -> None: diff --git a/src/tqdm_publisher/_demos/_single_bar/_server.py b/src/tqdm_publisher/_demos/_single_bar/_server.py index c8e739b..a29c89b 100644 --- a/src/tqdm_publisher/_demos/_single_bar/_server.py +++ b/src/tqdm_publisher/_demos/_single_bar/_server.py @@ -12,7 +12,7 @@ def start_progress_bar(*, progress_callback: callable) -> None: """ Emulate running the specified number of tasks by sleeping the specified amount of time on each iteration. - Defaults are chosen for a deterministic and regular update period of one second for a total time of one minute. + Defaults are chosen for a deterministic and regular update period of one second for a total time of 10 seconds. """ all_task_durations_in_seconds = [1.0 for _ in range(10)] # Ten seconds at one second per task progress_bar = tqdm_publisher.TQDMPublisher(iterable=all_task_durations_in_seconds) @@ -29,7 +29,7 @@ def run_function_on_progress_update(format_dict: dict) -> None: This specifically requires the `id` of the progress bar and the `format_dict` of the TQDM instance. """ - progress_callback(id=progress_bar.id, format_dict=format_dict) + progress_callback(format_dict=format_dict, progress_bar_id=progress_bar.id) progress_bar.subscribe(callback=run_function_on_progress_update) @@ -37,34 +37,26 @@ def run_function_on_progress_update(format_dict: dict) -> None: time.sleep(task_duration) -def send_message_to_client(*, websocket: websockets.WebSocketServerProtocol, message: dict) -> None: - """ - Send a message to a specific client. - - This expects a WebSocket connection and a message (dict) to send. - """ - - asyncio.run(websocket.send(message=json.dumps(obj=message))) - - async def handler(websocket: websockets.WebSocketServerProtocol) -> None: """Handle messages from the client and manage the client connections.""" + def send_progress_update_to_client(*, format_dict: dict, progress_bar_id: str) -> None: + """ + This is the callback that actually sends the updated `format_dict` to the front end webpage. + + It must be defined within the scope of the handler so that the `websocket` is inherited from the higher level. + """ + message = json.dumps(obj=dict(format_dict=format_dict, progress_bar_id=progress_bar_id)) + asyncio.run(websocket.send(message=message)) + # Wait for messages from the client async for message in websocket: message_from_client = json.loads(message) if message_from_client["command"] == "start": - # Start the progress bar in a separate thread thread = threading.Thread( - target=start_progress_bar, - # On each update of the progress bar, send this update to the requesting client - kwargs=dict( - progress_callback=lambda id, format_dict: send_message_to_client( - websocket=websocket, message=dict(id=id, format_dict=format_dict) - ) - ), + target=start_progress_bar, kwargs=dict(progress_callback=send_progress_update_to_client) ) thread.start() diff --git a/src/tqdm_publisher/_publisher.py b/src/tqdm_publisher/_publisher.py index d5cc3b3..81a7ee8 100644 --- a/src/tqdm_publisher/_publisher.py +++ b/src/tqdm_publisher/_publisher.py @@ -8,7 +8,7 @@ class TQDMPublisher(base_tqdm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.id = str(uuid4()) - self.callbacks = {} + self.callbacks = dict() # Override the update method to run callbacks def update(self, n: int = 1) -> Union[bool, None]: