Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make compatible with Python 3.12 #120

Merged
merged 6 commits into from
Jan 23, 2024
Merged

Make compatible with Python 3.12 #120

merged 6 commits into from
Jan 23, 2024

Conversation

mwaskom
Copy link
Contributor

@mwaskom mwaskom commented Jan 16, 2024

This PR adds Python 3.11 and 3.12 to the CI build and addresses an issue that popped up when doing so (which blocks modal-client from being Python 3.12 compatible).

In Python 3.12, TypeVar no longer has a __dict__ attribute. I gather that is a result of the work done to implement PEP695, although I haven't surfaced any specific discussion of this apparently abrupt API break on TypeVar. That caused a few manipulations in synchronicity to fail.

There's also a new behavior in Python 3.12 where a RuntimeError is raised when functions registered with atexit start a new thread. We're seeing that coming out of synchronicity but aren't completely sure why. Since no behavior depends on the old behavior we're just going to swallow the exception for now and revisit later.

Also 3.7 is EOL, so I am dropping it from our CI matrix and bumping the python-requires in pyproject.toml. We recently dropped 3.7 support from the modal client too.

Fixes #118

Copy link

@modal-pr-review-automation modal-pr-review-automation bot left a comment

Choose a reason for hiding this comment

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

Auto-approved 👍. This diff qualified for automatic approval and doesn't need follow up review.

@mwaskom mwaskom force-pushed the michael/ci-python-versions branch from 3232421 to 03d0629 Compare January 16, 2024 18:58
@mwaskom mwaskom changed the title Run test on 3.11 and 3.12 Make compatible with Python 3.12 Jan 16, 2024
@mwaskom mwaskom requested review from erikbern and freider January 16, 2024 19:09
synchronicity/synchronizer.py Show resolved Hide resolved
@freider
Copy link
Contributor

freider commented Jan 17, 2024 via email

@mwaskom
Copy link
Contributor Author

mwaskom commented Jan 17, 2024

While passing tests, the following traceback is printed out when modal run (along with serve, maybe others) exits:

Exception in thread Thread-1 (thread_inner):
Traceback (most recent call last):
  File "/usr/lib/python3.12/threading.py", line 1073, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3.12/threading.py", line 1010, in run
    self._target(*self._args, **self._kwargs)
  File "/home/ubuntu/venvs/py312/lib/python3.12/site-packages/synchronicity/synchronizer.py", line 159, in thread_inner
    asyncio.run(loop_inner())
  File "/usr/lib/python3.12/asyncio/runners.py", line 193, in run
    with Runner(debug=debug, loop_factory=loop_factory) as runner:
  File "/usr/lib/python3.12/asyncio/runners.py", line 62, in __exit__
    self.close()
  File "/usr/lib/python3.12/asyncio/runners.py", line 72, in close
    loop.run_until_complete(
  File "/usr/lib/python3.12/asyncio/base_events.py", line 684, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/asyncio/base_events.py", line 596, in shutdown_default_executor
    thread.start()
  File "/usr/lib/python3.12/threading.py", line 992, in start
    _start_new_thread(self._bootstrap, ())
RuntimeError: can't create new thread at interpreter shutdown

I suspect this is Python 3.12 related because I found an issue on sentry-python describing the same problem, also only on 3.12. I would like to fix this before merging this PR (but I don't totally understand it, or how to reproduce it outside of modal, right now).

@mwaskom
Copy link
Contributor Author

mwaskom commented Jan 18, 2024

Here is the relevant Python change: python/cpython@ce558e6

@freider
Copy link
Contributor

freider commented Jan 19, 2024

Ahhh, graceful shutdown handling in synchronicity has been a long standing issue in general and has been on my todo. I even started a PR working on related code here: #113

@mwaskom
Copy link
Contributor Author

mwaskom commented Jan 22, 2024

I still can't quite figure out how we're tripping the "can't start a new thread at shutdown" exception shown above. How do we feel about just swallowing it, e.g.

@@ -156,7 +156,11 @@ class Synchronizer:
                     is_ready.set()
                     await self._stopping.wait()  # wait until told to stop
 
-                asyncio.run(loop_inner())
+                try:
+                    asyncio.run(loop_inner())
+                except RuntimeError as exc:
+                    if "can't create new thread at interpreter shutdown" not in str(exc):
+                        raise exc
 
             self._thread = threading.Thread(target=thread_inner, daemon=True)
             self._thread.start()

@freider
Copy link
Contributor

freider commented Jan 23, 2024

In my experience it has been things like standard library dns lookups in shutdown handlers that trigger this error, since they use run_in_executor to convert the blocking lookup into a thread. That it happens with Modal is probably due to shutdown handlers doing network requests. Not trivial to fix, so maybe worth just swallowing the exception for now and add a comment about revisiting once we have refactored shutdown handlers?

@mwaskom mwaskom merged commit 2e70cc7 into main Jan 23, 2024
9 checks passed
@mwaskom mwaskom deleted the michael/ci-python-versions branch January 23, 2024 15:42
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.

AttributeError
3 participants