aboutsummaryrefslogtreecommitdiffstats
path: root/yt_dlp/jsinterp.py
diff options
context:
space:
mode:
Diffstat (limited to 'yt_dlp/jsinterp.py')
-rw-r--r--yt_dlp/jsinterp.py62
1 files changed, 18 insertions, 44 deletions
diff --git a/yt_dlp/jsinterp.py b/yt_dlp/jsinterp.py
index 350b44dd0..70857b798 100644
--- a/yt_dlp/jsinterp.py
+++ b/yt_dlp/jsinterp.py
@@ -1,12 +1,10 @@
-from collections.abc import MutableMapping
+import collections
+import contextlib
import json
import operator
import re
-from .utils import (
- ExtractorError,
- remove_quotes,
-)
+from .utils import ExtractorError, remove_quotes
_OPERATORS = [
('|', operator.or_),
@@ -38,40 +36,19 @@ class JS_Continue(ExtractorError):
ExtractorError.__init__(self, 'Invalid continue')
-class LocalNameSpace(MutableMapping):
- def __init__(self, *stack):
- self.stack = tuple(stack)
-
- def __getitem__(self, key):
- for scope in self.stack:
- if key in scope:
- return scope[key]
- raise KeyError(key)
-
+class LocalNameSpace(collections.ChainMap):
def __setitem__(self, key, value):
- for scope in self.stack:
+ for scope in self.maps:
if key in scope:
scope[key] = value
- break
- else:
- self.stack[0][key] = value
- return value
+ return
+ self.maps[0][key] = value
def __delitem__(self, key):
raise NotImplementedError('Deleting is not supported')
- def __iter__(self):
- for scope in self.stack:
- yield from scope
-
- def __len__(self, key):
- return len(iter(self))
-
- def __repr__(self):
- return f'LocalNameSpace{self.stack}'
-
-class JSInterpreter(object):
+class JSInterpreter:
def __init__(self, code, objects=None):
if objects is None:
objects = {}
@@ -232,7 +209,7 @@ class JSInterpreter(object):
for default in (False, True):
matched = False
for item in items:
- case, stmt = [i.strip() for i in self._separate(item, ':', 1)]
+ case, stmt = (i.strip() for i in self._separate(item, ':', 1))
if default:
matched = matched or case == 'default'
elif not matched:
@@ -268,10 +245,10 @@ class JSInterpreter(object):
expr = expr[:start] + json.dumps(ret) + expr[end:]
for op, opfunc in _ASSIGN_OPERATORS:
- m = re.match(r'''(?x)
- (?P<out>%s)(?:\[(?P<index>[^\]]+?)\])?
- \s*%s
- (?P<expr>.*)$''' % (_NAME_RE, re.escape(op)), expr)
+ m = re.match(rf'''(?x)
+ (?P<out>{_NAME_RE})(?:\[(?P<index>[^\]]+?)\])?
+ \s*{re.escape(op)}
+ (?P<expr>.*)$''', expr)
if not m:
continue
right_val = self.interpret_expression(m.group('expr'), local_vars, allow_recursion)
@@ -305,10 +282,8 @@ class JSInterpreter(object):
if var_m:
return local_vars[var_m.group('name')]
- try:
+ with contextlib.suppress(ValueError):
return json.loads(expr)
- except ValueError:
- pass
m = re.match(
r'(?P<in>%s)\[(?P<idx>.+)\]$' % _NAME_RE, expr)
@@ -451,9 +426,9 @@ class JSInterpreter(object):
m = re.match(r'^(?P<func>%s)\((?P<args>[a-zA-Z0-9_$,]*)\)$' % _NAME_RE, expr)
if m:
fname = m.group('func')
- argvals = tuple([
+ argvals = tuple(
int(v) if v.isdigit() else local_vars[v]
- for v in self._separate(m.group('args'))])
+ for v in self._separate(m.group('args')))
if fname in local_vars:
return local_vars[fname](argvals)
elif fname not in self._functions:
@@ -524,14 +499,13 @@ class JSInterpreter(object):
def build_function(self, argnames, code, *global_stack):
global_stack = list(global_stack) or [{}]
- local_vars = global_stack.pop(0)
def resf(args, **kwargs):
- local_vars.update({
+ global_stack[0].update({
**dict(zip(argnames, args)),
**kwargs
})
- var_stack = LocalNameSpace(local_vars, *global_stack)
+ var_stack = LocalNameSpace(*global_stack)
for stmt in self._separate(code.replace('\n', ''), ';'):
ret, should_abort = self.interpret_statement(stmt, var_stack)
if should_abort: