Replace complicated crap with simple blocking queue
This commit is contained in:
parent
f2b97f5678
commit
5671ee8f55
67
main.py
67
main.py
@ -6,6 +6,7 @@ import getpass
|
||||
import textwrap
|
||||
import threading
|
||||
import platform
|
||||
import queue
|
||||
import re
|
||||
import toml
|
||||
import readchar
|
||||
@ -74,8 +75,16 @@ def main():
|
||||
# Might as well reuse the websocket object for my websocket context data (oops, is that bad?)
|
||||
ws.context = context
|
||||
ws.user_info = context.user_me()
|
||||
# Output from the websocket can only get truly printed to the screen when this lock is released.
|
||||
# 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()
|
||||
ws.output_buffer = [] # Individual print statements buffered from output.
|
||||
# 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.current_room = 0
|
||||
ws.current_room_data = False
|
||||
@ -107,20 +116,17 @@ def ws_onopen(ws):
|
||||
if not ws.current_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!
|
||||
while True:
|
||||
|
||||
# Dump the buffer, if there is any, since we're outside of user input
|
||||
with ws.output_lock:
|
||||
for output in ws.output_buffer:
|
||||
printr(output)
|
||||
# # Dump the buffer, if there is any, since we're outside of user input
|
||||
# with ws.output_lock:
|
||||
# for output in ws.output_buffer:
|
||||
# printr(output)
|
||||
|
||||
ws.output_buffer = []
|
||||
|
||||
if printstatus:
|
||||
print_statusline(ws)
|
||||
# ws.output_buffer = []
|
||||
|
||||
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 == " ":
|
||||
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
|
||||
# status in the userlist
|
||||
if ws.main_config["default_room"]:
|
||||
set_current_room(ws, ws.main_config["default_room"])
|
||||
|
||||
# create a thread to run the blocking task
|
||||
thread = threading.Thread(target=main_loop)
|
||||
thread.start()
|
||||
mainthread = threading.Thread(target=main_loop)
|
||||
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
|
||||
@ -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?)
|
||||
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 {}
|
||||
ws_print(ws, usermessage)
|
||||
userlist_output = usermessage
|
||||
for key,value in statuses.items():
|
||||
key = int(key)
|
||||
user = contentapi.get_user_or_default(result["data"]["objects"]["user"], key)
|
||||
# Weird parenthesis are because I was aligning printed data before
|
||||
ws_print(ws, Style.BRIGHT + " " + ("%s" % (user["username"] + Style.DIM + " #%d" % key)) + Style.RESET_ALL + " - " + value)
|
||||
userlist_output += "\n" + Style.BRIGHT + " " + ("%s" % (user["username"] + Style.DIM + " #%d" % key)) + Style.RESET_ALL + " - " + value
|
||||
ws_print(ws, userlist_output)
|
||||
return
|
||||
# Live updates are messages, edits, user updates, etc. Check the event list to see
|
||||
elif result["type"] == "live":
|
||||
@ -224,6 +246,7 @@ def ws_onmessage(ws, message):
|
||||
ws.ignored[result["type"]] = 0
|
||||
ws.ignored[result["type"]] += 1
|
||||
|
||||
|
||||
# Produce the string output for a given message. Can be printed directly to console
|
||||
def get_message_string(ws, message, user):
|
||||
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")
|
||||
return result.rstrip("\n")
|
||||
|
||||
|
||||
# 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):
|
||||
if ws.current_room:
|
||||
@ -244,18 +268,13 @@ def get_message_history_string(ws):
|
||||
return message_block.rstrip("\n")
|
||||
return None
|
||||
|
||||
|
||||
# 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
|
||||
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
|
||||
if ws.output_lock.acquire(timeout=ws.main_config["output_buffer_timeout"]):
|
||||
try:
|
||||
printr(output)
|
||||
finally:
|
||||
ws.output_lock.release()
|
||||
else:
|
||||
logging.debug("Buffering output")
|
||||
ws.output_buffer.append(output)
|
||||
# Queueing is supposed to be threadsafe, so just slap a new one in there. This will wake up
|
||||
# the printing thread automatically
|
||||
ws.output_buffer.put(output)
|
||||
|
||||
|
||||
# Loads the config from file into the global config var. If the file
|
||||
|
Loading…
Reference in New Issue
Block a user