Skip to content

Commit 7b58316

Browse files
authored
vstools: deobfuscate proxy module (#339)
- Fix duplicated/missing symbols in `__all__` - Add more documentation and comments things - Make `(un)register_on_creation` thread safe - Add `has_environment`, `get_policy`, `get_policy_api` - Deprecate `clear_cache`, `EnvironmentProxy.policy`, `EnvironmentProxy.policy_api`, `VSCoreProxy.register_on_destroy`, `VSCoreProxy.unregister_on_destroy` - Improve typing a bit
1 parent dfe3a37 commit 7b58316

8 files changed

Lines changed: 395 additions & 236 deletions

File tree

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ dependencies = [
5151
"numpy>=2.0.0",
5252
"rich>=15.0.0",
5353
"psutil>=7.0.0",
54-
"typing_extensions>=4.15.0; python_version<'3.13'",
54+
"typing_extensions>=4.15.0; python_version<'3.15'",
5555
# VapourSynth plugins
5656
"vapoursynth-akarin>=1.4.1",
5757
"vapoursynth-fmtconv>=31",
@@ -346,6 +346,7 @@ warn_unreachable = true
346346

347347
allow_redefinition = true
348348
implicit_reexport = false
349+
enable_incomplete_feature = ["TypeForm"]
349350
strict = true
350351

351352
show_column_numbers = true

stubs/vapoursynth/__init__.pyi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -280,8 +280,8 @@ class EnvironmentPolicyAPI:
280280

281281
def register_policy(policy: EnvironmentPolicy) -> None: ...
282282
def has_policy() -> bool: ...
283-
def register_on_destroy(callback: Callable[..., None]) -> None: ...
284-
def unregister_on_destroy(callback: Callable[..., None]) -> None: ...
283+
def register_on_destroy(callback: Callable[[], None]) -> None: ...
284+
def unregister_on_destroy(callback: Callable[[], None]) -> None: ...
285285
def _try_enable_introspection(version: int | None = None) -> bool: ...
286286

287287
@final

uv.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vsexprtools/exprop.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ def cache_clear() -> None:
137137
for token in ExprToken:
138138
token.get_value.cache_clear()
139139

140-
vs.core.register_on_destroy(cache_clear)
140+
vs.register_on_destroy(cache_clear)
141141

142142

143143
vs.register_on_creation(_cache_clear_expr_token)

vsjetpack/types.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import sys
22

3-
__all__ = ["TypeIs", "TypeVar", "deprecated"]
3+
__all__ = ["TypeForm", "TypeIs", "TypeVar", "deprecated"]
44

55
if sys.version_info < (3, 13):
6-
import typing_extensions
7-
8-
TypeIs = typing_extensions.TypeIs
9-
TypeVar = typing_extensions.TypeVar
10-
deprecated = typing_extensions.deprecated
6+
from typing_extensions import TypeIs, TypeVar, deprecated
117
else:
128
from typing import TypeIs, TypeVar
139
from warnings import deprecated
10+
11+
if sys.version_info < (3, 15):
12+
from typing_extensions import TypeForm
13+
else:
14+
from typing import TypeForm

vstools/vs_proxy/__init__.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,34 @@
1+
"""
2+
Drop-in replacement for ``import vapoursynth`` that re-exports the upstream API
3+
and layers additional functionality on top.
4+
5+
Key additions:
6+
7+
- Lazy / proxied core access
8+
9+
Allows holding references like ``core.std.BlankClip`` without triggering core creation
10+
or locking into an environment policy.
11+
12+
* ``core.proxied``: A weakref-backed proxy to the current active core.
13+
* ``core.lazy``: A completely lazy proxy resolving only on function invocation.
14+
15+
- Core lifecycle hooks
16+
17+
``register_on_creation`` / ``unregister_on_creation`` react to core instantiation.
18+
19+
- Policy and environment introspection
20+
21+
Retrieve environment policies via GC graph walking and check active environment status.
22+
23+
- Interactive compatibility
24+
25+
Stubs ``__file__`` and the ``__vapoursynth__`` module for Jupyter notebooks, REPLs,
26+
and IDE interactive windows so VSScript environment policies resolve correctly.
27+
28+
- Extended preset video formats
29+
30+
Exports missing bit-depth and subsampling variant constants.
31+
"""
32+
133
from .objects import *
234
from .vs_vars import *

vstools/vs_proxy/objects.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
from __future__ import annotations
22

3+
import logging
34
import weakref
45
from abc import ABC, ABCMeta
56
from collections.abc import Callable, Mapping, MutableMapping, MutableSequence, MutableSet
67
from enum import Flag
78
from functools import partial
89
from itertools import chain
9-
from logging import getLogger
1010
from typing import TYPE_CHECKING, Any, Self
1111

12-
from jetpytools import Singleton, classproperty
12+
from jetpytools import Singleton, classproperty, to_arr
1313

1414
from vsjetpack import is_from_vs_module
1515

@@ -18,7 +18,7 @@
1818
__all__ = ["VSDebug", "VSObject", "VSObjectABC", "VSObjectABCMeta", "VSObjectMeta"]
1919

2020

21-
log = getLogger(__name__)
21+
log = logging.getLogger(__name__)
2222

2323

2424
def _get_mangle_name(name: str) -> str:
@@ -131,7 +131,7 @@ def vsdel_partial_register() -> None:
131131
)
132132

133133
setattr(obj, prefix + partial_attr, vsdel_partial_register)
134-
core.register_on_destroy(vsdel_partial_register)
134+
register_on_destroy(vsdel_partial_register)
135135

136136
setattr(obj, prefix + register_attr, del_register)
137137
register_on_creation(del_register)
@@ -161,7 +161,14 @@ def __new__[MetaSelf: VSObjectMeta](
161161
mname = _get_mangle_name(name)
162162

163163
original_slots = tuple(namespace.get("__slots__", ()))
164-
extra_slots = tuple(f"{mname}{slot}" for slot in _objregisters)
164+
extra_slots = [f"{mname}{slot}" for slot in _objregisters]
165+
166+
# Collect all slots defined in parent classes to check if __weakref__ is already defined
167+
all_base_slots = set(chain.from_iterable(to_arr(getattr(base, "__slots__", ())) for base in bases))
168+
169+
if "__weakref__" not in original_slots and "__weakref__" not in all_base_slots:
170+
extra_slots.append("__weakref__")
171+
165172
namespace["__slots__"] = (*extra_slots, *original_slots)
166173

167174
for reg in _clsregisters:
@@ -246,17 +253,15 @@ def __init__(self, *, env_life: bool = True, core_fetch: bool = False, use_loggi
246253
trying to find the code path that is locking you into a EnvironmentPolicy.
247254
"""
248255
if use_logging:
249-
import logging
250-
251256
VSDebug._print_func = logging.debug
252257
else:
253258
VSDebug._print_func = print
254259

255260
if env_life:
256-
register_on_creation(VSDebug._print_env_live, True)
261+
register_on_creation(VSDebug._print_env_live)
257262

258263
if core_fetch:
259-
register_on_creation(VSDebug._print_stack, True)
264+
register_on_creation(VSDebug._print_stack)
260265

261266
@staticmethod
262267
def _print_stack(core_id: int) -> None:
@@ -266,7 +271,7 @@ def _print_stack(core_id: int) -> None:
266271
def _print_env_live(core_id: int) -> None:
267272
VSDebug._print_func(f"New core created with id: {core_id}")
268273

269-
core.register_on_destroy(VSDebug._print_core_destroy, False)
274+
register_on_destroy(partial(VSDebug._print_core_destroy, core.env.env_id, core_id))
270275
register_on_destroy(partial(VSDebug._print_destroy, core.env.env_id, core_id))
271276

272277
@staticmethod

0 commit comments

Comments
 (0)