Repository navigation
Description
Description
I'm new to Ray, but while reading the docs and source code I noticed that the typing story for actor methods could be improved.
The official doc recommends decorating actor methods with @ray.method so that static checkers give better auto-completion / type checking for .remote() calls. This works for the simplest cases, yet breaks as soon as the method has default arguments.
Minimal reproducer
import ray
@ray.method
def f2(self, b: int = 1) -> bool:
return False
fn = f2 # pyright / mypy infer the type herePyright infers fn as _RemoteMethodNoArgs, i.e. a remote method that accepts no arguments (see actor.py#L93).
Consequently fn.remote(5) is flagged by the type checker although it is perfectly valid at runtime.
Root cause
The current stubs use overloads on plain Callable types:
@overload
def method(__method: Callable[[Any, T0], Ret]) -> bool: ...
@overload
def method(__method: Callable[[Any], Ret]) -> float: ...A function with a default argument (self, b: int = 1) matches both overloads; which one is chosen depends only on declaration order, so the no-argument version wins and the information about the optional parameter is lost.
Possible improvement for modern Python (≥3.10)
We can keep the same runtime behaviour while giving exact static types:
from typing import TypeVar, Generic, Concatenate, ParamSpec, Callable
P = ParamSpec("P")
T = TypeVar("T")
C = TypeVar("C") # the actor class
class RemoteMethod(Generic[P, T]):
def remote(self, *args: P.args, **kwargs: P.kwargs) -> T: ...
def bind(self, *args: P.args, **kwargs: P.kwargs) -> Any: ...
def ray_method(f: Callable[Concatenate[C, P], T]) -> RemoteMethod[P, T]:
import ray
return ray.method(f) # runtime delegateThis preserves the signature of the original method (including defaults) inside the RemoteMethod instance, so type checkers know exactly which arguments .remote() accepts.
Suggestion
Would the team accept a PR that adds ParamSpec-based overloads for @ray.method when Python version is satisfied?, while keeping the current overloads for older versions?
Use case
No response
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsIn progress
Activity
[Enhance] Add `ray_method` decorator to decorate remote method
[Enhance] Add `ray_method` decorator to decorate remote method
edoakes commentedon Dec 12, 2025
Contributions in this area are definitely welcome! Note that we just stopped releasing Python 3.9 wheels, so Python >= 3.10 changes can be done safely by default.
I'll defer to @SolitaryThinker on the details of your proposed change
[Typing] Preserve function signatures through Ray decorators (#60479)
[Typing] Preserve function signatures through Ray decorators (ray-pro…
hg1112 commentedon Jul 30, 2026
Thanks for the clear repro and root-cause analysis — I dug into this before starting a PR and wanted to share findings, since they change the shape of the fix a bit.
Confirmed the bug end-to-end, both statically and at runtime:
fn.remote(5)for a method with a default arg, matching the_RemoteMethodNoArgsinference described above.@ray.remoteactor with the equivalent method executes the call correctly at runtime — confirmed false positive, not a behavioral bug.ray/actor.py:method()'s stub is ~20 hand-written@overloads, one pair (sync +Awaitable) per fixed argument count, smallest arity declared first. A method with a default arg is structurally callable with fewer args, so it matches multiple buckets and the checker silently picks the smaller one, discarding the optional parameter.Also validated the proposed
ParamSpecdirection — and it holds up. I built an isolated, minimal repro comparing three approaches side by side on the same test functions, checked with both mypy and pyright:Section 2 reproduces the exact failure mode from this issue in isolation; sections 1 and 3 both type-check clean on mypy and pyright, including with
selfstripped viaConcatenatethe way an actual actor method needs. So theParamSpec-based direction looks solid — happy to draft a PR replacing the fixed-arity overload buckets with this approach (gated to Python ≥3.10 per @edoakes's note above, keeping the current overloads for older versions).One implementation detail worth flagging early: binding
selfaway needs to happen with a real runtime wrapper (a closure/functools.partial, matching howRemoteMethod-style objects would need to be constructed per actor instance) — annotating it away withConcatenatealone isn't enough, the checker correctly catches that mismatch if you try.Does this direction sound right before I put together the PR, or is there a preference for a different structure?
cc @edoakes @SolitaryThinker
dyami-andrews-e3 commentedon Sep 9, 2026
Hello All! Just bumping this as I would love to see this included in a future ray release.