Skip to content

[Serve][1/n] Improve type annotations for DeploymentHandle, DeploymentResponse, and DeploymentResponseGenerator#59363

Merged
abrarsheikh merged 1 commit intomasterfrom
52654-abrar-generics
Dec 15, 2025
Merged

[Serve][1/n] Improve type annotations for DeploymentHandle, DeploymentResponse, and DeploymentResponseGenerator#59363
abrarsheikh merged 1 commit intomasterfrom
52654-abrar-generics

Conversation

@abrarsheikh
Copy link
Copy Markdown
Contributor

@abrarsheikh abrarsheikh commented Dec 10, 2025

Fixes #52654

Summary

This PR improves the generic type annotations on Ray Serve's handle classes to enable better IDE support and type inference. It also adds a type checking test file to verify the annotations work correctly.

Changes

Type Annotation Improvements (handle.py)

  • Generic return types: Updated methods to return the generic type parameter R instead of Any:

    • DeploymentResponse.result()R
    • DeploymentResponse.__await__()Generator[Any, None, R]
    • DeploymentResponseGenerator.__iter__()Iterator[R]
    • DeploymentResponseGenerator.__next__()R
    • DeploymentResponseGenerator.__aiter__()AsyncIterator[R]
    • DeploymentResponseGenerator.__anext__()R
  • mypy compatibility fixes:

    • Added cast() for Future types in sync/async fetch methods to help mypy understand the conditional types
    • Aligned _DeploymentHandleBase.options() signature with DeploymentHandle.options() to fix override error
    • Refactored remote() to return directly from each branch instead of assigning different class types to a variable

Type Checking Tests (check_handle_typing.py)

Added a mypy test file that verifies:

  • Generic type R is preserved through result(), __await__, iteration methods
  • Generic type T is preserved through options() and method access
  • Placeholder tests for future mypy plugin (commented out) that will verify method return type inference

How to Test

    mypy python/ray/serve/tests/typing_files/check_handle_typing.py python/ray/serve/handle.py  \
        --follow-imports=skip \
        --ignore-missing-imports

Future Work

A mypy plugin can be implemented to infer the return type of .remote() based on which deployment method is being called:

handle: DeploymentHandle[MyDeployment]
response = handle.get_user.remote(123)  # Plugin would infer DeploymentResponse[str]
user = response.result()                 # Would be typed as str

The test file includes commented-out tests that should pass once the plugin is implemented.

Tests

From master

❯ mypy python/ray/serve/tests/typing_files/check_handle_typing.py python/ray/serve/handle.py  --follow-imports=skip --ignore-missing-imports
python/ray/serve/handle.py:10: error: Module "ray._raylet" has no attribute "ObjectRefGenerator"  [attr-defined]
python/ray/serve/handle.py:162: error: Item "None" of "Optional[Any]" has no attribute "_run_router_in_separate_loop"  [union-attr]
python/ray/serve/handle.py:210: error: Item "None" of "Optional[Any]" has no attribute "assign_request"  [union-attr]
python/ray/serve/handle.py:229: note: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs  [annotation-unchecked]
python/ray/serve/handle.py:292: error: Unexpected keyword argument "timeout" for "result" of "Future"  [call-arg]
/home/ubuntu/.local/lib/python3.9/site-packages/mypy/typeshed/stdlib/_asyncio.pyi: note: "result" of "Future" defined here
python/ray/serve/handle.py:319: error: Incompatible types in "await" (actual type "Union[concurrent.futures._base.Future[Any], _asyncio.Future[Any]]", expected type "Awaitable[Any]")  [misc]
python/ray/serve/handle.py:803: error: Incompatible types in assignment (expression has type "type[DeploymentResponse]", variable has type "type[DeploymentResponseGenerator]")  [assignment]
python/ray/serve/tests/typing_files/check_handle_typing.py:24: error: "DeploymentResponse" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:24: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:28: error: Expression is of type "Any", not "str"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:37: error: "DeploymentResponse" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:37: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:41: error: Expression is of type "Any", not "str"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:46: error: "DeploymentResponseGenerator" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:46: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:49: error: Expression is of type "Iterator[Any]", not "Iterator[int]"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:53: error: Expression is of type "Any", not "int"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:57: error: Expression is of type "Any", not "int"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:63: error: "DeploymentResponseGenerator" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:63: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:66: error: Expression is of type "AsyncIterator[Any]", not "AsyncIterator[int]"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:70: error: Expression is of type "Any", not "int"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:74: error: Expression is of type "Any", not "int"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:88: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:88: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:97: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:104: error: "DeploymentResponse" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:104: error: "DeploymentResponseGenerator" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:115: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:115: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:119: error: Expression is of type "Any", not "DeploymentHandle"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:119: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:151: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:151: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:177: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:177: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:200: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:200: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:230: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:230: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:262: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:262: note: Error code "type-arg" not covered by "type: ignore" comment
Found 30 errors in 2 files (checked 2 source files)

Because of changes in this PR

❯ mypy python/ray/serve/tests/typing_files/check_handle_typing.py python/ray/serve/handle.py  --follow-imports=skip --ignore-missing-imports
python/ray/serve/handle.py:261: note: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs  [annotation-unchecked]
Success: no issues found in 2 source files

Signed-off-by: abrar <abrar@anyscale.com>
@abrarsheikh abrarsheikh requested a review from a team as a code owner December 10, 2025 22:33
@abrarsheikh abrarsheikh added the go add ONLY when ready to merge, run all tests label Dec 10, 2025
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request significantly improves the type annotations for Ray Serve's handle classes, enabling better IDE support and static analysis. The introduction of generics for DeploymentHandle, DeploymentResponse, and DeploymentResponseGenerator is well-executed, making the return types more precise. The changes to make the code more mypy-compatible, such as adding cast() for Future types and refactoring the remote() method, are thoughtful and effective. The addition of a dedicated type-checking test file (check_handle_typing.py) is an excellent practice that ensures these new annotations are correct and will be maintained. The code quality is high, and the changes are clear and well-documented in the pull request description. I've found one minor issue in the new test file's commented-out code.

yield b"chunk1"
yield b"chunk2"

_: DeploymentHandle[MyDeployment] = None # type: ignore[assignment]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

In the commented-out tests for the mypy plugin, the DeploymentHandle variable is named _, but the following commented-out code attempts to use handle. This will cause a NameError when these tests are uncommented. To fix this, the variable should be named handle. This issue is present in all the placeholder tests for the mypy plugin.

Suggested change
_: DeploymentHandle[MyDeployment] = None # type: ignore[assignment]
handle: DeploymentHandle[MyDeployment] = None # type: ignore[assignment]

@ray-gardener ray-gardener bot added the serve Ray Serve Related Issue label Dec 11, 2025
Copy link
Copy Markdown
Contributor

@marwan116 marwan116 left a comment

Choose a reason for hiding this comment

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

Did a very brief review - it is very nice to see type annotations making their way to Ray Serve deployments. Let's make sure the docs are also updated as part of future work to reflect this capability.

# out because they will fail without the plugin.
#
# To enable after plugin is implemented:
# 1. Uncomment the tests below
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: but why not use pytest annotations like xfail instead of commenting code ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

these are not run as part of pytest. these get run from mypy, which is activated from pre-commit-hook in the repo

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

thanks for the clarification - just found out about https://pypi.org/project/pytest-mypy/ btw - maybe worth considering ?

@@ -0,0 +1,277 @@
"""Type checking tests for DeploymentHandle, DeploymentResponse, and DeploymentResponseGenerator.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

would it make sense to have a test example of composition in here as well ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

will do.

@abrarsheikh
Copy link
Copy Markdown
Contributor Author

Let's make sure the docs are also updated as part of future work to reflect this capability.

Good point, we will update the doc after we ship the full implementation with the mypy plugin.

@abrarsheikh abrarsheikh merged commit d891f23 into master Dec 15, 2025
6 checks passed
@abrarsheikh abrarsheikh deleted the 52654-abrar-generics branch December 15, 2025 17:37
kriyanshii pushed a commit to kriyanshii/ray that referenced this pull request Dec 16, 2025
…tResponse, and DeploymentResponseGenerator (ray-project#59363)

Fixes ray-project#52654

### Summary

This PR improves the generic type annotations on Ray Serve's handle
classes to enable better IDE support and type inference. It also adds a
type checking test file to verify the annotations work correctly.

### Changes

#### Type Annotation Improvements (`handle.py`)

- **Generic return types**: Updated methods to return the generic type
parameter `R` instead of `Any`:
  - `DeploymentResponse.result()` → `R`
  - `DeploymentResponse.__await__()` → `Generator[Any, None, R]`
  - `DeploymentResponseGenerator.__iter__()` → `Iterator[R]`
  - `DeploymentResponseGenerator.__next__()` → `R`
  - `DeploymentResponseGenerator.__aiter__()` → `AsyncIterator[R]`
  - `DeploymentResponseGenerator.__anext__()` → `R`

- **mypy compatibility fixes**:
- Added `cast()` for `Future` types in sync/async fetch methods to help
mypy understand the conditional types
- Aligned `_DeploymentHandleBase.options()` signature with
`DeploymentHandle.options()` to fix override error
- Refactored `remote()` to return directly from each branch instead of
assigning different class types to a variable

#### Type Checking Tests (`check_handle_typing.py`)

Added a mypy test file that verifies:
- Generic type `R` is preserved through `result()`, `__await__`,
iteration methods
- Generic type `T` is preserved through `options()` and method access
- Placeholder tests for future mypy plugin (commented out) that will
verify method return type inference

### How to Test

```bash
    mypy python/ray/serve/tests/typing_files/check_handle_typing.py python/ray/serve/handle.py  \
        --follow-imports=skip \
        --ignore-missing-imports
```

### Future Work

A mypy plugin can be implemented to infer the return type of `.remote()`
based on which deployment method is being called:

```python
handle: DeploymentHandle[MyDeployment]
response = handle.get_user.remote(123)  # Plugin would infer DeploymentResponse[str]
user = response.result()                 # Would be typed as str
```

The test file includes commented-out tests that should pass once the
plugin is implemented.

## Tests

From master
```
❯ mypy python/ray/serve/tests/typing_files/check_handle_typing.py python/ray/serve/handle.py  --follow-imports=skip --ignore-missing-imports
python/ray/serve/handle.py:10: error: Module "ray._raylet" has no attribute "ObjectRefGenerator"  [attr-defined]
python/ray/serve/handle.py:162: error: Item "None" of "Optional[Any]" has no attribute "_run_router_in_separate_loop"  [union-attr]
python/ray/serve/handle.py:210: error: Item "None" of "Optional[Any]" has no attribute "assign_request"  [union-attr]
python/ray/serve/handle.py:229: note: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs  [annotation-unchecked]
python/ray/serve/handle.py:292: error: Unexpected keyword argument "timeout" for "result" of "Future"  [call-arg]
/home/ubuntu/.local/lib/python3.9/site-packages/mypy/typeshed/stdlib/_asyncio.pyi: note: "result" of "Future" defined here
python/ray/serve/handle.py:319: error: Incompatible types in "await" (actual type "Union[concurrent.futures._base.Future[Any], _asyncio.Future[Any]]", expected type "Awaitable[Any]")  [misc]
python/ray/serve/handle.py:803: error: Incompatible types in assignment (expression has type "type[DeploymentResponse]", variable has type "type[DeploymentResponseGenerator]")  [assignment]
python/ray/serve/tests/typing_files/check_handle_typing.py:24: error: "DeploymentResponse" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:24: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:28: error: Expression is of type "Any", not "str"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:37: error: "DeploymentResponse" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:37: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:41: error: Expression is of type "Any", not "str"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:46: error: "DeploymentResponseGenerator" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:46: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:49: error: Expression is of type "Iterator[Any]", not "Iterator[int]"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:53: error: Expression is of type "Any", not "int"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:57: error: Expression is of type "Any", not "int"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:63: error: "DeploymentResponseGenerator" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:63: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:66: error: Expression is of type "AsyncIterator[Any]", not "AsyncIterator[int]"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:70: error: Expression is of type "Any", not "int"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:74: error: Expression is of type "Any", not "int"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:88: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:88: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:97: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:104: error: "DeploymentResponse" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:104: error: "DeploymentResponseGenerator" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:115: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:115: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:119: error: Expression is of type "Any", not "DeploymentHandle"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:119: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:151: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:151: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:177: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:177: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:200: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:200: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:230: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:230: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:262: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:262: note: Error code "type-arg" not covered by "type: ignore" comment
Found 30 errors in 2 files (checked 2 source files)
```

Because of changes in this PR
```
❯ mypy python/ray/serve/tests/typing_files/check_handle_typing.py python/ray/serve/handle.py  --follow-imports=skip --ignore-missing-imports
python/ray/serve/handle.py:261: note: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs  [annotation-unchecked]
Success: no issues found in 2 source files
```

Signed-off-by: abrar <abrar@anyscale.com>
Signed-off-by: kriyanshii <kriyanshishah06@gmail.com>
Yicheng-Lu-llll pushed a commit to Yicheng-Lu-llll/ray that referenced this pull request Dec 22, 2025
…tResponse, and DeploymentResponseGenerator (ray-project#59363)

Fixes ray-project#52654

### Summary

This PR improves the generic type annotations on Ray Serve's handle
classes to enable better IDE support and type inference. It also adds a
type checking test file to verify the annotations work correctly.

### Changes

#### Type Annotation Improvements (`handle.py`)

- **Generic return types**: Updated methods to return the generic type
parameter `R` instead of `Any`:
  - `DeploymentResponse.result()` → `R`
  - `DeploymentResponse.__await__()` → `Generator[Any, None, R]`
  - `DeploymentResponseGenerator.__iter__()` → `Iterator[R]`
  - `DeploymentResponseGenerator.__next__()` → `R`
  - `DeploymentResponseGenerator.__aiter__()` → `AsyncIterator[R]`
  - `DeploymentResponseGenerator.__anext__()` → `R`

- **mypy compatibility fixes**:
- Added `cast()` for `Future` types in sync/async fetch methods to help
mypy understand the conditional types
- Aligned `_DeploymentHandleBase.options()` signature with
`DeploymentHandle.options()` to fix override error
- Refactored `remote()` to return directly from each branch instead of
assigning different class types to a variable

#### Type Checking Tests (`check_handle_typing.py`)

Added a mypy test file that verifies:
- Generic type `R` is preserved through `result()`, `__await__`,
iteration methods
- Generic type `T` is preserved through `options()` and method access
- Placeholder tests for future mypy plugin (commented out) that will
verify method return type inference

### How to Test

```bash
    mypy python/ray/serve/tests/typing_files/check_handle_typing.py python/ray/serve/handle.py  \
        --follow-imports=skip \
        --ignore-missing-imports
```

### Future Work

A mypy plugin can be implemented to infer the return type of `.remote()`
based on which deployment method is being called:

```python
handle: DeploymentHandle[MyDeployment]
response = handle.get_user.remote(123)  # Plugin would infer DeploymentResponse[str]
user = response.result()                 # Would be typed as str
```

The test file includes commented-out tests that should pass once the
plugin is implemented.


## Tests

From master
```
❯ mypy python/ray/serve/tests/typing_files/check_handle_typing.py python/ray/serve/handle.py  --follow-imports=skip --ignore-missing-imports
python/ray/serve/handle.py:10: error: Module "ray._raylet" has no attribute "ObjectRefGenerator"  [attr-defined]
python/ray/serve/handle.py:162: error: Item "None" of "Optional[Any]" has no attribute "_run_router_in_separate_loop"  [union-attr]
python/ray/serve/handle.py:210: error: Item "None" of "Optional[Any]" has no attribute "assign_request"  [union-attr]
python/ray/serve/handle.py:229: note: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs  [annotation-unchecked]
python/ray/serve/handle.py:292: error: Unexpected keyword argument "timeout" for "result" of "Future"  [call-arg]
/home/ubuntu/.local/lib/python3.9/site-packages/mypy/typeshed/stdlib/_asyncio.pyi: note: "result" of "Future" defined here
python/ray/serve/handle.py:319: error: Incompatible types in "await" (actual type "Union[concurrent.futures._base.Future[Any], _asyncio.Future[Any]]", expected type "Awaitable[Any]")  [misc]
python/ray/serve/handle.py:803: error: Incompatible types in assignment (expression has type "type[DeploymentResponse]", variable has type "type[DeploymentResponseGenerator]")  [assignment]
python/ray/serve/tests/typing_files/check_handle_typing.py:24: error: "DeploymentResponse" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:24: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:28: error: Expression is of type "Any", not "str"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:37: error: "DeploymentResponse" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:37: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:41: error: Expression is of type "Any", not "str"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:46: error: "DeploymentResponseGenerator" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:46: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:49: error: Expression is of type "Iterator[Any]", not "Iterator[int]"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:53: error: Expression is of type "Any", not "int"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:57: error: Expression is of type "Any", not "int"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:63: error: "DeploymentResponseGenerator" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:63: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:66: error: Expression is of type "AsyncIterator[Any]", not "AsyncIterator[int]"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:70: error: Expression is of type "Any", not "int"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:74: error: Expression is of type "Any", not "int"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:88: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:88: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:97: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:104: error: "DeploymentResponse" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:104: error: "DeploymentResponseGenerator" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:115: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:115: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:119: error: Expression is of type "Any", not "DeploymentHandle"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:119: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:151: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:151: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:177: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:177: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:200: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:200: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:230: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:230: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:262: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:262: note: Error code "type-arg" not covered by "type: ignore" comment
Found 30 errors in 2 files (checked 2 source files)
```

Because of changes in this PR
```
❯ mypy python/ray/serve/tests/typing_files/check_handle_typing.py python/ray/serve/handle.py  --follow-imports=skip --ignore-missing-imports
python/ray/serve/handle.py:261: note: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs  [annotation-unchecked]
Success: no issues found in 2 source files
```

Signed-off-by: abrar <abrar@anyscale.com>
peterxcli pushed a commit to peterxcli/ray that referenced this pull request Feb 25, 2026
…tResponse, and DeploymentResponseGenerator (ray-project#59363)

Fixes ray-project#52654

### Summary

This PR improves the generic type annotations on Ray Serve's handle
classes to enable better IDE support and type inference. It also adds a
type checking test file to verify the annotations work correctly.

### Changes

#### Type Annotation Improvements (`handle.py`)

- **Generic return types**: Updated methods to return the generic type
parameter `R` instead of `Any`:
  - `DeploymentResponse.result()` → `R`
  - `DeploymentResponse.__await__()` → `Generator[Any, None, R]`
  - `DeploymentResponseGenerator.__iter__()` → `Iterator[R]`
  - `DeploymentResponseGenerator.__next__()` → `R`
  - `DeploymentResponseGenerator.__aiter__()` → `AsyncIterator[R]`
  - `DeploymentResponseGenerator.__anext__()` → `R`

- **mypy compatibility fixes**:
- Added `cast()` for `Future` types in sync/async fetch methods to help
mypy understand the conditional types
- Aligned `_DeploymentHandleBase.options()` signature with
`DeploymentHandle.options()` to fix override error
- Refactored `remote()` to return directly from each branch instead of
assigning different class types to a variable

#### Type Checking Tests (`check_handle_typing.py`)

Added a mypy test file that verifies:
- Generic type `R` is preserved through `result()`, `__await__`,
iteration methods
- Generic type `T` is preserved through `options()` and method access
- Placeholder tests for future mypy plugin (commented out) that will
verify method return type inference

### How to Test

```bash
    mypy python/ray/serve/tests/typing_files/check_handle_typing.py python/ray/serve/handle.py  \
        --follow-imports=skip \
        --ignore-missing-imports
```

### Future Work

A mypy plugin can be implemented to infer the return type of `.remote()`
based on which deployment method is being called:

```python
handle: DeploymentHandle[MyDeployment]
response = handle.get_user.remote(123)  # Plugin would infer DeploymentResponse[str]
user = response.result()                 # Would be typed as str
```

The test file includes commented-out tests that should pass once the
plugin is implemented.

## Tests

From master
```
❯ mypy python/ray/serve/tests/typing_files/check_handle_typing.py python/ray/serve/handle.py  --follow-imports=skip --ignore-missing-imports
python/ray/serve/handle.py:10: error: Module "ray._raylet" has no attribute "ObjectRefGenerator"  [attr-defined]
python/ray/serve/handle.py:162: error: Item "None" of "Optional[Any]" has no attribute "_run_router_in_separate_loop"  [union-attr]
python/ray/serve/handle.py:210: error: Item "None" of "Optional[Any]" has no attribute "assign_request"  [union-attr]
python/ray/serve/handle.py:229: note: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs  [annotation-unchecked]
python/ray/serve/handle.py:292: error: Unexpected keyword argument "timeout" for "result" of "Future"  [call-arg]
/home/ubuntu/.local/lib/python3.9/site-packages/mypy/typeshed/stdlib/_asyncio.pyi: note: "result" of "Future" defined here
python/ray/serve/handle.py:319: error: Incompatible types in "await" (actual type "Union[concurrent.futures._base.Future[Any], _asyncio.Future[Any]]", expected type "Awaitable[Any]")  [misc]
python/ray/serve/handle.py:803: error: Incompatible types in assignment (expression has type "type[DeploymentResponse]", variable has type "type[DeploymentResponseGenerator]")  [assignment]
python/ray/serve/tests/typing_files/check_handle_typing.py:24: error: "DeploymentResponse" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:24: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:28: error: Expression is of type "Any", not "str"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:37: error: "DeploymentResponse" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:37: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:41: error: Expression is of type "Any", not "str"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:46: error: "DeploymentResponseGenerator" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:46: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:49: error: Expression is of type "Iterator[Any]", not "Iterator[int]"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:53: error: Expression is of type "Any", not "int"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:57: error: Expression is of type "Any", not "int"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:63: error: "DeploymentResponseGenerator" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:63: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:66: error: Expression is of type "AsyncIterator[Any]", not "AsyncIterator[int]"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:70: error: Expression is of type "Any", not "int"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:74: error: Expression is of type "Any", not "int"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:88: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:88: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:97: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:104: error: "DeploymentResponse" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:104: error: "DeploymentResponseGenerator" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:115: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:115: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:119: error: Expression is of type "Any", not "DeploymentHandle"  [assert-type]
python/ray/serve/tests/typing_files/check_handle_typing.py:119: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:151: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:151: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:177: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:177: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:200: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:200: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:230: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:230: note: Error code "type-arg" not covered by "type: ignore" comment
python/ray/serve/tests/typing_files/check_handle_typing.py:262: error: "DeploymentHandle" expects no type arguments, but 1 given  [type-arg]
python/ray/serve/tests/typing_files/check_handle_typing.py:262: note: Error code "type-arg" not covered by "type: ignore" comment
Found 30 errors in 2 files (checked 2 source files)
```

Because of changes in this PR
```
❯ mypy python/ray/serve/tests/typing_files/check_handle_typing.py python/ray/serve/handle.py  --follow-imports=skip --ignore-missing-imports
python/ray/serve/handle.py:261: note: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs  [annotation-unchecked]
Success: no issues found in 2 source files
```

Signed-off-by: abrar <abrar@anyscale.com>
Signed-off-by: peterxcli <peterxcli@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

go add ONLY when ready to merge, run all tests serve Ray Serve Related Issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Serve] Support generics for DeploymentHandle type hints

4 participants