Skip to content

Commit 7c72fc2

Browse files
committed
Support PyPy 3.7 officially and auto-test it in CI
PyPy might give [2-5x speedup](https://speed.pypy.org/) and [2-3x less memory consumption](https://dev.nextthought.com/blog/2018/08/cpython-vs-pypy-memory-usage.html) on pure-Python scripts — compared to equivalent CPython 3.7. This enables Kubernetes operators in CPU-/RAM-tight environments without special improvements and optimizations from Kopf's side. PyPy was unofficially supported and tested manually from time to time. With this change, let's make it official and automated. Some implementation nuances: MyPy is excluded from test-time dependencies: one of MyPy's sub-dependency ("typed-ast") fails at being installed (python/typed_ast#111). However, PyPy is not used for linting/type-checking, only CPython is. Therefore, MyPy is not needed for PyPy. There are some slowdowns in CI: +2m30s mins for OS-/pip-dependencies — since no wheels are usually available for PyPy. And slower tests: 2m40s vs. 1m50s in CPython — my assumption is because JIT is not very useful for non-repetitive code fragments of tests (but should be faster at runtime; no proofs, though). Coverage for PyPy is disabled completely — it makes it disastrously slow (13m vs. 3m). The internet hints it is because tracing is not compatible with JIT (old articles from 2013-2015). Signed-off-by: Sergey Vasilyev <[email protected]>
1 parent a3d9888 commit 7c72fc2

File tree

7 files changed

+72
-2
lines changed

7 files changed

+72
-2
lines changed

.github/workflows/ci.yaml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,33 @@ jobs:
6969
env_vars: PYTHON
7070
continue-on-error: true
7171

72+
# Only the core functionality is tested: no e2e or functional tests (for simplicity).
73+
# No coverage: PyPy performs extremely poorly with tracing/coverage (13 mins vs. 3 mins).
74+
# Extra time: 2-3 mins for building the dependencies (since no binary wheels are available).
75+
# Extra time: PyPy is good with JIT for repetitive code; tests are too unique for JIT.
76+
pypy-tests:
77+
strategy:
78+
fail-fast: false
79+
matrix:
80+
install-extras: [ "", "full-auth" ]
81+
python-version: [ "pypy-3.7" ]
82+
name: Python ${{ matrix.python-version }} ${{ matrix.install-extras }}
83+
runs-on: ubuntu-20.04
84+
timeout-minutes: 10
85+
steps:
86+
- uses: actions/checkout@v2
87+
- uses: actions/setup-python@v2
88+
with:
89+
python-version: ${{ matrix.python-version }}
90+
91+
- run: sudo apt-get update && sudo apt-get install libxml2-dev libxslt-dev
92+
- run: pip install wheel
93+
94+
- run: pip install -r requirements.txt
95+
- run: pip install -e .[${{ matrix.install-extras }}]
96+
if: ${{ matrix.install-extras }}
97+
- run: pytest --color=yes --no-cov
98+
7299
functional:
73100
strategy:
74101
fail-fast: false

.github/workflows/thorough.yaml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,33 @@ jobs:
7171
flags: unit
7272
env_vars: PYTHON
7373

74+
# Only the core functionality is tested: no e2e or functional tests (for simplicity).
75+
# No coverage: PyPy performs extremely poorly with tracing/coverage (13 mins vs. 3 mins).
76+
# Extra time: 2-3 mins for building the dependencies (since no binary wheels are available).
77+
# Extra time: PyPy is good with JIT for repetitive code; tests are too unique for JIT.
78+
pypy-tests:
79+
strategy:
80+
fail-fast: false
81+
matrix:
82+
install-extras: [ "", "full-auth" ]
83+
python-version: [ "pypy-3.7" ]
84+
name: Python ${{ matrix.python-version }} ${{ matrix.install-extras }}
85+
runs-on: ubuntu-20.04
86+
timeout-minutes: 10
87+
steps:
88+
- uses: actions/checkout@v2
89+
- uses: actions/setup-python@v2
90+
with:
91+
python-version: ${{ matrix.python-version }}
92+
93+
- run: sudo apt-get update && sudo apt-get install libxml2-dev libxslt-dev
94+
- run: pip install wheel
95+
96+
- run: pip install -r requirements.txt
97+
- run: pip install -e .[${{ matrix.install-extras }}]
98+
if: ${{ matrix.install-extras }}
99+
- run: pytest --color=yes --no-cov
100+
74101
functional:
75102
strategy:
76103
fail-fast: false

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ That easy! For more features, see the [documentation](https://kopf.readthedocs.i
124124

125125
## Usage
126126

127+
Python 3.7+ is required:
128+
[CPython](https://www.python.org/) and [PyPy](https://www.pypy.org/)
129+
are officially supported and tested; other Python implementations can work too.
130+
127131
We assume that when the operator is executed in the cluster, it must be packaged
128132
into a docker image with a CI/CD tool of your preference.
129133

docs/install.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ Installation
44

55
.. highlight:: bash
66

7+
Prerequisites:
8+
9+
* Python >= 3.7 (CPython and PyPy are officially tested and supported).
10+
711
To install Kopf::
812

913
pip install kopf

kopf/_kits/webhooks.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,11 @@ async def _serve_fn(request: aiohttp.web.Request) -> aiohttp.web.Response:
160160
runner = aiohttp.web.AppRunner(app, handle_signals=False)
161161
await runner.setup()
162162
try:
163+
# Note: reuse_port is mostly (but not only) for fast-running tests with SSL sockets;
164+
# multi-threaded sockets are not really used -- high load is not expected for webhooks.
163165
addr = self.addr or None # None is aiohttp's "any interface"
164166
port = self.port or self._allocate_free_port()
165-
site = aiohttp.web.TCPSite(runner, addr, port, ssl_context=context)
167+
site = aiohttp.web.TCPSite(runner, addr, port, ssl_context=context, reuse_port=True)
166168
await site.start()
167169

168170
# Log with the actual URL: normalised, with hostname/port set.

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ freezegun
1414
import-linter
1515
isort>=5.5.0
1616
lxml
17-
mypy==0.910
17+
# Mypy requires typed-ast, which is broken on PyPy 3.7 (could work in PyPy 3.8).
18+
mypy==0.910; implementation_name == "cpython"
1819
pre-commit
1920
pyngrok
2021
pytest>=6.0.0

tests/reactor/test_queueing.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"""
1717
import asyncio
1818
import contextlib
19+
import gc
1920
import weakref
2021

2122
import async_timeout
@@ -224,6 +225,10 @@ async def test_garbage_collection_of_streams(settings, stream, events, unique, w
224225
# The jobs can take a tiny moment more, but this is noticeable in the tests.
225226
await asyncio.sleep(0.1)
226227

228+
# For PyPy: force the gc! (GC can be delayed in PyPy, unlike in CPython.)
229+
# https://doc.pypy.org/en/latest/cpython_differences.html#differences-related-to-garbage-collection-strategies
230+
gc.collect()
231+
227232
# Truly garbage-collected? Memory freed?
228233
assert all([ref() is None for ref in refs])
229234

0 commit comments

Comments
 (0)