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

Jaded-Encoding-Thaumaturgy / vs-tools / 13116114213

03 Feb 2025 02:47PM UTC coverage: 58.14% (-0.02%) from 58.162%
13116114213

Pull #171

github

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

6 of 16 new or added lines in 7 files covered. (37.5%)

29 existing lines in 1 file now uncovered.

2832 of 4871 relevant lines covered (58.14%)

0.58 hits per line

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

36.32
/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
                    with output.clip.get_frame(0):
×
NEW
173
                        pass
×
174
                    break
×
175
        except Exception:
×
NEW
176
            with core.std.BlankClip().get_frame(0):
×
NEW
177
                pass
×
178
        core.max_cache_size = cache_size
×
179
    except Exception:
×
180
        ...
×
181

182

183
if TYPE_CHECKING:
1✔
184
    class FunctionProxyBase(Function):
×
185
        ...
×
186

187
    class PluginProxyBase(Plugin):
×
188
        ...
×
189

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

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

200

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

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

209
        function = proxy_utils.get_vs_function(self)
×
210

211
        return getattr(function, name)
×
212

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

216

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

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

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

227
        vs_core = proxy_utils.get_vs_core(core)
×
228

229
        plugin = getattr(vs_core, namespace)
×
230

231
        if name in dir(plugin):
×
232
            return FunctionProxy(self, name)
×
233

234
        return getattr(plugin, name)
×
235

236

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

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

246
        core = proxy_utils.get_vs_core(self)
×
247

248
        if name in dir(core):
×
249
            return PluginProxy(self, name)
×
250

251
        return getattr(core, name)
×
252

253

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

259
        vs_core = (vs_core_ref and vs_core_ref())
×
260

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

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

268
        return vs_core or _get_core_with_cb()
×
269

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

276
        return getattr(getattr(vs_core, namespace), func_name)
×
277

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

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

286

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

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

299
    return builtins_isinstance(__obj, __class_or_tuple)
1✔
300

301

302
if builtins.isinstance is not vstools_isinstance:
1✔
303
    builtins_isinstance = builtins.isinstance
1✔
304
    builtins.isinstance = vstools_isinstance
1✔
305

306

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

311
    if core := (core_ref and core_ref()):
1✔
312
        return core
×
313

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

319
    return None
1✔
320

321

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

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

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

334
added_callback_cores = set[int]()
1✔
335

336

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

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

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

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

354
            try:
×
355
                callback(env_id, core_id)
×
356
            except TypeError:
×
357
                callback()
×
358

359
    if not _forced:
×
360
        core_on_destroy_callbacks.pop(env_id)
×
361

362
    gc.collect()
×
363

364

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

368
    if not _vs_core:
1✔
369
        _vs_core = vs.core.core
1✔
370

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

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

384
        core_on_creation_callbacks_cores.add(id(_vs_core))
1✔
385

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

390
    return _vs_core
1✔
391

392

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

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

402
            if (isinstance(obj, dict) and '__name__' in obj):
×
403
                continue
×
404

405
            if isinstance(obj, (Core, _CoreProxy, _FastManager)):
×
406
                continue
×
407

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

412
                value = _find_ref(obj, to_return, it - 1)
×
413

414
                if value:
×
415
                    return value
×
416

417
    return None
×
418

419

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

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

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

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

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

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

449
        found_core = _find_ref(self.data, Core)
×
450

451
        if not found_core:
×
452
            return False
×
453

454
        core_refs = gc.get_referrers(found_core)
×
455

456
        return len(core_refs) > 1
×
457

458

459
_curr_env_proxy = EnvironmentProxy()
1✔
460

461

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

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

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

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

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

480
        return _curr_env_proxy
×
481

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

487
        return id(self.core)
×
488

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

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

497
        return _get_core_with_cb(self)
1✔
498

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

506
        if self not in _objproxies:
×
507
            _objproxies[self] = {}
×
508

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

512
        return _objproxies[self]['proxied']  # type: ignore
×
513

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

522
        if self not in _objproxies:
×
523
            _objproxies[self] = {}
×
524

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

528
        return _objproxies[self]['lazy']  # type: ignore
×
529

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

533
        _check_environment()
×
534

535
        env_id = get_current_environment().env_id
×
536

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

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

545
        _check_environment()
×
546

547
        env_id = get_current_environment().env_id
×
548

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

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

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

565
        :raises DependencyNotFoundError:    Psutil was not found.
566
        """
567

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

573
            raise DependencyNotFoundError(self.set_affinity, e)
×
574

575
        if threads is None:
×
576
            threads = 0.6
×
577

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

582
            threads = ceil(cpu_count() * threads)
×
583

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

589
        threads = list(set(threads))
×
590

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

597
        self.core.num_threads = len(threads)
×
598

599
        Process().cpu_affinity(threads)
×
600

601
        if max_cache is not None:
×
602
            self.core.max_cache_size = max_cache
×
603

604

605
def _core_on_destroy_try() -> None:
1✔
606
    ...
×
607

608

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

617

618
_objproxies = {}  # type: ignore
1✔
619

620
core = VSCoreProxy()
1✔
621

622

623
if TYPE_CHECKING:
1✔
624
    class PyCapsule(Structure):
×
625
        ...
×
626

627
    pyx_capi: dict[str, PyCapsule] = ...  # type: ignore
×
628

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

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

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

639
        def on_policy_cleared(self) -> None:
×
640
            ...
×
641

642
        def get_current_environment(self) -> EnvironmentData:
×
643
            ...
×
644

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

648
        def is_alive(self, environment: EnvironmentData) -> bool:
×
649
            ...
×
650

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

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

658
        def on_policy_cleared(self) -> None:
×
659
            ...
×
660

661
        def get_current_environment(self) -> EnvironmentData | None:
×
662
            ...
×
663

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

667
        def is_alive(self, environment: EnvironmentData) -> bool:
×
668
            ...
×
669

670
    def construct_type(signature: str) -> type:
×
671
        ...
×
672

673
    def construct_parameter(signature: str) -> Parameter:
×
674
        ...
×
675

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

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

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

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

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

696
        def emit(self, record: LogRecord) -> None:
×
697
            ...
×
698

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

713

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

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

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