Repository: spyder-ide/spyder

Issue:
OSError when saving files on Linux
## Problem Description

Attempting to save a .py script in spyder4 beta5 leads to error. 

Save Error
Unable to save file 'test_save.py'
Error message:
[Errno 22] Invalid argument

Apologies if simple issue.

### What steps reproduce the problem?

1.  Open spyder4 in terminal
2.  Create new script
3.  Attempt to save script

### What is the expected output? What do you see instead?
Expect to save script without problem.
Instead, see error box as above, resulting in no save.


### Paste Traceback/Error Below (if applicable)
<!--- Copy from error dialog or View > Panes > Internal Console --->

```python
ERROR:spyder.plugins.editor.widgets.autosaveerror:Error while autosaving /scratch/yms23/netscratch/PhD/Python/Scripts/test_save.py to /home/yms23/.config/spyder-py3-dev/autosave/test_save.py
Traceback (most recent call last):
  File "/home/yms23/.conda/envs/spyder4beta/lib/python3.7/site-packages/spyder/plugins/editor/widgets/editor.py", line 1818, in save
    self._write_to_file(finfo, finfo.filename)
  File "/home/yms23/.conda/envs/spyder4beta/lib/python3.7/site-packages/spyder/plugins/editor/widgets/editor.py", line 1777, in _write_to_file
    fileinfo.encoding = encoding.write(txt, filename, fileinfo.encoding)
  File "/home/yms23/.conda/envs/spyder4beta/lib/python3.7/site-packages/spyder/utils/encoding.py", line 251, in write
    textfile.write(text)
  File "/home/yms23/.conda/envs/spyder4beta/lib/python3.7/contextlib.py", line 119, in __exit__
    next(self.gen)
  File "/home/yms23/.conda/envs/spyder4beta/lib/python3.7/site-packages/atomicwrites/__init__.py", line 159, in _open
    self.commit(f)
  File "/home/yms23/.conda/envs/spyder4beta/lib/python3.7/site-packages/atomicwrites/__init__.py", line 192, in commit
    replace_atomic(f.name, self._path)
  File "/home/yms23/.conda/envs/spyder4beta/lib/python3.7/site-packages/atomicwrites/__init__.py", line 93, in replace_atomic
    return _replace_atomic(src, dst)
  File "/home/yms23/.conda/envs/spyder4beta/lib/python3.7/site-packages/atomicwrites/__init__.py", line 50, in _replace_atomic
    _sync_directory(os.path.normpath(os.path.dirname(dst)))
  File "/home/yms23/.conda/envs/spyder4beta/lib/python3.7/site-packages/atomicwrites/__init__.py", line 44, in _sync_directory
    _proper_fsync(fd)
OSError: [Errno 22] Invalid argument

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/yms23/.conda/envs/spyder4beta/lib/python3.7/site-packages/spyder/plugins/editor/utils/autosave.py", line 362, in autosave
    self.stack._write_to_file(finfo, autosave_filename)
  File "/home/yms23/.conda/envs/spyder4beta/lib/python3.7/site-packages/spyder/plugins/editor/widgets/editor.py", line 1777, in _write_to_file
    fileinfo.encoding = encoding.write(txt, filename, fileinfo.encoding)
  File "/home/yms23/.conda/envs/spyder4beta/lib/python3.7/site-packages/spyder/utils/encoding.py", line 251, in write
    textfile.write(text)
  File "/home/yms23/.conda/envs/spyder4beta/lib/python3.7/contextlib.py", line 119, in __exit__
    next(self.gen)
  File "/home/yms23/.conda/envs/spyder4beta/lib/python3.7/site-packages/atomicwrites/__init__.py", line 159, in _open
    self.commit(f)
  File "/home/yms23/.conda/envs/spyder4beta/lib/python3.7/site-packages/atomicwrites/__init__.py", line 192, in commit
    replace_atomic(f.name, self._path)
  File "/home/yms23/.conda/envs/spyder4beta/lib/python3.7/site-packages/atomicwrites/__init__.py", line 93, in replace_atomic
    return _replace_atomic(src, dst)
  File "/home/yms23/.conda/envs/spyder4beta/lib/python3.7/site-packages/atomicwrites/__init__.py", line 50, in _replace_atomic
    _sync_directory(os.path.normpath(os.path.dirname(dst)))
  File "/home/yms23/.conda/envs/spyder4beta/lib/python3.7/site-packages/atomicwrites/__init__.py", line 44, in _sync_directory
    _proper_fsync(fd)
OSError: [Errno 22] Invalid argument
Traceback (most recent call last):
  File "/home/yms23/.conda/envs/spyder4beta/lib/python3.7/site-packages/spyder/plugins/editor/utils/autosave.py", line 116, in do_autosave
    stack.autosave.autosave_all()
  File "/home/yms23/.conda/envs/spyder4beta/lib/python3.7/site-packages/spyder/plugins/editor/utils/autosave.py", line 374, in autosave_all
    self.maybe_autosave(index)
  File "/home/yms23/.conda/envs/spyder4beta/lib/python3.7/site-packages/spyder/plugins/editor/utils/autosave.py", line 338, in maybe_autosave
    autosave_hash = self.file_hashes[autosave_filename]
KeyError: '/home/yms23/.config/spyder-py3-dev/autosave/test_save.py'
```

## Versions
<!--- You can get this information from Help > About Spyder...
or (if Spyder won't launch) the "conda list" command
from the Anaconda Prompt/Terminal/command line. --->

* Spyder version:  4.0.0b5
* Python version:  3.7.4
* Qt version:  5.9.6
* PyQt version:  5.9.2
* Operating System name/version:  Linux 4.4.0-161-generic 

### Dependencies
<!--- Please go to the menu entry Help > Dependencies,
press the Copy to clipboard button and paste below --->

cloudpickle >=0.5.0          :  1.2.1 (OK)
pygments >=2.0               :  2.4.2 (OK)
qtconsole >=4.5.5            :  4.5.5 (OK)
nbconvert >=4.0              :  5.5.0 (OK)
sphinx >=0.6.6               :  2.1.2 (OK)
pylint >=0.25                :  2.3.1 (OK)
psutil >=0.3                 :  5.6.3 (OK)
qtawesome >=0.5.7            :  0.5.7 (OK)
qtpy >=1.5.0                 :  1.9.0 (OK)
pickleshare >=0.4            :  0.7.5 (OK)
zmq >=17                     :  18.1.0 (OK)
chardet >=2.0.0              :  3.0.4 (OK)
numpydoc >=0.6.0             :  0.9.1 (OK)
spyder_kernels >=1.5.0;<2.0.0:  1.5.0 (OK)
qdarkstyle >=2.7             :  2.7 (OK)
atomicwrites >=1.2.0         :  1.3.0 (OK)
diff_match_patch >=20181111  :  20181111 (OK)
watchdog                     :  None (OK)
keyring                      :  None (OK)
pexpect >=4.4.0              :  4.7.0 (OK)
pympler                      :  None (OK)
sympy >=0.7.3                :  None (NOK)
cython >=0.21                :  None (NOK)
IPython >=4.0                :  7.7.0 (OK)
matplotlib >=2.0.0           :  3.1.0 (OK)
pandas >=0.13.1              :  0.25.0 (OK)
numpy >=1.7                  :  1.16.4 (OK)
scipy >=0.17.0               :  1.3.1 (OK)
pyls >=0.28.2;<0.29.0        :  0.28.3 (OK)
xdg >=0.26                   :  0.26 (OK)
rtree >=0.8.3                :  0.8.3 (OK)



Discussion:
This happens because some filesystems don't support the option to sync directories (e.g. network file systems), as described in issue https://github.com/untitaker/python-atomicwrites/issues/17

@andfoy, please take care of this one. We basically need to catch the error thrown by `atomicwrites` and perform a regular write instead. Code to catch that error is shown in PR https://github.com/untitaker/python-atomicwrites/pull/28

@matthew-shin, until this problem is fixed I'm afraid you'll have to keep using Spyder 3.3.6.
Unfortunate. Will revert to spyder3; thanks for the response!
Sorry @andfoy, let's assign this one to @steff456 because it should be relatively easy.

Resolving patch:
diff --git a/spyder/plugins/editor/widgets/editor.py b/spyder/plugins/editor/widgets/editor.py
--- a/spyder/plugins/editor/widgets/editor.py
+++ b/spyder/plugins/editor/widgets/editor.py
@@ -35,7 +35,8 @@
 from spyder.config.gui import config_shortcut, is_dark_interface, get_shortcut
 from spyder.config.utils import (get_edit_filetypes, get_edit_filters,
                                  get_filter, is_kde_desktop, is_anaconda)
-from spyder.py3compat import qbytearray_to_str, to_text_string, MutableSequence
+from spyder.py3compat import (qbytearray_to_str, to_text_string,
+                              MutableSequence)
 from spyder.utils import icon_manager as ima
 from spyder.utils import encoding, sourcecode, syntaxhighlighters
 from spyder.utils.qthelpers import (add_actions, create_action,
diff --git a/spyder/utils/encoding.py b/spyder/utils/encoding.py
--- a/spyder/utils/encoding.py
+++ b/spyder/utils/encoding.py
@@ -17,6 +17,7 @@
 import re
 import os
 import sys
+import errno
 
 # Third-party imports
 from chardet.universaldetector import UniversalDetector
@@ -245,10 +246,16 @@ def write(text, filename, encoding='utf-8', mode='wb'):
             umask = os.umask(0)
             os.umask(umask)
             original_mode = 0o777 & ~umask
-        with atomic_write(filename,
-                          overwrite=True,
-                          mode=mode) as textfile:
-            textfile.write(text)
+        try:
+            with atomic_write(filename, overwrite=True,
+                              mode=mode) as textfile:
+                textfile.write(text)
+        except OSError as error:
+            # Some filesystems don't support the option to sync directories
+            # See untitaker/python-atomicwrites#17
+            if error.errno != errno.EINVAL:
+                with open(filename, mode) as textfile:
+                    textfile.write(text)
         os.chmod(filename, original_mode)
     return encoding
 


Test patch: