From 383d36fa3de13596d918ffb96d70d54df6b95748 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 22 Oct 2026 08:27:58 +0200 Subject: [PATCH] Import on Pyodide, which has no thread_time_ns --- CHANGELOG.md | 2 ++ src/scope_profiler/concurrency.py | 7 ++++++- src/scope_profiler/tests/unit/test_concurrency.py | 14 ++++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 443e62de..1574eca1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -173,6 +173,8 @@ ### Fixed +- `import scope_profiler` works on Pyodide, whose `time` has no `thread_time_ns`; the process + clock stands in for it there. - Summary: a recursive row's total sums only the outermost calls, so it no longer exceeds the time of its caller, and the regions called from a recursive function are no longer marked with the recursive symbol (#313). diff --git a/src/scope_profiler/concurrency.py b/src/scope_profiler/concurrency.py index 3870fe46..581cf335 100644 --- a/src/scope_profiler/concurrency.py +++ b/src/scope_profiler/concurrency.py @@ -35,7 +35,12 @@ import sys import threading import weakref -from time import perf_counter_ns, thread_time_ns +from time import perf_counter_ns + +try: + from time import thread_time_ns +except ImportError: # Emscripten (Pyodide): no per-thread clock, and only one thread + from time import process_time_ns as thread_time_ns from typing import Any import numpy as np diff --git a/src/scope_profiler/tests/unit/test_concurrency.py b/src/scope_profiler/tests/unit/test_concurrency.py index 7b673253..28dbac58 100644 --- a/src/scope_profiler/tests/unit/test_concurrency.py +++ b/src/scope_profiler/tests/unit/test_concurrency.py @@ -1,6 +1,8 @@ """The lane bookkeeping behind ``track_threads`` and ``track_async``.""" import asyncio +import subprocess +import sys import threading import numpy as np @@ -316,3 +318,15 @@ def previous(frame, event, arg): sys.setprofile(None) tracker.uninstall() threading.setprofile(None) + + +def test_imports_without_a_per_thread_clock(): + """Pyodide's ``time`` has no ``thread_time_ns``; the process clock stands in.""" + code = ( + "import time\n" + "del time.thread_time_ns\n" + "import scope_profiler\n" + "from scope_profiler import concurrency\n" + "assert concurrency.thread_time_ns is time.process_time_ns\n" + ) + subprocess.run([sys.executable, "-c", code], check=True)