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
)