diff --git a/simple_typing_application/__init__.py b/simple_typing_application/__init__.py index 7e101f3..dc38134 100644 --- a/simple_typing_application/__init__.py +++ b/simple_typing_application/__init__.py @@ -10,7 +10,7 @@ typing_game, ) -__version__ = '0.2.0' +__version__ = "0.2.0" __all__ = [ const.__name__, diff --git a/simple_typing_application/__main__.py b/simple_typing_application/__main__.py index c32699f..5d6a810 100644 --- a/simple_typing_application/__main__.py +++ b/simple_typing_application/__main__.py @@ -1,2 +1,3 @@ from .main import main + main() diff --git a/simple_typing_application/config.py b/simple_typing_application/config.py index d026e41..0330d3a 100644 --- a/simple_typing_application/config.py +++ b/simple_typing_application/config.py @@ -8,7 +8,7 @@ def load_config( path: str, logger: Logger = getLogger(__name__), ) -> ConfigModel: # noqa - '''Load config file. + """Load config file. Args: path (str): path to config file. @@ -19,22 +19,23 @@ def load_config( Returns: ConfigModel: config model. - ''' # noqa - logger.debug(f'load config from {path}') + """ # noqa + logger.debug(f"load config from {path}") # load config - if os.path.splitext(path)[1] == '.json': - logger.debug('load json config') + if os.path.splitext(path)[1] == ".json": + logger.debug("load json config") try: - config = ConfigModel(**json.load(open(path, 'r', encoding='utf-8'))) # type: ignore # noqa + with open(path, "r", encoding="utf-8") as f: + config = ConfigModel(**json.load(f)) # type: ignore # noqa except FileNotFoundError: - logger.warning(f'config file not found: {path}. So use default config.') # noqa + logger.warning(f"config file not found: {path}. So use default config.") # noqa config = ConfigModel() - elif os.path.splitext(path)[1] in ['.yaml', '.yml']: - logger.debug('load yaml config') - raise NotImplementedError('yaml is not supported yet.') + elif os.path.splitext(path)[1] in [".yaml", ".yml"]: + logger.debug("load yaml config") + raise NotImplementedError("yaml is not supported yet.") else: - raise ValueError(f'Unsupported file type: {os.path.splitext(path)[1]}') + raise ValueError(f"Unsupported file type: {os.path.splitext(path)[1]}") - logger.debug(f'config: {config}') + logger.debug(f"config: {config}") return config diff --git a/simple_typing_application/const/__init__.py b/simple_typing_application/const/__init__.py index 6e5cf38..e4510fe 100644 --- a/simple_typing_application/const/__init__.py +++ b/simple_typing_application/const/__init__.py @@ -9,7 +9,7 @@ ) -ASCII_CHARS: str = ''.join( +ASCII_CHARS: str = "".join( chr(i) for i in range(0x0021, 0x007E + 1) # 0x0021 - 0x007E @@ -17,7 +17,7 @@ __all__ = [ - 'ASCII_CHARS', + "ASCII_CHARS", color.__name__, hiragana_katakana_map.__name__, hiragana_romaji_map.__name__, diff --git a/simple_typing_application/const/color.py b/simple_typing_application/const/color.py index f2fb579..4d4aa0d 100644 --- a/simple_typing_application/const/color.py +++ b/simple_typing_application/const/color.py @@ -17,15 +17,15 @@ class EColor(Enum): ecolor2terminalcolor_map: dict[EColor, str] = { - EColor.BLACK: '\033[30m', - EColor.RED: '\033[31m', - EColor.GREEN: '\033[32m', - EColor.YELLOW: '\033[33m', - EColor.BLUE: '\033[34m', - EColor.PURPLE: '\033[35m', - EColor.CYAN: '\033[36m', - EColor.WHITE: '\033[37m', - EColor.END: '\033[0m', - EColor.BOLD: '\038[1m', - EColor.DEFAULT: '\033[0m', + EColor.BLACK: "\033[30m", + EColor.RED: "\033[31m", + EColor.GREEN: "\033[32m", + EColor.YELLOW: "\033[33m", + EColor.BLUE: "\033[34m", + EColor.PURPLE: "\033[35m", + EColor.CYAN: "\033[36m", + EColor.WHITE: "\033[37m", + EColor.END: "\033[0m", + EColor.BOLD: "\038[1m", + EColor.DEFAULT: "\033[0m", } diff --git a/simple_typing_application/const/hiragana_romaji_map.py b/simple_typing_application/const/hiragana_romaji_map.py index 93124f0..ee51cc1 100644 --- a/simple_typing_application/const/hiragana_romaji_map.py +++ b/simple_typing_application/const/hiragana_romaji_map.py @@ -6,52 +6,222 @@ HIRA2ROMA_MAP: dict[str, list[str | None]] = { # 50-on 50音 - "あ": ["a"], "い": ["i", "yi"], "う": ["u", "wu", "whu"], "え": ["e"], "お": ["o"], # noqa - "か": ["ka", "ca"], "き": ["ki"], "く": ["ku", "cu", "qu"], "け": ["ke"], "こ": ["ko", "co"], # noqa - "さ": ["sa"], "し": ["si", "shi", "ci"], "す": ["su"], "せ": ["se", "ce"], "そ": ["so"], # noqa - "た": ["ta"], "ち": ["ti", "chi"], "つ": ["tu", "tsu"], "て": ["te"], "と": ["to"], # noqa - "な": ["na"], "に": ["ni"], "ぬ": ["nu"], "ね": ["ne"], "の": ["no"], # noqa - "は": ["ha"], "ひ": ["hi"], "ふ": ["fu", "hu"], "へ": ["he"], "ほ": ["ho"], # noqa - "ま": ["ma"], "み": ["mi"], "む": ["mu"], "め": ["me"], "も": ["mo"], # noqa - "や": ["ya"], "ゆ": ["yu"], "いぇ": ["ye"], "よ": ["yo"], # noqa - "ら": ["ra"], "り": ["ri"], "る": ["ru"], "れ": ["re"], "ろ": ["ro"], # noqa - "わ": ["wa"], "を": ["wo"], "ん": ["nn", "n", "n'", "xn"], # NOTE: "n" is valid for 'ん' when the next character is not in "a", "i", "u", "e", "o", "n", "y". # noqa + "あ": ["a"], + "い": ["i", "yi"], + "う": ["u", "wu", "whu"], + "え": ["e"], + "お": ["o"], # noqa + "か": ["ka", "ca"], + "き": ["ki"], + "く": ["ku", "cu", "qu"], + "け": ["ke"], + "こ": ["ko", "co"], # noqa + "さ": ["sa"], + "し": ["si", "shi", "ci"], + "す": ["su"], + "せ": ["se", "ce"], + "そ": ["so"], # noqa + "た": ["ta"], + "ち": ["ti", "chi"], + "つ": ["tu", "tsu"], + "て": ["te"], + "と": ["to"], # noqa + "な": ["na"], + "に": ["ni"], + "ぬ": ["nu"], + "ね": ["ne"], + "の": ["no"], # noqa + "は": ["ha"], + "ひ": ["hi"], + "ふ": ["fu", "hu"], + "へ": ["he"], + "ほ": ["ho"], # noqa + "ま": ["ma"], + "み": ["mi"], + "む": ["mu"], + "め": ["me"], + "も": ["mo"], # noqa + "や": ["ya"], + "ゆ": ["yu"], + "いぇ": ["ye"], + "よ": ["yo"], # noqa + "ら": ["ra"], + "り": ["ri"], + "る": ["ru"], + "れ": ["re"], + "ろ": ["ro"], # noqa + "わ": ["wa"], + "を": ["wo"], + "ん": [ + "nn", + "n", + "n'", + "xn", + ], # NOTE: "n" is valid for 'ん' when the next character is not in "a", "i", "u", "e", "o", "n", "y". # noqa # Dakuon/Handakuon 濁音/半濁音 "ゔ": ["vu"], # noqa - "が": ["ga"], "ぎ": ["gi"], "ぐ": ["gu"], "げ": ["ge"], "ご": ["go"], # noqa - "ざ": ["za"], "じ": ["zi", "ji"], "ず": ["zu"], "ぜ": ["ze"], "ぞ": ["zo"], # noqa - "だ": ["da"], "ぢ": ["di"], "づ": ["du"], "で": ["de"], "ど": ["do"], # noqa - "ば": ["ba"], "び": ["bi"], "ぶ": ["bu"], "べ": ["be"], "ぼ": ["bo"], # noqa - "ぱ": ["pa"], "ぴ": ["pi"], "ぷ": ["pu"], "ぺ": ["pe"], "ぽ": ["po"], # noqa + "が": ["ga"], + "ぎ": ["gi"], + "ぐ": ["gu"], + "げ": ["ge"], + "ご": ["go"], # noqa + "ざ": ["za"], + "じ": ["zi", "ji"], + "ず": ["zu"], + "ぜ": ["ze"], + "ぞ": ["zo"], # noqa + "だ": ["da"], + "ぢ": ["di"], + "づ": ["du"], + "で": ["de"], + "ど": ["do"], # noqa + "ば": ["ba"], + "び": ["bi"], + "ぶ": ["bu"], + "べ": ["be"], + "ぼ": ["bo"], # noqa + "ぱ": ["pa"], + "ぴ": ["pi"], + "ぷ": ["pu"], + "ぺ": ["pe"], + "ぽ": ["po"], # noqa # Yohon/拗音 - "うぁ": ["wha"], "うぃ": ["wi", "whi"], "うぇ": ["we", "whe"], "うぉ": ["who"], # noqa - "ゔぁ": ["va"], "ゔぃ": ["vi"], "ゔぇ": ["ve"], "ゔぉ": ["vo"], # noqa - "きゃ": ["kya"], "きぃ": ["kyi"], "きゅ": ["kyu"], "きぇ": ["kye"], "きょ": ["kyo"], # noqa - "ぎゃ": ["gya"], "ぎぃ": ["gyi"], "ぎゅ": ["gyu"], "ぎぇ": ["gye"], "ぎょ": ["gyo"], # noqa - "くぁ": ["qa", "qwa", "kwa"], "くぃ": ["qi", "qwi"], "くぅ": ["qu", "qwu"], "くぇ": ["qe", "qwe"], "くぉ": ["qo", "qwo"], # noqa - "ぐぁ": ["gwa"], "ぐぃ": ["gwi"], "ぐぅ": ["gwu"], "ぐぇ": ["gwe"], "ぐぉ": ["gwo"], # noqa - "くゃ": ["qya"], "くゅ": ["qyu"], "くょ": ["qyo"], # noqa - "しゃ": ["sya", "sha"], "しぃ": ["syi"], "しゅ": ["syu", "shu"], "しぇ": ["sye", "she"], "しょ": ["syo", "sho"], # noqa - "じゃ": ["ja", "zya", "jya"], "じぃ": ["zyi", "jyi"], "じゅ": ["ju", "zyu", "jyu"], "じぇ": ["je", "zye", "jye"], "じょ": ["jo", "zyo", "jyo"], # noqa - "すぁ": ["swa"], "すぃ": ["swi"], "すぅ": ["swu"], "すぇ": ["swe"], "すぉ": ["swo"], # noqa - "ちゃ": ["tya", "cya", "cha"], "ちぃ": ["tyi", "cyi"], "ちゅ": ["tyu", "cyu", "chu"], "ちぇ": ["tye", "cye", "che"], "ちょ": ["tyo", "cyo", "cho"], # noqa - "ぢゃ": ["dya"], "ぢぃ": ["dyi"], "ぢゅ": ["dyu"], "ぢぇ": ["dye"], "ぢょ": ["dyo"], # noqa - "つぁ": ["tsa"], "つぃ": ["tsi"], "つぇ": ["tse"], "つぉ": ["tso"], # noqa - "てゃ": ["tha"], "てぃ": ["thi"], "てゅ": ["thu"], "てぇ": ["the"], "てょ": ["tho"], # noqa - "でゃ": ["dha"], "でぃ": ["dhi"], "でゅ": ["dhu"], "でぇ": ["dhe"], "でょ": ["dho"], # noqa - "とぁ": ["twa"], "とぃ": ["twi"], "とぅ": ["twu"], "とぇ": ["twe"], "とぉ": ["two"], # noqa - "どぁ": ["dwa"], "どぃ": ["dwi"], "どぅ": ["dwu"], "どぇ": ["dwe"], "どぉ": ["dwo"], # noqa - "にゃ": ["nya"], "にぃ": ["nyi"], "にゅ": ["nyu"], "にぇ": ["nye"], "にょ": ["nyo"], # noqa - "ひゃ": ["hya"], "ひぃ": ["hyi"], "ひゅ": ["hyu"], "ひぇ": ["hye"], "ひょ": ["hyo"], # noqa - "びゃ": ["bya"], "びぃ": ["byi"], "びゅ": ["byu"], "びぇ": ["bye"], "びょ": ["byo"], # noqa - "ぴゃ": ["pya"], "ぴぃ": ["pyi"], "ぴゅ": ["pyu"], "ぴぇ": ["pye"], "ぴょ": ["pyo"], # noqa - "ふぁ": ["fa"], "ふぃ": ["fi"], "ふぅ": ["fu"], "ふぇ": ["fe"], "ふぉ": ["fo"], "ふゃ": ["fya"], "ふゅ": ["fyu"], "ふょ": ["fyo"], # noqa - "ぶぁ": ["bwa"], "ぶぃ": ["bwi"], "ぶぅ": ["bwu"], "ぶぇ": ["bwe"], "ぶぉ": ["bwo"], # noqa - "ぷぁ": ["pwa"], "ぷぃ": ["pwi"], "ぷぅ": ["pwu"], "ぷぇ": ["pwe"], "ぷぉ": ["pwo"], # noqa - "みゃ": ["mya"], "みぃ": ["myi"], "みゅ": ["myu"], "みぇ": ["mye"], "みょ": ["myo"], # noqa - "りゃ": ["rya"], "りぃ": ["ryi"], "りゅ": ["ryu"], "りぇ": ["rye"], "りょ": ["ryo"], # noqa + "うぁ": ["wha"], + "うぃ": ["wi", "whi"], + "うぇ": ["we", "whe"], + "うぉ": ["who"], # noqa + "ゔぁ": ["va"], + "ゔぃ": ["vi"], + "ゔぇ": ["ve"], + "ゔぉ": ["vo"], # noqa + "きゃ": ["kya"], + "きぃ": ["kyi"], + "きゅ": ["kyu"], + "きぇ": ["kye"], + "きょ": ["kyo"], # noqa + "ぎゃ": ["gya"], + "ぎぃ": ["gyi"], + "ぎゅ": ["gyu"], + "ぎぇ": ["gye"], + "ぎょ": ["gyo"], # noqa + "くぁ": ["qa", "qwa", "kwa"], + "くぃ": ["qi", "qwi"], + "くぅ": ["qu", "qwu"], + "くぇ": ["qe", "qwe"], + "くぉ": ["qo", "qwo"], # noqa + "ぐぁ": ["gwa"], + "ぐぃ": ["gwi"], + "ぐぅ": ["gwu"], + "ぐぇ": ["gwe"], + "ぐぉ": ["gwo"], # noqa + "くゃ": ["qya"], + "くゅ": ["qyu"], + "くょ": ["qyo"], # noqa + "しゃ": ["sya", "sha"], + "しぃ": ["syi"], + "しゅ": ["syu", "shu"], + "しぇ": ["sye", "she"], + "しょ": ["syo", "sho"], # noqa + "じゃ": ["ja", "zya", "jya"], + "じぃ": ["zyi", "jyi"], + "じゅ": ["ju", "zyu", "jyu"], + "じぇ": ["je", "zye", "jye"], + "じょ": ["jo", "zyo", "jyo"], # noqa + "すぁ": ["swa"], + "すぃ": ["swi"], + "すぅ": ["swu"], + "すぇ": ["swe"], + "すぉ": ["swo"], # noqa + "ちゃ": ["tya", "cya", "cha"], + "ちぃ": ["tyi", "cyi"], + "ちゅ": ["tyu", "cyu", "chu"], + "ちぇ": ["tye", "cye", "che"], + "ちょ": ["tyo", "cyo", "cho"], # noqa + "ぢゃ": ["dya"], + "ぢぃ": ["dyi"], + "ぢゅ": ["dyu"], + "ぢぇ": ["dye"], + "ぢょ": ["dyo"], # noqa + "つぁ": ["tsa"], + "つぃ": ["tsi"], + "つぇ": ["tse"], + "つぉ": ["tso"], # noqa + "てゃ": ["tha"], + "てぃ": ["thi"], + "てゅ": ["thu"], + "てぇ": ["the"], + "てょ": ["tho"], # noqa + "でゃ": ["dha"], + "でぃ": ["dhi"], + "でゅ": ["dhu"], + "でぇ": ["dhe"], + "でょ": ["dho"], # noqa + "とぁ": ["twa"], + "とぃ": ["twi"], + "とぅ": ["twu"], + "とぇ": ["twe"], + "とぉ": ["two"], # noqa + "どぁ": ["dwa"], + "どぃ": ["dwi"], + "どぅ": ["dwu"], + "どぇ": ["dwe"], + "どぉ": ["dwo"], # noqa + "にゃ": ["nya"], + "にぃ": ["nyi"], + "にゅ": ["nyu"], + "にぇ": ["nye"], + "にょ": ["nyo"], # noqa + "ひゃ": ["hya"], + "ひぃ": ["hyi"], + "ひゅ": ["hyu"], + "ひぇ": ["hye"], + "ひょ": ["hyo"], # noqa + "びゃ": ["bya"], + "びぃ": ["byi"], + "びゅ": ["byu"], + "びぇ": ["bye"], + "びょ": ["byo"], # noqa + "ぴゃ": ["pya"], + "ぴぃ": ["pyi"], + "ぴゅ": ["pyu"], + "ぴぇ": ["pye"], + "ぴょ": ["pyo"], # noqa + "ふぁ": ["fa"], + "ふぃ": ["fi"], + "ふぅ": ["fu"], + "ふぇ": ["fe"], + "ふぉ": ["fo"], + "ふゃ": ["fya"], + "ふゅ": ["fyu"], + "ふょ": ["fyo"], # noqa + "ぶぁ": ["bwa"], + "ぶぃ": ["bwi"], + "ぶぅ": ["bwu"], + "ぶぇ": ["bwe"], + "ぶぉ": ["bwo"], # noqa + "ぷぁ": ["pwa"], + "ぷぃ": ["pwi"], + "ぷぅ": ["pwu"], + "ぷぇ": ["pwe"], + "ぷぉ": ["pwo"], # noqa + "みゃ": ["mya"], + "みぃ": ["myi"], + "みゅ": ["myu"], + "みぇ": ["mye"], + "みょ": ["myo"], # noqa + "りゃ": ["rya"], + "りぃ": ["ryi"], + "りゅ": ["ryu"], + "りぇ": ["rye"], + "りょ": ["ryo"], # noqa # Sokuon 促音 - "っ": [None, "xtu", "xtsu", "ltu", "ltsu"], # NOTE: None represents the next character except for "a", "i", "u", "e", "o", "n". # noqa + "っ": [ + None, + "xtu", + "xtsu", + "ltu", + "ltsu", + ], # NOTE: None represents the next character except for "a", "i", "u", "e", "o", "n". # noqa } @@ -72,8 +242,6 @@ for k in HIRA2ROMA_MAP.keys(): if s_hira in k: # NOTE: Assume that k is a combination of a single capital hinagana and a single small hiragana # noqa - HIRA2ROMA_MAP[k].extend([ - v1+v2 - for v1, v2 in product(HIRA2ROMA_MAP[k[0]], s_romas) - if v1 is not None and v2 is not None - ]) + HIRA2ROMA_MAP[k].extend( + [v1 + v2 for v1, v2 in product(HIRA2ROMA_MAP[k[0]], s_romas) if v1 is not None and v2 is not None] + ) diff --git a/simple_typing_application/const/key_monitor.py b/simple_typing_application/const/key_monitor.py index e08018b..c946b30 100644 --- a/simple_typing_application/const/key_monitor.py +++ b/simple_typing_application/const/key_monitor.py @@ -2,5 +2,5 @@ class EKeyMonitorType(Enum): - SSHKEYBOARD = 'SSHKEYBOARD' - PYNPUT = 'PYNPUT' + SSHKEYBOARD = "SSHKEYBOARD" + PYNPUT = "PYNPUT" diff --git a/simple_typing_application/const/keys.py b/simple_typing_application/const/keys.py index d5582ea..ccf84b7 100644 --- a/simple_typing_application/const/keys.py +++ b/simple_typing_application/const/keys.py @@ -2,6 +2,5 @@ class EMetaKey(Enum): - - ESC = 'ESC' - TAB = 'TAB' + ESC = "ESC" + TAB = "TAB" diff --git a/simple_typing_application/const/sentence_generator.py b/simple_typing_application/const/sentence_generator.py index 8d88faa..d528d7f 100644 --- a/simple_typing_application/const/sentence_generator.py +++ b/simple_typing_application/const/sentence_generator.py @@ -2,6 +2,6 @@ class ESentenceGeneratorType(Enum): - OPENAI = 'OPENAI' - HUGGINGFACE = 'HUGGINGFACE' - STATIC = 'STATIC' + OPENAI = "OPENAI" + HUGGINGFACE = "HUGGINGFACE" + STATIC = "STATIC" diff --git a/simple_typing_application/const/user_interface.py b/simple_typing_application/const/user_interface.py index 2827321..c384daa 100644 --- a/simple_typing_application/const/user_interface.py +++ b/simple_typing_application/const/user_interface.py @@ -2,4 +2,4 @@ class EUserInterfaceType(Enum): - CONSOLE = 'CONSOLE' + CONSOLE = "CONSOLE" diff --git a/simple_typing_application/key_monitor/base.py b/simple_typing_application/key_monitor/base.py index e6cf6ce..513d3e1 100644 --- a/simple_typing_application/key_monitor/base.py +++ b/simple_typing_application/key_monitor/base.py @@ -7,31 +7,30 @@ class BaseKeyMonitor(ABC): - def set_on_press_callback( self, callback: Callable[[EMetaKey | str | None], bool | None] | None, ): - '''Set callback function for key press event. + """Set callback function for key press event. Args: callback (Callable[[EMetaKey | str | None], bool | None] | None): callback function for key press event. If the callback function returns False, the key monitor will stop. - ''' # noqa + """ # noqa self._on_press_callback = callback def set_on_release_callback( self, callback: Callable[[EMetaKey | str | None], bool | None] | None, ): - '''Set callback function for key release event. + """Set callback function for key release event. Args: callback (Callable[[EMetaKey | str | None], bool | None] | None): callback function for key release event. If the callback function returns False, the key monitor will stop. - ''' # noqa + """ # noqa self._on_release_callback = callback @abstractmethod diff --git a/simple_typing_application/key_monitor/factory.py b/simple_typing_application/key_monitor/factory.py index 5003188..9559115 100644 --- a/simple_typing_application/key_monitor/factory.py +++ b/simple_typing_application/key_monitor/factory.py @@ -14,13 +14,12 @@ def _select_class_and_config_model(key_monitor_type: EKeyMonitorType) -> tuple[type, type]: # noqa - if key_monitor_type == EKeyMonitorType.PYNPUT: return PynputBasedKeyMonitor, PynputBasedKeyMonitorConfigModel elif key_monitor_type == EKeyMonitorType.SSHKEYBOARD: return SSHKeyboardBasedKeyMonitor, SSHKeyboardBasedKeyMonitorConfigModel # noqa else: - raise ValueError(f'Unsupported key monitor type: {key_monitor_type}') + raise ValueError(f"Unsupported key monitor type: {key_monitor_type}") def create_key_monitor( @@ -28,16 +27,17 @@ def create_key_monitor( dict_config: dict[str, str | float | int | bool | None | dict | list], logger: Logger = getLogger(__name__), ) -> BaseKeyMonitor: - # select key monitor class and config model try: key_monitor_cls, key_monitor_config_model = _select_class_and_config_model(key_monitor_type) # noqa except NameError: - raise ImportError(f'Failed to import key monitor class and config model for key_monitor_type={key_monitor_type}') # noqa + raise ImportError( + f"Failed to import key monitor class and config model for key_monitor_type={key_monitor_type}" + ) # noqa # create key monitor - logger.debug(f'create {key_monitor_cls.__name__}') + logger.debug(f"create {key_monitor_cls.__name__}") key_monitor_config: BaseKeyMonitorConfigModel = key_monitor_config_model(**dict_config) # noqa - key_monitor: BaseKeyMonitor = key_monitor_cls(**key_monitor_config.model_dump()) # type: ignore # noqa + key_monitor: BaseKeyMonitor = key_monitor_cls(**key_monitor_config.model_dump()) # type: ignore # noqa return key_monitor diff --git a/simple_typing_application/key_monitor/pynput.py b/simple_typing_application/key_monitor/pynput.py index d5133fb..279d192 100644 --- a/simple_typing_application/key_monitor/pynput.py +++ b/simple_typing_application/key_monitor/pynput.py @@ -7,7 +7,6 @@ class PynputBasedKeyMonitor(BaseKeyMonitor): - def __init__( self, logger: Logger = getLogger(__name__), @@ -26,9 +25,8 @@ def start(self): self._listener.join() def stop(self): - if self._listener is None: - self._logger.warning(f'{self.__class__.__name__}() has not been started.') # noqa + self._logger.warning(f"{self.__class__.__name__}() has not been started.") # noqa return self._listener.stop() @@ -39,32 +37,32 @@ def _clean_key( self, key: keyboard.Key | keyboard.KeyCode | None, ) -> EMetaKey | str | None: - self._logger.debug(f'key: {key}') + self._logger.debug(f"key: {key}") if isinstance(key, keyboard.Key): if key == keyboard.Key.esc: return EMetaKey.ESC elif key == keyboard.Key.tab: return EMetaKey.TAB elif key == keyboard.Key.space: - return ' ' + return " " else: - self._logger.warning(f'key: {key} is not supported.') + self._logger.warning(f"key: {key} is not supported.") return None elif isinstance(key, keyboard.KeyCode): return key.char else: - self._logger.warning(f'key: {key} is not supported.') + self._logger.warning(f"key: {key} is not supported.") return None def _on_press_callback_wrapper( self, key: keyboard.Key | keyboard.KeyCode | None, ): - self._logger.debug(f'pressed key: {key}') + self._logger.debug(f"pressed key: {key}") cleaned_key = self._clean_key(key) if self._on_press_callback is None: - self._logger.warning(f'{self.__class__.__name__}._on_press_callback is None.') # noqa + self._logger.warning(f"{self.__class__.__name__}._on_press_callback is None.") # noqa return return self._on_press_callback(cleaned_key) @@ -73,11 +71,11 @@ def _on_release_callback_wrapper( self, key: keyboard.Key | keyboard.KeyCode | None, ): - self._logger.debug(f'released key: {key}') + self._logger.debug(f"released key: {key}") cleaned_key = self._clean_key(key) if self._on_release_callback is None: - self._logger.warning(f'{self.__class__.__name__}._on_release_callback is None.') # noqa + self._logger.warning(f"{self.__class__.__name__}._on_release_callback is None.") # noqa return return self._on_release_callback(cleaned_key) diff --git a/simple_typing_application/key_monitor/sshkeyboard.py b/simple_typing_application/key_monitor/sshkeyboard.py index 4f50f9f..31f398f 100644 --- a/simple_typing_application/key_monitor/sshkeyboard.py +++ b/simple_typing_application/key_monitor/sshkeyboard.py @@ -9,7 +9,6 @@ class SSHKeyboardBasedKeyMonitor(BaseKeyMonitor): - def __init__( self, logger: Logger = getLogger(__name__), @@ -27,15 +26,14 @@ def start(self): kwargs=dict( on_press=self._on_press_callback_wrapper, on_release=self._on_release_callback_wrapper, - ) + ), ) self.__thread.start() self.__thread.join() def stop(self): - if self.__thread is None: - self._logger.warning(f'{self.__class__.__name__}() has not been started.') # noqa + self._logger.warning(f"{self.__class__.__name__}() has not been started.") # noqa return # send stop command @@ -51,18 +49,18 @@ def _reset_keys_queue(self): def _clean_key(self, key: str) -> EMetaKey | str | None: if len(key) == 1: return key - elif key.lower() == 'tab': + elif key.lower() == "tab": return EMetaKey.TAB - elif key.lower() == 'esc': + elif key.lower() == "esc": return EMetaKey.ESC - elif key.lower() == 'space': - return ' ' + elif key.lower() == "space": + return " " else: - self._logger.warning(f'Invalid key: {key}') + self._logger.warning(f"Invalid key: {key}") return None def _on_press_callback_wrapper(self, key: str): - self._logger.debug(f'pressed key: {key}') + self._logger.debug(f"pressed key: {key}") # record key if len(key) == 1: # NOTE: key can be two or more characters when it is a special key like "tab" # noqa @@ -71,13 +69,13 @@ def _on_press_callback_wrapper(self, key: str): key_ = self._clean_key(key) # callback if self._on_press_callback is None: - self._logger.warning(f'{self.__class__.__name__}._on_press_callback is None.') # noqa + self._logger.warning(f"{self.__class__.__name__}._on_press_callback is None.") # noqa return return self._on_press_callback(key_) def _on_release_callback_wrapper(self, key: str): - self._logger.debug(f'released key: {key}') + self._logger.debug(f"released key: {key}") if self._on_release_callback is None: - self._logger.warning(f'{self.__class__.__name__}._on_release_callback is None.') # noqa + self._logger.warning(f"{self.__class__.__name__}._on_release_callback is None.") # noqa return return self._on_release_callback(self._clean_key(key)) diff --git a/simple_typing_application/main.py b/simple_typing_application/main.py index 4d84f46..e20c4be 100644 --- a/simple_typing_application/main.py +++ b/simple_typing_application/main.py @@ -10,32 +10,31 @@ @click.command() -@click.option('--config-path', '-c', default='./config.json', help='path to config file. Defaults to ./config.json') # noqa -@click.option('--log-level', '-l', default='INFO', help='log level. Defaults to INFO.') # noqa -@click.option('--debug', '-d', is_flag=True, help='debug mode.') +@click.option("--config-path", "-c", default="./config.json", help="path to config file. Defaults to ./config.json") # noqa +@click.option("--log-level", "-l", default="INFO", help="log level. Defaults to INFO.") # noqa +@click.option("--debug", "-d", is_flag=True, help="debug mode.") def main( config_path: str, log_level: str, debug: bool, logger: logging.Logger = logging.getLogger(__name__), ): - # set log level if debug: logging.basicConfig(level=logging.DEBUG) - elif log_level == 'DEBUG': + elif log_level == "DEBUG": logging.basicConfig(level=logging.DEBUG) - elif log_level == 'INFO': + elif log_level == "INFO": logging.basicConfig(level=logging.INFO) - elif log_level == 'WARNING': + elif log_level == "WARNING": logging.basicConfig(level=logging.WARNING) - elif log_level == 'ERROR': + elif log_level == "ERROR": logging.basicConfig(level=logging.ERROR) - elif log_level == 'CRITICAL': + elif log_level == "CRITICAL": logging.basicConfig(level=logging.CRITICAL) else: logging.basicConfig(level=logging.INFO) - logger.warning(f'invalid log level: {log_level}. Set to INFO') + logger.warning(f"invalid log level: {log_level}. Set to INFO") # load config config = load_config(config_path) # noqa diff --git a/simple_typing_application/models/config_models/general_config_model.py b/simple_typing_application/models/config_models/general_config_model.py index b3b984c..6da2d28 100644 --- a/simple_typing_application/models/config_models/general_config_model.py +++ b/simple_typing_application/models/config_models/general_config_model.py @@ -11,12 +11,16 @@ class ConfigModel(BaseModel): sentence_generator_type: ESentenceGeneratorType = ESentenceGeneratorType.OPENAI # noqa - sentence_generator_config: dict[str, str | float | int | bool | None | dict | list] = BaseSentenceGeneratorConfigModel().model_dump() # noqa + sentence_generator_config: dict[str, str | float | int | bool | None | dict | list] = ( + BaseSentenceGeneratorConfigModel().model_dump() + ) # noqa user_interface_type: EUserInterfaceType = EUserInterfaceType.CONSOLE - user_interface_config: dict[str, str | float | int | None | dict | list] = BaseUserInterfaceConfigModel().model_dump() # noqa + user_interface_config: dict[str, str | float | int | None | dict | list] = ( + BaseUserInterfaceConfigModel().model_dump() + ) # noqa key_monitor_type: EKeyMonitorType = EKeyMonitorType.PYNPUT key_monitor_config: dict[str, str | float | int | None | dict | list] = BaseKeyMonitorConfigModel().model_dump() # noqa - record_direc: str = './record' + record_direc: str = "./record" diff --git a/simple_typing_application/models/config_models/sentence_generator_config_model.py b/simple_typing_application/models/config_models/sentence_generator_config_model.py index 4c357df..12e211d 100644 --- a/simple_typing_application/models/config_models/sentence_generator_config_model.py +++ b/simple_typing_application/models/config_models/sentence_generator_config_model.py @@ -11,20 +11,20 @@ class BaseSentenceGeneratorConfigModel(BaseModel): class OpenAISentenceGeneratorConfigModel(BaseSentenceGeneratorConfigModel): - model: str = 'gpt-3.5-turbo-16k' + model: str = "gpt-3.5-turbo-16k" temperature: float = 0.7 - openai_api_key: SecretStr | None = os.getenv('OPENAI_API_KEY') # type: ignore # noqa + openai_api_key: SecretStr | None = os.getenv("OPENAI_API_KEY") # type: ignore # noqa memory_size: int = 0 max_retry: int = 5 class HuggingfaceSentenceGeneratorConfigModel(BaseSentenceGeneratorConfigModel): # noqa - model: str = 'line-corporation/japanese-large-lm-3.6b' + model: str = "line-corporation/japanese-large-lm-3.6b" max_length: int = 100 do_sample: bool = True top_k: int = 50 top_p: float = 0.95 - device: str = 'cuda' + device: str = "cuda" class StaticSentenceGeneratorConfigModel(BaseSentenceGeneratorConfigModel): diff --git a/simple_typing_application/models/output_model.py b/simple_typing_application/models/output_model.py index 333d3ba..d789c72 100644 --- a/simple_typing_application/models/output_model.py +++ b/simple_typing_application/models/output_model.py @@ -6,7 +6,6 @@ class OutputModel(BaseModel): - timestamp: datetime.datetime typing_target: TypingTargetModel records: list[RecordModel] diff --git a/simple_typing_application/models/record_model.py b/simple_typing_application/models/record_model.py index a4d5f9e..f8a884b 100644 --- a/simple_typing_application/models/record_model.py +++ b/simple_typing_application/models/record_model.py @@ -4,7 +4,6 @@ class RecordModel(BaseModel): - timestamp: datetime.datetime = Field(..., description="The timestamp when the key was pressed.") # noqa pressed_key: str = Field(..., description="The pressed key.") correct_keys: list[str] = Field([], description="The correct keys.") diff --git a/simple_typing_application/models/typing_target_model.py b/simple_typing_application/models/typing_target_model.py index 30101d3..cfeac5b 100644 --- a/simple_typing_application/models/typing_target_model.py +++ b/simple_typing_application/models/typing_target_model.py @@ -4,5 +4,7 @@ class TypingTargetModel(BaseModel): text: str = Field(..., description="The raw text to be typed.", min_length=1) # noqa - text_hiragana_alphabet_symbol: str = Field(..., description="The text to be typed in hiragana, alphabet, and symbol.", min_length=1) # noqa + text_hiragana_alphabet_symbol: str = Field( + ..., description="The text to be typed in hiragana, alphabet, and symbol.", min_length=1 + ) # noqa typing_target: list[list[str]] = Field([], description="The typing target.") # noqa diff --git a/simple_typing_application/sentence_generator/__init__.py b/simple_typing_application/sentence_generator/__init__.py index e615eb6..61e62ba 100644 --- a/simple_typing_application/sentence_generator/__init__.py +++ b/simple_typing_application/sentence_generator/__init__.py @@ -16,7 +16,6 @@ openai_sentence_generator.__name__, static_sentence_generator.__name__, utils.__name__, - BaseSentenceGenerator.__name__, create_sentence_generator.__name__, OpenaiSentenceGenerator.__name__, diff --git a/simple_typing_application/sentence_generator/base.py b/simple_typing_application/sentence_generator/base.py index af85ea1..9426865 100644 --- a/simple_typing_application/sentence_generator/base.py +++ b/simple_typing_application/sentence_generator/base.py @@ -4,7 +4,6 @@ class BaseSentenceGenerator(ABC): - @abstractmethod async def generate( self, diff --git a/simple_typing_application/sentence_generator/factory.py b/simple_typing_application/sentence_generator/factory.py index 175eb51..9cf39bc 100644 --- a/simple_typing_application/sentence_generator/factory.py +++ b/simple_typing_application/sentence_generator/factory.py @@ -3,12 +3,13 @@ from logging import getLogger, Logger from .base import BaseSentenceGenerator # noqa + try: from .huggingface_sentence_generator import HuggingfaceSentenceGenerator # noqa except ImportError: logging.warning( - 'Failed to import HuggingfaceSentenceGenerator. ' - 'If you want to use HuggingfaceSentenceGenerator, `pip install simple_typing_application[huggingface]`.' # noqa + "Failed to import HuggingfaceSentenceGenerator. " + "If you want to use HuggingfaceSentenceGenerator, `pip install simple_typing_application[huggingface]`." # noqa ) from .openai_sentence_generator import OpenaiSentenceGenerator # noqa from .static_sentence_generator import StaticSentenceGenerator # noqa @@ -22,7 +23,6 @@ def _select_class_and_config_model(sentence_generator_type: ESentenceGeneratorType) -> tuple[type, type]: # noqa - if sentence_generator_type == ESentenceGeneratorType.OPENAI: return OpenaiSentenceGenerator, OpenAISentenceGeneratorConfigModel elif sentence_generator_type == ESentenceGeneratorType.HUGGINGFACE: @@ -30,7 +30,7 @@ def _select_class_and_config_model(sentence_generator_type: ESentenceGeneratorTy elif sentence_generator_type == ESentenceGeneratorType.STATIC: return StaticSentenceGenerator, StaticSentenceGeneratorConfigModel else: - raise ValueError(f'Unsupported sentence generator type: {sentence_generator_type}') # noqa + raise ValueError(f"Unsupported sentence generator type: {sentence_generator_type}") # noqa def create_sentence_generator( @@ -38,16 +38,19 @@ def create_sentence_generator( dict_config: dict[str, str | float | int | bool | None | dict | list], logger: Logger = getLogger(__name__), ) -> BaseSentenceGenerator: - # select sentence generator class and config model try: - sentence_generator_cls, sentence_generator_config_model = _select_class_and_config_model(sentence_generator_type) # noqa + sentence_generator_cls, sentence_generator_config_model = _select_class_and_config_model( + sentence_generator_type + ) # noqa except NameError: - raise ImportError(f'Failed to import sentence generator class and config model for sentence_generator_type={sentence_generator_type}') # noqa + raise ImportError( + f"Failed to import sentence generator class and config model for sentence_generator_type={sentence_generator_type}" + ) # noqa # create sentence generator - logger.debug(f'create {sentence_generator_cls.__name__}') + logger.debug(f"create {sentence_generator_cls.__name__}") sentence_generator_config: BaseSentenceGeneratorConfigModel = sentence_generator_config_model(**dict_config) # noqa - sentence_generator: BaseSentenceGenerator = sentence_generator_cls(**sentence_generator_config.model_dump()) # type: ignore # noqa + sentence_generator: BaseSentenceGenerator = sentence_generator_cls(**sentence_generator_config.model_dump()) # type: ignore # noqa return sentence_generator diff --git a/simple_typing_application/sentence_generator/huggingface_sentence_generator.py b/simple_typing_application/sentence_generator/huggingface_sentence_generator.py index b5b35bf..b3fd943 100644 --- a/simple_typing_application/sentence_generator/huggingface_sentence_generator.py +++ b/simple_typing_application/sentence_generator/huggingface_sentence_generator.py @@ -21,23 +21,22 @@ class HuggingfaceSentenceGenerator(BaseSentenceGenerator): - def __init__( self, - model: str = 'line-corporation/japanese-large-lm-3.6b', + model: str = "line-corporation/japanese-large-lm-3.6b", max_length: int = 20, do_sample: bool = True, top_k: int = 50, top_p: float = 0.95, - torch_dtype: 'torch.dtype' | None = None, + torch_dtype: "torch.dtype" | None = None, device: str | None = None, logger: Logger = getLogger(__name__), ) -> None: torch_dtype = torch_dtype or torch.float32 - device = device or ('cuda' if torch.cuda.is_available() else 'cpu') + device = device or ("cuda" if torch.cuda.is_available() else "cpu") self._tokenizer = AutoTokenizer.from_pretrained(model, use_fast=False) # noqa self._model = AutoModelForCausalLM.from_pretrained(model, torch_dtype=torch_dtype) # noqa - self._generator = pipeline('text-generation', model=self._model, tokenizer=self._tokenizer, device=device) # noqa + self._generator = pipeline("text-generation", model=self._model, tokenizer=self._tokenizer, device=device) # noqa self._max_length = max_length self._do_sample = do_sample self._top_k = top_k @@ -48,7 +47,6 @@ async def generate( self, callback: Callable[[TypingTargetModel], TypingTargetModel] | None = None, # noqa ) -> TypingTargetModel: - # generate ret = self._generator( self._prompt, @@ -56,20 +54,20 @@ async def generate( do_sample=self._do_sample, top_k=self._top_k, top_p=self._top_p, - pad_token_id=self._tokenizer.pad_token_id + pad_token_id=self._tokenizer.pad_token_id, ) # postprocess - generated_text: str = delete_space_between_hiraganas(ret[0]['generated_text']) # noqa - generated_text = generated_text.replace(self._prompt, '') - generated_text = generated_text.split('。')[0] + ('。' if '。' in generated_text else '') # noqa - self._logger.debug(f'generated text: {generated_text}') + generated_text: str = delete_space_between_hiraganas(ret[0]["generated_text"]) # noqa + generated_text = generated_text.replace(self._prompt, "") + generated_text = generated_text.split("。")[0] + ("。" if "。" in generated_text else "") # noqa + self._logger.debug(f"generated text: {generated_text}") hiragana_text: str = excelapi_kanji2kana(generated_text) - self._logger.debug(f'generated text (hira): {hiragana_text}') + self._logger.debug(f"generated text (hira): {hiragana_text}") splitted: list[str] = split_hiraganas_alphabets_symbols(hiragana_text) # noqa - self._logger.debug(f'splitted pattern: {splitted}') + self._logger.debug(f"splitted pattern: {splitted}") typing_target: list[list[str]] = splitted_hiraganas_alphabets_symbols_to_typing_target(splitted) # noqa - self._logger.debug(f'typing target: {typing_target}') + self._logger.debug(f"typing target: {typing_target}") if callback is None: return TypingTargetModel( @@ -78,12 +76,14 @@ async def generate( typing_target=typing_target, ) else: - return callback(TypingTargetModel( - text=generated_text, - text_hiragana_alphabet_symbol=hiragana_text, - typing_target=typing_target, - )) + return callback( + TypingTargetModel( + text=generated_text, + text_hiragana_alphabet_symbol=hiragana_text, + typing_target=typing_target, + ) + ) @property def _prompt(self) -> str: - return '日本語の例文: ・' + return "日本語の例文: ・" diff --git a/simple_typing_application/sentence_generator/openai_sentence_generator.py b/simple_typing_application/sentence_generator/openai_sentence_generator.py index e110b33..24663fb 100644 --- a/simple_typing_application/sentence_generator/openai_sentence_generator.py +++ b/simple_typing_application/sentence_generator/openai_sentence_generator.py @@ -16,33 +16,29 @@ class _OutputSchema(BaseModel): - text: str = Field(..., description='生成された一文') + text: str = Field(..., description="生成された一文") text_hiragana_alphabet_symbol: str = Field( # noqa - ..., description='一文をひらがな、アルファベット、記号のみに変換したもの' # noqa + ..., + description="一文をひらがな、アルファベット、記号のみに変換したもの", # noqa ) def build_typing_target(self) -> TypingTargetModel: dic: dict[str, str | list[list[str]]] = {} # assign values - dic['text'] = self.text + dic["text"] = self.text # delete space between hiraganas - dic['text_hiragana_alphabet_symbol'] = delete_space_between_hiraganas( - self.text_hiragana_alphabet_symbol - ) + dic["text_hiragana_alphabet_symbol"] = delete_space_between_hiraganas(self.text_hiragana_alphabet_symbol) # create typing target - splitted = split_hiraganas_alphabets_symbols( - self.text_hiragana_alphabet_symbol - ) - dic['typing_target'] = splitted_hiraganas_alphabets_symbols_to_typing_target(splitted) # noqa + splitted = split_hiraganas_alphabets_symbols(self.text_hiragana_alphabet_symbol) + dic["typing_target"] = splitted_hiraganas_alphabets_symbols_to_typing_target(splitted) # noqa return TypingTargetModel(**dic) class OpenaiSentenceGenerator(BaseSentenceGenerator): - def __init__( self, - model: str = 'gpt-5-nano', - temperature: float = .7, + model: str = "gpt-5-nano", + temperature: float = 0.7, openai_api_key: SecretStr | str | None = None, memory_size: int = 5, max_retry: int = 5, @@ -73,17 +69,13 @@ def __init__( def _retry_callback(self) -> None: if self._memory: - self._logger.info( - 'reducing memory size: ' - f'{len(self._memory)} -> {len(self._memory) - 1}' - ) + self._logger.info(f"reducing memory size: {len(self._memory)} -> {len(self._memory) - 1}") del self._memory[0] async def generate( self, callback: Callable[[TypingTargetModel], TypingTargetModel] | None = None, # noqa ) -> TypingTargetModel: - # invoke agent messages = [ { @@ -109,9 +101,7 @@ async def generate( self._memory.pop(0) # build typing target - cleaned_ret: TypingTargetModel = ( - output.build_typing_target() - ) + cleaned_ret: TypingTargetModel = output.build_typing_target() if callback is None: return cleaned_ret @@ -121,11 +111,8 @@ async def generate( @property def _system_prompt(self) -> str: key = dt.now().strftime("%Y/%m/%d %H:%M:%S.%f")[::-1] - past_outputs = '\n'.join([ - '- `' + m.model_dump_json(indent=None) + '`' - for m in self._memory - ]) - return f'''あなたは非常に優秀な日本語の短文作家です。 + past_outputs = "\n".join(["- `" + m.model_dump_json(indent=None) + "`" for m in self._memory]) + return f"""あなたは非常に優秀な日本語の短文作家です。 あなたが素晴らしいと思う 20 文字以上の日本語の一文を下記の手順で step-by-step に生成してください。 Step 1. 20文字以上の日本語の一文を生成する。 @@ -152,8 +139,8 @@ def _system_prompt(self) -> str: {past_outputs} 乱数シード:{key} -''' # noqa +""" # noqa @property def _user_prompt(self) -> str: - return '生成してください。' + return "生成してください。" diff --git a/simple_typing_application/sentence_generator/static_sentence_generator.py b/simple_typing_application/sentence_generator/static_sentence_generator.py index 05dd55f..6209ee4 100644 --- a/simple_typing_application/sentence_generator/static_sentence_generator.py +++ b/simple_typing_application/sentence_generator/static_sentence_generator.py @@ -14,8 +14,7 @@ class StaticSentenceGenerator(BaseSentenceGenerator): - - __WAIT_TIME_SEC: float = 1. + __WAIT_TIME_SEC: float = 1.0 def __init__( self, @@ -24,8 +23,7 @@ def __init__( logger: Logger = getLogger(__name__), ) -> None: self._text_kana_pairs = tuple( - (k, v if v is not None else self._kanji2kana(k)) - for k, v in text_kana_map.items() + (k, v if v is not None else self._kanji2kana(k)) for k, v in text_kana_map.items() ) self._is_random: bool = is_random self._index: int = -1 @@ -35,17 +33,16 @@ async def generate( self, callback: Callable[[TypingTargetModel], TypingTargetModel] | None = None, # noqa ) -> TypingTargetModel: - # generate generated_text, generated_kana = self._get_next() - self._logger.debug(f'generated text: {generated_text}') - self._logger.debug(f'generated kana: {generated_kana}') + self._logger.debug(f"generated text: {generated_text}") + self._logger.debug(f"generated kana: {generated_kana}") # postprocess splitted = split_hiraganas_alphabets_symbols(generated_kana) - self._logger.debug(f'splitted pattern: {splitted}') + self._logger.debug(f"splitted pattern: {splitted}") typing_target = splitted_hiraganas_alphabets_symbols_to_typing_target(splitted) # noqa - self._logger.debug(f'typing target: {typing_target}') + self._logger.debug(f"typing target: {typing_target}") # callback if callback is None: @@ -55,14 +52,15 @@ async def generate( typing_target=typing_target, ) else: - return callback(TypingTargetModel( - text=generated_text, - text_hiragana_alphabet_symbol=generated_kana, - typing_target=typing_target, - )) + return callback( + TypingTargetModel( + text=generated_text, + text_hiragana_alphabet_symbol=generated_kana, + typing_target=typing_target, + ) + ) def _get_next(self) -> tuple[str, str]: - # get index if self._is_random: self._index = random.randint(0, len(self._text_kana_pairs) - 1) diff --git a/simple_typing_application/sentence_generator/utils.py b/simple_typing_application/sentence_generator/utils.py index 3cbe456..f669b8f 100644 --- a/simple_typing_application/sentence_generator/utils.py +++ b/simple_typing_application/sentence_generator/utils.py @@ -5,7 +5,7 @@ def split_hiraganas_alphabets_symbols(s: str) -> list[str]: - '''Split a string into a list of hiraganas, alphabets, and symbols. + """Split a string into a list of hiraganas, alphabets, and symbols. Args: s (str): a string of hiraganas, alphabets, and symbols. @@ -42,24 +42,24 @@ def split_hiraganas_alphabets_symbols(s: str) -> list[str]: >>> s = 'っきゃぁ' >>> split_hiraganas_alphabets_symbols(s) ['っきゃぁ'] - ''' + """ # split hiraganas into patterns patterns: list[str] = [] pattern: list[str] = [] - for c, next_c in zip(s, s[1:]+' '): + for c, next_c in zip(s, s[1:] + " "): pattern.append(c) - if c in ['っ', 'ん'] or next_c in SMALL_HIRA2ROMA_MAP: + if c in ["っ", "ん"] or next_c in SMALL_HIRA2ROMA_MAP: # NOTE: 'っ' and 'ん' requires the next character. # NOTE: keys of SMALL_HIRA2ROMA_MAP require the previous character. # See ../const/hiragana_romaji_map.py. continue else: - patterns.append(''.join(pattern)) + patterns.append("".join(pattern)) pattern = [] - if c in ['っ', 'ん']: + if c in ["っ", "ん"]: # Case: the sentence ends with 'っ' or 'ん'. - patterns.append(''.join(pattern)) + patterns.append("".join(pattern)) pattern = [] return patterns @@ -69,7 +69,7 @@ def splitted_hiraganas_alphabets_symbols_to_typing_target( splitted_patterns: list[str], logger: Logger = getLogger(__name__), ) -> list[list[str]]: - '''Convert a list of splitted hiraganas, alphabets, and symbols into a typing target. + """Convert a list of splitted hiraganas, alphabets, and symbols into a typing target. Args: splitted_patterns (list[str]): a list of splitted hiraganas, alphabets, and symbols. @@ -92,35 +92,38 @@ def splitted_hiraganas_alphabets_symbols_to_typing_target( >>> splitted_patterns = ['っあ'] >>> splitted_hiraganas_alphabets_symbols_to_typing_target(splitted_patterns) [['ltsua', 'ltua', 'xtsua', 'xtua']] - ''' # noqa + """ # noqa # initialize typing_targets: list[list[str]] = [] for pattern in splitted_patterns: - # clean # zenkaku ascii -> hankaku ascii - pattern = pattern.translate(str.maketrans( - "# !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~¥", # noqa - "# !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~¥", # noqa - )) + pattern = pattern.translate( + str.maketrans( + "# !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~¥", # noqa + "# !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~¥", # noqa + ) + ) # zenkaku symbol -> hankaku symbol - pattern = pattern.translate(str.maketrans( - '、。・「」ー', - ',./[]-', - )) + pattern = pattern.translate( + str.maketrans( + "、。・「」ー", + ",./[]-", + ) + ) # pattern -> typing target - if pattern.isascii() or pattern == '¥': + if pattern.isascii() or pattern == "¥": # NOTE: pattern is typing target when pattern is ascii. typing_targets.append([pattern]) - elif pattern == 'ん': + elif pattern == "ん": # Case: the sentence ends with 'ん'. - typing_targets.append([c for c in HIRA2ROMA_MAP[pattern] if c != 'n' and c is not None]) # noqa + typing_targets.append([c for c in HIRA2ROMA_MAP[pattern] if c != "n" and c is not None]) # noqa - elif pattern == 'っ': + elif pattern == "っ": # Case: the sentence ends with 'っ'. typing_targets.append([c for c in HIRA2ROMA_MAP[pattern] if c is not None]) # noqa @@ -138,27 +141,21 @@ def splitted_hiraganas_alphabets_symbols_to_typing_target( # split _splitted: list[str] = [] for c in pattern: - if ( - c in SMALL_HIRA2ROMA_MAP - and len(_splitted) > 0 - and _splitted[-1] + c in HIRA2ROMA_MAP - ): + if c in SMALL_HIRA2ROMA_MAP and len(_splitted) > 0 and _splitted[-1] + c in HIRA2ROMA_MAP: _splitted[-1] += c else: _splitted.append(c) # extract typing targets from candidates candidate: tuple[str | None, ...] - for candidate in itertools.product(*[ - HIRA2ROMA_MAP.get(c, SMALL_HIRA2ROMA_MAP.get(c, [c])) - for c in _splitted - ]): - + for candidate in itertools.product( + *[HIRA2ROMA_MAP.get(c, SMALL_HIRA2ROMA_MAP.get(c, [c])) for c in _splitted] + ): # preparation target_flag = True # check - if candidate[-1] == 'n' or candidate[-1] is None: + if candidate[-1] == "n" or candidate[-1] is None: target_flag = False continue @@ -167,15 +164,15 @@ def splitted_hiraganas_alphabets_symbols_to_typing_target( # Example: 'tttu' -> 'tっつ' target_flag = False break - if s is None and t is not None and t[0] in ['a', 'i', 'u', 'e', 'o', 'n']: # noqa + if s is None and t is not None and t[0] in ["a", "i", "u", "e", "o", "n"]: # noqa # Example: 'aa' -> not 'っあ' but 'ああ' target_flag = False break - if s is None and t is not None and not ('a' <= t[0] <= 'z' or 'A' <= t[0] <= 'Z'): # noqa + if s is None and t is not None and not ("a" <= t[0] <= "z" or "A" <= t[0] <= "Z"): # noqa # Example: ',,' -> not 'っ、' but '、、' target_flag = False break - if s == 'n' and t is not None and t[0] in ['a', 'i', 'u', 'e', 'o', 'n', 'y']: # noqa + if s == "n" and t is not None and t[0] in ["a", "i", "u", "e", "o", "n", "y"]: # noqa # Example: 'nna' -> not 'んな' but 'んあ' target_flag = False break @@ -184,42 +181,45 @@ def splitted_hiraganas_alphabets_symbols_to_typing_target( continue # Skip this candidate. # join - _joined = ''.join([ - x if x is not None else y[0] # type: ignore - # NOTE: when x is None, y is not None. See the above for-loop. # noqa - for x, y in zip(candidate, candidate[1:] + (None,)) - ]) + _joined = "".join( + [ + x if x is not None else y[0] # type: ignore + # NOTE: when x is None, y is not None. See the above for-loop. # noqa + for x, y in zip(candidate, candidate[1:] + (None,)) + ] + ) # invalid repetition patterns - if 'xxtsu' in _joined: + if "xxtsu" in _joined: # NOTE: 'xxtsu' -> 'xっ' target_flag = False continue - if 'lltsu' in _joined: + if "lltsu" in _joined: # NOTE: 'lltsu' -> 'lっ' target_flag = False continue # target registration if target_flag: - _target.append(''.join([ - x if x is not None else y[0] # type: ignore - # NOTE: when x is None, y is not None. See the above for-loop. # noqa - for x, y in zip(candidate, candidate[1:] + (None,)) - ])) + _target.append( + "".join( + [ + x if x is not None else y[0] # type: ignore + # NOTE: when x is None, y is not None. See the above for-loop. # noqa + for x, y in zip(candidate, candidate[1:] + (None,)) + ] + ) + ) typing_targets.append(_target) # check for targets in typing_targets: for target in targets: - if not target.isascii() and target != '¥': - raise ValueError(f'Invalid typing target: {target}') + if not target.isascii() and target != "¥": + raise ValueError(f"Invalid typing target: {target}") # clean - typing_targets = [ - sorted(set(targets)) - for targets in typing_targets - ] + typing_targets = [sorted(set(targets)) for targets in typing_targets] return typing_targets diff --git a/simple_typing_application/typing_game.py b/simple_typing_application/typing_game.py index b5dc5e0..f8beed7 100644 --- a/simple_typing_application/typing_game.py +++ b/simple_typing_application/typing_game.py @@ -20,7 +20,7 @@ def _input_char_is_correct( char: str, typing_target: TypingTargetModel, ) -> tuple[bool, TypingTargetModel]: - '''Check if the input character is correct. + """Check if the input character is correct. Args: char (str): an input character. @@ -29,13 +29,11 @@ def _input_char_is_correct( Returns: bool: True if the input character is correct. TypingTargetModel: the next typing pattern. - ''' + """ # check new_typing_target = typing_target.model_copy(deep=True) new_typing_target.typing_target[0] = [ - pattern[1:] - for pattern in new_typing_target.typing_target[0] - if pattern.startswith(char) + pattern[1:] for pattern in new_typing_target.typing_target[0] if pattern.startswith(char) ] # return result @@ -55,7 +53,7 @@ def _input_char_is_correct( def _typing_is_done(typing_target: TypingTargetModel) -> bool: - '''Check if typing is done. + """Check if typing is done. Args: typing_target (TypingTargetModel): typing target. @@ -65,12 +63,11 @@ def _typing_is_done(typing_target: TypingTargetModel) -> bool: NOTE: Typing is done when typing_target.typing_target is empty. - ''' # noqa + """ # noqa return len(typing_target.typing_target) == 0 class TypingGame: - _typing_target_title_color = EColor.YELLOW _typing_target_text_color = EColor.DEFAULT _correct_user_input_color = EColor.GREEN @@ -100,14 +97,13 @@ def start(self): asyncio.run(self._main_loop()) async def _main_loop(self): - # initialize task1 = asyncio.create_task(self._sentence_generator.generate()) typing_target: TypingTargetModel = await task1 # type: ignore # typing start while True: - self._logger.debug(f'typing target: {typing_target}') + self._logger.debug(f"typing target: {typing_target}") self._show_typing_target(typing_target) task1 = asyncio.create_task(self._sentence_generator.generate()) task2 = asyncio.create_task(self._typing_step(typing_target)) @@ -116,25 +112,24 @@ async def _main_loop(self): def _show_typing_target(self, typing_target: TypingTargetModel): self._ui.show_typing_target( typing_target.text, - title='Typing Target', + title="Typing Target", color=self._typing_target_text_color, title_color=self._typing_target_title_color, ) self._ui.show_typing_target( typing_target.text_hiragana_alphabet_symbol, - title='Typing Target (Hiragana)', + title="Typing Target (Hiragana)", color=self._typing_target_text_color, title_color=self._typing_target_title_color, ) self._ui.show_typing_target( "".join([target[0] for target in typing_target.typing_target]), - title='Typing Target (Romaji)', + title="Typing Target (Romaji)", color=self._typing_target_text_color, title_color=self._typing_target_title_color, ) async def _typing_step(self, typing_target: TypingTargetModel): - # initialize self.__initialize_typing_step(typing_target.model_copy(deep=True)) assert self.__current_typing_target is not None @@ -144,7 +139,7 @@ async def _typing_step(self, typing_target: TypingTargetModel): typing_target=typing_target.model_copy(deep=True), records=self.__current_records, ) - output_path = os.path.join(self._record_direc, f'{dt.now().strftime("%Y%m%d_%H%M%S")}.json') # noqa + output_path = os.path.join(self._record_direc, f"{dt.now().strftime('%Y%m%d_%H%M%S')}.json") # noqa # start self._key_monitor.start() @@ -152,8 +147,11 @@ async def _typing_step(self, typing_target: TypingTargetModel): # wait for done # post process output.records = sorted(list(self.__current_records), key=lambda x: x.timestamp) # noqa - self._logger.debug(f'The following data has been saved to {output_path}: {output.model_dump(mode="json")}') # noqa - json.dump(output.model_dump(mode='json'), open(output_path, 'a', encoding='utf-8'), indent=4, ensure_ascii=False) # noqa + self._logger.debug(f"The following data has been saved to {output_path}: {output.model_dump(mode='json')}") # noqa + with open(output_path, "a", encoding="utf-8") as f: + json.dump( + output.model_dump(mode="json"), f, indent=4, ensure_ascii=False + ) # noqa # clean up self.__clean_up_typing_step() @@ -173,15 +171,15 @@ def __clean_up_typing_step(self): self._key_monitor.set_on_release_callback(None) def __skip_typing_step(self): - self._ui.system_anounce('SKIP!', color=self._system_anounce_color) + self._ui.system_anounce("SKIP!", color=self._system_anounce_color) self._key_monitor.stop() def __done_typing_step(self): - self._ui.system_anounce('DONE!', color=self._system_anounce_color) + self._ui.system_anounce("DONE!", color=self._system_anounce_color) self._key_monitor.stop() def __exit_typing_step(self): - self._ui.system_anounce('EXIT!', color=self._system_anounce_color) + self._ui.system_anounce("EXIT!", color=self._system_anounce_color) self._key_monitor.stop() exit(-1) @@ -196,34 +194,34 @@ def __on_press_callback( # preparation record = RecordModel( timestamp=dt.now(), - pressed_key='', + pressed_key="", is_correct=False, correct_keys=list(set([x[0] for x in self.__current_typing_target.typing_target[0] if len(x) > 0])), # noqa ) - self._logger.debug(f'current typing target: {self.__current_typing_target}') # noqa - self._logger.debug(f'correct keys: {record.correct_keys}') + self._logger.debug(f"current typing target: {self.__current_typing_target}") # noqa + self._logger.debug(f"correct keys: {record.correct_keys}") # chcek key if key == EMetaKey.ESC: - self._logger.debug('Escape key has been pressed.') + self._logger.debug("Escape key has been pressed.") elif key == EMetaKey.TAB: - self._logger.debug('Tab key has been pressed.') + self._logger.debug("Tab key has been pressed.") elif isinstance(key, str): - self._logger.debug(f'{key} key has been pressed.') + self._logger.debug(f"{key} key has been pressed.") record.pressed_key = key record.is_correct, self.__current_typing_target = _input_char_is_correct(key, self.__current_typing_target) # noqa self._ui.show_user_input(key, color=EColor.GREEN if record.is_correct else EColor.RED) # noqa else: - self._logger.debug('key is invalid.') + self._logger.debug("key is invalid.") # record - if record.pressed_key != '': + if record.pressed_key != "": self.__current_records.append(record) return None def __on_release_callback(self, key: EMetaKey | str | None) -> bool | None: - self._logger.debug(f'{key} key has been released.') + self._logger.debug(f"{key} key has been released.") # validation assert self.__current_typing_target is not None @@ -239,7 +237,7 @@ def __on_release_callback(self, key: EMetaKey | str | None) -> bool | None: # check if typing is done if _typing_is_done(self.__current_typing_target): - self._logger.debug('Typing is done.') + self._logger.debug("Typing is done.") self.__done_typing_step() return False diff --git a/simple_typing_application/ui/__init__.py b/simple_typing_application/ui/__init__.py index 0893eba..3cd66a3 100644 --- a/simple_typing_application/ui/__init__.py +++ b/simple_typing_application/ui/__init__.py @@ -13,7 +13,6 @@ base.__name__, factory.__name__, cui.__name__, - BaseUserInterface.__name__, ConsoleUserInterface.__name__, create_user_interface.__name__, diff --git a/simple_typing_application/ui/base.py b/simple_typing_application/ui/base.py index efe7636..c80a6f7 100644 --- a/simple_typing_application/ui/base.py +++ b/simple_typing_application/ui/base.py @@ -3,7 +3,6 @@ class BaseUserInterface(ABC): - @abstractmethod def show_typing_target( self, diff --git a/simple_typing_application/ui/cui.py b/simple_typing_application/ui/cui.py index b02ba84..1df0b09 100644 --- a/simple_typing_application/ui/cui.py +++ b/simple_typing_application/ui/cui.py @@ -3,7 +3,6 @@ class ConsoleUserInterface(BaseUserInterface): - def show_typing_target( self, text: str, @@ -12,26 +11,16 @@ def show_typing_target( color: EColor = EColor.DEFAULT, title_color: EColor = EColor.DEFAULT, ) -> None: - text = f'{ecolor2terminalcolor_map[color]}{text}{ecolor2terminalcolor_map[EColor.END]}' # noqa + text = f"{ecolor2terminalcolor_map[color]}{text}{ecolor2terminalcolor_map[EColor.END]}" # noqa if title: - title = f'[{ecolor2terminalcolor_map[title_color]}{title}{ecolor2terminalcolor_map[EColor.END]}]' # noqa - text = f'{title} {text}' + title = f"[{ecolor2terminalcolor_map[title_color]}{title}{ecolor2terminalcolor_map[EColor.END]}]" # noqa + text = f"{title} {text}" print(text, flush=True) - def show_user_input( - self, - text: str, - *, - color: EColor = EColor.DEFAULT - ) -> None: - text = f'{ecolor2terminalcolor_map[color]}{text}{ecolor2terminalcolor_map[EColor.END]}' # noqa - print(text, end='', flush=True) + def show_user_input(self, text: str, *, color: EColor = EColor.DEFAULT) -> None: + text = f"{ecolor2terminalcolor_map[color]}{text}{ecolor2terminalcolor_map[EColor.END]}" # noqa + print(text, end="", flush=True) - def system_anounce( - self, - text: str, - *, - color: EColor = EColor.DEFAULT - ) -> None: - text = f'{ecolor2terminalcolor_map[color]}{text}{ecolor2terminalcolor_map[EColor.END]}' # noqa + def system_anounce(self, text: str, *, color: EColor = EColor.DEFAULT) -> None: + text = f"{ecolor2terminalcolor_map[color]}{text}{ecolor2terminalcolor_map[EColor.END]}" # noqa print(text, flush=True) diff --git a/simple_typing_application/ui/factory.py b/simple_typing_application/ui/factory.py index 41f9128..0ad5ea6 100644 --- a/simple_typing_application/ui/factory.py +++ b/simple_typing_application/ui/factory.py @@ -11,11 +11,10 @@ def _select_class_and_config_model(user_interface_type: EUserInterfaceType) -> tuple[type, type]: # noqa - if user_interface_type == EUserInterfaceType.CONSOLE: return ConsoleUserInterface, ConsoleUserInterfaceConfigModel else: - raise ValueError(f'Unsupported user interface type: {user_interface_type}') # noqa + raise ValueError(f"Unsupported user interface type: {user_interface_type}") # noqa def create_user_interface( @@ -23,16 +22,17 @@ def create_user_interface( dict_config: dict[str, str | float | int | bool | None | dict | list], logger: Logger = getLogger(__name__), ) -> BaseUserInterface: - # select user interface class and config model try: user_interface_cls, user_interface_config_model = _select_class_and_config_model(user_interface_type) # noqa except NameError: - raise ImportError(f'Failed to import user interface class and config model for user_interface_type={user_interface_type}') # noqa + raise ImportError( + f"Failed to import user interface class and config model for user_interface_type={user_interface_type}" + ) # noqa # create user interface - logger.debug(f'create {user_interface_cls.__name__}') + logger.debug(f"create {user_interface_cls.__name__}") user_interface_config: BaseUserInterfaceConfigModel = user_interface_config_model(**dict_config) # noqa - user_interface: BaseUserInterface = user_interface_cls(**user_interface_config.model_dump()) # type: ignore # noqa + user_interface: BaseUserInterface = user_interface_cls(**user_interface_config.model_dump()) # type: ignore # noqa return user_interface diff --git a/simple_typing_application/utils/japanese_string_utils.py b/simple_typing_application/utils/japanese_string_utils.py index a1ae32e..6ac9489 100644 --- a/simple_typing_application/utils/japanese_string_utils.py +++ b/simple_typing_application/utils/japanese_string_utils.py @@ -6,7 +6,7 @@ def is_hiragana(c: str) -> bool: - '''Check if a character is a hiragana. + """Check if a character is a hiragana. Args: c (str): a character. @@ -24,14 +24,14 @@ def is_hiragana(c: str) -> bool: False >>> is_hiragana('a') False - ''' + """ if len(c) != 1: - raise ValueError(f'len(c) must be 1, but {len(c)}') - return 'ぁ' <= c <= 'ゔ' + raise ValueError(f"len(c) must be 1, but {len(c)}") + return "ぁ" <= c <= "ゔ" def delete_space_between_hiraganas(s: str) -> str: - '''Delete spaces between hiraganas. + """Delete spaces between hiraganas. Args: s (str): a string of hiraganas and spaces. @@ -43,15 +43,18 @@ def delete_space_between_hiraganas(s: str) -> str: >>> s = 'こ んに ち は' >>> delete_space_between_hiraganas(s) 'こんにちは' - ''' + """ if len(s) <= 2: return s return ( s[:1] - + ''.join([ - b for a, b, c in zip(s[:-2], s[1:-1], s[2:]) - if not (is_hiragana(a) and is_hiragana(c) and b in [' ', ' ']) - ]) + + "".join( + [ + b + for a, b, c in zip(s[:-2], s[1:-1], s[2:]) + if not (is_hiragana(a) and is_hiragana(c) and b in [" ", " "]) + ] + ) + s[-1:] ) @@ -60,9 +63,9 @@ def excelapi_kanji2kana( text: str, transform_katakana_to_hiragana: bool = True, max_retry: int = 3, - interval_sec: float = 3., + interval_sec: float = 3.0, ) -> str: - '''Convert kanji to kana using excelapi.org. + """Convert kanji to kana using excelapi.org. Args: text (str): a string including kanji. @@ -79,9 +82,9 @@ def excelapi_kanji2kana( NOTE: ref. https://excelapi.org/docs/language/kanji2kana/ - ''' # noqa + """ # noqa - url = f'https://api.excelapi.org/language/kanji2kana?text={text}' + url = f"https://api.excelapi.org/language/kanji2kana?text={text}" response = rerun_deco( requests.get, max_retry=max_retry, diff --git a/simple_typing_application/utils/rerun.py b/simple_typing_application/utils/rerun.py index ad6b3a2..5a5c5b4 100644 --- a/simple_typing_application/utils/rerun.py +++ b/simple_typing_application/utils/rerun.py @@ -6,7 +6,7 @@ class MaxRetryError(Exception): - def __init__(self, message: str = ''): + def __init__(self, message: str = ""): super().__init__(message) @@ -16,7 +16,7 @@ def rerun_deco( callback: Callable[..., None] | None = None, logger: Logger = getLogger(__name__), ) -> Callable[..., Any]: - '''Decorator to rerun a function when it raises an exception. + """Decorator to rerun a function when it raises an exception. Args: func (Callable[..., Any], optional): function to be decorated. Defaults to None. @@ -32,7 +32,7 @@ def rerun_deco( Note: If func is None, return a decorator. Otherwise, return a wrapper. - ''' # noqa + """ # noqa # if func is None, return a decorator if func is None: @@ -45,30 +45,36 @@ def rerun_deco( # if func is not None, return a wrapper if asyncio.iscoroutinefunction(func): + @wraps(func) async def wrapped(*args, **kwargs): for i in range(max_retry): try: return await func(*args, **kwargs) except Exception as e: - if hasattr(func, '__name__'): - logger.warning(f'[{i+1}/{max_retry}] failed to run {func.__name__}(): {e.__class__.__name__}({e})') # noqa + if hasattr(func, "__name__"): + logger.warning( + f"[{i + 1}/{max_retry}] failed to run {func.__name__}(): {e.__class__.__name__}({e})" + ) # noqa else: - logger.warning(f'[{i+1}/{max_retry}] failed to run an unnamed function: {e.__class__.__name__}({e})') # noqa + logger.warning( + f"[{i + 1}/{max_retry}] failed to run an unnamed function: {e.__class__.__name__}({e})" + ) # noqa if callback is not None: callback(*args, **kwargs) raise MaxRetryError() else: + @wraps(func) def wrapped(*args, **kwargs): for i in range(max_retry): try: return func(*args, **kwargs) except Exception as e: - if hasattr(func, '__name__'): - logger.warning(f'failed to run {func.__name__}(): {e.__class__.__name__}({e})') # noqa + if hasattr(func, "__name__"): + logger.warning(f"failed to run {func.__name__}(): {e.__class__.__name__}({e})") # noqa else: - logger.warning(f'failed to run an unnamed function: {e.__class__.__name__}({e})') # noqa + logger.warning(f"failed to run an unnamed function: {e.__class__.__name__}({e})") # noqa if callback is not None: callback(*args, **kwargs) raise MaxRetryError() diff --git a/tests/key_monitor/test_factory.py b/tests/key_monitor/test_factory.py index e567bb1..b13d683 100644 --- a/tests/key_monitor/test_factory.py +++ b/tests/key_monitor/test_factory.py @@ -7,10 +7,7 @@ PynputBasedKeyMonitorConfigModel, ) from simple_typing_application.key_monitor.sshkeyboard import SSHKeyboardBasedKeyMonitor # noqa -from simple_typing_application.key_monitor.factory import ( - create_key_monitor, - _select_class_and_config_model -) +from simple_typing_application.key_monitor.factory import create_key_monitor, _select_class_and_config_model from simple_typing_application.key_monitor.pynput import PynputBasedKeyMonitor # noqa @@ -19,14 +16,13 @@ [ (EKeyMonitorType.PYNPUT, PynputBasedKeyMonitor, PynputBasedKeyMonitorConfigModel), # noqa (EKeyMonitorType.SSHKEYBOARD, SSHKeyboardBasedKeyMonitor, SSHKeyboardBasedKeyMonitorConfigModel), # noqa - ] + ], ) def test_select_class_and_config_model( key_monitor_type: EKeyMonitorType, expected_class: type, expected_config_model: type, ): - # execute key_monitor_cls, key_monitor_config_model = _select_class_and_config_model(key_monitor_type) # noqa @@ -38,7 +34,7 @@ def test_select_class_and_config_model( def test_select_class_and_config_model_raise_value_error(): # execute with pytest.raises(ValueError): - _select_class_and_config_model('invalid_key_monitor_type') # type: ignore # noqa + _select_class_and_config_model("invalid_key_monitor_type") # type: ignore # noqa @pytest.mark.parametrize( @@ -55,7 +51,7 @@ def test_select_class_and_config_model_raise_value_error(): PynputBasedKeyMonitor, ), (EKeyMonitorType.SSHKEYBOARD, SSHKeyboardBasedKeyMonitorConfigModel().model_dump(), SSHKeyboardBasedKeyMonitor), # noqa - ] + ], ) def test_create_key_monitor( key_monitor_type: EKeyMonitorType, @@ -63,7 +59,6 @@ def test_create_key_monitor( expected_class: type, mocker, ): - # mock # for PynputBasedKeyMonitor # None @@ -79,10 +74,9 @@ def test_create_key_monitor( def test_create_key_monitor_raise_import_error(mocker): - # mock mocker.patch( - 'simple_typing_application.key_monitor.factory._select_class_and_config_model', # noqa + "simple_typing_application.key_monitor.factory._select_class_and_config_model", # noqa side_effect=NameError, ) @@ -95,16 +89,15 @@ def test_create_key_monitor_raise_import_error(mocker): def test_create_key_monitor_raise_value_error(mocker): - # mock mocker.patch( - 'simple_typing_application.key_monitor.factory._select_class_and_config_model', # noqa + "simple_typing_application.key_monitor.factory._select_class_and_config_model", # noqa side_effect=ValueError, ) # execute with pytest.raises(ValueError): create_key_monitor( - 'invalid_key_monitor_type', # type: ignore + "invalid_key_monitor_type", # type: ignore {}, ) diff --git a/tests/key_monitor/test_pynput.py b/tests/key_monitor/test_pynput.py index bd97c8c..399768d 100644 --- a/tests/key_monitor/test_pynput.py +++ b/tests/key_monitor/test_pynput.py @@ -9,7 +9,7 @@ PYNPUT_KEY_MAP: tuple[tuple[keyboard.Key | keyboard.KeyCode | None, EMetaKey | str | None], ...] = ( # noqa (keyboard.Key.esc, EMetaKey.ESC), (keyboard.Key.tab, EMetaKey.TAB), - (keyboard.Key.space, ' '), + (keyboard.Key.space, " "), (keyboard.Key.ctrl, None), (keyboard.Key.alt, None), (keyboard.Key.cmd, None), @@ -27,9 +27,9 @@ (keyboard.Key.page_up, None), (keyboard.Key.page_down, None), (keyboard.Key.insert, None), - (keyboard.KeyCode.from_char('a'), 'a'), - (keyboard.KeyCode.from_char('A'), 'A'), - (keyboard.KeyCode.from_char('#'), '#'), + (keyboard.KeyCode.from_char("a"), "a"), + (keyboard.KeyCode.from_char("A"), "A"), + (keyboard.KeyCode.from_char("#"), "#"), (None, None), ) @@ -57,11 +57,8 @@ def test__clean_key( list(PYNPUT_KEY_MAP), ) def test__on_press_callback_wrapper( - key: keyboard.Key | keyboard.KeyCode, - intemediate_expected: EMetaKey | str | None, - mocker + key: keyboard.Key | keyboard.KeyCode, intemediate_expected: EMetaKey | str | None, mocker ): - # mock mock_callback = mocker.MagicMock() @@ -81,11 +78,8 @@ def test__on_press_callback_wrapper( list(PYNPUT_KEY_MAP), ) def test__on_release_callback_wrapper( - key: keyboard.Key | keyboard.KeyCode, - intemediate_expected: EMetaKey | str | None, - mocker + key: keyboard.Key | keyboard.KeyCode, intemediate_expected: EMetaKey | str | None, mocker ): - # mock mock_callback = mocker.MagicMock() @@ -101,13 +95,9 @@ def test__on_release_callback_wrapper( def test_start(mocker): - # mock mock_keyboard_listener = mocker.MagicMock(spec=keyboard.Listener) - mocker.patch( - 'simple_typing_application.key_monitor.pynput.keyboard.Listener', - return_value=mock_keyboard_listener - ) + mocker.patch("simple_typing_application.key_monitor.pynput.keyboard.Listener", return_value=mock_keyboard_listener) # preparation key_monitor = PynputBasedKeyMonitor() @@ -121,13 +111,9 @@ def test_start(mocker): def test_stop(mocker): - # mock mock_keyboard_listener = mocker.MagicMock(spec=keyboard.Listener) - mocker.patch( - 'simple_typing_application.key_monitor.pynput.keyboard.Listener', - return_value=mock_keyboard_listener - ) + mocker.patch("simple_typing_application.key_monitor.pynput.keyboard.Listener", return_value=mock_keyboard_listener) # preparation key_monitor = PynputBasedKeyMonitor() @@ -143,13 +129,9 @@ def test_stop(mocker): def test_stop_before_start(mocker, caplog): - # mock mock_keyboard_listener = mocker.MagicMock(spec=keyboard.Listener) - mocker.patch( - 'simple_typing_application.key_monitor.pynput.keyboard.Listener', - return_value=mock_keyboard_listener - ) + mocker.patch("simple_typing_application.key_monitor.pynput.keyboard.Listener", return_value=mock_keyboard_listener) # preparation key_monitor = PynputBasedKeyMonitor() @@ -159,5 +141,5 @@ def test_stop_before_start(mocker, caplog): # assert # Test whether the warning message is output. - caplog.records[0].message == f'{key_monitor.__class__.__name__}() has not been started.' # noqa - caplog.records[0].levelname == 'WARNING' + caplog.records[0].message == f"{key_monitor.__class__.__name__}() has not been started." # noqa + caplog.records[0].levelname == "WARNING" diff --git a/tests/key_monitor/test_sshkeyboard.py b/tests/key_monitor/test_sshkeyboard.py index f0d1524..7a7129e 100644 --- a/tests/key_monitor/test_sshkeyboard.py +++ b/tests/key_monitor/test_sshkeyboard.py @@ -43,9 +43,9 @@ ("enter", None), ("space", " "), ("tab", EMetaKey.TAB), - ('a', 'a'), - ('A', 'A'), - ('#', '#'), + ("a", "a"), + ("A", "A"), + ("#", "#"), ) @@ -56,14 +56,14 @@ def test_inheritance(): def test__reset_keys_queue(): # preparation key_monitor = SSHKeyboardBasedKeyMonitor() - key_monitor._SSHKeyboardBasedKeyMonitor__keys_queue.append('a') # type: ignore # noqa - assert len(key_monitor._SSHKeyboardBasedKeyMonitor__keys_queue) > 0 # type: ignore # noqa + key_monitor._SSHKeyboardBasedKeyMonitor__keys_queue.append("a") # type: ignore # noqa + assert len(key_monitor._SSHKeyboardBasedKeyMonitor__keys_queue) > 0 # type: ignore # noqa # execute key_monitor._reset_keys_queue() # assert - assert len(key_monitor._SSHKeyboardBasedKeyMonitor__keys_queue) == 0 # type: ignore # noqa + assert len(key_monitor._SSHKeyboardBasedKeyMonitor__keys_queue) == 0 # type: ignore # noqa @pytest.mark.parametrize( @@ -84,12 +84,7 @@ def test__clean_key( "key, intemediate_expected", list(KEY_STR_MAP), ) -def test__on_press_callback_wrapper( - key: str, - intemediate_expected: EMetaKey | str | None, - mocker -): - +def test__on_press_callback_wrapper(key: str, intemediate_expected: EMetaKey | str | None, mocker): # mock mock_callback = mocker.MagicMock() @@ -108,12 +103,7 @@ def test__on_press_callback_wrapper( "key, intemediate_expected", list(KEY_STR_MAP), ) -def test__on_release_callback_wrapper( - key: str, - intemediate_expected: EMetaKey | str | None, - mocker -): - +def test__on_release_callback_wrapper(key: str, intemediate_expected: EMetaKey | str | None, mocker): # mock mock_callback = mocker.MagicMock() @@ -129,15 +119,14 @@ def test__on_release_callback_wrapper( def test_start(mocker): - # mock mock_thread = mocker.MagicMock(spec=Thread) mock_Thread = mocker.patch( - 'simple_typing_application.key_monitor.sshkeyboard.Thread', + "simple_typing_application.key_monitor.sshkeyboard.Thread", return_value=mock_thread, ) mock_listen_keyboard = mocker.patch( - 'simple_typing_application.key_monitor.sshkeyboard.listen_keyboard', + "simple_typing_application.key_monitor.sshkeyboard.listen_keyboard", ) # preparation @@ -152,7 +141,7 @@ def test_start(mocker): kwargs=dict( on_press=key_monitor._on_press_callback_wrapper, on_release=key_monitor._on_release_callback_wrapper, - ) + ), ) mock_thread.start.assert_called_once() mock_thread.join.assert_called_once() @@ -164,15 +153,14 @@ def test_stop(mocker): # mock mock_thread = mocker.MagicMock(spec=Thread) mock_Thread = mocker.patch( - 'simple_typing_application.key_monitor.sshkeyboard.Thread', + "simple_typing_application.key_monitor.sshkeyboard.Thread", return_value=mock_thread, ) mock_listen_keyboard = mocker.patch( - 'simple_typing_application.key_monitor.sshkeyboard.listen_keyboard', + "simple_typing_application.key_monitor.sshkeyboard.listen_keyboard", ) mock_stop_listening = mocker.patch( - 'simple_typing_application.key_monitor.sshkeyboard.stop_listening', - side_effect=stop_listening + "simple_typing_application.key_monitor.sshkeyboard.stop_listening", side_effect=stop_listening ) # preparation @@ -181,7 +169,7 @@ def test_stop(mocker): # execute key_monitor.start() # emulate send 'a' key - key_monitor._SSHKeyboardBasedKeyMonitor__keys_queue.append('a') # type: ignore # noqa + key_monitor._SSHKeyboardBasedKeyMonitor__keys_queue.append("a") # type: ignore # noqa mock_stop_listening.assert_not_called() key_monitor.stop() @@ -191,7 +179,7 @@ def test_stop(mocker): kwargs=dict( on_press=key_monitor._on_press_callback_wrapper, on_release=key_monitor._on_release_callback_wrapper, - ) + ), ) mock_thread.start.assert_called_once() mock_thread.join.assert_called_once() diff --git a/tests/sentence_generator/test_factory.py b/tests/sentence_generator/test_factory.py index caca5b7..a4434e7 100644 --- a/tests/sentence_generator/test_factory.py +++ b/tests/sentence_generator/test_factory.py @@ -7,6 +7,7 @@ import protobuf # type: ignore # noqa import transformers # type: ignore # noqa import sentencepiece # type: ignore # noqa + HUGGINGFACE_SETUP = True except ImportError: HUGGINGFACE_SETUP = False @@ -44,14 +45,13 @@ StaticSentenceGenerator, StaticSentenceGeneratorConfigModel, ), - ] + ], ) def test_select_class_and_config_model( sentence_generator_type: ESentenceGeneratorType, expected_class: type, expected_config_model: type, ): - # execute sentence_generator_cls, sentence_generator_config_model = _select_class_and_config_model(sentence_generator_type) # noqa @@ -63,7 +63,7 @@ def test_select_class_and_config_model( def test_select_class_and_config_model_raise_value_error(): # execute with pytest.raises(ValueError): - _select_class_and_config_model('invalid_key_monitor_type') # type: ignore # noqa + _select_class_and_config_model("invalid_key_monitor_type") # type: ignore # noqa @pytest.mark.parametrize( @@ -89,7 +89,8 @@ def test_select_class_and_config_model_raise_value_error(): {}, StaticSentenceGenerator, ), - ] + ( + ] + + ( [ ( ESentenceGeneratorType.HUGGINGFACE, @@ -102,9 +103,9 @@ def test_select_class_and_config_model_raise_value_error(): HuggingfaceSentenceGenerator, ), ] - if HUGGINGFACE_SETUP else - [] - ) + if HUGGINGFACE_SETUP + else [] + ), ) def test_create_sentence_generator( sentence_generator_type: ESentenceGeneratorType, @@ -112,15 +113,18 @@ def test_create_sentence_generator( expected_class: type, mocker, ): - # mock # for OpenaiSentenceGenerator - mocker.patch('simple_typing_application.sentence_generator.openai_sentence_generator.ChatOpenAI') # noqa + mocker.patch("simple_typing_application.sentence_generator.openai_sentence_generator.ChatOpenAI") # noqa # for HuggingfaceSentenceGenerator if HUGGINGFACE_SETUP: - mocker.patch('simple_typing_application.sentence_generator.huggingface_sentence_generator.AutoModelForCausalLM.from_pretrained') # noqa - mocker.patch('simple_typing_application.sentence_generator.huggingface_sentence_generator.AutoTokenizer.from_pretrained') # noqa - mocker.patch('simple_typing_application.sentence_generator.huggingface_sentence_generator.pipeline') # noqa + mocker.patch( + "simple_typing_application.sentence_generator.huggingface_sentence_generator.AutoModelForCausalLM.from_pretrained" + ) # noqa + mocker.patch( + "simple_typing_application.sentence_generator.huggingface_sentence_generator.AutoTokenizer.from_pretrained" + ) # noqa + mocker.patch("simple_typing_application.sentence_generator.huggingface_sentence_generator.pipeline") # noqa # for StaticSentenceGenerator # None @@ -135,10 +139,9 @@ def test_create_sentence_generator( def test_create_sentence_generator_raise_import_error(mocker): - # mock mocker.patch( - 'simple_typing_application.sentence_generator.factory._select_class_and_config_model', # noqa + "simple_typing_application.sentence_generator.factory._select_class_and_config_model", # noqa side_effect=NameError, ) @@ -151,16 +154,15 @@ def test_create_sentence_generator_raise_import_error(mocker): def test_create_sentence_generator_raise_value_error(mocker): - # mock mocker.patch( - 'simple_typing_application.sentence_generator.factory._select_class_and_config_model', # noqa + "simple_typing_application.sentence_generator.factory._select_class_and_config_model", # noqa side_effect=ValueError, ) # execute with pytest.raises(ValueError): create_sentence_generator( - 'invalid_sentence_generator_type', # type: ignore + "invalid_sentence_generator_type", # type: ignore {}, ) diff --git a/tests/sentence_generator/test_huggingface_sentence_generator.py b/tests/sentence_generator/test_huggingface_sentence_generator.py index 1e574fb..b50c7b9 100644 --- a/tests/sentence_generator/test_huggingface_sentence_generator.py +++ b/tests/sentence_generator/test_huggingface_sentence_generator.py @@ -5,6 +5,7 @@ try: import torch # type: ignore # noqa import transformers # type: ignore # noqa + HUGGINGFACE_SETUP = True except ImportError: HUGGINGFACE_SETUP = False @@ -16,7 +17,7 @@ @pytest.mark.skipif( not HUGGINGFACE_SETUP, - reason='Libs for HuggingfaceSentenceGenerator like torch is not installed.', # noqa + reason="Libs for HuggingfaceSentenceGenerator like torch is not installed.", # noqa ) def test_inheritance(): assert issubclass(HuggingfaceSentenceGenerator, BaseSentenceGenerator) @@ -24,51 +25,78 @@ def test_inheritance(): @pytest.mark.skipif( not HUGGINGFACE_SETUP, - reason='Libs for HuggingfaceSentenceGenerator like torch is not installed.', # noqa + reason="Libs for HuggingfaceSentenceGenerator like torch is not installed.", # noqa ) def test_generate(mocker): - # preparation expected = TypingTargetModel( text="text これはサンプルの文章です。", text_hiragana_alphabet_symbol="text これはさんぷるのぶんしょうです。", typing_target=[ - ["t"], ["e"], ["x"], ["t"], [" "], - ['ko', 'co'], - ['re'], - ['ha'], - ['sa'], + ["t"], + ["e"], + ["x"], + ["t"], + [" "], + ["ko", "co"], + ["re"], + ["ha"], + ["sa"], ["nnpu", "n'pu", "xnpu", "npu"], - ['ru'], - ['no'], - ['bu'], + ["ru"], + ["no"], + ["bu"], [ - "nnsyo", "n'syo", "xnsyo", "nsyo", - "nnsho", "n'sho", "xnsho", "nsho", - "nnsixyo", "n'sixyo", "xnsixyo", "nsixyo", - "nnsilyo", "n'silyo", "xnsilyo", "nsilyo", - "nnshixyo", "n'shixyo", "xnshixyo", "nshixyo", - "nnshilyo", "n'shilyo", "xnshilyo", "nshilyo", - "nncixyo", "n'cixyo", "xncixyo", "ncixyo", - "nncilyo", "n'cilyo", "xncilyo", "ncilyo", + "nnsyo", + "n'syo", + "xnsyo", + "nsyo", + "nnsho", + "n'sho", + "xnsho", + "nsho", + "nnsixyo", + "n'sixyo", + "xnsixyo", + "nsixyo", + "nnsilyo", + "n'silyo", + "xnsilyo", + "nsilyo", + "nnshixyo", + "n'shixyo", + "xnshixyo", + "nshixyo", + "nnshilyo", + "n'shilyo", + "xnshilyo", + "nshilyo", + "nncixyo", + "n'cixyo", + "xncixyo", + "ncixyo", + "nncilyo", + "n'cilyo", + "xncilyo", + "ncilyo", ], - ['u', 'wu', "whu"], - ['de'], - ['su'], - ['.'], - ] + ["u", "wu", "whu"], + ["de"], + ["su"], + ["."], + ], ) # mock - mocker.patch('simple_typing_application.sentence_generator.huggingface_sentence_generator.AutoModelForCausalLM') # noqa - mocker.patch('simple_typing_application.sentence_generator.huggingface_sentence_generator.AutoTokenizer') # noqa + mocker.patch("simple_typing_application.sentence_generator.huggingface_sentence_generator.AutoModelForCausalLM") # noqa + mocker.patch("simple_typing_application.sentence_generator.huggingface_sentence_generator.AutoTokenizer") # noqa mock_generator = mocker.MagicMock(return_value=[{"generated_text": expected.text}]) # noqa mocker.patch( - 'simple_typing_application.sentence_generator.huggingface_sentence_generator.pipeline', # noqa + "simple_typing_application.sentence_generator.huggingface_sentence_generator.pipeline", # noqa return_value=mock_generator, ) mocker.patch( - 'simple_typing_application.sentence_generator.huggingface_sentence_generator.excelapi_kanji2kana', # noqa + "simple_typing_application.sentence_generator.huggingface_sentence_generator.excelapi_kanji2kana", # noqa return_value=expected.text_hiragana_alphabet_symbol, ) @@ -86,14 +114,14 @@ def test_generate(mocker): @pytest.mark.skipif( not HUGGINGFACE_SETUP, - reason='Libs for HuggingfaceSentenceGenerator like torch is not installed.', # noqa + reason="Libs for HuggingfaceSentenceGenerator like torch is not installed.", # noqa ) @pytest.mark.integrate def test_generate_integrate(): # execute actual = asyncio.run(HuggingfaceSentenceGenerator().generate()) - logging.info(f'generated: {actual}') + logging.info(f"generated: {actual}") if len(actual.text) < 5: - logging.warning(f'generated text may be too small: {actual.text}') + logging.warning(f"generated text may be too small: {actual.text}") # TODO: assert diff --git a/tests/sentence_generator/test_openai_sentence_generator.py b/tests/sentence_generator/test_openai_sentence_generator.py index 80878d4..106a426 100644 --- a/tests/sentence_generator/test_openai_sentence_generator.py +++ b/tests/sentence_generator/test_openai_sentence_generator.py @@ -14,7 +14,6 @@ def test_inheritance(): def test__OutputSchema_build_typing_target(): - # preparation output_schema = _OutputSchema( text="text これはサンプルの文章です。", @@ -24,30 +23,58 @@ def test__OutputSchema_build_typing_target(): text="text これはサンプルの文章です。", text_hiragana_alphabet_symbol="text これはさんぷるのぶんしょうです。", typing_target=[ - ["t"], ["e"], ["x"], ["t"], [" "], - ['ko', 'co'], - ['re'], - ['ha'], - ['sa'], + ["t"], + ["e"], + ["x"], + ["t"], + [" "], + ["ko", "co"], + ["re"], + ["ha"], + ["sa"], ["nnpu", "n'pu", "xnpu", "npu"], - ['ru'], - ['no'], - ['bu'], + ["ru"], + ["no"], + ["bu"], [ - "nnsyo", "n'syo", "xnsyo", "nsyo", - "nnsho", "n'sho", "xnsho", "nsho", - "nnsixyo", "n'sixyo", "xnsixyo", "nsixyo", - "nnsilyo", "n'silyo", "xnsilyo", "nsilyo", - "nnshixyo", "n'shixyo", "xnshixyo", "nshixyo", - "nnshilyo", "n'shilyo", "xnshilyo", "nshilyo", - "nncixyo", "n'cixyo", "xncixyo", "ncixyo", - "nncilyo", "n'cilyo", "xncilyo", "ncilyo", + "nnsyo", + "n'syo", + "xnsyo", + "nsyo", + "nnsho", + "n'sho", + "xnsho", + "nsho", + "nnsixyo", + "n'sixyo", + "xnsixyo", + "nsixyo", + "nnsilyo", + "n'silyo", + "xnsilyo", + "nsilyo", + "nnshixyo", + "n'shixyo", + "xnshixyo", + "nshixyo", + "nnshilyo", + "n'shilyo", + "xnshilyo", + "nshilyo", + "nncixyo", + "n'cixyo", + "xncixyo", + "ncixyo", + "nncilyo", + "n'cilyo", + "xncilyo", + "ncilyo", ], - ['u', 'wu', "whu"], - ['de'], - ['su'], - ['.'], - ] + ["u", "wu", "whu"], + ["de"], + ["su"], + ["."], + ], ) # execute @@ -63,7 +90,6 @@ def test__OutputSchema_build_typing_target(): def test_generate(mocker): - # preparation mocker.patch( "simple_typing_application.sentence_generator.openai_sentence_generator.ChatOpenAI", # noqa @@ -76,41 +102,71 @@ def test_generate(mocker): ) sentence_generator = OpenaiSentenceGenerator() sentence_generator._agent = mocker.Mock() - sentence_generator._agent.ainvoke = mocker.AsyncMock(return_value={ - "structured_response": _OutputSchema( - text="text これはサンプルの文章です。", - text_hiragana_alphabet_symbol="text これはさんぷるのぶんしょうです。", - ) - }) + sentence_generator._agent.ainvoke = mocker.AsyncMock( + return_value={ + "structured_response": _OutputSchema( + text="text これはサンプルの文章です。", + text_hiragana_alphabet_symbol="text これはさんぷるのぶんしょうです。", + ) + } + ) expected = TypingTargetModel( text="text これはサンプルの文章です。", text_hiragana_alphabet_symbol="text これはさんぷるのぶんしょうです。", typing_target=[ - ["t"], ["e"], ["x"], ["t"], [" "], - ['ko', 'co'], - ['re'], - ['ha'], - ['sa'], + ["t"], + ["e"], + ["x"], + ["t"], + [" "], + ["ko", "co"], + ["re"], + ["ha"], + ["sa"], ["nnpu", "n'pu", "xnpu", "npu"], - ['ru'], - ['no'], - ['bu'], + ["ru"], + ["no"], + ["bu"], [ - "nnsyo", "n'syo", "xnsyo", "nsyo", - "nnsho", "n'sho", "xnsho", "nsho", - "nnsixyo", "n'sixyo", "xnsixyo", "nsixyo", - "nnsilyo", "n'silyo", "xnsilyo", "nsilyo", - "nnshixyo", "n'shixyo", "xnshixyo", "nshixyo", - "nnshilyo", "n'shilyo", "xnshilyo", "nshilyo", - "nncixyo", "n'cixyo", "xncixyo", "ncixyo", - "nncilyo", "n'cilyo", "xncilyo", "ncilyo", + "nnsyo", + "n'syo", + "xnsyo", + "nsyo", + "nnsho", + "n'sho", + "xnsho", + "nsho", + "nnsixyo", + "n'sixyo", + "xnsixyo", + "nsixyo", + "nnsilyo", + "n'silyo", + "xnsilyo", + "nsilyo", + "nnshixyo", + "n'shixyo", + "xnshixyo", + "nshixyo", + "nnshilyo", + "n'shilyo", + "xnshilyo", + "nshilyo", + "nncixyo", + "n'cixyo", + "xncixyo", + "ncixyo", + "nncilyo", + "n'cilyo", + "xncilyo", + "ncilyo", ], - ['u', 'wu', "whu"], - ['de'], - ['su'], - ['.'], - ] + ["u", "wu", "whu"], + ["de"], + ["su"], + ["."], + ], ) # execute @@ -126,10 +182,7 @@ def test_generate(mocker): @pytest.mark.integrate -@pytest.mark.skipif( - os.getenv('OPENAI_API_KEY') is None, - reason='Environment variable "OPENAI_API_KEY" is not set.' -) +@pytest.mark.skipif(os.getenv("OPENAI_API_KEY") is None, reason='Environment variable "OPENAI_API_KEY" is not set.') def test_generate_integrate(): # execute asyncio.run(OpenaiSentenceGenerator().generate()) diff --git a/tests/sentence_generator/test_static_sentence_generator.py b/tests/sentence_generator/test_static_sentence_generator.py index 3bf49a2..9dc88b7 100644 --- a/tests/sentence_generator/test_static_sentence_generator.py +++ b/tests/sentence_generator/test_static_sentence_generator.py @@ -10,14 +10,16 @@ TUP_TYPING_TARGET: tuple[TypingTargetModel, ...] = ( TypingTargetModel( - text='Hello, 世界!', - text_hiragana_alphabet_symbol='Hello、せかい!', - typing_target=list(map(sorted, [['H'], ['e'], ['l'], ['l'], ['o'], [','], ['se', 'ce'], ['ka', 'ca'], ['i', 'yi'], ['!']])), # type: ignore # noqa + text="Hello, 世界!", + text_hiragana_alphabet_symbol="Hello、せかい!", + typing_target=list( + map(sorted, [["H"], ["e"], ["l"], ["l"], ["o"], [","], ["se", "ce"], ["ka", "ca"], ["i", "yi"], ["!"]]) + ), # type: ignore # noqa ), TypingTargetModel( - text='ファーブル', - text_hiragana_alphabet_symbol='ふぁーぶる', - typing_target=list(map(sorted, [['fa', 'huxa', 'hula', 'fuxa', 'fula'], ['-'], ['bu'], ['ru']])), # type: ignore # noqa + text="ファーブル", + text_hiragana_alphabet_symbol="ふぁーぶる", + typing_target=list(map(sorted, [["fa", "huxa", "hula", "fuxa", "fula"], ["-"], ["bu"], ["ru"]])), # type: ignore # noqa ), ) @@ -47,39 +49,28 @@ def test_inhertance(): tuple( v for k, v in TUP_TYPING_TARGET[random.randint(0, len(TUP_TYPING_TARGET) - 1)].model_dump().items() # noqa - if k in [ - 'text', - 'text_hiragana_alphabet_symbol', + if k + in [ + "text", + "text_hiragana_alphabet_symbol", ] ) for _ in range(5 * len(TUP_TYPING_TARGET)) ], ), - ] + ], ) -def test__get_next( - is_random: bool, - state: tuple, - expecteds: list[tuple[str, str]], - mocker -): - +def test__get_next(is_random: bool, state: tuple, expecteds: list[tuple[str, str]], mocker): # mock mock_excelapi_kanji2kana = mocker.patch( - 'simple_typing_application.sentence_generator.static_sentence_generator.excelapi_kanji2kana', # noqa - side_effect={ - t.text: t.text_hiragana_alphabet_symbol - for t in TUP_TYPING_TARGET - }.get, + "simple_typing_application.sentence_generator.static_sentence_generator.excelapi_kanji2kana", # noqa + side_effect={t.text: t.text_hiragana_alphabet_symbol for t in TUP_TYPING_TARGET}.get, ) # preparation num_calls: int = len(expecteds) generator = StaticSentenceGenerator( - text_kana_map={ - target.text: None - for target in TUP_TYPING_TARGET - }, + text_kana_map={target.text: None for target in TUP_TYPING_TARGET}, is_random=is_random, ) # NOTE: to check whether _get_next generates targets cyclically # noqa @@ -100,10 +91,7 @@ def test__get_next( ( False, random.getstate(), - [ - TUP_TYPING_TARGET[idx % len(TUP_TYPING_TARGET)] - for idx in range(5 * len(TUP_TYPING_TARGET)) - ], + [TUP_TYPING_TARGET[idx % len(TUP_TYPING_TARGET)] for idx in range(5 * len(TUP_TYPING_TARGET))], ), ( True, @@ -113,31 +101,19 @@ def test__get_next( for _ in range(5 * len(TUP_TYPING_TARGET)) ], ), - ] + ], ) -def test_generate( - is_random: bool, - state: tuple, - expecteds: list[TypingTargetModel], - mocker -): - +def test_generate(is_random: bool, state: tuple, expecteds: list[TypingTargetModel], mocker): # mock mock_excelapi_kanji2kana = mocker.patch( - 'simple_typing_application.sentence_generator.static_sentence_generator.excelapi_kanji2kana', # noqa - side_effect={ - t.text: t.text_hiragana_alphabet_symbol - for t in TUP_TYPING_TARGET - }.get, + "simple_typing_application.sentence_generator.static_sentence_generator.excelapi_kanji2kana", # noqa + side_effect={t.text: t.text_hiragana_alphabet_symbol for t in TUP_TYPING_TARGET}.get, ) # preparation num_calls: int = len(expecteds) generator = StaticSentenceGenerator( - text_kana_map={ - target.text: None - for target in TUP_TYPING_TARGET - }, + text_kana_map={target.text: None for target in TUP_TYPING_TARGET}, is_random=is_random, ) # NOTE: to check whether _get_next generates targets cyclically # noqa diff --git a/tests/sentence_generator/test_utils.py b/tests/sentence_generator/test_utils.py index c2cd58d..21227ca 100644 --- a/tests/sentence_generator/test_utils.py +++ b/tests/sentence_generator/test_utils.py @@ -10,23 +10,23 @@ @pytest.mark.parametrize( "s,expected", [ - ("HELLO、せかい!", ['H', 'E', 'L', 'L', 'O', '、', 'せ', 'か', 'い', '!']), - ("こんにちは", ['こ', 'んに', 'ち', 'は']), - ("あっというま", ['あ', 'っと', 'い', 'う', 'ま']), - ("ふぁーぶる", ['ふぁ', 'ー', 'ぶ', 'る']), - ("あっ、なんなん?", ['あ', 'っ、', 'な', 'んな', 'ん?']), - ("しょっく", ['しょ', 'っく']), + ("HELLO、せかい!", ["H", "E", "L", "L", "O", "、", "せ", "か", "い", "!"]), + ("こんにちは", ["こ", "んに", "ち", "は"]), + ("あっというま", ["あ", "っと", "い", "う", "ま"]), + ("ふぁーぶる", ["ふぁ", "ー", "ぶ", "る"]), + ("あっ、なんなん?", ["あ", "っ、", "な", "んな", "ん?"]), + ("しょっく", ["しょ", "っく"]), ( - 'あしたはあめがふるかもしれません', - ['あ', 'し', 'た', 'は', 'あ', 'め', 'が', 'ふ', 'る', 'か', 'も', 'し', 'れ', 'ま', 'せ', 'ん'], # noqa + "あしたはあめがふるかもしれません", + ["あ", "し", "た", "は", "あ", "め", "が", "ふ", "る", "か", "も", "し", "れ", "ま", "せ", "ん"], # noqa ), - ('あっ', ['あ', 'っ']), + ("あっ", ["あ", "っ"]), # Unusual but acceptable cases - ('っっ', ['っっ']), - ('ふぁぁ', ['ふぁぁ']), # NOTE: 'ふぁぁ' is not in HIRA2ROMA_MAP. - ('あぁぁ', ['あぁぁ']), # NOTE: 'あぁぁ' is not in HIRA2ROMA_MAP. - ('っきゃぁ', ['っきゃぁ']), - ('ぁぁ', ['ぁぁ']), + ("っっ", ["っっ"]), + ("ふぁぁ", ["ふぁぁ"]), # NOTE: 'ふぁぁ' is not in HIRA2ROMA_MAP. + ("あぁぁ", ["あぁぁ"]), # NOTE: 'あぁぁ' is not in HIRA2ROMA_MAP. + ("っきゃぁ", ["っきゃぁ"]), + ("ぁぁ", ["ぁぁ"]), ], ) def test_split_hiraganas_alphabets_symbols( @@ -41,139 +41,268 @@ def test_split_hiraganas_alphabets_symbols( "splitted_patterns,expected", [ ( - ['H', 'e', 'l', 'l', 'o', '、', 'せ', 'か', 'い', '!'], - [['H'], ['e'], ['l'], ['l'], ['o'], [','], ['se', 'ce'], ['ka', 'ca'], ['i', 'yi'], ['!']] # noqa + ["H", "e", "l", "l", "o", "、", "せ", "か", "い", "!"], + [["H"], ["e"], ["l"], ["l"], ["o"], [","], ["se", "ce"], ["ka", "ca"], ["i", "yi"], ["!"]], # noqa ), ( - ['こ', 'んに', 'ち', 'は', '。'], - [['ko', 'co'], ['nnni', 'n\'ni', 'xnni'], ['ti', 'chi'], ['ha'], ['.']], # noqa + ["こ", "んに", "ち", "は", "。"], + [["ko", "co"], ["nnni", "n'ni", "xnni"], ["ti", "chi"], ["ha"], ["."]], # noqa ), ( - ['あ', 'っと', 'い', 'う', 'ま'], - [['a'], ['xtuto', 'xtsuto', 'ltuto', 'ltsuto', 'tto'], ['i', 'yi'], ['u', 'wu', 'whu'], ['ma']], # noqa + ["あ", "っと", "い", "う", "ま"], + [["a"], ["xtuto", "xtsuto", "ltuto", "ltsuto", "tto"], ["i", "yi"], ["u", "wu", "whu"], ["ma"]], # noqa ), ( - ['ふぁ', 'ー', 'ぶ', 'る'], - [['fa', 'huxa', 'hula', 'fuxa', 'fula'], ['-'], ['bu'], ['ru']], + ["ふぁ", "ー", "ぶ", "る"], + [["fa", "huxa", "hula", "fuxa", "fula"], ["-"], ["bu"], ["ru"]], ), ( - ['あ', 'っ、', 'な', 'んな', 'ん?'], - [['a'], ['xtu,', 'xtsu,', 'ltu,', 'ltsu,'], ['na'], ['nnna', 'n\'na', 'xnna'], ["nn?", "n'?", "xn?", 'n?']], # noqa + ["あ", "っ、", "な", "んな", "ん?"], + [["a"], ["xtu,", "xtsu,", "ltu,", "ltsu,"], ["na"], ["nnna", "n'na", "xnna"], ["nn?", "n'?", "xn?", "n?"]], # noqa ), ( - ['しょ', 'っく'], + ["しょ", "っく"], [ ["syo", "sho", "sixyo", "shixyo", "cixyo", "silyo", "shilyo", "cilyo"], # noqa - ["xtuku", "xtsuku", "ltuku", "ltsuku", 'kku', "xtucu", "xtsucu", "ltucu", "ltsucu", 'ccu', "xtuqu", "xtsuqu", "ltuqu", "ltsuqu", 'qqu'], # noqa + [ + "xtuku", + "xtsuku", + "ltuku", + "ltsuku", + "kku", + "xtucu", + "xtsucu", + "ltucu", + "ltsucu", + "ccu", + "xtuqu", + "xtsuqu", + "ltuqu", + "ltsuqu", + "qqu", + ], # noqa ], ), ( - list(ASCII_CHARS+"¥"), - list(ASCII_CHARS+"¥"), + list(ASCII_CHARS + "¥"), + list(ASCII_CHARS + "¥"), ), ( - ['、', '。', '・', '「', '」', 'ー'], - [[','], ['.'], ['/'], ['['], [']'], ['-']], + ["、", "。", "・", "「", "」", "ー"], + [[","], ["."], ["/"], ["["], ["]"], ["-"]], ), ( - ['ぶ', 'んしょ', 'う'], + ["ぶ", "んしょ", "う"], [ - ['bu'], + ["bu"], [ - "nnsyo", "n'syo", "xnsyo", "nsyo", - "nnsho", "n'sho", "xnsho", "nsho", - "nnsixyo", "n'sixyo", "xnsixyo", "nsixyo", - "nnsilyo", "n'silyo", "xnsilyo", "nsilyo", - "nnshixyo", "n'shixyo", "xnshixyo", "nshixyo", - "nnshilyo", "n'shilyo", "xnshilyo", "nshilyo", - "nncixyo", "n'cixyo", "xncixyo", "ncixyo", - "nncilyo", "n'cilyo", "xncilyo", "ncilyo", + "nnsyo", + "n'syo", + "xnsyo", + "nsyo", + "nnsho", + "n'sho", + "xnsho", + "nsho", + "nnsixyo", + "n'sixyo", + "xnsixyo", + "nsixyo", + "nnsilyo", + "n'silyo", + "xnsilyo", + "nsilyo", + "nnshixyo", + "n'shixyo", + "xnshixyo", + "nshixyo", + "nnshilyo", + "n'shilyo", + "xnshilyo", + "nshilyo", + "nncixyo", + "n'cixyo", + "xncixyo", + "ncixyo", + "nncilyo", + "n'cilyo", + "xncilyo", + "ncilyo", ], - ['u', 'wu', "whu"], - ] + ["u", "wu", "whu"], + ], ), ( - ['あ', 'し', 'た', 'は', 'あ', 'め', 'が', 'ふ', 'る', 'か', 'も', 'し', 'れ', 'ま', 'せ', 'ん'], # noqa + ["あ", "し", "た", "は", "あ", "め", "が", "ふ", "る", "か", "も", "し", "れ", "ま", "せ", "ん"], # noqa [ - ['a'], ['si', 'shi', 'ci'], ['ta'], [ - 'ha'], ['a'], ['me'], ['ga'], - ['hu', 'fu'], ['ru'], ['ka', 'ca'], ['mo'], - ['si', 'shi', 'ci'], ['re'], ['ma'], [ - 'se', 'ce'], ["nn", "n'", "xn"], + ["a"], + ["si", "shi", "ci"], + ["ta"], + ["ha"], + ["a"], + ["me"], + ["ga"], + ["hu", "fu"], + ["ru"], + ["ka", "ca"], + ["mo"], + ["si", "shi", "ci"], + ["re"], + ["ma"], + ["se", "ce"], + ["nn", "n'", "xn"], ], ), ( - ['あ', 'っ'], - [['a'], ['xtu', 'xtsu', 'ltu', 'ltsu']], + ["あ", "っ"], + [["a"], ["xtu", "xtsu", "ltu", "ltsu"]], ), ( - ['っっ'], - [[ - # 'っ' and 'っと' - 'xtuxtu', 'xtuxtsu', 'xtultu', 'xtultsu', - 'xtsuxtu', 'xtsuxtsu', 'xtsultu', 'xtsultsu', - 'ltuxtu', 'ltuxtsu', 'ltultu', 'ltultsu', - 'ltsuxtu', 'ltsuxtsu', 'ltsultu', 'ltsultsu', - #  'っっ' and 'と' - 'xxtu', - # 'xxtsu', # NOTE: 'xxtsu' -> 'xっ' - 'lltu', - # 'lltsu', # NOTE: 'lltsu' -> 'lっ' - ]] + ["っっ"], + [ + [ + # 'っ' and 'っと' + "xtuxtu", + "xtuxtsu", + "xtultu", + "xtultsu", + "xtsuxtu", + "xtsuxtsu", + "xtsultu", + "xtsultsu", + "ltuxtu", + "ltuxtsu", + "ltultu", + "ltultsu", + "ltsuxtu", + "ltsuxtsu", + "ltsultu", + "ltsultsu", + #  'っっ' and 'と' + "xxtu", + # 'xxtsu', # NOTE: 'xxtsu' -> 'xっ' + "lltu", + # 'lltsu', # NOTE: 'lltsu' -> 'lっ' + ] + ], ), ( - ['っっと'], - [[ - # 'っ' and 'っと' - 'xtuxtuto', 'xtuxtsuto', 'xtultuto', 'xtultsuto', 'xtutto', - 'xtsuxtuto', 'xtsuxtsuto', 'xtsultuto', 'xtsultsuto', 'xtsutto', # noqa - 'ltuxtuto', 'ltuxtsuto', 'ltultuto', 'ltultsuto', 'ltutto', - 'ltsuxtuto', 'ltsuxtsuto', 'ltsultuto', 'ltsultsuto', 'ltsutto', # noqa - #  'っっ' and 'と' - 'xxtuto', - # 'xxtsuto', # NOTE: 'xxtsuto' -> 'xっと' - 'lltuto', - # 'lltsuto', # NOTE: 'lltsuto' -> 'lっと' - ]], + ["っっと"], + [ + [ + # 'っ' and 'っと' + "xtuxtuto", + "xtuxtsuto", + "xtultuto", + "xtultsuto", + "xtutto", + "xtsuxtuto", + "xtsuxtsuto", + "xtsultuto", + "xtsultsuto", + "xtsutto", # noqa + "ltuxtuto", + "ltuxtsuto", + "ltultuto", + "ltultsuto", + "ltutto", + "ltsuxtuto", + "ltsuxtsuto", + "ltsultuto", + "ltsultsuto", + "ltsutto", # noqa + #  'っっ' and 'と' + "xxtuto", + # 'xxtsuto', # NOTE: 'xxtsuto' -> 'xっと' + "lltuto", + # 'lltsuto', # NOTE: 'lltsuto' -> 'lっと' + ] + ], ), ( - ['っあ'], - [['xtua', 'xtsua', 'ltua', 'ltsua']], + ["っあ"], + [["xtua", "xtsua", "ltua", "ltsua"]], ), ( - ['んん'], - [[ - 'nxn', 'nnnn', "n'nn", "xnnn", "nnn'", - "n'n'", "xnn'", 'nnxn', "n'xn", "xnxn", - ]], + ["んん"], + [ + [ + "nxn", + "nnnn", + "n'nn", + "xnnn", + "nnn'", + "n'n'", + "xnn'", + "nnxn", + "n'xn", + "xnxn", + ] + ], ), # Unusual but acceptable cases ( - ['ふぁぁ'], - [[ - 'faxa', 'fuxaxa', 'huxaxa', 'fulaxa', 'hulaxa', - 'fala', 'fuxala', 'huxala', 'fulala', 'hulala', - ]], + ["ふぁぁ"], + [ + [ + "faxa", + "fuxaxa", + "huxaxa", + "fulaxa", + "hulaxa", + "fala", + "fuxala", + "huxala", + "fulala", + "hulala", + ] + ], ), # NOTE: 'ふぁぁ' is not in HIRA2ROMA_MAP. ( - ['あぁぁ'], - [['axaxa', 'alaxa', 'axala', 'alala']], + ["あぁぁ"], + [["axaxa", "alaxa", "axala", "alala"]], ), # NOTE: 'あぁぁ' is not in HIRA2ROMA_MAP. ( - ['っきゃぁ'], - [[ - 'kkyaxa', "xtukyaxa", "xtsukyaxa", "ltukyaxa", "ltsukyaxa", - 'kkixyaxa', "xtukixyaxa", "xtsukixyaxa", "ltukixyaxa", "ltsukixyaxa", # noqa - 'kkilyaxa', "xtukilyaxa", "xtsukilyaxa", "ltukilyaxa", "ltsukilyaxa", # noqa - 'kkyala', "xtukyala", "xtsukyala", "ltukyala", "ltsukyala", - 'kkixyala', "xtukixyala", "xtsukixyala", "ltukixyala", "ltsukixyala", # noqa - 'kkilyala', "xtukilyala", "xtsukilyala", "ltukilyala", "ltsukilyala", # noqa - ]] + ["っきゃぁ"], + [ + [ + "kkyaxa", + "xtukyaxa", + "xtsukyaxa", + "ltukyaxa", + "ltsukyaxa", + "kkixyaxa", + "xtukixyaxa", + "xtsukixyaxa", + "ltukixyaxa", + "ltsukixyaxa", # noqa + "kkilyaxa", + "xtukilyaxa", + "xtsukilyaxa", + "ltukilyaxa", + "ltsukilyaxa", # noqa + "kkyala", + "xtukyala", + "xtsukyala", + "ltukyala", + "ltsukyala", + "kkixyala", + "xtukixyala", + "xtsukixyala", + "ltukixyala", + "ltsukixyala", # noqa + "kkilyala", + "xtukilyala", + "xtsukilyala", + "ltukilyala", + "ltsukilyala", # noqa + ] + ], ), ( - ['ぁぁ'], - [['xaxa', 'laxa', 'xala', 'lala']], - ) + ["ぁぁ"], + [["xaxa", "laxa", "xala", "lala"]], + ), ], ) def test_splitted_hiraganas_alphabets_symbols_to_typing_target( @@ -193,4 +322,4 @@ def test_splitted_hiraganas_alphabets_symbols_to_typing_target( def test_splitted_hiraganas_alphabets_symbols_to_typing_target_raise_error(): with pytest.raises(ValueError): - splitted_hiraganas_alphabets_symbols_to_typing_target(['β']) + splitted_hiraganas_alphabets_symbols_to_typing_target(["β"]) diff --git a/tests/test_config.py b/tests/test_config.py index 5395835..747cdba 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -13,9 +13,8 @@ def test_load_config_json(mocker): - # preparation - path = 'dummy.json' + path = "dummy.json" expected = ConfigModel( **{ "sentence_generator_type": "OPENAI", @@ -24,16 +23,16 @@ def test_load_config_json(mocker): "temperature": 0.7, "openai_api_key": "HERE_IS_YOUR_API_KEY", "memory_size": 1, - "max_retry": 5 + "max_retry": 5, }, "user_interface_type": "CONSOLE", "user_interface_config": {}, - "record_direc": "./record" + "record_direc": "./record", } ) # mock mocker.patch( - 'simple_typing_application.config.open', + "simple_typing_application.config.open", mock_open(read_data=expected.model_dump_json(indent=4)), ) @@ -45,15 +44,14 @@ def test_load_config_json(mocker): def test_load_config_json_not_found(mocker): - # mock mocker.patch( - 'simple_typing_application.config.open', + "simple_typing_application.config.open", side_effect=FileNotFoundError, ) # preparation - path = 'does_not_exist.json' + path = "does_not_exist.json" expected = ConfigModel() # run @@ -65,9 +63,8 @@ def test_load_config_json_not_found(mocker): def test_load_config_yaml(mocker): - # preparation - path = 'dummy.yaml' + path = "dummy.yaml" # run with pytest.raises(NotImplementedError): @@ -75,9 +72,8 @@ def test_load_config_yaml(mocker): def test_load_config_unsupported_file_type(mocker): - # preparation - path = 'dummy.txt' + path = "dummy.txt" # run with pytest.raises(ValueError): diff --git a/tests/test_typing_game.py b/tests/test_typing_game.py index b00bd02..bdce856 100644 --- a/tests/test_typing_game.py +++ b/tests/test_typing_game.py @@ -21,140 +21,139 @@ FIXED_TIMESTAMP: dt = dt(2021, 1, 1, 0, 0, 0, 0) -@pytest.fixture(scope='function') +@pytest.fixture(scope="function") def typing_game_with_mocks(mocker) -> tuple[TypingGame, dict[str, mock.MagicMock]]: # type: ignore # noqa - # create mocks mocks = dict( mock_key_monitor=mocker.MagicMock(spec=BaseKeyMonitor), mock_sentence_generator=mocker.MagicMock(spec=BaseSentenceGenerator), mock_user_interface=mocker.MagicMock(spec=BaseUserInterface), - mock_os_make_dirs=mocker.patch('simple_typing_application.typing_game.os.makedirs'), # noqa - mock_dt=mocker.patch('simple_typing_application.typing_game.dt'), - mock_exit=mocker.patch('builtins.exit'), - mock_record_direc='./dummy', - mock_open=mocker.patch('builtins.open', return_value=None), # noqa - mock_json_dump=mocker.patch('simple_typing_application.typing_game.json.dump'), # noqa + mock_os_make_dirs=mocker.patch("simple_typing_application.typing_game.os.makedirs"), # noqa + mock_dt=mocker.patch("simple_typing_application.typing_game.dt"), + mock_exit=mocker.patch("builtins.exit"), + mock_record_direc="./dummy", + mock_open=mocker.patch("builtins.open", mocker.mock_open(read_data="")), # noqa + mock_json_dump=mocker.patch("simple_typing_application.typing_game.json.dump"), # noqa ) - mocks['mock_dt'].now = mocker.Mock(return_value=FIXED_TIMESTAMP) + mocks["mock_dt"].now = mocker.Mock(return_value=FIXED_TIMESTAMP) # create typing game typing_game = TypingGame( - sentence_generator=mocks['mock_sentence_generator'], - key_monitor=mocks['mock_key_monitor'], - ui=mocks['mock_user_interface'], - record_direc=mocks['mock_record_direc'], + sentence_generator=mocks["mock_sentence_generator"], + key_monitor=mocks["mock_key_monitor"], + ui=mocks["mock_user_interface"], + record_direc=mocks["mock_record_direc"], ) yield typing_game, mocks @pytest.mark.parametrize( - 'char, typing_target, expected', + "char, typing_target, expected", [ # Alphabet Cases (True) ( - 'a', + "a", TypingTargetModel( - text='abc', # dummy - text_hiragana_alphabet_symbol='abc', # dummy - typing_target=[['a'], ['b'], ['c']], + text="abc", # dummy + text_hiragana_alphabet_symbol="abc", # dummy + typing_target=[["a"], ["b"], ["c"]], ), ( True, TypingTargetModel( - text='abc', # dummy - text_hiragana_alphabet_symbol='abc', # dummy - typing_target=[['b'], ['c']], + text="abc", # dummy + text_hiragana_alphabet_symbol="abc", # dummy + typing_target=[["b"], ["c"]], ), ), ), # Alphabet Cases (Single, True) ( - 'a', + "a", TypingTargetModel( - text='a', # dummy - text_hiragana_alphabet_symbol='a', # dummy - typing_target=[['a']], + text="a", # dummy + text_hiragana_alphabet_symbol="a", # dummy + typing_target=[["a"]], ), ( True, TypingTargetModel( - text='a', # dummy - text_hiragana_alphabet_symbol='a', # dummy + text="a", # dummy + text_hiragana_alphabet_symbol="a", # dummy typing_target=[], ), ), ), # Alphabet Cases (False) ( - 'z', + "z", TypingTargetModel( - text='abc', # dummy - text_hiragana_alphabet_symbol='abc', # dummy - typing_target=[['a'], ['b'], ['c']], + text="abc", # dummy + text_hiragana_alphabet_symbol="abc", # dummy + typing_target=[["a"], ["b"], ["c"]], ), ( False, TypingTargetModel( - text='abc', # dummy - text_hiragana_alphabet_symbol='abc', # dummy - typing_target=[['a'], ['b'], ['c']], + text="abc", # dummy + text_hiragana_alphabet_symbol="abc", # dummy + typing_target=[["a"], ["b"], ["c"]], ), ), ), # Hiragana Cases (True 1) ( - 't', + "t", TypingTargetModel( - text='ち', # dummy - text_hiragana_alphabet_symbol='ち', # dummy + text="ち", # dummy + text_hiragana_alphabet_symbol="ち", # dummy typing_target=[["ti", "chi"]], ), ( True, TypingTargetModel( - text='ち', # dummy - text_hiragana_alphabet_symbol='ち', # dummy + text="ち", # dummy + text_hiragana_alphabet_symbol="ち", # dummy typing_target=[["i"]], ), ), ), # Hiragana Cases (True 2) ( - 'c', + "c", TypingTargetModel( - text='ち', # dummy - text_hiragana_alphabet_symbol='ち', # dummy + text="ち", # dummy + text_hiragana_alphabet_symbol="ち", # dummy typing_target=[["ti", "chi"]], ), ( True, TypingTargetModel( - text='ち', # dummy - text_hiragana_alphabet_symbol='ち', # dummy + text="ち", # dummy + text_hiragana_alphabet_symbol="ち", # dummy typing_target=[["hi"]], ), ), ), # Hiragana Cases (False) ( - 'x', + "x", TypingTargetModel( - text='ち', # dummy - text_hiragana_alphabet_symbol='ち', # dummy + text="ち", # dummy + text_hiragana_alphabet_symbol="ち", # dummy typing_target=[["ti", "chi"]], ), ( False, TypingTargetModel( - text='ち', # dummy - text_hiragana_alphabet_symbol='ち', # dummy + text="ち", # dummy + text_hiragana_alphabet_symbol="ち", # dummy typing_target=[["ti", "chi"]], ), ), ), - ] + ], ) def test_input_char_is_correct( char: str, @@ -167,25 +166,25 @@ def test_input_char_is_correct( @pytest.mark.parametrize( - 'typing_target, expected', + "typing_target, expected", [ ( TypingTargetModel( - text='abc', # dummy - text_hiragana_alphabet_symbol='abc', # dummy + text="abc", # dummy + text_hiragana_alphabet_symbol="abc", # dummy typing_target=[], ), True, ), ( TypingTargetModel( - text='abc', # dummy - text_hiragana_alphabet_symbol='abc', # dummy - typing_target=[['c']], + text="abc", # dummy + text_hiragana_alphabet_symbol="abc", # dummy + typing_target=[["c"]], ), False, ), - ] + ], ) def test_typing_is_done( typing_target: TypingTargetModel, @@ -197,64 +196,61 @@ def test_typing_is_done( def test_typing_game_instantiation(typing_game_with_mocks: tuple[TypingGame, dict[str, mock.MagicMock]]): # type: ignore # noqa - # unpack typing_game, mocks = typing_game_with_mocks # assert - assert typing_game._sentence_generator is mocks['mock_sentence_generator'] - assert typing_game._key_monitor is mocks['mock_key_monitor'] - assert typing_game._ui is mocks['mock_user_interface'] - assert typing_game._record_direc == mocks['mock_record_direc'] - mocks['mock_os_make_dirs'].assert_called_once_with(mocks['mock_record_direc'], exist_ok=True) # noqa + assert typing_game._sentence_generator is mocks["mock_sentence_generator"] + assert typing_game._key_monitor is mocks["mock_key_monitor"] + assert typing_game._ui is mocks["mock_user_interface"] + assert typing_game._record_direc == mocks["mock_record_direc"] + mocks["mock_os_make_dirs"].assert_called_once_with(mocks["mock_record_direc"], exist_ok=True) # noqa def test_typing_game__show_typing_target(typing_game_with_mocks: tuple[TypingGame, dict[str, mock.MagicMock]]): # noqa - # unpack typing_game, mocks = typing_game_with_mocks # preparation typing_target = TypingTargetModel( - text='abc', - text_hiragana_alphabet_symbol='abc', - typing_target=[['a'], ['b'], ['c']], + text="abc", + text_hiragana_alphabet_symbol="abc", + typing_target=[["a"], ["b"], ["c"]], ) # execute typing_game._show_typing_target(typing_target) # assert - mocks['mock_user_interface'].show_typing_target.assert_any_call( + mocks["mock_user_interface"].show_typing_target.assert_any_call( typing_target.text, - title='Typing Target', + title="Typing Target", color=typing_game._typing_target_text_color, title_color=typing_game._typing_target_title_color, ) - mocks['mock_user_interface'].show_typing_target.assert_any_call( + mocks["mock_user_interface"].show_typing_target.assert_any_call( typing_target.text_hiragana_alphabet_symbol, - title='Typing Target (Hiragana)', + title="Typing Target (Hiragana)", color=typing_game._typing_target_text_color, title_color=typing_game._typing_target_title_color, ) - mocks['mock_user_interface'].show_typing_target.assert_any_call( + mocks["mock_user_interface"].show_typing_target.assert_any_call( "".join([target[0] for target in typing_target.typing_target]), - title='Typing Target (Romaji)', + title="Typing Target (Romaji)", color=typing_game._typing_target_text_color, title_color=typing_game._typing_target_title_color, ) def test_typing_game___initialize_typing_step(typing_game_with_mocks: tuple[TypingGame, dict[str, mock.MagicMock]]): # noqa - # unpack typing_game, mocks = typing_game_with_mocks # preparation typing_target = TypingTargetModel( - text='abc', - text_hiragana_alphabet_symbol='abc', - typing_target=[['a'], ['b'], ['c']], + text="abc", + text_hiragana_alphabet_symbol="abc", + typing_target=[["a"], ["b"], ["c"]], ) # execute @@ -264,19 +260,20 @@ def test_typing_game___initialize_typing_step(typing_game_with_mocks: tuple[Typi assert typing_game._TypingGame__current_typing_target is typing_target # type: ignore # noqa assert typing_game._TypingGame__current_records == [] # type: ignore typing_game._key_monitor.set_on_press_callback.assert_called_once_with(typing_game._TypingGame__on_press_callback) # type: ignore # noqa - typing_game._key_monitor.set_on_release_callback.assert_called_once_with(typing_game._TypingGame__on_release_callback) # type: ignore # noqa + on_release_callback_of_typing_game = typing_game._TypingGame__on_release_callback # type: ignore # noqa + typing_game._key_monitor.set_on_release_callback.assert_called_once_with(on_release_callback_of_typing_game) # type: ignore # noqa -def test_typing_game___clean_up_typing_step(typing_game_with_mocks: tuple[TypingGame, dict[str, mock.MagicMock]]): # noqa +def test_typing_game___clean_up_typing_step(typing_game_with_mocks: tuple[TypingGame, dict[str, mock.MagicMock]]): # noqa # unpack typing_game, mocks = typing_game_with_mocks # preparation typing_target = TypingTargetModel( - text='abc', - text_hiragana_alphabet_symbol='abc', - typing_target=[['a'], ['b'], ['c']], + text="abc", + text_hiragana_alphabet_symbol="abc", + typing_target=[["a"], ["b"], ["c"]], ) typing_game._TypingGame__initialize_typing_step(typing_target) # type: ignore # noqa typing_game._key_monitor.set_on_press_callback.reset_mock() # type: ignore # noqa @@ -293,7 +290,6 @@ def test_typing_game___clean_up_typing_step(typing_game_with_mocks: tuple[Typing def test_typing_game__skip_typing_step(typing_game_with_mocks: tuple[TypingGame, dict[str, mock.MagicMock]]): # noqa - # unpack typing_game, _ = typing_game_with_mocks @@ -302,14 +298,13 @@ def test_typing_game__skip_typing_step(typing_game_with_mocks: tuple[TypingGame, # assert typing_game._ui.system_anounce.assert_called_once_with( # type: ignore # noqa - 'SKIP!', + "SKIP!", color=typing_game._system_anounce_color, ) typing_game._key_monitor.stop.assert_called_once_with() # type: ignore # noqa def test_typing_game__done_typing_step(typing_game_with_mocks: tuple[TypingGame, dict[str, mock.MagicMock]]): # noqa - # unpack typing_game, _ = typing_game_with_mocks @@ -318,14 +313,13 @@ def test_typing_game__done_typing_step(typing_game_with_mocks: tuple[TypingGame, # assert typing_game._ui.system_anounce.assert_called_once_with( # type: ignore # noqa - 'DONE!', + "DONE!", color=typing_game._system_anounce_color, ) typing_game._key_monitor.stop.assert_called_once_with() # type: ignore # noqa def test_typing_game__exit_typing_step(typing_game_with_mocks: tuple[TypingGame, dict[str, mock.MagicMock]]): # noqa - # unpack typing_game, mocks = typing_game_with_mocks @@ -334,56 +328,56 @@ def test_typing_game__exit_typing_step(typing_game_with_mocks: tuple[TypingGame, # assert typing_game._ui.system_anounce.assert_called_once_with( # type: ignore - 'EXIT!', + "EXIT!", color=typing_game._system_anounce_color, ) typing_game._key_monitor.stop.assert_called_once_with() # type: ignore - mocks['mock_exit'].assert_called_once_with(-1) + mocks["mock_exit"].assert_called_once_with(-1) @pytest.mark.parametrize( - 'key,typing_target,expected___current_typing_target,expected____current_records,expected', # noqa + "key,typing_target,expected___current_typing_target,expected____current_records,expected", # noqa [ ( - 'a', + "a", TypingTargetModel( - text='abc', - text_hiragana_alphabet_symbol='abc', - typing_target=[['a'], ['b'], ['c']], + text="abc", + text_hiragana_alphabet_symbol="abc", + typing_target=[["a"], ["b"], ["c"]], ), TypingTargetModel( - text='abc', - text_hiragana_alphabet_symbol='abc', - typing_target=[['b'], ['c']], + text="abc", + text_hiragana_alphabet_symbol="abc", + typing_target=[["b"], ["c"]], ), [ RecordModel( timestamp=FIXED_TIMESTAMP, - pressed_key='a', + pressed_key="a", is_correct=True, - correct_keys=['a'], + correct_keys=["a"], ), ], None, ), ( - 'z', + "z", TypingTargetModel( - text='abc', - text_hiragana_alphabet_symbol='abc', - typing_target=[['a'], ['b'], ['c']], + text="abc", + text_hiragana_alphabet_symbol="abc", + typing_target=[["a"], ["b"], ["c"]], ), TypingTargetModel( - text='abc', - text_hiragana_alphabet_symbol='abc', - typing_target=[['a'], ['b'], ['c']], + text="abc", + text_hiragana_alphabet_symbol="abc", + typing_target=[["a"], ["b"], ["c"]], ), [ RecordModel( timestamp=FIXED_TIMESTAMP, - pressed_key='z', + pressed_key="z", is_correct=False, - correct_keys=['a'], + correct_keys=["a"], ), ], None, @@ -391,14 +385,14 @@ def test_typing_game__exit_typing_step(typing_game_with_mocks: tuple[TypingGame, ( EMetaKey.ESC, TypingTargetModel( - text='abc', - text_hiragana_alphabet_symbol='abc', - typing_target=[['a'], ['b'], ['c']], + text="abc", + text_hiragana_alphabet_symbol="abc", + typing_target=[["a"], ["b"], ["c"]], ), TypingTargetModel( - text='abc', - text_hiragana_alphabet_symbol='abc', - typing_target=[['a'], ['b'], ['c']], + text="abc", + text_hiragana_alphabet_symbol="abc", + typing_target=[["a"], ["b"], ["c"]], ), [], None, @@ -406,14 +400,14 @@ def test_typing_game__exit_typing_step(typing_game_with_mocks: tuple[TypingGame, ( EMetaKey.TAB, TypingTargetModel( - text='abc', - text_hiragana_alphabet_symbol='abc', - typing_target=[['a'], ['b'], ['c']], + text="abc", + text_hiragana_alphabet_symbol="abc", + typing_target=[["a"], ["b"], ["c"]], ), TypingTargetModel( - text='abc', - text_hiragana_alphabet_symbol='abc', - typing_target=[['a'], ['b'], ['c']], + text="abc", + text_hiragana_alphabet_symbol="abc", + typing_target=[["a"], ["b"], ["c"]], ), [], None, @@ -421,19 +415,19 @@ def test_typing_game__exit_typing_step(typing_game_with_mocks: tuple[TypingGame, ( None, TypingTargetModel( - text='abc', - text_hiragana_alphabet_symbol='abc', - typing_target=[['a'], ['b'], ['c']], + text="abc", + text_hiragana_alphabet_symbol="abc", + typing_target=[["a"], ["b"], ["c"]], ), TypingTargetModel( - text='abc', - text_hiragana_alphabet_symbol='abc', - typing_target=[['a'], ['b'], ['c']], + text="abc", + text_hiragana_alphabet_symbol="abc", + typing_target=[["a"], ["b"], ["c"]], ), [], None, - ) - ] + ), + ], ) def test_typing_game__on_press_callback( key: EMetaKey | str | None, @@ -443,7 +437,6 @@ def test_typing_game__on_press_callback( expected: bool | None, typing_game_with_mocks: tuple[TypingGame, dict[str, mock.MagicMock]], ): - # unpack typing_game, mocks = typing_game_with_mocks @@ -453,36 +446,36 @@ def test_typing_game__on_press_callback( # assert assert typing_game._TypingGame__current_typing_target == expected___current_typing_target # type: ignore # noqa - assert typing_game._TypingGame__current_records == expected____current_records # type: ignore # noqa\ + assert typing_game._TypingGame__current_records == expected____current_records # type: ignore # noqa assert actual == expected @pytest.mark.parametrize( - 'key, current_typing_target, expected', + "key, current_typing_target, expected", [ ( EMetaKey.ESC, TypingTargetModel( - text='abc', - text_hiragana_alphabet_symbol='abc', - typing_target=[['a'], ['b'], ['c']], + text="abc", + text_hiragana_alphabet_symbol="abc", + typing_target=[["a"], ["b"], ["c"]], ), False, ), ( EMetaKey.TAB, TypingTargetModel( - text='abc', - text_hiragana_alphabet_symbol='abc', - typing_target=[['a'], ['b'], ['c']], + text="abc", + text_hiragana_alphabet_symbol="abc", + typing_target=[["a"], ["b"], ["c"]], ), False, ), ( EMetaKey.ESC, TypingTargetModel( - text='abc', - text_hiragana_alphabet_symbol='abc', + text="abc", + text_hiragana_alphabet_symbol="abc", typing_target=[], ), False, @@ -490,8 +483,8 @@ def test_typing_game__on_press_callback( ( EMetaKey.TAB, TypingTargetModel( - text='abc', - text_hiragana_alphabet_symbol='abc', + text="abc", + text_hiragana_alphabet_symbol="abc", typing_target=[], ), False, @@ -499,40 +492,40 @@ def test_typing_game__on_press_callback( ( None, TypingTargetModel( - text='abc', - text_hiragana_alphabet_symbol='abc', - typing_target=[['a'], ['b'], ['c']], + text="abc", + text_hiragana_alphabet_symbol="abc", + typing_target=[["a"], ["b"], ["c"]], ), None, ), ( - 'a', + "a", TypingTargetModel( - text='abc', - text_hiragana_alphabet_symbol='abc', - typing_target=[['a'], ['b'], ['c']], + text="abc", + text_hiragana_alphabet_symbol="abc", + typing_target=[["a"], ["b"], ["c"]], ), None, ), ( None, TypingTargetModel( - text='abc', - text_hiragana_alphabet_symbol='abc', + text="abc", + text_hiragana_alphabet_symbol="abc", typing_target=[], ), False, ), ( - 'a', + "a", TypingTargetModel( - text='abc', - text_hiragana_alphabet_symbol='abc', + text="abc", + text_hiragana_alphabet_symbol="abc", typing_target=[], ), False, ), - ] + ], ) def test_typing_game__on_release_callback( key: EMetaKey | str | None, @@ -540,7 +533,6 @@ def test_typing_game__on_release_callback( expected: bool | None, typing_game_with_mocks: tuple[TypingGame, dict[str, mock.MagicMock]], ): - # unpack typing_game, mocks = typing_game_with_mocks @@ -553,52 +545,52 @@ def test_typing_game__on_release_callback( @pytest.mark.parametrize( - 'typing_target, expected_output', + "typing_target, expected_output", [ ( TypingTargetModel( - text='abc', - text_hiragana_alphabet_symbol='abc', - typing_target=[['a'], ['b'], ['c']], + text="abc", + text_hiragana_alphabet_symbol="abc", + typing_target=[["a"], ["b"], ["c"]], ), OutputModel( timestamp=FIXED_TIMESTAMP, typing_target=TypingTargetModel( - text='abc', - text_hiragana_alphabet_symbol='abc', - typing_target=[['a'], ['b'], ['c']], + text="abc", + text_hiragana_alphabet_symbol="abc", + typing_target=[["a"], ["b"], ["c"]], ), records=[ # NOTE: `sorted` is stable sort. # See https://docs.python.org/3/howto/sorting.html#sort-stability-and-complex-sorts # noqa RecordModel( timestamp=FIXED_TIMESTAMP, - pressed_key='a', + pressed_key="a", is_correct=True, - correct_keys=['a'], + correct_keys=["a"], ), RecordModel( timestamp=FIXED_TIMESTAMP, - pressed_key='b', + pressed_key="b", is_correct=True, - correct_keys=['b'], + correct_keys=["b"], ), RecordModel( timestamp=FIXED_TIMESTAMP, - pressed_key='z', + pressed_key="z", is_correct=False, - correct_keys=['c'], + correct_keys=["c"], ), RecordModel( timestamp=FIXED_TIMESTAMP, - pressed_key='c', + pressed_key="c", is_correct=True, - correct_keys=['c'], + correct_keys=["c"], ), ], - ) + ), ) - ] + ], ) def test_typing_game__typing_step( typing_target: TypingTargetModel, @@ -606,7 +598,6 @@ def test_typing_game__typing_step( typing_game_with_mocks: tuple[TypingGame, dict[str, mock.MagicMock]], mocker, ): - # unpack typing_game, mocks = typing_game_with_mocks assert typing_game._TypingGame__current_typing_target is None # type: ignore # noqa @@ -620,18 +611,15 @@ def emulate_pressing_keys(): for record in expected_output.records: typing_game._TypingGame__on_press_callback(record.pressed_key) # type: ignore # noqa typing_game._TypingGame__on_release_callback(record.pressed_key) # type: ignore # noqa - logging.debug(f'current records: {typing_game._TypingGame__current_records}') # type: ignore # noqa + logging.debug(f"current records: {typing_game._TypingGame__current_records}") # type: ignore # noqa - mocks['mock_key_monitor'].start = mocker.Mock(side_effect=emulate_pressing_keys) # type: ignore # noqa + mocks["mock_key_monitor"].start = mocker.Mock(side_effect=emulate_pressing_keys) # type: ignore # noqa # execute asyncio.run(typing_game._typing_step(typing_target)) # assert typing_game._key_monitor.start.assert_called_once_with() # type: ignore # noqa - mocks['mock_json_dump'].assert_called_once_with( - expected_output.model_dump(mode='json'), - mocks['mock_open'].return_value, - indent=4, - ensure_ascii=False + mocks["mock_json_dump"].assert_called_once_with( + expected_output.model_dump(mode="json"), mocks["mock_open"].return_value, indent=4, ensure_ascii=False ) diff --git a/tests/ui/test_cui.py b/tests/ui/test_cui.py index 8bb8bd3..3a8bc8f 100644 --- a/tests/ui/test_cui.py +++ b/tests/ui/test_cui.py @@ -3,17 +3,16 @@ def test_show_typing_target(mocker): - # mock - mock_print = mocker.patch('builtins.print') + mock_print = mocker.patch("builtins.print") # preparation cui = ConsoleUserInterface() - text = 'Hello, World!' - title = 'title' + text = "Hello, World!" + title = "title" color = EColor.RED title_color = EColor.BLUE - expected = f'[{ecolor2terminalcolor_map[EColor.BLUE]}{title}{ecolor2terminalcolor_map[EColor.END]}] {ecolor2terminalcolor_map[EColor.RED]}{text}{ecolor2terminalcolor_map[EColor.END]}' # noqa + expected = f"[{ecolor2terminalcolor_map[EColor.BLUE]}{title}{ecolor2terminalcolor_map[EColor.END]}] {ecolor2terminalcolor_map[EColor.RED]}{text}{ecolor2terminalcolor_map[EColor.END]}" # noqa # execute cui.show_typing_target(text, title=title, color=color, title_color=title_color) # noqa @@ -23,33 +22,31 @@ def test_show_typing_target(mocker): def test_show_user_input(mocker): - # mock - mock_print = mocker.patch('builtins.print') + mock_print = mocker.patch("builtins.print") # preparation cui = ConsoleUserInterface() - text = 'Hello, World!' + text = "Hello, World!" color = EColor.RED - expected = f'{ecolor2terminalcolor_map[EColor.RED]}{text}{ecolor2terminalcolor_map[EColor.END]}' # noqa + expected = f"{ecolor2terminalcolor_map[EColor.RED]}{text}{ecolor2terminalcolor_map[EColor.END]}" # noqa # execute cui.show_user_input(text, color=color) # asert - mock_print.assert_called_once_with(expected, end='', flush=True) + mock_print.assert_called_once_with(expected, end="", flush=True) def test_system_anounce(mocker): - # mock - mock_print = mocker.patch('builtins.print') + mock_print = mocker.patch("builtins.print") # preparation cui = ConsoleUserInterface() - text = 'Hello, World!' + text = "Hello, World!" color = EColor.RED - expected = f'{ecolor2terminalcolor_map[EColor.RED]}{text}{ecolor2terminalcolor_map[EColor.END]}' # noqa + expected = f"{ecolor2terminalcolor_map[EColor.RED]}{text}{ecolor2terminalcolor_map[EColor.END]}" # noqa # execute cui.system_anounce(text, color=color) diff --git a/tests/ui/test_factory.py b/tests/ui/test_factory.py index 11963ac..efc7989 100644 --- a/tests/ui/test_factory.py +++ b/tests/ui/test_factory.py @@ -13,7 +13,7 @@ @pytest.mark.parametrize( - 'user_interface_type, expected_class, expected_config_model', + "user_interface_type, expected_class, expected_config_model", [ ( EUserInterfaceType.CONSOLE, @@ -27,7 +27,6 @@ def test_select_class_and_config_model( expected_class: type, expected_config_model: type, ): - # execute user_interface_cls, user_interface_config_model = _select_class_and_config_model(user_interface_type) # noqa @@ -39,11 +38,11 @@ def test_select_class_and_config_model( def test_select_class_and_config_model_raise_value_error(): # execute with pytest.raises(ValueError): - _select_class_and_config_model('invalid_key_monitor_type') # type: ignore # noqa + _select_class_and_config_model("invalid_key_monitor_type") # type: ignore # noqa @pytest.mark.parametrize( - 'user_interface_type, user_interface_config_dict, expected_class', + "user_interface_type, user_interface_config_dict, expected_class", [ ( EUserInterfaceType.CONSOLE, @@ -78,10 +77,9 @@ def test_create_user_interface( def test_create_user_interface_raise_import_error(mocker): - # mock mocker.patch( - 'simple_typing_application.ui.factory._select_class_and_config_model', + "simple_typing_application.ui.factory._select_class_and_config_model", side_effect=NameError, ) @@ -94,16 +92,15 @@ def test_create_user_interface_raise_import_error(mocker): def test_create_user_interface_raise_value_error(mocker): - # mock mocker.patch( - 'simple_typing_application.ui.factory._select_class_and_config_model', + "simple_typing_application.ui.factory._select_class_and_config_model", side_effect=ValueError, ) # execute with pytest.raises(ValueError): create_user_interface( - 'invalid_user_interface_type', # type: ignore + "invalid_user_interface_type", # type: ignore {}, ) diff --git a/tests/utils/test_japanese_string_utils.py b/tests/utils/test_japanese_string_utils.py index 0ca684b..6bf813e 100644 --- a/tests/utils/test_japanese_string_utils.py +++ b/tests/utils/test_japanese_string_utils.py @@ -18,8 +18,8 @@ ("あ", True), ("い", True), ("ゔ", True), - (chr(ord('ぁ')-1), False), - (chr(ord('ゔ')+1), False), + (chr(ord("ぁ") - 1), False), + (chr(ord("ゔ") + 1), False), ("ア", False), ("a", False), (")", False), @@ -65,13 +65,7 @@ def test_delete_space_between_hiraganas( ("これは日本語デス。", False, "これはにほんごデス。"), ], ) -def test_excelapi_kanji2kana( - input_text: str, - transform_katakana_to_hiragana: bool, - expected: str, - mocker -): - +def test_excelapi_kanji2kana(input_text: str, transform_katakana_to_hiragana: bool, expected: str, mocker): # mock mock_response = mocker.MagicMock(spec=Response) mock_response.text = expected @@ -83,17 +77,13 @@ def test_excelapi_kanji2kana( ) # execute - actual: str = excelapi_kanji2kana( - input_text, - transform_katakana_to_hiragana=transform_katakana_to_hiragana - ) + actual: str = excelapi_kanji2kana(input_text, transform_katakana_to_hiragana=transform_katakana_to_hiragana) # assert assert actual == expected def test_excelapi_kanji2kana_request_get_raises_1error(mocker): - # preparation input_text: str = "これは日本語デス。" expected: str = "これはにほんごです。" @@ -128,7 +118,6 @@ def mock_requests_get(*args, **kwargs): def test_excelapi_kanji2kana_request_get_raises_error_always(mocker): - # preparation input_text: str = "これは日本語です。" max_retry: int = 3 @@ -146,7 +135,7 @@ def test_excelapi_kanji2kana_request_get_raises_error_always(mocker): @pytest.mark.parametrize( - 'status_code', + "status_code", [ 400, 500, @@ -177,7 +166,6 @@ def test_excelapi_kanji2kana_invalid_status_code( @pytest.mark.integrate def test_excelapi_kanji2kana_integrate(mocker): - # preparation input_text: str = "これは日本語デス。ABC abc" expected: str = "これはにほんごです。ABC abc" diff --git a/tests/utils/test_rerun.py b/tests/utils/test_rerun.py index 9b42423..7667322 100644 --- a/tests/utils/test_rerun.py +++ b/tests/utils/test_rerun.py @@ -1,13 +1,9 @@ import asyncio import pytest -from simple_typing_application.utils.rerun import ( - rerun_deco, - MaxRetryError -) +from simple_typing_application.utils.rerun import rerun_deco, MaxRetryError def test_rerun_deco(): - # preparation execution_ctr: int = 0 expected: int = 3 @@ -55,7 +51,6 @@ def func_always_raise_errors(): def test_rerun_deco_async(): - # preparation execution_ctr: int = 0 expected: int = 3 @@ -103,7 +98,6 @@ async def func_always_raise_errors(): def test_rerun_deco_with_callback(): - # preparation execution_ctr: int = 0 expected: int = 3 @@ -130,7 +124,6 @@ def func_w_errors(): def test_rerun_deco_async_with_callback(): - # preparation execution_ctr: int = 0 expected: int = 3