1+ from copy import copy
12from inspect import isclass , signature , Signature
2- from pygments .formatters .terminal256 import Terminal256Formatter
33from typing import (
44 Annotated ,
55 AnyStr ,
66 Callable ,
7- Dict ,
87 Literal ,
98 NamedTuple ,
109 NewType ,
1110 Optional ,
1211 Protocol ,
13- Set ,
1412 Sequence ,
15- Tuple ,
16- Type ,
1713 TypeGuard ,
1814 Union ,
1915 get_args ,
@@ -97,6 +93,7 @@ class EvaluationPolicy:
9793 allow_builtins_access : bool = False
9894 allow_all_operations : bool = False
9995 allow_any_calls : bool = False
96+ allow_auto_import : bool = False
10097 allowed_calls : set [Callable ] = field (default_factory = set )
10198
10299 def can_get_item (self , value , item ):
@@ -330,6 +327,10 @@ class EvaluationContext(NamedTuple):
330327 #: Whether the evaluation of code takes place inside of a subscript.
331328 #: Useful for evaluating ``:-1, 'col'`` in ``df[:-1, 'col']``.
332329 in_subscript : bool = False
330+ #: Auto import method
331+ auto_import : Callable [list [str ], ModuleType ] | None = None
332+ #: Overrides for evaluation policy
333+ policy_overrides : dict = {}
333334
334335
335336class _IdentitySubscript :
@@ -463,6 +464,17 @@ def _find_dunder(node_op, dunders) -> Union[tuple[str, ...], None]:
463464 return dunder
464465
465466
467+ def get_policy (context : EvaluationContext ) -> EvaluationPolicy :
468+ policy = copy (EVALUATION_POLICIES [context .evaluation ])
469+
470+ for key , value in context .policy_overrides .items ():
471+ if hasattr (policy , key ):
472+ setattr (policy , key , value )
473+ else :
474+ print (f"Incorrect policy override key: { key } " )
475+ return policy
476+
477+
466478def eval_node (node : Union [ast .AST , None ], context : EvaluationContext ):
467479 """Evaluate AST node in provided context.
468480
@@ -490,7 +502,8 @@ def eval_node(node: Union[ast.AST, None], context: EvaluationContext):
490502 The purpose of this function is to guard against unwanted side-effects;
491503 it does not give guarantees on protection from malicious code execution.
492504 """
493- policy = EVALUATION_POLICIES [context .evaluation ]
505+ policy = get_policy (context )
506+
494507 if node is None :
495508 return None
496509 if isinstance (node , ast .Expression ):
@@ -711,14 +724,16 @@ def _resolve_annotation(
711724
712725
713726def _eval_node_name (node_id : str , context : EvaluationContext ):
714- policy = EVALUATION_POLICIES [ context . evaluation ]
727+ policy = get_policy ( context )
715728 if policy .allow_locals_access and node_id in context .locals :
716729 return context .locals [node_id ]
717730 if policy .allow_globals_access and node_id in context .globals :
718731 return context .globals [node_id ]
719732 if policy .allow_builtins_access and hasattr (builtins , node_id ):
720733 # note: do not use __builtins__, it is implementation detail of cPython
721734 return getattr (builtins , node_id )
735+ if policy .allow_auto_import and context .auto_import :
736+ return context .auto_import (node_id )
722737 if not policy .allow_globals_access and not policy .allow_locals_access :
723738 raise GuardRejection (
724739 f"Namespace access not allowed in { context .evaluation } mode"
@@ -728,7 +743,7 @@ def _eval_node_name(node_id: str, context: EvaluationContext):
728743
729744
730745def _eval_or_create_duck (duck_type , node : ast .Call , context : EvaluationContext ):
731- policy = EVALUATION_POLICIES [ context . evaluation ]
746+ policy = get_policy ( context )
732747 # if allow-listed builtin is on type annotation, instantiate it
733748 if policy .can_call (duck_type ) and not node .keywords :
734749 args = [eval_node (arg , context ) for arg in node .args ]
0 commit comments