Skip to content

Commit 15f6afd

Browse files
bobo yangbobo yang
authored andcommitted
fix: Improve directory deletion on Windows
- Implement custom rmtree function to handle Windows deletion errors - Replace shutil.rmtree calls with new rmtree utility function - Move __onerror handler to utils.py for centralized error handling
1 parent ea3ba91 commit 15f6afd

File tree

5 files changed

+38
-33
lines changed

5 files changed

+38
-33
lines changed

devchat/_cli/run.py

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from typing import List, Optional, Tuple
22

33
import click
4+
from devchat.utils import rmtree
45

56

67
@click.command(
@@ -115,28 +116,6 @@ def run(
115116
return
116117

117118

118-
def __onerror(func, path, _1):
119-
"""
120-
Error handler for shutil.rmtree.
121-
122-
If the error is due to an access error (read only file)
123-
it attempts to add write permission and then retries.
124-
125-
If the error is for another reason it re-raises the error.
126-
127-
Usage : shutil.rmtree(path, onerror=onerror)
128-
"""
129-
import os
130-
import stat
131-
132-
# Check if file access issue
133-
if not os.access(path, os.W_OK):
134-
# Try to change the file to be writable (remove read-only flag)
135-
os.chmod(path, stat.S_IWUSR)
136-
# Retry the function that failed
137-
func(path)
138-
139-
140119
def __make_files_writable(directory):
141120
"""
142121
Recursively make all files in the directory writable.
@@ -180,9 +159,9 @@ def _clone_or_pull_git_repo(target_dir: str, repo_urls: List[Tuple[str, str]], z
180159
bak_dir = target_dir + "_bak"
181160
new_dir = target_dir + "_old"
182161
if os.path.exists(new_dir):
183-
shutil.rmtree(new_dir, onerror=__onerror)
162+
rmtree(new_dir)
184163
if os.path.exists(bak_dir):
185-
shutil.rmtree(bak_dir, onerror=__onerror)
164+
rmtree(bak_dir)
186165
print(f"{target_dir} is already exists. Moved to {new_dir}")
187166
clone_git_repo(bak_dir, repo_urls)
188167
try:

devchat/_cli/utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from typing import Any, List, Optional, Tuple
77

88
from devchat._cli.errors import MissContentInPromptException
9-
from devchat.utils import add_gitignore, find_root_dir, get_logger, setup_logger
9+
from devchat.utils import add_gitignore, find_root_dir, get_logger, rmtree, setup_logger
1010

1111
logger = get_logger(__name__)
1212

@@ -31,7 +31,7 @@ def download_and_extract_workflow(workflow_url, target_dir):
3131

3232
# Delete target directory if exists
3333
if os.path.exists(target_dir):
34-
shutil.rmtree(target_dir)
34+
rmtree(target_dir)
3535

3636
# Rename extracted directory to target directory
3737
extracted_dir = os.path.join(parent_dir, "workflows-main")

devchat/_service/route/workflows.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from fastapi.responses import JSONResponse
88

99
from devchat._service.schema import response
10-
from devchat.utils import get_logger
10+
from devchat.utils import get_logger, rmtree
1111
from devchat.workflow.namespace import (
1212
WorkflowMeta,
1313
get_prioritized_namespace_path,
@@ -100,7 +100,7 @@ def update_custom_workflows():
100100

101101
if repo_path.exists():
102102
logger.info(f"Repo path not empty {repo_path}, removing it.")
103-
shutil.rmtree(repo_path)
103+
rmtree(repo_path)
104104

105105
logger.info(
106106
f"Starting update for {repo_name} at {repo_path} "

devchat/utils.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,3 +243,29 @@ def get_encoding(name: str):
243243
def openai_response_tokens(message: dict, model: str) -> int:
244244
"""Returns the number of tokens used by a response."""
245245
return openai_message_tokens(message, model)
246+
247+
def rmtree(path: str) -> None:
248+
import shutil
249+
250+
def __onerror(func, path, _1):
251+
"""
252+
Error handler for shutil.rmtree.
253+
254+
If the error is due to an access error (read only file)
255+
it attempts to add write permission and then retries.
256+
257+
If the error is for another reason it re-raises the error.
258+
259+
Usage : shutil.rmtree(path, onerror=onerror)
260+
"""
261+
import os
262+
import stat
263+
264+
# Check if file access issue
265+
if not os.access(path, os.W_OK):
266+
# Try to change the file to be writable (remove read-only flag)
267+
os.chmod(path, stat.S_IWUSR)
268+
# Retry the function that failed
269+
func(path)
270+
271+
shutil.rmtree(path, onerror=__onerror)

devchat/workflow/update_util.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import requests
1010

11-
from devchat.utils import get_logger
11+
from devchat.utils import get_logger, rmtree
1212
from devchat.workflow.path import (
1313
CHAT_DIR,
1414
CUSTOM_BASE,
@@ -167,7 +167,7 @@ def update_by_zip(workflow_base: Path) -> Tuple[bool, str]:
167167
# Has previous workflows, download as tmp_new
168168
tmp_new = parent / f"{WORKFLOWS_BASE_NAME}_new"
169169
if tmp_new.exists():
170-
shutil.rmtree(tmp_new)
170+
rmtree(tmp_new)
171171
# TODO: handle error?
172172
# shutil.rmtree(tmp_new, onerror=__onerror)
173173

@@ -182,7 +182,7 @@ def update_by_zip(workflow_base: Path) -> Tuple[bool, str]:
182182
backup_zip = _backup(workflow_base)
183183

184184
# rename the new dir to the workflow_base
185-
shutil.rmtree(workflow_base)
185+
rmtree(workflow_base)
186186
shutil.move(tmp_new, workflow_base)
187187

188188
msg = f"Updated {workflow_base} by zip. (backup: {backup_zip})"
@@ -223,7 +223,7 @@ def update_by_git(workflow_base: Path) -> Tuple[bool, str]:
223223
# try to clone the new repo to tmp_new
224224
tmp_new = parent / f"{WORKFLOWS_BASE_NAME}_new"
225225
if tmp_new.exists():
226-
shutil.rmtree(tmp_new)
226+
rmtree(tmp_new)
227227

228228
clone_ok = _clone_repo_to_dir(REPO_URLS, tmp_new)
229229
if not clone_ok:
@@ -235,7 +235,7 @@ def update_by_git(workflow_base: Path) -> Tuple[bool, str]:
235235
backup_zip = _backup(workflow_base)
236236

237237
# rename the new dir to the workflow_base
238-
shutil.rmtree(workflow_base)
238+
rmtree(workflow_base)
239239
shutil.move(tmp_new, workflow_base)
240240

241241
msg = f"Updated {workflow_base} by git. (backup: {backup_zip})"

0 commit comments

Comments
 (0)