Repository: ipython/traitlets (python)

Issue:
KeyError in get_default_logging_config
If sys.executable is set to pythonw.exe calling get_default_logging_config results in a KeyError because it tries to delete a key that doesn't exist.

https://github.com/ipython/traitlets/blob/main/traitlets/config/application.py#L259

```
2022-05-12 15:42:05,811 - ERROR :   File "C:\Users\tony\miniconda3\envs\py39-7165\lib\site-packages\traitlets\config\application.py", line 259, in get_default_logging_config
2022-05-12 15:42:05,812 - ERROR :     del config["handlers"]["loggers"]
2022-05-12 15:42:05,812 - ERROR : KeyError: 'loggers'

Pull request description:
Fix KeyError in Application.get_default_logging_config
Fixes #723

Patch:
diff --git a/traitlets/config/application.py b/traitlets/config/application.py
index e5a8686..efff262 100644
--- a/traitlets/config/application.py
+++ b/traitlets/config/application.py
@@ -92,6 +92,9 @@ else:
     )
 
 
+IS_PYTHONW = sys.executable and sys.executable.endswith("pythonw.exe")
+
+
 def catch_config_error(method):
     """Method decorator for catching invalid config (Trait/ArgumentErrors) during init.
 
@@ -252,11 +255,12 @@ class Application(SingletonConfigurable):
             "disable_existing_loggers": False,
         }
 
-        if sys.executable and sys.executable.endswith("pythonw.exe"):
+        if IS_PYTHONW:
             # disable logging
             # (this should really go to a file, but file-logging is only
             # hooked up in parallel applications)
-            del config["handlers"]["loggers"]
+            del config["handlers"]
+            del config["loggers"]
 
         return config
 


Test patch:
diff --git a/traitlets/config/tests/test_application.py b/traitlets/config/tests/test_application.py
index 4c64811..f38765c 100644
--- a/traitlets/config/tests/test_application.py
+++ b/traitlets/config/tests/test_application.py
@@ -804,6 +804,19 @@ def test_logging_config(tmp_path, capsys):
     assert capsys.readouterr().err == "[Application] WARNING | warn\n"
 
 
+def test_get_default_logging_config_pythonw(monkeypatch):
+    """Ensure logging is correctly disabled for pythonw usage."""
+    monkeypatch.setattr("traitlets.config.application.IS_PYTHONW", True)
+    config = Application().get_default_logging_config()
+    assert "handlers" not in config
+    assert "loggers" not in config
+
+    monkeypatch.setattr("traitlets.config.application.IS_PYTHONW", False)
+    config = Application().get_default_logging_config()
+    assert "handlers" in config
+    assert "loggers" in config
+
+
 @pytest.fixture
 def caplogconfig(monkeypatch):
     """Capture logging config events for DictConfigurator objects.
@@ -826,6 +839,7 @@ def caplogconfig(monkeypatch):
     return calls
 
 
+@pytest.mark.skipif(sys.implementation.name == "pypy", reason="Test does not work on pypy")
 def test_logging_teardown_on_error(capsys, caplogconfig):
     """Ensure we don't try to open logs in order to close them (See #722).