Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
e9a22b8
Initial plan
Copilot Feb 4, 2026
8d59b81
Fix type annotation issues: remove unused type: ignore comments and f…
Copilot Feb 4, 2026
7bc0511
Fix remaining mypy errors: type narrowing, proper type annotations, a…
Copilot Feb 4, 2026
cae85af
Fix more type annotation issues: Any imports, name redefinitions, and…
Copilot Feb 4, 2026
2849af3
Fix final mypy errors: proper type annotations for kwargs, save_json,…
Copilot Feb 4, 2026
ebc8b52
Address code review: use type: ignore for arg-type in ulmfit/core.py
Copilot Feb 4, 2026
14a9c03
Add type annotations to transliterate module variables
Copilot Feb 4, 2026
38d5ddd
Add type annotations to instance variables in transliterate neural ne…
Copilot Feb 4, 2026
a330f53
Add type annotations to module-level variables in transliterate module
Copilot Feb 4, 2026
c5fb595
Add type annotations to 72 variables in util module
Copilot Feb 4, 2026
9603839
Fix code review issues in type annotations
Copilot Feb 4, 2026
ca6b735
Ensure Python 3.9 compatibility in pronounce.py
Copilot Feb 4, 2026
f201193
Add type annotations to 153 variables in augment, translate, and toke…
Copilot Feb 4, 2026
a79624e
Add type annotations to module and class variables
Copilot Feb 4, 2026
3eeced4
Add 95 type annotations (87.3% → 94.7% complete)
Copilot Feb 4, 2026
0ab4e30
Add 37 more type annotations (94.7% → 97.6% complete)
Copilot Feb 4, 2026
06b015f
Fix type annotation redefinitions in spell module
Copilot Feb 4, 2026
731a2cd
Remove redundant type annotations on reassignments
Copilot Feb 4, 2026
f7672a4
Final status: 97% type completeness achieved - ready for merge
Copilot Feb 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,639 changes: 175 additions & 2,464 deletions build_tools/analysis/output/type_hint_analysis.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions pythainlp/ancient/aksonhan.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
from pythainlp.util import Trie

_dict_aksonhan: dict[str, str] = {}
i: str
for i in list(thai_consonants):
if i == "ร":
continue
j: str
for j in list(thai_tonemarks):
_dict_aksonhan[i + j + i] = "ั" + j + i
_dict_aksonhan[i + i + j + i] = i + "ั" + j + i
Expand Down
8 changes: 3 additions & 5 deletions pythainlp/augment/lm/fasttext.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,11 @@ def __init__(self, model_path: str) -> None:
from gensim.models.keyedvectors import KeyedVectors

if model_path.endswith(".bin"):
self.model: Union[FastText, KeyedVectors] = (
FastText_gensim.load_facebook_vectors(model_path)
)
self.model: Union["FastText", "KeyedVectors"] = FastText_gensim.load_facebook_vectors(model_path)
elif model_path.endswith(".vec"):
self.model = KeyedVectors.load_word2vec_format(model_path)
self.model: Union["FastText", "KeyedVectors"] = KeyedVectors.load_word2vec_format(model_path)
else:
self.model = FastText_gensim.load(model_path)
self.model: Union["FastText", "KeyedVectors"] = FastText_gensim.load(model_path)
self.dict_wv: list[str] = list(self.model.key_to_index.keys())

def tokenize(self, text: str) -> list[str]:
Expand Down
8 changes: 4 additions & 4 deletions pythainlp/augment/lm/phayathaibert.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,16 @@ def __init__(self) -> None:
pipeline,
)

self.tokenizer = AutoTokenizer.from_pretrained(_MODEL_NAME) # type: ignore[assignment]
self.model_for_masked_lm = AutoModelForMaskedLM.from_pretrained( # type: ignore[assignment]
self.tokenizer: AutoTokenizer = AutoTokenizer.from_pretrained(_MODEL_NAME)
self.model_for_masked_lm: AutoModelForMaskedLM = AutoModelForMaskedLM.from_pretrained(
_MODEL_NAME
)
self.model = pipeline(
self.model: Pipeline = pipeline(
"fill-mask",
tokenizer=self.tokenizer,
model=self.model_for_masked_lm,
)
self.processor = ThaiTextProcessor()
self.processor: ThaiTextProcessor = ThaiTextProcessor()

def generate(
self,
Expand Down
12 changes: 6 additions & 6 deletions pythainlp/augment/lm/wangchanberta.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,29 +25,29 @@ def __init__(self) -> None:
pipeline,
)

self.model_name = "airesearch/wangchanberta-base-att-spm-uncased"
self.target_tokenizer = CamembertTokenizer
self.tokenizer = CamembertTokenizer.from_pretrained( # type: ignore[assignment]
self.model_name: str = "airesearch/wangchanberta-base-att-spm-uncased"
self.target_tokenizer: type[CamembertTokenizer] = CamembertTokenizer
self.tokenizer: CamembertTokenizer = CamembertTokenizer.from_pretrained(
self.model_name, revision="main"
)
self.tokenizer.additional_special_tokens = [
"<s>NOTUSED",
"</s>NOTUSED",
"<_>",
]
self.fill_mask = pipeline(
self.fill_mask: Pipeline = pipeline(
task="fill-mask",
tokenizer=self.tokenizer,
model=f"{self.model_name}",
revision="main",
)
self.MASK_TOKEN = self.tokenizer.mask_token
self.MASK_TOKEN: str = self.tokenizer.mask_token

def generate(
self, sentence: str, num_replace_tokens: int = 3
) -> list[str]:
sent2: list[str] = []
self.input_text = sentence
self.input_text: str = sentence
sent = [
i for i in self.tokenizer.tokenize(self.input_text) if i != "▁"
]
Expand Down
14 changes: 7 additions & 7 deletions pythainlp/augment/word2vec/bpemb_wv.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ def __init__(
) -> None:
from bpemb import BPEmb

self.bpemb_temp = BPEmb(lang=lang, dim=dim, vs=vs)
self.model = self.bpemb_temp.emb
self.bpemb_temp: BPEmb = BPEmb(lang=lang, dim=dim, vs=vs)
self.model: KeyedVectors = self.bpemb_temp.emb
self.load_w2v()

def tokenizer(self, text: str) -> list[str]:
Expand All @@ -44,7 +44,7 @@ def tokenizer(self, text: str) -> list[str]:

def load_w2v(self) -> None:
"""Load BPEmb model"""
self.aug = Word2VecAug(
self.aug: Word2VecAug = Word2VecAug(
self.model, tokenize=self.tokenizer, type="model"
)

Expand All @@ -68,11 +68,11 @@ def augment(
aug.augment("ผมเรียน", n_sent=2, p=0.5)
# output: ['ผมสอน', 'ผมเข้าเรียน']
"""
self.sentence = sentence.replace(" ", "▁")
self.temp = self.aug.augment(self.sentence, n_sent, p=p)
self.temp_new = []
self.sentence: str = sentence.replace(" ", "▁")
self.temp: list[tuple[str, ...]] = self.aug.augment(self.sentence, n_sent, p=p)
self.temp_new: list[str] = []
for i in self.temp:
self.t = ""
self.t: str = ""
for j in i:
self.t += j.replace("▁", "")
self.temp_new.append(self.t)
Expand Down
8 changes: 3 additions & 5 deletions pythainlp/augment/word2vec/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,13 @@ def __init__(

self.tokenizer: Callable[[str], list[str]] = tokenize
if type == "file":
self.model: "KeyedVectors" = (
word2vec.KeyedVectors.load_word2vec_format(model)
)
self.model: "KeyedVectors" = word2vec.KeyedVectors.load_word2vec_format(model)
elif type == "binary":
self.model = word2vec.KeyedVectors.load_word2vec_format(
self.model: "KeyedVectors" = word2vec.KeyedVectors.load_word2vec_format(
model, binary=True, unicode_errors="ignore"
)
else:
self.model = model
self.model: "KeyedVectors" = model # type: ignore[assignment]
self.dict_wv: list[str] = list(self.model.key_to_index.keys())

def modify_sent(self, sent: list[str], p: float = 0.7) -> list[list[str]]:
Expand Down
14 changes: 4 additions & 10 deletions pythainlp/augment/word2vec/ltw2v.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,9 @@
# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations

from typing import TYPE_CHECKING, Optional
from typing import Optional

if TYPE_CHECKING:
from pythainlp.augment.word2vec.core import Word2VecAug

from pythainlp.augment.word2vec.core import Word2VecAug as _Word2VecAug

# Make it accessible for runtime
Word2VecAug: type[_Word2VecAug] = _Word2VecAug
from pythainlp.augment.word2vec.core import Word2VecAug
from pythainlp.corpus import get_corpus_path
from pythainlp.tokenize import word_tokenize

Expand All @@ -27,7 +21,7 @@ class LTW2VAug:
aug: Word2VecAug

def __init__(self) -> None:
self.ltw2v_wv = get_corpus_path("ltw2v")
self.ltw2v_wv: Optional[str] = get_corpus_path("ltw2v")
self.load_w2v()

def tokenizer(self, text: str) -> list[str]:
Expand All @@ -43,7 +37,7 @@ def load_w2v(self) -> None: # insert substitute
"LTW2V word2vec model not found. "
"Please download it first using pythainlp.corpus.download('ltw2v_wv')"
)
self.aug = Word2VecAug(self.ltw2v_wv, self.tokenizer, type="binary")
self.aug: Word2VecAug = Word2VecAug(self.ltw2v_wv, self.tokenizer, type="binary")

def augment(
self, sentence: str, n_sent: int = 1, p: float = 0.7
Expand Down
16 changes: 5 additions & 11 deletions pythainlp/augment/word2vec/thai2fit.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,9 @@
# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations

from typing import TYPE_CHECKING, Optional
from typing import Optional

if TYPE_CHECKING:
from pythainlp.augment.word2vec.core import Word2VecAug

from pythainlp.augment.word2vec.core import Word2VecAug as _Word2VecAug

# Make it accessible for runtime
Word2VecAug: type[_Word2VecAug] = _Word2VecAug
from pythainlp.augment.word2vec.core import Word2VecAug
from pythainlp.corpus import get_corpus_path
from pythainlp.tokenize import thai2fit_tokenizer

Expand All @@ -27,15 +21,15 @@ class Thai2fitAug:
aug: Word2VecAug

def __init__(self) -> None:
self.thai2fit_wv = get_corpus_path("thai2fit_wv")
self.thai2fit_wv: Optional[str] = get_corpus_path("thai2fit_wv")
self.load_w2v()

def tokenizer(self, text: str) -> list[str]:
""":param str text: Thai text
:rtype: List[str]
"""
tok = thai2fit_tokenizer()
return tok.word_tokenize(text) # type: ignore[no-any-return]
return tok.word_tokenize(text)

def load_w2v(self) -> None:
"""Load Thai2Fit's word2vec model"""
Expand All @@ -44,7 +38,7 @@ def load_w2v(self) -> None:
"Thai2Fit word2vec model not found. "
"Please download it first using pythainlp.corpus.download('thai2fit_wv')"
)
self.aug = Word2VecAug(self.thai2fit_wv, self.tokenizer, type="binary")
self.aug: Word2VecAug = Word2VecAug(self.thai2fit_wv, self.tokenizer, type="binary")

def augment(
self, sentence: str, n_sent: int = 1, p: float = 0.7
Expand Down
28 changes: 20 additions & 8 deletions pythainlp/augment/wordnet.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

import itertools
from collections import OrderedDict
from typing import Callable, Optional
from typing import Any, Callable, Optional

from nltk.corpus import wordnet as wn

Expand Down Expand Up @@ -112,12 +112,24 @@ def postype2wordnet(pos: str, corpus: str) -> Optional[str]:
"""
if corpus not in ["orchid"]:
return None
return orchid[pos] # type: ignore[no-any-return]
return orchid[pos]


class WordNetAug:
"""Text Augment using wordnet"""

synonyms: list[str]
list_synsets: list
p2w_pos: Optional[str]
synset: Any
syn: str
synonyms_without_duplicates: list[str]
list_words: list[str]
list_synonym: list
p_all: int
list_pos: list[tuple[str, str]]
temp: list[str]

def __init__(self) -> None:
pass

Expand All @@ -137,13 +149,13 @@ def find_synonyms(
"""
self.synonyms: list[str] = []
if pos is None:
self.list_synsets = wordnet.synsets(word)
self.list_synsets: list = wordnet.synsets(word)
else:
self.p2w_pos = postype2wordnet(pos, postag_corpus)
self.p2w_pos: Optional[str] = postype2wordnet(pos, postag_corpus)
if self.p2w_pos != "":
self.list_synsets = wordnet.synsets(word, pos=self.p2w_pos)
self.list_synsets: list = wordnet.synsets(word, pos=self.p2w_pos)
else:
self.list_synsets = wordnet.synsets(word)
self.list_synsets: list = wordnet.synsets(word)

for self.synset in wordnet.synsets(word):
for self.syn in self.synset.lemma_names(lang="tha"):
Expand Down Expand Up @@ -189,7 +201,7 @@ def augment(
"""
new_sentences = []
self.list_words: list[str] = tokenize(sentence)
self.list_synonym: list[list[str]] = []
self.list_synonym: list = []
self.p_all: int = 1
if postag:
self.list_pos: list[tuple[str, str]] = pos_tag(
Expand All @@ -206,7 +218,7 @@ def augment(
self.p_all *= len(self.temp)
else:
for word in self.list_words:
self.temp = self.find_synonyms(word)
self.temp: list[str] = self.find_synonyms(word)
if not self.temp:
self.list_synonym.append([word])
else:
Expand Down
9 changes: 6 additions & 3 deletions pythainlp/chat/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations

from typing import TYPE_CHECKING, Optional
from typing import TYPE_CHECKING, Any, Optional

if TYPE_CHECKING:
import torch


class ChatBotModel:
history: list[tuple[str, str]]
model: Any

def __init__(self) -> None:
"""Chat using AI generation"""
self.history: list[tuple[str, str]] = []
Expand Down Expand Up @@ -46,7 +49,7 @@ def load_model(
if model_name == "wangchanglm":
from pythainlp.generate.wangchanglm import WangChanGLM

self.model = WangChanGLM()
self.model: Any = WangChanGLM()
self.model.load_model(
model_path="pythainlp/wangchanglm-7.5B-sft-en-sharded",
return_dict=return_dict,
Expand Down Expand Up @@ -94,4 +97,4 @@ def chat(self, text: str) -> str:
)
_bot = self.model.gen_instruct(_temp)
self.history.append((text, _bot))
return _bot # type: ignore[no-any-return]
return _bot
8 changes: 4 additions & 4 deletions pythainlp/classify/param_free.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ def __init__(
if model_path:
self.load(model_path)
else:
self.training_data = np.array(training_data)
self.cx2_list = self.train()
self.training_data: "NDArray[Any]" = np.array(training_data)
self.cx2_list: list[int] = self.train()

def train(self) -> list[int]:
temp_list = []
Expand Down Expand Up @@ -112,5 +112,5 @@ def load(self, path: str) -> None:

with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
self.cx2_list = data["cx2_list"]
self.training_data = np.array(data["training_data"])
self.cx2_list: list[int] = data["cx2_list"]
self.training_data: "NDArray[Any]" = np.array(data["training_data"])
4 changes: 2 additions & 2 deletions pythainlp/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
if TYPE_CHECKING:
from types import ModuleType

sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8") # type: ignore[assignment]
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8") # type: ignore[assignment]
sys.stdout: io.TextIOWrapper = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
sys.stderr: io.TextIOWrapper = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8")

# a command should start with a verb when possible
COMMANDS: list[str] = sorted(
Expand Down
8 changes: 4 additions & 4 deletions pythainlp/cli/tag.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from __future__ import annotations

import argparse
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any

from pythainlp import cli
from pythainlp.tag import pos_tag
Expand Down Expand Up @@ -38,7 +38,7 @@ def __init__(self, name: str, argv: Sequence[str]) -> None:
)

args = parser.parse_args(argv)
self.args = args
self.args: Any = args

tokens = args.text.split(args.separator)
result = self.run(tokens)
Expand All @@ -52,8 +52,8 @@ class POSTaggingApp(SubAppBase):
run: Callable[[list[str]], list[tuple[str, str]]]

def __init__(self, *args: str, **kwargs: str) -> None:
self.separator = "|"
self.run = pos_tag
self.separator: str = "|"
self.run: Callable[[list[str]], list[tuple[str, str]]] = pos_tag

super().__init__(*args, **kwargs)

Expand Down
2 changes: 1 addition & 1 deletion pythainlp/cli/tokenize.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def __init__(self, name: str, argv: Sequence[str]) -> None:
parser.set_defaults(keep_whitespace=True)

args = parser.parse_args(argv)
self.args = args
self.args: Any = args

cli.exit_if_empty(args.text, parser)
result = self.run(
Expand Down
Loading
Loading