Skip to content

[Python][Support 3.14] Enable 3.14 in Python Basic, Bazel and Distrib tests#40403

Closed
sreenithi wants to merge 25 commits intogrpc:masterfrom
sreenithi:python_314_basictests_update
Closed

[Python][Support 3.14] Enable 3.14 in Python Basic, Bazel and Distrib tests#40403
sreenithi wants to merge 25 commits intogrpc:masterfrom
sreenithi:python_314_basictests_update

Conversation

@sreenithi
Copy link
Contributor

@sreenithi sreenithi commented Aug 6, 2025

This PR enables Python 3.14 in all the different tests - Basic tests (Native Python tests), Bazel tests and Distrib tests to build Python 3.14 artifacts. In addition, it also updates all the public facing METADATA versions.

Distribtests

Required pre-requisite changes to enable 3.14 artifacts are covered in #40289 .

Bazel tests

Enabling Python 3.14 required updating the rules_python version to a more recent version that supports 3.14. This was done in #40602

Basic tests

The following errors were caught by the Basic tests when running via Python 3.14 and resolved in this PR:

1) No running event loop for AsyncIO when run outside an async function

Traceback (most recent call last):
  File "src/python/grpcio/grpc/_cython/_cygrpc/aio/common.pyx.pxi", line 184, in grpc._cython.cygrpc.get_working_loop
RuntimeError: no running event loop

This was caught by the tests_aio.unit.outside_init_test.TestOutsideInit and tests_aio.unit.init_test.TestInit tests, and was also previously reported in #39507 with the root cause.

Following some investigation, the fix is being worked on by @sergiitk in PR #40293. In order to parallelize the fix and this PR, these 2 tests are currently being skipped for Python 3.14 and above.

2) Pickling error from the multiprocessing library

_pickle.PicklingError: Can't pickle <function _test_well_known_types at 0x7f3937eee610>: it's not the same object as tests.unit._dynamic_stubs_test._test_well_known_types
when serializing dict item '_target'
when serializing multiprocessing.context.Process state
when serializing multiprocessing.context.Process object

This was caught by the tests.unit._dynamic_stubs_test.DynamicStubTest which runs test cases in a subprocess using the multiprocessing library.
Error root cause:

  • The default start method of multiprocessing in linux has changed to forkserver instead of fork from Python 3.14.
  • forkserver has a few extra restrictions for picklability as compared to fork (Ref: Python Docs)
  • All the test case functions in the DynamicStubTest that are provided as target to the multiprocessing.Process use decorators. This causes problems when pickling them.

Hence to resolve this, we manually set the 'start method' of multiprocessing to use the fork start method.

@sreenithi sreenithi marked this pull request as ready for review August 8, 2025 10:26
@sreenithi sreenithi requested a review from sergiitk as a code owner August 8, 2025 10:26
@sreenithi sreenithi added this to the Python 3.14 support milestone Aug 8, 2025
@sreenithi sreenithi requested a review from asheshvidyut August 8, 2025 10:28
not be set as the default event loop for the main thread.
"""
try:
return asyncio.get_running_loop()
Copy link
Member

Choose a reason for hiding this comment

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

This method returns a running loop, but in the new method introduced, we are not returning a running loop.

Question - Do we need to make the loop running before returning?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point, but we're just retaining the previous behaviour here. Even previously, we returned a running loop only if it was already running, and otherwise just return an existing (not running) event loop returned by asyncio.get_event_loop_policy().get_event_loop() or a new loop returned by asyncio.get_event_loop_policy().new_event_loop().

We're using the same behaviour here, and in addition also setting this newly created loop as the current event loop for the thread using asyncio.set_event_loop(loop).

And following that, we bind to the event loop and continue using it here.

Copy link
Member

Choose a reason for hiding this comment

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

For posterity: this will be handled separately, as a fix for #39507.

@asheshvidyut
Copy link
Member

asheshvidyut commented Aug 11, 2025

The current implementation doesn't return a running loop and creates a new loop every time we call the function _get_or_create_default_loop. Do you think it makes sense to return a running loop so that the loop created once could be reused again? cc @sergiitk @sreenithi

import asyncio
import threading

# Global variable to track the created loop
_global_loop = None
_loop_lock = threading.Lock()

def get_running_event_loop():
    global _global_loop
    
    try:
        return asyncio.get_running_loop()
    except RuntimeError:
        with _loop_lock:
            if _global_loop is not None and _global_loop.is_running():
                print("reusing existing loop")
                return _global_loop
            
            # No loop is running, so we need to create and start one
            print("creating a loop")
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
            
            # Start the loop in a daemon thread so it doesn't block the main thread
            def run_forever():
                loop.run_forever()
            
            thread = threading.Thread(target=run_forever, daemon=True)
            thread.start()
                    
            _global_loop = loop
            return loop

@sreenithi sreenithi self-assigned this Aug 22, 2025
@sreenithi sreenithi requested a review from eugeneo as a code owner September 1, 2025 12:11
@sreenithi sreenithi changed the title [Python][Support 3.14] Enable 3.14 in Python Basic tests [Python][Support 3.14] Enable 3.14 in Python Basic, Bazel and Distrib tests Sep 3, 2025
copybara-service bot pushed a commit that referenced this pull request Sep 4, 2025
…ts (#40289)

This PR adds Python 3.14 support for Distribtests by updating relevant docker images and distribtest build files for Linux, MacOS and Windows environments. However, artifact generation in distribtests will be enabled in the next PR #40403

Prerequisites merged before this PR:
- #40317
- #40354
- #40383

Closes #40289

COPYBARA_INTEGRATE_REVIEW=#40289 from sreenithi:python_314_test_support 3b8bf96
PiperOrigin-RevId: 802967999
Copy link
Member

@sergiitk sergiitk left a comment

Choose a reason for hiding this comment

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

Approved as is. There's a few comments I left, but nothing that can't be done in a follow-up PR.

Comment on lines +71 to +76
# The default start method of multiprocessing in linux has changed to
# `forkserver` instead of `fork` from Python 3.14. This causes problems
# with pickling target methods that use decorators.
# Hence manually set to use the `fork` start method.
if sys.version_info >= (3, 14) and ("linux" in sys.platform):
multiprocessing.set_start_method("fork", force=True)
Copy link
Member

Choose a reason for hiding this comment

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

A few notes regarding this change. First, we can do this once in DynamicStubTest.setUpClass(), and force=True won't be needed. Second, we should add the same logic to tools/distrib/python/grpcio_tools/grpc_tools/test/protoc_test.py.
But this doesn't block this PR, we can fix it in a follow-up.


@unittest.skipIf(
sys.version_info >= (3, 14),
"Skip for Python 3.14+ until https://github.com/grpc/grpc/pull/40293 is merged",
Copy link
Member

Choose a reason for hiding this comment

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

That PR was pretty much a demo I wasn't intending to merge this specific branch. I'd rather change this to

Suggested change
"Skip for Python 3.14+ until https://github.com/grpc/grpc/pull/40293 is merged",
"Skip for Python 3.14+ until https://github.com/grpc/grpc/issues/39507 is fixed",


@unittest.skipIf(
sys.version_info >= (3, 14),
"Skip for Python 3.14+ until https://github.com/grpc/grpc/pull/40293 is merged",
Copy link
Member

Choose a reason for hiding this comment

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

Same

Suggested change
"Skip for Python 3.14+ until https://github.com/grpc/grpc/pull/40293 is merged",
"Skip for Python 3.14+ until https://github.com/grpc/grpc/issues/39507 is fixed",

@copybara-service copybara-service bot closed this in ee5325b Sep 8, 2025
sreenithi added a commit to sreenithi/grpc that referenced this pull request Sep 8, 2025
…ts (grpc#40289)

This PR adds Python 3.14 support for Distribtests by updating relevant docker images and distribtest build files for Linux, MacOS and Windows environments. However, artifact generation in distribtests will be enabled in the next PR grpc#40403

Prerequisites merged before this PR:
- grpc#40317
- grpc#40354
- grpc#40383

Closes grpc#40289

COPYBARA_INTEGRATE_REVIEW=grpc#40289 from sreenithi:python_314_test_support 3b8bf96
PiperOrigin-RevId: 802967999
sreenithi added a commit to sreenithi/grpc that referenced this pull request Sep 9, 2025
…ts (grpc#40289)

This PR adds Python 3.14 support for Distribtests by updating relevant docker images and distribtest build files for Linux, MacOS and Windows environments. However, artifact generation in distribtests will be enabled in the next PR grpc#40403

Prerequisites merged before this PR:
- grpc#40317
- grpc#40354
- grpc#40383

Closes grpc#40289

COPYBARA_INTEGRATE_REVIEW=grpc#40289 from sreenithi:python_314_test_support 3b8bf96
PiperOrigin-RevId: 802967999
sergiitk pushed a commit that referenced this pull request Sep 9, 2025
…ters for distribtests (#40637)

Backport of #40289 to v1.75.x.
---
This PR adds Python 3.14 support for Distribtests by updating relevant
docker images and distribtest build files for Linux, MacOS and Windows
environments. However, artifact generation in distribtests will be
enabled in the next PR #40403

Prerequisites merged before this PR:
- #40317
- #40354
- #40383
asheshvidyut pushed a commit to asheshvidyut/grpc that referenced this pull request Sep 12, 2025
…ts (grpc#40289)

This PR adds Python 3.14 support for Distribtests by updating relevant docker images and distribtest build files for Linux, MacOS and Windows environments. However, artifact generation in distribtests will be enabled in the next PR grpc#40403

Prerequisites merged before this PR:
- grpc#40317
- grpc#40354
- grpc#40383

Closes grpc#40289

COPYBARA_INTEGRATE_REVIEW=grpc#40289 from sreenithi:python_314_test_support 3b8bf96
PiperOrigin-RevId: 802967999
asheshvidyut pushed a commit to asheshvidyut/grpc that referenced this pull request Sep 12, 2025
… tests (grpc#40403)

This PR enables Python 3.14 in all the different tests - Basic tests (Native Python tests), Bazel tests and Distrib tests to build Python 3.14 artifacts. In addition, it also updates all the public facing METADATA versions.

## Distribtests
Required pre-requisite changes to enable 3.14 artifacts are covered in grpc#40289 .

## Bazel tests
Enabling Python 3.14 required updating the rules_python version to a more recent version that supports 3.14. This was done in grpc#40602

## Basic tests
The following errors were caught by the Basic tests when running via Python 3.14 and resolved in this PR:

### 1) No running event loop for AsyncIO when run outside an async function
```
Traceback (most recent call last):
  File "src/python/grpcio/grpc/_cython/_cygrpc/aio/common.pyx.pxi", line 184, in grpc._cython.cygrpc.get_working_loop
RuntimeError: no running event loop
```
This was caught by the `tests_aio.unit.outside_init_test.TestOutsideInit` and `tests_aio.unit.init_test.TestInit` tests, and was also previously reported in grpc#39507 with the root cause.

Following some investigation, the fix is being worked on by @sergiitk  in PR grpc#40293. In order to parallelize the fix and this PR, these 2 tests are currently being skipped for Python 3.14 and above.

### 2) Pickling error from the `multiprocessing` library
```
_pickle.PicklingError: Can't pickle <function _test_well_known_types at 0x7f3937eee610>: it's not the same object as tests.unit._dynamic_stubs_test._test_well_known_types
when serializing dict item '_target'
when serializing multiprocessing.context.Process state
when serializing multiprocessing.context.Process object
```
This was caught by the `tests.unit._dynamic_stubs_test.DynamicStubTest` which runs test cases in a subprocess using the `multiprocessing` library.
Error root cause:
- The default start method of multiprocessing in linux has changed to `forkserver` instead of `fork` from Python 3.14.
- `forkserver` has a few extra restrictions for picklability as compared to `fork` (Ref: [Python Docs](https://docs.python.org/3.14/library/multiprocessing.html#the-spawn-and-forkserver-start-methods))
- All the [test case functions](https://github.com/grpc/grpc/blob/0243842d5d10f624bf8f09f45026dd300805502f/src/python/grpcio_tests/tests/unit/_dynamic_stubs_test.py#L115) in the DynamicStubTest that are provided as `target` to the `multiprocessing.Process` use decorators. This causes problems when pickling them.

Hence to resolve this, we manually set the 'start method' of `multiprocessing` to use the `fork` start method.

Closes grpc#40403

PiperOrigin-RevId: 804290760
sreenithi added a commit to sreenithi/grpc that referenced this pull request Sep 17, 2025
… tests (grpc#40403)

This PR enables Python 3.14 in all the different tests - Basic tests (Native Python tests), Bazel tests and Distrib tests to build Python 3.14 artifacts. In addition, it also updates all the public facing METADATA versions.
Required pre-requisite changes to enable 3.14 artifacts are covered in grpc#40289 .
Enabling Python 3.14 required updating the rules_python version to a more recent version that supports 3.14. This was done in grpc#40602
The following errors were caught by the Basic tests when running via Python 3.14 and resolved in this PR:
```
Traceback (most recent call last):
  File "src/python/grpcio/grpc/_cython/_cygrpc/aio/common.pyx.pxi", line 184, in grpc._cython.cygrpc.get_working_loop
RuntimeError: no running event loop
```
This was caught by the `tests_aio.unit.outside_init_test.TestOutsideInit` and `tests_aio.unit.init_test.TestInit` tests, and was also previously reported in grpc#39507 with the root cause.

Following some investigation, the fix is being worked on by @sergiitk  in PR grpc#40293. In order to parallelize the fix and this PR, these 2 tests are currently being skipped for Python 3.14 and above.
```
_pickle.PicklingError: Can't pickle <function _test_well_known_types at 0x7f3937eee610>: it's not the same object as tests.unit._dynamic_stubs_test._test_well_known_types
when serializing dict item '_target'
when serializing multiprocessing.context.Process state
when serializing multiprocessing.context.Process object
```
This was caught by the `tests.unit._dynamic_stubs_test.DynamicStubTest` which runs test cases in a subprocess using the `multiprocessing` library.
Error root cause:
- The default start method of multiprocessing in linux has changed to `forkserver` instead of `fork` from Python 3.14.
- `forkserver` has a few extra restrictions for picklability as compared to `fork` (Ref: [Python Docs](https://docs.python.org/3.14/library/multiprocessing.html#the-spawn-and-forkserver-start-methods))
- All the [test case functions](https://github.com/grpc/grpc/blob/0243842d5d10f624bf8f09f45026dd300805502f/src/python/grpcio_tests/tests/unit/_dynamic_stubs_test.py#L115) in the DynamicStubTest that are provided as `target` to the `multiprocessing.Process` use decorators. This causes problems when pickling them.

Hence to resolve this, we manually set the 'start method' of `multiprocessing` to use the `fork` start method.

Closes grpc#40403

PiperOrigin-RevId: 804290760
sreenithi added a commit that referenced this pull request Sep 18, 2025
#40726)

Backport of #40403 to v1.75.x.
---
This PR enables Python 3.14 in all the different tests - Basic tests
(Native Python tests), Bazel tests and Distrib tests to build Python
3.14 artifacts. In addition, it also updates all the public facing
METADATA versions.

## Distribtests
Required pre-requisite changes to enable 3.14 artifacts are covered in
#40289 .

## Bazel tests
Enabling Python 3.14 required updating the rules_python version to a
more recent version that supports 3.14. This was done in #40602

## Basic tests
The following errors were caught by the Basic tests when running via
Python 3.14 and resolved in this PR:

### 1) No running event loop for AsyncIO when run outside an async
function
```
Traceback (most recent call last):
  File "src/python/grpcio/grpc/_cython/_cygrpc/aio/common.pyx.pxi", line 184, in grpc._cython.cygrpc.get_working_loop
RuntimeError: no running event loop
```
This was caught by the
`tests_aio.unit.outside_init_test.TestOutsideInit` and
`tests_aio.unit.init_test.TestInit` tests, and was also previously
reported in #39507 with the root cause.

Following some investigation, the fix is being worked on by @sergiitk in
PR #40293. In order to parallelize the fix and this PR, these 2 tests
are currently being skipped for Python 3.14 and above.

### 2) Pickling error from the `multiprocessing` library
```
_pickle.PicklingError: Can't pickle <function _test_well_known_types at 0x7f3937eee610>: it's not the same object as tests.unit._dynamic_stubs_test._test_well_known_types
when serializing dict item '_target'
when serializing multiprocessing.context.Process state
when serializing multiprocessing.context.Process object
```
This was caught by the `tests.unit._dynamic_stubs_test.DynamicStubTest`
which runs test cases in a subprocess using the `multiprocessing`
library.
Error root cause:
- The default start method of multiprocessing in linux has changed to
`forkserver` instead of `fork` from Python 3.14.
- `forkserver` has a few extra restrictions for picklability as compared
to `fork` (Ref: [Python
Docs](https://docs.python.org/3.14/library/multiprocessing.html#the-spawn-and-forkserver-start-methods))
- All the [test case
functions](https://github.com/grpc/grpc/blob/0243842d5d10f624bf8f09f45026dd300805502f/src/python/grpcio_tests/tests/unit/_dynamic_stubs_test.py#L115)
in the DynamicStubTest that are provided as `target` to the
`multiprocessing.Process` use decorators. This causes problems when
pickling them.

Hence to resolve this, we manually set the 'start method' of
`multiprocessing` to use the `fork` start method.
@anniefrchz anniefrchz mentioned this pull request Oct 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants