Replace complicated crap with simple blocking queue
This commit is contained in:
parent
f2b97f5678
commit
5671ee8f55
69
main.py
69
main.py
@ -6,6 +6,7 @@ import getpass
|
|||||||
import textwrap
|
import textwrap
|
||||||
import threading
|
import threading
|
||||||
import platform
|
import platform
|
||||||
|
import queue
|
||||||
import re
|
import re
|
||||||
import toml
|
import toml
|
||||||
import readchar
|
import readchar
|
||||||
@ -74,8 +75,16 @@ def main():
|
|||||||
# Might as well reuse the websocket object for my websocket context data (oops, is that bad?)
|
# Might as well reuse the websocket object for my websocket context data (oops, is that bad?)
|
||||||
ws.context = context
|
ws.context = context
|
||||||
ws.user_info = context.user_me()
|
ws.user_info = context.user_me()
|
||||||
ws.output_lock = threading.Lock()
|
# Output from the websocket can only get truly printed to the screen when this lock is released.
|
||||||
ws.output_buffer = [] # Individual print statements buffered from output.
|
# We grab the lock when the user is in some kind of "input" mode, which blocks the websocket printing
|
||||||
|
# thread from processing the queue (if it has anything anyway)
|
||||||
|
ws.output_lock = threading.Lock()
|
||||||
|
# Output from the websocket is ALWAYS buffered, and we use a thread-safe queue to add and remove
|
||||||
|
# output safely. We buffer all messages to ensure the order is preserved; if we SOMETIMES queued and
|
||||||
|
# SOMETIMES did not, we would need to be very careful about whether the queue was empty, which requires
|
||||||
|
# additional locking and etc.
|
||||||
|
ws.output_buffer = queue.Queue()
|
||||||
|
|
||||||
ws.main_config = config
|
ws.main_config = config
|
||||||
ws.current_room = 0
|
ws.current_room = 0
|
||||||
ws.current_room_data = False
|
ws.current_room_data = False
|
||||||
@ -107,20 +116,17 @@ def ws_onopen(ws):
|
|||||||
if not ws.current_room:
|
if not ws.current_room:
|
||||||
printr(Fore.YELLOW + "* You are not connected to any room! Press 'S' to search for a room! *")
|
printr(Fore.YELLOW + "* You are not connected to any room! Press 'S' to search for a room! *")
|
||||||
|
|
||||||
printstatus = True
|
print_statusline(ws)
|
||||||
|
|
||||||
# The infinite input loop! Or something!
|
# The infinite input loop! Or something!
|
||||||
while True:
|
while True:
|
||||||
|
|
||||||
# Dump the buffer, if there is any, since we're outside of user input
|
# # Dump the buffer, if there is any, since we're outside of user input
|
||||||
with ws.output_lock:
|
# with ws.output_lock:
|
||||||
for output in ws.output_buffer:
|
# for output in ws.output_buffer:
|
||||||
printr(output)
|
# printr(output)
|
||||||
|
|
||||||
ws.output_buffer = []
|
# ws.output_buffer = []
|
||||||
|
|
||||||
if printstatus:
|
|
||||||
print_statusline(ws)
|
|
||||||
|
|
||||||
printstatus = False # Assume we are not printing the status every time (it's kinda annoying)
|
printstatus = False # Assume we are not printing the status every time (it's kinda annoying)
|
||||||
|
|
||||||
@ -175,14 +181,30 @@ def ws_onopen(ws):
|
|||||||
elif key == " ":
|
elif key == " ":
|
||||||
printstatus = True
|
printstatus = True
|
||||||
|
|
||||||
|
# At the end of the loop, but still in the printing lock, print the status line
|
||||||
|
if printstatus:
|
||||||
|
print_statusline(ws)
|
||||||
|
|
||||||
|
# Just a simple infinite loop which blocks on the queue until something is available
|
||||||
|
def ws_print_loop():
|
||||||
|
while True:
|
||||||
|
next_output = ws.output_buffer.get()
|
||||||
|
with ws.output_lock:
|
||||||
|
printr(next_output)
|
||||||
|
|
||||||
# Set the main room; we want to wait until the websocket is open because this also sets your
|
# Set the main room; we want to wait until the websocket is open because this also sets your
|
||||||
# status in the userlist
|
# status in the userlist
|
||||||
if ws.main_config["default_room"]:
|
if ws.main_config["default_room"]:
|
||||||
set_current_room(ws, ws.main_config["default_room"])
|
set_current_room(ws, ws.main_config["default_room"])
|
||||||
|
|
||||||
# create a thread to run the blocking task
|
# create a thread to run the blocking task
|
||||||
thread = threading.Thread(target=main_loop)
|
mainthread = threading.Thread(target=main_loop)
|
||||||
thread.start()
|
mainthread.start()
|
||||||
|
|
||||||
|
# create a thread to process websocket output
|
||||||
|
printthread = threading.Thread(target=ws_print_loop)
|
||||||
|
printthread.daemon = True
|
||||||
|
printthread.start()
|
||||||
|
|
||||||
|
|
||||||
# Message handler for our websocket; will handle live messages for the room you're listening to and
|
# Message handler for our websocket; will handle live messages for the room you're listening to and
|
||||||
@ -200,12 +222,12 @@ def ws_onmessage(ws, message):
|
|||||||
else: # This is a bad assumption, it should parse the room id out of the id instead (maybe?)
|
else: # This is a bad assumption, it should parse the room id out of the id instead (maybe?)
|
||||||
usermessage = " -- Userlist for %s -- " % ws.current_room_data["name"]
|
usermessage = " -- Userlist for %s -- " % ws.current_room_data["name"]
|
||||||
statuses = all_statuses[str(ws.current_room)] if str(ws.current_room) in all_statuses else {}
|
statuses = all_statuses[str(ws.current_room)] if str(ws.current_room) in all_statuses else {}
|
||||||
ws_print(ws, usermessage)
|
userlist_output = usermessage
|
||||||
for key,value in statuses.items():
|
for key,value in statuses.items():
|
||||||
key = int(key)
|
key = int(key)
|
||||||
user = contentapi.get_user_or_default(result["data"]["objects"]["user"], key)
|
user = contentapi.get_user_or_default(result["data"]["objects"]["user"], key)
|
||||||
# Weird parenthesis are because I was aligning printed data before
|
userlist_output += "\n" + Style.BRIGHT + " " + ("%s" % (user["username"] + Style.DIM + " #%d" % key)) + Style.RESET_ALL + " - " + value
|
||||||
ws_print(ws, Style.BRIGHT + " " + ("%s" % (user["username"] + Style.DIM + " #%d" % key)) + Style.RESET_ALL + " - " + value)
|
ws_print(ws, userlist_output)
|
||||||
return
|
return
|
||||||
# Live updates are messages, edits, user updates, etc. Check the event list to see
|
# Live updates are messages, edits, user updates, etc. Check the event list to see
|
||||||
elif result["type"] == "live":
|
elif result["type"] == "live":
|
||||||
@ -224,6 +246,7 @@ def ws_onmessage(ws, message):
|
|||||||
ws.ignored[result["type"]] = 0
|
ws.ignored[result["type"]] = 0
|
||||||
ws.ignored[result["type"]] += 1
|
ws.ignored[result["type"]] += 1
|
||||||
|
|
||||||
|
|
||||||
# Produce the string output for a given message. Can be printed directly to console
|
# Produce the string output for a given message. Can be printed directly to console
|
||||||
def get_message_string(ws, message, user):
|
def get_message_string(ws, message, user):
|
||||||
result = (MSGPREFIX + Fore.CYAN + Style.BRIGHT + user["username"] + " " + Style.DIM + "#%d" % user["id"] +
|
result = (MSGPREFIX + Fore.CYAN + Style.BRIGHT + user["username"] + " " + Style.DIM + "#%d" % user["id"] +
|
||||||
@ -232,6 +255,7 @@ def get_message_string(ws, message, user):
|
|||||||
result += (MSGPREFIX + t + "\n")
|
result += (MSGPREFIX + t + "\n")
|
||||||
return result.rstrip("\n")
|
return result.rstrip("\n")
|
||||||
|
|
||||||
|
|
||||||
# Produce a large string of output for all history in the current room. Can be printed directly to console
|
# Produce a large string of output for all history in the current room. Can be printed directly to console
|
||||||
def get_message_history_string(ws):
|
def get_message_history_string(ws):
|
||||||
if ws.current_room:
|
if ws.current_room:
|
||||||
@ -244,18 +268,13 @@ def get_message_history_string(ws):
|
|||||||
return message_block.rstrip("\n")
|
return message_block.rstrip("\n")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
# Printing websocket event output is tricky because we don't want to interrupt user input (we don't have
|
# Printing websocket event output is tricky because we don't want to interrupt user input (we don't have
|
||||||
# curses). As such, we must buffer our output IF we are asked to pause
|
# curses). As such, we must buffer our output IF we are asked to pause
|
||||||
def ws_print(ws, output):
|
def ws_print(ws, output):
|
||||||
# We can't block output for too long, so we need to only try to acquire the lock for a very short time
|
# Queueing is supposed to be threadsafe, so just slap a new one in there. This will wake up
|
||||||
if ws.output_lock.acquire(timeout=ws.main_config["output_buffer_timeout"]):
|
# the printing thread automatically
|
||||||
try:
|
ws.output_buffer.put(output)
|
||||||
printr(output)
|
|
||||||
finally:
|
|
||||||
ws.output_lock.release()
|
|
||||||
else:
|
|
||||||
logging.debug("Buffering output")
|
|
||||||
ws.output_buffer.append(output)
|
|
||||||
|
|
||||||
|
|
||||||
# Loads the config from file into the global config var. If the file
|
# Loads the config from file into the global config var. If the file
|
||||||
|
Loading…
Reference in New Issue
Block a user