Repository: spyder-ide/spyder

Issue:
Deleting a folder which contains a sub-folder(s) raises an error
When deleting a folder which contains subfolders(s), an error is raised saying that cannot delete the folder. But eventually folder's contents are deleted.

![delete_folder_issue](https://user-images.githubusercontent.com/22060413/51087008-99add780-1745-11e9-982b-623c3e958050.gif)

Spyder: Master (currently under development) 
OS: windows: 10
Python version: 3.6




Discussion:
@Khalilsqu, would you like to work on this one?
@ccordoba12 Gladly, Thanks a lot, However, I do it next weekend, I have paper submission deadline on 15th, it is done but just few minor modification on some figures, I also have a presentation in a meeting on 17th. I will take care of it next weekend. Promise. 💯 


No problem. Thanks for your help!

Resolving patch:
diff --git a/spyder/plugins/explorer/widgets.py b/spyder/plugins/explorer/widgets.py
--- a/spyder/plugins/explorer/widgets.py
+++ b/spyder/plugins/explorer/widgets.py
@@ -603,7 +603,15 @@ def run(self, fnames=None):
     def remove_tree(self, dirname):
         """Remove whole directory tree
         Reimplemented in project explorer widget"""
-        shutil.rmtree(dirname, onerror=misc.onerror)
+        while osp.exists(dirname):
+            try:
+                shutil.rmtree(dirname, onerror=misc.onerror)
+            except Exception as e:
+                # This handles a Windows problem with shutil.rmtree.
+                # See issue #8567.
+                if type(e).__name__ == "OSError":
+                    error_path = to_text_string(e.filename)
+                    shutil.rmtree(error_path, ignore_errors=True)
     
     def delete_file(self, fname, multiple, yes_to_all):
         """Delete file"""
@@ -1293,11 +1301,15 @@ def toggle_icontext(self, state):
 # Tests
 #==============================================================================
 class FileExplorerTest(QWidget):
-    def __init__(self):
+    def __init__(self, directory=None):
         QWidget.__init__(self)
         vlayout = QVBoxLayout()
         self.setLayout(vlayout)
         self.explorer = ExplorerWidget(self, show_cd_only=None)
+        if directory is not None:
+            self.directory = directory
+        else:
+            self.directory = osp.dirname(osp.abspath(__file__))
         vlayout.addWidget(self.explorer)
         
         hlayout1 = QHBoxLayout()


Test patch:
diff --git a/spyder/plugins/explorer/tests/test_explorer.py b/spyder/plugins/explorer/tests/test_explorer.py
--- a/spyder/plugins/explorer/tests/test_explorer.py
+++ b/spyder/plugins/explorer/tests/test_explorer.py
@@ -8,12 +8,20 @@
 Tests for explorer.py
 """
 
-# Test library imports
+# Standard imports
+import os
+import os.path as osp
+
+# Third party imports
 import pytest
+from qtpy.QtWidgets import QMessageBox
 
 # Local imports
 from spyder.plugins.explorer.widgets import (FileExplorerTest,
                                              ProjectExplorerTest)
+from spyder.py3compat import to_text_string
+from spyder.plugins.projects.widgets.explorer import ProjectExplorerTest as \
+    ProjectExplorerTest2
 
 
 @pytest.fixture
@@ -32,6 +40,49 @@ def project_explorer(qtbot):
     return widget
 
 
+@pytest.fixture(params=[['script.py', 'dir1/dir2/dir3/dir4'],
+                        ['script.py', 'script1.py', 'testdir/dir1/script2.py'],
+                        ['subdir/innerdir/dir3/text.txt', 'dir1/dir2/dir3',
+                         'dir1/dir2/dir3/file.txt',
+                         'dir1/dir2/dir3/dir4/python.py']])
+def create_folders_files(tmpdir, request):
+    """A project directory with dirs and files for testing."""
+    project_dir = to_text_string(tmpdir.mkdir('project'))
+    top_folder = osp.join(project_dir, 'top_folder_in_proj')
+    if not osp.exists(top_folder):
+        os.mkdir(top_folder)
+    list_paths = []
+    for item in request.param:
+        if osp.splitext(item)[1]:
+            if osp.split(item)[0]:
+                dirs, fname = osp.split(item)
+                dirpath = osp.join(top_folder, dirs)
+                if not osp.exists(dirpath):
+                    os.makedirs(dirpath)
+                    item_path = osp.join(dirpath, fname)
+            else:
+                item_path = osp.join(top_folder, item)
+        else:
+            dirpath = osp.join(top_folder, item)
+            if not osp.exists(dirpath):
+                os.makedirs(dirpath)
+                item_path = dirpath
+        if not osp.isdir(item_path):
+            with open(item_path, 'w') as fh:
+                fh.write("File Path:\n" + str(item_path) + '\n')
+        list_paths.append(item_path)
+    return list_paths, project_dir, top_folder
+
+
+@pytest.fixture(params=[FileExplorerTest, ProjectExplorerTest2])
+def explorer_with_files(qtbot, create_folders_files, request):
+    """Setup Project/File Explorer widget."""
+    paths, project_dir, top_folder = create_folders_files
+    project_explorer_orig = request.param(directory=project_dir)
+    qtbot.addWidget(project_explorer_orig)
+    return project_explorer_orig, paths, top_folder
+
+
 def test_file_explorer(file_explorer):
     """Run FileExplorerTest."""
     file_explorer.resize(640, 480)
@@ -46,5 +97,13 @@ def test_project_explorer(project_explorer):
     assert project_explorer
 
 
+def test_delete_files_folders(explorer_with_files, mocker):
+    """Test delete file(s)/folders(s)."""
+    project, __, top_folder = explorer_with_files
+    mocker.patch.object(QMessageBox, 'warning', return_value=QMessageBox.Yes)
+    project.explorer.treewidget.delete(fnames=[top_folder])
+    assert not osp.exists(top_folder)
+
+
 if __name__ == "__main__":
     pytest.main()