briefcase icon indicating copy to clipboard operation
briefcase copied to clipboard

On Windows, renaming a directory that was just created can fail with `PermissionError: [WinError 5] Access denied`

Open rmartin16 opened this issue 1 year ago • 5 comments

Describe the bug

Using os.rename() (or pathlib.Path.rename()) can fail on Windows if Briefcase does not have exclusive access to all files in the directory.

A common pattern Briefcase uses is to run shutil.unpack_archive() and rename the unpacked directory; for instance, Android cmdline-tools and Java's JDK. In between these two operations, a background process may acquire a handle on a file; for instance, a Windows file indexer or an antivirus scanner.

Modern Linux file systems are much more resilient to these types of conflicts; so, I would only expect to see this on Windows.

Steps to reproduce

import os
from pathlib import Path

Path("orig-dir-1").mkdir()
Path("orig-dir-1/orig-file").touch()
Path("orig-dir-1").rename("new-dir-1")
os.listdir("new-dir-1")

Path("orig-dir-2").mkdir()
Path("orig-dir-2/orig-file").touch()
Path("orig-dir-2/orig-file").open()
Path("orig-dir-2").rename("new-dir-2")
❯ python
Python 3.11.3 (tags/v3.11.3:f3909b8, Apr  4 2023, 23:49:59) [MSC v.1934 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
>>> from pathlib import Path
>>>
>>> Path("orig-dir-1").mkdir()
>>> Path("orig-dir-1/orig-file").touch()
>>> Path("orig-dir-1").rename("new-dir-1")
WindowsPath('new-dir-1')
>>> os.listdir("new-dir-1")
['orig-file']
>>>
>>> Path("orig-dir-2").mkdir()
>>> Path("orig-dir-2/orig-file").touch()
>>> Path("orig-dir-2/orig-file").open()
<_io.TextIOWrapper name='orig-dir-2\\orig-file' mode='r' encoding='cp1252'>
>>> Path("orig-dir-2").rename("new-dir-2")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Users\user\.pyenv\pyenv-win\versions\3.11.3\Lib\pathlib.py", line 1175, in rename
    os.rename(self, target)
PermissionError: [WinError 5] Access is denied: 'orig-dir-2' -> 'new-dir-2'
>>>

Expected behavior

Renaming these directories in the Briefcase cache should be successful.

Screenshots

No response

Environment

  • Operating System: Windows 11
  • Python version: 3.11.3
  • Software versions:
    • Briefcase: 0.3.18

Logs

No response

Additional context

Origin: https://github.com/beeware/beeware/pull/348

rmartin16 avatar May 06 '24 22:05 rmartin16

See also #394 for a tangentially related issue.

freakboy3742 avatar May 07 '24 03:05 freakboy3742

Unfortunately this is a common issue on WIndows, and I don't think there's any workaround except putting all renames and deletions in a retry loop. There are packages such as tenacity which make this easier.

mhsmith avatar May 07 '24 16:05 mhsmith

pycon24, taking a look at this. Thanks!

spa51273 avatar May 21 '24 01:05 spa51273

Path("orig-dir-1").mkdir() Path("orig-dir-1/orig-file").touch() Path("orig-dir-1").rename("new-dir-1") os.listdir("new-dir-1")

Path("orig-dir-2").mkdir() Path("orig-dir-2/orig-file").touch() Path("orig-dir-2/orig-file").open() Path("orig-dir-2").rename("new-dir-2")

Path("orig-dir-2").mkdir() Path("orig-dir-2/orig-file").touch() Path("orig-dir-2/orig-file").open() Path("orig-dir-2").rename("new-dir-3")

Path("orig-dir-2").mkdir() Path("orig-dir-2/orig-file").touch() Path("orig-dir-2/orig-file").open() Path("orig-dir-2").rename("new-dir-4")

Deepak6546kumar avatar May 31 '24 10:05 Deepak6546kumar

@Deepak6546kumar It's not clear what you're trying to communicate with this code sample. We already had a replication case; what extra information are you providing here?

freakboy3742 avatar Jun 01 '24 00:06 freakboy3742