Skip to content

Commit 73a7a06

Browse files
committed
python: lint and avoid calling Py_FinalizeEx (see comment in the source file)
1 parent 10a268a commit 73a7a06

File tree

3 files changed

+21
-7
lines changed

3 files changed

+21
-7
lines changed

TOOLS/python/acompressor.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
# of the parameters.
66

77
import re
8-
import typing
98
from mpvclient import mpv # type: ignore
109

1110
o = dict(
@@ -73,7 +72,7 @@ def show_osd(filter): # noqa: A002
7372
mpv.commandv("show-text", "Dynamic range compressor: disabled", o["osd_timeout"])
7473
return
7574

76-
pretty: typing.Union[str, list] = []
75+
pretty: str | list = []
7776
for param in params:
7877
value = parse_value(filter["params"][param["name"]])
7978
if not (param["hide_default"] and value == o["default_" + param["name"]]): # type: ignore

player/main.c

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,24 @@ void mp_destroy(struct MPContext *mpctx)
213213
uninit_libav(mpctx->global);
214214

215215
#if HAVE_PYTHON
216-
if (mpctx->opts->enable_python) {
217-
if (Py_IsInitialized()) Py_FinalizeEx();
218-
}
216+
// Do NOT call Py_FinalizeEx() here.
217+
//
218+
// Subinterpreters use OWN_GIL + use_main_obmalloc=0, giving each script
219+
// its own isolated pymalloc arena. Py_EndInterpreter() (called per
220+
// script in end_interpreter()) frees that arena. However, Py_AtExit()
221+
// handlers registered by C extension modules (e.g. _ssl via
222+
// requests/urllib3) are invoked by Py_FinalizeEx() in the main
223+
// interpreter's context. If such a handler holds a reference to a
224+
// Python object that lived in the now-freed subinterpreter arena and
225+
// tries to Py_XDECREF / PyObject_Free it through the main allocator,
226+
// glibc reports "free(): invalid pointer" and aborts.
227+
//
228+
// Skipping Py_FinalizeEx() is safe here: all script subinterpreters are
229+
// already finalized by the time we reach this point, and the OS reclaims
230+
// all remaining memory on process exit.
231+
// if (mpctx->opts->enable_python) {
232+
// if (Py_IsInitialized()) Py_FinalizeEx();
233+
// }
219234
#endif
220235

221236
mp_msg_uninit(mpctx->global);

player/python/defaults.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ def typeconv(self, desttypeval, val):
5252
val = None
5353
return val
5454

55-
def read_options(self, options: dict, identifier: typing.Optional[str] = None,
56-
on_update: typing.Optional[typing.Callable[[typing.Any], typing.Any]] = None,
55+
def read_options(self, options: dict, identifier: str | None = None,
56+
on_update: typing.Callable[[typing.Any], typing.Any] | None = None,
5757
):
5858
option_types = options.copy()
5959

0 commit comments

Comments
 (0)