Repository: ansible/tower-cli (python)

Issue:
pyyaml yaml.load Deprecation
https://github.com/ansible/tower-cli/blob/123bb21693bbc4d867cae1eeb1bcd59a5670d8b7/tower_cli/utils/parser.py#L94

Hi Ansible,
since PyYaml deprecreated the load function, can you fix this yaml.load() accordingly?
Otherwise, tower-cli is not working with latest python docker image with has python3.7 and the pyYaml 5.1 updated a couple weeks ago.
nb: also update pip install, please
thanks,
-Jingtao

Pull request description:
Explicitly add Loader to yaml.load 
Fixes #675 #676 

Patch:
diff --git a/tower_cli/utils/parser.py b/tower_cli/utils/parser.py
index a837959..a79d1d8 100644
--- a/tower_cli/utils/parser.py
+++ b/tower_cli/utils/parser.py
@@ -91,7 +91,7 @@ def string_to_dict(var_string, allow_kv=True, require_dict=True):
     # except (TypeError, AttributeError, ValueError, AssertionError):
     try:
         # Accept all JSON and YAML
-        return_dict = yaml.load(var_string)
+        return_dict = yaml.load(var_string, Loader=yaml.SafeLoader)
         if require_dict:
             assert type(return_dict) is dict
     except (AttributeError, yaml.YAMLError, AssertionError):
@@ -141,7 +141,7 @@ def process_extra_vars(extra_vars_list, force_json=True):
     if not force_json:
         try:
             # Conditions to verify it is safe to return rolling YAML string
-            try_dict = yaml.load(extra_vars_yaml)
+            try_dict = yaml.load(extra_vars_yaml, Loader=yaml.SafeLoader)
             assert type(try_dict) is dict
             debug.log('Using unprocessed YAML', header='decision', nl=2)
             return extra_vars_yaml.rstrip()


Test patch:
diff --git a/tests/test_resources_job.py b/tests/test_resources_job.py
index 3f9b2b0..3ed3cef 100644
--- a/tests/test_resources_job.py
+++ b/tests/test_resources_job.py
@@ -157,7 +157,7 @@ class LaunchTests(unittest.TestCase):
                 result = self.res.launch(1, no_input=False)
                 self.assertDictContainsSubset(
                     {"spam": "eggs"},
-                    yaml.load(edit.mock_calls[0][1][0])
+                    yaml.load(edit.mock_calls[0][1][0], Loader=yaml.SafeLoader)
                 )
             self.assertDictContainsSubset(
                 {'foo': 'bar'},
@@ -178,7 +178,7 @@ class LaunchTests(unittest.TestCase):
                 self.assertIn('# comment', edit.mock_calls[0][1][0])
                 self.assertDictContainsSubset(
                     {"spam": "eggs"},
-                    yaml.load(edit.mock_calls[0][1][0])
+                    yaml.load(edit.mock_calls[0][1][0], Loader=yaml.SafeLoader)
                 )
 
     def test_extra_vars_at_runtime_no_user_data(self):
@@ -208,8 +208,8 @@ class LaunchTests(unittest.TestCase):
             jt_vars_registration(t, 'spam: eggs')
             t.register_json('/config/', {'version': '2.4'}, method='GET')
             result = self.res.launch(1, extra_vars=['foo: bar'])
-            response_json = yaml.load(t.requests[2].body)
-            ev_json = yaml.load(response_json['extra_vars'])
+            response_json = yaml.load(t.requests[2].body, Loader=yaml.SafeLoader)
+            ev_json = yaml.load(response_json['extra_vars'], Loader=yaml.SafeLoader)
             self.assertTrue('foo' in ev_json)
             self.assertTrue('spam' not in ev_json)
             self.assertDictContainsSubset({'changed': True, 'id': 42}, result)
diff --git a/tests/test_utils_parser.py b/tests/test_utils_parser.py
index fbd8b28..a99288f 100644
--- a/tests/test_utils_parser.py
+++ b/tests/test_utils_parser.py
@@ -37,7 +37,7 @@ class ParserTests(unittest.TestCase):
         bjson = yaml.dump(bdict, default_flow_style=True)
         cyml = "c: 5"
         result = parser.process_extra_vars([ayml, bjson, cyml])
-        rdict = yaml.load(result)
+        rdict = yaml.load(result, Loader=yaml.SafeLoader)
         self.assertEqual(rdict['a'], 1)
         self.assertEqual(rdict['b'], 2)
 
@@ -49,17 +49,18 @@ class ParserTests(unittest.TestCase):
         yaml_w_comment = '{a: b,\n# comment\nc: d}'
         json_text = '{"z":"p"}'
         self.assertDictContainsSubset(
-            yaml.load(yaml_w_comment),
+            yaml.load(yaml_w_comment, Loader=yaml.SafeLoader),
             yaml.load(parser.process_extra_vars(
-                [yaml_w_comment, json_text], force_json=False
-            ))
+                [yaml_w_comment, json_text], force_json=False),
+                Loader=yaml.SafeLoader
+            )
         )
         # Test that it correctly combines a diverse set of YAML
         yml1 = "a: 1\n# a comment on second line \nb: 2"
         yml2 = "c: 3"
         self.assertEqual(
             yaml.load(parser.process_extra_vars(
-                [yml1, yml2], force_json=False)),
+                [yml1, yml2], force_json=False), Loader=yaml.SafeLoader),
             {'a': 1, 'b': 2, 'c': 3}
         )
         # make sure it combined them into valid yaml
@@ -73,7 +74,7 @@ class ParserTests(unittest.TestCase):
         a2dict = {"a": 2}
         a2yml = yaml.dump(a2dict)
         result = parser.process_extra_vars([ayml, a2yml])
-        rdict = yaml.load(result)
+        rdict = yaml.load(result, Loader=yaml.SafeLoader)
         self.assertEqual(rdict['a'], 2)
 
     def test_read_from_file(self):
@@ -101,7 +102,7 @@ class ParserTests(unittest.TestCase):
 
         # but accept data if there are just two equals
         res = parser.process_extra_vars(['password==pa#exp&U=!9Rop'])
-        self.assertEqual(yaml.load(res)['password'], '=pa#exp&U=!9Rop')
+        self.assertEqual(yaml.load(res, Loader=yaml.SafeLoader)['password'], '=pa#exp&U=!9Rop')
 
         with self.assertRaises(exc.TowerCLIError):
             parser.process_extra_vars(["left_param="])
@@ -248,7 +249,7 @@ class TestSplitter_Gen(unittest.TestCase):
         """Custom input-output scenario tests for 2 sources into one."""
         for data in self.COMBINATION_DATA:
             self.assertEqual(
-                yaml.load(parser.process_extra_vars(data[0])),
+                yaml.load(parser.process_extra_vars(data[0]), Loader=yaml.SafeLoader),
                 data[1]
             )
 
@@ -256,7 +257,7 @@ class TestSplitter_Gen(unittest.TestCase):
         """Test that data is dumped without unicode character marking."""
         for data in self.COMBINATION_DATA:
             string_rep = parser.process_extra_vars(data[0])
-            self.assertEqual(yaml.load(string_rep), data[1])
+            self.assertEqual(yaml.load(string_rep, Loader=yaml.SafeLoader), data[1])
             assert "python/unicode" not in string_rep
             assert "\\n" not in string_rep