-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdict_evals.py
129 lines (116 loc) · 4.74 KB
/
dict_evals.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#!/usr/bin/env python3
"""
author [email protected]
"""
# Note: This module is used to resolve all 'eval(...)' string values in dict object.
# This module is for processing complex Python dict types that have
# field values such as:
# "eval(some_function())"
# "eval(other_function(arg1))"
# "eval(different_function(arg1, option1=foo))"
# Usually, but not always, the Python dict object will have originated in a JSON configuration file.
# The python dict object will be recursed to find the target values and then the value
# will be passed to the Python built-in eval function. Whatever
# the target function (some_function, other_function, etc) returns
# will replace the string value naming the target function. Thus,
# "eval(some_function()"
# will be replaced with
# "value that some_function returned"
# or
# value_that_some_function_returned
# or
# { "foo": "bar", "baz": [1,2,3] }
# or
# whatever.
#
# There are no restrictions on the parameters that can be passed to
# the target function. The target function must return some value
# of any type.
#
# This module contains two categories of functions:
# 1. Functions that appear in string values in a target dict object.
# 2. Functions that recurse a target dict object.
#
# For category 2, there are two functions available:
# 1. find_replace(obj, vars_dict=None)
# 2. find_replace_module(identifier, vars_dict=None)
#
# For find_replace, the obj must be Python dict type.
# For find_replace_module, the identifier is the name of some import'ed module.
#
# For find_replace_module, the identifier of the import'ed module will be searched
# for dict objects that it contains and then each dict object is passed to find_replace.
#
# The target functions invoked by eval can have parameters. The parameters must be
# available in the scope where the eval is called (inside find_replace). Therefore,
# the user must populate the vars_dict with parameters that will be passed to the
# target functions (which themselves are processed by the Python built-in eval function).
# Variables not present in the vars_dict but named on the string value such as:
# "some_function(my_variable)"
# will result in a NameError exception being raised for the "my_variable" identifier.
# So use of the vars_dict is required where any parameters are passed to the target function.
#
#
# Standard imports
import sys
import copy
import inspect
import os
import logging
import json
import re
# Local imports
from user_functions import *
def find_replace(obj, vars_dict=None):
""" Recurse the dict obj to find string values such as "eval(some_function())", call the function, and replace the value. """
# Local function to put variables in vars_dict into local scope
# and call the eval function.
def replace_value(obj, vars_dict=None):
is_bytes = False
if isinstance(obj, str) or isinstance(obj, bytes):
if isinstance(obj, bytes):
is_bytes = True
temp = obj.decode('utf-8')
else:
temp = copy.deepcopy(obj)
temp = temp.strip()
match = re.search(r'^eval\s*\(', temp)
if match is not None:
eval_param = temp[match.span()[1]:]
eval_param = eval_param[:-1]
if isinstance(vars_dict, dict) and bool(vars_dict): # and dict non-empty?
# Create local variable references in scope for the eval below
locals().update(vars_dict)
try:
retval = eval(eval_param)
if isinstance(retval, str) and is_bytes:
retval = retval.encode('utf-8')
return retval
except Exception as ex:
msg = "Exception: {}".format(ex)
raise RuntimeError(msg)
return obj
# recurse, find, and replace
if isinstance(obj, str) or isinstance(obj, bytes):
return replace_value(obj, vars_dict=vars_dict)
elif isinstance(obj, dict):
for key in obj:
val = obj[key]
obj[key] = find_replace(val, vars_dict=vars_dict)
elif isinstance(obj, list):
for idx in range(len(obj)):
val = obj[idx]
obj[idx] = find_replace(val, vars_dict=vars_dict)
return obj
def find_replace_module(identifier, vars_dict=None):
""" For an imported module identifier, iterate the dict objects and then call find_replace. """
rc = False
if inspect.ismodule(identifier):
rc = True
for item in identifier.__dict__.items():
k = item[0]
v = item[1]
if isinstance(v, dict):
new_v = find_replace(v, vars_dict)
identifier.__dict__[k] = new_v
return rc