샘플
tenacity 9.1.4: Distinguish retry_unless_exception_type from retry_if_not_exception_type when configuring Tenacity retry conditions for successful returns and exceptions
검증된 샘플 — pypi tenacity 9.1.4: Distinguish retry_unless_exception_type from retry_if_not_exception_type when configuring Tenacity retry conditions for…
sha256:c37ea4818294c080a3b21c3b473dd01a1cd43ab6e6d2406e47862296bb3d5231
이 네트워크가 제공하는 것은 하나입니다. 빌드되는 샘플. 샌드박스에서 돌리고 서명된 영수증을 보관합니다. 등급을 매기지 않고 무엇도 보증하지 않습니다 — 같은 코드가 당신 환경에서 빌드되는지는 측정한 적이 없습니다.
통과한 계약 영수증을 낸 서로 다른 서명 키의 수입니다. 하나면 작성자 혼자이고, 둘 이상이면 다른 사람도 빌드했다는 뜻입니다. 키는 스스로 만드는 것이고 뒤에 등록된 신원이 없으므로, 세는 것은 사람이 아니라 키입니다.
MIT-0
실행 증거
선언된 환경과 서명된 실행을 분리해 두었습니다. 이 샘플이 무엇을 어디서 실행했는지 그대로 볼 수 있습니다.
- 증거 기준
- 서명된 컨트랙트 통과
- 검증 영수증
- 2
- 빌드한 서명 키
- 2
선언된 환경
python linux x64 python python pip
검증 실행 환경
| 환경 | 컨트랙트 | 단계 | 실행일 |
|---|---|---|---|
| python 3.12 · linux alpine/x64 · docker ed25519:d91480838ac982c9 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · python@1 |
2026-08-17 |
| python 3.12 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 | PASS | compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS CONTAINER_RUN · python@1 |
2026-08-18 |
케이스
HOW- 목표
- Distinguish retry_unless_exception_type from retry_if_not_exception_type when configuring Tenacity retry conditions for successful returns and exceptions
- 패키지
- 심벌
-
- tenacity.retry
- tenacity.retry_unless_exception_type
- tenacity.retry_if_not_exception_type
- tenacity.stop_after_attempt
- tenacity.wait_none
- tenacity.RetryError
- 환경
- python
- 생성일
- 2026-08-17T11:21:09Z
컨트랙트
- retry_unless_exception_type treats successful return values as retryable states, retrying until stop exhaustion and raising RetryError with a successful last_attempt Future.
- retry_if_not_exception_type accepts successful return values immediately on the first attempt without triggering retries.
- retry_unless_exception_type stops retrying and raises immediately on the first attempt when the target exception type is raised.
- retry_unless_exception_type retries untargeted exceptions until stop exhaustion and wraps the final failure in RetryError.
파일
- NOTES.md
- csx.json
- requirements.txt
- test/contract.py
소스
# Tenacity Retry Condition Configuration
A search of public findings identified sample sha256:7a46f903288b789d79dfa5936fd063508511f5a098495b06e8df22068651871e, which demonstrates bounded retry exhaustion with result predicates and exception filters.
This sample isolates the configuration trap between `retry_unless_exception_type` and `retry_if_not_exception_type` under pinned release `tenacity==9.1.4`.
## Observed Behaviour
In Tenacity, `retry_unless_exception_type(T)` implements "retry until an exception of type T occurs". As a result:
1. When a function executes successfully and returns a value (`outcome.failed is False`), `retry_unless_exception_type` unconditionally returns `True`. With bounded stops like `stop_after_attempt(3)`, successful execution is repeatedly re-invoked until attempts are exhausted, failing loudly with `RetryError` where `last_attempt.failed is False` and `last_attempt.result()` contains the returned value.
2. When an untargeted exception occurs (e.g. `KeyError` when configured for `ValueError`), `retry_unless_exception_type` retries until attempt exhaustion and raises `RetryError` wrapping the exception.
3. When the targeted exception occurs (`ValueError`), `retry_unless_exception_type` returns `False`, causing Tenacity to immediately bubble up the targeted exception on attempt 1.
Developers intending to retry on unexpected failures while allowing successful returns to exit immediately must configure `retry_if_not_exception_type(T)`. `retry_if_not_exception_type` only checks failed outcomes, allowing clean returns to complete on the initial attempt.
{"case":{"believed":"retry_unless_exception_type retries functions that raise unexpected exceptions while allowing successful returns to exit immediately without retrying.","caseId":"case:sha256:70d6b9a2fa75b17a1e15bd619a313acdd01b8de60631be7238383d677d1d0e5d","contract":["retry_unless_exception_type treats successful return values as retryable states, retrying until stop exhaustion and raising RetryError with a successful last_attempt Future.","retry_if_not_exception_type accepts successful return values immediately on the first attempt without triggering retries.","retry_unless_exception_type stops retrying and raises immediately on the first attempt when the target exception type is raised.","retry_unless_exception_type retries untargeted exceptions until stop exhaustion and wraps the final failure in RetryError."],"goal":"Distinguish retry_unless_exception_type from retry_if_not_exception_type when configuring Tenacity retry conditions for successful returns and exceptions","kind":"HOW","packages":["pkg:pypi/tenacity@9.1.4"],"schemaVersion":1,"symbols":["tenacity.retry","tenacity.retry_unless_exception_type","tenacity.retry_if_not_exception_type","tenacity.stop_after_attempt","tenacity.wait_none","tenacity.RetryError"]},"contractCommand":["python","test/contract.py"],"environment":{"arch":"x64","ecosystem":"pypi","executionContext":"python","language":"python","os":"linux","packageManager":"pip","runtime":"python","schemaVersion":1},"license":"MIT-0","packages":["pkg:pypi/tenacity@9.1.4"],"schemaVersion":1,"symbols":["tenacity.retry","tenacity.retry_unless_exception_type","tenacity.retry_if_not_exception_type","tenacity.stop_after_attempt","tenacity.wait_none","tenacity.RetryError"],"verifierAdapter":"python@1"}
tenacity==9.1.4
from tenacity import (
RetryError,
retry,
retry_if_not_exception_type,
retry_unless_exception_type,
stop_after_attempt,
wait_none,
)
# 1. retry_unless_exception_type treats successful return values as retryable states
unless_success_calls = 0
@retry(
retry=retry_unless_exception_type(ValueError),
stop=stop_after_attempt(3),
wait=wait_none(),
)
def successful_under_retry_unless():
global unless_success_calls
unless_success_calls += 1
return "completed_value"
try:
successful_under_retry_unless()
except RetryError as exc:
assert unless_success_calls == 3
assert exc.last_attempt.failed is False
assert exc.last_attempt.result() == "completed_value"
else:
raise AssertionError(
"retry_unless_exception_type must retry successful returns until stop exhaustion"
)
# 2. retry_if_not_exception_type accepts successful returns immediately
if_not_success_calls = 0
@retry(
retry=retry_if_not_exception_type(ValueError),
stop=stop_after_attempt(3),
wait=wait_none(),
)
def successful_under_retry_if_not():
global if_not_success_calls
if_not_success_calls += 1
return "completed_value"
result = successful_under_retry_if_not()
assert result == "completed_value"
assert if_not_success_calls == 1
# 3. retry_unless_exception_type stops retrying immediately on target exception
target_exc_calls = 0
@retry(
retry=retry_unless_exception_type(ValueError),
stop=stop_after_attempt(3),
wait=wait_none(),
)
def target_exception_under_retry_unless():
global target_exc_calls
target_exc_calls += 1
raise ValueError("terminal stop exception")
try:
target_exception_under_retry_unless()
except ValueError as exc:
assert str(exc) == "terminal stop exception"
assert target_exc_calls == 1
else:
raise AssertionError(
"retry_unless_exception_type must raise the target exception on attempt 1"
)
# 4. retry_unless_exception_type retries untargeted exceptions until stop exhaustion
untargeted_exc_calls = 0
@retry(
retry=retry_unless_exception_type(ValueError),
stop=stop_after_attempt(3),
wait=wait_none(),
)
def untargeted_exception_under_retry_unless():
global untargeted_exc_calls
untargeted_exc_calls += 1
raise KeyError("transient retry exception")
try:
untargeted_exception_under_retry_unless()
except RetryError as exc:
assert untargeted_exc_calls == 3
assert exc.last_attempt.failed is True
assert isinstance(exc.last_attempt.exception(), KeyError)
else:
raise AssertionError(
"retry_unless_exception_type must retry untargeted exceptions until stop exhaustion"
)