• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

TEN-framework / ten-framework / 20358357829

19 Dec 2025 03:00AM UTC coverage: 57.587% (+0.2%) from 57.34%
20358357829

push

github

web-flow
fix: coveralls remove container for all test jobs (#1878)

* chore: calculate coverage in debug mode

* Revert "chore: calculate coverage in debug mode"

This reverts commit f68a031af.

* Reapply "chore: calculate coverage in debug mode"

This reverts commit bc27f2c1c.

* fix: remove container for test jobs

* fix: add C++ toolchain, go builder, sanitizer support, nodejs env

* fix: compiler version incompatible

* Revert "fix: compiler version incompatible"

This reverts commit 23f5c3f7d.

* fix: set PATH so that clang-21 is find earlier than older versions

* fix: uninstall older versions of clang

* Revert "fix: set PATH so that clang-21 is find earlier than older versions"

This reverts commit b66e56c08.

* fix: uninstall old clang first then install clang21

* fix: supplement test jobs deps according to tools Dockerfile

* fix: install libasan5 for all jobs and remove unecessary deps

* chore: refine codes and output clang version before every test

* fix: go back to clang 18 to detect __lsan_do_leak_check

* Revert "fix: go back to clang 18 to detect __lsan_do_leak_check"

This reverts commit d247818a8.

* fix: upgrade libasan5 to libasan8 to match clang 21

* fix: install libclang-rt-21-dev

* Revert "fix: install libclang-rt-21-dev"

This reverts commit 39b96e030.

* fix: ten_enable_go_app_leak_check

* chore: uninstall useless dep libasan8

* chore: refine codes and uninstall unecessary dep clang-tool-21

54480 of 94605 relevant lines covered (57.59%)

682622.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.17
/core/src/ten_runtime/binding/python/native/extension/extension.c
1
//
2
// Copyright © 2025 Agora
3
// This file is part of TEN Framework, an open source project.
4
// Licensed under the Apache License, Version 2.0, with certain conditions.
5
// Refer to the "LICENSE" file in the root directory for more information.
6
//
7
#include "include_internal/ten_runtime/extension/extension.h"
8

9
#include "include_internal/ten_runtime/binding/python/common.h"
10
#include "include_internal/ten_runtime/binding/python/common/common.h"
11
#include "include_internal/ten_runtime/binding/python/common/error.h"
12
#include "include_internal/ten_runtime/binding/python/extension/extension.h"
13
#include "include_internal/ten_runtime/binding/python/msg/audio_frame.h"
14
#include "include_internal/ten_runtime/binding/python/msg/cmd/cmd.h"
15
#include "include_internal/ten_runtime/binding/python/msg/data.h"
16
#include "include_internal/ten_runtime/binding/python/msg/video_frame.h"
17
#include "include_internal/ten_runtime/binding/python/ten_env/ten_env.h"
18
#include "include_internal/ten_runtime/metadata/metadata_info.h"
19
#include "include_internal/ten_runtime/msg/msg.h"
20
#include "include_internal/ten_runtime/ten_env_proxy/ten_env_proxy.h"
21
#include "ten_runtime/binding/common.h"
22
#include "ten_runtime/extension/extension.h"
23
#include "ten_runtime/ten_env/ten_env.h"
24
#include "ten_runtime/ten_env_proxy/ten_env_proxy.h"
25
#include "ten_utils/lib/smart_ptr.h"
26
#include "ten_utils/macro/mark.h"
27
#include "ten_utils/macro/memory.h"
28

29
static bool ten_py_extension_check_integrity(ten_py_extension_t *self,
30
                                             bool check_thread) {
197✔
31
  TEN_ASSERT(self, "Should not happen.");
197✔
32

33
  if (ten_signature_get(&self->signature) !=
197✔
34
      (ten_signature_t)TEN_PY_EXTENSION_SIGNATURE) {
197✔
35
    return false;
×
36
  }
×
37

38
  return ten_extension_check_integrity(self->c_extension, check_thread);
197✔
39
}
197✔
40

41
static PyObject *stub_on_callback(TEN_UNUSED PyObject *self,
42
                                  TEN_UNUSED PyObject *args) {
×
43
  Py_RETURN_NONE;
×
44
}
×
45

46
static void proxy_on_configure(ten_extension_t *extension, ten_env_t *ten_env) {
27✔
47
  TEN_ASSERT(extension, "Invalid argument.");
27✔
48
  TEN_ASSERT(ten_extension_check_integrity(extension, true),
27✔
49
             "Invalid argument.");
27✔
50
  TEN_ASSERT(ten_env, "Invalid argument.");
27✔
51
  TEN_ASSERT(ten_env_check_integrity(ten_env, true), "Invalid argument.");
27✔
52

53
  // About to call the Python function, so it's necessary to ensure that the GIL
54
  // has been acquired.
55
  PyGILState_STATE prev_state = ten_py_gil_state_ensure_internal();
27✔
56
  // This function can only be called on the native thread not a Python
57
  // thread.
58
  TEN_ASSERT(prev_state == PyGILState_UNLOCKED,
27✔
59
             "The GIL should not be help by the extension thread now.");
27✔
60

61
  ten_py_extension_t *py_extension =
27✔
62
      (ten_py_extension_t *)ten_binding_handle_get_me_in_target_lang(
27✔
63
          (ten_binding_handle_t *)extension);
27✔
64
  TEN_ASSERT(py_extension, "Invalid argument.");
27✔
65
  TEN_ASSERT(ten_py_extension_check_integrity(py_extension, true),
27✔
66
             "Invalid argument.");
27✔
67

68
  ten_py_ten_env_t *py_ten_env = ten_py_ten_env_wrap(ten_env);
27✔
69
  py_extension->py_ten_env = (PyObject *)py_ten_env;
27✔
70

71
  py_ten_env->c_ten_env_proxy = ten_env_proxy_create(ten_env, 1, NULL);
27✔
72
  TEN_ASSERT(py_ten_env->c_ten_env_proxy &&
27✔
73
                 ten_env_proxy_check_integrity(py_ten_env->c_ten_env_proxy),
27✔
74
             "Invalid argument.");
27✔
75

76
  PyObject *py_res =
27✔
77
      PyObject_CallMethod((PyObject *)py_extension, "_proxy_on_configure", "O",
27✔
78
                          py_ten_env->actual_py_ten_env);
27✔
79
  Py_XDECREF(py_res);
27✔
80

81
  bool err_occurred = ten_py_check_and_clear_py_error();
27✔
82
  TEN_ASSERT(!err_occurred, "Should not happen.");
27✔
83

84
  // We should release the GIL but not destroy the PyThreadState. The
85
  // PyThreadState will not be released until the last extension calls
86
  // 'on_deinit_done' in the group.
87
  py_ten_env->py_thread_state = ten_py_eval_save_thread();
27✔
88

89
  py_ten_env->need_to_release_gil_state = true;
27✔
90
}
27✔
91

92
static void proxy_on_init(ten_extension_t *extension, ten_env_t *ten_env) {
27✔
93
  TEN_ASSERT(extension, "Invalid argument.");
27✔
94
  TEN_ASSERT(ten_extension_check_integrity(extension, true),
27✔
95
             "Invalid argument.");
27✔
96
  TEN_ASSERT(ten_env, "Invalid argument.");
27✔
97
  TEN_ASSERT(ten_env_check_integrity(ten_env, true), "Invalid argument.");
27✔
98

99
  // About to call the Python function, so it's necessary to ensure that the GIL
100
  // has been acquired.
101
  PyGILState_STATE prev_state = ten_py_gil_state_ensure_internal();
27✔
102
  // This function can only be called on the native thread not a Python
103
  // thread.
104
  TEN_ASSERT(prev_state == PyGILState_UNLOCKED,
27✔
105
             "The GIL should not be help by the extension thread now.");
27✔
106

107
  ten_py_extension_t *py_extension =
27✔
108
      (ten_py_extension_t *)ten_binding_handle_get_me_in_target_lang(
27✔
109
          (ten_binding_handle_t *)extension);
27✔
110
  TEN_ASSERT(py_extension, "Invalid argument.");
27✔
111
  TEN_ASSERT(ten_py_extension_check_integrity(py_extension, true),
27✔
112
             "Invalid argument.");
27✔
113

114
  PyObject *py_ten_env = py_extension->py_ten_env;
27✔
115
  TEN_ASSERT(py_ten_env, "Should not happen.");
27✔
116

117
  PyObject *py_res =
27✔
118
      PyObject_CallMethod((PyObject *)py_extension, "_proxy_on_init", "O",
27✔
119
                          ((ten_py_ten_env_t *)py_ten_env)->actual_py_ten_env);
27✔
120
  Py_XDECREF(py_res);
27✔
121

122
  bool err_occurred = ten_py_check_and_clear_py_error();
27✔
123
  TEN_ASSERT(!err_occurred, "Should not happen.");
27✔
124

125
  ten_py_gil_state_release_internal(prev_state);
27✔
126
}
27✔
127

128
static void proxy_on_start(ten_extension_t *extension, ten_env_t *ten_env) {
26✔
129
  TEN_ASSERT(extension, "Invalid argument.");
26✔
130
  TEN_ASSERT(ten_extension_check_integrity(extension, true),
26✔
131
             "Invalid argument.");
26✔
132
  TEN_ASSERT(ten_env, "Invalid argument.");
26✔
133
  TEN_ASSERT(ten_env_check_integrity(ten_env, true), "Invalid argument.");
26✔
134

135
  // About to call the Python function, so it's necessary to ensure that the GIL
136
  // has been acquired.
137
  PyGILState_STATE prev_state = ten_py_gil_state_ensure_internal();
26✔
138
  TEN_ASSERT(prev_state == PyGILState_UNLOCKED,
26✔
139
             "The GIL should not be help by the extension thread now.");
26✔
140

141
  ten_py_extension_t *py_extension =
26✔
142
      (ten_py_extension_t *)ten_binding_handle_get_me_in_target_lang(
26✔
143
          (ten_binding_handle_t *)extension);
26✔
144
  TEN_ASSERT(py_extension, "Invalid argument.");
26✔
145
  TEN_ASSERT(ten_py_extension_check_integrity(py_extension, true),
26✔
146
             "Invalid argument.");
26✔
147

148
  PyObject *py_ten_env = py_extension->py_ten_env;
26✔
149
  TEN_ASSERT(py_ten_env, "Should not happen.");
26✔
150

151
  PyObject *py_res =
26✔
152
      PyObject_CallMethod((PyObject *)py_extension, "_proxy_on_start", "O",
26✔
153
                          ((ten_py_ten_env_t *)py_ten_env)->actual_py_ten_env);
26✔
154
  Py_XDECREF(py_res);
26✔
155

156
  bool err_occurred = ten_py_check_and_clear_py_error();
26✔
157
  TEN_ASSERT(!err_occurred, "Should not happen.");
26✔
158

159
  ten_py_gil_state_release_internal(prev_state);
26✔
160
}
26✔
161

162
static void proxy_on_stop(ten_extension_t *extension, ten_env_t *ten_env) {
27✔
163
  TEN_ASSERT(extension, "Invalid argument.");
27✔
164
  TEN_ASSERT(ten_extension_check_integrity(extension, true),
27✔
165
             "Invalid argument.");
27✔
166
  TEN_ASSERT(ten_env, "Invalid argument.");
27✔
167
  TEN_ASSERT(ten_env_check_integrity(ten_env, true), "Invalid argument.");
27✔
168

169
  // About to call the Python function, so it's necessary to ensure that the GIL
170
  // has been acquired.
171
  PyGILState_STATE prev_state = ten_py_gil_state_ensure_internal();
27✔
172
  TEN_ASSERT(prev_state == PyGILState_UNLOCKED,
27✔
173
             "The GIL should not be help by the extension thread now.");
27✔
174

175
  ten_py_extension_t *py_extension =
27✔
176
      (ten_py_extension_t *)ten_binding_handle_get_me_in_target_lang(
27✔
177
          (ten_binding_handle_t *)extension);
27✔
178
  TEN_ASSERT(py_extension, "Invalid argument.");
27✔
179
  TEN_ASSERT(ten_py_extension_check_integrity(py_extension, true),
27✔
180
             "Invalid argument.");
27✔
181

182
  PyObject *py_ten_env = py_extension->py_ten_env;
27✔
183
  TEN_ASSERT(py_ten_env, "Should not happen.");
27✔
184

185
  PyObject *py_res =
27✔
186
      PyObject_CallMethod((PyObject *)py_extension, "_proxy_on_stop", "O",
27✔
187
                          ((ten_py_ten_env_t *)py_ten_env)->actual_py_ten_env);
27✔
188
  Py_XDECREF(py_res);
27✔
189

190
  bool err_occurred = ten_py_check_and_clear_py_error();
27✔
191
  TEN_ASSERT(!err_occurred, "Should not happen.");
27✔
192

193
  ten_py_gil_state_release_internal(prev_state);
27✔
194
}
27✔
195

196
static void proxy_on_deinit(ten_extension_t *extension, ten_env_t *ten_env) {
27✔
197
  TEN_ASSERT(extension, "Invalid argument.");
27✔
198
  TEN_ASSERT(ten_extension_check_integrity(extension, true),
27✔
199
             "Invalid argument.");
27✔
200
  TEN_ASSERT(ten_env, "Invalid argument.");
27✔
201
  TEN_ASSERT(ten_env_check_integrity(ten_env, true), "Invalid argument.");
27✔
202

203
  // About to call the Python function, so it's necessary to ensure that the GIL
204
  // has been acquired.
205
  PyGILState_STATE prev_state = ten_py_gil_state_ensure_internal();
27✔
206
  TEN_ASSERT(prev_state == PyGILState_UNLOCKED,
27✔
207
             "The GIL should not be help by the extension thread now.");
27✔
208

209
  ten_py_extension_t *py_extension =
27✔
210
      (ten_py_extension_t *)ten_binding_handle_get_me_in_target_lang(
27✔
211
          (ten_binding_handle_t *)extension);
27✔
212
  TEN_ASSERT(py_extension, "Invalid argument.");
27✔
213
  TEN_ASSERT(ten_py_extension_check_integrity(py_extension, true),
27✔
214
             "Invalid argument.");
27✔
215

216
  PyObject *py_ten_env = py_extension->py_ten_env;
27✔
217
  TEN_ASSERT(py_ten_env, "Should not happen.");
27✔
218

219
  PyObject *py_res =
27✔
220
      PyObject_CallMethod((PyObject *)py_extension, "_proxy_on_deinit", "O",
27✔
221
                          ((ten_py_ten_env_t *)py_ten_env)->actual_py_ten_env);
27✔
222
  Py_XDECREF(py_res);
27✔
223

224
  bool err_occurred = ten_py_check_and_clear_py_error();
27✔
225
  TEN_ASSERT(!err_occurred, "Should not happen.");
27✔
226

227
  ten_py_gil_state_release_internal(prev_state);
27✔
228
}
27✔
229

230
static void proxy_on_cmd(ten_extension_t *extension, ten_env_t *ten_env,
231
                         ten_shared_ptr_t *cmd) {
26✔
232
  TEN_ASSERT(extension, "Invalid argument.");
26✔
233
  TEN_ASSERT(ten_extension_check_integrity(extension, true),
26✔
234
             "Invalid argument.");
26✔
235
  TEN_ASSERT(ten_env, "Invalid argument.");
26✔
236
  TEN_ASSERT(ten_env_check_integrity(ten_env, true), "Invalid argument.");
26✔
237
  TEN_ASSERT(cmd, "Invalid argument.");
26✔
238
  TEN_ASSERT(ten_msg_check_integrity(cmd), "Invalid argument.");
26✔
239

240
  // About to call the Python function, so it's necessary to ensure that the GIL
241
  // has been acquired.
242
  PyGILState_STATE prev_state = ten_py_gil_state_ensure_internal();
26✔
243

244
  ten_py_extension_t *py_extension =
26✔
245
      (ten_py_extension_t *)ten_binding_handle_get_me_in_target_lang(
26✔
246
          (ten_binding_handle_t *)extension);
26✔
247
  TEN_ASSERT(py_extension, "Invalid argument.");
26✔
248
  TEN_ASSERT(ten_py_extension_check_integrity(py_extension, true),
26✔
249
             "Invalid argument.");
26✔
250

251
  PyObject *py_ten_env = py_extension->py_ten_env;
26✔
252
  TEN_ASSERT(py_ten_env, "Should not happen.");
26✔
253

254
  ten_py_cmd_t *py_cmd = ten_py_cmd_wrap(cmd);
26✔
255

256
  PyObject *py_res = PyObject_CallMethod(
26✔
257
      (PyObject *)py_extension, "_proxy_on_cmd", "OO",
26✔
258
      ((ten_py_ten_env_t *)py_ten_env)->actual_py_ten_env, py_cmd);
26✔
259
  Py_XDECREF(py_res);
26✔
260

261
  bool err_occurred = ten_py_check_and_clear_py_error();
26✔
262
  TEN_ASSERT(!err_occurred, "Should not happen.");
26✔
263

264
  ten_py_cmd_invalidate(py_cmd);
26✔
265

266
  ten_py_gil_state_release_internal(prev_state);
26✔
267
}
26✔
268

269
static void proxy_on_data(ten_extension_t *extension, ten_env_t *ten_env,
270
                          ten_shared_ptr_t *data) {
5✔
271
  TEN_ASSERT(extension, "Invalid argument.");
5✔
272
  TEN_ASSERT(ten_extension_check_integrity(extension, true),
5✔
273
             "Invalid argument.");
5✔
274
  TEN_ASSERT(ten_env, "Invalid argument.");
5✔
275
  TEN_ASSERT(ten_env_check_integrity(ten_env, true), "Invalid argument.");
5✔
276
  TEN_ASSERT(data, "Invalid argument.");
5✔
277
  TEN_ASSERT(ten_msg_check_integrity(data), "Invalid argument.");
5✔
278

279
  // About to call the Python function, so it's necessary to ensure that the GIL
280
  // has been acquired.
281
  PyGILState_STATE prev_state = ten_py_gil_state_ensure_internal();
5✔
282

283
  ten_py_extension_t *py_extension =
5✔
284
      (ten_py_extension_t *)ten_binding_handle_get_me_in_target_lang(
5✔
285
          (ten_binding_handle_t *)extension);
5✔
286
  TEN_ASSERT(py_extension, "Invalid argument.");
5✔
287
  TEN_ASSERT(ten_py_extension_check_integrity(py_extension, true),
5✔
288
             "Invalid argument.");
5✔
289

290
  PyObject *py_ten_env = py_extension->py_ten_env;
5✔
291
  TEN_ASSERT(py_ten_env, "Should not happen.");
5✔
292

293
  ten_py_data_t *py_data = ten_py_data_wrap(data);
5✔
294

295
  PyObject *py_res = PyObject_CallMethod(
5✔
296
      (PyObject *)py_extension, "_proxy_on_data", "OO",
5✔
297
      ((ten_py_ten_env_t *)py_ten_env)->actual_py_ten_env, py_data);
5✔
298
  Py_XDECREF(py_res);
5✔
299

300
  bool err_occurred = ten_py_check_and_clear_py_error();
5✔
301
  TEN_ASSERT(!err_occurred, "Should not happen.");
5✔
302

303
  ten_py_data_invalidate(py_data);
5✔
304

305
  ten_py_gil_state_release_internal(prev_state);
5✔
306
}
5✔
307

308
static void proxy_on_audio_frame(ten_extension_t *extension, ten_env_t *ten_env,
309
                                 ten_shared_ptr_t *audio_frame) {
5✔
310
  TEN_ASSERT(extension, "Invalid argument.");
5✔
311
  TEN_ASSERT(ten_extension_check_integrity(extension, true),
5✔
312
             "Invalid argument.");
5✔
313
  TEN_ASSERT(ten_env, "Invalid argument.");
5✔
314
  TEN_ASSERT(ten_env_check_integrity(ten_env, true), "Invalid argument.");
5✔
315
  TEN_ASSERT(audio_frame, "Invalid argument.");
5✔
316
  TEN_ASSERT(ten_msg_check_integrity(audio_frame), "Invalid argument.");
5✔
317

318
  // About to call the Python function, so it's necessary to ensure that the GIL
319
  // has been acquired.
320
  PyGILState_STATE prev_state = ten_py_gil_state_ensure_internal();
5✔
321

322
  ten_py_extension_t *py_extension =
5✔
323
      (ten_py_extension_t *)ten_binding_handle_get_me_in_target_lang(
5✔
324
          (ten_binding_handle_t *)extension);
5✔
325
  TEN_ASSERT(py_extension, "Invalid argument.");
5✔
326
  TEN_ASSERT(ten_py_extension_check_integrity(py_extension, true),
5✔
327
             "Invalid argument.");
5✔
328

329
  PyObject *py_ten_env = py_extension->py_ten_env;
5✔
330
  TEN_ASSERT(py_ten_env, "Should not happen.");
5✔
331

332
  ten_py_audio_frame_t *py_audio_frame = ten_py_audio_frame_wrap(audio_frame);
5✔
333

334
  PyObject *py_res = PyObject_CallMethod(
5✔
335
      (PyObject *)py_extension, "_proxy_on_audio_frame", "OO",
5✔
336
      ((ten_py_ten_env_t *)py_ten_env)->actual_py_ten_env, py_audio_frame);
5✔
337
  Py_XDECREF(py_res);
5✔
338

339
  bool err_occurred = ten_py_check_and_clear_py_error();
5✔
340
  TEN_ASSERT(!err_occurred, "Should not happen.");
5✔
341

342
  ten_py_audio_frame_invalidate(py_audio_frame);
5✔
343

344
  ten_py_gil_state_release_internal(prev_state);
5✔
345
}
5✔
346

347
static void proxy_on_video_frame(ten_extension_t *extension, ten_env_t *ten_env,
348
                                 ten_shared_ptr_t *video_frame) {
5✔
349
  TEN_ASSERT(extension, "Invalid argument.");
5✔
350
  TEN_ASSERT(ten_extension_check_integrity(extension, true),
5✔
351
             "Invalid argument.");
5✔
352
  TEN_ASSERT(ten_env, "Invalid argument.");
5✔
353
  TEN_ASSERT(ten_env_check_integrity(ten_env, true), "Invalid argument.");
5✔
354
  TEN_ASSERT(video_frame, "Invalid argument.");
5✔
355
  TEN_ASSERT(ten_msg_check_integrity(video_frame), "Invalid argument.");
5✔
356

357
  // About to call the Python function, so it's necessary to ensure that the GIL
358
  // has been acquired.
359
  PyGILState_STATE prev_state = ten_py_gil_state_ensure_internal();
5✔
360

361
  PyObject *py_extension = (PyObject *)ten_binding_handle_get_me_in_target_lang(
5✔
362
      (ten_binding_handle_t *)extension);
5✔
363
  PyObject *py_ten_env = ((ten_py_extension_t *)py_extension)->py_ten_env;
5✔
364
  ten_py_video_frame_t *py_video_frame = ten_py_video_frame_wrap(video_frame);
5✔
365

366
  PyObject *py_res = PyObject_CallMethod(
5✔
367
      py_extension, "_proxy_on_video_frame", "OO",
5✔
368
      ((ten_py_ten_env_t *)py_ten_env)->actual_py_ten_env, py_video_frame);
5✔
369
  Py_XDECREF(py_res);
5✔
370

371
  bool err_occurred = ten_py_check_and_clear_py_error();
5✔
372
  TEN_ASSERT(!err_occurred, "Should not happen.");
5✔
373

374
  ten_py_video_frame_invalidate(py_video_frame);
5✔
375

376
  ten_py_gil_state_release_internal(prev_state);
5✔
377
}
5✔
378

379
static PyObject *ten_py_extension_create(PyTypeObject *type, PyObject *py_name,
380
                                         TEN_UNUSED PyObject *kwds) {
27✔
381
#if defined(_DEBUG)
27✔
382
  const char *enable_intentional_memory_leak =
27✔
383
      // NOLINTNEXTLINE(concurrency-mt-unsafe)
384
      getenv("TEN_ENABLE_INTENTIONAL_MEMORY_LEAK");
27✔
385
  if (enable_intentional_memory_leak &&
27✔
386
      !strcmp(enable_intentional_memory_leak, "true")) {
27✔
387
    TEN_LOGD(
×
388
        "TEN_ENABLE_INTENTIONAL_MEMORY_LEAK is defined. Memory leak is "
×
389
        "happening in python extension creation.");
×
390
    TEN_MALLOC(10);
×
391
  }
×
392
#endif
27✔
393

394
  ten_py_extension_t *py_extension =
27✔
395
      (ten_py_extension_t *)type->tp_alloc(type, 0);
27✔
396
  if (!py_extension) {
27✔
397
    TEN_ASSERT(0, "Failed to allocate Python extension.");
×
398

399
    return ten_py_raise_py_memory_error_exception(
×
400
        "Failed to allocate memory for ten_py_extension_t");
×
401
  }
×
402

403
  const char *name = NULL;
27✔
404
  if (!PyArg_ParseTuple(py_name, "s", &name)) {
27✔
405
    return ten_py_raise_py_type_error_exception("Invalid argument.");
×
406
  }
×
407

408
  ten_signature_set(&py_extension->signature, TEN_PY_EXTENSION_SIGNATURE);
27✔
409

410
  py_extension->c_extension = ten_extension_create(
27✔
411
      name, proxy_on_configure, proxy_on_init, proxy_on_start, proxy_on_stop,
27✔
412
      proxy_on_deinit, proxy_on_cmd, proxy_on_data, proxy_on_audio_frame,
27✔
413
      proxy_on_video_frame, NULL);
27✔
414
  TEN_ASSERT(py_extension->c_extension, "Should not happen.");
27✔
415

416
  ten_binding_handle_set_me_in_target_lang(
27✔
417
      &py_extension->c_extension->binding_handle, py_extension);
27✔
418
  py_extension->py_ten_env = Py_None;
27✔
419

420
  return (PyObject *)py_extension;
27✔
421
}
27✔
422

423
static void ten_py_extension_destroy(PyObject *self) {
27✔
424
  ten_py_extension_t *py_extension = (ten_py_extension_t *)self;
27✔
425

426
  // TEN_NOLINTNEXTLINE(thread-check)
427
  // thread-check: In TEN world, the destroy operations need to be performed in
428
  // any threads.
429
  TEN_ASSERT(py_extension, "Invalid argument.");
27✔
430
  TEN_ASSERT(ten_py_extension_check_integrity(py_extension, false),
27✔
431
             "Invalid argument.");
27✔
432

433
  ten_extension_destroy(py_extension->c_extension);
27✔
434
  py_extension->c_extension = NULL;
27✔
435

436
  Py_TYPE(self)->tp_free(self);
27✔
437
}
27✔
438

439
PyTypeObject *ten_py_extension_py_type(void) {
32✔
440
  static PyGetSetDef py_extension_type_properties[] = {
32✔
441
      {NULL, NULL, NULL, NULL, NULL}};
32✔
442

443
  static PyMethodDef py_extension_type_methods[] = {
32✔
444
      {"on_init", stub_on_callback, METH_VARARGS, NULL},
32✔
445
      {"on_start", stub_on_callback, METH_VARARGS, NULL},
32✔
446
      {"on_stop", stub_on_callback, METH_VARARGS, NULL},
32✔
447
      {"on_deinit", stub_on_callback, METH_VARARGS, NULL},
32✔
448
      {"on_cmd", stub_on_callback, METH_VARARGS, NULL},
32✔
449
      {"on_data", stub_on_callback, METH_VARARGS, NULL},
32✔
450
      {"on_audio_frame", stub_on_callback, METH_VARARGS, NULL},
32✔
451
      {"on_video_frame", stub_on_callback, METH_VARARGS, NULL},
32✔
452
      {NULL, NULL, 0, NULL},
32✔
453
  };
32✔
454

455
  static PyTypeObject py_extension_type = {
32✔
456
      PyVarObject_HEAD_INIT(NULL, 0).tp_name =
32✔
457
          "libten_runtime_python._Extension",
32✔
458
      .tp_doc = PyDoc_STR("Extension"),
32✔
459
      .tp_basicsize = sizeof(ten_py_extension_t),
32✔
460
      .tp_itemsize = 0,
32✔
461
      .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
32✔
462
      .tp_new = ten_py_extension_create,
32✔
463
      .tp_init = NULL,
32✔
464
      .tp_dealloc = ten_py_extension_destroy,
32✔
465
      .tp_getset = py_extension_type_properties,
32✔
466
      .tp_methods = py_extension_type_methods,
32✔
467
  };
32✔
468

469
  return &py_extension_type;
32✔
470
}
32✔
471

472
bool ten_py_extension_init_for_module(PyObject *module) {
5✔
473
  PyTypeObject *py_type = ten_py_extension_py_type();
5✔
474
  if (PyType_Ready(py_type) < 0) {
5✔
475
    ten_py_raise_py_system_error_exception(
×
476
        "Python Extension class is not ready.");
×
477

478
    TEN_ASSERT(0, "Should not happen.");
×
479
    return false;
×
480
  }
×
481

482
  if (PyModule_AddObjectRef(module, "_Extension", (PyObject *)py_type) < 0) {
5✔
483
    ten_py_raise_py_import_error_exception(
×
484
        "Failed to add Python type to module.");
×
485

486
    TEN_ASSERT(0, "Should not happen.");
×
487
    return false;
×
488
  }
×
489

490
  return true;
5✔
491
}
5✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc