Skip to content

docs(workflow): clarify RetryPolicy docstrings and add max_attempts alias#1068

Open
vkbajpaii wants to merge 1 commit into
dapr:mainfrom
vkbajpaii:clarify-retry-policy-836
Open

docs(workflow): clarify RetryPolicy docstrings and add max_attempts alias#1068
vkbajpaii wants to merge 1 commit into
dapr:mainfrom
vkbajpaii:clarify-retry-policy-836

Conversation

@vkbajpaii
Copy link
Copy Markdown

Description

Addresses #836.

The current dapr.ext.workflow.RetryPolicy has several user-facing gotchas (called out in the issue) that are not visible from its docstrings or field names. This PR is the docs + naming-clarity slice of that issue; behavior changes are left for follow-up PRs.

What this PR does

  1. Rewrites the RetryPolicy docstrings in ext/dapr-ext-workflow/dapr/ext/workflow/retry_policy.py to spell out:

    • max_number_of_attempts counts the total number of attempts, not retries — 5 means up to 4 retries after the first attempt.
    • retry_timeout failures behave like any other task failure: the surrounding workflow fails unless the call is wrapped in try / except.
    • max_retry_interval only caps the per-attempt delay; retries still happen when it is left as None. (The historical "won't retry unless max_retry_interval is set" bug was fixed in the now-vendored _durabletask engine — verified in _durabletask/task.py RetryableTask.compute_next_delay, which contains no early bail-out for unset max_retry_interval.)
    • Adds a class-level usage example.
  2. Adds a new max_attempts keyword-only argument and property as the preferred name. The legacy max_number_of_attempts argument and property remain supported for backward compatibility. Specifying both or neither raises ValueError to avoid silent inconsistencies.

  3. Adds ext/dapr-ext-workflow/tests/test_retry_policy.py with 11 tests covering construction, validation, the new alias, and the both/neither rejection paths.

  4. Updates the four workflow examples that construct a RetryPolicy (examples/workflow/simple.py, simple_aio_client.py, multi-app1.py, multi-app2.py) to use the new max_attempts name.

Acceptance criteria coverage (from #836)

  • all field docstrings updated to be clearer on what they do
  • max_attempts added as the new preferred field; max_number_of_attempts kept as a deprecated alias (docs-only deprecation for now — no runtime DeprecationWarning to keep this PR backward-compatible and small)
  • retry_timeout docs updated to indicate the surrounding workflow fails when it fires
  • retries apply when max_retry_interval is unset — already true in the vendored _durabletask engine; the docs now state this explicitly
  • sensible defaults for fields without one — intentionally out of scope for this PR (behavior change, backward-compat risk; happy to do as a follow-up PR)

Testing

uv run python -m unittest discover -v ./ext/dapr-ext-workflow/tests
# Ran 152 tests in 0.566s — OK

uv run ruff check && uv run ruff format --check
# All checks passed; 332 files already formatted

uv run mypy
# Success: no issues found in 112 source files

Backward compatibility

Existing call sites using max_number_of_attempts=... continue to work unchanged. The max_number_of_attempts property is preserved and returns the same value as the new max_attempts property.

Closes #836 partially (sensible-defaults follow-up tracked in description above).

…lias

Addresses dapr#836.

* Rewrite the RetryPolicy class-level and per-field docstrings to spell
  out the behavior that has historically confused users:
  - max_number_of_attempts counts *total* attempts, not retries
  - retry_timeout failures propagate to the workflow unless wrapped in
    try/except
  - max_retry_interval only caps the per-attempt delay; retries still
    occur when it is left as None (the historical 'no retries unless
    max_retry_interval is set' bug was fixed in the vendored
    _durabletask engine)
* Add a new keyword-only max_attempts argument and matching property as
  the preferred name. The legacy max_number_of_attempts argument and
  property remain supported for backward compatibility. Specifying both
  or neither raises ValueError.
* Add ext/dapr-ext-workflow/tests/test_retry_policy.py covering
  construction, validation, the new alias, and the both/neither cases.
* Update the workflow examples (simple.py, simple_aio_client.py,
  multi-app1.py, multi-app2.py) to use max_attempts.

Out of scope for this PR (better as separate, focused changes):
* Introducing sensible defaults for fields that are currently required
  (backward-compatibility risk).
* Emitting a DeprecationWarning at runtime for max_number_of_attempts
  (kept as docs-only deprecation for now).

Signed-off-by: vkbajpaii <129022330+vkbajpaii@users.noreply.github.com>

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: vkbajpaii <129022330+vkbajpaii@users.noreply.github.com>
@vkbajpaii vkbajpaii requested review from a team as code owners May 31, 2026 22:47
@sicoyle sicoyle requested review from Copilot June 1, 2026 15:26
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves the workflow extension’s RetryPolicy developer experience by clarifying docstrings around retry semantics and introducing max_attempts as a clearer alias for max_number_of_attempts, while keeping backward compatibility.

Changes:

  • Reworked RetryPolicy docstrings (including a usage example) to clarify attempt vs retry semantics, retry_timeout behavior, and max_retry_interval behavior.
  • Added max_attempts keyword-only alias + validation to require exactly one of max_attempts / max_number_of_attempts.
  • Added unit tests for construction, validation, and alias behavior; updated workflow examples to use max_attempts.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
ext/dapr-ext-workflow/dapr/ext/workflow/retry_policy.py Adds max_attempts alias, validates “exactly one provided”, and rewrites/expands docstrings.
ext/dapr-ext-workflow/tests/test_retry_policy.py Adds focused unit tests for the new alias and validation paths.
examples/workflow/simple.py Updates example usage to prefer max_attempts.
examples/workflow/simple_aio_client.py Updates example usage to prefer max_attempts.
examples/workflow/multi-app1.py Updates example usage to prefer max_attempts.
examples/workflow/multi-app2.py Updates example usage to prefer max_attempts.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 113 to 116
raise ValueError('first_retry_interval must be >= 0')
if max_number_of_attempts < 1:
raise ValueError('max_number_of_attempts must be >= 1')
if attempts_resolved < 1:
raise ValueError('max_attempts must be >= 1')
if backoff_coefficient is not None and backoff_coefficient < 1:
Comment on lines +81 to +83
backoff_coefficient: Exponential backoff multiplier applied to
successive retry intervals. Must be ``>= 1``. Defaults to
``1.0`` (constant delay between retries).
Comment on lines +84 to +88
max_retry_interval: Upper bound on the delay between any two
consecutive attempts. When ``None`` (the default) the
delay grows unbounded according to the backoff. Retries
still occur when this is ``None``; it only caps the
per-attempt delay.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

style: clarify workflow activity retry fields/defaults

2 participants