Skip to content

Conversation

@WouldYouKindly
Copy link
Contributor

This PR adds flags to generate only sync or only async stubs.

@WouldYouKindly WouldYouKindly changed the title Add flags to generate only sync or only async stubs DRAFT Add flags to generate only sync or only async stubs Nov 25, 2025
@aidandj
Copy link
Collaborator

aidandj commented Nov 25, 2025

What is the need for this with the last round of changes that generates both?

@WouldYouKindly
Copy link
Contributor Author

WouldYouKindly commented Nov 25, 2025

What is the need for this with the last round of changes that generates both?

Two things:

  1. On our codebase, running mypy with generated stubs results in OOM - mypy 1.18.2 consumes 20GB+ of RAM. Probably because of too many generics.
  2. It's nice to be able to copy the types from the generated stubs to the implementations. Sync+Async stubs are quite unwieldy, and I assume most people wouldn't want to see them in their code.

@aidandj
Copy link
Collaborator

aidandj commented Nov 25, 2025

Sounds good, I'll review later today.

Can you update the readme documenting the option, and fix the tests.

@aidandj
Copy link
Collaborator

aidandj commented Nov 25, 2025

@nipunn1313 I know we just talked about expanding options too much, but I've definitely wanted to be able to copy the stubs straight across, and this allows a bit more configuration.

Yesterday I was using ts-proto which has about a billion options, and with documentation it was fine.

I think as long as this has full tests for each option, I'm not opposed to it.

@nipunn1313
Copy link
Owner

This one seems like a reasonable one to do. What is the command line flags you are envisioning and what are the defaults that you envision?

I think onus is high here to have sane defaults (maybe async-only?) - and then have a flag to flip to the other options.

Re: memory usage - I think it may be (separately) worth for you to consider profiling to figure out how to resolve that issue to make the generator more efficient. This feels like it will only be a bandaid for an OOM issue.

@aidandj
Copy link
Collaborator

aidandj commented Nov 25, 2025

I think the default should probably be both still. I think it's a better general default for normal first-time users, and also won't be a breaking change.

2 similar comments
@aidandj
Copy link
Collaborator

aidandj commented Nov 25, 2025

I think the default should probably be both still. I think it's a better general default for normal first-time users, and also won't be a breaking change.

@aidandj
Copy link
Collaborator

aidandj commented Nov 25, 2025

I think the default should probably be both still. I think it's a better general default for normal first-time users, and also won't be a breaking change.

@WouldYouKindly
Copy link
Contributor Author

This one seems like a reasonable one to do. What is the command line flags you are envisioning and what are the defaults that you envision?

I think onus is high here to have sane defaults (maybe async-only?) - and then have a flag to flip to the other options.

Re: memory usage - I think it may be (separately) worth for you to consider profiling to figure out how to resolve that issue to make the generator more efficient. This feels like it will only be a bandaid for an OOM issue.

Currently the flags are sync_only/async_only. I agree with Aidan that default should be both - I have no idea if more people need sync or async stubs.

w.r.t. memory usage, I don't think it will be very easy to profile mypy's memory usage, especially given that it has mypyc-compiled parts... I know that the issue is with stubs - as soon as I remove a stub from .pyi file, I stop getting OOM.

@aidandj
Copy link
Collaborator

aidandj commented Nov 26, 2025

This one seems like a reasonable one to do. What is the command line flags you are envisioning and what are the defaults that you envision?

I think onus is high here to have sane defaults (maybe async-only?) - and then have a flag to flip to the other options.

Re: memory usage - I think it may be (separately) worth for you to consider profiling to figure out how to resolve that issue to make the generator more efficient. This feels like it will only be a bandaid for an OOM issue.

Currently the flags are sync_only/async_only. I agree with Aidan that default should be both - I have no idea if more people need sync or async stubs.

w.r.t. memory usage, I don't think it will be very easy to profile mypy's memory usage, especially given that it has mypyc-compiled parts... I know that the issue is with stubs - as soon as I remove a stub from .pyi file, I stop getting OOM.

I've had really good luck with memray and profiling. I think this would be good data to have, Even if we go ahead with the sync/async only stub generation. I know I personally don't have a code base large enough to reproduce this. Maybe you could give us an estimate on the size of your code base?

1 similar comment
@aidandj
Copy link
Collaborator

aidandj commented Nov 26, 2025

This one seems like a reasonable one to do. What is the command line flags you are envisioning and what are the defaults that you envision?

I think onus is high here to have sane defaults (maybe async-only?) - and then have a flag to flip to the other options.

Re: memory usage - I think it may be (separately) worth for you to consider profiling to figure out how to resolve that issue to make the generator more efficient. This feels like it will only be a bandaid for an OOM issue.

Currently the flags are sync_only/async_only. I agree with Aidan that default should be both - I have no idea if more people need sync or async stubs.

w.r.t. memory usage, I don't think it will be very easy to profile mypy's memory usage, especially given that it has mypyc-compiled parts... I know that the issue is with stubs - as soon as I remove a stub from .pyi file, I stop getting OOM.

I've had really good luck with memray and profiling. I think this would be good data to have, Even if we go ahead with the sync/async only stub generation. I know I personally don't have a code base large enough to reproduce this. Maybe you could give us an estimate on the size of your code base?

@WouldYouKindly WouldYouKindly force-pushed the sync-async-only branch 3 times, most recently from 45ba768 to 52e18dd Compare November 26, 2025 17:21
@WouldYouKindly
Copy link
Contributor Author

@aidandj it's really not that big. It's also public: https://github.com/Couchers-org/couchers. Here are the protos: https://github.com/Couchers-org/couchers/tree/develop/app/proto

@WouldYouKindly
Copy link
Contributor Author

@aidandj I will try to make a repro a bit later, and submit an issue at least. In the meantime, the tests are fixed and the docs are updated - ready to be reviewed

@aidandj
Copy link
Collaborator

aidandj commented Nov 26, 2025

Hoping to get to it today, otherwise after our holiday weekend

Copy link
Collaborator

@aidandj aidandj left a comment

Choose a reason for hiding this comment

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

Good start. Missing a few things.

  • Typed constructors.
  • Tests are incomplete
    • Please add full tests for the stub usage, at least equivalent to the tests in the existing grpc_usage and async_grpc_usage
  • Incorrect folder names (I realize I started this pattern with the concrete folder, fixed in the below PR)

When I was testing, I made some changes, here is a PR into your current branch: https://github.com/WouldYouKindly/mypy-protobuf/pull/1

@aidandj
Copy link
Collaborator

aidandj commented Dec 5, 2025

@WouldYouKindly is this still something you'd like to finish working on?

@WouldYouKindly
Copy link
Contributor Author

WouldYouKindly commented Dec 9, 2025

@WouldYouKindly is this still something you'd like to finish working on?

Yes, absolutely. I will be a bit freer next week to finish this hopefully.

I think this is useful to have even if the OOM is fixed - I'd imagine most people want to have either sync or async, not both at the same time. Not having overloads also allows one to copy the types from the generated classes into their implementation.

@aidandj
Copy link
Collaborator

aidandj commented Dec 9, 2025

I agree. Let's get that other change fixed first and rebase on it. I'd like to get that one rolled out to unblock people who upgraded.

1 similar comment
@aidandj
Copy link
Collaborator

aidandj commented Dec 9, 2025

I agree. Let's get that other change fixed first and rebase on it. I'd like to get that one rolled out to unblock people who upgraded.

WouldYouKindly and others added 2 commits December 14, 2025 22:30
- Fix F402 linting error where 'enum' loop variable was shadowing
  the enum module import. Renamed loop variable to 'enum_proto'.
- Stage deletion of test/generated_sync_only and test/generated_async_only
  directories - these will be regenerated during test runs.
- Update generated_concrete files with latest output.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Remove executionEnvironments entries for test/generated_sync_only and
  test/generated_async_only since these directories are being removed
- Exclude test/async_only and test/sync_only directories from Pyright
  checking since they depend on generated files that are no longer
  committed to git

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Copy link
Collaborator

@aidandj aidandj left a comment

Choose a reason for hiding this comment

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

Looking good. I think the generated stubs should be checked in so tests can run in CI.

WouldYouKindly and others added 8 commits December 14, 2025 22:40
Add mkdir -p commands to create test/generated_sync_only and
test/generated_async_only directories before protoc tries to write
files into them. These directories are no longer committed to git
and need to be created during test runs.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The sync_only and async_only mypy checks were missing the
--report-deprecated-as-note flag, causing deprecation warnings to be
treated as errors. This flag is already used for the concrete module
checks and should also be used for these tests.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
When generating with the only_async flag, the stub class is named
DummyServiceAsyncStub, not DummyServiceStub. Update the test to use
the correct async stub class name.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add error checking when both only_sync and only_async options are specified
- Simplify make_server_type() logic using direct comparisons instead of if/elif chain
- Check in generated sync_only and async_only test files so CI can run tests
- Add __init__.py files to generated sync/async directories for proper package structure

Typed constructors were already present in the generated code.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add comprehensive tests for sync_only and async_only that actually run
  servers and clients, equivalent to test_grpc_usage.py and test_grpc_async_usage.py
- Restore pyright executionEnvironments for generated_sync_only and generated_async_only
- Add executionEnvironments for test/async_only and test/sync_only with proper extraPaths
- Clean generated_sync_only and generated_async_only directories in test script

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Update ServicerContext type parameters to match the exact types in the
generated async_only stubs. The context type parameters should match
the method return types (Awaitable or AsyncIterator).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The generated _pb2_grpc.py file only exports DummyServiceStub, while the
.pyi file has DummyServiceAsyncStub as a type alias. This matches the
pattern in test_grpc_async_usage.py.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
In async_only mode, the .pyi file now defines both DummyServiceAsyncStub
(the actual class) and DummyServiceStub (a type alias). This matches the
runtime behavior where grpc_tools.protoc generates DummyServiceStub in
the .py file.

Fixes type checking errors where mypy couldn't find DummyServiceStub.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
In async_only mode, generate class DummyServiceStub instead of
DummyServiceAsyncStub with a type alias. Since there's only one stub
type, it should use the standard Stub naming that matches the runtime
.py file generated by grpc_tools.protoc.

This addresses review feedback from @aidandj.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@aidandj
Copy link
Collaborator

aidandj commented Dec 14, 2025

This is all looking good. I'll give it another pass tonight. Feel free to add yourself to the contributors list in the readme, and make sure the change log is updated.

@WouldYouKindly
Copy link
Contributor Author

This is all looking good. I'll give it another pass tonight. Feel free to add yourself to the contributors list in the readme, and make sure the change log is updated.

Thanks! I've updated the changelog.

@aidandj aidandj merged commit f35584a into nipunn1313:main Dec 15, 2025
80 checks passed
@WouldYouKindly WouldYouKindly changed the title DRAFT Add flags to generate only sync or only async stubs Add flags to generate only sync or only async stubs Dec 15, 2025
@Marenz
Copy link

Marenz commented Dec 15, 2025

Thanks for merging this! Is there an ETA for the next release that will include this feature? We have downstream projects waiting on this functionality. 🙏

@aidandj
Copy link
Collaborator

aidandj commented Dec 15, 2025

Thanks for merging this! Is there an ETA for the next release that will include this feature? We have downstream projects waiting on this functionality. 🙏

Hoping sometime in the next week. Have one more feature I want to get in, and holidays are slowing me down a bit.

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.

4 participants