russmiles: Good to go ... obviously when the build is ok ;)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 835f999..eca3b3f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,8 +8,11 @@
 
 - Fix a failure when no top-level controls were provided but an activity
   control was declared [#88][88]
+- Warn when control Python package is not installed but do not fail experiment
+  during validation [#90][90]. Controls should remain optional.
 
 [88]: https://github.com/chaostoolkit/chaostoolkit-lib/issues/88
+[90]: https://github.com/chaostoolkit/chaostoolkit-lib/issues/90
 
 
 ## [1.1.0][] - 2019-03-11
diff --git a/chaoslib/control/python.py b/chaoslib/control/python.py
index 028ac48..26e4a4c 100644
--- a/chaoslib/control/python.py
+++ b/chaoslib/control/python.py
@@ -63,9 +63,10 @@ def validate_python_control(control: Control):
     try:
         importlib.import_module(mod_name)
     except ImportError:
-        raise InvalidActivity("could not find Python module '{mod}' "
-                              "in control '{name}'".format(
-                                  mod=mod_name, name=name))
+        logger.warning("Could not find Python module '{mod}' "
+                       "in control '{name}'. Did you install the Python "
+                       "module? The experiment will carry on running "
+                       "nonetheless.".format(mod=mod_name, name=name))
 
 
 def apply_python_control(level: str, control: Control, experiment: Experiment,

Correct activity-level control behaviour where missing controls are simply warned of in the logging
diff --git a/tests/test_control.py b/tests/test_control.py
index 2fe3430..cb2ca95 100644
--- a/tests/test_control.py
+++ b/tests/test_control.py
@@ -2,9 +2,12 @@
 from copy import deepcopy
 from concurrent.futures import ThreadPoolExecutor
 import os
+import tempfile
 from typing import Any, Dict, List
+from unittest.mock import patch
 
 from pkg_resources import Distribution, EntryPoint, working_set
+import logzero
 import pytest
 
 from chaoslib.activity import execute_activity
@@ -208,15 +211,19 @@ def test_not_automatic_does_not_go_deep_down_the_tree():
         assert len(controls) == 0
 
 
-def test_validate_python_control_must_be_loadable():
-    with pytest.raises(InvalidActivity):
-        validate_python_control({
-            "name": "a-python-control",
-            "provider": {
-                "type": "python",
-                "module": "blah.blah"
-            }
-        })
+@patch('chaoslib.control.python.logger', autospec=True)
+def test_validate_python_control_must_be_loadable(logger):
+    validate_python_control({
+        "name": "a-python-control",
+        "provider": {
+            "type": "python",
+            "module": "blah.blah"
+        }
+    })
+    args = logger.warning.call_args
+    msg = "Could not find Python module 'blah.blah' in control " \
+        "'a-python-control'. Did you install the Python module?"
+    assert msg in args[0][0]
 
 
 def test_validate_python_control_needs_a_module():

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest pytest-cov pytest-sugar
3.6
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs==22.2.0
certifi==2021.5.30
-e git+https://github.com/chaostoolkit/chaostoolkit-lib.git@fb1ac7e421f0396c940546951972617889b9a926#egg=chaostoolkit_lib
charset-normalizer==2.0.12
coverage==6.2
idna==3.10
importlib-metadata==4.8.3
iniconfig==1.1.1
logzero==1.7.0
packaging==21.3
pluggy==1.0.0
py==1.11.0
pyparsing==3.1.4
pytest==7.0.1
pytest-cov==4.0.0
pytest-sugar==0.9.6
PyYAML==6.0.1
requests==2.27.1
termcolor==1.1.0
tomli==1.2.3
typing_extensions==4.1.1
urllib3==1.26.20
zipp==3.6.0

name: chaostoolkit-lib
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - attrs==22.2.0
      - charset-normalizer==2.0.12
      - coverage==6.2
      - idna==3.10
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - logzero==1.7.0
      - packaging==21.3
      - pluggy==1.0.0
      - py==1.11.0
      - pyparsing==3.1.4
      - pytest==7.0.1
      - pytest-cov==4.0.0
      - pytest-sugar==0.9.6
      - pyyaml==6.0.1
      - requests==2.27.1
      - termcolor==1.1.0
      - tomli==1.2.3
      - typing-extensions==4.1.1
      - urllib3==1.26.20
      - zipp==3.6.0
prefix: /opt/conda/envs/chaostoolkit-lib

tests/test_control.py::test_validate_python_control_must_be_loadable
tests/test_control.py::test_initialize_controls_will_configure_a_control tests/test_control.py::test_initialize_controls_will_cleanup_a_control tests/test_control.py::test_controls_are_applied_before_and_after_experiment tests/test_control.py::test_controls_are_applied_before_and_but_not_after_experiment tests/test_control.py::test_controls_are_applied_not_before_and_but_after_experiment tests/test_control.py::test_controls_may_interrupt_experiment tests/test_control.py::test_controls_are_applied_before_and_after_hypothesis tests/test_control.py::test_controls_are_applied_before_and_after_method tests/test_control.py::test_controls_are_applied_before_and_after_rollbacks tests/test_control.py::test_controls_are_applied_before_and_after_activities tests/test_control.py::test_no_controls_get_applied_when_none_defined tests/test_control.py::test_automatic_goes_deep_down_the_tree tests/test_control.py::test_not_automatic_does_not_go_deep_down_the_tree tests/test_control.py::test_validate_python_control_needs_a_module tests/test_control.py::test_controls_can_access_experiment tests/test_control.py::test_controls_are_applied_at_various_levels tests/test_control.py::test_controls_are_applied_when_they_are_not_top_level
chaoslib/control/python.py CHANGELOG.md
chaoslib/control/python.py CHANGELOG.md