From 4de0275c08354feef2e7e729c2610e017be05fc2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 10 Jan 2026 22:57:51 +0000 Subject: [PATCH 1/6] Initial plan From 5155590d9175135436afd30a62dbf90355e75ab4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 10 Jan 2026 23:01:36 +0000 Subject: [PATCH 2/6] Fix inappropriate str = None defaults in function parameters Co-authored-by: bact <128572+bact@users.noreply.github.com> --- pythainlp/classify/param_free.py | 4 ++-- pythainlp/corpus/core.py | 4 ++-- pythainlp/generate/wangchanglm.py | 4 ++-- pythainlp/summarize/mt5.py | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pythainlp/classify/param_free.py b/pythainlp/classify/param_free.py index b3ccac213..883dbb5f1 100644 --- a/pythainlp/classify/param_free.py +++ b/pythainlp/classify/param_free.py @@ -22,9 +22,9 @@ class GzipModel: def __init__( self, training_data: list[tuple[str, str]] = None, - model_path: str = None, + model_path: str = "", ): - if model_path is not None: + if model_path: self.load(model_path) else: self.training_data = np.array(training_data) diff --git a/pythainlp/corpus/core.py b/pythainlp/corpus/core.py index ecaa33197..39df91f51 100644 --- a/pythainlp/corpus/core.py +++ b/pythainlp/corpus/core.py @@ -656,7 +656,7 @@ def make_safe_directory_name(name: str) -> str: return safe_name -def get_hf_hub(repo_id: str, filename: str = None) -> str: +def get_hf_hub(repo_id: str, filename: str = "") -> str: """ HuggingFace Hub in :mod:`pythainlp` data directory. @@ -677,7 +677,7 @@ def get_hf_hub(repo_id: str, filename: str = None) -> str: hf_root = get_full_data_path("hf_models") name_dir = make_safe_directory_name(repo_id) root_project = os.path.join(hf_root, name_dir) - if filename is not None: + if filename: output_path = hf_hub_download( repo_id=repo_id, filename=filename, local_dir=root_project ) diff --git a/pythainlp/generate/wangchanglm.py b/pythainlp/generate/wangchanglm.py index 1e93b6413..b3abf681c 100644 --- a/pythainlp/generate/wangchanglm.py +++ b/pythainlp/generate/wangchanglm.py @@ -126,7 +126,7 @@ def gen_instruct( def instruct_generate( self, instruct: str, - context: str = None, + context: str = "", max_new_tokens=512, temperature: float = 0.9, top_p: float = 0.95, @@ -172,7 +172,7 @@ def instruct_generate( # และเครื่องดื่มแอลกอฮอล์ """ - if context in (None, ""): + if not context: prompt = self.PROMPT_DICT["prompt_no_input"].format_map( {"instruction": instruct, "input": ""} ) diff --git a/pythainlp/summarize/mt5.py b/pythainlp/summarize/mt5.py index cb0727804..c6691dda0 100644 --- a/pythainlp/summarize/mt5.py +++ b/pythainlp/summarize/mt5.py @@ -21,10 +21,10 @@ def __init__( min_length: int = 30, max_length: int = 100, skip_special_tokens: bool = True, - pretrained_mt5_model_name: str = None, + pretrained_mt5_model_name: str = "", ): model_name = "" - if pretrained_mt5_model_name is None: + if not pretrained_mt5_model_name: if model_size not in ["small", "base", "large", "xl", "xxl"]: raise ValueError( f"""model_size \"{model_size}\" not found. From 1b9ae7a162969c0eb49a5a35be955f3285074e61 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 10 Jan 2026 23:05:54 +0000 Subject: [PATCH 3/6] Update docstrings to reflect new default values for str parameters Co-authored-by: bact <128572+bact@users.noreply.github.com> --- pythainlp/classify/param_free.py | 2 +- pythainlp/corpus/core.py | 2 +- pythainlp/generate/wangchanglm.py | 2 +- pythainlp/summarize/mt5.py | 11 +++++++++++ 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/pythainlp/classify/param_free.py b/pythainlp/classify/param_free.py index 883dbb5f1..1d1500e49 100644 --- a/pythainlp/classify/param_free.py +++ b/pythainlp/classify/param_free.py @@ -16,7 +16,7 @@ class GzipModel: (Jiang et al., Findings 2023) :param list training_data: list [(text_sample,label)] - :param str model_path: Path for loading model (if you saved the model) + :param str model_path: Path for loading model (if you saved the model). Default is empty string. """ def __init__( diff --git a/pythainlp/corpus/core.py b/pythainlp/corpus/core.py index 39df91f51..9a8141e1b 100644 --- a/pythainlp/corpus/core.py +++ b/pythainlp/corpus/core.py @@ -661,7 +661,7 @@ def get_hf_hub(repo_id: str, filename: str = "") -> str: HuggingFace Hub in :mod:`pythainlp` data directory. :param str repo_id: repo_id - :param str filename: filename + :param str filename: filename (optional, default is empty string). If empty, downloads entire snapshot. :return: path :rtype: str """ diff --git a/pythainlp/generate/wangchanglm.py b/pythainlp/generate/wangchanglm.py index b3abf681c..99031bc19 100644 --- a/pythainlp/generate/wangchanglm.py +++ b/pythainlp/generate/wangchanglm.py @@ -140,7 +140,7 @@ def instruct_generate( Generate Instruct :param str instruct: Instruct - :param str context: context + :param str context: context (optional, default is empty string) :param int max_new_tokens: maximum number of new tokens :param float top_p: top p :param float temperature: temperature diff --git a/pythainlp/summarize/mt5.py b/pythainlp/summarize/mt5.py index c6691dda0..aaf6ffefc 100644 --- a/pythainlp/summarize/mt5.py +++ b/pythainlp/summarize/mt5.py @@ -23,6 +23,17 @@ def __init__( skip_special_tokens: bool = True, pretrained_mt5_model_name: str = "", ): + """ + Initialize mT5 Summarizer. + + :param str model_size: Size of the model ("small", "base", "large", "xl", "xxl"). Default is "small". + :param int num_beams: Number of beams for beam search. Default is 4. + :param int no_repeat_ngram_size: Size of n-grams to avoid repeating. Default is 2. + :param int min_length: Minimum length of generated summary. Default is 30. + :param int max_length: Maximum length of generated summary. Default is 100. + :param bool skip_special_tokens: Whether to skip special tokens in output. Default is True. + :param str pretrained_mt5_model_name: Name of pretrained model. If empty (default), uses google/mt5-{model_size}. + """ model_name = "" if not pretrained_mt5_model_name: if model_size not in ["small", "base", "large", "xl", "xxl"]: From 7e77fe242ceb6a4173e7d0e50c2444331baa7dd1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 10 Jan 2026 23:30:55 +0000 Subject: [PATCH 4/6] Convert None defaults to Optional[] type hints for type safety Co-authored-by: bact <128572+bact@users.noreply.github.com> --- pythainlp/augment/wordnet.py | 4 ++-- pythainlp/corpus/wordnet.py | 24 ++++++++++++------------ pythainlp/transliterate/wunsen.py | 16 ++++++++-------- pythainlp/util/date.py | 4 ++-- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/pythainlp/augment/wordnet.py b/pythainlp/augment/wordnet.py index 074e4a60c..6c8966ea7 100644 --- a/pythainlp/augment/wordnet.py +++ b/pythainlp/augment/wordnet.py @@ -126,13 +126,13 @@ def __init__(self): pass def find_synonyms( - self, word: str, pos: str = None, postag_corpus: str = "orchid" + self, word: str, pos: str | None = None, postag_corpus: str = "orchid" ) -> list[str]: """ Find synonyms using wordnet :param str word: word - :param str pos: part-of-speech type + :param str | None pos: part-of-speech type. Default is None. :param str postag_corpus: name of POS tag corpus :return: list of synonyms :rtype: List[str] diff --git a/pythainlp/corpus/wordnet.py b/pythainlp/corpus/wordnet.py index 6ea2a0097..c76d8729c 100644 --- a/pythainlp/corpus/wordnet.py +++ b/pythainlp/corpus/wordnet.py @@ -28,15 +28,15 @@ from nltk.corpus import wordnet -def synsets(word: str, pos: str = None, lang: str = "tha"): +def synsets(word: str, pos: str | None = None, lang: str = "tha"): """ This function returns the synonym set for all lemmas of the given word with an optional argument to constrain the part of speech of the word. :param str word: word to find synsets of - :param str pos: constraint of the part of speech (i.e. *n* for Noun, *v* + :param str | None pos: constraint of the part of speech (i.e. *n* for Noun, *v* for Verb, *a* for Adjective, *s* for Adjective - satellites, and *r* for Adverb) + satellites, and *r* for Adverb). Default is None. :param str lang: abbreviation of language (i.e. *eng*, *tha*). By default, it is *tha* @@ -101,13 +101,13 @@ def synset(name_synsets): return wordnet.synset(name_synsets) -def all_lemma_names(pos: str = None, lang: str = "tha"): +def all_lemma_names(pos: str | None = None, lang: str = "tha"): """ This function returns all lemma names for all synsets of the given part of speech tag and language. If part of speech tag is not specified, all synsets of all parts of speech will be used. - :param str pos: constraint of the part of speech (i.e. *n* for Noun, + :param str | None pos: constraint of the part of speech (i.e. *n* for Noun, *v* for Verb, *a* for Adjective, *s* for Adjective satellites, and *r* for Adverb). By default, *pos* is **None**. @@ -144,12 +144,12 @@ def all_lemma_names(pos: str = None, lang: str = "tha"): return wordnet.all_lemma_names(pos=pos, lang=lang) -def all_synsets(pos: str = None): +def all_synsets(pos: str | None = None): """ This function iterates over all synsets constrained by the given part of speech tag. - :param str pos: part of speech tag + :param str | None pos: part of speech tag. Default is None. :return: list of synsets constrained by the given part of speech tag. :rtype: Iterable[:class:`Synset`] @@ -194,15 +194,15 @@ def langs(): return wordnet.langs() -def lemmas(word: str, pos: str = None, lang: str = "tha"): +def lemmas(word: str, pos: str | None = None, lang: str = "tha"): """ This function returns all lemmas given the word with an optional argument to constrain the part of speech of the word. :param str word: word to find lemmas of - :param str pos: constraint of the part of speech (i.e. *n* for Noun, + :param str | None pos: constraint of the part of speech (i.e. *n* for Noun, *v* for Verb, *a* for Adjective, *s* for - Adjective satellites, and *r* for Adverb) + Adjective satellites, and *r* for Adverb). Default is None. :param str lang: abbreviation of language (i.e. *eng*, *tha*). By default, it is *tha*. @@ -403,13 +403,13 @@ def wup_similarity(synsets1, synsets2): return wordnet.wup_similarity(synsets1, synsets2) -def morphy(form, pos: str = None): +def morphy(form, pos: str | None = None): """ This function finds a possible base form for the given form, with the given part of speech. :param str form: the form to finds the base form of - :param str pos: part of speech tag of words to be searched + :param str | None pos: part of speech tag of words to be searched. Default is None. :return: base form of the given form :rtype: str diff --git a/pythainlp/transliterate/wunsen.py b/pythainlp/transliterate/wunsen.py index 92ed17faf..75b98c5c6 100644 --- a/pythainlp/transliterate/wunsen.py +++ b/pythainlp/transliterate/wunsen.py @@ -38,20 +38,20 @@ def transliterate( self, text: str, lang: str, - jp_input: str = None, - zh_sandhi: bool = None, - system: str = None, + jp_input: str | None = None, + zh_sandhi: bool | None = None, + system: str | None = None, ): """ Use Wunsen for transliteration :param str text: text to be transliterated to Thai text. :param str lang: source language - :param str jp_input: Japanese input method (for Japanese only) - :param bool zh_sandhi: Mandarin third tone sandhi option - (for Mandarin only) - :param str system: transliteration system (for Japanese and - Mandarin only) + :param str | None jp_input: Japanese input method (for Japanese only). Default is None. + :param bool | None zh_sandhi: Mandarin third tone sandhi option + (for Mandarin only). Default is None. + :param str | None system: transliteration system (for Japanese and + Mandarin only). Default is None. :return: Thai text :rtype: str diff --git a/pythainlp/util/date.py b/pythainlp/util/date.py index d8c3bfc14..21327d1e6 100644 --- a/pythainlp/util/date.py +++ b/pythainlp/util/date.py @@ -201,7 +201,7 @@ def thai_strptime( text: str, fmt: str, year: str = "be", - add_year: int = None, + add_year: int | None = None, tzinfo=ZoneInfo("Asia/Bangkok"), ): """ @@ -211,7 +211,7 @@ def thai_strptime( :param str fmt: string containing date and time directives :param str year: year of the text \ (ad is Anno Domini and be is Buddhist Era) - :param int add_year: add to year when converting to ad + :param int | None add_year: add to year when converting to ad. Default is None. :param object tzinfo: tzinfo (default is Asia/Bangkok) :return: The year that is converted to datetime.datetime :rtype: datetime.datetime From e7f140a0a8d29baec4366e497bc3f629642cfe97 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 10 Jan 2026 23:33:27 +0000 Subject: [PATCH 5/6] Fix training_data list type hint in GzipModel.__init__ Co-authored-by: bact <128572+bact@users.noreply.github.com> --- pythainlp/classify/param_free.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pythainlp/classify/param_free.py b/pythainlp/classify/param_free.py index 1d1500e49..caa4448bf 100644 --- a/pythainlp/classify/param_free.py +++ b/pythainlp/classify/param_free.py @@ -15,13 +15,13 @@ class GzipModel: “Low-Resource” Text Classification: A Parameter-Free Classification Method with Compressors (Jiang et al., Findings 2023) - :param list training_data: list [(text_sample,label)] + :param list | None training_data: list [(text_sample,label)]. Default is None. :param str model_path: Path for loading model (if you saved the model). Default is empty string. """ def __init__( self, - training_data: list[tuple[str, str]] = None, + training_data: list[tuple[str, str]] | None = None, model_path: str = "", ): if model_path: From e7960810b2e54050837426c8b00e234f24a813f3 Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Sun, 11 Jan 2026 01:08:15 +0000 Subject: [PATCH 6/6] Shorten line length --- pythainlp/classify/param_free.py | 19 ++++++++++++------- pythainlp/corpus/core.py | 3 ++- pythainlp/corpus/wordnet.py | 3 ++- pythainlp/summarize/mt5.py | 18 ++++++++++++------ 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/pythainlp/classify/param_free.py b/pythainlp/classify/param_free.py index caa4448bf..097607314 100644 --- a/pythainlp/classify/param_free.py +++ b/pythainlp/classify/param_free.py @@ -12,11 +12,13 @@ class GzipModel: """ This class is a re-implementation of - “Low-Resource” Text Classification: A Parameter-Free Classification Method with Compressors - (Jiang et al., Findings 2023) + “Low-Resource” Text Classification: A Parameter-Free Classification Method + with Compressors (Jiang et al., Findings 2023) - :param list | None training_data: list [(text_sample,label)]. Default is None. - :param str model_path: Path for loading model (if you saved the model). Default is empty string. + :param list | None training_data: list [(text_sample,label)]. + Default is None. + :param str model_path: Path for loading model (if you saved the model). + Default is empty string. """ def __init__( @@ -85,9 +87,9 @@ def predict(self, x1: str, k: int = 1) -> str: def save(self, path: str): """ - :param str path: path for save model + :param str path: path to save model """ - with open(path, "w") as f: + with open(path, "w", encoding="utf-8") as f: json.dump( { "training_data": self.training_data.tolist(), @@ -98,7 +100,10 @@ def save(self, path: str): ) def load(self, path: str): - with open(path) as f: + """ + :param str path: path to load model + """ + 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"]) diff --git a/pythainlp/corpus/core.py b/pythainlp/corpus/core.py index 9a8141e1b..084202403 100644 --- a/pythainlp/corpus/core.py +++ b/pythainlp/corpus/core.py @@ -661,7 +661,8 @@ def get_hf_hub(repo_id: str, filename: str = "") -> str: HuggingFace Hub in :mod:`pythainlp` data directory. :param str repo_id: repo_id - :param str filename: filename (optional, default is empty string). If empty, downloads entire snapshot. + :param str filename: filename (optional, default is empty string). + If empty, downloads entire snapshot. :return: path :rtype: str """ diff --git a/pythainlp/corpus/wordnet.py b/pythainlp/corpus/wordnet.py index c76d8729c..afdd27548 100644 --- a/pythainlp/corpus/wordnet.py +++ b/pythainlp/corpus/wordnet.py @@ -409,7 +409,8 @@ def morphy(form, pos: str | None = None): with the given part of speech. :param str form: the form to finds the base form of - :param str | None pos: part of speech tag of words to be searched. Default is None. + :param str | None pos: part of speech tag of words to be searched. + Default is None. :return: base form of the given form :rtype: str diff --git a/pythainlp/summarize/mt5.py b/pythainlp/summarize/mt5.py index aaf6ffefc..ad226b7ab 100644 --- a/pythainlp/summarize/mt5.py +++ b/pythainlp/summarize/mt5.py @@ -26,13 +26,19 @@ def __init__( """ Initialize mT5 Summarizer. - :param str model_size: Size of the model ("small", "base", "large", "xl", "xxl"). Default is "small". + :param str model_size: Size of the model ("small", "base", "large", + "xl", "xxl"). Default is "small". :param int num_beams: Number of beams for beam search. Default is 4. - :param int no_repeat_ngram_size: Size of n-grams to avoid repeating. Default is 2. - :param int min_length: Minimum length of generated summary. Default is 30. - :param int max_length: Maximum length of generated summary. Default is 100. - :param bool skip_special_tokens: Whether to skip special tokens in output. Default is True. - :param str pretrained_mt5_model_name: Name of pretrained model. If empty (default), uses google/mt5-{model_size}. + :param int no_repeat_ngram_size: Size of n-grams to avoid repeating. + Default is 2. + :param int min_length: Minimum length of generated summary. + Default is 30. + :param int max_length: Maximum length of generated summary. + Default is 100. + :param bool skip_special_tokens: Whether to skip special tokens in + output. Default is True. + :param str pretrained_mt5_model_name: Name of pretrained model. + If empty (default), uses google/mt5-{model_size}. """ model_name = "" if not pretrained_mt5_model_name: