Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/joblib/_utils.py: 42%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Adapted from https://stackoverflow.com/a/9558001/2536294
3import ast
4import functools
5import operator as op
6from dataclasses import dataclass
8from ._multiprocessing_helpers import mp
10if mp is not None:
11 from .externals.loky.process_executor import _ExceptionWithTraceback
14# supported operators
15operators = {
16 ast.Add: op.add,
17 ast.Sub: op.sub,
18 ast.Mult: op.mul,
19 ast.Div: op.truediv,
20 ast.FloorDiv: op.floordiv,
21 ast.Mod: op.mod,
22 ast.Pow: op.pow,
23 ast.USub: op.neg,
24}
27def eval_expr(expr):
28 """Somewhat safely evaluate an arithmetic expression.
30 >>> eval_expr('2*6')
31 12
32 >>> eval_expr('2**6')
33 64
34 >>> eval_expr('1 + 2*3**(4) / (6 + -7)')
35 -161.0
37 Raises ValueError if the expression is invalid, too long
38 or its computation involves too large values.
39 """
40 # Restrict the length of the expression to avoid potential Python crashes
41 # as per the documentation of ast.parse.
42 max_length = 30
43 if len(expr) > max_length:
44 raise ValueError(
45 f"Expression {expr[:max_length]!r}... is too long. "
46 f"Max length is {max_length}, got {len(expr)}."
47 )
48 try:
49 return eval_(ast.parse(expr, mode="eval").body)
50 except (TypeError, SyntaxError, OverflowError, KeyError) as e:
51 raise ValueError(
52 f"{expr!r} is not a valid or supported arithmetic expression."
53 ) from e
56def limit(max_=None):
57 """Return decorator that limits allowed returned values."""
59 def decorator(func):
60 @functools.wraps(func)
61 def wrapper(*args, **kwargs):
62 ret = func(*args, **kwargs)
63 try:
64 mag = abs(ret)
65 except TypeError:
66 pass # not applicable
67 else:
68 if mag > max_:
69 raise ValueError(
70 f"Numeric literal {ret} is too large, max is {max_}."
71 )
72 return ret
74 return wrapper
76 return decorator
79@limit(max_=10**6)
80def eval_(node):
81 if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)):
82 return node.value
83 elif isinstance(node, ast.BinOp): # <left> <operator> <right>
84 return operators[type(node.op)](eval_(node.left), eval_(node.right))
85 elif isinstance(node, ast.UnaryOp): # <operator> <operand> e.g., -1
86 return operators[type(node.op)](eval_(node.operand))
87 else:
88 raise TypeError(node)
91@dataclass(frozen=True)
92class _Sentinel:
93 """A sentinel to mark a parameter as not explicitly set"""
95 default_value: object
97 def __repr__(self):
98 return f"default({self.default_value!r})"
101class _TracebackCapturingWrapper:
102 """Protect function call and return error with traceback."""
104 def __init__(self, func):
105 self.func = func
107 def __call__(self, **kwargs):
108 try:
109 return self.func(**kwargs)
110 except BaseException as e:
111 return _ExceptionWithTraceback(e)
114def _retrieve_traceback_capturing_wrapped_call(out):
115 if isinstance(out, _ExceptionWithTraceback):
116 rebuild, args = out.__reduce__()
117 out = rebuild(*args)
118 if isinstance(out, BaseException):
119 raise out
120 return out