Repository: spyder-ide/spyder

Issue:
Another KeyError while autosaving
## Description

### What steps will reproduce the problem?

<!--- You can use Markdown here --->

I have no idea; this only happens while I'm typing, seemingly at random. 

Also, there is a copy anaconda running and it will. not. stop. Task manager can't close it. Powershell in admin mode still doesn't have permission. I want to downgrade to a more stable version but I cannot without rebooting. 

### Traceback
```python-traceback
  File "C:\Users\joven\Anaconda3\lib\site-packages\spyder\plugins\editor\utils\autosave.py", line 116, in do_autosave
    stack.autosave.autosave_all()
  File "C:\Users\joven\Anaconda3\lib\site-packages\spyder\plugins\editor\utils\autosave.py", line 375, in autosave_all
    self.maybe_autosave(index)
  File "C:\Users\joven\Anaconda3\lib\site-packages\spyder\plugins\editor\utils\autosave.py", line 335, in maybe_autosave
    orig_hash = self.file_hashes[orig_filename]
KeyError: 'C:\\Users\\joven\\.spyder-py3\\autosave\\optpath.py'
```

## Versions

* Spyder version: 4.0.1 
* Python version: 3.6.5
* Qt version: 5.9.4
* PyQt5 version: 5.9.2
* Operating System: Windows 10

### Dependencies

```
atomicwrites >=1.2.0         :  1.3.0 (OK)
chardet >=2.0.0              :  3.0.4 (OK)
cloudpickle >=0.5.0          :  1.2.2 (OK)
diff_match_patch >=20181111  :  20181111 (OK)
intervaltree                 :  None (OK)
IPython >=4.0                :  7.8.0 (OK)
jedi =0.14.1                 :  0.14.1 (OK)
nbconvert >=4.0              :  5.6.0 (OK)
numpydoc >=0.6.0             :  0.9.1 (OK)
pexpect >=4.4.0              :  4.8.0 (OK)
pickleshare >=0.4            :  0.7.5 (OK)
psutil >=0.3                 :  5.6.3 (OK)
pygments >=2.0               :  2.4.2 (OK)
pylint >=0.25                :  2.4.2 (OK)
pyls >=0.31.2;<0.32.0        :  0.31.7 (OK)
zmq >=17                     :  18.1.0 (OK)
qdarkstyle >=2.7             :  2.8 (OK)
qtawesome >=0.5.7            :  0.6.0 (OK)
qtconsole >=4.6.0            :  4.6.0 (OK)
qtpy >=1.5.0                 :  1.9.0 (OK)
rtree >=0.8.3                :  0.9.3 (OK)
sphinx >=0.6.6               :  2.2.0 (OK)
spyder_kernels >=1.8.1;<2.0.0:  1.8.1 (OK)
watchdog                     :  None (OK)
cython >=0.21                :  None (NOK)
matplotlib >=2.0.0           :  None (NOK)
numpy >=1.7                  :  1.16.5 (OK)
pandas >=0.13.1              :  None (NOK)
scipy >=0.17.0               :  None (NOK)
sympy >=0.7.3                :  None (NOK)
```



Discussion:
@jitseniesen, do you understand when this error can appear?
> do you understand when this error can appear?

Not immediately, but I think it means that the autosave file `C:\Users\joven\.spyder-py3\autosave\optpath.py` is open in the editor. 

@Rectangle , is that correct? As a work around you can try closing that file.

I will need to think a bit about exactly what the problem could be.


Hi I also landed into the same problem after updating spyder, I was coding then it crashed all of a sudden. What could be the problem?
Well, after having this error followed by:

- Reinstalling anaconda (which takes about 20 minutes),
- Watching anaconda try and fail to upgrade spyder, time after time, leaving me watching an infinite loading bar,
- Reinstalling it again (20 minutes)
- Finding out, after trial and error, that if you install it for all users, the command prompt can't actually find anything,
- Reinstalling it again (20 minutes)
- Now finding that upgrading spyder isn't even a visible option, somehow
- Realizing that all I want to do is run code I've written in a language that is supposed to be easy to use, and this shouldn't require hours of frustration just to figure out,

I've finally found the solution to the problem: uninstalling anaconda. 

Resolving patch:
diff --git a/spyder/plugins/editor/utils/autosave.py b/spyder/plugins/editor/utils/autosave.py
--- a/spyder/plugins/editor/utils/autosave.py
+++ b/spyder/plugins/editor/utils/autosave.py
@@ -4,7 +4,26 @@
 # Licensed under the terms of the MIT License
 # (see spyder/__init__.py for details)
 
-"""Autosave components for the Editor plugin and the EditorStack widget"""
+"""
+Autosave components for the Editor plugin and the EditorStack widget
+
+The autosave system regularly checks the contents of all opened files and saves
+a copy in the autosave directory if the contents are different from the
+autosave file (if it exists) or original file (if there is no autosave file).
+
+The mapping between original files and autosave files is stored in the
+variable `name_mapping` and saved in the file `pidNNN.txt` in the autosave
+directory, where `NNN` stands for the pid. This filename is chosen so that
+multiple instances of Spyder can run simultaneously.
+
+File contents are compared using their hash. The variable `file_hashes`
+contains the hash of all files currently open in the editor and all autosave
+files.
+
+On startup, the contents of the autosave directory is checked and if autosave
+files are found, the user is asked whether to recover them;
+see `spyder/plugins/editor/widgets/recover.py`.
+"""
 
 # Standard library imports
 import ast
@@ -339,7 +358,15 @@ def maybe_autosave(self, index):
         if finfo.newly_created:
             return
         orig_filename = finfo.filename
-        orig_hash = self.file_hashes[orig_filename]
+        try:
+            orig_hash = self.file_hashes[orig_filename]
+        except KeyError:
+            # This should not happen, but it does: spyder-ide/spyder#11468
+            # In this case, use an impossible value for the hash, so that
+            # contents of buffer are considered different from contents of
+            # original file.
+            logger.error('KeyError when retrieving hash of %s', orig_filename)
+            orig_hash = None
         new_hash = self.stack.compute_hash(finfo)
         if orig_filename in self.name_mapping:
             autosave_filename = self.name_mapping[orig_filename]


Test patch:
diff --git a/spyder/plugins/editor/utils/tests/test_autosave.py b/spyder/plugins/editor/utils/tests/test_autosave.py
--- a/spyder/plugins/editor/utils/tests/test_autosave.py
+++ b/spyder/plugins/editor/utils/tests/test_autosave.py
@@ -146,7 +146,8 @@ def test_try_recover(mocker, tmpdir, error_on_remove):
         assert not pidfile.check()
 
 
-def test_autosave(mocker):
+@pytest.mark.parametrize('have_hash', [True, False])
+def test_autosave(mocker, have_hash):
     """Test that AutosaveForStack.maybe_autosave writes the contents to the
     autosave file and updates the file_hashes."""
     mock_editor = mocker.Mock()
@@ -157,14 +158,19 @@ def test_autosave(mocker):
     mock_stack = mocker.Mock(data=[mock_fileinfo])
     addon = AutosaveForStack(mock_stack)
     addon.name_mapping = {'orig': 'autosave'}
-    addon.file_hashes = {'orig': 1, 'autosave': 2}
+    addon.file_hashes = {'autosave': 2}
+    if have_hash:
+        addon.file_hashes['orig'] = 1
     mock_stack.compute_hash.return_value = 3
 
     addon.maybe_autosave(0)
 
     mock_stack._write_to_file.assert_called_with(mock_fileinfo, 'autosave')
     mock_stack.compute_hash.assert_called_with(mock_fileinfo)
-    assert addon.file_hashes == {'orig': 1, 'autosave': 3}
+    if have_hash:
+        assert addon.file_hashes == {'orig': 1, 'autosave': 3}
+    else:
+        assert addon.file_hashes == {'autosave': 3}
 
 
 def test_save_autosave_mapping_with_nonempty_mapping(mocker, tmpdir):