test: run pytest in strict mode and fail on unexpected warnings - #292
Merged
Merged
Conversation
allen0099
force-pushed
the
test/strict-pytest-config-189
branch
from
September 27, 2026 11:16
b8aef41 to
65e9844
Compare
- asyncio_mode = "auto" and drop the 383 per-test @pytest.mark.asyncio - testpaths, --strict-markers and xfail_strict = true - filterwarnings = ["error"]: expected warnings are asserted with pytest.warns, live Memcached resets go through flush_memcached, and the HS384/HS512 round-trip uses a key long enough for both - sync tests that called asyncio.run() are async tests now; under pytest-asyncio 0.26 they left an unclosed event loop behind - one global ignore: starlette 1.0.0's deprecated anyio alias, hit only by the lowest-dependencies env
With filterwarnings = ["error"], a client a test dropped without closing failed whichever later test the garbage collector happened to run in: ResourceWarnings for unclosed asyncio transports (Redis) and sockets (Memcached pools), surfaced as PytestUnraisableExceptionWarning. An autouse fixture records each AsyncRedisCacheBackend and MemcachedBackend as it is built (via __new__, so constructor warnings keep pointing at the test) and closes its client at teardown on the test's loop. The one sync test that drives Redis through TestClient closes it on the portal loop that owns the connections.
The cleanup fixture imported pymemcache and redis at the top of tests/conftest.py, so the whole suite failed to collect without those extras. Match the client's type by module name instead.
allen0099
force-pushed
the
test/strict-pytest-config-189
branch
from
September 27, 2026 11:21
65e9844 to
baa881d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #189
Summary
Tightens
[tool.pytest.ini_options]:asyncio_mode = "auto": removes all 383 per-test@pytest.mark.asyncio.asyncio_default_fixture_loop_scope = "function"stays.testpaths = ["tests"],--strict-markersandxfail_strict = true.filterwarnings = ["error"]: a warning now fails the test that raised it.Every warning the suite used to emit is fixed in the test that emits it. The master CI run with live servers reported 57 warnings.
clear()flush warning intest_memcached_clear,clear_path(include_params=True),clear_pattern,get_all_keys()andget_cache_data(). The tests now assert them withpytest.warns(..., match=...).flush_memcached()intests/live_servers.py. It silences only theMemcached.clear() flushes ALLwarning, and only around that call.clear_pattern("/nonexistent/*"). The test now asserts the path-shapedRuntimeWarning.UserWarningnor PyJWT'sInsecureKeyLengthWarningfires. The dedicated short-key test still covers the warning.asyncio.run(). There were four intest_cache_invalidate.py, one intest_cache.pyand two insession/test_hardening.py, and they are nowasynctests. Under pytest-asyncio 0.26 (thelowestenv), they left an unclosed event loop behind, and itsResourceWarningfailed a later test.docs/DEVELOPMENT.mdgets a short "Strict pytest settings" section, and CLAUDE.md's Test Setup gets a note.Rebased onto master after #291, which added
tests/test_examples.py. That file needed no changes: it runs green under these settings.Live clients are closed inside their test
With
"error", the live suite failed because Redis and Memcached clients were dropped without being closed. The garbage collector found them during some later test, and theirResourceWarningfailed that test throughPytestUnraisableExceptionWarning:test_redis.py,test_cache_manager.py[RedisBackend]andstate/test_manager.py.test_memcached.pyand fromtest_cache_status_headers.py.The backends have no close method, so the tests now close the clients themselves:
close_network_clientsintests/conftest.py, records everyAsyncRedisCacheBackendandMemcachedBackenda test builds. It records through__new__, so constructor warnings still point at the test. At teardown it closes each client on the test's own loop:await client.aclose()for Redis,client.close()for Memcached.test_cached_route_with_ttl_zero_is_serveddrives Redis throughTestClient, so it closes the client viaclient.portal, the loop that owns those connections.No ignores were added for these.
Global ignores
Only one:
ignore:The anyio.abc.BlockingPortal alias is deprecated:DeprecationWarning. starlette 1.0.0, our declared floor that thelowesttox env installs, referencesanyio.abc.BlockingPortalwhenstarlette.testclientis imported, and current anyio deprecates that alias. Later starlette releases don't use it, and neither this package nor its tests do. The warning fires at import time, so no test-level fix is possible.Verification
CACHEX_TEST_REDIS_PORT/CACHEX_TEST_MEMCACHED_PORTset,CACHEX_REQUIRE_LIVE_SERVERS=1), each with 0 warnings:uv run pytestanduv run pytest -p no:randomly: 1048 passed each.tox -e lowest(pytest 8.3.5, pytest-asyncio 0.26.0, starlette 1.0.0, redis 5.3.0): 1048 passed.tox -e py310andtox -e py314: 1048 passed each.uv run pytest: 857 passed, 191 skipped.tox -e lowest: 857 passed, 191 skipped.warnings.warn("x")inescape_key_componentmade 99 tests fail.gc.collect()after every test and found none, on either the latest or the lowest environment, live included.CHANGELOG
None: test-only.
Update
tests/conftest.pyno longer importspymemcacheorredis: the cleanup fixture recognises a live client by its type's module, so the suite still collects without those extras. Mutation-checked: skipping the Redis close gives 4 failed / 139 errors, skipping the Memcached close 4 failed / 3 errors.CACHEX_REQUIRE_LIVE_SERVERS=1):uv run pytestandtox -e lowestboth 1048 passed.