Repository: ipython/ipython

Issue:
Changing `c.TerminalInteractiveShell.autosuggestions_provider` in the configuration file will cause IPython shell to fail to start
<!-- This is the repository for IPython command line, if you can try to make sure this question/bug/feature belong here and not on one of the Jupyter repositories. 

If it's a generic Python/Jupyter question, try other forums or discourse.jupyter.org.

If you are unsure, it's ok to post here, though, there are few maintainer so you might not get a fast response. 

-->
IPython version: 8.9.0, Python version: CPython 3.10.9

The default value of the configuration item `c.TerminalInteractiveShell.autosuggestions_provider` is `'NavigableAutoSuggestFromHistory'`. And the documentation said:

> \## Specifies from which source automatic suggestions are provided. Can be set to
> \#  ``'NavigableAutoSuggestFromHistory'`` (:kbd:`up` and :kbd:`down` swap
> \#  suggestions), ``'AutoSuggestFromHistory'``,  or ``None`` to disable automatic
> \#  suggestions. Default is `'NavigableAutoSuggestFromHistory`'.
> \#  Default: 'NavigableAutoSuggestFromHistory'

But if you set it to `'AutoSuggestFromHistory'` or `None` will cause IPython shell to fail to start (Extra-detailed tracebacks is enabled):

```
$ > ipython
Error in sys.excepthook:
Traceback (most recent call last):
  File "/usr/lib/python3.10/pathlib.py", line 1305, in is_dir
    return S_ISDIR(self.stat().st_mode)
AttributeError: 'str' object has no attribute 'stat'

Original exception was:
Traceback (most recent call last):
  File "/usr/bin/ipython", line 8, in <module>
    sys.exit(start_ipython())
  File "/usr/lib/python3.10/site-packages/IPython/__init__.py", line 124, in start_ipython
    return launch_new_instance(argv=argv, **kwargs)
  File "/usr/lib/python3.10/site-packages/traitlets/config/application.py", line 1040, in launch_instance
    app.initialize(argv)
  File "/usr/lib/python3.10/site-packages/traitlets/config/application.py", line 113, in inner
    return method(app, *args, **kwargs)
  File "/usr/lib/python3.10/site-packages/IPython/terminal/ipapp.py", line 279, in initialize
    self.init_shell()
  File "/usr/lib/python3.10/site-packages/IPython/terminal/ipapp.py", line 293, in init_shell
    self.shell = self.interactive_shell_class.instance(parent=self,
  File "/usr/lib/python3.10/site-packages/traitlets/config/configurable.py", line 551, in instance
    inst = cls(*args, **kwargs)
  File "/usr/lib/python3.10/site-packages/IPython/terminal/interactiveshell.py", line 687, in __init__
    super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
  File "/usr/lib/python3.10/site-packages/IPython/core/interactiveshell.py", line 560, in __init__
    super(InteractiveShell, self).__init__(**kwargs)
  File "/usr/lib/python3.10/site-packages/traitlets/config/configurable.py", line 109, in __init__
    self.config = config
  File "/usr/lib/python3.10/site-packages/traitlets/traitlets.py", line 732, in __set__
    self.set(obj, value)
  File "/usr/lib/python3.10/site-packages/traitlets/traitlets.py", line 721, in set
    obj._notify_trait(self.name, old_value, new_value)
  File "/usr/lib/python3.10/site-packages/traitlets/traitlets.py", line 1505, in _notify_trait
    self.notify_change(
  File "/usr/lib/python3.10/site-packages/traitlets/traitlets.py", line 1517, in notify_change
    return self._notify_observers(change)
  File "/usr/lib/python3.10/site-packages/traitlets/traitlets.py", line 1564, in _notify_observers
    c(event)
  File "/usr/lib/python3.10/site-packages/traitlets/traitlets.py", line 1146, in compatible_observer
    return func(self, change)
  File "/usr/lib/python3.10/site-packages/traitlets/config/configurable.py", line 218, in _config_changed
    self._load_config(change.new, traits=traits, section_names=section_names)
  File "/usr/lib/python3.10/site-packages/traitlets/config/configurable.py", line 167, in _load_config
    with self.hold_trait_notifications():
  File "/usr/lib/python3.10/contextlib.py", line 142, in __exit__
    next(self.gen)
  File "/usr/lib/python3.10/site-packages/traitlets/traitlets.py", line 1502, in hold_trait_notifications
    self.notify_change(change)
  File "/usr/lib/python3.10/site-packages/traitlets/traitlets.py", line 1517, in notify_change
    return self._notify_observers(change)
  File "/usr/lib/python3.10/site-packages/traitlets/traitlets.py", line 1564, in _notify_observers
    c(event)
  File "/usr/lib/python3.10/site-packages/IPython/terminal/interactiveshell.py", line 417, in _autosuggestions_provider_changed
    self._set_autosuggestions(provider)
  File "/usr/lib/python3.10/site-packages/IPython/terminal/interactiveshell.py", line 399, in _set_autosuggestions
    if self.auto_suggest and isinstance(
AttributeError: 'TerminalInteractiveShell' object has no attribute 'auto_suggest'
```


Discussion:

Resolving patch:
diff --git a/IPython/terminal/interactiveshell.py b/IPython/terminal/interactiveshell.py
--- a/IPython/terminal/interactiveshell.py
+++ b/IPython/terminal/interactiveshell.py
@@ -147,10 +147,6 @@ class PtkHistoryAdapter(History):
 
     """
 
-    auto_suggest: UnionType[
-        AutoSuggestFromHistory, NavigableAutoSuggestFromHistory, None
-    ]
-
     def __init__(self, shell):
         super().__init__()
         self.shell = shell
@@ -193,6 +189,9 @@ class TerminalInteractiveShell(InteractiveShell):
                             ).tag(config=True)
 
     pt_app: UnionType[PromptSession, None] = None
+    auto_suggest: UnionType[
+        AutoSuggestFromHistory, NavigableAutoSuggestFromHistory, None
+    ] = None
     debugger_history = None
 
     debugger_history_file = Unicode(
@@ -685,7 +684,6 @@ def init_alias(self):
 
     def __init__(self, *args, **kwargs) -> None:
         super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
-        self.auto_suggest = None
         self._set_autosuggestions(self.autosuggestions_provider)
         self.init_prompt_toolkit_cli()
         self.init_term_title()


Test patch:
diff --git a/IPython/terminal/tests/test_interactivshell.py b/IPython/terminal/tests/test_interactivshell.py
--- a/IPython/terminal/tests/test_interactivshell.py
+++ b/IPython/terminal/tests/test_interactivshell.py
@@ -7,11 +7,25 @@
 import unittest
 import os
 
+from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
+
 from IPython.core.inputtransformer import InputTransformer
 from IPython.testing import tools as tt
 from IPython.utils.capture import capture_output
 
 from IPython.terminal.ptutils import _elide, _adjust_completion_text_based_on_context
+from IPython.terminal.shortcuts.auto_suggest import NavigableAutoSuggestFromHistory
+
+
+class TestAutoSuggest(unittest.TestCase):
+    def test_changing_provider(self):
+        ip = get_ipython()
+        ip.autosuggestions_provider = None
+        self.assertEqual(ip.auto_suggest, None)
+        ip.autosuggestions_provider = "AutoSuggestFromHistory"
+        self.assertIsInstance(ip.auto_suggest, AutoSuggestFromHistory)
+        ip.autosuggestions_provider = "NavigableAutoSuggestFromHistory"
+        self.assertIsInstance(ip.auto_suggest, NavigableAutoSuggestFromHistory)
 
 
 class TestElide(unittest.TestCase):
@@ -24,10 +38,10 @@ def test_elide(self):
         )
 
         test_string = os.sep.join(["", 10 * "a", 10 * "b", 10 * "c", ""])
-        expect_stirng = (
+        expect_string = (
             os.sep + "a" + "\N{HORIZONTAL ELLIPSIS}" + "b" + os.sep + 10 * "c"
         )
-        self.assertEqual(_elide(test_string, ""), expect_stirng)
+        self.assertEqual(_elide(test_string, ""), expect_string)
 
     def test_elide_typed_normal(self):
         self.assertEqual(