diff --git a/.github/workflows/bandit.yml b/.github/workflows/bandit.yml index fc866f89c..ddfdfa838 100644 --- a/.github/workflows/bandit.yml +++ b/.github/workflows/bandit.yml @@ -62,7 +62,7 @@ jobs: - name: Set up Python uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: - python-version: "3.x" + python-version: "3.14" cache: "pip" cache-dependency-path: pyproject.toml diff --git a/.github/workflows/mypy.yml b/.github/workflows/mypy.yml index d59a6f44e..ef09f6e20 100644 --- a/.github/workflows/mypy.yml +++ b/.github/workflows/mypy.yml @@ -59,7 +59,7 @@ jobs: - name: Set up Python uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: - python-version: "3.9" + python-version: "3.10" cache: "pip" cache-dependency-path: pyproject.toml diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml index 3cb2beca1..6464ae505 100644 --- a/.github/workflows/pypi-publish.yml +++ b/.github/workflows/pypi-publish.yml @@ -113,9 +113,7 @@ jobs: - name: Check distributions run: twine check dist/* - # Embeds after the build, not with Pitloom's Hatchling build hook: - # Pitloom needs Python 3.10+, but PyThaiNLP still builds from source on - # Python 3.9, where an unknown build hook fails the build. + # Embeds after the build, not with Pitloom's Hatchling build hook. # Pitloom 0.20.0+ lists only the wheel's payload, so the files the # embedding rewrites (RECORD and the SBOM itself) are not in the SBOM # with stale hashes, as they were with 0.19.0. diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index 330d58f4d..c38e1dfda 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -68,7 +68,7 @@ jobs: # See details of test matrix in: # https://github.com/PyThaiNLP/pythainlp/blob/dev/tests/README.md os: ["ubuntu-latest", "windows-latest", "macos-latest"] - python-version: ["3.14", "3.9"] # Latest and earliest + python-version: ["3.14", "3.10"] # Latest and earliest include: # Test the rest of Python versions only on Ubuntu - os: "ubuntu-latest" @@ -77,17 +77,15 @@ jobs: python-version: "3.12" - os: "ubuntu-latest" python-version: "3.11" - - os: "ubuntu-latest" - python-version: "3.10" runs-on: ${{ matrix.os }} timeout-minutes: 60 env: - PYICU_WIN_VER: 2.15 # 2.15 is the last version that supports Python 3.9 + PYICU_WIN_VER: 2.15 # 2.15 is the last version that supports Python 3.10 INSTALL_FULL_DEPS: false PYTHON_VERSION_LATEST: "3.14" PYTHON_VERSION_LATEST_2: "3.13" # Second-latest supported version - PYTHON_VERSION_EARLIEST: "3.9" + PYTHON_VERSION_EARLIEST: "3.10" steps: - name: Checkout diff --git a/CHANGELOG.md b/CHANGELOG.md index 4579c0cd1..6e382ee03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,8 @@ and this project adheres to ## [Unreleased] +The minimum Python version is now 3.10. + ### Added - `thai_name_to_char` and `thai_char_to_name` in `pythainlp.util` diff --git a/README.md b/README.md index 9e7f1463a..9253b49a4 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ![Project Logo](./docs/images/logo.png) [![pypi](https://img.shields.io/pypi/v/pythainlp.svg)](https://pypi.python.org/pypi/pythainlp) -[![Python 3.9](https://img.shields.io/badge/python-3.9-blue.svg)](https://www.python.org/downloads/) +[![Python 3.10](https://img.shields.io/badge/python-3.10-blue.svg)](https://www.python.org/downloads/) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.3519354.svg)](https://doi.org/10.5281/zenodo.3519354) [![Project Status: Active](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) @@ -32,7 +32,7 @@ pip install pythainlp | Version | Python version | Changes | Documentation | |:-------:|:--------------:|:-------:|:-------------:| | [5.4.0](https://github.com/PyThaiNLP/pythainlp/releases) | 3.9+ | [Log](https://github.com/PyThaiNLP/pythainlp/blob/dev/CHANGELOG.md) | [pythainlp.org/docs](https://pythainlp.org/docs) | -| [`dev`](https://github.com/PyThaiNLP/pythainlp/tree/dev) | 3.9+ | [Log](https://github.com/PyThaiNLP/pythainlp/compare/v5.4.0...HEAD) | [pythainlp.org/dev-docs](https://pythainlp.org/dev-docs/) | +| [`dev`](https://github.com/PyThaiNLP/pythainlp/tree/dev) | 3.10+ | [Log](https://github.com/PyThaiNLP/pythainlp/compare/v5.4.0...HEAD) | [pythainlp.org/dev-docs](https://pythainlp.org/dev-docs/) | ## Features diff --git a/README_TH.md b/README_TH.md index 40009be7e..aebad8267 100644 --- a/README_TH.md +++ b/README_TH.md @@ -3,7 +3,7 @@ ![Project Logo](https://avatars0.githubusercontent.com/u/32934255?s=200&v=4) [![pypi](https://img.shields.io/pypi/v/pythainlp.svg)](https://pypi.python.org/pypi/pythainlp) -[![Python 3.9](https://img.shields.io/badge/python-3.9-blue.svg)](https://www.python.org/downloads/) +[![Python 3.10](https://img.shields.io/badge/python-3.10-blue.svg)](https://www.python.org/downloads/) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.3519354.svg)](https://doi.org/10.5281/zenodo.3519354) [![Project Status: Active](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) @@ -30,7 +30,7 @@ pip install pythainlp | รุ่น | รุ่นของ Python | มีอะไรเปลี่ยน | เอกสาร | | :-: | :-: | :-: | :-: | | [5.4.0](https://github.com/PyThaiNLP/pythainlp/releases) | 3.9+ | [Log](https://github.com/PyThaiNLP/pythainlp/blob/dev/CHANGELOG.md) | [pythainlp.org/docs](https://pythainlp.org/docs) | -| [`dev`](https://github.com/PyThaiNLP/pythainlp/tree/dev) | 3.9+ | [Log](https://github.com/PyThaiNLP/pythainlp/compare/v5.4.0...HEAD) | [pythainlp.org/dev-docs](https://pythainlp.org/dev-docs/) | +| [`dev`](https://github.com/PyThaiNLP/pythainlp/tree/dev) | 3.10+ | [Log](https://github.com/PyThaiNLP/pythainlp/compare/v5.4.0...HEAD) | [pythainlp.org/dev-docs](https://pythainlp.org/dev-docs/) | ## ความสามารถ diff --git a/pyproject.toml b/pyproject.toml index b0e8b479d..4aa5cc38d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,7 @@ name = "pythainlp" version = "6.0.0-dev0" description = "Thai Natural Language Processing library" readme = "README.md" -requires-python = ">=3.9" +requires-python = ">=3.10" license = "Apache-2.0" authors = [ { name = "Wannaphong Phatthiyaphaibun", email = "wannaphong@pythainlp.org" }, @@ -45,7 +45,6 @@ classifiers = [ "Development Status :: 5 - Production/Stable", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -71,28 +70,19 @@ dependencies = [ ## 1) Development ######################################## dev = [ - # Some tools drop Python 3.9 in newer releases. Use the latest version - # for each Python range. bandit 1.8.x reports false positives for B615 - # (revision is a variable). - "bandit>=1.8.6;python_version<'3.10'", - "bandit>=1.9.4;python_version>='3.10'", - "build>=1.4.4;python_version<'3.10'", - "build>=1.6.1;python_version>='3.10'", + # bandit 1.8.x reports false positives for B615 (revision is a variable). + "bandit>=1.9.4", + "build>=1.6.1", "bump-my-version>=1.5.1", - "coverage>=7.10.7;python_version<'3.10'", - "coverage>=7.16.2;python_version>='3.10'", + "coverage>=7.16.2", "diff-cover>=10.6.0", - "flake8>=7.3.0;python_version<'3.10'", - "flake8>=7.4.1;python_version>='3.10'", + "flake8>=7.4.1", "flake8-cognitive-complexity>=0.1.0", - # Same as CI. mypy 2.x requires Python 3.10 or later and rejects - # python_version = "3.9" in [tool.mypy]. + # Same as CI. "mypy==1.19.1", - "pylint>=3.3.9;python_version<'3.10'", - "pylint>=4.1.2;python_version>='3.10'", + "pylint>=4.1.2", "ruff>=0.16.10", - "tox>=4.30.3;python_version<'3.10'", - "tox>=4.64.9;python_version>='3.10'", + "tox>=4.64.9", ] docs = [ @@ -452,7 +442,7 @@ ignore = ["D203", "D212"] [tool.ruff] indent-width = 4 line-length = 79 -target-version = "py39" +target-version = "py310" [tool.ruff.format] docstring-code-format = true @@ -554,7 +544,7 @@ extend-ignore = ["B905", "E501", "RET504", "SIM108", "SIM117"] "docs/**" = ["D1"] [tool.tox] -env_list = ["py39", "py310", "py311", "py312", "py313", "py314", "pypy310", "pypy311", "ruff", "flake8", "mypy"] +env_list = ["py310", "py311", "py312", "py313", "py314", "pypy310", "pypy311", "ruff", "flake8", "mypy"] skip_missing_interpreters = true [tool.tox.env_run_base] @@ -585,8 +575,7 @@ skip_install = true [tool.tox.env.mypy] commands = [["mypy", "pythainlp", "tests"]] -# Same version as mypy.yml. mypy 1.19 is the last release that targets -# Python 3.9; python_version in [tool.mypy] sets the target. +# Same version as mypy.yml. python_version in [tool.mypy] sets the target. deps = ["mypy==1.19.1"] extras = [] @@ -597,7 +586,7 @@ disallow_subclassing_any = true disallow_untyped_decorators = true disallow_untyped_defs = true pretty = true -python_version = "3.9" +python_version = "3.10" show_column_numbers = true show_error_code_links = true show_error_context = true @@ -619,7 +608,7 @@ disallow_untyped_defs = false # Treat optional dependencies as untyped, whether installed or not. # Without "follow_imports = skip", mypy analyzes installed packages -# (for example, torch) with python_version = "3.9" and fails on +# (for example, torch) with python_version = "3.10" and fails on # newer syntax. [[tool.mypy.overrides]] module = [ diff --git a/pythainlp/augment/word2vec/core.py b/pythainlp/augment/word2vec/core.py index 693b797f9..7e088454d 100644 --- a/pythainlp/augment/word2vec/core.py +++ b/pythainlp/augment/word2vec/core.py @@ -7,9 +7,11 @@ import itertools import logging -from typing import TYPE_CHECKING, Callable, Union +from typing import TYPE_CHECKING, Union if TYPE_CHECKING: + from collections.abc import Callable + from gensim.models.keyedvectors import KeyedVectors diff --git a/pythainlp/augment/wordnet.py b/pythainlp/augment/wordnet.py index f8f00490d..b2ffdae0b 100644 --- a/pythainlp/augment/wordnet.py +++ b/pythainlp/augment/wordnet.py @@ -17,7 +17,8 @@ import itertools from collections import OrderedDict -from typing import TYPE_CHECKING, Callable, Optional +from collections.abc import Callable # noqa: TC003 # for get_type_hints() +from typing import TYPE_CHECKING, Optional from nltk.corpus import wordnet as wn diff --git a/pythainlp/tokenize/_registry.py b/pythainlp/tokenize/_registry.py index 9e01c5120..8c0bb1480 100644 --- a/pythainlp/tokenize/_registry.py +++ b/pythainlp/tokenize/_registry.py @@ -19,7 +19,8 @@ from __future__ import annotations import re -from typing import TYPE_CHECKING, Callable, NoReturn, Optional, TypeVar +from collections.abc import Callable +from typing import TYPE_CHECKING, NoReturn, Optional, TypeVar if TYPE_CHECKING: from pythainlp.util.trie import Trie diff --git a/pythainlp/tools/path.py b/pythainlp/tools/path.py index 9a971b4ec..4fc4a7741 100644 --- a/pythainlp/tools/path.py +++ b/pythainlp/tools/path.py @@ -18,7 +18,7 @@ if sys.version_info >= (3, 11): from importlib.resources import files # Available in Python 3.11+ -else: # pragma: no cover - only runs on Python 3.9 and 3.10 +else: # pragma: no cover - only runs on Python 3.10 from importlib_resources import files PYTHAINLP_DEFAULT_DATA_DIR: str = "pythainlp-data" diff --git a/pythainlp/transliterate/core.py b/pythainlp/transliterate/core.py index 42fd225a3..41788a925 100644 --- a/pythainlp/transliterate/core.py +++ b/pythainlp/transliterate/core.py @@ -5,7 +5,10 @@ from __future__ import annotations -from typing import Callable +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from collections.abc import Callable DEFAULT_ROMANIZE_ENGINE: str = "royin" DEFAULT_TRANSLITERATE_ENGINE: str = "thaig2p" diff --git a/pythainlp/util/time.py b/pythainlp/util/time.py index e2fb2b4d3..971ef6b65 100644 --- a/pythainlp/util/time.py +++ b/pythainlp/util/time.py @@ -9,10 +9,11 @@ from __future__ import annotations +from collections.abc import Callable # noqa: TC003 # for get_type_hints() from contextlib import suppress from datetime import datetime, time from functools import lru_cache -from typing import Callable, Optional, Union +from typing import Optional, Union from pythainlp.tokenize import Tokenizer from pythainlp.util.numtoword import num_to_thaiword diff --git a/tests/README.md b/tests/README.md index fd55d1310..ceaf612d0 100644 --- a/tests/README.md +++ b/tests/README.md @@ -36,14 +36,13 @@ inside that test suite's directory. The following table outlines the automated test coverage across supported Python versions and operating systems: -| Python | Ubuntu | Windows | macOS | -| -------------- | ------- | ------- | ----- | -| 3.14 (Latest) | O+C | O | O | -| 3.13 | O+C+X | O+C | O+C | -| 3.12 | O | | | -| 3.11 | O | | | -| 3.10 | O | | | -| 3.9 (Earliest) | O+C | O+C | O+C | +| Python | Ubuntu | Windows | macOS | +| --------------- | ------- | ------- | ----- | +| 3.14 (Latest) | O+C | O | O | +| 3.13 | O+C+X | O+C | O+C | +| 3.12 | O | | | +| 3.11 | O | | | +| 3.10 (Earliest) | O+C | O+C | O+C | The CI/CD test workflow is at . diff --git a/tests/core/test_util_syllable.py b/tests/core/test_util_syllable.py index a3857f19b..453076163 100644 --- a/tests/core/test_util_syllable.py +++ b/tests/core/test_util_syllable.py @@ -14,7 +14,7 @@ import itertools import random import unittest -from typing import TYPE_CHECKING, Callable +from typing import TYPE_CHECKING from pythainlp import thai_consonants, thai_tonemarks, thai_vowels from pythainlp.util.syllable import ( @@ -26,7 +26,7 @@ ) if TYPE_CHECKING: - from collections.abc import Iterator + from collections.abc import Callable, Iterator # (syllable, sound_syllable result, tone_detector result); # "!Name" means that the exception type Name is raised. diff --git a/tests/test_helpers.py b/tests/test_helpers.py index d1a38cafe..63e2956d8 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -9,7 +9,7 @@ """ import unittest -from typing import Callable +from collections.abc import Callable def assert_segment_handles_none_and_empty(