Base Client

Chat client handling message emission and reception

Parameters

Name Type Description Default
server_host str ip address of remote server 'localhost'
server_port int port of remote server 1234

Example

    client = Client(
        server_host="localhost",
        server_port=1234,
    )
    client.run()

close(self)

Show source code in base/client.py
113
114
115
116
117
    def close(self) -> None:
        """Close sever connection and interrupt client."""
        logger.info("Close client writer")
        if self.writer:
            self.writer.close()

Close sever connection and interrupt client.

on_new_message(self, data)

Show source code in base/client.py
76
77
78
79
80
81
82
83
84
    async def on_new_message(self, data: Dict[str, Any]) -> None:
        """
        Handle a new message received by server.
        This method simply prints it with a `<<<` prefix.

        Arguments:
            data: dictionary containing `from` (=sender id) and `message` keys
        """
        print(f"<<< {data['from']} > {data['message']}")

Handle a new message received by server. This method simply prints it with a <<< prefix.

Parameters

Name Type Description Default
data Dict[str, Any] dictionary containing from (=sender id) and message keys required

receive_messages(self)

Show source code in base/client.py
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
    async def receive_messages(self) -> None:
        """
        Listen messages one by one received from server.

        !!! note
            after decoding, messages received are json with the following format:
            ```json
                {
                    "from": str, // sender id
                    "message": str, // message
                }
            ```

        For each message, this method decodes the received json and "fire and forget"
        a task with `self.on_new_message()` method.
        """
        while True:
            if not self.reader:
                raise ConnectionAbortedError("No socket reader")
            # message received one by one because server add \n after every message
            data = await self.reader.readline()
            if not data:
                logger.warning("Empty message received from server")
                raise ConnectionAbortedError("Empty message received from server")
            logger.debug("Message received from server %s" % data.decode())
            asyncio.ensure_future(self.on_new_message(data=json.loads(data.decode())))

Listen messages one by one received from server.

Note

after decoding, messages received are json with the following format:

    {
        "from": str, // sender id
        "message": str, // message
    }

For each message, this method decodes the received json and "fire and forget" a task with self.on_new_message() method.

run(self)

Show source code in base/client.py
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
    def run(self) -> None:
        """
        Run client by starting an event loop that listen to user input and
        server messages.
        """
        try:
            self.loop.run_until_complete(self.start())
            self.loop.create_task(self.wait_for_message())
            self.loop.create_task(self.receive_messages())
            self.loop.run_forever()

        except KeyboardInterrupt:
            logger.warning("Client closed manually by user")
        # exceptions raised by tasks are handled in handle_exception
        except Exception as e:
            logger.error("Uncatched exception %s" % e)
        finally:
            logger.info("Close client")
            self.close()
            logger.debug("Close loop")
            self.loop.close()

Run client by starting an event loop that listen to user input and server messages.

send_message(self, message)

Show source code in base/client.py
48
49
50
51
52
53
54
55
56
57
58
59
    def send_message(self, message: str) -> None:
        """
        Send message to server

        Arguments:
            message: to send to server

        """
        if not self.writer:
            raise ConnectionAbortedError("Client has no active writer")

        self.writer.write(message.encode("utf8"))

Send message to server

Parameters

Name Type Description Default
message str to send to server required

start(self)

Show source code in base/client.py
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
    async def start(self) -> None:
        """
        Start server connection

        Raise:
            ConnectionRefusedError: if connection to server failed

        """
        logger.debug("Register the open socket to wait for data")
        try:
            self.reader, self.writer = await asyncio.open_connection(
                host=self.server_host, port=self.server_port,
            )
        except OSError:
            raise ConnectionRefusedError("Impossible to find server")

Start server connection

Exceptions

Type Description
ConnectionRefusedError if connection to server failed

wait_for_message(self)

Show source code in base/client.py
61
62
63
64
65
66
67
68
69
70
71
72
73
74
    async def wait_for_message(self) -> None:
        """
        Listen for user input and send input to server.
        """
        logger.debug("wait for user input")
        while True:
            message = await ainput(">>>")
            if message:
                if message == ":q":
                    self.send_message(message)
                    break
                else:
                    logger.debug("send %s to server" % message)
                    self.send_message(message)

Listen for user input and send input to server.