Repository: boxed/mutmut

Issue:
``mutmut_config.init()`` not called
I have troubles in getting my ``mutmut_config.py`` to do what I want it to.

The content ist:
```python
def init():
    from mutmut import mutations_by_type
    del mutations_by_type["string"]
    print(mutations_by_type)
```

When I run ``mutmut run``, the ``init()`` is not called.

What I could analyse so far:
* all the imports (from ``mutmut``'s ``__init__.py`` and ``__main__.py``) are done twice when running ``mutmut``, once when starting the program and once after the "2. Checking mutants" message
* At the first import, ``mutmut_config`` cannot be imported and is None. That is why the ``init()`` is not called. Only after the second import it can be loaded, and things like ``pre_mutation(context)`` work as expected

My directory layout is:
```
(.venv) tracematrix [try-mutmut●●] % tree
.
├── CHANGELOG.md
├── LICENSE
├── README.md
├── mutmut_config.py
├── poetry.lock
├── pyproject.toml
├── src
│   └── tracematrix
│       ├── item.py
│       ├── matrix.py
│       └── reporters
│           ├── __init__.py
│           ├── base_reporter.py
│           ├── csv_reporter.py
│           ├── html_reporter.py
│           └── template.html
└── tests
```

Comments:

Patch:
diff --git a/HISTORY.rst b/HISTORY.rst
index 1bd1287..03768a0 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -6,6 +6,8 @@ Changelog
 
 * Add `--disable-mutation-types` and `--enable-mutation-types` to control what types of mutations are performed
 
+* Fixed error where ``mutmut_config.init()`` was not called when running without explicitly having set ``PYTHONPATH``
+
 2.2.0
 ~~~~~
 
diff --git a/mutmut/__init__.py b/mutmut/__init__.py
index 71088c4..0d98509 100644
--- a/mutmut/__init__.py
+++ b/mutmut/__init__.py
@@ -35,6 +35,8 @@ from parso.python.tree import Name, Number, Keyword
 __version__ = '2.3.0'
 
 
+if os.getcwd() not in sys.path:
+    sys.path.insert(0, os.getcwd())
 try:
     import mutmut_config
 except ImportError:
diff --git a/mutmut/__main__.py b/mutmut/__main__.py
index 566521a..4b6fb74 100644
--- a/mutmut/__main__.py
+++ b/mutmut/__main__.py
@@ -48,9 +48,6 @@ from mutmut.cache import print_result_cache, print_result_ids_cache, \
 from collections import namedtuple
 import re
 
-if os.getcwd() not in sys.path:
-    sys.path.insert(0, os.getcwd())
-
 
 def do_apply(mutation_pk, dict_synonyms, backup):
     """Apply a specified mutant to the source code
@@ -181,7 +178,7 @@ def main(command, argument, argument2, paths_to_mutate, disable_mutation_types,
         mutation_types_to_apply = set(mutations_by_type.keys())
         invalid_types = None
     if invalid_types:
-        raise click.BadArgumentUsage(f"The following are not valid mutation types: {', '.join(invalid_types)}. Valid mutation types are: {', '.join(mutations_by_type.keys())}")
+        raise click.BadArgumentUsage(f"The following are not valid mutation types: {', '.join(sorted(invalid_types))}. Valid mutation types are: {', '.join(mutations_by_type.keys())}")
 
     valid_commands = ['run', 'results', 'result-ids', 'apply', 'show', 'junitxml', 'html']
     if command not in valid_commands:


Test patch:
diff --git a/tests/test_main.py b/tests/test_main.py
index 04ec25a..09fda7f 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -506,7 +506,7 @@ def test_select_unknown_mutation_type(option):
         ]
     )
     assert result.exception.code == 2
-    assert f"The following are not valid mutation types: foo, bar. Valid mutation types are: {', '.join(mutations_by_type.keys())}" in result.output
+    assert f"The following are not valid mutation types: bar, foo. Valid mutation types are: {', '.join(mutations_by_type.keys())}" in result.output, result.output
 
 
 def test_enable_and_disable_mutation_type_are_exclusive():
diff --git a/tests/test_mutmut_config_hooks.py b/tests/test_mutmut_config_hooks.py
new file mode 100644
index 0000000..629f323
--- /dev/null
+++ b/tests/test_mutmut_config_hooks.py
@@ -0,0 +1,57 @@
+import os
+import sys
+import subprocess
+
+import pytest
+
+
+@pytest.fixture
+def basic_filesystem(tmpdir):
+    source_file = tmpdir / "foo.py"
+    source_file.write("def add(a, b): return a + b")
+    tests_dir = tmpdir / "tests"
+    tests_dir.mkdir()
+    test_file = tests_dir / "test_foo.py"
+    test_file.write("""
+from foo import add
+
+def test_add(): 
+    assert add(1, 1) == 2
+""")
+    mutmut_config_py = tmpdir / "mutmut_config.py"
+    mutmut_config_py.write(f"""
+from pathlib import Path
+
+def init(): 
+    Path("init_hook").touch()
+
+def pre_mutation(context):
+    Path("pre_mutation_hook").touch()
+
+def pre_mutation_ast(context):
+    Path("pre_mutation_ast_hook").touch()
+""")
+    yield tmpdir
+
+
+@pytest.fixture
+def set_working_dir_and_path(basic_filesystem):
+    original_dir = os.path.abspath(os.getcwd())
+    original_path = sys.path[:]
+
+    os.chdir(basic_filesystem)
+    if str(basic_filesystem) in sys.path:
+        sys.path.remove(str(basic_filesystem))
+    
+    yield basic_filesystem
+
+    sys.path = original_path
+    os.chdir(original_dir)
+
+
+@pytest.mark.usefixtures("set_working_dir_and_path")
+def test_hooks(basic_filesystem):
+    subprocess.check_output(["python",  "-m", "mutmut", "run", "--paths-to-mutate=foo.py"])
+    assert (basic_filesystem / "init_hook").exists(), "init was not called."
+    assert (basic_filesystem / "pre_mutation_hook").exists(), "pre_mutation was not called."
+    assert (basic_filesystem / "pre_mutation_ast_hook").exists(), "pre_mutation_ast was not called."