Skip to content

Commit 52352da

Browse files
authored
Merge pull request #33 from techouse/feat/pypy
✨ add PyPy support
2 parents 8b5a36d + 44572d8 commit 52352da

File tree

8 files changed

+74
-4
lines changed

8 files changed

+74
-4
lines changed

.github/workflows/test.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,14 @@ jobs:
5656
py: "3.13"
5757
- toxenv: "python3.14"
5858
py: "3.14"
59+
- toxenv: "pypy3.8"
60+
py: "pypy-3.8"
61+
- toxenv: "pypy3.9"
62+
py: "pypy-3.9"
63+
- toxenv: "pypy3.10"
64+
py: "pypy-3.10"
65+
- toxenv: "pypy3.11"
66+
py: "pypy-3.11"
5967
steps:
6068
- uses: actions/checkout@v5
6169
- name: Set up Python ${{ matrix.py }}
@@ -128,4 +136,4 @@ jobs:
128136
else
129137
echo "The outputs are different."
130138
exit 1
131-
fi
139+
fi

README.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ Highlights
2828
- Safety limits: configurable nesting depth, parameter limit, and list index limit; optional strict-depth errors; duplicate-key strategies (combine/first/last).
2929
- Extras: numeric entity decoding (e.g. ``☺`` → ☺), alternate delimiters/regex, and query-prefix helpers.
3030

31+
Compatibility
32+
-------------
33+
34+
- CPython 3.8–3.14 (default tox envs).
35+
- PyPy 3.8–3.11 (run ``tox -e pypy3.8`` through ``tox -e pypy3.11`` locally; CI mirrors this matrix).
36+
3137
Usage
3238
-----
3339

docs/index.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ Highlights
3232
- Safety limits: configurable nesting depth, parameter limit, and list index limit; optional strict-depth errors; duplicate-key strategies (combine/first/last).
3333
- Extras: numeric entity decoding (e.g. ``☺`` → ☺), alternate delimiters/regex, and query-prefix helpers.
3434

35+
Compatibility
36+
-------------
37+
38+
- CPython 3.8–3.14 (default tox envs).
39+
- PyPy 3.8–3.11 (run ``tox -e pypy3.8`` through ``tox -e pypy3.11`` locally; CI mirrors this matrix).
40+
3541
Usage
3642
-----
3743

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ classifiers = [
3434
"Programming Language :: Python :: 3.14",
3535
"Programming Language :: Python :: 3 :: Only",
3636
"Programming Language :: Python :: Implementation :: CPython",
37+
"Programming Language :: Python :: Implementation :: PyPy",
38+
"Topic :: Internet :: WWW/HTTP",
3739
"Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries",
40+
"Topic :: Software Development :: Libraries",
3841
"Topic :: Software Development :: Libraries :: Python Modules",
3942
"Topic :: Text Processing :: General",
4043
"Topic :: Utilities",

tests/unit/decode_test.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,15 @@ def test_continues_parsing_when_no_parent_is_found(
653653

654654
def test_does_not_error_when_parsing_a_very_long_list(self) -> None:
655655
buf: str = "a[]=a"
656-
while getsizeof(buf) < 128 * 1024:
656+
657+
def _approx_size(value: str) -> int:
658+
try:
659+
return getsizeof(value)
660+
except TypeError:
661+
# PyPy does not implement getsizeof; fall back to len, which matches byte size for ASCII payloads.
662+
return len(value)
663+
664+
while _approx_size(buf) < 128 * 1024:
657665
buf += "&"
658666
buf += buf
659667

tests/unit/example_test.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,16 @@ def custom_decoder(value: t.Any, charset: t.Optional[qs_codec.Charset]):
296296
else datetime.datetime.utcfromtimestamp(7)
297297
)
298298
},
299-
qs_codec.EncodeOptions(encode=False, serialize_date=lambda date: str(int(date.timestamp()))),
299+
qs_codec.EncodeOptions(
300+
encode=False,
301+
serialize_date=lambda date: str(
302+
int(
303+
(
304+
date if date.tzinfo is not None else date.replace(tzinfo=datetime.timezone.utc)
305+
).timestamp()
306+
)
307+
),
308+
),
300309
)
301310
== "a=7"
302311
)
@@ -334,7 +343,15 @@ def custom_decoder(value: t.Any, charset: t.Optional[qs_codec.Charset]):
334343
encode=False,
335344
filter=lambda prefix, value: {
336345
"b": None,
337-
"e[f]": int(value.timestamp()) if isinstance(value, datetime.datetime) else value,
346+
"e[f]": (
347+
int(
348+
(
349+
value if value.tzinfo is not None else value.replace(tzinfo=datetime.timezone.utc)
350+
).timestamp()
351+
)
352+
if isinstance(value, datetime.datetime)
353+
else value
354+
),
338355
"e[g][0]": value * 2 if isinstance(value, int) else value,
339356
}.get(prefix, value),
340357
),

tests/unit/weakref_test.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ def test_weak_key_dict_with_dict_keys(self) -> None:
1818
assert d.get(foo) == 123
1919
assert d.get(foo_copy) == 123
2020
del foo
21+
gc.collect()
2122
assert len(d) == 0
2223
assert d.get(foo_copy) is None
2324

@@ -31,6 +32,7 @@ def test_weak_key_dict_with_nested_dict_keys(self) -> None:
3132
assert d.get(foo) == 123
3233
assert d.get(foo_copy) == 123
3334
del foo
35+
gc.collect()
3436
assert len(d) == 0
3537
assert d.get(foo_copy) is None
3638

tox.ini

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ envlist =
88
python3.12,
99
python3.13,
1010
python3.14,
11+
pypy3.8,
12+
pypy3.9,
13+
pypy3.10,
14+
pypy3.11,
1115
black,
1216
flake8,
1317
linters,
@@ -22,6 +26,10 @@ python =
2226
3.12: python3.12
2327
3.13: python3.13
2428
3.14: python3.14
29+
pypy-3.8: pypy3.8
30+
pypy-3.9: pypy3.9
31+
pypy-3.10: pypy3.10
32+
pypy-3.11: pypy3.11
2533

2634
[testenv]
2735
deps =
@@ -66,6 +74,18 @@ deps =
6674
commands =
6775
pylint --rcfile=tox.ini src/qs_codec
6876

77+
[testenv:pypy3.8]
78+
basepython = pypy3.8
79+
80+
[testenv:pypy3.9]
81+
basepython = pypy3.9
82+
83+
[testenv:pypy3.10]
84+
basepython = pypy3.10
85+
86+
[testenv:pypy3.11]
87+
basepython = pypy3.11
88+
6989
[testenv:bandit]
7090
basepython = python3
7191
skip_install = true

0 commit comments

Comments
 (0)