From abfc76adb38e25a27b91211f033c91f1ee3164fb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Mar 2026 03:07:50 +0000 Subject: [PATCH 01/12] Initial plan From 9a754f99950f41c86dfdacd5f8c94b7a80fecd36 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Mar 2026 03:10:38 +0000 Subject: [PATCH 02/12] Add mypy static type check workflow Co-authored-by: bact <128572+bact@users.noreply.github.com> --- .github/workflows/mypy.yml | 61 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 .github/workflows/mypy.yml diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml new file mode 100644 index 000000000..ea1416d32 --- /dev/null +++ b/.github/workflows/mypy.yml @@ -0,0 +1,61 @@ +# SPDX-FileCopyrightText: 2016-2026 PyThaiNLP Project +# SPDX-License-Identifier: Apache-2.0 + +name: Type check + +on: + push: + branches: + - dev + paths-ignore: + - '**.cff' + - '**.json' + - '**.md' + - '**.rst' + - '**.txt' + - 'docs/**' + pull_request: + branches: + - dev + paths-ignore: + - '**.cff' + - '**.json' + - '**.md' + - '**.rst' + - '**.txt' + - 'docs/**' + +# Avoid duplicate runs for the same source branch and repository. +# For pull_request events, uses the source repo name from +# github.event.pull_request.head.repo.full_name; otherwise uses github.repository. +# For push events, uses the branch name from github.ref_name. +# For pull_request events, uses the source branch name from github.head_ref. +# This ensures events for the same repo and branch share the same group, +# and avoids cross-fork collisions when branch names are reused. +concurrency: + group: >- + ${{ github.workflow }}-${{ + github.event.pull_request.head.repo.full_name || github.repository + }}-${{ github.head_ref || github.ref_name }} + cancel-in-progress: true + +jobs: + mypy: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: "3.9" + cache: "pip" + + - name: Install mypy + run: pip install ".[dev]" + + - name: Run mypy + run: mypy pythainlp From 436c64b6f79484e33e9448a781cb5ab7a3426d87 Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Mon, 9 Mar 2026 10:14:43 +0700 Subject: [PATCH 03/12] Separate mypy installation in workflow Updated mypy installation steps to install mypy separately. --- .github/workflows/mypy.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index ea1416d32..58cc4672b 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -55,7 +55,9 @@ jobs: cache: "pip" - name: Install mypy - run: pip install ".[dev]" + run: | + pip install . + pip install mypy - name: Run mypy run: mypy pythainlp From ded6721ad7216e70ed888a2b7c3024667783abba Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Mar 2026 03:26:43 +0000 Subject: [PATCH 04/12] Fix all 11 mypy errors to reach 0 errors Co-authored-by: bact <128572+bact@users.noreply.github.com> --- pyproject.toml | 3 +++ pythainlp/__init__.py | 2 +- pythainlp/benchmarks/metrics.py | 16 ++++++++++------ pythainlp/braille/core.py | 12 +++++++----- pythainlp/lm/qwen3.py | 8 ++++---- pythainlp/tools/path.py | 6 +++--- pythainlp/transliterate/core.py | 2 +- 7 files changed, 29 insertions(+), 20 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index efd02f6e2..e4376bfc8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -460,10 +460,13 @@ module = [ "gensim.*", "huggingface_hub.*", "icu.*", + "importlib_resources", + "importlib_resources.*", "khamyo.*", "khanaa.*", "multiel.*", "nltk.*", + "nlpo3.*", "numpy.*", "onnxruntime.*", "oskut.*", diff --git a/pythainlp/__init__.py b/pythainlp/__init__.py index 360441982..88d907802 100644 --- a/pythainlp/__init__.py +++ b/pythainlp/__init__.py @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: 2016-2026 PyThaiNLP Project # SPDX-FileType: SOURCE # SPDX-License-Identifier: Apache-2.0 -__version__ = "5.2.0" # type: ignore +__version__ = "5.2.0" thai_consonants: str = ( "กขฃคฅฆงจฉชซฌญฎฏฐฑฒณดตถทธนบปผฝพฟภมยรลวศษสหฬอฮ" # 44 chars diff --git a/pythainlp/benchmarks/metrics.py b/pythainlp/benchmarks/metrics.py index 34df4f9a7..62e01221a 100644 --- a/pythainlp/benchmarks/metrics.py +++ b/pythainlp/benchmarks/metrics.py @@ -11,7 +11,7 @@ import math from collections import Counter -from typing import Union +from typing import Union, cast def _get_ngrams(tokens: list[str], n: int) -> list[tuple[str, ...]]: @@ -82,7 +82,7 @@ def bleu_score( lowercase: bool = False, max_ngram: int = 4, smooth: bool = True, -) -> dict[str, float]: +) -> dict[str, Union[float, list[float]]]: """ Calculate BLEU score for Thai text with automatic tokenization. @@ -103,9 +103,11 @@ def bleu_score( :param bool smooth: whether to use smoothing for zero counts (default: True) - :return: dictionary with 'bleu', 'precisions', 'bp', 'length_ratio', - 'hyp_length', and 'ref_length' - :rtype: dict[str, float] + :return: dictionary with ``'bleu'``, ``'precisions'``, ``'bp'``, + ``'length_ratio'``, ``'hyp_length'``, and ``'ref_length'``. + The ``'precisions'`` value is a ``list[float]``; all other values + are ``float``. + :rtype: dict[str, float | list[float]] :Example: :: @@ -131,7 +133,9 @@ def bleu_score( # Normalize references format if references and isinstance(references[0], str): - refs_normalized: list[list[str]] = [[ref] for ref in references] + refs_normalized: list[list[str]] = [ + [ref] for ref in cast(list[str], references) + ] else: refs_normalized = references # type: ignore[assignment] diff --git a/pythainlp/braille/core.py b/pythainlp/braille/core.py index 22b3cb079..6ac835853 100644 --- a/pythainlp/braille/core.py +++ b/pythainlp/braille/core.py @@ -6,6 +6,7 @@ from __future__ import annotations import re +from typing import cast from pythainlp.tokenize import word_tokenize from pythainlp.util import Trie @@ -250,9 +251,10 @@ def __init__(self, data: list[list[str]] | list[str] | str) -> None: self.inputdata: list[list[str]] | list[str] | str = data if isinstance(data, list): if len(data) > 1: - self.data: list[list[str]] | list[str] = [""] * len(data) - for i in range(len(data)): - self.data[i] = sorted(list(data[i])) + nested_data: list[list[str]] = [[] for _ in range(len(data))] + for i, item in enumerate(data): + nested_data[i] = sorted(list(item)) + self.data: list[list[str]] | list[str] = nested_data elif len(data) == 1: self.data = sorted(list(data[0])) else: @@ -540,7 +542,7 @@ def tobraille(self) -> str: result += self.db[pattern_str] return result else: - pattern_str = "".join(self.data) + pattern_str = "".join(cast(list[str], self.data)) return self.db.get(pattern_str, "") def printbraille(self) -> str: @@ -571,6 +573,6 @@ def printbraille(self) -> str: mirrored_patterns.reverse() return "".join(mirrored_patterns) else: - mirrored = "".join(mirror_map[dot] for dot in self.data) + mirrored = "".join(mirror_map[dot] for dot in cast(list[str], self.data)) mirrored_sorted = "".join(sorted(mirrored)) return self.db[mirrored_sorted] diff --git a/pythainlp/lm/qwen3.py b/pythainlp/lm/qwen3.py index b67d20ab5..6b2fdf3b5 100644 --- a/pythainlp/lm/qwen3.py +++ b/pythainlp/lm/qwen3.py @@ -176,10 +176,10 @@ def generate( # Decode only the newly generated tokens # output_ids and input_ids are guaranteed to be 2D tensors with # batch size 1 from the tokenizer call above - generated_text = self.tokenizer.decode( + generated_text = str(self.tokenizer.decode( output_ids[0][len(input_ids[0]) :], skip_special_tokens=skip_special_tokens, - ) + )) return generated_text @@ -270,9 +270,9 @@ def chat( # Decode only the newly generated tokens # output_ids and input_ids are guaranteed to be 2D tensors with # batch size 1 from the tokenizer call above - generated_text = self.tokenizer.decode( + generated_text = str(self.tokenizer.decode( output_ids[0][len(input_ids[0]) :], skip_special_tokens=skip_special_tokens, - ) + )) return generated_text diff --git a/pythainlp/tools/path.py b/pythainlp/tools/path.py index 7459bce2d..cdc7e95d0 100644 --- a/pythainlp/tools/path.py +++ b/pythainlp/tools/path.py @@ -9,14 +9,14 @@ from __future__ import annotations import os +import sys from os import PathLike -from sys import version_info from typing import cast -if version_info >= (3, 11): +if sys.version_info >= (3, 11): from importlib.resources import files # Available in Python 3.11+ else: - from importlib_resources import files # type: ignore[no-redef] # noqa: I001 + from importlib_resources import files # noqa: I001 PYTHAINLP_DEFAULT_DATA_DIR: str = "pythainlp-data" diff --git a/pythainlp/transliterate/core.py b/pythainlp/transliterate/core.py index ba5cbbece..cd01bad32 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[no-redef] # noqa: I001 + from pythainlp.translate.umt5_thaig2p import transliterate # type: ignore[import-not-found, no-redef] # noqa: I001 else: # use default engine: "thaig2p" from pythainlp.transliterate.thaig2p import transliterate # noqa: I001 From 2c12064a782a07d0fe2dba89cfa4cc6539fa554e Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Mon, 9 Mar 2026 10:30:20 +0700 Subject: [PATCH 05/12] Simplify sorting of nested data --- pythainlp/braille/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythainlp/braille/core.py b/pythainlp/braille/core.py index 6ac835853..b5ac0ad70 100644 --- a/pythainlp/braille/core.py +++ b/pythainlp/braille/core.py @@ -253,7 +253,7 @@ def __init__(self, data: list[list[str]] | list[str] | str) -> None: if len(data) > 1: nested_data: list[list[str]] = [[] for _ in range(len(data))] for i, item in enumerate(data): - nested_data[i] = sorted(list(item)) + nested_data[i] = sorted(item) self.data: list[list[str]] | list[str] = nested_data elif len(data) == 1: self.data = sorted(list(data[0])) From a43ca590ca4a8e1346e32f4ed3acfacefcc1103d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Mar 2026 03:48:24 +0000 Subject: [PATCH 06/12] Fix X|Y union type syntax for Python 3.9 compatibility Co-authored-by: bact <128572+bact@users.noreply.github.com> --- pythainlp/benchmarks/metrics.py | 10 +++++----- pythainlp/braille/core.py | 10 +++++----- pythainlp/transliterate/core.py | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/pythainlp/benchmarks/metrics.py b/pythainlp/benchmarks/metrics.py index 62e01221a..84a221c18 100644 --- a/pythainlp/benchmarks/metrics.py +++ b/pythainlp/benchmarks/metrics.py @@ -11,7 +11,7 @@ import math from collections import Counter -from typing import Union, cast +from typing import Optional, Union, cast def _get_ngrams(tokens: list[str], n: int) -> list[tuple[str, ...]]: @@ -90,7 +90,7 @@ def bleu_score( Understudy) metric that automatically tokenizes Thai text using PyThaiNLP before calculating the score. - :param list[str] | list[list[str]] references: reference translations. + :param Union[list[str], list[list[str]]] references: reference translations. Can be: - A list of strings (one reference per hypothesis) - A list of lists of strings (multiple references per hypothesis) @@ -107,7 +107,7 @@ def bleu_score( ``'length_ratio'``, ``'hyp_length'``, and ``'ref_length'``. The ``'precisions'`` value is a ``list[float]``; all other values are ``float``. - :rtype: dict[str, float | list[float]] + :rtype: dict[str, Union[float, list[float]]] :Example: :: @@ -235,7 +235,7 @@ def rouge_score( reference: str, hypothesis: str, tokenize: str = "newmm", - rouge_types: list[str] | None = None, + rouge_types: Optional[list[str]] = None, ) -> dict[str, tuple[float, float, float]]: """ Calculate ROUGE scores for Thai text with automatic tokenization. @@ -253,7 +253,7 @@ def rouge_score( :param str hypothesis: hypothesis text to evaluate :param str tokenize: tokenization engine to use (default: "newmm"). See :func:`pythainlp.tokenize.word_tokenize` for available engines. - :param list[str] | None rouge_types: list of ROUGE types to calculate. + :param Optional[list[str]] rouge_types: list of ROUGE types to calculate. Default is ["rouge1", "rouge2", "rougeL"] :return: dictionary mapping ROUGE type to (precision, recall, fmeasure) diff --git a/pythainlp/braille/core.py b/pythainlp/braille/core.py index b5ac0ad70..41bd2576e 100644 --- a/pythainlp/braille/core.py +++ b/pythainlp/braille/core.py @@ -6,7 +6,7 @@ from __future__ import annotations import re -from typing import cast +from typing import Union, cast from pythainlp.tokenize import word_tokenize from pythainlp.util import Trie @@ -242,19 +242,19 @@ class Braille: Converts dot number patterns to Unicode braille characters. """ - def __init__(self, data: list[list[str]] | list[str] | str) -> None: + def __init__(self, data: Union[list[list[str]], list[str], str]) -> None: """Initialize Braille converter. :param data: Braille dot patterns as list or string - :type data: list[list[str]] | list[str] | str + :type data: list[list[str]] or list[str] or str """ - self.inputdata: list[list[str]] | list[str] | str = data + self.inputdata: Union[list[list[str]], list[str], str] = data if isinstance(data, list): if len(data) > 1: nested_data: list[list[str]] = [[] for _ in range(len(data))] for i, item in enumerate(data): nested_data[i] = sorted(item) - self.data: list[list[str]] | list[str] = nested_data + self.data: Union[list[list[str]], list[str]] = nested_data elif len(data) == 1: self.data = sorted(list(data[0])) else: diff --git a/pythainlp/transliterate/core.py b/pythainlp/transliterate/core.py index cd01bad32..f975f41d3 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-not-found, no-redef] # noqa: I001 + from pythainlp.translate.umt5_thaig2p import transliterate # type: ignore[import-not-found, no-redef] else: # use default engine: "thaig2p" from pythainlp.transliterate.thaig2p import transliterate # noqa: I001 From 61e55693a013862a40ac7f605626a78e7d40dc1e Mon Sep 17 00:00:00 2001 From: Arthit Suriyawongkul Date: Mon, 9 Mar 2026 11:00:22 +0700 Subject: [PATCH 07/12] Fix import path for umt5_thaig2p transliterate --- pythainlp/transliterate/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythainlp/transliterate/core.py b/pythainlp/transliterate/core.py index f975f41d3..6078bf1a2 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-not-found, no-redef] + from pythainlp.transliterate.umt5_thaig2p import transliterate # noqa: I001 else: # use default engine: "thaig2p" from pythainlp.transliterate.thaig2p import transliterate # noqa: I001 From 42ea68263238cb62f910b7f68e32b76df97a4a1f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Mar 2026 04:01:34 +0000 Subject: [PATCH 08/12] Add flake8-type-checking; fix TC006, TC005, F401 noqa placement; recheck type: ignore comments Co-authored-by: bact <128572+bact@users.noreply.github.com> --- .flake8 | 26 ++++++++++++++++++++ pyproject.toml | 14 +++++++++++ pythainlp/benchmarks/metrics.py | 2 +- pythainlp/braille/core.py | 6 ++--- pythainlp/parse/transformers_ud.py | 2 +- pythainlp/parse/ud_goeswith.py | 5 +--- pythainlp/phayathaibert/core.py | 2 +- pythainlp/spell/pn.py | 4 +-- pythainlp/summarize/core.py | 2 +- pythainlp/summarize/freq.py | 2 +- pythainlp/tag/thai_nner.py | 2 +- pythainlp/tokenize/attacut.py | 2 +- pythainlp/tokenize/budoux.py | 2 +- pythainlp/tokenize/deepcut.py | 4 +-- pythainlp/tokenize/nlpo3.py | 4 +-- pythainlp/tokenize/oskut.py | 2 +- pythainlp/tokenize/sefr_cut.py | 2 +- pythainlp/tokenize/ssg.py | 2 +- pythainlp/tokenize/wtsplit.py | 6 ++--- pythainlp/tools/path.py | 2 +- pythainlp/translate/tokenization_small100.py | 2 +- pythainlp/translate/zh_th.py | 2 +- pythainlp/transliterate/core.py | 2 +- pythainlp/transliterate/wunsen.py | 5 +--- pythainlp/util/digitconv.py | 6 ++--- pythainlp/util/keyboard.py | 4 +-- pythainlp/wsd/core.py | 6 ++--- 27 files changed, 77 insertions(+), 43 deletions(-) create mode 100644 .flake8 diff --git a/.flake8 b/.flake8 new file mode 100644 index 000000000..4f03afb0a --- /dev/null +++ b/.flake8 @@ -0,0 +1,26 @@ +[flake8] +max-line-length = 120 +extend-ignore = + # Whitespace before ':' — conflicts with Black slice formatting + E203, + # Module level import not at top of file — needed for optional/lazy imports + E402, + # Line too long — ruff already handles style; E501 in ruff extend-ignore too + E501, + # Line break before binary operator — Black compatibility + W503, + # Redefinition of unused name — intentional lazy-import pattern + F811, + # Move app/third-party/built-in import into TYPE_CHECKING block + # These are valid optimizations but require a codebase-wide refactor; + # suppress here and address in a dedicated follow-up. + TC001, + TC002, + TC003, +per-file-ignores = + # Pre-existing bare-except usage + pythainlp/benchmarks/word_tokenization.py: E722 + pythainlp/khavee/core.py: E722 + pythainlp/spell/wanchanberta_thai_grammarly.py: E722 + # Pre-existing missing blank lines + pythainlp/chat/core.py: E302 diff --git a/pyproject.toml b/pyproject.toml index e4376bfc8..9dfeba779 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -75,7 +75,10 @@ dev = [ "build>=1.0.0", "bump-my-version>=1.2.6", "coverage>=7.10.7", + "flake8>=7.0.0", + "flake8-type-checking>=3.2.0", "mypy>=1.19.1", + "pylint>=4.0.0", "ruff>=0.14.14", "tox>=4.30.3", ] @@ -497,3 +500,14 @@ module = [ "yaml.*", ] ignore_missing_imports = true + +[tool.pylint.main] +disable = [ + "import-error", + "no-name-in-module", + "too-few-public-methods", + "too-many-arguments", + "too-many-locals", + "too-many-branches", + "too-many-statements", +] diff --git a/pythainlp/benchmarks/metrics.py b/pythainlp/benchmarks/metrics.py index 84a221c18..a44f036b5 100644 --- a/pythainlp/benchmarks/metrics.py +++ b/pythainlp/benchmarks/metrics.py @@ -134,7 +134,7 @@ def bleu_score( # Normalize references format if references and isinstance(references[0], str): refs_normalized: list[list[str]] = [ - [ref] for ref in cast(list[str], references) + [ref] for ref in cast("list[str]", references) ] else: refs_normalized = references # type: ignore[assignment] diff --git a/pythainlp/braille/core.py b/pythainlp/braille/core.py index 41bd2576e..6acb33794 100644 --- a/pythainlp/braille/core.py +++ b/pythainlp/braille/core.py @@ -246,7 +246,7 @@ def __init__(self, data: Union[list[list[str]], list[str], str]) -> None: """Initialize Braille converter. :param data: Braille dot patterns as list or string - :type data: list[list[str]] or list[str] or str + :type data: Union[list[list[str]], list[str], str] """ self.inputdata: Union[list[list[str]], list[str], str] = data if isinstance(data, list): @@ -542,7 +542,7 @@ def tobraille(self) -> str: result += self.db[pattern_str] return result else: - pattern_str = "".join(cast(list[str], self.data)) + pattern_str = "".join(cast("list[str]", self.data)) return self.db.get(pattern_str, "") def printbraille(self) -> str: @@ -573,6 +573,6 @@ def printbraille(self) -> str: mirrored_patterns.reverse() return "".join(mirrored_patterns) else: - mirrored = "".join(mirror_map[dot] for dot in cast(list[str], self.data)) + mirrored = "".join(mirror_map[dot] for dot in cast("list[str]", self.data)) mirrored_sorted = "".join(sorted(mirrored)) return self.db[mirrored_sorted] diff --git a/pythainlp/parse/transformers_ud.py b/pythainlp/parse/transformers_ud.py index 7105cee03..f0a79bef6 100644 --- a/pythainlp/parse/transformers_ud.py +++ b/pythainlp/parse/transformers_ud.py @@ -15,7 +15,7 @@ from typing import TYPE_CHECKING, Optional, Union if TYPE_CHECKING: - from transformers import ( + from transformers import ( # noqa: F401 AutoModelForQuestionAnswering, AutoTokenizer, TokenClassificationPipeline, diff --git a/pythainlp/parse/ud_goeswith.py b/pythainlp/parse/ud_goeswith.py index 76826109f..1038fbe1f 100644 --- a/pythainlp/parse/ud_goeswith.py +++ b/pythainlp/parse/ud_goeswith.py @@ -11,13 +11,10 @@ from __future__ import annotations -from typing import TYPE_CHECKING, List, Optional, Union +from typing import List, Optional, Union from transformers import AutoModelForTokenClassification, AutoTokenizer -if TYPE_CHECKING: - pass - class Parse: def __init__( diff --git a/pythainlp/phayathaibert/core.py b/pythainlp/phayathaibert/core.py index dba072907..aa61fe614 100644 --- a/pythainlp/phayathaibert/core.py +++ b/pythainlp/phayathaibert/core.py @@ -10,7 +10,7 @@ from typing import TYPE_CHECKING, Union if TYPE_CHECKING: - from transformers import ( + from transformers import ( # noqa: F401 AutoModelForMaskedLM, AutoModelForTokenClassification, CamembertTokenizer, diff --git a/pythainlp/spell/pn.py b/pythainlp/spell/pn.py index e63611741..3488e2230 100644 --- a/pythainlp/spell/pn.py +++ b/pythainlp/spell/pn.py @@ -97,7 +97,7 @@ def _convert_custom_dict( if isinstance(first_member, str): # create tuples of a word with frequency equaling 1, # and filter word list - custom_dict = cast(Iterable[str], custom_dict) + custom_dict = cast("Iterable[str]", custom_dict) result = [ (word, 1) for word in custom_dict @@ -105,7 +105,7 @@ def _convert_custom_dict( ] elif isinstance(first_member, tuple): # filter word list - custom_dict = cast(Iterable[tuple[str, int]], custom_dict) + custom_dict = cast("Iterable[tuple[str, int]]", custom_dict) result = [ word_freq for word_freq in custom_dict diff --git a/pythainlp/summarize/core.py b/pythainlp/summarize/core.py index f0c106ff2..6604d0179 100644 --- a/pythainlp/summarize/core.py +++ b/pythainlp/summarize/core.py @@ -227,7 +227,7 @@ def rank_by_frequency( from .keybert import KeyBERT keywords = cast( - list[str], + "list[str]", KeyBERT().extract_keywords( text, keyphrase_ngram_range=keyphrase_ngram_range, diff --git a/pythainlp/summarize/freq.py b/pythainlp/summarize/freq.py index 503be9969..c488e1da8 100644 --- a/pythainlp/summarize/freq.py +++ b/pythainlp/summarize/freq.py @@ -56,7 +56,7 @@ def summarize( ) -> list[str]: # sent_tokenize with str input returns list[str] sents = cast( - list[str], sent_tokenize(text, engine="whitespace+newline") + "list[str]", sent_tokenize(text, engine="whitespace+newline") ) word_tokenized_sents = [ word_tokenize(sent, engine=tokenizer) for sent in sents diff --git a/pythainlp/tag/thai_nner.py b/pythainlp/tag/thai_nner.py index 60d945db5..d956f3333 100644 --- a/pythainlp/tag/thai_nner.py +++ b/pythainlp/tag/thai_nner.py @@ -14,7 +14,7 @@ from pythainlp.corpus import get_corpus_path if TYPE_CHECKING: - from thai_nner import NNER + from thai_nner import NNER # noqa: F401 __all__: list[str] = ["ThaiNNER"] diff --git a/pythainlp/tokenize/attacut.py b/pythainlp/tokenize/attacut.py index 4be1f0e9b..6f584f28f 100644 --- a/pythainlp/tokenize/attacut.py +++ b/pythainlp/tokenize/attacut.py @@ -28,7 +28,7 @@ def __init__(self, model: str = "attacut-sc") -> None: self._tokenizer: Tokenizer = Tokenizer(model=self._MODEL_NAME) def tokenize(self, text: str) -> list[str]: - return cast(list[str], self._tokenizer.tokenize(text)) + return cast("list[str]", self._tokenizer.tokenize(text)) _tokenizers: dict[str, AttacutTokenizer] = {} diff --git a/pythainlp/tokenize/budoux.py b/pythainlp/tokenize/budoux.py index 3b613b75e..bff80f23d 100644 --- a/pythainlp/tokenize/budoux.py +++ b/pythainlp/tokenize/budoux.py @@ -56,6 +56,6 @@ def segment(text: str) -> list[str]: _parser = _init_parser() parser = _parser - result = cast(list[str], parser.parse(text)) + result = cast("list[str]", parser.parse(text)) return result diff --git a/pythainlp/tokenize/deepcut.py b/pythainlp/tokenize/deepcut.py index 8fda7477b..ba397ab17 100644 --- a/pythainlp/tokenize/deepcut.py +++ b/pythainlp/tokenize/deepcut.py @@ -31,6 +31,6 @@ def segment( if isinstance(custom_dict, Trie): custom_dict = list(custom_dict) - return cast(list[str], tokenize(text, custom_dict)) + return cast("list[str]", tokenize(text, custom_dict)) - return cast(list[str], tokenize(text)) + return cast("list[str]", tokenize(text)) diff --git a/pythainlp/tokenize/nlpo3.py b/pythainlp/tokenize/nlpo3.py index a117a8706..0f047690e 100644 --- a/pythainlp/tokenize/nlpo3.py +++ b/pythainlp/tokenize/nlpo3.py @@ -9,8 +9,8 @@ from typing import TYPE_CHECKING, Any, Optional if TYPE_CHECKING: - from nlpo3 import ( - load_dict as nlpo3_load_dict, # noqa: F401 + from nlpo3 import ( # noqa: F401 + load_dict as nlpo3_load_dict, ) from nlpo3 import segment as nlpo3_segment # noqa: F401 diff --git a/pythainlp/tokenize/oskut.py b/pythainlp/tokenize/oskut.py index 854be2b42..48235354e 100644 --- a/pythainlp/tokenize/oskut.py +++ b/pythainlp/tokenize/oskut.py @@ -46,4 +46,4 @@ def segment(text: str, engine: str = "ws") -> list[str]: _DEFAULT_ENGINE = engine oskut.load_model(engine=_DEFAULT_ENGINE) - return cast(list[str], oskut.OSKut(text)) + return cast("list[str]", oskut.OSKut(text)) diff --git a/pythainlp/tokenize/sefr_cut.py b/pythainlp/tokenize/sefr_cut.py index 8c9b23f43..d208a8daf 100644 --- a/pythainlp/tokenize/sefr_cut.py +++ b/pythainlp/tokenize/sefr_cut.py @@ -45,4 +45,4 @@ def segment(text: str, engine: str = "ws1000") -> list[str]: _DEFAULT_ENGINE = engine sefr_cut.load_model(engine=_DEFAULT_ENGINE) - return cast(list[str], sefr_cut.tokenize(text)[0]) + return cast("list[str]", sefr_cut.tokenize(text)[0]) diff --git a/pythainlp/tokenize/ssg.py b/pythainlp/tokenize/ssg.py index 50b929cfe..b12ac2cdc 100644 --- a/pythainlp/tokenize/ssg.py +++ b/pythainlp/tokenize/ssg.py @@ -13,4 +13,4 @@ def segment(text: str) -> list[str]: if not text or not isinstance(text, str): return [] - return cast(list[str], syllable_tokenize(text)) + return cast("list[str]", syllable_tokenize(text)) diff --git a/pythainlp/tokenize/wtsplit.py b/pythainlp/tokenize/wtsplit.py index 90c8ac897..2de89a73b 100644 --- a/pythainlp/tokenize/wtsplit.py +++ b/pythainlp/tokenize/wtsplit.py @@ -46,11 +46,11 @@ def _tokenize( raise RuntimeError("Model failed to load") if tokenize == "sentence": - return cast(list[str], model_instance.split(text, lang_code=lang_code)) + return cast("list[str]", model_instance.split(text, lang_code=lang_code)) else: # Paragraph if style == "newline": return cast( - list[str], + "list[str]", model_instance.split( text, lang_code=lang_code, @@ -60,7 +60,7 @@ def _tokenize( ) elif style == "opus100": return cast( - list[str], + "list[str]", model_instance.split( text, lang_code=lang_code, diff --git a/pythainlp/tools/path.py b/pythainlp/tools/path.py index cdc7e95d0..2cf597b1d 100644 --- a/pythainlp/tools/path.py +++ b/pythainlp/tools/path.py @@ -159,6 +159,6 @@ def get_pythainlp_path() -> str: # For compatibility, convert to string path if possible # This works for both regular installations and zip files if hasattr(package_path, "__fspath__"): - return os.fspath(cast(PathLike[str], package_path)) + return os.fspath(cast("PathLike[str]", package_path)) # Fallback for traversable objects that don't support __fspath__ return str(package_path) diff --git a/pythainlp/translate/tokenization_small100.py b/pythainlp/translate/tokenization_small100.py index 979d6491c..98cdde20d 100644 --- a/pythainlp/translate/tokenization_small100.py +++ b/pythainlp/translate/tokenization_small100.py @@ -197,7 +197,7 @@ def __init__( encoder_data = load_json(vocab_file) if not isinstance(encoder_data, dict): raise ValueError("encoder must be a dict") - self.encoder: dict[str, int] = cast(dict[str, int], encoder_data) + self.encoder: dict[str, int] = cast("dict[str, int]", encoder_data) self.decoder: dict[int, str] = {v: k for k, v in self.encoder.items()} self.spm_file: str = spm_file self.sp_model: SentencePieceProcessor = load_spm( diff --git a/pythainlp/translate/zh_th.py b/pythainlp/translate/zh_th.py index 4523a9caf..bd3cfb700 100644 --- a/pythainlp/translate/zh_th.py +++ b/pythainlp/translate/zh_th.py @@ -15,7 +15,7 @@ if TYPE_CHECKING: import torch - from transformers import AutoModelForSeq2SeqLM, AutoTokenizer + from transformers import AutoModelForSeq2SeqLM, AutoTokenizer # noqa: F401 class ThZhTranslator: diff --git a/pythainlp/transliterate/core.py b/pythainlp/transliterate/core.py index 6078bf1a2..cd01bad32 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.transliterate.umt5_thaig2p import transliterate # noqa: I001 + from pythainlp.translate.umt5_thaig2p import transliterate # type: ignore[import-not-found, no-redef] # noqa: I001 else: # use default engine: "thaig2p" from pythainlp.transliterate.thaig2p import transliterate # noqa: I001 diff --git a/pythainlp/transliterate/wunsen.py b/pythainlp/transliterate/wunsen.py index 62c333663..0d79c7672 100644 --- a/pythainlp/transliterate/wunsen.py +++ b/pythainlp/transliterate/wunsen.py @@ -12,13 +12,10 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Optional, Union +from typing import Optional, Union from wunsen import ThapSap -if TYPE_CHECKING: - pass - class WunsenTransliterate: """Transliterating Japanese/Korean/Mandarin/Vietnamese romanization text diff --git a/pythainlp/util/digitconv.py b/pythainlp/util/digitconv.py index 2d96acfc0..162ffafa9 100644 --- a/pythainlp/util/digitconv.py +++ b/pythainlp/util/digitconv.py @@ -60,13 +60,13 @@ } _arabic_thai_translate_table: dict[int, Union[int, str, None]] = str.maketrans( - cast(dict[str, Union[int, str, None]], _arabic_thai) + cast("dict[str, Union[int, str, None]]", _arabic_thai) ) _thai_arabic_translate_table: dict[int, Union[int, str, None]] = str.maketrans( - cast(dict[str, Union[int, str, None]], _thai_arabic) + cast("dict[str, Union[int, str, None]]", _thai_arabic) ) _digit_spell_translate_table: dict[int, Union[int, str, None]] = str.maketrans( - cast(dict[str, Union[int, str, None]], _digit_spell) + cast("dict[str, Union[int, str, None]]", _digit_spell) ) diff --git a/pythainlp/util/keyboard.py b/pythainlp/util/keyboard.py index d15a9e62c..0a697b1c5 100644 --- a/pythainlp/util/keyboard.py +++ b/pythainlp/util/keyboard.py @@ -105,10 +105,10 @@ TH_EN_KEYB_PAIRS: dict[str, str] = {v: k for k, v in EN_TH_KEYB_PAIRS.items()} EN_TH_TRANSLATE_TABLE: dict[int, Union[int, str, None]] = str.maketrans( - cast(dict[str, Union[int, str, None]], EN_TH_KEYB_PAIRS) + cast("dict[str, Union[int, str, None]]", EN_TH_KEYB_PAIRS) ) TH_EN_TRANSLATE_TABLE: dict[int, Union[int, str, None]] = str.maketrans( - cast(dict[str, Union[int, str, None]], TH_EN_KEYB_PAIRS) + cast("dict[str, Union[int, str, None]]", TH_EN_KEYB_PAIRS) ) TIS_820_2531_MOD: list[list[str]] = [ diff --git a/pythainlp/wsd/core.py b/pythainlp/wsd/core.py index 9e6d64fe8..e64a47342 100644 --- a/pythainlp/wsd/core.py +++ b/pythainlp/wsd/core.py @@ -15,12 +15,12 @@ _wsd_dict: dict[str, Union[list[str], list[list[str]]]] = thai_wsd_dict() _mean_all: dict[str, Any] = {} -_all_word: set[str] = cast(set[str], set(_mean_all.keys())) +_all_word: set[str] = cast("set[str]", set(_mean_all.keys())) _TRIE: Trie = Trie(_all_word) _word_cut: Tokenizer = Tokenizer(custom_dict=_TRIE) -words: list[str] = cast(list[str], _wsd_dict["word"]) -meanings: list[list[str]] = cast(list[list[str]], _wsd_dict["meaning"]) +words: list[str] = cast("list[str]", _wsd_dict["word"]) +meanings: list[list[str]] = cast("list[list[str]]", _wsd_dict["meaning"]) i: str j: list[str] for i, j in zip(words, meanings): From e411138e94dba88314485d1583076e3217351690 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Mar 2026 04:04:57 +0000 Subject: [PATCH 09/12] Add flake8-type-checking; fix TC006, TC005, F401 noqa; recheck type: ignore comments Co-authored-by: bact <128572+bact@users.noreply.github.com> --- pythainlp/transliterate/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pythainlp/transliterate/core.py b/pythainlp/transliterate/core.py index cd01bad32..6078bf1a2 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-not-found, no-redef] # noqa: I001 + from pythainlp.transliterate.umt5_thaig2p import transliterate # noqa: I001 else: # use default engine: "thaig2p" from pythainlp.transliterate.thaig2p import transliterate # noqa: I001 From 614b412615ac36b35d31431171abc89a0d658fed Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Mar 2026 04:19:48 +0000 Subject: [PATCH 10/12] Fix bare-except E722 and missing blank line E302; remove per-file-ignores from .flake8 Co-authored-by: bact <128572+bact@users.noreply.github.com> --- .flake8 | 7 ------- pythainlp/benchmarks/word_tokenization.py | 2 +- pythainlp/chat/core.py | 1 + pythainlp/khavee/core.py | 4 ++-- pythainlp/spell/wanchanberta_thai_grammarly.py | 2 +- 5 files changed, 5 insertions(+), 11 deletions(-) diff --git a/.flake8 b/.flake8 index 4f03afb0a..8b97e99fe 100644 --- a/.flake8 +++ b/.flake8 @@ -17,10 +17,3 @@ extend-ignore = TC001, TC002, TC003, -per-file-ignores = - # Pre-existing bare-except usage - pythainlp/benchmarks/word_tokenization.py: E722 - pythainlp/khavee/core.py: E722 - pythainlp/spell/wanchanberta_thai_grammarly.py: E722 - # Pre-existing missing blank lines - pythainlp/chat/core.py: E302 diff --git a/pythainlp/benchmarks/word_tokenization.py b/pythainlp/benchmarks/word_tokenization.py index 3afaeaa13..c98eb0d88 100644 --- a/pythainlp/benchmarks/word_tokenization.py +++ b/pythainlp/benchmarks/word_tokenization.py @@ -89,7 +89,7 @@ def benchmark(ref_samples: list[str], samples: list[str]) -> "pd.DataFrame": flat_stats["expected"] = r flat_stats["actual"] = s results.append(flat_stats) - except: + except Exception: reason = """ [Error] Reason: %s diff --git a/pythainlp/chat/core.py b/pythainlp/chat/core.py index 0b2a4dd4a..f8b034173 100644 --- a/pythainlp/chat/core.py +++ b/pythainlp/chat/core.py @@ -10,6 +10,7 @@ from pythainlp.generate.wangchanglm import WangChanGLM + class ChatBotModel: history: list[tuple[str, str]] model: "WangChanGLM" diff --git a/pythainlp/khavee/core.py b/pythainlp/khavee/core.py index b40c349a2..f940e8922 100644 --- a/pythainlp/khavee/core.py +++ b/pythainlp/khavee/core.py @@ -521,7 +521,7 @@ def check_klon(self, text: str, k_type: int = 8) -> Union[list[str], str]: ) else: return error - except: + except Exception: return "Something went wrong. Make sure you enter it in the correct form of klon 8." elif k_type == 4: try: @@ -620,7 +620,7 @@ def check_klon(self, text: str, k_type: int = 8) -> Union[list[str], str]: ) else: return error - except: + except Exception: return "Something went wrong. Make sure you enter it in the correct form." else: diff --git a/pythainlp/spell/wanchanberta_thai_grammarly.py b/pythainlp/spell/wanchanberta_thai_grammarly.py index 7053491ac..d020bfc5b 100644 --- a/pythainlp/spell/wanchanberta_thai_grammarly.py +++ b/pythainlp/spell/wanchanberta_thai_grammarly.py @@ -73,7 +73,7 @@ def align_word_ids(texts: str) -> list[int]: else: try: label_ids.append(2) - except: + except Exception: label_ids.append(-100) return label_ids From e9663dd96f39a723038e849346aae99fabcd3a05 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Mar 2026 04:34:55 +0000 Subject: [PATCH 11/12] WIP: begin TC001/TC002/TC003 and flake8 exception cleanup audit Co-authored-by: bact <128572+bact@users.noreply.github.com> --- .flake8 | 6 ----- pythainlp/benchmarks/metrics.py | 21 ++++++++++++----- pythainlp/benchmarks/word_tokenization.py | 8 +++++-- pythainlp/braille/core.py | 13 ++++++++--- pythainlp/corpus/phupha.py | 2 +- pythainlp/lm/__init__.py | 6 ++++- pythainlp/lm/qwen3.py | 24 +++++++++++--------- pythainlp/phayathaibert/core.py | 12 ++++++---- pythainlp/spell/words_spelling_correction.py | 4 +--- pythainlp/tag/crfchunk.py | 8 +++++-- pythainlp/tag/named_entity.py | 4 +--- pythainlp/tokenize/nlpo3.py | 4 +--- pythainlp/tokenize/wtsplit.py | 4 +++- pythainlp/translate/core.py | 6 ++--- pythainlp/util/khuap_klam.py | 14 ++++++------ 15 files changed, 80 insertions(+), 56 deletions(-) diff --git a/.flake8 b/.flake8 index 8b97e99fe..793054d27 100644 --- a/.flake8 +++ b/.flake8 @@ -11,9 +11,3 @@ extend-ignore = W503, # Redefinition of unused name — intentional lazy-import pattern F811, - # Move app/third-party/built-in import into TYPE_CHECKING block - # These are valid optimizations but require a codebase-wide refactor; - # suppress here and address in a dedicated follow-up. - TC001, - TC002, - TC003, diff --git a/pythainlp/benchmarks/metrics.py b/pythainlp/benchmarks/metrics.py index a44f036b5..8326694ea 100644 --- a/pythainlp/benchmarks/metrics.py +++ b/pythainlp/benchmarks/metrics.py @@ -7,6 +7,7 @@ This module provides pure Python implementations of common evaluation metrics (BLEU, ROUGE) that handle Thai text tokenization automatically. """ + from __future__ import annotations import math @@ -277,8 +278,12 @@ def rouge_score( rouge_types = ["rouge1", "rouge2", "rougeL"] # Tokenize texts - ref_tokens = word_tokenize(reference, engine=tokenize, keep_whitespace=False) - hyp_tokens = word_tokenize(hypothesis, engine=tokenize, keep_whitespace=False) + ref_tokens = word_tokenize( + reference, engine=tokenize, keep_whitespace=False + ) + hyp_tokens = word_tokenize( + hypothesis, engine=tokenize, keep_whitespace=False + ) result: dict[str, tuple[float, float, float]] = {} @@ -368,8 +373,12 @@ def word_error_rate( from pythainlp.tokenize import word_tokenize # Tokenize texts - ref_tokens = word_tokenize(reference, engine=tokenize, keep_whitespace=False) - hyp_tokens = word_tokenize(hypothesis, engine=tokenize, keep_whitespace=False) + ref_tokens = word_tokenize( + reference, engine=tokenize, keep_whitespace=False + ) + hyp_tokens = word_tokenize( + hypothesis, engine=tokenize, keep_whitespace=False + ) # Calculate edit distance using dynamic programming r = len(ref_tokens) @@ -397,7 +406,7 @@ def word_error_rate( # Calculate WER if r == 0: - return 0.0 if h == 0 else float('inf') + return 0.0 if h == 0 else float("inf") return d[r][h] / r @@ -469,6 +478,6 @@ def character_error_rate( # Calculate CER if r == 0: - return 0.0 if h == 0 else float('inf') + return 0.0 if h == 0 else float("inf") return d[r][h] / r diff --git a/pythainlp/benchmarks/word_tokenization.py b/pythainlp/benchmarks/word_tokenization.py index c98eb0d88..f74e1b1f1 100644 --- a/pythainlp/benchmarks/word_tokenization.py +++ b/pythainlp/benchmarks/word_tokenization.py @@ -42,7 +42,9 @@ def _f1(precision: float, recall: float) -> float: return 2 * precision * recall / (precision + recall) -def _flatten_result(my_dict: dict, sep: str = ":") -> dict[str, Union[int, str]]: +def _flatten_result( + my_dict: dict, sep: str = ":" +) -> dict[str, Union[int, str]]: """Flatten two-dimension dictionary. Use keys in the first dimension as a prefix for keys in the second dimension. @@ -133,7 +135,9 @@ def preprocessing(txt: str, remove_space: bool = True) -> str: return txt -def compute_stats(ref_sample: str, raw_sample: str) -> dict[str, dict[str, Union[int, str]]]: +def compute_stats( + ref_sample: str, raw_sample: str +) -> dict[str, dict[str, Union[int, str]]]: """Compute statistics for tokenization quality These statistics include: diff --git a/pythainlp/braille/core.py b/pythainlp/braille/core.py index 6acb33794..682415630 100644 --- a/pythainlp/braille/core.py +++ b/pythainlp/braille/core.py @@ -139,11 +139,16 @@ _v1: list[str] = ["เ-tอ", "เ-ีtย", "เ-ืtอ", "-ัtว", "เ-tา", "เ-tาะ"] # Create trie for efficient pattern matching -char_trie: Trie = Trie(list(thai_braille_mapping_dict.keys()) + _v1 + [" ", ""]) +char_trie: Trie = Trie( + list(thai_braille_mapping_dict.keys()) + _v1 + [" ", ""] +) # Build vowel replacement patterns _vowel_patterns: list[str] = [ - i.replace("-", "([ก-ฮ])").replace("t", "([่้๊๋])") + ",\\1" + i.replace("t", "") + "\\2" + i.replace("-", "([ก-ฮ])").replace("t", "([่้๊๋])") + + ",\\1" + + i.replace("t", "") + + "\\2" for i in _v1 ] _vowel_patterns += [ @@ -573,6 +578,8 @@ def printbraille(self) -> str: mirrored_patterns.reverse() return "".join(mirrored_patterns) else: - mirrored = "".join(mirror_map[dot] for dot in cast("list[str]", self.data)) + mirrored = "".join( + mirror_map[dot] for dot in cast("list[str]", self.data) + ) mirrored_sorted = "".join(sorted(mirrored)) return self.db[mirrored_sorted] diff --git a/pythainlp/corpus/phupha.py b/pythainlp/corpus/phupha.py index 693f4c459..6711371f3 100644 --- a/pythainlp/corpus/phupha.py +++ b/pythainlp/corpus/phupha.py @@ -72,7 +72,7 @@ def unigram_word_freqs() -> dict[str, int]: from pythainlp.corpus import phupha freqs = phupha.unigram_word_freqs() - print(freqs.get('ไทย', 0)) + print(freqs.get("ไทย", 0)) # output: frequency count for 'ไทย' **Dataset Citation:** diff --git a/pythainlp/lm/__init__.py b/pythainlp/lm/__init__.py index 36ff60a7c..f09558359 100644 --- a/pythainlp/lm/__init__.py +++ b/pythainlp/lm/__init__.py @@ -2,7 +2,11 @@ # SPDX-FileType: SOURCE # SPDX-License-Identifier: Apache-2.0 -__all__: list[str] = ["calculate_ngram_counts", "remove_repeated_ngrams", "Qwen3"] +__all__: list[str] = [ + "calculate_ngram_counts", + "remove_repeated_ngrams", + "Qwen3", +] from pythainlp.lm.qwen3 import Qwen3 from pythainlp.lm.text_util import ( diff --git a/pythainlp/lm/qwen3.py b/pythainlp/lm/qwen3.py index 6b2fdf3b5..778ab29dc 100644 --- a/pythainlp/lm/qwen3.py +++ b/pythainlp/lm/qwen3.py @@ -146,9 +146,7 @@ def generate( ) if not text or not isinstance(text, str): - raise ValueError( - "text parameter must be a non-empty string." - ) + raise ValueError("text parameter must be a non-empty string.") try: import torch @@ -176,10 +174,12 @@ def generate( # Decode only the newly generated tokens # output_ids and input_ids are guaranteed to be 2D tensors with # batch size 1 from the tokenizer call above - generated_text = str(self.tokenizer.decode( - output_ids[0][len(input_ids[0]) :], - skip_special_tokens=skip_special_tokens, - )) + generated_text = str( + self.tokenizer.decode( + output_ids[0][len(input_ids[0]) :], + skip_special_tokens=skip_special_tokens, + ) + ) return generated_text @@ -270,9 +270,11 @@ def chat( # Decode only the newly generated tokens # output_ids and input_ids are guaranteed to be 2D tensors with # batch size 1 from the tokenizer call above - generated_text = str(self.tokenizer.decode( - output_ids[0][len(input_ids[0]) :], - skip_special_tokens=skip_special_tokens, - )) + generated_text = str( + self.tokenizer.decode( + output_ids[0][len(input_ids[0]) :], + skip_special_tokens=skip_special_tokens, + ) + ) return generated_text diff --git a/pythainlp/phayathaibert/core.py b/pythainlp/phayathaibert/core.py index aa61fe614..4773947a4 100644 --- a/pythainlp/phayathaibert/core.py +++ b/pythainlp/phayathaibert/core.py @@ -217,8 +217,8 @@ def __init__(self) -> None: pipeline, ) - self.tokenizer: "PreTrainedTokenizerBase" = AutoTokenizer.from_pretrained( - _model_name + self.tokenizer: "PreTrainedTokenizerBase" = ( + AutoTokenizer.from_pretrained(_model_name) ) self.model_for_masked_lm: "AutoModelForMaskedLM" = ( AutoModelForMaskedLM.from_pretrained(_model_name) @@ -316,7 +316,9 @@ def __init__(self, model: str = "lunarlist/pos_thai_phayathai") -> None: AutoTokenizer, ) - self.tokenizer: "PreTrainedTokenizerBase" = AutoTokenizer.from_pretrained(model) + self.tokenizer: "PreTrainedTokenizerBase" = ( + AutoTokenizer.from_pretrained(model) + ) self.model: "AutoModelForTokenClassification" = ( AutoModelForTokenClassification.from_pretrained(model) ) @@ -361,7 +363,9 @@ def __init__(self, model: str = "Pavarissy/phayathaibert-thainer") -> None: AutoTokenizer, ) - self.tokenizer: "PreTrainedTokenizerBase" = AutoTokenizer.from_pretrained(model) + self.tokenizer: "PreTrainedTokenizerBase" = ( + AutoTokenizer.from_pretrained(model) + ) self.model: "AutoModelForTokenClassification" = ( AutoModelForTokenClassification.from_pretrained(model) ) diff --git a/pythainlp/spell/words_spelling_correction.py b/pythainlp/spell/words_spelling_correction.py index 1be5a6e16..847d63fe1 100644 --- a/pythainlp/spell/words_spelling_correction.py +++ b/pythainlp/spell/words_spelling_correction.py @@ -268,9 +268,7 @@ class Words_Spelling_Correction(FastTextEncoder): def __init__(self) -> None: self.model_name = "pythainlp/word-spelling-correction-char2vec" self.model_path = get_hf_hub(self.model_name) - self.model_onnx = get_hf_hub( - self.model_name, "nearest_neighbors.onnx" - ) + self.model_onnx = get_hf_hub(self.model_name, "nearest_neighbors.onnx") with open( get_hf_hub( self.model_name, "list_word-spelling-correction-char2vec.txt" diff --git a/pythainlp/tag/crfchunk.py b/pythainlp/tag/crfchunk.py index d3c2ead37..96a45be52 100644 --- a/pythainlp/tag/crfchunk.py +++ b/pythainlp/tag/crfchunk.py @@ -17,7 +17,9 @@ def _is_stopword(word: str) -> bool: # check Thai stopword return word in thai_stopwords() -def _doc2features(tokens: list[tuple[str, str]], index: int) -> dict[str, Union[str, bool]]: +def _doc2features( + tokens: list[tuple[str, str]], index: int +) -> dict[str, Union[str, bool]]: """`tokens` = a POS-tagged sentence [(w1, t1), ...] `index` = the index of the token we want to extract features for """ @@ -55,7 +57,9 @@ def _doc2features(tokens: list[tuple[str, str]], index: int) -> dict[str, Union[ return f -def extract_features(doc: list[tuple[str, str]]) -> list[dict[str, Union[str, bool]]]: +def extract_features( + doc: list[tuple[str, str]], +) -> list[dict[str, Union[str, bool]]]: return [_doc2features(doc, i) for i in range(0, len(doc))] diff --git a/pythainlp/tag/named_entity.py b/pythainlp/tag/named_entity.py index beb8ebedb..7a920f4c2 100644 --- a/pythainlp/tag/named_entity.py +++ b/pythainlp/tag/named_entity.py @@ -92,9 +92,7 @@ def load_engine(self, engine: str, corpus: str) -> None: ThaiNameTagger as WangchanbertaThaiNameTagger, ) # noqa: I001,E501 - self.engine = WangchanbertaThaiNameTagger( - dataset_name=corpus - ) + self.engine = WangchanbertaThaiNameTagger(dataset_name=corpus) elif corpus == "thainer-v2": if engine == "phayathaibert": from pythainlp.phayathaibert.core import NamedEntityTagger diff --git a/pythainlp/tokenize/nlpo3.py b/pythainlp/tokenize/nlpo3.py index 0f047690e..05ed29e2b 100644 --- a/pythainlp/tokenize/nlpo3.py +++ b/pythainlp/tokenize/nlpo3.py @@ -88,9 +88,7 @@ def load_dict(file_path: str, dict_name: str) -> bool: msg: str success: bool - msg, success = nlpo3_load_dict( - file_path=file_path, dict_name=dict_name - ) + msg, success = nlpo3_load_dict(file_path=file_path, dict_name=dict_name) if not success: print(msg, file=stderr) return success diff --git a/pythainlp/tokenize/wtsplit.py b/pythainlp/tokenize/wtsplit.py index 2de89a73b..52c1a09e5 100644 --- a/pythainlp/tokenize/wtsplit.py +++ b/pythainlp/tokenize/wtsplit.py @@ -46,7 +46,9 @@ def _tokenize( raise RuntimeError("Model failed to load") if tokenize == "sentence": - return cast("list[str]", model_instance.split(text, lang_code=lang_code)) + return cast( + "list[str]", model_instance.split(text, lang_code=lang_code) + ) else: # Paragraph if style == "newline": return cast( diff --git a/pythainlp/translate/core.py b/pythainlp/translate/core.py index 5774e1840..56f289a9b 100644 --- a/pythainlp/translate/core.py +++ b/pythainlp/translate/core.py @@ -66,12 +66,12 @@ def _prepare_text_with_exclusions( # - a delimiter character such as whitespace or common punctuation. # This allows matching words like "cat" in "I love cat.". delimiter_chars = r"\s" + re.escape( - ".,!?;:'\"()[]{}<>/\\|`~@#$%^&*-+=""''、,。!?;:()【】《》" + ".,!?;:'\"()[]{}<>/\\|`~@#$%^&*-+=''、,。!?;:()【】《》" ) pattern = ( - fr"(?:(?<=^)|(?<=[{delimiter_chars}]))" + rf"(?:(?<=^)|(?<=[{delimiter_chars}]))" f"{escaped_word}" - fr"(?:(?=$)|(?=[{delimiter_chars}]))" + rf"(?:(?=$)|(?=[{delimiter_chars}]))" ) # Check if there's a match with token boundaries diff --git a/pythainlp/util/khuap_klam.py b/pythainlp/util/khuap_klam.py index 81af7e5d4..e1fa9855c 100644 --- a/pythainlp/util/khuap_klam.py +++ b/pythainlp/util/khuap_klam.py @@ -40,19 +40,19 @@ def check_khuap_klam(word: str) -> Optional[bool]: from pythainlp.util import check_khuap_klam # True consonant clusters (คำควบกล้ำแท้) - print(check_khuap_klam("กราบ")) # True - print(check_khuap_klam("ปลา")) # True + print(check_khuap_klam("กราบ")) # True + print(check_khuap_klam("ปลา")) # True print(check_khuap_klam("เพราะ")) # True - print(check_khuap_klam("ตรง")) # True + print(check_khuap_klam("ตรง")) # True # False consonant clusters (คำควบกล้ำไม่แท้) - print(check_khuap_klam("จริง")) # False - print(check_khuap_klam("ทราย")) # False + print(check_khuap_klam("จริง")) # False + print(check_khuap_klam("ทราย")) # False print(check_khuap_klam("เศร้า")) # False # Not a consonant cluster - print(check_khuap_klam("แม่")) # None - print(check_khuap_klam("ตา")) # None + print(check_khuap_klam("แม่")) # None + print(check_khuap_klam("ตา")) # None """ if not word: return None From f7278b2a34b3bc42cdb1be557fd16bf9e1608026 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 9 Mar 2026 04:42:44 +0000 Subject: [PATCH 12/12] Move type-only imports into TYPE_CHECKING blocks (fix TC001/TC002/TC003) Move 37 type-only imports across 27 files into if TYPE_CHECKING: blocks to satisfy flake8-type-checking rules TC001, TC002, and TC003. All affected files already have 'from __future__ import annotations', so annotations are lazily evaluated strings at runtime. These imports are only used in type annotations and are not needed at runtime. Files with existing TYPE_CHECKING block (imports merged in): - pythainlp/corpus/core.py - pythainlp/phayathaibert/core.py - pythainlp/summarize/keybert.py - pythainlp/ulmfit/core.py Files with new TYPE_CHECKING block added: - pythainlp/augment/wordnet.py - pythainlp/corpus/util.py - pythainlp/corpus/wordnet.py - pythainlp/spell/pn.py - pythainlp/summarize/core.py - pythainlp/tag/_tag_perceptron.py - pythainlp/tag/crfchunk.py - pythainlp/tokenize/_utils.py - pythainlp/tokenize/core.py - pythainlp/tokenize/longest.py - pythainlp/tokenize/multi_cut.py - pythainlp/tokenize/nercut.py - pythainlp/tokenize/newmm.py - pythainlp/tokenize/pyicu.py - pythainlp/tokenize/tcc.py - pythainlp/tokenize/tcc_p.py - pythainlp/tools/path.py - pythainlp/transliterate/lookup.py - pythainlp/ulmfit/preprocess.py - pythainlp/ulmfit/tokenizer.py - pythainlp/util/collate.py - pythainlp/util/remove_trailing_repeat_consonants.py - pythainlp/util/strftime.py Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- pythainlp/augment/wordnet.py | 6 ++++-- pythainlp/corpus/core.py | 2 +- pythainlp/corpus/util.py | 5 ++++- pythainlp/corpus/wordnet.py | 6 ++++-- pythainlp/phayathaibert/core.py | 3 ++- pythainlp/spell/pn.py | 6 ++++-- pythainlp/summarize/core.py | 6 ++++-- pythainlp/summarize/keybert.py | 3 ++- pythainlp/tag/_tag_perceptron.py | 6 ++++-- pythainlp/tag/crfchunk.py | 8 +++++--- pythainlp/tokenize/_utils.py | 5 ++++- pythainlp/tokenize/core.py | 6 ++++-- pythainlp/tokenize/longest.py | 6 ++++-- pythainlp/tokenize/multi_cut.py | 9 ++++++--- pythainlp/tokenize/nercut.py | 5 ++++- pythainlp/tokenize/newmm.py | 9 ++++++--- pythainlp/tokenize/pyicu.py | 5 ++++- pythainlp/tokenize/tcc.py | 5 ++++- pythainlp/tokenize/tcc_p.py | 5 ++++- pythainlp/tools/path.py | 6 ++++-- pythainlp/transliterate/lookup.py | 6 ++++-- pythainlp/ulmfit/core.py | 3 ++- pythainlp/ulmfit/preprocess.py | 6 ++++-- pythainlp/ulmfit/tokenizer.py | 5 ++++- pythainlp/util/collate.py | 6 ++++-- pythainlp/util/remove_trailing_repeat_consonants.py | 5 ++++- pythainlp/util/strftime.py | 5 ++++- 27 files changed, 104 insertions(+), 44 deletions(-) diff --git a/pythainlp/augment/wordnet.py b/pythainlp/augment/wordnet.py index 01b2aa706..454d707a0 100644 --- a/pythainlp/augment/wordnet.py +++ b/pythainlp/augment/wordnet.py @@ -12,10 +12,12 @@ import itertools from collections import OrderedDict -from typing import Callable, Optional +from typing import TYPE_CHECKING, Callable, Optional from nltk.corpus import wordnet as wn -from nltk.corpus.reader.wordnet import Synset + +if TYPE_CHECKING: + from nltk.corpus.reader.wordnet import Synset from pythainlp.corpus import wordnet from pythainlp.tag import pos_tag diff --git a/pythainlp/corpus/core.py b/pythainlp/corpus/core.py index 403c00c84..5dc1f94d9 100644 --- a/pythainlp/corpus/core.py +++ b/pythainlp/corpus/core.py @@ -11,7 +11,6 @@ import sys import tarfile import zipfile -from http.client import HTTPMessage, HTTPResponse from importlib.resources import files from typing import TYPE_CHECKING @@ -21,6 +20,7 @@ from pythainlp.tools.path import is_offline_mode if TYPE_CHECKING: + from http.client import HTTPMessage, HTTPResponse from typing import Any, Optional _CHECK_MODE: Optional[str] = os.getenv("PYTHAINLP_READ_MODE") diff --git a/pythainlp/corpus/util.py b/pythainlp/corpus/util.py index 1555b2e14..5f7e568d2 100644 --- a/pythainlp/corpus/util.py +++ b/pythainlp/corpus/util.py @@ -14,7 +14,10 @@ from __future__ import annotations from collections import Counter -from collections.abc import Callable, Iterable, Iterator +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from collections.abc import Callable, Iterable, Iterator from pythainlp.corpus import thai_words from pythainlp.tokenize import newmm diff --git a/pythainlp/corpus/wordnet.py b/pythainlp/corpus/wordnet.py index b0a8cec3b..f3b3244f6 100644 --- a/pythainlp/corpus/wordnet.py +++ b/pythainlp/corpus/wordnet.py @@ -12,8 +12,10 @@ from __future__ import annotations -from collections.abc import Iterable -from typing import IO, Optional, Union +from typing import IO, TYPE_CHECKING, Optional, Union + +if TYPE_CHECKING: + from collections.abc import Iterable import nltk diff --git a/pythainlp/phayathaibert/core.py b/pythainlp/phayathaibert/core.py index 4773947a4..3bbbb3011 100644 --- a/pythainlp/phayathaibert/core.py +++ b/pythainlp/phayathaibert/core.py @@ -6,10 +6,11 @@ import random import re import warnings -from collections.abc import Callable from typing import TYPE_CHECKING, Union if TYPE_CHECKING: + from collections.abc import Callable + from transformers import ( # noqa: F401 AutoModelForMaskedLM, AutoModelForTokenClassification, diff --git a/pythainlp/spell/pn.py b/pythainlp/spell/pn.py index 3488e2230..b04e7a5ca 100644 --- a/pythainlp/spell/pn.py +++ b/pythainlp/spell/pn.py @@ -9,9 +9,11 @@ from __future__ import annotations from collections import Counter -from collections.abc import Callable, ItemsView, Iterable from string import digits -from typing import Optional, Union, cast +from typing import TYPE_CHECKING, Optional, Union, cast + +if TYPE_CHECKING: + from collections.abc import Callable, ItemsView, Iterable from pythainlp import thai_digits, thai_letters from pythainlp.corpus import phupha, thai_orst_words diff --git a/pythainlp/summarize/core.py b/pythainlp/summarize/core.py index 6604d0179..77c9ad56d 100644 --- a/pythainlp/summarize/core.py +++ b/pythainlp/summarize/core.py @@ -5,8 +5,10 @@ from __future__ import annotations -from collections.abc import Iterable -from typing import Optional, cast +from typing import TYPE_CHECKING, Optional, cast + +if TYPE_CHECKING: + from collections.abc import Iterable from pythainlp.summarize import ( CPE_KMUTT_THAI_SENTENCE_SUM, diff --git a/pythainlp/summarize/keybert.py b/pythainlp/summarize/keybert.py index 46f72ccf9..a6007a6dd 100644 --- a/pythainlp/summarize/keybert.py +++ b/pythainlp/summarize/keybert.py @@ -13,13 +13,14 @@ from __future__ import annotations from collections import Counter -from collections.abc import Iterable from typing import TYPE_CHECKING, Optional, Union from pythainlp.corpus import thai_stopwords from pythainlp.tokenize import word_tokenize if TYPE_CHECKING: + from collections.abc import Iterable + import numpy as np from transformers.pipelines.base import Pipeline diff --git a/pythainlp/tag/_tag_perceptron.py b/pythainlp/tag/_tag_perceptron.py index df6f2715b..42a488fba 100644 --- a/pythainlp/tag/_tag_perceptron.py +++ b/pythainlp/tag/_tag_perceptron.py @@ -21,8 +21,10 @@ import json from collections import defaultdict -from collections.abc import Iterable -from typing import Optional, Union +from typing import TYPE_CHECKING, Optional, Union + +if TYPE_CHECKING: + from collections.abc import Iterable class AveragedPerceptron: diff --git a/pythainlp/tag/crfchunk.py b/pythainlp/tag/crfchunk.py index 96a45be52..87aa883f0 100644 --- a/pythainlp/tag/crfchunk.py +++ b/pythainlp/tag/crfchunk.py @@ -3,10 +3,12 @@ # SPDX-License-Identifier: Apache-2.0 from __future__ import annotations -import types -from contextlib import AbstractContextManager from importlib.resources import as_file, files -from typing import Any, Optional, Union +from typing import TYPE_CHECKING, Any, Optional, Union + +if TYPE_CHECKING: + import types + from contextlib import AbstractContextManager from pycrfsuite import Tagger as CRFTagger diff --git a/pythainlp/tokenize/_utils.py b/pythainlp/tokenize/_utils.py index 8e3ec1374..d9dad6ed9 100644 --- a/pythainlp/tokenize/_utils.py +++ b/pythainlp/tokenize/_utils.py @@ -6,7 +6,10 @@ from __future__ import annotations import re -from collections.abc import Callable, Sequence +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from collections.abc import Callable, Sequence _DIGITS_WITH_SEPARATOR: re.Pattern[str] = re.compile(r"(\d+[\.\,:])+\d+") diff --git a/pythainlp/tokenize/core.py b/pythainlp/tokenize/core.py index 48d3a3cd7..e896507d8 100644 --- a/pythainlp/tokenize/core.py +++ b/pythainlp/tokenize/core.py @@ -7,8 +7,10 @@ import re from collections import deque -from collections.abc import Iterable -from typing import Optional, Union +from typing import TYPE_CHECKING, Optional, Union + +if TYPE_CHECKING: + from collections.abc import Iterable from pythainlp.tokenize import ( DEFAULT_SENT_TOKENIZE_ENGINE, diff --git a/pythainlp/tokenize/longest.py b/pythainlp/tokenize/longest.py index 5b4636179..e2453c7de 100644 --- a/pythainlp/tokenize/longest.py +++ b/pythainlp/tokenize/longest.py @@ -14,11 +14,13 @@ import re import threading -from typing import Optional +from typing import TYPE_CHECKING, Optional + +if TYPE_CHECKING: + from pythainlp.util import Trie from pythainlp import thai_tonemarks from pythainlp.tokenize import word_dict_trie -from pythainlp.util import Trie _FRONT_DEP_CHAR: list[str] = [ "ะ", diff --git a/pythainlp/tokenize/multi_cut.py b/pythainlp/tokenize/multi_cut.py index 9a9af5a16..22f559628 100644 --- a/pythainlp/tokenize/multi_cut.py +++ b/pythainlp/tokenize/multi_cut.py @@ -15,11 +15,14 @@ import re from collections import defaultdict -from collections.abc import Iterator -from typing import Optional +from typing import TYPE_CHECKING, Optional + +if TYPE_CHECKING: + from collections.abc import Iterator + + from pythainlp.util import Trie from pythainlp.tokenize import word_dict_trie -from pythainlp.util import Trie class LatticeString(str): diff --git a/pythainlp/tokenize/nercut.py b/pythainlp/tokenize/nercut.py index dcb52766e..9b14d5992 100644 --- a/pythainlp/tokenize/nercut.py +++ b/pythainlp/tokenize/nercut.py @@ -12,7 +12,10 @@ from __future__ import annotations -from collections.abc import Iterable +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from collections.abc import Iterable from pythainlp.tag.named_entity import NER diff --git a/pythainlp/tokenize/newmm.py b/pythainlp/tokenize/newmm.py index a087c5a7f..76d6d3479 100644 --- a/pythainlp/tokenize/newmm.py +++ b/pythainlp/tokenize/newmm.py @@ -18,13 +18,16 @@ import re from collections import defaultdict -from collections.abc import Generator from heapq import heappop, heappush -from typing import Optional +from typing import TYPE_CHECKING, Optional + +if TYPE_CHECKING: + from collections.abc import Generator + + from pythainlp.util import Trie from pythainlp.tokenize import word_dict_trie from pythainlp.tokenize.tcc_p import tcc_pos -from pythainlp.util import Trie # match non-Thai tokens # `|` is used as like "early return", diff --git a/pythainlp/tokenize/pyicu.py b/pythainlp/tokenize/pyicu.py index fd9ca714a..a9e07e157 100644 --- a/pythainlp/tokenize/pyicu.py +++ b/pythainlp/tokenize/pyicu.py @@ -13,7 +13,10 @@ import re import threading -from collections.abc import Iterator +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from collections.abc import Iterator from icu import BreakIterator, Locale diff --git a/pythainlp/tokenize/tcc.py b/pythainlp/tokenize/tcc.py index 4d921f6d4..ae748c343 100644 --- a/pythainlp/tokenize/tcc.py +++ b/pythainlp/tokenize/tcc.py @@ -15,7 +15,10 @@ from __future__ import annotations import re -from collections.abc import Iterator +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from collections.abc import Iterator _RE_TCC: list[str] = ( """\ diff --git a/pythainlp/tokenize/tcc_p.py b/pythainlp/tokenize/tcc_p.py index 2daec5b02..e1e4d8c5a 100644 --- a/pythainlp/tokenize/tcc_p.py +++ b/pythainlp/tokenize/tcc_p.py @@ -16,7 +16,10 @@ from __future__ import annotations import re -from collections.abc import Iterator +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from collections.abc import Iterator _RE_TCC: list[str] = ( """\ diff --git a/pythainlp/tools/path.py b/pythainlp/tools/path.py index 2cf597b1d..9ee9bf85c 100644 --- a/pythainlp/tools/path.py +++ b/pythainlp/tools/path.py @@ -10,8 +10,10 @@ import os import sys -from os import PathLike -from typing import cast +from typing import TYPE_CHECKING, cast + +if TYPE_CHECKING: + from os import PathLike if sys.version_info >= (3, 11): from importlib.resources import files # Available in Python 3.11+ diff --git a/pythainlp/transliterate/lookup.py b/pythainlp/transliterate/lookup.py index b36245442..36178b39a 100644 --- a/pythainlp/transliterate/lookup.py +++ b/pythainlp/transliterate/lookup.py @@ -10,8 +10,10 @@ from __future__ import annotations -from collections.abc import Callable -from typing import Optional +from typing import TYPE_CHECKING, Optional + +if TYPE_CHECKING: + from collections.abc import Callable from pythainlp.corpus.th_en_translit import ( TRANSLITERATE_DICT, diff --git a/pythainlp/ulmfit/core.py b/pythainlp/ulmfit/core.py index 23af19986..ae2e29fad 100644 --- a/pythainlp/ulmfit/core.py +++ b/pythainlp/ulmfit/core.py @@ -6,12 +6,13 @@ from __future__ import annotations import collections -from collections.abc import Callable, Collection from typing import TYPE_CHECKING, Any, Optional, Union import torch if TYPE_CHECKING: + from collections.abc import Callable, Collection + import numpy as np from fastai.basic_data import DataBunch from fastai.basic_train import Learner diff --git a/pythainlp/ulmfit/preprocess.py b/pythainlp/ulmfit/preprocess.py index cbe6cfd87..e5f80686b 100644 --- a/pythainlp/ulmfit/preprocess.py +++ b/pythainlp/ulmfit/preprocess.py @@ -7,8 +7,10 @@ import html import re -from collections.abc import Collection -from typing import Optional +from typing import TYPE_CHECKING, Optional + +if TYPE_CHECKING: + from collections.abc import Collection import emoji diff --git a/pythainlp/ulmfit/tokenizer.py b/pythainlp/ulmfit/tokenizer.py index 6eccdf7a2..90654fd2c 100644 --- a/pythainlp/ulmfit/tokenizer.py +++ b/pythainlp/ulmfit/tokenizer.py @@ -5,7 +5,10 @@ from __future__ import annotations -from collections.abc import Collection +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from collections.abc import Collection from pythainlp.tokenize import thai2fit_tokenizer diff --git a/pythainlp/util/collate.py b/pythainlp/util/collate.py index 1a7cee1f0..3431dd73c 100644 --- a/pythainlp/util/collate.py +++ b/pythainlp/util/collate.py @@ -8,8 +8,10 @@ from __future__ import annotations import re -from collections.abc import Iterable -from typing import Pattern +from typing import TYPE_CHECKING, Pattern + +if TYPE_CHECKING: + from collections.abc import Iterable _RE_TONE: Pattern[str] = re.compile(r"[็-์]") _RE_LV_C: Pattern[str] = re.compile(r"([เ-ไ])([ก-ฮ])") diff --git a/pythainlp/util/remove_trailing_repeat_consonants.py b/pythainlp/util/remove_trailing_repeat_consonants.py index 17b15cd21..dd3eb4d2e 100644 --- a/pythainlp/util/remove_trailing_repeat_consonants.py +++ b/pythainlp/util/remove_trailing_repeat_consonants.py @@ -5,7 +5,10 @@ from __future__ import annotations -from collections.abc import Iterable +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from collections.abc import Iterable from pythainlp import thai_consonants as consonants from pythainlp.corpus import thai_words diff --git a/pythainlp/util/strftime.py b/pythainlp/util/strftime.py index 01ff741fa..5fe1639c3 100644 --- a/pythainlp/util/strftime.py +++ b/pythainlp/util/strftime.py @@ -6,8 +6,11 @@ from __future__ import annotations import warnings -from datetime import datetime from string import digits +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from datetime import datetime from pythainlp import thai_digits from pythainlp.util.date import (