Make RedisException the canonical error contract for all public Redis operations - #338
Open
willronchetti wants to merge 2 commits into
Open
Make RedisException the canonical error contract for all public Redis operations#338willronchetti wants to merge 2 commits into
willronchetti wants to merge 2 commits into
Conversation
…ions Redis driver exceptions leaked out of dcicutils' public Redis API: every RedisBase method, create_redis_client, and every RedisSessionToken operation except store_session_token propagated redis.exceptions.* directly, forcing consumers to import redis-driver exception types to handle an unreachable or failing Redis. Add a translate_redis_exceptions decorator in redis_utils and apply it to create_redis_client, all public RedisBase methods, and all session token operations (from_redis, store/validate/update/delete_session_token). Driver failures - redis.exceptions.RedisError and redis.exceptions.RedisClusterException and their subclasses - now raise RedisException with the original attached as __cause__. Programmer errors (TypeError, AttributeError, ...) are deliberately not translated, which narrows store_session_token's previous bare `except Exception`. Successful behavior is unchanged, and absence stays distinct from failure: validate_session_token still returns False for a missing token and from_redis still returns None for a missing record. Add test/test_redis_error_contract.py, which injects representative connection, timeout, response, busy-loading and cluster failures into every affected public operation. The tests are mock-based and need no running redis-server. Bump to 8.20.0 for the added public symbols.
…aths The __all__ added to redis_tools narrowed the module's star-import surface for no benefit: RedisException is already referenced in store_session_token, so the import stands on its own. Also add success-path assertions for the RedisBase methods that had error coverage only, including that set_expiration still passes gt=True through to the driver.
Coverage Report for CI Build 31202017610Coverage increased (+0.1%) to 74.762%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
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.
Problem
Redis driver exceptions leak out of dcicutils' public Redis API. Every
RedisBasemethod,create_redis_client, and everyRedisSessionTokenoperation exceptstore_session_tokenpropagated
redis.exceptions.*straight to the caller, so a consumer that wanted to survive anunreachable or failing Redis had to import redis-driver exception types itself.
Verified against the pre-change code with an injected
redis.exceptions.ConnectionError:Change
dcicutils.redis_utilsgains a publictranslate_redis_exceptionsdecorator (and theREDIS_DRIVER_EXCEPTIONStuple it uses). It is applied to:create_redis_clientRedisBasemethod:info,set,get,set_expiration,ttl,delete,hget,hgetall,hset,hset_multiple,dbsizeRedisSessionTokenoperation:from_redis,store_session_token,validate_session_token,update_session_token,delete_session_tokenDriver failures —
redis.exceptions.RedisErrorandredis.exceptions.RedisClusterException(which deliberately sits outside the
RedisErrortree) and their subclasses — now raiseRedisException, with the original attached as__cause__for callers that want to inspect it.Programmer errors (
TypeError,AttributeError,ValueError,KeyError) are deliberatelynot translated, so real defects are not masked. Nesting decorated calls is idempotent: an
already-canonical
RedisExceptionpasses through unwrapped.Snovault should consume this public error contract rather than importing Redis-driver
exceptions.
RedisExceptionis importable from bothdcicutils.redis_utilsanddcicutils.redis_tools; catching it is sufficient for every operation listed above, andSnovault's configurable Redis session-mode work should not need
redis.exceptionsat all.Behavior preserved
validate_session_tokenstill returnsFalsefor amissing token and raises only when Redis actually fails;
from_redisstill returnsNonefor a missing record.
RedisException()still constructs with no arguments.functools.wrapskeeps names and docstrings intact for the static lane.Behavior change (intentional, called out in CHANGELOG)
store_session_tokenpreviously caught anyExceptionand re-raised a bareRedisException. It now translates only driver failures, so programmer errors propagateunchanged instead of being masked. The error log line it emitted is preserved.
Tests
New
test/test_redis_error_contract.py(164 tests) injects representative connection, timeout,response, busy-loading and cluster failures into every affected public operation and asserts
RedisException. It also covers cause chaining, non-translation of programmer errors,unchanged success paths and lifecycle, the absence-vs-failure distinction, and a guard test
that fails if a new
RedisBasepublic method is added without coverage. The tests aremock-based and need no running
redis-server.flake8 is clean on
dcicutils/redis_utils.py,dcicutils/redis_tools.py, and the new test file.The existing
redisdb-backed suites were not run locally:redis_execinpyproject.tomlpointsat
/usr/local/bin/redis-server, which is not the homebrew prefix on arm64. That is apre-existing environment gap and untouched here; CI runs those.
Versioning
Bumped to
8.20.0(added public symbols) with a matchingCHANGELOG.rstentry, pertest_misc.py::test_changelog_consistency.