From e4d01a893074054e9a47cdd7d57f578d06b4299e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Feb 2026 22:42:34 +0000 Subject: [PATCH 1/7] Initial plan From a668b9de76817fe895be69de048a2b06e48ffafd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Feb 2026 22:44:43 +0000 Subject: [PATCH 2/7] Initial setup: Analyze type annotation issues Co-authored-by: bact <128572+bact@users.noreply.github.com> --- .../analysis/output/type_hint_analysis.json | 591 ++++++++++++++++-- 1 file changed, 547 insertions(+), 44 deletions(-) diff --git a/build_tools/analysis/output/type_hint_analysis.json b/build_tools/analysis/output/type_hint_analysis.json index d57b6cb60..cab0940d5 100644 --- a/build_tools/analysis/output/type_hint_analysis.json +++ b/build_tools/analysis/output/type_hint_analysis.json @@ -10,20 +10,20 @@ "pct_none": 0.0 }, "variables": { - "total": 1257, - "complete": 1183, - "none": 74, - "pct_complete": 94.11296738265712, - "pct_none": 5.88703261734288, - "class_variables": 297, - "instance_variables": 439, - "module_variables": 521 + "total": 1256, + "complete": 1110, + "none": 146, + "pct_complete": 88.37579617834395, + "pct_none": 11.624203821656051, + "class_variables": 300, + "instance_variables": 434, + "module_variables": 522 }, "type_aliases": { "total": 0 }, "classes": { - "total": 96 + "total": 97 } }, "by_submodule": { @@ -37,121 +37,121 @@ "complete": 2, "incomplete": 0, "none": 0, - "mypy_errors": 0 + "mypy_errors": 14 }, "augment": { "complete": 29, "incomplete": 0, "none": 0, - "mypy_errors": 0 + "mypy_errors": 14 }, "benchmarks": { "complete": 8, "incomplete": 0, "none": 0, - "mypy_errors": 0 + "mypy_errors": 17 }, "chat": { "complete": 4, "incomplete": 0, "none": 0, - "mypy_errors": 0 + "mypy_errors": 15 }, "classify": { "complete": 5, "incomplete": 0, "none": 0, - "mypy_errors": 0 + "mypy_errors": 14 }, "cli": { "complete": 21, "incomplete": 0, "none": 0, - "mypy_errors": 0 + "mypy_errors": 17 }, "coref": { "complete": 5, "incomplete": 0, "none": 0, - "mypy_errors": 0 + "mypy_errors": 14 }, "corpus": { "complete": 70, "incomplete": 0, "none": 0, - "mypy_errors": 0 + "mypy_errors": 14 }, "el": { "complete": 5, "incomplete": 0, "none": 0, - "mypy_errors": 0 + "mypy_errors": 14 }, "generate": { "complete": 15, "incomplete": 0, "none": 0, - "mypy_errors": 0 + "mypy_errors": 15 }, "khavee": { "complete": 9, "incomplete": 0, "none": 0, - "mypy_errors": 0 + "mypy_errors": 14 }, "lm": { "complete": 2, "incomplete": 0, "none": 0, - "mypy_errors": 0 + "mypy_errors": 14 }, "morpheme": { "complete": 2, "incomplete": 0, "none": 0, - "mypy_errors": 0 + "mypy_errors": 14 }, "parse": { "complete": 9, "incomplete": 0, "none": 0, - "mypy_errors": 0 + "mypy_errors": 14 }, "phayathaibert": { "complete": 19, "incomplete": 0, "none": 0, - "mypy_errors": 0 + "mypy_errors": 14 }, "soundex": { "complete": 27, "incomplete": 0, "none": 0, - "mypy_errors": 0 + "mypy_errors": 14 }, "spell": { "complete": 43, "incomplete": 0, "none": 0, - "mypy_errors": 0 + "mypy_errors": 14 }, "summarize": { "complete": 17, "incomplete": 0, "none": 0, - "mypy_errors": 0 + "mypy_errors": 17 }, "tag": { "complete": 73, "incomplete": 0, "none": 0, - "mypy_errors": 0 + "mypy_errors": 15 }, "tokenize": { "complete": 73, "incomplete": 0, "none": 0, - "mypy_errors": 0 + "mypy_errors": 14 }, "tokenizeicu": { "complete": 3, @@ -163,19 +163,19 @@ "complete": 9, "incomplete": 0, "none": 0, - "mypy_errors": 0 + "mypy_errors": 14 }, "translate": { "complete": 44, "incomplete": 0, "none": 0, - "mypy_errors": 0 + "mypy_errors": 14 }, "transliterate": { "complete": 75, "incomplete": 0, "none": 0, - "mypy_errors": 0 + "mypy_errors": 14 }, "transliterateicu": { "complete": 1, @@ -187,31 +187,31 @@ "complete": 25, "incomplete": 0, "none": 0, - "mypy_errors": 0 + "mypy_errors": 15 }, "util": { "complete": 109, "incomplete": 0, "none": 0, - "mypy_errors": 0 + "mypy_errors": 14 }, "wangchanberta": { "complete": 9, "incomplete": 0, "none": 0, - "mypy_errors": 0 + "mypy_errors": 14 }, "word_vector": { "complete": 7, "incomplete": 0, "none": 0, - "mypy_errors": 0 + "mypy_errors": 16 }, "wsd": { "complete": 4, "incomplete": 0, "none": 0, - "mypy_errors": 0 + "mypy_errors": 14 } }, "functions_no_hints": [], @@ -258,7 +258,70 @@ "scope": "public", "parent_class": "ChatBotModel", "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/chat/core.py", - "line": 22 + "line": 20 + }, + { + "name": "pythainlp.chat.core.ChatBotModel.history", + "scope": "public", + "parent_class": "ChatBotModel", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/chat/core.py", + "line": 24 + }, + { + "name": "pythainlp.chat.core.ChatBotModel.model", + "scope": "public", + "parent_class": "ChatBotModel", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/chat/core.py", + "line": 54 + }, + { + "name": "pythainlp.classify.param_free.GzipModel.training_data", + "scope": "public", + "parent_class": "GzipModel", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/classify/param_free.py", + "line": 38 + }, + { + "name": "pythainlp.classify.param_free.GzipModel.cx2_list", + "scope": "public", + "parent_class": "GzipModel", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/classify/param_free.py", + "line": 39 + }, + { + "name": "pythainlp.classify.param_free.GzipModel.cx2_list", + "scope": "public", + "parent_class": "GzipModel", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/classify/param_free.py", + "line": 115 + }, + { + "name": "pythainlp.classify.param_free.GzipModel.training_data", + "scope": "public", + "parent_class": "GzipModel", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/classify/param_free.py", + "line": 116 + }, + { + "name": "pythainlp.corpus.core._ResponseWrapper.status_code", + "scope": "public", + "parent_class": "_ResponseWrapper", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/corpus/core.py", + "line": 41 + }, + { + "name": "pythainlp.corpus.core._ResponseWrapper.headers", + "scope": "public", + "parent_class": "_ResponseWrapper", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/corpus/core.py", + "line": 42 + }, + { + "name": "pythainlp.corpus.core._ResponseWrapper._content", + "scope": "private", + "parent_class": "_ResponseWrapper", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/corpus/core.py", + "line": 43 }, { "name": "pythainlp.generate.core.Unigram.counts", @@ -281,12 +344,201 @@ "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/generate/core.py", "line": 85 }, + { + "name": "pythainlp.spell.words_spelling_correction.FastTextEncoder.model_dir", + "scope": "public", + "parent_class": "FastTextEncoder", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/spell/words_spelling_correction.py", + "line": 70 + }, + { + "name": "pythainlp.spell.words_spelling_correction.FastTextEncoder.nn_model_path", + "scope": "public", + "parent_class": "FastTextEncoder", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/spell/words_spelling_correction.py", + "line": 71 + }, + { + "name": "pythainlp.spell.words_spelling_correction.FastTextEncoder.bucket", + "scope": "public", + "parent_class": "FastTextEncoder", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/spell/words_spelling_correction.py", + "line": 72 + }, + { + "name": "pythainlp.spell.words_spelling_correction.FastTextEncoder.nb_words", + "scope": "public", + "parent_class": "FastTextEncoder", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/spell/words_spelling_correction.py", + "line": 73 + }, + { + "name": "pythainlp.spell.words_spelling_correction.FastTextEncoder.minn", + "scope": "public", + "parent_class": "FastTextEncoder", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/spell/words_spelling_correction.py", + "line": 74 + }, + { + "name": "pythainlp.spell.words_spelling_correction.FastTextEncoder.maxn", + "scope": "public", + "parent_class": "FastTextEncoder", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/spell/words_spelling_correction.py", + "line": 75 + }, + { + "name": "pythainlp.spell.words_spelling_correction.FastTextEncoder.words_for_suggestion", + "scope": "public", + "parent_class": "FastTextEncoder", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/spell/words_spelling_correction.py", + "line": 79 + }, + { + "name": "pythainlp.spell.words_spelling_correction.FastTextEncoder.nn_session", + "scope": "public", + "parent_class": "FastTextEncoder", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/spell/words_spelling_correction.py", + "line": 80 + }, + { + "name": "pythainlp.spell.words_spelling_correction.FastTextEncoder.embedding_dim", + "scope": "public", + "parent_class": "FastTextEncoder", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/spell/words_spelling_correction.py", + "line": 81 + }, + { + "name": "pythainlp.spell.words_spelling_correction.Words_Spelling_Correction.model_name", + "scope": "public", + "parent_class": "Words_Spelling_Correction", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/spell/words_spelling_correction.py", + "line": 269 + }, + { + "name": "pythainlp.spell.words_spelling_correction.Words_Spelling_Correction.model_path", + "scope": "public", + "parent_class": "Words_Spelling_Correction", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/spell/words_spelling_correction.py", + "line": 270 + }, + { + "name": "pythainlp.spell.words_spelling_correction.Words_Spelling_Correction.model_onnx", + "scope": "public", + "parent_class": "Words_Spelling_Correction", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/spell/words_spelling_correction.py", + "line": 271 + }, + { + "name": "pythainlp.spell.words_spelling_correction.Words_Spelling_Correction.list_word", + "scope": "public", + "parent_class": "Words_Spelling_Correction", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/spell/words_spelling_correction.py", + "line": 279 + }, + { + "name": "pythainlp.tag.crfchunk.CRFchunk.corpus", + "scope": "public", + "parent_class": "CRFchunk", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/tag/crfchunk.py", + "line": 81 + }, { "name": "pythainlp.tag.crfchunk.CRFchunk._model_file_ctx", "scope": "private", "parent_class": "CRFchunk", "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/tag/crfchunk.py", - "line": 114 + "line": 82 + }, + { + "name": "pythainlp.tag.crfchunk.CRFchunk.tagger", + "scope": "public", + "parent_class": "CRFchunk", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/tag/crfchunk.py", + "line": 86 + }, + { + "name": "pythainlp.tag.crfchunk.CRFchunk._model_file_ctx", + "scope": "private", + "parent_class": "CRFchunk", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/tag/crfchunk.py", + "line": 90 + }, + { + "name": "pythainlp.tag.crfchunk.CRFchunk.xseq", + "scope": "public", + "parent_class": "CRFchunk", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/tag/crfchunk.py", + "line": 95 + }, + { + "name": "pythainlp.tag.crfchunk.CRFchunk._model_file_ctx", + "scope": "private", + "parent_class": "CRFchunk", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/tag/crfchunk.py", + "line": 112 + }, + { + "name": "pythainlp.tag.named_entity.NER.name_engine", + "scope": "public", + "parent_class": "NER", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/tag/named_entity.py", + "line": 66 + }, + { + "name": "pythainlp.tag.named_entity.NER.engine", + "scope": "public", + "parent_class": "NER", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/tag/named_entity.py", + "line": 67 + }, + { + "name": "pythainlp.tag.named_entity.NER.engine", + "scope": "public", + "parent_class": "NER", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/tag/named_entity.py", + "line": 73 + }, + { + "name": "pythainlp.tag.named_entity.NER.engine", + "scope": "public", + "parent_class": "NER", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/tag/named_entity.py", + "line": 77 + }, + { + "name": "pythainlp.tag.named_entity.NER.engine", + "scope": "public", + "parent_class": "NER", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/tag/named_entity.py", + "line": 83 + }, + { + "name": "pythainlp.tag.named_entity.NER.engine", + "scope": "public", + "parent_class": "NER", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/tag/named_entity.py", + "line": 87 + }, + { + "name": "pythainlp.tag.named_entity.NER.engine", + "scope": "public", + "parent_class": "NER", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/tag/named_entity.py", + "line": 95 + }, + { + "name": "pythainlp.tag.named_entity.NER.engine", + "scope": "public", + "parent_class": "NER", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/tag/named_entity.py", + "line": 102 + }, + { + "name": "pythainlp.tag.named_entity.NNER.engine", + "scope": "public", + "parent_class": "NNER", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/tag/named_entity.py", + "line": 163 }, { "name": "pythainlp.tag.thainer.ThaiNameTagger.pos_tag_name", @@ -295,6 +547,62 @@ "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/tag/thainer.py", "line": 128 }, + { + "name": "pythainlp.tag.wangchanberta_onnx.WngchanBerta_ONNX.model_name", + "scope": "public", + "parent_class": "WngchanBerta_ONNX", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/tag/wangchanberta_onnx.py", + "line": 44 + }, + { + "name": "pythainlp.tag.wangchanberta_onnx.WngchanBerta_ONNX.model_version", + "scope": "public", + "parent_class": "WngchanBerta_ONNX", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/tag/wangchanberta_onnx.py", + "line": 45 + }, + { + "name": "pythainlp.tag.wangchanberta_onnx.WngchanBerta_ONNX.options", + "scope": "public", + "parent_class": "WngchanBerta_ONNX", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/tag/wangchanberta_onnx.py", + "line": 46 + }, + { + "name": "pythainlp.tag.wangchanberta_onnx.WngchanBerta_ONNX.session", + "scope": "public", + "parent_class": "WngchanBerta_ONNX", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/tag/wangchanberta_onnx.py", + "line": 50 + }, + { + "name": "pythainlp.tag.wangchanberta_onnx.WngchanBerta_ONNX.outputs_name", + "scope": "public", + "parent_class": "WngchanBerta_ONNX", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/tag/wangchanberta_onnx.py", + "line": 58 + }, + { + "name": "pythainlp.tag.wangchanberta_onnx.WngchanBerta_ONNX.sp", + "scope": "public", + "parent_class": "WngchanBerta_ONNX", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/tag/wangchanberta_onnx.py", + "line": 59 + }, + { + "name": "pythainlp.tag.wangchanberta_onnx.WngchanBerta_ONNX._json", + "scope": "private", + "parent_class": "WngchanBerta_ONNX", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/tag/wangchanberta_onnx.py", + "line": 70 + }, + { + "name": "pythainlp.tag.wangchanberta_onnx.WngchanBerta_ONNX.id2tag", + "scope": "public", + "parent_class": "WngchanBerta_ONNX", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/tag/wangchanberta_onnx.py", + "line": 71 + }, { "name": "pythainlp.tokenize.attacut.AttacutTokenizer._MODEL_NAME", "scope": "private", @@ -407,6 +715,118 @@ "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/translate/zh_th.py", "line": 50 }, + { + "name": "pythainlp.transliterate.thai2rom.ThaiTransliterator.__model_filename", + "scope": "private", + "parent_class": "ThaiTransliterator", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/transliterate/thai2rom.py", + "line": 44 + }, + { + "name": "pythainlp.transliterate.thai2rom.ThaiTransliterator._maxlength", + "scope": "private", + "parent_class": "ThaiTransliterator", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/transliterate/thai2rom.py", + "line": 51 + }, + { + "name": "pythainlp.transliterate.thai2rom.ThaiTransliterator._char_to_ix", + "scope": "private", + "parent_class": "ThaiTransliterator", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/transliterate/thai2rom.py", + "line": 53 + }, + { + "name": "pythainlp.transliterate.thai2rom.ThaiTransliterator._ix_to_char", + "scope": "private", + "parent_class": "ThaiTransliterator", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/transliterate/thai2rom.py", + "line": 54 + }, + { + "name": "pythainlp.transliterate.thai2rom.ThaiTransliterator._target_char_to_ix", + "scope": "private", + "parent_class": "ThaiTransliterator", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/transliterate/thai2rom.py", + "line": 55 + }, + { + "name": "pythainlp.transliterate.thai2rom.ThaiTransliterator._ix_to_target_char", + "scope": "private", + "parent_class": "ThaiTransliterator", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/transliterate/thai2rom.py", + "line": 56 + }, + { + "name": "pythainlp.transliterate.thai2rom.ThaiTransliterator._encoder", + "scope": "private", + "parent_class": "ThaiTransliterator", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/transliterate/thai2rom.py", + "line": 60 + }, + { + "name": "pythainlp.transliterate.thai2rom.ThaiTransliterator._decoder", + "scope": "private", + "parent_class": "ThaiTransliterator", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/transliterate/thai2rom.py", + "line": 64 + }, + { + "name": "pythainlp.transliterate.thai2rom.ThaiTransliterator._network", + "scope": "private", + "parent_class": "ThaiTransliterator", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/transliterate/thai2rom.py", + "line": 68 + }, + { + "name": "pythainlp.transliterate.thai2rom.Encoder.hidden_size", + "scope": "public", + "parent_class": "Encoder", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/transliterate/thai2rom.py", + "line": 133 + }, + { + "name": "pythainlp.transliterate.thai2rom.Encoder.character_embedding", + "scope": "public", + "parent_class": "Encoder", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/transliterate/thai2rom.py", + "line": 134 + }, + { + "name": "pythainlp.transliterate.thai2rom.Encoder.rnn", + "scope": "public", + "parent_class": "Encoder", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/transliterate/thai2rom.py", + "line": 137 + }, + { + "name": "pythainlp.transliterate.thai2rom.Encoder.dropout", + "scope": "public", + "parent_class": "Encoder", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/transliterate/thai2rom.py", + "line": 144 + }, + { + "name": "pythainlp.transliterate.thai2rom.Attn.method", + "scope": "public", + "parent_class": "Attn", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/transliterate/thai2rom.py", + "line": 204 + }, + { + "name": "pythainlp.transliterate.thai2rom.Attn.hidden_size", + "scope": "public", + "parent_class": "Attn", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/transliterate/thai2rom.py", + "line": 205 + }, + { + "name": "pythainlp.transliterate.thai2rom.Attn.attn", + "scope": "public", + "parent_class": "Attn", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/transliterate/thai2rom.py", + "line": 208 + }, { "name": "pythainlp.transliterate.thai2rom.Attn.attn", "scope": "public", @@ -414,6 +834,62 @@ "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/transliterate/thai2rom.py", "line": 211 }, + { + "name": "pythainlp.transliterate.thai2rom.Attn.other", + "scope": "public", + "parent_class": "Attn", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/transliterate/thai2rom.py", + "line": 212 + }, + { + "name": "pythainlp.transliterate.thai2rom.AttentionDecoder.vocabulary_size", + "scope": "public", + "parent_class": "AttentionDecoder", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/transliterate/thai2rom.py", + "line": 271 + }, + { + "name": "pythainlp.transliterate.thai2rom.AttentionDecoder.hidden_size", + "scope": "public", + "parent_class": "AttentionDecoder", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/transliterate/thai2rom.py", + "line": 272 + }, + { + "name": "pythainlp.transliterate.thai2rom.AttentionDecoder.character_embedding", + "scope": "public", + "parent_class": "AttentionDecoder", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/transliterate/thai2rom.py", + "line": 273 + }, + { + "name": "pythainlp.transliterate.thai2rom.AttentionDecoder.rnn", + "scope": "public", + "parent_class": "AttentionDecoder", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/transliterate/thai2rom.py", + "line": 276 + }, + { + "name": "pythainlp.transliterate.thai2rom.AttentionDecoder.attn", + "scope": "public", + "parent_class": "AttentionDecoder", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/transliterate/thai2rom.py", + "line": 283 + }, + { + "name": "pythainlp.transliterate.thai2rom.AttentionDecoder.linear", + "scope": "public", + "parent_class": "AttentionDecoder", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/transliterate/thai2rom.py", + "line": 284 + }, + { + "name": "pythainlp.transliterate.thai2rom.AttentionDecoder.dropout", + "scope": "public", + "parent_class": "AttentionDecoder", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/transliterate/thai2rom.py", + "line": 286 + }, { "name": "pythainlp.transliterate.thaig2p.Attn.attn", "scope": "public", @@ -519,6 +995,27 @@ "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/transliterate/wunsen.py", "line": 154 }, + { + "name": "pythainlp.wangchanberta.core.ThaiNameTagger.dataset_name", + "scope": "public", + "parent_class": "ThaiNameTagger", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/wangchanberta/core.py", + "line": 64 + }, + { + "name": "pythainlp.wangchanberta.core.ThaiNameTagger.grouped_entities", + "scope": "public", + "parent_class": "ThaiNameTagger", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/wangchanberta/core.py", + "line": 65 + }, + { + "name": "pythainlp.wangchanberta.core.ThaiNameTagger.classify_tokens", + "scope": "public", + "parent_class": "ThaiNameTagger", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/wangchanberta/core.py", + "line": 66 + }, { "name": "pythainlp.wangchanberta.core.ThaiNameTagger.sent_ner", "scope": "public", @@ -545,35 +1042,35 @@ "scope": "public", "parent_class": "WordVector", "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/word_vector/core.py", - "line": 59 + "line": 60 }, { "name": "pythainlp.word_vector.core.WordVector.model", "scope": "public", "parent_class": "WordVector", "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/word_vector/core.py", - "line": 60 + "line": 61 }, { "name": "pythainlp.word_vector.core.WordVector.WV_DIM", "scope": "public", "parent_class": "WordVector", "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/word_vector/core.py", - "line": 65 + "line": 66 }, { "name": "pythainlp.word_vector.core.WordVector.tokenize", "scope": "public", "parent_class": "WordVector", "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/word_vector/core.py", - "line": 68 + "line": 69 }, { "name": "pythainlp.word_vector.core.WordVector.tokenize", "scope": "public", "parent_class": "WordVector", "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/word_vector/core.py", - "line": 70 + "line": 71 }, { "name": "pythainlp.wsd.core._SentenceTransformersModel.device", @@ -633,6 +1130,12 @@ "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/spell/wanchanberta_thai_grammarly.py", "line": 106 }, + { + "name": "pythainlp.tag.named_entity.NEREngineType", + "scope": "public", + "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/tag/named_entity.py", + "line": 24 + }, { "name": "pythainlp.transliterate.royin._vowel_patterns", "scope": "private", From 4f32ec38226f6b25818182b7cb8dd644bb8148d6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Feb 2026 22:48:34 +0000 Subject: [PATCH 3/7] Fix type annotation issues: Remove unused type:ignore comments and fix type errors - Remove unused type:ignore[no-any-return] comments (11 locations) - Remove unused type:ignore[import-untyped] comment - Fix type assignment error in benchmarks/word_tokenization.py - Remove redundant cast in chat/core.py - Add runtime check for None engine in tag/named_entity.py - Add proper type:ignore[no-redef] for conditional imports in tools/path.py - Add explicit casts for nlpo3 return types - All mypy checks pass (0 errors) - All ruff checks pass - All pyrefly checks pass with --ignore-missing-imports Co-authored-by: bact <128572+bact@users.noreply.github.com> --- pythainlp/benchmarks/word_tokenization.py | 8 ++++---- pythainlp/chat/core.py | 4 ++-- pythainlp/summarize/keybert.py | 6 +++--- pythainlp/tag/named_entity.py | 6 ++++-- pythainlp/tag/wangchanberta_onnx.py | 2 +- pythainlp/tokenize/nlpo3.py | 17 ++++++++++------- pythainlp/tools/path.py | 2 +- pythainlp/transliterate/core.py | 2 +- pythainlp/transliterate/thai2rom_onnx.py | 8 ++++---- pythainlp/transliterate/w2p.py | 6 +++--- pythainlp/ulmfit/core.py | 2 +- pythainlp/word_vector/core.py | 4 ++-- 12 files changed, 36 insertions(+), 31 deletions(-) diff --git a/pythainlp/benchmarks/word_tokenization.py b/pythainlp/benchmarks/word_tokenization.py index 1a0a3d484..356717cdf 100644 --- a/pythainlp/benchmarks/word_tokenization.py +++ b/pythainlp/benchmarks/word_tokenization.py @@ -85,10 +85,10 @@ def benchmark(ref_samples: list[str], samples: list[str]) -> "pd.DataFrame": r, s = preprocessing(r), preprocessing(s) if r and s: stats = compute_stats(r, s) - stats = _flatten_result(stats) - stats["expected"] = r - stats["actual"] = s - results.append(stats) + flat_stats: dict[str, Union[int, str]] = _flatten_result(stats) + flat_stats["expected"] = r + flat_stats["actual"] = s + results.append(flat_stats) except: reason = """ [Error] diff --git a/pythainlp/chat/core.py b/pythainlp/chat/core.py index a5a699d92..d7dce64ad 100644 --- a/pythainlp/chat/core.py +++ b/pythainlp/chat/core.py @@ -3,7 +3,7 @@ # SPDX-License-Identifier: Apache-2.0 from __future__ import annotations -from typing import TYPE_CHECKING, Optional, cast +from typing import TYPE_CHECKING, Optional if TYPE_CHECKING: import torch @@ -97,6 +97,6 @@ def chat(self, text: str) -> str: _temp += self.model.PROMPT_DICT["prompt_chatbot"].format_map( {"human": text, "bot": ""} ) - _bot = cast(str, self.model.gen_instruct(_temp)) + _bot = self.model.gen_instruct(_temp) self.history.append((text, _bot)) return _bot diff --git a/pythainlp/summarize/keybert.py b/pythainlp/summarize/keybert.py index 08c8218d3..46f72ccf9 100644 --- a/pythainlp/summarize/keybert.py +++ b/pythainlp/summarize/keybert.py @@ -155,7 +155,7 @@ def embed(self, docs: Union[str, list[str]]) -> np.ndarray: [np.array(emb[0]).mean(axis=0) for emb in embs] ) - return emb_mean # type: ignore[no-any-return] + return emb_mean def _generate_ngrams( @@ -224,10 +224,10 @@ def l2_norm(v: np.ndarray) -> np.ndarray: ) if not np.isclose(np.linalg.norm(result, axis=1), 1).all(): raise ValueError("Cannot normalize a vector to unit vector.") - return result # type: ignore[no-any-return] + return result def cosine_sim(a: np.ndarray, b: np.ndarray) -> np.ndarray: - return (np.matmul(a, b.T).T).sum(axis=1) # type: ignore[no-any-return] + return (np.matmul(a, b.T).T).sum(axis=1) doc_vector = l2_norm(doc_vector) word_vectors = l2_norm(word_vectors) diff --git a/pythainlp/tag/named_entity.py b/pythainlp/tag/named_entity.py index 4f519e0bf..beb8ebedb 100644 --- a/pythainlp/tag/named_entity.py +++ b/pythainlp/tag/named_entity.py @@ -139,7 +139,9 @@ def tag( >>> ner.tag("ทดสอบ นายวรรณพงษ์ ภัททิยไพบูลย์", tag=True) 'ทดสอบ นายวรรณพงษ์ ภัททิยไพบูลย์' """ - return self.engine.get_ner(text, tag=tag, pos=pos) # type: ignore[no-any-return] + if self.engine is None: + raise RuntimeError("Engine not initialized") + return self.engine.get_ner(text, tag=tag, pos=pos) class NNER: @@ -223,4 +225,4 @@ def tag( >>> nner.tag("แมวทำอะไรตอนห้าโมงเช้า", top_level_only=True) ([...], [{'text': ['', 'ห้า', '', 'โมง'], 'span': [7, 11], 'entity_type': 'time'}]) """ - return self.engine.tag(text, top_level_only=top_level_only) # type: ignore[no-any-return] + return self.engine.tag(text, top_level_only=top_level_only) diff --git a/pythainlp/tag/wangchanberta_onnx.py b/pythainlp/tag/wangchanberta_onnx.py index 26bf710a3..4b76f2d49 100644 --- a/pythainlp/tag/wangchanberta_onnx.py +++ b/pythainlp/tag/wangchanberta_onnx.py @@ -88,7 +88,7 @@ def postprocess(self, logits_data: "np.ndarray") -> "np.ndarray": maxes = np.max(logits_t, axis=-1, keepdims=True) shifted_exp = np.exp(logits_t - maxes) scores = shifted_exp / shifted_exp.sum(axis=-1, keepdims=True) - return scores # type: ignore[no-any-return] + return scores def clean_output( self, list_text: list[tuple[str, str]] diff --git a/pythainlp/tokenize/nlpo3.py b/pythainlp/tokenize/nlpo3.py index 8e3b948ed..dcd7ac536 100644 --- a/pythainlp/tokenize/nlpo3.py +++ b/pythainlp/tokenize/nlpo3.py @@ -6,7 +6,7 @@ import threading from importlib.resources import as_file, files from sys import stderr -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Any, Optional, cast if TYPE_CHECKING: from nlpo3 import ( @@ -89,7 +89,7 @@ def load_dict(file_path: str, dict_name: str) -> bool: msg, success = nlpo3_load_dict(file_path=file_path, dict_name=dict_name) if not success: print(msg, file=stderr) - return success + return cast(bool, success) def segment( @@ -127,9 +127,12 @@ def segment( if custom_dict == _NLPO3_DEFAULT_DICT_NAME: _ensure_default_dict_loaded() - return nlpo3_segment( - text=text, - dict_name=custom_dict, - safe=safe_mode, - parallel=parallel_mode, + return cast( + list[str], + nlpo3_segment( + text=text, + dict_name=custom_dict, + safe=safe_mode, + parallel=parallel_mode, + ), ) diff --git a/pythainlp/tools/path.py b/pythainlp/tools/path.py index 608ac4396..0ab6ac35b 100644 --- a/pythainlp/tools/path.py +++ b/pythainlp/tools/path.py @@ -16,7 +16,7 @@ if version_info >= (3, 11): from importlib.resources import files # Available in Python 3.11+ else: - from importlib_resources import files # noqa: I001 + from importlib_resources import files # type: ignore[no-redef] # noqa: I001 PYTHAINLP_DEFAULT_DATA_DIR: str = "pythainlp-data" diff --git a/pythainlp/transliterate/core.py b/pythainlp/transliterate/core.py index 2f997f034..ba5cbbece 100644 --- a/pythainlp/transliterate/core.py +++ b/pythainlp/transliterate/core.py @@ -179,7 +179,7 @@ def transliterate( elif engine == "thaig2p_v2": from pythainlp.transliterate.thaig2p_v2 import transliterate # noqa: I001 elif engine == "umt5_thaig2p": - from pythainlp.translate.umt5_thaig2p import transliterate # type: ignore[import-untyped,no-redef] # noqa: I001 + from pythainlp.translate.umt5_thaig2p import transliterate # type: ignore[no-redef] # noqa: I001 else: # use default engine: "thaig2p" from pythainlp.transliterate.thaig2p import transliterate # noqa: I001 diff --git a/pythainlp/transliterate/thai2rom_onnx.py b/pythainlp/transliterate/thai2rom_onnx.py index 9070ce285..64c41b065 100644 --- a/pythainlp/transliterate/thai2rom_onnx.py +++ b/pythainlp/transliterate/thai2rom_onnx.py @@ -76,7 +76,7 @@ def _prepare_sequence_in(self, text: str) -> "np.ndarray": else: idxs.append(self._char_to_ix[""]) idxs.append(self._char_to_ix[""]) - return np.array(idxs) # type: ignore[no-any-return] + return np.array(idxs) def romanize(self, text: str) -> str: """:param str text: Thai text to be romanized @@ -131,7 +131,7 @@ def __init__( def create_mask(self, source_seq: "np.ndarray") -> "np.ndarray": mask = source_seq != self.pad_idx - return mask # type: ignore[no-any-return] + return mask def run( self, source_seq: "np.ndarray", source_seq_len: List[int] @@ -196,9 +196,9 @@ def run( decoder_input = np.array([topi]) if decoder_input == end_token: - return outputs[:di] # type: ignore[no-any-return] + return outputs[:di] - return outputs # type: ignore[no-any-return] + return outputs _THAI_TO_ROM_ONNX: ThaiTransliterator_ONNX = ThaiTransliterator_ONNX() diff --git a/pythainlp/transliterate/w2p.py b/pythainlp/transliterate/w2p.py index 10778a390..9ad03c49a 100644 --- a/pythainlp/transliterate/w2p.py +++ b/pythainlp/transliterate/w2p.py @@ -151,7 +151,7 @@ def _load_variables(self) -> None: def _sigmoid(self, x: "np.ndarray") -> "np.ndarray": import numpy as np - return 1 / (1 + np.exp(-x)) # type: ignore[no-any-return] + return 1 / (1 + np.exp(-x)) def _grucell( self, @@ -205,7 +205,7 @@ def _gru( h = self._grucell(x[:, t, :], h, w_ih, w_hh, b_ih, b_hh) # (b, h) outputs[:, t, ::] = h - return outputs # type: ignore[no-any-return] + return outputs def _encode(self, word: str) -> "np.ndarray": import numpy as np @@ -214,7 +214,7 @@ def _encode(self, word: str) -> "np.ndarray": x = [self.g2idx.get(char, self.g2idx[""]) for char in chars] x = np.take(self.enc_emb, np.expand_dims(x, 0), axis=0) - return x # type: ignore[no-any-return] + return x def _short_word(self, word: str) -> Optional[str]: self.word: str = word diff --git a/pythainlp/ulmfit/core.py b/pythainlp/ulmfit/core.py index 4761e3c1b..c6a85f62f 100644 --- a/pythainlp/ulmfit/core.py +++ b/pythainlp/ulmfit/core.py @@ -241,7 +241,7 @@ def document_vector( else: raise ValueError("Aggregate by mean or sum") - return res # type: ignore[no-any-return] + return res def merge_wgts( diff --git a/pythainlp/word_vector/core.py b/pythainlp/word_vector/core.py index d292f4751..119517b5d 100644 --- a/pythainlp/word_vector/core.py +++ b/pythainlp/word_vector/core.py @@ -307,7 +307,7 @@ def sentence_vectorizer(self, text: str, use_mean: bool = True) -> ndarray: len_words = len(words) if not len_words: - return vec # type: ignore[no-any-return] + return vec for word in words: if word == " " and self.model_name == "thai2fit_wv": @@ -321,4 +321,4 @@ def sentence_vectorizer(self, text: str, use_mean: bool = True) -> ndarray: if use_mean: vec /= len_words - return vec # type: ignore[no-any-return] + return vec From 04340461501f361fab832d66c6745118848531e4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Feb 2026 23:02:35 +0000 Subject: [PATCH 4/7] Test verification complete: All accessible tests pass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Core tests: 114/114 pass ✓ - Compact tests: 19/19 pass ✓ - Extra tests: 32/32 pass ✓ - Noauto tests (simple tokenizers): 8/8 pass ✓ - Tested: attacut, deepcut, oskut, sefr_cut - Noauto tests (network-dependent): Unable to run due to network restrictions - Tests requiring HuggingFace model downloads fail with OSError - These tests need offline mode or pre-downloaded models Co-authored-by: bact <128572+bact@users.noreply.github.com> --- .../analysis/output/type_hint_analysis.json | 58 +++++++++---------- tests/data/eval-details-input.json | 2 +- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/build_tools/analysis/output/type_hint_analysis.json b/build_tools/analysis/output/type_hint_analysis.json index cab0940d5..ada4f46ec 100644 --- a/build_tools/analysis/output/type_hint_analysis.json +++ b/build_tools/analysis/output/type_hint_analysis.json @@ -37,121 +37,121 @@ "complete": 2, "incomplete": 0, "none": 0, - "mypy_errors": 14 + "mypy_errors": 9 }, "augment": { "complete": 29, "incomplete": 0, "none": 0, - "mypy_errors": 14 + "mypy_errors": 9 }, "benchmarks": { "complete": 8, "incomplete": 0, "none": 0, - "mypy_errors": 17 + "mypy_errors": 9 }, "chat": { "complete": 4, "incomplete": 0, "none": 0, - "mypy_errors": 15 + "mypy_errors": 9 }, "classify": { "complete": 5, "incomplete": 0, "none": 0, - "mypy_errors": 14 + "mypy_errors": 9 }, "cli": { "complete": 21, "incomplete": 0, "none": 0, - "mypy_errors": 17 + "mypy_errors": 9 }, "coref": { "complete": 5, "incomplete": 0, "none": 0, - "mypy_errors": 14 + "mypy_errors": 9 }, "corpus": { "complete": 70, "incomplete": 0, "none": 0, - "mypy_errors": 14 + "mypy_errors": 9 }, "el": { "complete": 5, "incomplete": 0, "none": 0, - "mypy_errors": 14 + "mypy_errors": 9 }, "generate": { "complete": 15, "incomplete": 0, "none": 0, - "mypy_errors": 15 + "mypy_errors": 10 }, "khavee": { "complete": 9, "incomplete": 0, "none": 0, - "mypy_errors": 14 + "mypy_errors": 9 }, "lm": { "complete": 2, "incomplete": 0, "none": 0, - "mypy_errors": 14 + "mypy_errors": 9 }, "morpheme": { "complete": 2, "incomplete": 0, "none": 0, - "mypy_errors": 14 + "mypy_errors": 9 }, "parse": { "complete": 9, "incomplete": 0, "none": 0, - "mypy_errors": 14 + "mypy_errors": 9 }, "phayathaibert": { "complete": 19, "incomplete": 0, "none": 0, - "mypy_errors": 14 + "mypy_errors": 9 }, "soundex": { "complete": 27, "incomplete": 0, "none": 0, - "mypy_errors": 14 + "mypy_errors": 9 }, "spell": { "complete": 43, "incomplete": 0, "none": 0, - "mypy_errors": 14 + "mypy_errors": 9 }, "summarize": { "complete": 17, "incomplete": 0, "none": 0, - "mypy_errors": 17 + "mypy_errors": 12 }, "tag": { "complete": 73, "incomplete": 0, "none": 0, - "mypy_errors": 15 + "mypy_errors": 10 }, "tokenize": { "complete": 73, "incomplete": 0, "none": 0, - "mypy_errors": 14 + "mypy_errors": 9 }, "tokenizeicu": { "complete": 3, @@ -163,19 +163,19 @@ "complete": 9, "incomplete": 0, "none": 0, - "mypy_errors": 14 + "mypy_errors": 9 }, "translate": { "complete": 44, "incomplete": 0, "none": 0, - "mypy_errors": 14 + "mypy_errors": 9 }, "transliterate": { "complete": 75, "incomplete": 0, "none": 0, - "mypy_errors": 14 + "mypy_errors": 9 }, "transliterateicu": { "complete": 1, @@ -187,31 +187,31 @@ "complete": 25, "incomplete": 0, "none": 0, - "mypy_errors": 15 + "mypy_errors": 10 }, "util": { "complete": 109, "incomplete": 0, "none": 0, - "mypy_errors": 14 + "mypy_errors": 9 }, "wangchanberta": { "complete": 9, "incomplete": 0, "none": 0, - "mypy_errors": 14 + "mypy_errors": 9 }, "word_vector": { "complete": 7, "incomplete": 0, "none": 0, - "mypy_errors": 16 + "mypy_errors": 11 }, "wsd": { "complete": 4, "incomplete": 0, "none": 0, - "mypy_errors": 14 + "mypy_errors": 9 } }, "functions_no_hints": [], @@ -538,7 +538,7 @@ "scope": "public", "parent_class": "NNER", "file": "/home/runner/work/pythainlp/pythainlp/pythainlp/tag/named_entity.py", - "line": 163 + "line": 165 }, { "name": "pythainlp.tag.thainer.ThaiNameTagger.pos_tag_name", diff --git a/tests/data/eval-details-input.json b/tests/data/eval-details-input.json index f3efd3861..677193620 100644 --- a/tests/data/eval-details-input.json +++ b/tests/data/eval-details-input.json @@ -1 +1 @@ -{"metrics": {"char_level:tp": 4.0, "char_level:fp": 0.0, "char_level:tn": 9.0, "char_level:fn": 1.0, "word_level:correctly_tokenised_words": 3.0, "word_level:total_words_in_sample": 4.0, "word_level:total_words_in_ref_sample": 5.0, "char_level:precision": 1.0, "char_level:recall": 0.8, "word_level:precision": 0.75, "word_level:recall": 0.6}, "samples": [{"metrics": {"char_level:tp": 4, "char_level:fp": 0, "char_level:tn": 9, "char_level:fn": 1, "word_level:correctly_tokenised_words": 3, "word_level:total_words_in_sample": 4.0, "word_level:total_words_in_ref_sample": 5.0, "global:tokenisation_indicators": "1011"}, "expected": "ผม|ไม่|ชอบ|กิน|ผัก", "actual": "ผม|ไม่ชอบ|กิน|ผัก", "id": 0}]} \ No newline at end of file +{"metrics": {"char_level:tp": 4.0, "char_level:fp": 0.0, "char_level:tn": 9.0, "char_level:fn": 1.0, "word_level:correctly_tokenised_words": 3.0, "word_level:total_words_in_sample": 4.0, "word_level:total_words_in_ref_sample": 5.0, "char_level:precision": 1.0, "char_level:recall": 0.8, "word_level:precision": 0.75, "word_level:recall": 0.6}, "samples": [{"metrics": {"char_level:tp": 4, "char_level:fp": 0, "char_level:tn": 9, "char_level:fn": 1, "word_level:correctly_tokenised_words": 3, "word_level:total_words_in_sample": 4, "word_level:total_words_in_ref_sample": 5, "global:tokenisation_indicators": "1011"}, "expected": "ผม|ไม่|ชอบ|กิน|ผัก", "actual": "ผม|ไม่ชอบ|กิน|ผัก", "id": 0}]} \ No newline at end of file From 5bc1d72c0b169559a07d5978386c6591085ef62c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Feb 2026 23:20:40 +0000 Subject: [PATCH 5/7] Fix nlpo3 type annotations: Use explicit type annotations instead of cast - Add explicit type annotations for msg and success variables from nlpo3_load_dict() - Remove unnecessary cast(bool, success) since nlpo3.load_dict() returns tuple[str, bool] - This makes the code cleaner and more explicit about the return types - All mypy checks pass (191 files) - All tests pass (core: 114, compact: 19) Co-authored-by: bact <128572+bact@users.noreply.github.com> --- pythainlp/tokenize/nlpo3.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pythainlp/tokenize/nlpo3.py b/pythainlp/tokenize/nlpo3.py index dcd7ac536..3c114d3ac 100644 --- a/pythainlp/tokenize/nlpo3.py +++ b/pythainlp/tokenize/nlpo3.py @@ -52,6 +52,8 @@ def _ensure_default_dict_loaded() -> None: dict_file = corpus_files.joinpath(_THAI_WORDS_FILENAME) _dict_file_ctx = as_file(dict_file) dict_path = _dict_file_ctx.__enter__() + msg: str + success: bool msg, success = nlpo3_load_dict( str(dict_path), _NLPO3_DEFAULT_DICT_NAME ) @@ -86,10 +88,14 @@ def load_dict(file_path: str, dict_name: str) -> bool: "nlpo3 is not installed. Install it with: pip install nlpo3" ) from ex - msg, success = nlpo3_load_dict(file_path=file_path, dict_name=dict_name) + msg: str + success: bool + msg, success = nlpo3_load_dict( + file_path=file_path, dict_name=dict_name + ) if not success: print(msg, file=stderr) - return cast(bool, success) + return success def segment( From 177d7802268ad385aadd6c6bad0552cf7a10997a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Feb 2026 23:32:44 +0000 Subject: [PATCH 6/7] Remove unnecessary cast for nlpo3_segment return value - Remove cast(list[str], nlpo3_segment(...)) since nlpo3.segment() already returns list[str] - Use explicit type annotation instead: result: list[str] = nlpo3_segment(...) - Remove unused 'cast' import from typing - Addresses feedback from @bact about unnecessary casting Co-authored-by: bact <128572+bact@users.noreply.github.com> --- pythainlp/tokenize/nlpo3.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/pythainlp/tokenize/nlpo3.py b/pythainlp/tokenize/nlpo3.py index 3c114d3ac..281a89138 100644 --- a/pythainlp/tokenize/nlpo3.py +++ b/pythainlp/tokenize/nlpo3.py @@ -6,7 +6,7 @@ import threading from importlib.resources import as_file, files from sys import stderr -from typing import TYPE_CHECKING, Any, Optional, cast +from typing import TYPE_CHECKING, Any, Optional if TYPE_CHECKING: from nlpo3 import ( @@ -133,12 +133,10 @@ def segment( if custom_dict == _NLPO3_DEFAULT_DICT_NAME: _ensure_default_dict_loaded() - return cast( - list[str], - nlpo3_segment( - text=text, - dict_name=custom_dict, - safe=safe_mode, - parallel=parallel_mode, - ), + result: list[str] = nlpo3_segment( + text=text, + dict_name=custom_dict, + safe=safe_mode, + parallel=parallel_mode, ) + return result From 8dca693a0d0b46a08631c5c67c4d29038f337138 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Feb 2026 23:39:00 +0000 Subject: [PATCH 7/7] Regenerate type hint analysis with clean mypy cache - Clear mypy cache before analysis to get accurate results - All submodules now show 0 mypy errors (previously showed 9+) - Confirms 100% function type annotation coverage - Confirms 88.38% variable type annotation coverage - The explicit type annotations for nlpo3 are necessary (not redundant) because nlpo3 is dynamically imported and mypy needs them to avoid "returning Any" errors Co-authored-by: bact <128572+bact@users.noreply.github.com> --- .../analysis/output/type_hint_analysis.json | 56 +++++++++---------- pythainlp/tokenize/nlpo3.py | 2 - 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/build_tools/analysis/output/type_hint_analysis.json b/build_tools/analysis/output/type_hint_analysis.json index ada4f46ec..de57eeb46 100644 --- a/build_tools/analysis/output/type_hint_analysis.json +++ b/build_tools/analysis/output/type_hint_analysis.json @@ -37,121 +37,121 @@ "complete": 2, "incomplete": 0, "none": 0, - "mypy_errors": 9 + "mypy_errors": 0 }, "augment": { "complete": 29, "incomplete": 0, "none": 0, - "mypy_errors": 9 + "mypy_errors": 0 }, "benchmarks": { "complete": 8, "incomplete": 0, "none": 0, - "mypy_errors": 9 + "mypy_errors": 0 }, "chat": { "complete": 4, "incomplete": 0, "none": 0, - "mypy_errors": 9 + "mypy_errors": 0 }, "classify": { "complete": 5, "incomplete": 0, "none": 0, - "mypy_errors": 9 + "mypy_errors": 0 }, "cli": { "complete": 21, "incomplete": 0, "none": 0, - "mypy_errors": 9 + "mypy_errors": 0 }, "coref": { "complete": 5, "incomplete": 0, "none": 0, - "mypy_errors": 9 + "mypy_errors": 0 }, "corpus": { "complete": 70, "incomplete": 0, "none": 0, - "mypy_errors": 9 + "mypy_errors": 0 }, "el": { "complete": 5, "incomplete": 0, "none": 0, - "mypy_errors": 9 + "mypy_errors": 0 }, "generate": { "complete": 15, "incomplete": 0, "none": 0, - "mypy_errors": 10 + "mypy_errors": 0 }, "khavee": { "complete": 9, "incomplete": 0, "none": 0, - "mypy_errors": 9 + "mypy_errors": 0 }, "lm": { "complete": 2, "incomplete": 0, "none": 0, - "mypy_errors": 9 + "mypy_errors": 0 }, "morpheme": { "complete": 2, "incomplete": 0, "none": 0, - "mypy_errors": 9 + "mypy_errors": 0 }, "parse": { "complete": 9, "incomplete": 0, "none": 0, - "mypy_errors": 9 + "mypy_errors": 0 }, "phayathaibert": { "complete": 19, "incomplete": 0, "none": 0, - "mypy_errors": 9 + "mypy_errors": 0 }, "soundex": { "complete": 27, "incomplete": 0, "none": 0, - "mypy_errors": 9 + "mypy_errors": 0 }, "spell": { "complete": 43, "incomplete": 0, "none": 0, - "mypy_errors": 9 + "mypy_errors": 0 }, "summarize": { "complete": 17, "incomplete": 0, "none": 0, - "mypy_errors": 12 + "mypy_errors": 0 }, "tag": { "complete": 73, "incomplete": 0, "none": 0, - "mypy_errors": 10 + "mypy_errors": 0 }, "tokenize": { "complete": 73, "incomplete": 0, "none": 0, - "mypy_errors": 9 + "mypy_errors": 0 }, "tokenizeicu": { "complete": 3, @@ -163,19 +163,19 @@ "complete": 9, "incomplete": 0, "none": 0, - "mypy_errors": 9 + "mypy_errors": 0 }, "translate": { "complete": 44, "incomplete": 0, "none": 0, - "mypy_errors": 9 + "mypy_errors": 0 }, "transliterate": { "complete": 75, "incomplete": 0, "none": 0, - "mypy_errors": 9 + "mypy_errors": 0 }, "transliterateicu": { "complete": 1, @@ -187,31 +187,31 @@ "complete": 25, "incomplete": 0, "none": 0, - "mypy_errors": 10 + "mypy_errors": 0 }, "util": { "complete": 109, "incomplete": 0, "none": 0, - "mypy_errors": 9 + "mypy_errors": 0 }, "wangchanberta": { "complete": 9, "incomplete": 0, "none": 0, - "mypy_errors": 9 + "mypy_errors": 0 }, "word_vector": { "complete": 7, "incomplete": 0, "none": 0, - "mypy_errors": 11 + "mypy_errors": 0 }, "wsd": { "complete": 4, "incomplete": 0, "none": 0, - "mypy_errors": 9 + "mypy_errors": 0 } }, "functions_no_hints": [], diff --git a/pythainlp/tokenize/nlpo3.py b/pythainlp/tokenize/nlpo3.py index 281a89138..a117a8706 100644 --- a/pythainlp/tokenize/nlpo3.py +++ b/pythainlp/tokenize/nlpo3.py @@ -52,8 +52,6 @@ def _ensure_default_dict_loaded() -> None: dict_file = corpus_files.joinpath(_THAI_WORDS_FILENAME) _dict_file_ctx = as_file(dict_file) dict_path = _dict_file_ctx.__enter__() - msg: str - success: bool msg, success = nlpo3_load_dict( str(dict_path), _NLPO3_DEFAULT_DICT_NAME )