I wrote a script to execute a service on the Forestry TEP platform via its REST API. This service has certain input parameters, some of them are numerical values, other strings or files. The current workflow is:
There is a configuration file that has options for file and literal inputs. Each option is a dictionary, where each key is equal to one parameter of the service I want to execute. Example of such file:
file_inputs= {"cfg" : "forest_flux/FTEP_service/cfg.ini", "parsawen" : "forest_flux/FTEP_service/parsawen.csv", "weather" : "forestflux/data/fin_01/weather/weather_2004.csv"} literal_inputs = {"version" : 3}
In the script, I read the cfg and iterate over items in these dictionaries. For each
key,value
pair, I useexec()
to store the value in a class variable of the same name. For file inputs, I first upload the file on the platform and store the file's location on the platform in the variable.input_files_dict = json.loads(cfg.get("service_info", "file_inputs")) for key, val in input_files_dict.items(): exec("self.{} = self.post_data('{}')".format(key, val)) literal_inputs_dict = json.loads(cfg.get("service_info", "literal_inputs")) for key, val in literal_inputs_dict.items(): exec("self.{} = '{}'".format(key, val))
I request service inputs and I try to match the service parameters with my class variables. I add it to a dictionary of parameters which I then send to the platform when executing the service.
r2 = requests.get(url = url2, verify=False, allow_redirects=False, cookies=self.session_cookies) data_inputs = json.loads(r2.text)['serviceDescriptor']['dataInputs'] for _input in data_inputs: value_to_set = eval("self." + _input["id"].lower()) job_config["inputs"][_input["id"]] = [value_to_set]
This current workflow works well for me but I have strong suspicion that it is a very bad practice. I have read that using exec() and eval() is discouraged as there are security issues. However, I cannot think of a better way to achieve this functionality, i.e. to pair values in configuration with service inputs automatically. I want this script to be generic, i.e. usable for any service on the platform.
Question: How could I do this without using the problematic functions? How could using these functions be problematic in this case?
Please note that I removed a lot of code that is not related to the problem I am asking about (e.g. error handling).
-
\$\begingroup\$ You could use ast.literal_eval but beware that "It is possible to crash the Python interpreter with a sufficiently large/complex string due to stack depth limitations in Python’s AST compiler." \$\endgroup\$Grajdeanu Alex– Grajdeanu Alex2020年06月25日 20:26:03 +00:00Commented Jun 25, 2020 at 20:26
1 Answer 1
You can use getattr
, setattr
and delattr
to retrieve, modify and remove attributes respectively. This avoids most potential messes eval
has.
input_files_dict = json.loads(cfg.get("service_info", "file_inputs"))
for key, val in input_files_dict.items():
setattr(self, key, self.post_data(f'{value}'))
-
\$\begingroup\$ Thanks! So f'{value}') is equal to '{}'.format(value) ? Is one preferred over the other and why? \$\endgroup\$Jan Pisl– Jan Pisl2020年06月26日 17:40:17 +00:00Commented Jun 26, 2020 at 17:40
-
2\$\begingroup\$ @JanPisl Yes.
f'{value}'
is preferred (as it is shorter) thanstr.format
. But some modern programs still usestr.format
as they need to support versions of Python that f-strings are not available in. \$\endgroup\$2020年06月26日 17:49:21 +00:00Commented Jun 26, 2020 at 17:49
Explore related questions
See similar questions with these tags.