[jsinterp] Truncate error messages

Related: #4635
pull/4646/head
pukkandan 2 years ago
parent 5da42f2b9b
commit a1c5bd82ec
No known key found for this signature in database
GPG Key ID: 7EEE9E1E817D0A39

@ -4,7 +4,7 @@ import json
import operator import operator
import re import re
from .utils import ExtractorError, remove_quotes from .utils import ExtractorError, remove_quotes, truncate_string
_NAME_RE = r'[a-zA-Z_$][\w$]*' _NAME_RE = r'[a-zA-Z_$][\w$]*'
_OPERATORS = { _OPERATORS = {
@ -53,6 +53,12 @@ class JSInterpreter:
self.code, self._functions = code, {} self.code, self._functions = code, {}
self._objects = {} if objects is None else objects self._objects = {} if objects is None else objects
class Exception(ExtractorError):
def __init__(self, msg, expr=None, *args, **kwargs):
if expr is not None:
msg += f' in: {truncate_string(expr, 50, 50)}'
super().__init__(msg, *args, **kwargs)
def _named_object(self, namespace, obj): def _named_object(self, namespace, obj):
self.__named_object_counter += 1 self.__named_object_counter += 1
name = f'__yt_dlp_jsinterp_obj{self.__named_object_counter}' name = f'__yt_dlp_jsinterp_obj{self.__named_object_counter}'
@ -92,12 +98,12 @@ class JSInterpreter:
def _separate_at_paren(cls, expr, delim): def _separate_at_paren(cls, expr, delim):
separated = list(cls._separate(expr, delim, 1)) separated = list(cls._separate(expr, delim, 1))
if len(separated) < 2: if len(separated) < 2:
raise ExtractorError(f'No terminating paren {delim} in {expr}') raise cls.Exception(f'No terminating paren {delim}', expr)
return separated[0][1:].strip(), separated[1].strip() return separated[0][1:].strip(), separated[1].strip()
def interpret_statement(self, stmt, local_vars, allow_recursion=100): def interpret_statement(self, stmt, local_vars, allow_recursion=100):
if allow_recursion < 0: if allow_recursion < 0:
raise ExtractorError('Recursion limit reached') raise self.Exception('Recursion limit reached')
should_abort = False should_abort = False
sub_statements = list(self._separate(stmt, ';')) or [''] sub_statements = list(self._separate(stmt, ';')) or ['']
@ -177,8 +183,7 @@ class JSInterpreter:
body, expr = remaining, '' body, expr = remaining, ''
start, cndn, increment = self._separate(constructor, ';') start, cndn, increment = self._separate(constructor, ';')
if self.interpret_statement(start, local_vars, allow_recursion - 1)[1]: if self.interpret_statement(start, local_vars, allow_recursion - 1)[1]:
raise ExtractorError( raise self.Exception('Premature return in the initialization of a for loop', constructor)
f'Premature return in the initialization of a for loop in {constructor!r}')
while True: while True:
if not self.interpret_expression(cndn, local_vars, allow_recursion): if not self.interpret_expression(cndn, local_vars, allow_recursion):
break break
@ -191,8 +196,7 @@ class JSInterpreter:
except JS_Continue: except JS_Continue:
pass pass
if self.interpret_statement(increment, local_vars, allow_recursion - 1)[1]: if self.interpret_statement(increment, local_vars, allow_recursion - 1)[1]:
raise ExtractorError( raise self.Exception('Premature return in the initialization of a for loop', constructor)
f'Premature return in the initialization of a for loop in {constructor!r}')
return self.interpret_statement(expr, local_vars, allow_recursion - 1)[0] return self.interpret_statement(expr, local_vars, allow_recursion - 1)[0]
elif m and m.group('switch'): elif m and m.group('switch'):
@ -267,11 +271,11 @@ class JSInterpreter:
local_vars[m.group('out')] = opfunc(left_val, right_val) local_vars[m.group('out')] = opfunc(left_val, right_val)
return local_vars[m.group('out')] return local_vars[m.group('out')]
elif left_val is None: elif left_val is None:
raise ExtractorError(f'Cannot index undefined variable: {m.group("out")}') raise self.Exception(f'Cannot index undefined variable {m.group("out")}', expr)
idx = self.interpret_expression(m.group('index'), local_vars, allow_recursion) idx = self.interpret_expression(m.group('index'), local_vars, allow_recursion)
if not isinstance(idx, int): if not isinstance(idx, int):
raise ExtractorError(f'List indices must be integers: {idx}') raise self.Exception(f'List index {idx} must be integer', expr)
left_val[idx] = opfunc(left_val[idx], right_val) left_val[idx] = opfunc(left_val[idx], right_val)
return left_val[idx] return left_val[idx]
@ -303,11 +307,11 @@ class JSInterpreter:
left_val, should_abort = self.interpret_statement( left_val, should_abort = self.interpret_statement(
left_val, local_vars, allow_recursion - 1) left_val, local_vars, allow_recursion - 1)
if should_abort: if should_abort:
raise ExtractorError(f'Premature left-side return of {op} in {expr!r}') raise self.Exception(f'Premature left-side return of {op}', expr)
right_val, should_abort = self.interpret_statement( right_val, should_abort = self.interpret_statement(
right_val, local_vars, allow_recursion - 1) right_val, local_vars, allow_recursion - 1)
if should_abort: if should_abort:
raise ExtractorError(f'Premature right-side return of {op} in {expr!r}') raise self.Exception(f'Premature right-side return of {op}', expr)
return opfunc(left_val or 0, right_val) return opfunc(left_val or 0, right_val)
if m and m.group('attribute'): if m and m.group('attribute'):
@ -322,7 +326,7 @@ class JSInterpreter:
def assertion(cndn, msg): def assertion(cndn, msg):
""" assert, but without risk of getting optimized out """ """ assert, but without risk of getting optimized out """
if not cndn: if not cndn:
raise ExtractorError(f'{member} {msg}: {expr}') raise self.Exception(f'{member} {msg}', expr)
def eval_method(): def eval_method():
if variable == 'String': if variable == 'String':
@ -349,7 +353,7 @@ class JSInterpreter:
if member == 'fromCharCode': if member == 'fromCharCode':
assertion(argvals, 'takes one or more arguments') assertion(argvals, 'takes one or more arguments')
return ''.join(map(chr, argvals)) return ''.join(map(chr, argvals))
raise ExtractorError(f'Unsupported string method {member}') raise self.Exception(f'Unsupported string method {member}', expr)
if member == 'split': if member == 'split':
assertion(argvals, 'takes one or more arguments') assertion(argvals, 'takes one or more arguments')
@ -430,7 +434,7 @@ class JSInterpreter:
self._functions[fname] = self.extract_function(fname) self._functions[fname] = self.extract_function(fname)
return self._functions[fname](argvals) return self._functions[fname](argvals)
raise ExtractorError(f'Unsupported JS expression {expr!r}') raise self.Exception('Unsupported JS expression', expr)
def extract_object(self, objname): def extract_object(self, objname):
_FUNC_NAME_RE = r'''(?:[a-zA-Z$0-9]+|"[a-zA-Z$0-9]+"|'[a-zA-Z$0-9]+')''' _FUNC_NAME_RE = r'''(?:[a-zA-Z$0-9]+|"[a-zA-Z$0-9]+"|'[a-zA-Z$0-9]+')'''
@ -469,7 +473,7 @@ class JSInterpreter:
self.code) self.code)
code, _ = self._separate_at_paren(func_m.group('code'), '}') # refine the match code, _ = self._separate_at_paren(func_m.group('code'), '}') # refine the match
if func_m is None: if func_m is None:
raise ExtractorError(f'Could not find JS function "{funcname}"') raise self.Exception(f'Could not find JS function "{funcname}"')
return func_m.group('args').split(','), code return func_m.group('args').split(','), code
def extract_function(self, funcname): def extract_function(self, funcname):

@ -5759,6 +5759,13 @@ def make_archive_id(ie, video_id):
return f'{ie_key.lower()} {video_id}' return f'{ie_key.lower()} {video_id}'
def truncate_string(s, left, right=0):
assert left > 3 and right >= 0
if s is None or len(s) <= left + right:
return s
return f'{s[:left-3]}...{s[-right:]}'
# Deprecated # Deprecated
has_certifi = bool(certifi) has_certifi = bool(certifi)
has_websockets = bool(websockets) has_websockets = bool(websockets)

Loading…
Cancel
Save