<commit_before>import argparse
from asyncio import get_event_loop, gather, CancelledError
from collections import deque
from curses import wrapper, newwin
from curses.textpad import Textbox, rectangle
import html
import json
import logging
from threading import Thread, Lock
from time import sleep
from typing import Tuple, List

import websockets
from kawaiisync import sync, Waiter

from tools.audiosink import AudioSink
from tools.interface import Interface
from tools.userlist import UserList

argparser = argparse.ArgumentParser()
argparser.add_argument("--channel", "-c",
                       help="Loult channel on which to connect. Defaults to main channel",
                       default="",
                       type=str)
argparser.add_argument("--server", "-s",
                       help="Server on which to connect. Defaults to the 'Official' channel, loult.family",
                       default="loult.family",
                       type=str)
argparser.add_argument("--cookie", "-ck",
                       help="Sets the user cookie",
                       default="flpe",
                       type=str)


#class MessageLog:
#    """Widget that handles the message list"""
#
#    def __init__(self, height, width, x_offset = 0, y_offset = 0):
#        self.root_window = newwin(height, width)
#        self.dimensions = (height, width)
#        self.offsets = (x_offset, y_offset)
#        self.message_log = []  # type: List[Tuple[str,str]]
#
#    def _refresh(self):
#        self.root_window.refresh()
#
#    def add_message(self, username, message):
#        """Adds a message to the log, redraws the full message list to add that message
#        at the bottom of it"""
#        pass

class Interface:
    """ View object which asynchronously prints and gets messages

        #input: an awaitable which returns what the user has entered.
        #add_message: a coroutine function which adds a message to be
                displayed.
    """

    def __init__(self):
        # Wrapper will set up everything and clean up after a crash.
        wrapper(self._actual_init)

    def _actual_init(self, root_window)
        # Curses' objects of course aren't thread-safe,
        # so we'll need a lock for every operation while
        # other threads are running.
        self.lock = Lock()
        self.input = Waiter()
        self.loop = get_event_loop()
        # This might cause the sound system to produce logs we
        # can't control; the solution is to let them be and then
        # draw on top of them later.
        # TODO: I get "underrun occured" debug messages from ALSA,
        # which screws the graphics and the usability.
        self.sink = AudioSink()

        self.root_window = root_window
        self.root_window.clear()

        # A box to input things.
        self.input_window = newwin(1, 110, 1, 1)
        rectangle(self.root_window, 0, 0, 2, 111)
        self.textbox = Textbox(self.input_window)

        # Draw what we just created.
        self.root_window.refresh()

        # Launch the thread which will get the user's input.
        self.input_thread = Thread(target=self._get_input)
        self.input_thread.daemon = True
        self.input_thread.start()

    async def add_message(self, nickname, message):
        await self.loop.run_in_executor(None, self._add_message,
                                        nickname, message)

    def _get_input(self):
        while True:
            msg = self.textbox.edit()
            loop.call_soon_threadsafe(self.input, msg)

    def _add_message(self, nickname, message):
        with self.lock:
            self.root_window.addstr(10, 1, message)
            self.root_window.refresh()
<commit_msg>Warn about a race condition.<commit_after>import argparse
from asyncio import get_event_loop, gather, CancelledError
from collections import deque
from curses import wrapper, newwin
from curses.textpad import Textbox, rectangle
import html
import json
import logging
from threading import Thread, Lock
from time import sleep
from typing import Tuple, List

import websockets
from kawaiisync import sync, Waiter

from tools.audiosink import AudioSink
from tools.interface import Interface
from tools.userlist import UserList

argparser = argparse.ArgumentParser()
argparser.add_argument("--channel", "-c",
                       help="Loult channel on which to connect. Defaults to main channel",
                       default="",
                       type=str)
argparser.add_argument("--server", "-s",
                       help="Server on which to connect. Defaults to the 'Official' channel, loult.family",
                       default="loult.family",
                       type=str)
argparser.add_argument("--cookie", "-ck",
                       help="Sets the user cookie",
                       default="flpe",
                       type=str)


#class MessageLog:
#    """Widget that handles the message list"""
#
#    def __init__(self, height, width, x_offset = 0, y_offset = 0):
#        self.root_window = newwin(height, width)
#        self.dimensions = (height, width)
#        self.offsets = (x_offset, y_offset)
#        self.message_log = []  # type: List[Tuple[str,str]]
#
#    def _refresh(self):
#        self.root_window.refresh()
#
#    def add_message(self, username, message):
#        """Adds a message to the log, redraws the full message list to add that message
#        at the bottom of it"""
#        pass

class Interface:
    """ View object which asynchronously prints and gets messages

        #input: an awaitable which returns what the user has entered.
        #add_message: a coroutine function which adds a message to be
                displayed.
    """

    def __init__(self):
        # Wrapper will set up everything and clean up after a crash.
        wrapper(self._actual_init)

    def _actual_init(self, root_window)
        # Curses' objects of course aren't thread-safe,
        # so we'll need a lock for every operation while
        # other threads are running.
        self.lock = Lock()
        self.input = Waiter()
        self.loop = get_event_loop()
        # This might cause the sound system to produce logs we
        # can't control; the solution is to let them be and then
        # draw on top of them later.
        # TODO: I get "underrun occured" debug messages from ALSA,
        # which screws the graphics and the usability.
        self.sink = AudioSink()

        self.root_window = root_window
        self.root_window.clear()

        # A box to input things.
        self.input_window = newwin(1, 110, 1, 1)
        rectangle(self.root_window, 0, 0, 2, 111)
        self.textbox = Textbox(self.input_window)

        # Draw what we just created.
        self.root_window.refresh()

        # Launch the thread which will get the user's input.
        self.input_thread = Thread(target=self._get_input)
        self.input_thread.daemon = True
        self.input_thread.start()

    async def add_message(self, nickname, message):
        await self.loop.run_in_executor(None, self._add_message,
                                        nickname, message)

    def _get_input(self):
        while True:
            msg = self.textbox.edit()
            # There's a potential race condition here if
            # if the event loop doesn't await self.input
            # before this is called again.
            loop.call_soon_threadsafe(self.input, msg)

    def _add_message(self, nickname, message):
        with self.lock:
            self.root_window.addstr(10, 1, message)
            self.root_window.refresh()

{'fraction_non_alphanumeric': 0.08499475341028331, 'fraction_numerical': 0.004459601259181532, 'mean_word_length': 2.2753436426116838, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2169495', 'n_tokens_mistral': 2142, 'n_tokens_neox': 2052, 'n_words': 707}