Repository: chaostoolkit/chaostoolkit-lib

Issue:
Invalid name reported in logs on Python provider validation
**Describe the bug**

Say you have the following probe:

```python
 invalid_python_func_probe = {
        "name": "hello",
        "type": "probe",
        "provider": {
            "type": "python",
            "module": "os",
            "func": "whatever"
        }
    }
```

It's invalid becauset he `whatever` function does not exist in the module `os`.

Upon failing, CTK reports an invalid name in the logs:

```
'os' does not expose 'whatever' in activity 'getrandom'
```

The reason is simple, we override the `name` variable during the course of the validation function and therefore
log the wrong value.


Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cb63fa5..49e1527 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,13 @@
 
 ## [Unreleased][]
 
+### Changed
+
+- Fix activity name in the logs shown when the Python provider links to an
+  unknown function [#223][223]
+
+[223]: https://github.com/chaostoolkit/chaostoolkit-lib/issues/223
+
 ### Removed
 - Remove the problematic log line from exit.py
 
diff --git a/chaoslib/provider/python.py b/chaoslib/provider/python.py
index 82fd7ae..4686583 100644
--- a/chaoslib/provider/python.py
+++ b/chaoslib/provider/python.py
@@ -76,7 +76,7 @@ def validate_python_activity(activity: Activity):  # noqa: C901
 
     This should be considered as a private function.
     """
-    name = activity["name"]
+    activity_name = activity["name"]
     provider = activity["provider"]
     mod_name = provider.get("module")
     if not mod_name:
@@ -91,7 +91,7 @@ def validate_python_activity(activity: Activity):  # noqa: C901
     except ImportError:
         raise InvalidActivity("could not find Python module '{mod}' "
                               "in activity '{name}'".format(
-                                  mod=mod_name, name=name))
+                                  mod=mod_name, name=activity_name))
 
     found_func = False
     arguments = provider.get("arguments", {})
@@ -142,5 +142,7 @@ def validate_python_activity(activity: Activity):  # noqa: C901
 
     if not found_func:
         raise InvalidActivity(
-            "'{mod}' does not expose '{func}' in activity '{name}'".format(
-                mod=mod_name, func=func, name=name))
+            "The python module '{mod}' does not expose a function called "
+            "'{func}' in {type} '{name}'".format(
+                mod=mod_name, func=func, type=activity['type'],
+                name=activity_name))


Test patch:
diff --git a/tests/test_experiment.py b/tests/test_experiment.py
index e18f74d..eaf6fb5 100644
--- a/tests/test_experiment.py
+++ b/tests/test_experiment.py
@@ -16,6 +16,7 @@ from chaoslib.exceptions import ActivityFailed, InvalidActivity, \
     InvalidExperiment, InterruptExecution
 from chaoslib.experiment import ensure_experiment_is_valid, load_experiment, \
     run_experiment
+from chaoslib.provider.python import validate_python_activity
 
 from fixtures import config, experiments
 
@@ -446,3 +447,20 @@ def test_rollback_never_strategy_does_not_run_on_interrupted_experiment_in_metho
     journal = run_experiment(experiment, settings)
     assert journal["status"] == "interrupted"
     assert len(journal["rollbacks"]) == 0
+
+
+def test_activity_name_is_logged_correctly_when_function_not_exposed_in_module():
+    invalid_python_func_probe = {
+        "name": "hello",
+        "type": "probe",
+        "provider": {
+            "type": "python",
+            "module": "os",
+            "func": "whatever"
+        }
+    }
+
+    with pytest.raises(InvalidActivity) as x:
+        validate_python_activity(invalid_python_func_probe)
+
+    assert str(x.value) == "The python module 'os' does not expose a function called 'whatever' in probe 'hello'"