From 3dda7a1e6eea6927e9daf1a7d71f25e910fc6ec3 Mon Sep 17 00:00:00 2001 From: Klemen Tusar Date: Sun, 12 Oct 2025 13:35:15 +0100 Subject: [PATCH 1/2] :sparkles: add Python 3.7 compatibility --- .github/workflows/test.yml | 2 ++ CONTRIBUTING.md | 2 +- pyproject.toml | 15 ++++++++------- src/qs_codec/encode.py | 4 +++- src/qs_codec/utils/decode_utils.py | 6 ++++-- tests/unit/decode_test.py | 5 +++-- tox.ini | 2 ++ 7 files changed, 23 insertions(+), 13 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b6619d1..933a7f3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -42,6 +42,8 @@ jobs: strategy: matrix: include: + - toxenv: "python3.7" + py: "3.7" - toxenv: "python3.8" py: "3.8" - toxenv: "python3.9" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 05bb28f..0ecff98 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,7 +11,7 @@ related to the project. ## Python version support -Currently, the package supports Python versions 3.8 and above. Once a new Python version is released, we will aim to +Currently, the package supports Python versions 3.7 and above. Once a new Python version is released, we will aim to support it as soon as possible. If you encounter any issues with a new Python version, please create an issue in the repository. diff --git a/pyproject.toml b/pyproject.toml index 79fda8b..624960a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ description = "A query string encoding and decoding library for Python. Ported f readme = { file = "README.rst", content-type = "text/x-rst" } license = "BSD-3-Clause" license-files = ["LICENSE"] -requires-python = ">=3.8" +requires-python = ">=3.7" authors = [ { name = "Klemen Tusar", email = "techouse@gmail.com" }, ] @@ -25,6 +25,7 @@ classifiers = [ "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", @@ -51,10 +52,10 @@ PayPal = "https://paypal.me/ktusar" [project.optional-dependencies] dev = [ - "pytest>=8.1.2", - "pytest-cov>=5.0.0", - "mypy>=1.10.0", - "toml>=0.10.2", + "pytest>=7.4.4", + "pytest-cov", + "mypy>=1.8.0", + "toml", "tox", "black", "isort" @@ -80,7 +81,7 @@ include = ["src/qs_codec/py.typed"] [tool.black] line-length = 120 -target-version = ["py38", "py39", "py310", "py311", "py312", "py313", "py314"] +target-version = ["py37", "py38", "py39", "py310", "py311", "py312", "py313", "py314"] include = '\.pyi?$' exclude = ''' ( @@ -118,7 +119,7 @@ markers = [] [tool.mypy] mypy_path = "src" -python_version = "3.8" +python_version = "3.7" exclude = [ "tests", "docs", diff --git a/src/qs_codec/encode.py b/src/qs_codec/encode.py index 711018b..1804c1c 100644 --- a/src/qs_codec/encode.py +++ b/src/qs_codec/encode.py @@ -235,7 +235,8 @@ def _encode( # Walk up the chain looking for `obj_wrapper`. If we see it at the same "step" # again we've closed a loop. - while (tmp_sc := tmp_sc.get(_sentinel)) and not find_flag: # type: ignore [union-attr] + tmp_sc = tmp_sc.get(_sentinel) # type: ignore [union-attr] + while tmp_sc and not find_flag: # Where `value` last appeared in the ref tree pos: t.Optional[int] = tmp_sc.get(obj_wrapper) step += 1 @@ -246,6 +247,7 @@ def _encode( find_flag = True # Break while if tmp_sc.get(_sentinel) is None: step = 0 + tmp_sc = tmp_sc.get(_sentinel) # type: ignore [union-attr] # --- Pre-processing: filter & datetime handling --------------------------------------- if callable(filter): diff --git a/src/qs_codec/utils/decode_utils.py b/src/qs_codec/utils/decode_utils.py index 83cd673..054e2f2 100644 --- a/src/qs_codec/utils/decode_utils.py +++ b/src/qs_codec/utils/decode_utils.py @@ -151,9 +151,11 @@ def unescape(cls, string: str) -> str: return string def replacer(match: t.Match[str]) -> str: - if (unicode_val := match.group("unicode")) is not None: + unicode_val = match.group("unicode") + if unicode_val is not None: return chr(int(unicode_val, 16)) - elif (hex_val := match.group("hex")) is not None: + hex_val = match.group("hex") + if hex_val is not None: return chr(int(hex_val, 16)) return match.group(0) diff --git a/tests/unit/decode_test.py b/tests/unit/decode_test.py index cbf370f..eba3558 100644 --- a/tests/unit/decode_test.py +++ b/tests/unit/decode_test.py @@ -974,10 +974,11 @@ def _decode(s: t.Optional[str], charset: t.Optional[Charset]) -> t.Any: reg: re.Pattern = re.compile(r"%([0-9A-F]{2})", re.IGNORECASE) result: t.List[int] = [] - parts: t.Optional[re.Match] - while (parts := reg.search(s)) is not None: + parts: t.Optional[re.Match] = reg.search(s) + while parts is not None: result.append(int(parts.group(1), 16)) s = s[parts.end() :] + parts = reg.search(s) return bytes(result).decode("shift-jis") assert decode("%8c%a7=%91%e5%8d%e3%95%7b", DecodeOptions(decoder=_decode)) == {"県": "大阪府"} diff --git a/tox.ini b/tox.ini index d16ab94..5c1263e 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,7 @@ [tox] isolated_build = true envlist = + python3.7, python3.8, python3.9, python3.10, @@ -15,6 +16,7 @@ skip_missing_interpreters = true [gh-actions] python = + 3.7: python3.7 3.8: python3.8 3.9: python3.9 3.10: python3.10 From ab2e7b7e469fa37b588e59e96e6291c1acfa97cb Mon Sep 17 00:00:00 2001 From: Klemen Tusar Date: Sun, 12 Oct 2025 13:48:20 +0100 Subject: [PATCH 2/2] :green_heart: update CI configuration for Python 3.7 testing support --- .github/workflows/test.yml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 933a7f3..9ffb4a4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,6 +9,12 @@ on: - main workflow_call: +# NOTE: Python 3.7 Testing Support +# Python 3.7 reached end-of-life in June 2023 and is no longer available +# in GitHub's ubuntu-latest runners. We use ubuntu-20.04 specifically for +# Python 3.7 testing to maintain backward compatibility. Consider removing +# Python 3.7 support in future major releases. + defaults: run: shell: bash @@ -38,26 +44,35 @@ jobs: test: name: "Test" needs: analyze - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} strategy: matrix: include: + # Python 3.7 requires ubuntu-20.04 as it's no longer available on ubuntu-latest - toxenv: "python3.7" py: "3.7" + os: "ubuntu-20.04" - toxenv: "python3.8" py: "3.8" + os: "ubuntu-latest" - toxenv: "python3.9" py: "3.9" + os: "ubuntu-latest" - toxenv: "python3.10" py: "3.10" + os: "ubuntu-latest" - toxenv: "python3.11" py: "3.11" + os: "ubuntu-latest" - toxenv: "python3.12" py: "3.12" + os: "ubuntu-latest" - toxenv: "python3.13" py: "3.13" + os: "ubuntu-latest" - toxenv: "python3.14" py: "3.14" + os: "ubuntu-latest" steps: - uses: actions/checkout@v5 - name: Set up Python ${{ matrix.py }}