Skip to content

Commit 25361b5

Browse files
authored
Fixes missing class names in Score-P region names with Python 3.13 (#179)
Fixes #174
2 parents 40959ab + 658cafc commit 25361b5

File tree

5 files changed

+2407
-345
lines changed

5 files changed

+2407
-345
lines changed

.github/workflows/static_analysis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ jobs:
1010
uses: Flamefire/clang-format-lint-action@master
1111
with:
1212
source: src
13+
exclude: "src/scorepy/pythoncapi_compat.h"
1314
clangFormatVersion: 9
1415

1516
Python:

.github/workflows/unit_tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
runs-on: ubuntu-latest
1212
strategy:
1313
matrix:
14-
python: ["3.9", "3.10", "3.11", "3.12", "3.13", 'pypy-2.7', 'pypy-3.7', 'pypy-3.8', 'pypy-3.9', 'pypy-3.10', 'pypy-3.11']
14+
python: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14", 'pypy-2.7', 'pypy-3.7', 'pypy-3.8', 'pypy-3.9', 'pypy-3.10', 'pypy-3.11']
1515
fail-fast: false
1616

1717
steps:

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
"Programming Language :: Python :: 3.11",
9292
"Programming Language :: Python :: 3.12",
9393
"Programming Language :: Python :: 3.13",
94+
"Programming Language :: Python :: 3.14",
9495
"Programming Language :: Python :: Implementation :: CPython",
9596
"Programming Language :: Python :: Implementation :: PyPy",
9697
"Operating System :: POSIX",

src/scorepy/pythonHelpers.cpp

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,46 +3,78 @@
33
#include "pathUtils.hpp"
44
#include "pythoncapi_compat.h"
55

6+
#include <cstdio>
67
#include <sstream>
78
#include <stdlib.h>
9+
#include <string>
810

911
namespace scorepy
1012
{
13+
14+
static PyObject* get_self_from_frame(PyFrameObject* frame)
15+
{
16+
PyObject* self = nullptr;
17+
18+
#if PY_VERSION_HEX >= 0x030C0000 // Python 3.12+
19+
// Added in 3.12: directly fetch a local variable by name.
20+
self = PyFrame_GetVarString(frame, "self");
21+
if (!self && PyErr_Occurred())
22+
PyErr_Clear();
23+
#else
24+
PyObject* locals = PyFrame_GetLocals(frame); // New reference
25+
if (locals)
26+
{
27+
PyObject* tmp = PyDict_GetItemString(locals, "self"); // Borrowed
28+
if (tmp)
29+
{
30+
Py_INCREF(tmp);
31+
self = tmp;
32+
}
33+
Py_DECREF(locals);
34+
}
35+
#endif
36+
return self;
37+
}
38+
1139
std::string get_module_name(PyFrameObject& frame)
1240
{
1341
const char* self_name = nullptr;
14-
PyObject* locals = PyFrame_GetLocals(&frame);
15-
PyObject* self = PyDict_GetItemString(locals, "self");
42+
43+
PyObject* self = get_self_from_frame(&frame);
1644
if (self)
1745
{
18-
Py_INCREF(self);
1946
PyTypeObject* type = Py_TYPE(self);
2047
self_name = _PyType_Name(type);
2148
Py_DECREF(self);
2249
}
23-
Py_DECREF(locals);
2450

25-
PyObject* globals = PyFrame_GetGlobals(&frame);
26-
PyObject* module_name = PyDict_GetItemString(globals, "__name__");
27-
Py_DECREF(globals);
51+
// --- get module name from globals ---------------------------------------
52+
PyObject* globals = PyFrame_GetGlobals(&frame); // New reference
53+
PyObject* module_name = globals ? PyDict_GetItemString(globals, "__name__") // Borrowed
54+
:
55+
nullptr;
56+
if (globals)
57+
Py_DECREF(globals);
58+
2859
if (module_name)
2960
{
3061
std::stringstream result;
62+
// compat::get_string_as_utf_8() is assumed to convert PyObject* → UTF-8 std::string
3163
result << compat::get_string_as_utf_8(module_name);
3264
if (self_name)
3365
result << '.' << self_name;
34-
return std::move(result).str();
66+
return result.str();
3567
}
3668

37-
// this is a NUMPY special situation, see NEP-18, and Score-P issue #63
38-
// TODO: Use string_view/C-String to avoid creating 2 std::strings
69+
// --- special-case NumPy internal frames ---------------------------------
3970
PyCodeObject* code = PyFrame_GetCode(&frame);
4071
std::string_view filename = compat::get_string_as_utf_8(code->co_filename);
4172
Py_DECREF(code);
42-
if ((filename.size() > 0) && (filename == "<__array_function__ internals>"))
43-
return std::move(std::string("numpy.__array_function__"));
44-
else
45-
return std::move(std::string("unkown"));
73+
74+
if (filename == "<__array_function__ internals>")
75+
return "numpy.__array_function__";
76+
77+
return "unknown";
4678
}
4779

4880
std::string get_file_name(PyFrameObject& frame)
@@ -57,4 +89,5 @@ std::string get_file_name(PyFrameObject& frame)
5789
const auto full_file_name = abspath(compat::get_string_as_utf_8(filename));
5890
return !full_file_name.empty() ? std::move(full_file_name) : "ErrorPath";
5991
}
92+
6093
} // namespace scorepy

0 commit comments

Comments
 (0)