8000 Support Python 3.12 by antonpirker · Pull Request #2408 · getsentry/sentry-python · GitHub
[go: up one dir, main page]

Skip to content

Support Python 3.12 #2408

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

Closed
wants to merge 13 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test-common.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.5","3.6","3.7","3.8","3.9","3.10","3.11"]
python-version: ["3.5","3.6","3.7","3.8","3.9","3.10","3.11","3.12"]
# python3.6 reached EOL and is no longer being supported on
# new versions of hosted runners on Github Actions
# ubuntu-20.04 is the last version that supported python3.6
Expand Down
8 changes: 5 additions & 3 deletions sentry_sdk/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -637,14 +637,15 @@ def close(
self,
timeout=None, # type: Optional[float]
callback=None, # type: Optional[Callable[[int, float], None]]
shutdown=False, # type: bool
):
# type: (...) -> None
"""
Close the client and shut down the transport. Arguments have the same
semantics as :py:meth:`Client.flush`.
"""
if self.transport is not None:
self.flush(timeout=timeout, callback=callback)
self.flush(timeout=timeout, callback=callback, shutdown=shutdown)
self.session_flusher.kill()
if self.metrics_aggregator is not None:
self.metrics_aggregator.kill()
Expand All @@ -657,22 +658,23 @@ def flush(
self,
timeout=None, # type: Optional[float]
callback=None, # type: Optional[Callable[[int, float], None]]
shutdown=False, # type: bool
):
# type: (...) -> None
"""
Wait for the current events to be sent.

:param timeout: Wait for at most `timeout` seconds. If no `timeout` is provided, the `shutdown_timeout` option value is used.

:param callback: Is invoked with the number of pending events and the configured timeout.
:param shutdown: `flush` has been invoked on interpreter shutdown.
"""
if self.transport is not None:
if timeout is None:
timeout = self.options["shutdown_timeout"]
self.session_flusher.flush()
if self.metrics_aggregator is not None:
self.metrics_aggregator.flush()
self.transport.flush(timeout=timeout, callback=callback)
self.transport.flush(timeout=timeout, callback=callback, shutdown=shutdown)

def __enter__(self):
# type: () -> _Client
Expand Down
2 changes: 1 addition & 1 deletion sentry_sdk/integrations/atexit.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,4 @@ def _shutdown():

# If an integration is there, a client has to be there.
client = hub.client # type: Any
client.close(callback=integration.callback)
client.close(callback=integration.callback, shutdown=True)
1 change: 1 addition & 0 deletions sentry_sdk/integrations/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ class _BaseHandler(logging.Handler, object):
"relativeCreated",
"stack",
"tags",
"taskName",
"thread",
"threadName",
"stack_info",
Expand Down
2 changes: 1 addition & 1 deletion sentry_sdk/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ def _ensure_thread(self):
try:
self._flusher.start()
except RuntimeError:
# Unfortunately at this point the interpreter is in a start that no
# Unfortunately at this point the interpreter is in a state that no
# longer allows us to spawn a thread and we have to bail.
self._running = False
return False
Expand Down
5 changes: 5 additions & 0 deletions sentry_sdk/transport.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ def flush(
self,
timeout, # type: float
callback=None, # type: Optional[Any]
shutdown=False, # type: bool
):
# type: (...) -> None
"""Wait `timeout` seconds for the current events to be sent out."""
Expand Down Expand Up @@ -544,10 +545,14 @@ def flush(
self,
timeout, # type: float
callback=None, # type: Optional[Any]
shutdown=False, # type: bool
):
# type: (...) -> None
logger.debug("Flushing HTTP transport")

if shutdown:
self._worker.prepare_shutdown()

if timeout > 0:
self._worker.submit(lambda: self._flush_client_reports(force=True))
self._worker.flush(timeout, callback)
Expand Down
46 changes: 34 additions & 12 deletions sentry_sdk/worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def __init__(self, queue_size=DEFAULT_QUEUE_SIZE):
self._lock = threading.Lock()
self._thread = None # type: Optional[threading.Thread]
self._thread_for_pid = None # type: Optional[int]
self._can_start_threads = True # type: bool

@property
def is_alive(self):
Expand All @@ -41,6 +42,21 @@ def _ensure_thread(self):
if not self.is_alive:
self.start()

def _get_main_thread(self):
# type: () -> Optional[threading.Thread]
main_thread = None

try:
main_thread = threading.main_thread()
except AttributeError:
# Python 2.7 doesn't have threading.main_thread()
for thread in threading.enumerate():
if isinstance(thread, threading._MainThread): # type: ignore[attr-defined]
main_thread = thread
break

return main_thread

def _timed_queue_join(self, timeout):
# type: (float) -> bool
deadline = time() + timeout
Expand All @@ -63,18 +79,16 @@ def start(self):
# type: () -> None
with self._lock:
if not self.is_alive:
self._thread = threading.Thread(
target=self._target, name="raven-sentry.BackgroundWorker"
)
self._thread.daemon = True
try:
if self._can_start_threads:
self._thread = threading.Thread(
target=self._target, name="raven-sentry.BackgroundWorker"
)
self._thread.daemon = True
self._thread.start()
self._thread_for_pid = os.getpid()
except RuntimeError:
# At this point we can no longer start because the interpreter
# is already shutting down. Sadly at this point we can no longer
# send out events.
self._thread = None
else:
self._thread = self._get_main_thread()

self._thread_for_pid = os.getpid()

def kill(self):
# type: () -> None
Expand All @@ -84,7 +98,7 @@ def kill(self):
"""
logger.debug("background worker got kill request")
with self._lock:
if self._thread:
if self._thread and self._thread != self._get_main_thread():
try:
self._queue.put_nowait(_TERMINATOR)
except FullError:
Expand Down Expand Up @@ -141,3 +155,11 @@ def _target(self):
finally:
self._queue.task_done()
sleep(0)

def prepare_shutdown(self):
# type: () -> None
# If the Python 3.12+ interpreter is shutting down, trying to start a new
# thread throws a RuntimeError. If we're shutting down and the worker has
# no active thread, use the main thread instead of spawning a new one.
# See https://github.com/python/cpython/pull/104826
self._can_start_threads = False
9 changes: 5 additions & 4 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
[tox]
envlist =
# === Common ===
{py2.7,py3.5,py3.6,py3.7,py3.8,py3.9,py3.10,py3.11}-common
{py2.7,py3.5,py3.6,py3.7,py3.8,py3.9,py3.10,py3.11,py3.12}-common

# === Integrations ===
# General format is {pythonversion}-{integrationname}-v{frameworkversion}
Expand Down Expand Up @@ -195,7 +195,7 @@ deps =
linters: werkzeug<2.3.0

# Common
{py3.6,py3.7,py3.8,py3.9,py3.10,py3.11}-common: pytest-asyncio
{py3.6,py3.7,py3.8,py3.9,py3.10,py3.11,py3.12}-common: pytest-asyncio

# AIOHTTP
aiohttp-v3.4: aiohttp>=3.4.0,<3.5.0
Expand Down Expand Up @@ -341,7 +341,7 @@ deps =
# See https://stackoverflow.com/questions/51496550/runtime-warning-greenlet-greenlet-size-changed
# for justification why greenlet is pinned here
py3.5-gevent: greenlet==0.4.17
{py2.7,py3.6,py3.7,py3.8,py3.9,py3.10,py3.11}-gevent: gevent>=22.10.0, <22.11.0
{py2.7,py3.6,py3.7,py3.8,py3.9,py3.10,py3.11,py3.12}-gevent: gevent>=22.10.0, <22.11.0

# GQL
gql: gql[all]
Expand Down Expand Up @@ -597,6 +597,7 @@ basepython =
py3.9: python3.9
py3.10: python3.10
py3.11: python3.11
py3.12: python3.12

# Python version is pinned here because flake8 actually behaves differently
# depending on which version is used. You can patch this out to point to
Expand All @@ -623,7 +624,7 @@ commands =
; when loading tests in scenarios. In particular, django fails to
; load the settings from the test module.
{py2.7}: python -m pytest --ignore-glob='*py3.py' -rsx -s --durations=5 -vvv {env:TESTPATH} {posargs}
{py3.5,py3.6,py3.7,py3.8,py3.9,py3.10,py3.11}: python -m pytest -rsx -s --durations=5 -vvv {env:TESTPATH} {posargs}
{py3.5,py3.6,py3.7,py3.8,py3.9,py3.10,py3.11,py3.12}: python -m pytest -rsx -s --durations=5 -vvv {env:TESTPATH} {posargs}

[testenv:linters]
commands =
Expand Down
0