Repository: spyder-ide/spyder

Issue:
PYTHONPATH manager does not work with unicode characters in path 
## Problem Description
Unable to add a path with a unicode character, for example "ö", using the path manager.
Adding the path actually works, and the string is written to the `path` file under spyder configuration directory, but it is not read back from there. If later adding an additional path the unicode path is also removed from the `path` file.

Tested on 4.2.5 and 5.0.3. If it turns out an easy fix would be great to have this on both branches.

### What steps reproduce the problem?

1. Add a set of paths, some with unicode characters.
Example:
![image](https://user-images.githubusercontent.com/2330659/119460664-34b81280-bd3f-11eb-86db-3b7efd1ac9b9.png)

2. Click OK, see that all the path strings are written to the `path` file under spyder configuration directory
3. Reopen the PYTHONPATH manager, unicode paths are not populated in the list. Try to import from the unicode paths (not working). 
![image](https://user-images.githubusercontent.com/2330659/119460497-0b978200-bd3f-11eb-9a83-a786afd20e7f.png)


### What is the expected output? What do you see instead?
It is possible to add this type of path using sys.path.append(), and the expectation is that the path manager can also handle this.


## Versions
* Spyder version: 5.0.3 None
* Python version: 3.7.9 64-bit
* Qt version: 5.12.10
* PyQt5 version: 5.12.3
* Operating System: Windows 10
### Dependencies
<!--- Please go to the menu entry Help > Dependencies,
press the Copy to clipboard button and paste below --->
<details>
```

# Mandatory:
atomicwrites >=1.2.0          :  1.4.0 (OK)
chardet >=2.0.0               :  4.0.0 (OK)
cloudpickle >=0.5.0           :  1.6.0 (OK)
cookiecutter >=1.6.0          :  1.7.3 (OK)
diff_match_patch >=20181111   :  20200713 (OK)
intervaltree                  :  None (OK)
IPython >=7.6.0               :  7.23.1 (OK)
jedi =0.17.2                  :  0.17.2 (OK)
jsonschema >=3.2.0            :  3.2.0 (OK)
keyring >=17.0.0              :  23.0.1 (OK)
nbconvert >=4.0               :  6.0.7 (OK)
numpydoc >=0.6.0              :  1.1.0 (OK)
paramiko >=2.4.0              :  2.7.2 (OK)
parso =0.7.0                  :  0.7.0 (OK)
pexpect >=4.4.0               :  4.8.0 (OK)
pickleshare >=0.4             :  0.7.5 (OK)
psutil >=5.3                  :  5.8.0 (OK)
pygments >=2.0                :  2.9.0 (OK)
pylint >=1.0                  :  2.8.2 (OK)
pyls >=0.36.2;<1.0.0          :  0.36.2 (OK)
pyls_black >=0.4.6            :  0.4.6 (OK)
pyls_spyder >=0.3.2;<0.4.0    :  0.3.2 (OK)
qdarkstyle =3.0.2             :  3.0.2 (OK)
qstylizer >=0.1.10            :  0.2.0 (OK)
qtawesome >=1.0.2             :  1.0.2 (OK)
qtconsole >=5.1.0             :  5.1.0 (OK)
qtpy >=1.5.0                  :  1.9.0 (OK)
rtree >=0.9.7                 :  0.9.7 (OK)
setuptools >=39.0.0           :  56.2.0 (OK)
sphinx >=0.6.6                :  3.5.4 (OK)
spyder_kernels >=2.0.3;<2.1.0 :  2.0.3 (OK)
textdistance >=4.2.0          :  4.2.1 (OK)
three_merge >=0.1.1           :  0.1.1 (OK)
watchdog                      :  1.0.2 (OK)
zmq >=17                      :  22.0.3 (OK)

# Optional:
cython >=0.21                 :  0.29.23 (OK)
matplotlib >=2.0.0            :  3.4.2 (OK)
numpy >=1.7                   :  1.19.3 (OK)
pandas >=1.1.1                :  1.2.4 (OK)
scipy >=0.17.0                :  1.6.3 (OK)
sympy >=0.7.3                 :  1.8 (OK)

```
</details>


Discussion:
Had a little look. Below what I _think_ is causing the issue.

Appears to be the function `encoding()` imported from `spyder.utils` that is unable to handle the characters 

See `load_python_path()` in `mainwindow.py` (line 1858). Reads the path (line 1861) and rejects it since its not a directory (line 1862).

    1858            def load_python_path(self):
                    """Load path stored in Spyder configuration folder."""
                        if osp.isfile(self.SPYDER_PATH):
    1861                  path, _x = encoding.readlines(self.SPYDER_PATH)
    1862                  self.path = tuple(name for name in path if osp.isdir(name))

Example

      encoding.readlines(path_config_file)
      Out: (['c:\\test\\jam', 'c:\\test\\Ã¶rn', ''], 'ISO-8859-9')

In encoding.py
Function `readlines()` has default argument `encoding='utf-8'`, which is passed to function `read()` which again calls `decode()`. However, `decode()` does not use the `encoding` as input, but tries to detect the encoding and returns it (so the `encoding='utf-8'` is ignored essentially).
Hi @rhkarls,

Thanks for opening this issue, we indeed need to fix this behavior. If you already found the fix please consider to contribute to the project and send a PR with those changes. If you need any guidance please let me know.
Cheers @steff456 ! I think I can provide a simple fix for this, and we'll see if its acceptable :)

Resolving patch:
diff --git a/spyder/app/mainwindow.py b/spyder/app/mainwindow.py
--- a/spyder/app/mainwindow.py
+++ b/spyder/app/mainwindow.py
@@ -1858,12 +1858,14 @@ def open_external_file(self, fname):
     def load_python_path(self):
         """Load path stored in Spyder configuration folder."""
         if osp.isfile(self.SPYDER_PATH):
-            path, _x = encoding.readlines(self.SPYDER_PATH)
+            with open(self.SPYDER_PATH, 'r', encoding='utf-8') as f:
+                path = f.read().splitlines()
             self.path = tuple(name for name in path if osp.isdir(name))
 
         if osp.isfile(self.SPYDER_NOT_ACTIVE_PATH):
-            not_active_path, _x = encoding.readlines(
-                self.SPYDER_NOT_ACTIVE_PATH)
+            with open(self.SPYDER_NOT_ACTIVE_PATH, 'r',
+                      encoding='utf-8') as f:
+                not_active_path = f.read().splitlines()
             self.not_active_path = tuple(name for name in not_active_path
                                          if osp.isdir(name))
 


Test patch: