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

Jaded-Encoding-Thaumaturgy / vs-tools / 13117979512

03 Feb 2025 04:21PM UTC coverage: 58.155% (-0.007%) from 58.162%
13117979512

Pull #171

github

web-flow
Merge 598b737da into cca93572c
Pull Request #171: Use `with x.get_frame` instead of `x.get_frame` globally

5 of 13 new or added lines in 7 files covered. (38.46%)

29 existing lines in 1 file now uncovered.

2831 of 4868 relevant lines covered (58.16%)

0.58 hits per line

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

36.5
/vstools/utils/vs_proxy.py
1
from __future__ import annotations
1✔
2

3
import builtins
1✔
4
import gc
1✔
5
import weakref
1✔
6
from ctypes import Structure
1✔
7
from inspect import Parameter, Signature
1✔
8
from logging import NOTSET as LogLevelUnset
1✔
9
from logging import Handler, LogRecord
1✔
10
from math import ceil
1✔
11
from multiprocessing import cpu_count
1✔
12
from types import UnionType
1✔
13
from typing import TYPE_CHECKING, Any, Callable, Iterable, NoReturn
1✔
14
from weakref import ReferenceType
1✔
15

16
import vapoursynth as vs
1✔
17
from vapoursynth import (
1✔
18
    AUDIO, BACK_CENTER, BACK_LEFT, BACK_RIGHT, CHROMA_BOTTOM, CHROMA_BOTTOM_LEFT, CHROMA_CENTER, CHROMA_LEFT,
19
    CHROMA_TOP, CHROMA_TOP_LEFT, FIELD_BOTTOM, FIELD_PROGRESSIVE, FIELD_TOP, FLOAT, FRONT_CENTER, FRONT_LEFT,
20
    FRONT_LEFT_OF_CENTER, FRONT_RIGHT, FRONT_RIGHT_OF_CENTER, GRAY, INTEGER, LOW_FREQUENCY, LOW_FREQUENCY2,
21
    MATRIX_BT470_BG, MATRIX_BT709, MATRIX_BT2020_CL, MATRIX_BT2020_NCL, MATRIX_CHROMATICITY_DERIVED_CL,
22
    MATRIX_CHROMATICITY_DERIVED_NCL, MATRIX_FCC, MATRIX_ICTCP, MATRIX_RGB, MATRIX_ST170_M, MATRIX_UNSPECIFIED,
23
    MATRIX_YCGCO, MESSAGE_TYPE_CRITICAL, MESSAGE_TYPE_DEBUG, MESSAGE_TYPE_FATAL, MESSAGE_TYPE_INFORMATION,
24
    MESSAGE_TYPE_WARNING, NONE, PRIMARIES_BT470_BG, PRIMARIES_BT470_M, PRIMARIES_BT709, PRIMARIES_BT2020,
25
    PRIMARIES_EBU3213_E, PRIMARIES_FILM, PRIMARIES_ST170_M, PRIMARIES_ST240_M, PRIMARIES_ST428, PRIMARIES_ST431_2,
26
    PRIMARIES_ST432_1, PRIMARIES_UNSPECIFIED, RANGE_FULL, RANGE_LIMITED, RGB, SIDE_LEFT, SIDE_RIGHT, STEREO_LEFT,
27
    STEREO_RIGHT, SURROUND_DIRECT_LEFT, SURROUND_DIRECT_RIGHT, TOP_BACK_CENTER, TOP_BACK_LEFT, TOP_BACK_RIGHT,
28
    TOP_CENTER, TOP_FRONT_CENTER, TOP_FRONT_LEFT, TOP_FRONT_RIGHT, TRANSFER_ARIB_B67, TRANSFER_BT470_BG,
29
    TRANSFER_BT470_M, TRANSFER_BT601, TRANSFER_BT709, TRANSFER_BT2020_10, TRANSFER_BT2020_12, TRANSFER_IEC_61966_2_1,
30
    TRANSFER_IEC_61966_2_4, TRANSFER_LINEAR, TRANSFER_LOG_100, TRANSFER_LOG_316, TRANSFER_ST240_M, TRANSFER_ST2084,
31
    TRANSFER_UNSPECIFIED, UNDEFINED, VIDEO, WIDE_LEFT, WIDE_RIGHT, YUV, AudioChannels, AudioFrame, AudioNode,
32
    ChromaLocation, ColorFamily, ColorPrimaries, ColorRange, Core, CoreCreationFlags, Environment, EnvironmentData,
33
    EnvironmentPolicy, EnvironmentPolicyAPI, Error, FieldBased, FilterMode, FrameProps, FramePtr, Func, FuncData,
34
    Function, LogHandle, MatrixCoefficients, MediaType, MessageType, Plugin, RawFrame, RawNode, SampleType,
35
    TransferCharacteristics, VideoFormat, VideoFrame, VideoNode, VideoOutputTuple, __api_version__, __version__,
36
    _CoreProxy, clear_output, clear_outputs, get_current_environment, get_output, get_outputs, has_policy,
37
    register_on_destroy, register_policy, unregister_on_destroy
38
)
39

40
from ..exceptions import CustomRuntimeError
1✔
41
from .vs_enums import (
1✔
42
    GRAY8, GRAY9, GRAY10, GRAY11, GRAY12, GRAY13, GRAY14, GRAY15, GRAY16, GRAY17, GRAY18, GRAY19, GRAY20, GRAY21,
43
    GRAY22, GRAY23, GRAY24, GRAY25, GRAY26, GRAY27, GRAY28, GRAY29, GRAY30, GRAY31, GRAY32, GRAYH, GRAYS, RGB24, RGB27,
44
    RGB30, RGB33, RGB36, RGB39, RGB42, RGB45, RGB48, RGB51, RGB54, RGB57, RGB60, RGB63, RGB66, RGB69, RGB72, RGB75,
45
    RGB78, RGB81, RGB84, RGB87, RGB90, RGB93, RGB96, RGBH, RGBS, YUV410P8, YUV410P9, YUV410P10, YUV410P11, YUV410P12,
46
    YUV410P13, YUV410P14, YUV410P15, YUV410P16, YUV410P17, YUV410P18, YUV410P19, YUV410P20, YUV410P21, YUV410P22,
47
    YUV410P23, YUV410P24, YUV410P25, YUV410P26, YUV410P27, YUV410P28, YUV410P29, YUV410P30, YUV410P31, YUV410P32,
48
    YUV410PH, YUV410PS, YUV411P8, YUV411P9, YUV411P10, YUV411P11, YUV411P12, YUV411P13, YUV411P14, YUV411P15, YUV411P16,
49
    YUV411P17, YUV411P18, YUV411P19, YUV411P20, YUV411P21, YUV411P22, YUV411P23, YUV411P24, YUV411P25, YUV411P26,
50
    YUV411P27, YUV411P28, YUV411P29, YUV411P30, YUV411P31, YUV411P32, YUV411PH, YUV411PS, YUV420P8, YUV420P9, YUV420P10,
51
    YUV420P11, YUV420P12, YUV420P13, YUV420P14, YUV420P15, YUV420P16, YUV420P17, YUV420P18, YUV420P19, YUV420P20,
52
    YUV420P21, YUV420P22, YUV420P23, YUV420P24, YUV420P25, YUV420P26, YUV420P27, YUV420P28, YUV420P29, YUV420P30,
53
    YUV420P31, YUV420P32, YUV420PH, YUV420PS, YUV422P8, YUV422P9, YUV422P10, YUV422P11, YUV422P12, YUV422P13, YUV422P14,
54
    YUV422P15, YUV422P16, YUV422P17, YUV422P18, YUV422P19, YUV422P20, YUV422P21, YUV422P22, YUV422P23, YUV422P24,
55
    YUV422P25, YUV422P26, YUV422P27, YUV422P28, YUV422P29, YUV422P30, YUV422P31, YUV422P32, YUV422PH, YUV422PS,
56
    YUV440P8, YUV440P9, YUV440P10, YUV440P11, YUV440P12, YUV440P13, YUV440P14, YUV440P15, YUV440P16, YUV440P17,
57
    YUV440P18, YUV440P19, YUV440P20, YUV440P21, YUV440P22, YUV440P23, YUV440P24, YUV440P25, YUV440P26, YUV440P27,
58
    YUV440P28, YUV440P29, YUV440P30, YUV440P31, YUV440P32, YUV440PH, YUV440PS, YUV444P8, YUV444P9, YUV444P10, YUV444P11,
59
    YUV444P12, YUV444P13, YUV444P14, YUV444P15, YUV444P16, YUV444P17, YUV444P18, YUV444P19, YUV444P20, YUV444P21,
60
    YUV444P22, YUV444P23, YUV444P24, YUV444P25, YUV444P26, YUV444P27, YUV444P28, YUV444P29, YUV444P30, YUV444P31,
61
    YUV444P32, YUV444PH, YUV444PS, PresetFormat, PresetVideoFormat, VSPresetVideoFormat
62
)
63

64
__all__ = [
1✔
65
    'AUDIO', 'AudioChannels', 'AudioFrame', 'AudioNode', 'BACK_CENTER', 'BACK_LEFT', 'BACK_RIGHT', 'CHROMA_BOTTOM',
66
    'CHROMA_BOTTOM_LEFT', 'CHROMA_CENTER', 'CHROMA_LEFT', 'CHROMA_TOP', 'CHROMA_TOP_LEFT', 'CallbackData',
67
    'ChromaLocation', 'ColorFamily', 'ColorPrimaries', 'ColorRange', 'Core', 'CoreCreationFlags', 'Environment',
68
    'EnvironmentData', 'EnvironmentPolicy', 'EnvironmentPolicyAPI', 'Error', 'FIELD_BOTTOM', 'FIELD_PROGRESSIVE',
69
    'FIELD_TOP', 'FLOAT', 'FRONT_CENTER', 'FRONT_LEFT', 'FRONT_LEFT_OF_CENTER', 'FRONT_RIGHT', 'FRONT_RIGHT_OF_CENTER',
70
    'FieldBased', 'FilterMode', 'FrameProps', 'FramePtr', 'Func', 'FuncData', 'Function', 'GRAY', 'GRAY10', 'GRAY11',
71
    'GRAY12', 'GRAY13', 'GRAY14', 'GRAY15', 'GRAY16', 'GRAY17', 'GRAY18', 'GRAY19', 'GRAY20', 'GRAY21', 'GRAY22',
72
    'GRAY23', 'GRAY24', 'GRAY25', 'GRAY26', 'GRAY27', 'GRAY28', 'GRAY29', 'GRAY30', 'GRAY31', 'GRAY32', 'GRAY8',
73
    'GRAY9', 'GRAYH', 'GRAYS', 'INTEGER', 'LOW_FREQUENCY', 'LOW_FREQUENCY2', 'LogHandle', 'MATRIX_BT2020_CL',
74
    'MATRIX_BT2020_NCL', 'MATRIX_BT470_BG', 'MATRIX_BT709', 'MATRIX_CHROMATICITY_DERIVED_CL',
75
    'MATRIX_CHROMATICITY_DERIVED_NCL', 'MATRIX_FCC', 'MATRIX_ICTCP', 'MATRIX_RGB', 'MATRIX_ST170_M',
76
    'MATRIX_UNSPECIFIED', 'MATRIX_YCGCO', 'MESSAGE_TYPE_CRITICAL', 'MESSAGE_TYPE_DEBUG', 'MESSAGE_TYPE_FATAL',
77
    'MESSAGE_TYPE_INFORMATION', 'MESSAGE_TYPE_WARNING', 'MatrixCoefficients', 'MediaType', 'MessageType', 'NONE',
78
    'PRIMARIES_BT2020', 'PRIMARIES_BT470_BG', 'PRIMARIES_BT470_M', 'PRIMARIES_BT709', 'PRIMARIES_EBU3213_E',
79
    'PRIMARIES_FILM', 'PRIMARIES_ST170_M', 'PRIMARIES_ST240_M', 'PRIMARIES_ST428', 'PRIMARIES_ST431_2',
80
    'PRIMARIES_ST432_1', 'PRIMARIES_UNSPECIFIED', 'Plugin', 'PresetFormat', 'PresetVideoFormat',
81
    'PythonVSScriptLoggingBridge', 'RANGE_FULL',
82
    'RANGE_LIMITED', 'RGB', 'RGB24', 'RGB27', 'RGB30', 'RGB33', 'RGB36', 'RGB39', 'RGB42', 'RGB45', 'RGB48', 'RGB51',
83
    'RGB54', 'RGB57', 'RGB60', 'RGB63', 'RGB66', 'RGB69', 'RGB72', 'RGB75', 'RGB78', 'RGB81', 'RGB84', 'RGB87', 'RGB90',
84
    'RGB93', 'RGB96', 'RGBH', 'RGBS', 'RawFrame', 'RawNode', 'SIDE_LEFT', 'SIDE_RIGHT', 'STEREO_LEFT', 'STEREO_RIGHT',
85
    'SURROUND_DIRECT_LEFT', 'SURROUND_DIRECT_RIGHT', 'SampleType', 'StandaloneEnvironmentPolicy', 'TOP_BACK_CENTER',
86
    'TOP_BACK_LEFT', 'TOP_BACK_RIGHT', 'TOP_CENTER', 'TOP_FRONT_CENTER', 'TOP_FRONT_LEFT', 'TOP_FRONT_RIGHT',
87
    'TRANSFER_ARIB_B67', 'TRANSFER_BT2020_10', 'TRANSFER_BT2020_12', 'TRANSFER_BT470_BG', 'TRANSFER_BT470_M',
88
    'TRANSFER_BT601', 'TRANSFER_BT709', 'TRANSFER_IEC_61966_2_1', 'TRANSFER_IEC_61966_2_4', 'TRANSFER_LINEAR',
89
    'TRANSFER_LOG_100', 'TRANSFER_LOG_316', 'TRANSFER_ST2084', 'TRANSFER_ST240_M', 'TRANSFER_UNSPECIFIED',
90
    'TransferCharacteristics', 'UNDEFINED', 'VIDEO', 'VSScriptEnvironmentPolicy', 'VideoFormat', 'VideoFrame',
91
    'VideoNode', 'VideoOutputTuple', 'WIDE_LEFT', 'WIDE_RIGHT', 'YUV', 'YUV410P10', 'YUV410P11', 'YUV410P12',
92
    'YUV410P13', 'YUV410P14', 'YUV410P15', 'YUV410P16', 'YUV410P17', 'YUV410P18', 'YUV410P19', 'YUV410P20', 'YUV410P21',
93
    'YUV410P22', 'YUV410P23', 'YUV410P24', 'YUV410P25', 'YUV410P26', 'YUV410P27', 'YUV410P28', 'YUV410P29', 'YUV410P30',
94
    'YUV410P31', 'YUV410P32', 'YUV410P8', 'YUV410P9', 'YUV410PH', 'YUV410PS', 'YUV411P10', 'YUV411P11', 'YUV411P12',
95
    'YUV411P13', 'YUV411P14', 'YUV411P15', 'YUV411P16', 'YUV411P17', 'YUV411P18', 'YUV411P19', 'YUV411P20', 'YUV411P21',
96
    'YUV411P22', 'YUV411P23', 'YUV411P24', 'YUV411P25', 'YUV411P26', 'YUV411P27', 'YUV411P28', 'YUV411P29', 'YUV411P30',
97
    'YUV411P31', 'YUV411P32', 'YUV411P8', 'YUV411P9', 'YUV411PH', 'YUV411PS', 'YUV420P10', 'YUV420P11', 'YUV420P12',
98
    'YUV420P13', 'YUV420P14', 'YUV420P15', 'YUV420P16', 'YUV420P17', 'YUV420P18', 'YUV420P19', 'YUV420P20', 'YUV420P21',
99
    'YUV420P22', 'YUV420P23', 'YUV420P24', 'YUV420P25', 'YUV420P26', 'YUV420P27', 'YUV420P28', 'YUV420P29', 'YUV420P30',
100
    'YUV420P31', 'YUV420P32', 'YUV420P8', 'YUV420P9', 'YUV420PH', 'YUV420PS', 'YUV422P10', 'YUV422P11', 'YUV422P12',
101
    'YUV422P13', 'YUV422P14', 'YUV422P15', 'YUV422P16', 'YUV422P17', 'YUV422P18', 'YUV422P19', 'YUV422P20', 'YUV422P21',
102
    'YUV422P22', 'YUV422P23', 'YUV422P24', 'YUV422P25', 'YUV422P26', 'YUV422P27', 'YUV422P28', 'YUV422P29', 'YUV422P30',
103
    'YUV422P31', 'YUV422P32', 'YUV422P8', 'YUV422P9', 'YUV422PH', 'YUV422PS', 'YUV440P10', 'YUV440P11', 'YUV440P12',
104
    'YUV440P13', 'YUV440P14', 'YUV440P15', 'YUV440P16', 'YUV440P17', 'YUV440P18', 'YUV440P19', 'YUV440P20', 'YUV440P21',
105
    'YUV440P22', 'YUV440P23', 'YUV440P24', 'YUV440P25', 'YUV440P26', 'YUV440P27', 'YUV440P28', 'YUV440P29', 'YUV440P30',
106
    'YUV440P31', 'YUV440P32', 'YUV440P8', 'YUV440P9', 'YUV440PH', 'YUV440PS', 'YUV444P10', 'YUV444P11', 'YUV444P12',
107
    'YUV444P13', 'YUV444P14', 'YUV444P15', 'YUV444P16', 'YUV444P17', 'YUV444P18', 'YUV444P19', 'YUV444P20', 'YUV444P21',
108
    'YUV444P22', 'YUV444P23', 'YUV444P24', 'YUV444P25', 'YUV444P26', 'YUV444P27', 'YUV444P28', 'YUV444P29', 'YUV444P30',
109
    'YUV444P31', 'YUV444P32', 'YUV444P8', 'YUV444P9', 'YUV444PH', 'YUV444PS', '_CoreProxy', '__all__',
110
    '__api_version__', '__version__', 'CoreCreationFlags', 'ENABLE_GRAPH_INSPECTION', 'DISABLE_AUTO_LOADING',
111
    'DISABLE_LIBRARY_UNLOADING', 'ccfDisableAutoLoading', 'ccfDisableLibraryUnloading', 'ccfEnableGraphInspection',
112
    'clear_output', 'clear_outputs', 'construct_parameter', 'construct_signature', 'construct_type', 'core',
113
    'FilterMode', 'PARALLEL', 'PARALLEL_REQUESTS', 'UNORDERED', 'FRAME_STATE', 'fmFrameState', 'fmParallel',
114
    'fmParallelRequests', 'fmUnordered', 'get_current_environment', 'get_output', 'get_outputs', 'has_policy',
115
    'pyx_capi', 'register_on_creation', 'register_on_destroy', 'register_policy', 'try_enable_introspection',
116
    'unregister_on_creation', 'unregister_on_destroy', 'vs_file', 'clear_cache'
117
]
118

119

120
if not TYPE_CHECKING:
1✔
121
    import inspect
1✔
122
    import re
1✔
123
    import sys
1✔
124
    import warnings
1✔
125
    from pathlib import Path
1✔
126
    from types import ModuleType
1✔
127

128
    import __main__
1✔
129

130
    if not hasattr(__main__, '__file__') and '__vapoursynth__' not in sys.modules:
1✔
131
        first_stack = inspect.stack()[-1]
×
132

133
        sys.modules['__vapoursynth__'] = ModuleType('__vapoursynth__')
×
134

135
        cope = (Path.cwd() / first_stack.filename).resolve()
×
136

137
        first_stack = None
×
138

139
        sys.modules['__vapoursynth__'].__file__ = __main__.__file__ = str(cope)
×
140

141
        sys.path.append(str(cope.parent))
×
142

143
    warnings._add_filter('ignore', re.compile('.*smallest subnormal.*numpy.*'), Warning, None, 0, append=False)
1✔
144
    warnings._add_filter('ignore', re.compile('.*divide by zero.*'), Warning, None, 0, append=False)
1✔
145

146

147
def register_on_creation(callback: Callable[..., None], strict: bool = False) -> None:
1✔
148
    """Register a callback on every core creation."""
149

150
    core_on_creation_callbacks.update({id(callback): weakref.ref(callback)})
×
151

152
    if not strict and core.active:
×
153
        try:
×
154
            callback(core.core_id)
×
155
        except TypeError:
×
156
            callback()
×
157

158

159
def unregister_on_creation(callback: Callable[..., None]) -> None:
1✔
160
    """Unregister this callback from every core creation."""
161

162
    core_on_creation_callbacks.pop(id(callback), None)
×
163

164

165
def clear_cache() -> None:
1✔
166
    try:
×
167
        cache_size = int(core.max_cache_size)
×
168
        core.max_cache_size = 1
×
169
        try:
×
170
            for output in get_outputs().values():
×
171
                if isinstance(output, VideoOutputTuple):
×
NEW
172
                    output.clip.get_frame(0).close()
×
173
                    break
×
174
        except Exception:
×
NEW
175
            core.std.BlankClip().get_frame(0).close()
×
176
        core.max_cache_size = cache_size
×
177
    except Exception:
×
178
        ...
×
179

180

181
if TYPE_CHECKING:
1✔
182
    class FunctionProxyBase(Function):
×
183
        ...
×
184

185
    class PluginProxyBase(Plugin):
×
186
        ...
×
187

188
    class CoreProxyBase(Core):
×
189
        def __init__(self) -> None:
×
190
            ...
×
191

192
    class EnvironmentProxyBase(Environment):
×
193
        def __init__(self) -> None:
×
194
            ...
×
195
else:
196
    FunctionProxyBase = PluginProxyBase = CoreProxyBase = EnvironmentProxyBase = object
1✔
197

198

199
class FunctionProxy(FunctionProxyBase):
1✔
200
    def __init__(self, plugin: PluginProxy, func_name: str) -> None:
1✔
201
        self.__dict__['func_ref'] = (plugin, func_name)
×
202

203
    def __getattr__(self, name: str) -> Function:
1✔
204
        if name == '__isabstractmethod__':
×
205
            return False
×
206

207
        function = proxy_utils.get_vs_function(self)
×
208

209
        return getattr(function, name)
×
210

211
    def __call__(self, *args: Any, **kwargs: Any) -> Any:
1✔
212
        return proxy_utils.get_vs_function(self)(*args, **kwargs)
×
213

214

215
class PluginProxy(PluginProxyBase):
1✔
216
    def __init__(self, core: CoreProxy, namespace: str) -> None:
1✔
217
        self.__dict__['plugin_ref'] = (core, namespace)
×
218

219
    def __getattr__(self, name: str) -> Function:
1✔
220
        core, namespace = proxy_utils.get_core(self)
×
221

222
        if core.lazy and name not in vs.Plugin.__dict__:
×
223
            return FunctionProxy(self, name)
×
224

225
        vs_core = proxy_utils.get_vs_core(core)
×
226

227
        plugin = getattr(vs_core, namespace)
×
228

229
        if name in dir(plugin):
×
230
            return FunctionProxy(self, name)
×
231

232
        return getattr(plugin, name)
×
233

234

235
class CoreProxy(CoreProxyBase):
1✔
236
    def __init__(self, core: Core | None, vs_proxy: VSCoreProxy, lazy: bool) -> None:
1✔
237
        self.lazy = lazy
×
238
        self.__dict__['vs_core_ref'] = (core and weakref.ref(core), vs_proxy)
×
239

240
    def __getattr__(self, name: str) -> Plugin:
1✔
241
        if self.lazy and name not in vs.Core.__dict__:
×
242
            return PluginProxy(self, name)
×
243

244
        core = proxy_utils.get_vs_core(self)
×
245

246
        if name in dir(core):
×
247
            return PluginProxy(self, name)
×
248

249
        return getattr(core, name)
×
250

251

252
class proxy_utils:
1✔
253
    @staticmethod
1✔
254
    def get_vs_core(core: CoreProxy) -> Core:
1✔
255
        vs_core_ref, vs_proxy = core.__dict__['vs_core_ref']
×
256

257
        vs_core = (vs_core_ref and vs_core_ref())
×
258

259
        if vs_core_ref and vs_core is None:
×
260
            if object.__getattribute__(vs_proxy, '_own_core'):
×
261
                raise CustomRuntimeError('The VapourSynth core has been freed!', CoreProxy)
×
262

263
            vs_core = _get_core(vs_proxy)
×
264
            core.__dict__['vs_core_ref'] = (vs_core and weakref.ref(vs_core), vs_proxy)
×
265

266
        return vs_core or _get_core_with_cb()
×
267

268
    @staticmethod
1✔
269
    def get_vs_function(func: FunctionProxy) -> Function:
1✔
270
        plugin, func_name = proxy_utils.get_plugin(func)
×
271
        core, namespace = proxy_utils.get_core(plugin)
×
272
        vs_core = proxy_utils.get_vs_core(core)
×
273

274
        return getattr(getattr(vs_core, namespace), func_name)
×
275

276
    @staticmethod
1✔
277
    def get_plugin(func: FunctionProxy) -> tuple[PluginProxy, str]:
1✔
278
        return func.__dict__['func_ref']  # type: ignore
×
279

280
    @staticmethod
1✔
281
    def get_core(plugin: PluginProxy) -> tuple[CoreProxy, str]:
1✔
282
        return plugin.__dict__['plugin_ref']  # type: ignore
×
283

284

285
def vstools_isinstance(
1✔
286
    __obj: object, __class_or_tuple: type | UnionType | tuple[type | UnionType | tuple[Any, ...], ...]
287
) -> bool:
288
    if __class_or_tuple in (_CoreProxy, Core) and builtins_isinstance(__obj, CoreProxy):
1✔
289
        return True
×
290

291
    if __class_or_tuple is VSPresetVideoFormat and (
1✔
292
        builtins_isinstance(__obj, PresetVideoFormat)
293
        or builtins_isinstance(__obj, PresetFormat)  # LEGACY SUPPORT, PresetFormat is DEPRECATED
294
    ):
295
        return True
1✔
296

297
    return builtins_isinstance(__obj, __class_or_tuple)
1✔
298

299

300
if builtins.isinstance is not vstools_isinstance:
1✔
301
    builtins_isinstance = builtins.isinstance
1✔
302
    builtins.isinstance = vstools_isinstance
1✔
303

304

305
def _get_core(self: VSCoreProxy) -> Core | None:
1✔
306
    core_ref: ReferenceType[Core] | None = object.__getattribute__(self, '_core')
1✔
307
    own_core: bool = object.__getattribute__(self, '_own_core')
1✔
308

309
    if core := (core_ref and core_ref()):
1✔
310
        return core
×
311

312
    if own_core:
1✔
313
        raise CustomRuntimeError(
×
314
            'The core the proxy made reference to was freed!', 'VSCoreProxy'
315
        )
316

317
    return None
1✔
318

319

320
if TYPE_CHECKING:
1✔
321
    core_on_destroy_callbacks = dict[int, dict[int, tuple[weakref.ReferenceType[Callable[..., None]], bool]]]()
×
322
else:
323
    core_on_destroy_callbacks = {}
1✔
324

325
if TYPE_CHECKING:
1✔
326
    core_on_creation_callbacks = dict[int, weakref.ReferenceType[Callable[..., None]]]()
×
327
else:
328
    core_on_creation_callbacks = {}
1✔
329

330
core_on_creation_callbacks_cores = set[int]()
1✔
331

332
added_callback_cores = set[int]()
1✔
333

334

335
def _finalize_core(env_id: int, core_id: int, _forced: bool = True) -> None:
1✔
336
    if env_id not in core_on_destroy_callbacks:
×
337
        return
×
338

339
    for cb_id in list(core_on_destroy_callbacks[env_id].keys()):
×
340
        if _forced:
×
341
            callback_ref = core_on_destroy_callbacks[env_id].get(cb_id)
×
342
        else:
343
            callback_ref = core_on_destroy_callbacks[env_id].pop(cb_id)
×
344

345
        if callback_ref and (callback_ref[1] if _forced else True):
×
346
            callback = callback_ref[0]()
×
347

348
            if not callback:
×
349
                core_on_destroy_callbacks[env_id].pop(cb_id, None)
×
350
                continue
×
351

352
            try:
×
353
                callback(env_id, core_id)
×
354
            except TypeError:
×
355
                callback()
×
356

357
    if not _forced:
×
358
        core_on_destroy_callbacks.pop(env_id)
×
359

360
    gc.collect()
×
361

362

363
def _get_core_with_cb(self: VSCoreProxy | None = None) -> Core:
1✔
364
    _vs_core = _get_core(self) if self else None
1✔
365

366
    if not _vs_core:
1✔
367
        _vs_core = vs.core.core
1✔
368

369
    if (core_id := id(_vs_core)) not in core_on_creation_callbacks_cores:
1✔
370
        for cb_id in list(core_on_creation_callbacks.keys()):
1✔
371
            callback_ref = core_on_creation_callbacks.get(cb_id, None)
×
372

373
            if callback_ref and (callback := callback_ref()):
×
374
                try:
×
375
                    callback(core_id)
×
376
                except TypeError:
×
377
                    callback()
×
378
            else:
379
                # remove dead references
380
                core_on_creation_callbacks.pop(cb_id, None)
×
381

382
        core_on_creation_callbacks_cores.add(id(_vs_core))
1✔
383

384
    if core_id not in added_callback_cores:
1✔
385
        env_id = get_current_environment().env_id
1✔
386
        weakref.finalize(_vs_core, lambda: _finalize_core(env_id, core_id, False))
1✔
387

388
    return _vs_core
1✔
389

390

391
def _find_ref(start_data: Any, to_return: type | tuple[type, ...], it: int = 3) -> Any:
1✔
392
    if not it:
×
393
        return None
×
394

395
    for objects in [gc.get_referents(start_data), gc.get_referrers(start_data)]:
×
396
        for obj in objects:
×
397
            if isinstance(obj, to_return):
×
398
                return obj
×
399

400
            if (isinstance(obj, dict) and '__name__' in obj):
×
401
                continue
×
402

403
            if isinstance(obj, (Core, _CoreProxy, _FastManager)):
×
404
                continue
×
405

406
            for obj_obj in gc.get_referents(obj):
×
407
                if isinstance(obj_obj, to_return):
×
408
                    return obj_obj
×
409

410
                value = _find_ref(obj, to_return, it - 1)
×
411

412
                if value:
×
413
                    return value
×
414

415
    return None
×
416

417

418
class EnvironmentProxy(EnvironmentProxyBase):
1✔
419
    def __getattr__(self, name: str) -> Plugin:
1✔
420
        return getattr(get_current_environment(), name)
×
421

422
    def __setattr__(self, name: str, value: Any) -> None:
1✔
423
        return setattr(get_current_environment(), name, value)
×
424

425
    @property
1✔
426
    def data(self) -> None:
1✔
427
        data = self.env()
×
428
        assert data
×
429
        return data  # type: ignore
×
430

431
    @property
1✔
432
    def policy(self) -> EnvironmentPolicy:
1✔
433
        policy = _find_ref(self.data, (EnvironmentPolicy, VSScriptEnvironmentPolicy, StandaloneEnvironmentPolicy))
×
434
        assert policy is not None
×
435
        return policy
×
436

437
    @property
1✔
438
    def api(self) -> EnvironmentPolicyAPI:
1✔
439
        api = _find_ref(self.policy, EnvironmentPolicyAPI)
×
440
        assert api is not None
×
441
        return api
×
442

443
    @property
1✔
444
    def has_core(self) -> bool:
1✔
445
        import gc
×
446

447
        found_core = _find_ref(self.data, Core)
×
448

449
        if not found_core:
×
450
            return False
×
451

452
        core_refs = gc.get_referrers(found_core)
×
453

454
        return len(core_refs) > 1
×
455

456

457
_curr_env_proxy = EnvironmentProxy()
1✔
458

459

460
class VSCoreProxy(CoreProxyBase):
1✔
461
    """Class for wrapping a VapourSynth core."""
462

463
    def __init__(self, core: Core | None = None) -> None:
1✔
464
        object.__setattr__(self, '_own_core', core is not None)
1✔
465
        object.__setattr__(self, '_core', core and weakref.ref(core))
1✔
466

467
    def __getattr__(self, name: str) -> Plugin:
1✔
468
        return getattr(_get_core_with_cb(self), name)
1✔
469

470
    def __setattr__(self, name: str, value: Any) -> None:
1✔
471
        return setattr(_get_core_with_cb(self), name, value)
×
472

473
    @property
1✔
474
    def env(self) -> EnvironmentProxy:
1✔
475
        if not has_policy():
×
476
            raise CustomRuntimeError('No policy has been registered!')
×
477

478
        return _curr_env_proxy
×
479

480
    @property
1✔
481
    def core_id(self) -> int:
1✔
482
        if not self.active:
×
483
            raise CustomRuntimeError("Core hasn't been fetched yet!")
×
484

485
        return id(self.core)
×
486

487
    @property
1✔
488
    def active(self) -> bool:
1✔
489
        return (has_policy() and self.env.has_core) or (_get_core(self) is not None)
×
490

491
    @property
1✔
492
    def core(self) -> Core:
1✔
493
        """The underlying VapourSynth Core instance."""
494

495
        return _get_core_with_cb(self)
1✔
496

497
    @property
1✔
498
    def proxied(self) -> CoreProxy:
1✔
499
        """
500
        Proxied Core where plugins and functions are lazily retrieved,
501
        so it's safe to hold a reference of anything from this.
502
        """
503

504
        if self not in _objproxies:
×
505
            _objproxies[self] = {}
×
506

507
        if 'proxied' not in _objproxies[self]:
×
508
            _objproxies[self]['proxied'] = CoreProxy(_get_core(self), self, True)
×
509

510
        return _objproxies[self]['proxied']  # type: ignore
×
511

512
    @property
1✔
513
    def lazy(self) -> CoreProxy:
1✔
514
        """
515
        Lazy Core where plugins and functions are lazily retrieved and checked,
516
        so it's safe to hold a reference and set default of anything from this,
517
        without having to worry of creating a core.
518
        """
519

520
        if self not in _objproxies:
×
521
            _objproxies[self] = {}
×
522

523
        if 'lazy' not in _objproxies[self]:
×
524
            _objproxies[self]['lazy'] = CoreProxy(None, self, True)
×
525

526
        return _objproxies[self]['lazy']  # type: ignore
×
527

528
    def register_on_destroy(self, callback: Callable[..., None], on_forced: bool = True) -> None:
1✔
529
        """Register a callback on this core destroy."""
530

531
        _check_environment()
×
532

533
        env_id = get_current_environment().env_id
×
534

535
        if env_id not in core_on_destroy_callbacks:
×
536
            core_on_destroy_callbacks[env_id] = {id(callback): (weakref.ref(callback), on_forced)}
×
537
        else:
538
            core_on_destroy_callbacks[env_id] |= {id(callback): (weakref.ref(callback), on_forced)}
×
539

540
    def unregister_on_destroy(self, callback: Callable[..., None]) -> None:
1✔
541
        """Unregister a callback from this core destroy."""
542

543
        _check_environment()
×
544

545
        env_id = get_current_environment().env_id
×
546

547
        if env_id not in core_on_destroy_callbacks:
×
548
            core_on_destroy_callbacks[env_id] = {}
×
549
        else:
550
            core_on_destroy_callbacks[env_id].pop(id(callback), None)
×
551

552
    def set_affinity(
1✔
553
        self, threads: int | float | range | tuple[int, int] | list[int] | None = None,
554
        max_cache: int | None = None, reserve: int | Iterable[int] = 2
555
    ) -> None:
556
        """
557
        Set core affinity.
558

559
        :param threads:     How many and which threads to use for VapourSynth.
560
        :param max_cache:   Maximum cache used for frame data in VapourSynth.
561
        :param reserve:     Reserve n amount of or the specified threads.
562

563
        :raises DependencyNotFoundError:    Psutil was not found.
564
        """
565

566
        try:
×
567
            from psutil import Process  # type: ignore
×
568
        except ModuleNotFoundError as e:
×
569
            from ..exceptions import DependencyNotFoundError
×
570

571
            raise DependencyNotFoundError(self.set_affinity, e)
×
572

573
        if threads is None:
×
574
            threads = 0.6
×
575

576
        if isinstance(threads, float):
×
577
            if 0.0 <= threads or threads >= 1.0:
×
578
                threads = 1.0
×
579

580
            threads = ceil(cpu_count() * threads)
×
581

582
        if isinstance(threads, int):
×
583
            threads = range(0, threads)
×
584
        elif isinstance(threads, tuple):
×
585
            threads = range(*threads)
×
586

587
        threads = list(set(threads))
×
588

589
        if isinstance(reserve, int):
×
590
            if reserve > len(threads):
×
591
                threads = threads[:-reserve]
×
592
        else:
593
            threads = [t for t in threads if t not in reserve]
×
594

595
        self.core.num_threads = len(threads)
×
596

597
        Process().cpu_affinity(threads)
×
598

599
        if max_cache is not None:
×
600
            self.core.max_cache_size = max_cache
×
601

602

603
def _core_on_destroy_try() -> None:
1✔
604
    ...
×
605

606

607
def _check_environment() -> None:
1✔
608
    try:
×
609
        register_on_destroy(_core_on_destroy_try)
×
610
        unregister_on_destroy(_core_on_destroy_try)
×
611
    except Exception as e:
×
612
        if isinstance(e, ValueError) or not get_current_environment().active:
×
613
            raise ValueError("The environment has already been destroyed.")
×
614

615

616
_objproxies = {}  # type: ignore
1✔
617

618
core = VSCoreProxy()
1✔
619

620

621
if TYPE_CHECKING:
1✔
622
    class PyCapsule(Structure):
×
623
        ...
×
624

625
    pyx_capi: dict[str, PyCapsule] = ...  # type: ignore
×
626

627
    class StandaloneEnvironmentPolicy(EnvironmentPolicy):
×
628
        def __init__(self) -> NoReturn:
×
629
            ...
×
630

631
        def _on_log_message(self, level: MessageType, msg: str) -> None:
×
632
            ...
×
633

634
        def on_policy_registered(self, api: 'EnvironmentPolicyAPI') -> None:
×
635
            ...
×
636

637
        def on_policy_cleared(self) -> None:
×
638
            ...
×
639

640
        def get_current_environment(self) -> EnvironmentData:
×
641
            ...
×
642

643
        def set_environment(self, environment: EnvironmentData | None) -> EnvironmentData:
×
644
            ...
×
645

646
        def is_alive(self, environment: EnvironmentData) -> bool:
×
647
            ...
×
648

649
    class VSScriptEnvironmentPolicy(EnvironmentPolicy):
×
650
        def __init__(self) -> NoReturn:
×
651
            ...
×
652

653
        def on_policy_registered(self, policy_api: 'EnvironmentPolicyAPI') -> None:
×
654
            ...
×
655

656
        def on_policy_cleared(self) -> None:
×
657
            ...
×
658

659
        def get_current_environment(self) -> EnvironmentData | None:
×
660
            ...
×
661

662
        def set_environment(self, environment: EnvironmentData | None) -> EnvironmentData | None:
×
663
            ...
×
664

665
        def is_alive(self, environment: EnvironmentData) -> bool:
×
666
            ...
×
667

668
    def construct_type(signature: str) -> type:
×
669
        ...
×
670

671
    def construct_parameter(signature: str) -> Parameter:
×
672
        ...
×
673

674
    def construct_signature(signature: str, return_signature: str, injected: str | None = None) -> Signature:
×
675
        ...
×
676

677
    def try_enable_introspection(version: int | None = None) -> bool:  # noqa: F811
×
678
        ...
×
679

680
    class CallbackData:
×
681
        def __init__(
×
682
            self, node: RawNode, env: EnvironmentData,
683
            callback: Callable[[RawFrame | None, Exception | None], None] | None = None
684
        ) -> None:
685
            ...
×
686

687
        def receive(self, n: int, result: RawFrame | Exception) -> None:
×
688
            ...
×
689

690
    class PythonVSScriptLoggingBridge(Handler):
×
691
        def __init__(self, parent: Handler, level: int = LogLevelUnset) -> None:
×
692
            ...
×
693

694
        def emit(self, record: LogRecord) -> None:
×
695
            ...
×
696

697
    class _FastManager:
×
698
        ...
×
699
else:
700
    from vapoursynth import (
1✔
701
        CallbackData, PythonVSScriptLoggingBridge, StandaloneEnvironmentPolicy, VSScriptEnvironmentPolicy
702
    )
703
    from vapoursynth import __file__ as vs_file
1✔
704
    from vapoursynth import __pyx_capi__ as pyx_capi
1✔
705
    from vapoursynth import _construct_parameter as construct_parameter
1✔
706
    from vapoursynth import _construct_type as construct_type
1✔
707
    from vapoursynth import _FastManager
1✔
708
    from vapoursynth import _try_enable_introspection as try_enable_introspection
1✔
709
    from vapoursynth import construct_signature
1✔
710

711

712
if not TYPE_CHECKING:
1✔
713
    try:
1✔
714
        from vapoursynth import (
1✔
715
            ccfDisableAutoLoading, ccfDisableLibraryUnloading, ccfEnableGraphInspection, fmFrameState, fmParallel,
716
            fmParallelRequests, fmUnordered
717
        )
718

719
        PARALLEL = fmParallel
×
720
        PARALLEL_REQUESTS = fmParallelRequests
×
721
        UNORDERED = fmUnordered
×
722
        FRAME_STATE = fmFrameState
×
723
        ENABLE_GRAPH_INSPECTION = ccfEnableGraphInspection
×
724
        DISABLE_AUTO_LOADING = ccfDisableAutoLoading
×
725
        DISABLE_LIBRARY_UNLOADING = ccfDisableLibraryUnloading
×
726
    except ImportError:
1✔
727
        from vapoursynth import (
1✔
728
            DISABLE_AUTO_LOADING, DISABLE_LIBRARY_UNLOADING, ENABLE_GRAPH_INSPECTION, FRAME_STATE, PARALLEL,
729
            PARALLEL_REQUESTS, UNORDERED
730
        )
731

732
        fmParallel = PARALLEL
1✔
733
        fmParallelRequests = PARALLEL_REQUESTS
1✔
734
        fmUnordered = UNORDERED
1✔
735
        fmFrameState = FRAME_STATE
1✔
736
        ccfEnableGraphInspection = ENABLE_GRAPH_INSPECTION
1✔
737
        ccfDisableAutoLoading = DISABLE_AUTO_LOADING
1✔
738
        ccfDisableLibraryUnloading = DISABLE_LIBRARY_UNLOADING
1✔
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