Also I found strange definition and usage of this classes (ChatMode, Public, Channel, PrivateDM)
You are using it for chat mode checking over the isinstance, but it still not safe and not guarantee the instance will have an expected attributes. So it should be refactored.
In section
ChatMode is not an enum and also it have not attributes like Public/Channel/PrivateDM
It will raise AttributeError
if line == "/clear":
clear_screen()
print_banner()
mode_name = {
ChatMode.Public: "public chat",
ChatMode.Channel: f"channel {self.chat_context.current_mode.name}",
ChatMode.PrivateDM: f"DM with {self.chat_context.current_mode.nickname}",
}.get(type(self.chat_context.current_mode), "unknown")
print(f"» Cleared {mode_name}")
print("> ", end="", flush=True)
return
I propose you to refactor it into way like bellow, it should be more type safe
-
Declare ChatMode(Enum)
-
rename ChatMode to Chat should contain all possible fields for different chat modes
-
check chat mode over simple checking of .current_mode.mode which is ChatMode instead of isinstance statement
-
with this approach mode_name = {ChatMode.Public: ...} will work as expected
-
ChatContextcan be dataclass
-
[OPTIONAL] Public/Channel/PrivateDM should be a factory functions instead of classes
class ChatMode(Enum):
Public = auto()
Channel = auto()
PrivateDM = auto()
@dataclass
class Chat:
mode: ChatMode
channame: Optional[str] = None
nickname: Optional[str] = None # maybe use single `.name` attr for both nickname and channame
peer_id: Optional[str] = None
@dataclass
class ChatContext:
current_chat: Chat = field(default=ChatMode.Public)
active_channels: List[str] = field(default_factory=list)
active_dms: Dict[str, str] = field(default_factory=dict) # nickname -> peer_id
last_private_sender: Optional[Tuple[str, str]] = None
# refactoring of format_prompt
FORMAT_PROMPT: Dict[ChatMode, str] = {
ChatMode.Public: "[Public]",
ChatMode.Channel: "[Channel]",
ChatMode.PrivateDM: "[DM]",
}
def format_prompt(self) -> str:
return FORMAT_PROMPT.get(self.current_chat.mode, "[UNKNOWN]")
def switch_to_number(self, num: int) -> bool:
if num == 1:
self.current_mode = Chat() # Public by default
...
if 1 < num <= channel_end:
channel_idx = num - 2
if channel_idx < len(self.active_channels):
channel = self.active_channels[channel_idx]
self.current_chat = Chat(ChatMode.Channel, channame=channel)
...
if dm_idx < len(dm_list):
nick, peer_id = dm_list[dm_idx]
self.current_chat = Chat(ChatMode.PrivateDM, nickname=nick, peer_id=peer_id)
example of Chat factory methods
@dataclass
class Chat:
mode: ChatMode
channame: Optional[str] = None
nickname: Optional[str] = None # maybe use single `.name` attr for both nickname and channame
peer_id: Optional[str] = None
@classmethod
def pub(cls):
return cls(ChatMode.Public)
@classmethod
def chan(cls, channame):
return cls(ChatMode.Channel, channame=channel)
@classmethod
def private(cls, nickname, peer_id):
return cls(ChatMode.PrivateDM, nickname=nickname, peer_id=peer_id)
# to switch use like
if <condition>:
self.current_chat = Chat.pub()
self.current_chat = Chat.chan(channel)
self.current_chat = Chat.private(nick, peer_id)
Also I found strange definition and usage of this classes (ChatMode, Public, Channel, PrivateDM)
You are using it for chat mode checking over the isinstance, but it still not safe and not guarantee the instance will have an expected attributes. So it should be refactored.
In section
ChatModeis not an enum and also it have not attributes likePublic/Channel/PrivateDMIt will raise AttributeError
I propose you to refactor it into way like bellow, it should be more type safe
Declare
ChatMode(Enum)rename
ChatModetoChatshould contain all possible fields for different chat modescheck chat mode over simple checking of
.current_mode.modewhich is ChatMode instead ofisinstancestatementwith this approach
mode_name = {ChatMode.Public: ...}will work as expectedChatContextcan be dataclass[OPTIONAL]
Public/Channel/PrivateDMshould be a factory functions instead of classesexample of Chat factory methods