Skip to content

Fix type annotations - #385

Merged
wojcikstefan merged 3 commits into
masterfrom
fix-types
Oct 19, 2026
Merged

wojcikstefan merged 3 commits into
masterfrom
fix-types

Conversation

@wojcikstefan

Copy link
Copy Markdown
Member

I was working in an unrelated codebase to support Ty and I noticed that a few of TaskTiger's type annotations were not correct (in ways not caught by Mypy). See the individual commits and their commit messages for an explanation of each logical change.

`periodic()` and `cron_expr()` return a `(function, args)` tuple, and
the README tells users to give this tuple to `schedule`, for example
`@tiger.task(schedule=periodic(hours=1))`. But `task()` declares
`schedule` as `Optional[Callable]`, so type checkers reject this usual
call.

TaskTiger accepts both forms at runtime. `Task._queue_for_next_period()`
calls a callable directly, and it unpacks a tuple into a function and
its arguments. Declare the same two forms in the type hint. The new
`ScheduleStrategy` alias names the tuple form, as `RetryStrategy` does
for retry methods.
`Task.retry_method` declares that it returns a `RetryStrategy`, which is
a `(function, args)` tuple. But it returns the retry method in the form
that the task stores: the import path of the function as a string, and
the arguments. For example, `("tasktiger.retry:_fixed", (2, 3))`. When
a worker loads the task from Redis, the arguments are a list, because
they come from JSON.

Thus, type checkers reject correct code that gives this string to
`import_attribute()`, as the worker does. Add a `SerializedRetryStrategy`
alias for the stored form. Use it for `Task.retry_method`, and for
`serialize_retry_method()`, which makes this form.
A bare `Tuple` is a tuple of any length with items of any type, but it
does not say so. Write `Tuple[Any, ...]` for the arguments in
`RetryStrategy`, so that the hint is explicit.

The `retry_method` parameters of `Task()`, `TaskTiger.task()`, and
`TaskTiger.delay()` wrote out the same tuple type in full. Use the
`RetryStrategy` alias there. The accepted types do not change.
@wojcikstefan
wojcikstefan requested review from neob91-close, squeaky-pl and thomasst and removed request for neob91-close October 18, 2026 11:14
@squeaky-pl

Copy link
Copy Markdown

Correct me if I am wrong but the main difference I am seeing here that ty and mypy understand "bare", Tuple differently. But yeah in general I think Tuple is used for cases where each element has semantic meaning and using "bare" Tuple is an anti-pattern.

@wojcikstefan

Copy link
Copy Markdown
Member Author

Correct me if I am wrong but the main difference I am seeing here that ty and mypy understand "bare", Tuple differently.

Yes, that's mostly true, though there are other small nuances. For example, SerializedRetryStrategy is Tuple[str, Sequence[Any]], but previously serialize_retry_method was annotated as returning Tuple[str, Tuple]. That second Tuple was plain wrong because it's actually a list that's being returned. Could probably annotate it as such, but Sequence seemed good enough.

But yeah in general I think Tuple is used for cases where each element has semantic meaning and using "bare" Tuple is an anti-pattern.

Yep, 100% agree!

@wojcikstefan
wojcikstefan merged commit 06005a2 into master Oct 19, 2026
73 checks passed
@wojcikstefan wojcikstefan mentioned this pull request Oct 19, 2026
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.

2 participants