functiontrace 0.4.0__tar.gz → 0.4.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- functiontrace-0.4.2/Cargo.toml +21 -0
- {functiontrace-0.4.0 → functiontrace-0.4.2}/MANIFEST.in +1 -0
- {functiontrace-0.4.0/functiontrace.egg-info → functiontrace-0.4.2}/PKG-INFO +2 -2
- functiontrace-0.4.2/_functiontrace.c +277 -0
- functiontrace-0.4.2/build.rs +17 -0
- {functiontrace-0.4.0 → functiontrace-0.4.2/functiontrace.egg-info}/PKG-INFO +2 -2
- {functiontrace-0.4.0 → functiontrace-0.4.2}/functiontrace.egg-info/SOURCES.txt +1 -0
- {functiontrace-0.4.0 → functiontrace-0.4.2}/functiontrace.py +6 -7
- {functiontrace-0.4.0 → functiontrace-0.4.2}/pyproject.toml +5 -5
- functiontrace-0.4.2/rust/lib.rs +1237 -0
- {functiontrace-0.4.0 → functiontrace-0.4.2}/setup.py +1 -9
- functiontrace-0.4.0/Cargo.toml +0 -19
- functiontrace-0.4.0/_functiontrace.c +0 -1203
- functiontrace-0.4.0/rust/lib.rs +0 -100
- {functiontrace-0.4.0 → functiontrace-0.4.2}/README.rst +0 -0
- {functiontrace-0.4.0 → functiontrace-0.4.2}/functiontrace.egg-info/dependency_links.txt +0 -0
- {functiontrace-0.4.0 → functiontrace-0.4.2}/functiontrace.egg-info/entry_points.txt +0 -0
- {functiontrace-0.4.0 → functiontrace-0.4.2}/functiontrace.egg-info/top_level.txt +0 -0
- {functiontrace-0.4.0 → functiontrace-0.4.2}/mpack/LICENSE +0 -0
- {functiontrace-0.4.0 → functiontrace-0.4.2}/mpack/mpack.c +0 -0
- {functiontrace-0.4.0 → functiontrace-0.4.2}/mpack/mpack.h +0 -0
- {functiontrace-0.4.0 → functiontrace-0.4.2}/setup.cfg +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "_functiontrace_rs"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
|
|
6
|
+
[dependencies]
|
|
7
|
+
color-eyre = { version = "0.6", default-features = false }
|
|
8
|
+
functiontrace-server = { version = "0.8.4", default-features = false, features = ["serialize"] }
|
|
9
|
+
libc = "0.2"
|
|
10
|
+
pyo3 = { version = "0.24", features = ["eyre"] }
|
|
11
|
+
rmp-serde = "1.3"
|
|
12
|
+
serde = "1"
|
|
13
|
+
|
|
14
|
+
[build-dependencies]
|
|
15
|
+
toml = { version = "0.8", default-features = false, features = ["parse"] }
|
|
16
|
+
|
|
17
|
+
[lib]
|
|
18
|
+
# See https://github.com/PyO3/pyo3 for details
|
|
19
|
+
name = "_functiontrace_rs" # private module to be nested into Python package
|
|
20
|
+
crate-type = ["cdylib"]
|
|
21
|
+
path = "rust/lib.rs"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: functiontrace
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: A profiling tool that traces your application's execution
|
|
5
5
|
Author: Alistair Dobke
|
|
6
6
|
Author-email: Matt Bryant <functiontrace@programsareproofs.com>
|
|
@@ -16,7 +16,7 @@ Classifier: Environment :: Console
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
|
17
17
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
18
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
19
|
-
Requires-Python: >=3.
|
|
19
|
+
Requires-Python: >=3.8
|
|
20
20
|
Description-Content-Type: text/x-rst
|
|
21
21
|
|
|
22
22
|
Py-Functiontrace
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
#include <Python.h>
|
|
2
|
+
#include <frameobject.h>
|
|
3
|
+
#include <sys/types.h>
|
|
4
|
+
#include <pthread.h>
|
|
5
|
+
#include <stdio.h>
|
|
6
|
+
#include <stdbool.h>
|
|
7
|
+
#include <unistd.h>
|
|
8
|
+
|
|
9
|
+
#include "mpack/mpack.h"
|
|
10
|
+
|
|
11
|
+
//#include <assert.h>
|
|
12
|
+
// By default, python c extensions compile with asserts disable and it is not
|
|
13
|
+
// super easy to disable this behavior (AFAIK). Use this custom assert for now.
|
|
14
|
+
void assert_helper(int lineno, char* expr) {
|
|
15
|
+
printf("Assertion failed on line %d: %s\n", lineno, expr);
|
|
16
|
+
exit(-1);
|
|
17
|
+
}
|
|
18
|
+
#undef assert
|
|
19
|
+
#define assert(expr) \
|
|
20
|
+
if (!(expr)) \
|
|
21
|
+
assert_helper(__LINE__, #expr)
|
|
22
|
+
|
|
23
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
24
|
+
// Types
|
|
25
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
26
|
+
|
|
27
|
+
// The messagepack writer for the current thread.
|
|
28
|
+
typedef mpack_writer_t Writer;
|
|
29
|
+
|
|
30
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
31
|
+
// Prototypes
|
|
32
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
33
|
+
static Writer* Fprofile_Writer(void);
|
|
34
|
+
|
|
35
|
+
static int Fprofile_FunctionTrace(PyObject* obj, PyFrameObject* frame, int what, PyObject* arg);
|
|
36
|
+
|
|
37
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
38
|
+
// Globals
|
|
39
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
40
|
+
// This key is used to fetch the thread local instance of the `Writer`
|
|
41
|
+
// structure.
|
|
42
|
+
static pthread_key_t Tss_Key = 0;
|
|
43
|
+
|
|
44
|
+
// True iff we've started tracing and haven't been marked as terminated. When
|
|
45
|
+
// this is set, we're allowed to send messages to the profile generation
|
|
46
|
+
// server.
|
|
47
|
+
static bool started;
|
|
48
|
+
|
|
49
|
+
// The exposed apis of the Rust extension.
|
|
50
|
+
static struct {
|
|
51
|
+
pthread_key_t (*set_config)(bool* config, int (*functiontrace)(PyObject*, PyFrameObject*, int, PyObject*));
|
|
52
|
+
}* rust;
|
|
53
|
+
|
|
54
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
55
|
+
// Server Communication
|
|
56
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
57
|
+
|
|
58
|
+
static Writer* Fprofile_Writer(void) {
|
|
59
|
+
assert(Tss_Key != 0);
|
|
60
|
+
|
|
61
|
+
if (!started) {
|
|
62
|
+
// We aren't running (either starting up or shutting down), so
|
|
63
|
+
// shouldn't write anything.
|
|
64
|
+
return NULL;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Fetch the Writerr for the current thread. It's created as late as
|
|
68
|
+
// the first function call for the thread, so we can plausibly get messages
|
|
69
|
+
// before having anywhere to store/send them to. Drop them if so - they
|
|
70
|
+
// weren't too important if we aren't running code yet.
|
|
71
|
+
Writer* state = pthread_getspecific(Tss_Key);
|
|
72
|
+
if (state == NULL) {
|
|
73
|
+
// The thread hasn't officially started yet, so drop this message.
|
|
74
|
+
return NULL;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return state;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
81
|
+
// Misc
|
|
82
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
83
|
+
|
|
84
|
+
// Given a PyObject (likely `co_name` or similar), return a UTF-8
|
|
85
|
+
// representation.
|
|
86
|
+
static inline const char* Fprofile_UnicodeToUtf8(PyObject* obj) {
|
|
87
|
+
if (obj == NULL) {
|
|
88
|
+
return "<NULL>";
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (PyUnicode_Check(obj)) {
|
|
92
|
+
const char* utf8 = PyUnicode_AsUTF8(obj);
|
|
93
|
+
|
|
94
|
+
return utf8 != NULL ? utf8 : "<DECODE ERROR>";
|
|
95
|
+
} else if (obj == Py_None) {
|
|
96
|
+
return "<NONE>";
|
|
97
|
+
} else {
|
|
98
|
+
return "<UNKNOWN>";
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
103
|
+
// Callbacks
|
|
104
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
105
|
+
static int Fprofile_FunctionTrace(PyObject* obj, PyFrameObject* frame, int what, PyObject* arg) {
|
|
106
|
+
PyCFunctionObject* fn = (PyCFunctionObject*) arg;
|
|
107
|
+
struct timespec tsc = { 0 };
|
|
108
|
+
|
|
109
|
+
Writer* writer = Fprofile_Writer();
|
|
110
|
+
if (writer == NULL) {
|
|
111
|
+
return 0;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
clock_gettime(CLOCK_MONOTONIC, &tsc);
|
|
115
|
+
|
|
116
|
+
switch (what) {
|
|
117
|
+
case PyTrace_CALL:
|
|
118
|
+
mpack_start_array(writer, 5);
|
|
119
|
+
mpack_write_cstr(writer, "Call");
|
|
120
|
+
|
|
121
|
+
mpack_start_array(writer, 2);
|
|
122
|
+
mpack_write_u32(writer, tsc.tv_sec);
|
|
123
|
+
mpack_write_u32(writer, tsc.tv_nsec);
|
|
124
|
+
mpack_finish_array(writer);
|
|
125
|
+
|
|
126
|
+
{
|
|
127
|
+
// TODO: Check if frame is none or similar
|
|
128
|
+
#if PY_VERSION_HEX >= 0x030a00a0
|
|
129
|
+
// Python >= 3.10 should use the standard accessors
|
|
130
|
+
PyCodeObject* code = PyFrame_GetCode(frame);
|
|
131
|
+
int lineno = PyFrame_GetLineNumber(frame);
|
|
132
|
+
#else
|
|
133
|
+
PyCodeObject* code = frame->f_code;
|
|
134
|
+
int lineno = frame->f_lineno;
|
|
135
|
+
#endif
|
|
136
|
+
|
|
137
|
+
#if PY_VERSION_HEX >= 0x030b00a0
|
|
138
|
+
mpack_write_cstr(writer, Fprofile_UnicodeToUtf8(code->co_qualname));
|
|
139
|
+
#else
|
|
140
|
+
mpack_write_cstr(writer, Fprofile_UnicodeToUtf8(code->co_name));
|
|
141
|
+
#endif
|
|
142
|
+
mpack_write_cstr(writer, Fprofile_UnicodeToUtf8(code->co_filename));
|
|
143
|
+
mpack_write_u32(writer, lineno);
|
|
144
|
+
|
|
145
|
+
#if PY_VERSION_HEX >= 0x030a00a0
|
|
146
|
+
Py_DECREF(code);
|
|
147
|
+
#endif
|
|
148
|
+
}
|
|
149
|
+
mpack_finish_array(writer);
|
|
150
|
+
break;
|
|
151
|
+
case PyTrace_RETURN:
|
|
152
|
+
mpack_start_array(writer, 3);
|
|
153
|
+
mpack_write_cstr(writer, "Return");
|
|
154
|
+
|
|
155
|
+
mpack_start_array(writer, 2);
|
|
156
|
+
mpack_write_u32(writer, tsc.tv_sec);
|
|
157
|
+
mpack_write_u32(writer, tsc.tv_nsec);
|
|
158
|
+
mpack_finish_array(writer);
|
|
159
|
+
|
|
160
|
+
{
|
|
161
|
+
#if PY_VERSION_HEX >= 0x030a00a0
|
|
162
|
+
PyCodeObject* code = PyFrame_GetCode(frame);
|
|
163
|
+
#else
|
|
164
|
+
PyCodeObject* code = frame->f_code;
|
|
165
|
+
#endif
|
|
166
|
+
#if PY_VERSION_HEX >= 0x030b00a0
|
|
167
|
+
mpack_write_cstr(writer, Fprofile_UnicodeToUtf8(code->co_qualname));
|
|
168
|
+
#else
|
|
169
|
+
mpack_write_cstr(writer, Fprofile_UnicodeToUtf8(code->co_name));
|
|
170
|
+
#endif
|
|
171
|
+
|
|
172
|
+
#if PY_VERSION_HEX >= 0x030a00a0
|
|
173
|
+
Py_DECREF(code);
|
|
174
|
+
#endif
|
|
175
|
+
}
|
|
176
|
+
mpack_finish_array(writer);
|
|
177
|
+
break;
|
|
178
|
+
case PyTrace_C_CALL: {
|
|
179
|
+
mpack_start_array(writer, 4);
|
|
180
|
+
mpack_write_cstr(writer, "NativeCall");
|
|
181
|
+
|
|
182
|
+
mpack_start_array(writer, 2);
|
|
183
|
+
mpack_write_u32(writer, tsc.tv_sec);
|
|
184
|
+
mpack_write_u32(writer, tsc.tv_nsec);
|
|
185
|
+
mpack_finish_array(writer);
|
|
186
|
+
|
|
187
|
+
{
|
|
188
|
+
// Attempt to determine what module/class this function belongs
|
|
189
|
+
// to.
|
|
190
|
+
PyObject* self = fn->m_self;
|
|
191
|
+
PyObject* module = fn->m_module;
|
|
192
|
+
const char* name = fn->m_ml->ml_name;
|
|
193
|
+
const char* module_name = NULL;
|
|
194
|
+
|
|
195
|
+
// Check if we belong to a module, and if not we must be a
|
|
196
|
+
// method. We do this order to avoid finding that the object
|
|
197
|
+
// we belong to is of type module.
|
|
198
|
+
if (module != NULL) {
|
|
199
|
+
if (PyModule_Check(module)) {
|
|
200
|
+
module_name = PyModule_GetName(module);
|
|
201
|
+
} else if (PyUnicode_Check(module)) {
|
|
202
|
+
module_name = PyUnicode_AsUTF8(module);
|
|
203
|
+
}
|
|
204
|
+
} else if (self != NULL) {
|
|
205
|
+
// This is a method call on a class.
|
|
206
|
+
module_name = self->ob_type->tp_name;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
mpack_write_cstr(writer, name != NULL ? name : "NULL");
|
|
210
|
+
mpack_write_cstr(writer, module_name != NULL ? module_name : "NULL");
|
|
211
|
+
}
|
|
212
|
+
mpack_finish_array(writer);
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
case PyTrace_C_RETURN:
|
|
216
|
+
mpack_start_array(writer, 3);
|
|
217
|
+
mpack_write_cstr(writer, "NativeReturn");
|
|
218
|
+
|
|
219
|
+
mpack_start_array(writer, 2);
|
|
220
|
+
mpack_write_u32(writer, tsc.tv_sec);
|
|
221
|
+
mpack_write_u32(writer, tsc.tv_nsec);
|
|
222
|
+
mpack_finish_array(writer);
|
|
223
|
+
|
|
224
|
+
{
|
|
225
|
+
const char* name = fn->m_ml->ml_name;
|
|
226
|
+
mpack_write_cstr(writer, name != NULL ? name : "NULL");
|
|
227
|
+
}
|
|
228
|
+
mpack_finish_array(writer);
|
|
229
|
+
break;
|
|
230
|
+
default:
|
|
231
|
+
// TODO: We should handle exceptions here (or somewhere similar).
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return 0;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
239
|
+
// Module Initialization
|
|
240
|
+
////////////////////////////////////////////////////////////////////////////////
|
|
241
|
+
|
|
242
|
+
// The set of methods exposed by this module to Python.
|
|
243
|
+
static PyMethodDef methods[] = {
|
|
244
|
+
[0] = {NULL, NULL, 0, NULL}
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
static PyModuleDef module = {
|
|
248
|
+
PyModuleDef_HEAD_INIT,
|
|
249
|
+
"_functiontrace",
|
|
250
|
+
"",
|
|
251
|
+
-1,
|
|
252
|
+
methods
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
PyMODINIT_FUNC PyInit__functiontrace(void) {
|
|
256
|
+
// Setup our janky Rust dlopen alternative so we can use Rust code.
|
|
257
|
+
PyObject* rust_mod = PyImport_ImportModule("_functiontrace_rs");
|
|
258
|
+
|
|
259
|
+
// Verify that the rust extension loads if we're using it.
|
|
260
|
+
if (rust_mod == NULL) {
|
|
261
|
+
perror("Failed to load internal Rust extension");
|
|
262
|
+
exit(-1);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// This returns a pointer to the C API exposed by the Rust module.
|
|
266
|
+
PyObject* py_c_api = PyObject_GetAttrString(rust_mod, "c_api");
|
|
267
|
+
assert(py_c_api != NULL);
|
|
268
|
+
PyObject* c_api = PyObject_CallFunctionObjArgs(py_c_api, NULL);
|
|
269
|
+
|
|
270
|
+
// Save the Rust vtable so everyone can access it
|
|
271
|
+
rust = PyLong_AsVoidPtr(c_api);
|
|
272
|
+
|
|
273
|
+
// Share our globals with Rust
|
|
274
|
+
Tss_Key = rust->set_config(&started, Fprofile_FunctionTrace);
|
|
275
|
+
|
|
276
|
+
return PyModule_Create(&module);
|
|
277
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
fn main() -> std::io::Result<()> {
|
|
2
|
+
println!("cargo::rerun-if-changed=pyproject.toml");
|
|
3
|
+
|
|
4
|
+
// Read pyproject.toml to expose our version to the Rust extension
|
|
5
|
+
let pyproject = std::fs::read_to_string("pyproject.toml")?
|
|
6
|
+
.parse::<toml::Table>()
|
|
7
|
+
.expect("pyproject.toml is valid toml");
|
|
8
|
+
let version = pyproject
|
|
9
|
+
.get("project")
|
|
10
|
+
.and_then(|p| p.as_table())
|
|
11
|
+
.and_then(|p| p.get("version"))
|
|
12
|
+
.and_then(|v| v.as_str())
|
|
13
|
+
.expect("version field is present in pyproject.toml");
|
|
14
|
+
println!("cargo::rustc-env=PACKAGE_VERSION={}", version);
|
|
15
|
+
|
|
16
|
+
Ok(())
|
|
17
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: functiontrace
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: A profiling tool that traces your application's execution
|
|
5
5
|
Author: Alistair Dobke
|
|
6
6
|
Author-email: Matt Bryant <functiontrace@programsareproofs.com>
|
|
@@ -16,7 +16,7 @@ Classifier: Environment :: Console
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
|
17
17
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
18
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
19
|
-
Requires-Python: >=3.
|
|
19
|
+
Requires-Python: >=3.8
|
|
20
20
|
Description-Content-Type: text/x-rst
|
|
21
21
|
|
|
22
22
|
Py-Functiontrace
|
|
@@ -5,7 +5,8 @@ import sys
|
|
|
5
5
|
import tempfile
|
|
6
6
|
from importlib import machinery, util
|
|
7
7
|
|
|
8
|
-
import _functiontrace
|
|
8
|
+
import _functiontrace # noqa: F401 - this import is load bearing since we need to run code on import
|
|
9
|
+
import _functiontrace_rs as rust
|
|
9
10
|
|
|
10
11
|
PYTHON_TEMPLATE = """#!/bin/sh
|
|
11
12
|
|
|
@@ -70,9 +71,7 @@ def main():
|
|
|
70
71
|
action="store_true",
|
|
71
72
|
help="Disable compression of traces, which can be unnecessarily slow if filesize is not a concern.",
|
|
72
73
|
)
|
|
73
|
-
parser.add_argument(
|
|
74
|
-
"-v", "--version", action="version", version=_functiontrace.__version__
|
|
75
|
-
)
|
|
74
|
+
parser.add_argument("-v", "--version", action="version", version=rust.__version__)
|
|
76
75
|
parser.add_argument("script", nargs=argparse.REMAINDER)
|
|
77
76
|
args = parser.parse_args()
|
|
78
77
|
|
|
@@ -114,8 +113,8 @@ def main():
|
|
|
114
113
|
if args.no_compression:
|
|
115
114
|
os.environ["FUNCTIONTRACE_COMPRESSION"] = "false"
|
|
116
115
|
if args.trace_memory:
|
|
117
|
-
|
|
118
|
-
|
|
116
|
+
rust.enable_tracememory()
|
|
117
|
+
rust.begin_tracing(args.output_dir)
|
|
119
118
|
|
|
120
119
|
# Run their code now that we're tracing. This must be done in the context
|
|
121
120
|
# of the __main__ module we've created.
|
|
@@ -125,7 +124,7 @@ def main():
|
|
|
125
124
|
def trace():
|
|
126
125
|
# Make sure we're set up to work properly, then begin tracing.
|
|
127
126
|
setup_dependencies()
|
|
128
|
-
|
|
127
|
+
rust.begin_tracing(os.getcwd())
|
|
129
128
|
|
|
130
129
|
|
|
131
130
|
if __name__ == "__main__":
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "functiontrace"
|
|
3
|
-
version = "0.4.
|
|
3
|
+
version = "0.4.2"
|
|
4
4
|
description = "A profiling tool that traces your application's execution"
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "Matt Bryant", email = "functiontrace@programsareproofs.com"},
|
|
@@ -8,13 +8,13 @@ authors = [
|
|
|
8
8
|
]
|
|
9
9
|
license = { text = "The Prosperity Public License 3.0.0" }
|
|
10
10
|
readme = "README.rst"
|
|
11
|
-
# NOTE: Actively tested on Python 3.
|
|
11
|
+
# NOTE: Actively tested on Python 3.8 - 3.13 and may plausibly break on greater
|
|
12
12
|
# versions due to C extension hacks. However, setting a maximum version causes
|
|
13
13
|
# new Python versions to simply install very old versions of Functiontrace that
|
|
14
14
|
# didn't have the `requires-python` annotation on them and are also unlikely to
|
|
15
15
|
# work. Best to only set the minimum and let bug reports notify us of issues
|
|
16
16
|
# with new Python releases.
|
|
17
|
-
requires-python = ">=3.
|
|
17
|
+
requires-python = ">=3.8"
|
|
18
18
|
keywords = [
|
|
19
19
|
"profiler",
|
|
20
20
|
"debugger"
|
|
@@ -41,9 +41,9 @@ functiontrace = "functiontrace:main"
|
|
|
41
41
|
functiontrace = "functiontrace"
|
|
42
42
|
|
|
43
43
|
[build-system]
|
|
44
|
-
requires = ["setuptools", "
|
|
44
|
+
requires = ["setuptools", "setuptools-rust"]
|
|
45
45
|
build-backend = "setuptools.build_meta"
|
|
46
46
|
|
|
47
47
|
[[tool.setuptools-rust.ext-modules]]
|
|
48
|
-
target = "_functiontrace_rs
|
|
48
|
+
target = "_functiontrace_rs" # Must match `lib.name` in Cargo.toml
|
|
49
49
|
binding = "PyO3"
|