Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/django/core/files/move.py: 13%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

39 statements  

1""" 

2Move a file in the safest way possible:: 

3 

4 >>> from django.core.files.move import file_move_safe 

5 >>> file_move_safe("/tmp/old_file", "/tmp/new_file") 

6""" 

7 

8import os 

9from shutil import copymode, copystat 

10 

11from django.core.files import locks 

12 

13__all__ = ["file_move_safe"] 

14 

15 

16def file_move_safe( 

17 old_file_name, new_file_name, chunk_size=1024 * 64, allow_overwrite=False 

18): 

19 """ 

20 Move a file from one location to another in the safest way possible. 

21 

22 First, try ``os.rename``, which is simple but will break across filesystems. 

23 If that fails, stream manually from one file to another in pure Python. 

24 

25 If the destination file exists and ``allow_overwrite`` is ``False``, raise 

26 ``FileExistsError``. 

27 """ 

28 # There's no reason to move if we don't have to. 

29 try: 

30 if os.path.samefile(old_file_name, new_file_name): 

31 return 

32 except OSError: 

33 pass 

34 

35 if not allow_overwrite and os.access(new_file_name, os.F_OK): 

36 raise FileExistsError( 

37 f"Destination file {new_file_name} exists and allow_overwrite is False." 

38 ) 

39 

40 try: 

41 os.rename(old_file_name, new_file_name) 

42 return 

43 except OSError: 

44 # OSError happens with os.rename() if moving to another filesystem or 

45 # when moving opened files on certain operating systems. 

46 pass 

47 

48 # first open the old file, so that it won't go away 

49 with open(old_file_name, "rb") as old_file: 

50 # now open the new file, not forgetting allow_overwrite 

51 fd = os.open( 

52 new_file_name, 

53 ( 

54 os.O_WRONLY 

55 | os.O_CREAT 

56 | getattr(os, "O_BINARY", 0) 

57 | (os.O_EXCL if not allow_overwrite else 0) 

58 ), 

59 ) 

60 try: 

61 locks.lock(fd, locks.LOCK_EX) 

62 current_chunk = None 

63 while current_chunk != b"": 

64 current_chunk = old_file.read(chunk_size) 

65 os.write(fd, current_chunk) 

66 finally: 

67 locks.unlock(fd) 

68 os.close(fd) 

69 

70 try: 

71 copystat(old_file_name, new_file_name) 

72 except PermissionError: 

73 # Certain filesystems (e.g. CIFS) fail to copy the file's metadata if 

74 # the type of the destination filesystem isn't the same as the source 

75 # filesystem. This also happens with some SELinux-enabled systems. 

76 # Ignore that, but try to set basic permissions. 

77 try: 

78 copymode(old_file_name, new_file_name) 

79 except PermissionError: 

80 pass 

81 

82 try: 

83 os.remove(old_file_name) 

84 except PermissionError as e: 

85 # Certain operating systems (Cygwin and Windows) 

86 # fail when deleting opened files, ignore it. (For the 

87 # systems where this happens, temporary files will be auto-deleted 

88 # on close anyway.) 

89 if getattr(e, "winerror", 0) != 32: 

90 raise