Add pyright strict type-check CI workflow#146
Open
berndverst wants to merge 2 commits into
Open
Conversation
- New .github/workflows/typecheck.yml runs pyright in strict mode on Python 3.10 (lowest supported) across durabletask and durabletask-azuremanaged for PRs and pushes to main. - Add pyrightconfig.json at repo root (strict, Python 3.10, excludes generated protobuf/gRPC files). - Add pyright to dev-requirements.txt. - Clean up 1598 strict-mode type errors across the SDK while preserving runtime behavior. Changes are purely additive type annotations, casts, and targeted `# pyright: ignore` comments scoped to specific rules. - Address related typing issues: - #93: OrchestrationContext.create_timer now returns TimerTask (was CancellableTask). - #94: WhenAnyTask is now generic; when_any(tasks: Sequence[Task[T]]) returns WhenAnyTask[T], so the completing child Task[T] is statically typed. - #92: Broad improvements to generic type-safety hints. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
|
||
| class _SyncTaskHubSidecarServiceStub(Protocol): | ||
| def StartInstance(self, request: pb.CreateInstanceRequest) -> pb.CreateInstanceResponse: | ||
| ... |
| ... | ||
|
|
||
| def GetInstance(self, request: pb.GetInstanceRequest) -> pb.GetInstanceResponse: | ||
| ... |
| ... | ||
|
|
||
| def StreamInstanceHistory(self, request: pb.StreamInstanceHistoryRequest) -> Iterable[pb.HistoryChunk]: | ||
| ... |
| ... | ||
|
|
||
| def ListInstanceIds(self, request: pb.ListInstanceIdsRequest) -> pb.ListInstanceIdsResponse: | ||
| ... |
| ... | ||
|
|
||
| def QueryInstances(self, request: pb.QueryInstancesRequest) -> pb.QueryInstancesResponse: | ||
| ... |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR introduces strict static type-checking to the Durable Task Python SDK by adding a dedicated pyright (strict mode) CI workflow and updating the codebase across both durabletask and durabletask-azuremanaged to reach a clean strict-type baseline (also addressing typing issues #92, #93, #94).
Changes:
- Added pyright strict configuration (
pyrightconfig.json) and a new GitHub Actions workflow (.github/workflows/typecheck.yml) that runs on Python 3.10. - Improved public API type precision (notably
OrchestrationContext.create_timer() -> TimerTaskand genericwhen_any()/WhenAnyTasktyping). - Performed broad strict-mode typing cleanup across core runtime, testing backend, gRPC utilities, tracing, and Azure Blob payload store.
Reviewed changes
Copilot reviewed 27 out of 27 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
pyrightconfig.json |
Adds repo-wide pyright strict configuration and exclusions for generated files/venvs. |
.github/workflows/typecheck.yml |
New CI job to run pyright strict on Python 3.10 for both packages. |
dev-requirements.txt |
Adds pyright to dev dependencies. |
CHANGELOG.md |
Documents the new typecheck workflow and public typing improvements (#92/#93/#94). |
durabletask-azuremanaged/CHANGELOG.md |
Notes downstream typing improvements for Azure Managed consumers. |
durabletask/task.py |
Updates task/orchestration typing, including create_timer return type and generic when_any. |
durabletask/worker.py |
Large strict-typing pass across worker runtime and orchestration execution internals. |
durabletask/client.py |
Tightens typing around stubs/continuation tokens and adds typed stub Protocols. |
durabletask/history.py |
Improves typing for event conversion utilities and serialization helpers. |
durabletask/testing/in_memory_backend.py |
Adds strict typing across backend state, filters, and service method signatures. |
durabletask/grpc_options.py |
Typing adjustments for channel/retry options dataclasses. |
durabletask/internal/grpc_interceptor.py |
Reworks interceptor call-details typing and metadata typing for strict mode. |
durabletask/internal/grpc_resiliency.py |
Adds strict typing to sync/async resiliency interceptors. |
durabletask/internal/tracing.py |
Refactors optional OpenTelemetry imports to be strict-type-safe while remaining optional at runtime. |
durabletask/extensions/azure_blob_payloads/blob_payload_store.py |
Adds strict typing and explicit client types for sync/async blob clients. |
durabletask/extensions/azure_blob_payloads/__init__.py |
Adjusts optional import typing to satisfy strict checks. |
durabletask/payload/helpers.py |
Adds strict typing for protobuf descriptor helpers. |
durabletask/internal/shared.py |
Adds typing to JSON encode/decode helpers and encoder/decoder overrides. |
durabletask/internal/helpers.py |
Broadens is_empty typing to accept None. |
durabletask/internal/history_helpers.py |
Adds typing for history conversion helpers and private-usage annotations. |
durabletask/internal/orchestration_entity_context.py |
Adds typing for entity-context helpers and generator return types. |
durabletask/internal/entity_state_shim.py |
Adds typing for entity state shim methods. |
durabletask/internal/client_helpers.py |
Adds typing for continuation tokens and related helpers. |
durabletask/entities/entity_context.py |
Adds missing return typing and clarifies encoded payload types. |
durabletask/entities/entity_lock.py |
Adds targeted pyright ignore for intentional private call. |
durabletask/entities/entity_instance_id.py |
Tightens comparison method typing and corrects non-instance comparisons. |
durabletask-azuremanaged/durabletask/azuremanaged/internal/durabletask_grpc_interceptor.py |
Removes dependency on internal call-details types; uses public grpc call-details interfaces. |
Move Callable, Sequence, Generator, Iterable, Iterator, and AsyncIterable imports from typing to collections.abc per PEP 585. These typing aliases have been deprecated for runtime use since Python 3.9 in favor of the collections.abc originals, and the project floor is Python 3.10. No behavior change. Affected files: - durabletask/client.py - durabletask/task.py - durabletask/worker.py - durabletask/testing/in_memory_backend.py - durabletask/internal/client_helpers.py - durabletask/internal/grpc_interceptor.py - durabletask/internal/grpc_resiliency.py - durabletask/internal/history_helpers.py - durabletask/internal/orchestration_entity_context.py - durabletask/internal/proto_task_hub_sidecar_service_stub.py Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| request: pb.GetInstanceRequest, | ||
| *, | ||
| timeout: float | None = None) -> pb.GetInstanceResponse: | ||
| ... |
| request: pb.GetInstanceRequest, | ||
| *, | ||
| timeout: float | None = None) -> pb.GetInstanceResponse: | ||
| ... |
| ... | ||
|
|
||
| def RaiseEvent(self, request: pb.RaiseEventRequest) -> pb.RaiseEventResponse: | ||
| ... |
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.
Summary
Adds a new CI workflow that runs pyright in strict mode on the lowest
supported Python version (3.10) across both
durabletaskanddurabletask-azuremanagedpackages, and cleans up every strict-mode typeerror so the workflow starts at a green baseline. The cleanup also resolves
three related typing issues — #92, #93, and #94.
Note
All changes are purely additive: type annotations,
cast(...)fromtyping, and rule-specific# pyright: ignore[...]comments. Noruntime behavior is intentionally changed.
What changed
New CI workflow + config
.github/workflows/typecheck.yml— runs on pushes tomainand PRs tomain(matches the existingdurabletask.yml/durabletask-azuremanaged.ymltriggers). Installs both packages with
[azure-blob-payloads,opentelemetry]extras + the azuremanaged provider, then runs
pyright.pyrightconfig.jsonat repo root —typeCheckingMode: strict,pythonVersion: 3.10. Excludes generated*_pb2.py,*_pb2.pyi,*_pb2_grpc.py,*_pb2_grpc.pyi, and local.venv*folders.pyrightadded todev-requirements.txt.Type-error cleanup
Started at 1,598 strict-mode errors and brought the repo to
0 errors, 0 warnings, 0 informations. The biggest categories cleaned:
durabletask/worker.pydurabletask/history.pydurabletask/client.pydurabletask/testing/in_memory_backend.pydurabletask/internal/grpc_interceptor.pydurabletask/task.pydurabletask/extensions/azure_blob_payloads/blob_payload_store.pyRelated typing issues
create_timershould return specificTimerTask#93 —create_timershould return specificTimerTask.OrchestrationContext.create_timer(...)now returnsTimerTask(wasCancellableTask). The concrete_RuntimeOrchestrationContextoverridein
worker.pyand the in-memory backend override intesting/in_memory_backend.pymatch the new signature.
when_anyshould specify type for inputtasks#94 —when_anyshould specify type for inputtasks.WhenAnyTaskis now generic:Calling
when_any([t1, t2])now infersWhenAnyTask[T]and.get_result()returnsTask[T]instead ofTask[Unknown].Add generic type safety hints #92 — Generic type-safety hints (umbrella). Broad fan-out from the
workflow cleanup: registries use
Orchestrator[Any, Any]/Activity[Any, Any]/Entity[Any, Any]instead of bare aliases,CompositeTask._tasksislist[Task[Any]],_parent: CompositeTask[Any] | None,RetryableTaskis generic,wait_for_external_event(name) -> CancellableTask[Any],etc.
Notable design notes
durabletask/internal/tracing.py) —reworked the lazy-import block to use a
TYPE_CHECKINGdeclaration as thesource of truth, paired with a runtime
try/except ImportErrorthatrebinds the names. The
exceptbranch injects fallback stubs viaglobals()to avoid type-incompatible reassignment errors.ReplaySafeLogger—logging.LoggerAdapteris generic in stubs butnot subscriptable at runtime before Python 3.11. Used the standard
TYPE_CHECKINGalias trick to satisfy both pyright and runtime.internal members to non-underscore names, otherwise use targeted
# pyright: ignore[reportPrivateUsage]. For thedurabletask-azuremanagedinterceptor, eliminated dependence on theprivate
_ClientCallDetailsnamedtuples and updated overrides to usethe public
grpc.ClientCallDetails/grpc.aio.ClientCallDetails(also fixes a Liskov-substitution violation).
Verification
pyright— 0 errors, 0 warnings, 0 informations (validated withthe exact install commands the new workflow runs).
flake8— clean acrossdurabletask/,durabletask-azuremanaged/,examples/,tests/durabletask/,tests/durabletask-azuremanaged/.pymarkdownlnt— clean for bothCHANGELOG.mdfiles.pytest tests/durabletask/ -m "not dts").Note
Some
*_e2e.pytests fail locally on Windows withFailed to bind to address [::]:50060. This reproduces onmainwithout these changes(verified via a temporary checkout), so it is pre-existing and
unrelated to this PR.
Changelogs
CHANGELOG.md— new workflow + type-coverage improvements with explicitcallouts to Add generic type safety hints #92,
create_timershould return specificTimerTask#93,when_anyshould specify type for inputtasks#94.durabletask-azuremanaged/CHANGELOG.md— derived-benefit note (theTimerTaskand genericwhen_anychanges flow through toDurableTaskSchedulerWorkerorchestrations).Stats
27 files changed, 827 insertions(+), 459 deletions(-)Closes #92
Closes #93
Closes #94