Coverage for /Users/davegaeddert/Developer/dropseed/plain/plain/plain/internal/files/utils.py: 49%

51 statements  

« prev     ^ index     » next       coverage.py v7.6.9, created at 2024-12-23 11:16 -0600

1import os 

2import pathlib 

3 

4from plain.exceptions import SuspiciousFileOperation 

5 

6 

7def validate_file_name(name, allow_relative_path=False): 

8 # Remove potentially dangerous names 

9 if os.path.basename(name) in {"", ".", ".."}: 

10 raise SuspiciousFileOperation(f"Could not derive file name from '{name}'") 

11 

12 if allow_relative_path: 

13 # Use PurePosixPath() because this branch is checked only in 

14 # FileField.generate_filename() where all file paths are expected to be 

15 # Unix style (with forward slashes). 

16 path = pathlib.PurePosixPath(name) 

17 if path.is_absolute() or ".." in path.parts: 

18 raise SuspiciousFileOperation( 

19 f"Detected path traversal attempt in '{name}'" 

20 ) 

21 elif name != os.path.basename(name): 

22 raise SuspiciousFileOperation(f"File name '{name}' includes path elements") 

23 

24 return name 

25 

26 

27class FileProxyMixin: 

28 """ 

29 A mixin class used to forward file methods to an underlying file 

30 object. The internal file object has to be called "file":: 

31 

32 class FileProxy(FileProxyMixin): 

33 def __init__(self, file): 

34 self.file = file 

35 """ 

36 

37 encoding = property(lambda self: self.file.encoding) 

38 fileno = property(lambda self: self.file.fileno) 

39 flush = property(lambda self: self.file.flush) 

40 isatty = property(lambda self: self.file.isatty) 

41 newlines = property(lambda self: self.file.newlines) 

42 read = property(lambda self: self.file.read) 

43 readinto = property(lambda self: self.file.readinto) 

44 readline = property(lambda self: self.file.readline) 

45 readlines = property(lambda self: self.file.readlines) 

46 seek = property(lambda self: self.file.seek) 

47 tell = property(lambda self: self.file.tell) 

48 truncate = property(lambda self: self.file.truncate) 

49 write = property(lambda self: self.file.write) 

50 writelines = property(lambda self: self.file.writelines) 

51 

52 @property 

53 def closed(self): 

54 return not self.file or self.file.closed 

55 

56 def readable(self): 

57 if self.closed: 

58 return False 

59 if hasattr(self.file, "readable"): 

60 return self.file.readable() 

61 return True 

62 

63 def writable(self): 

64 if self.closed: 

65 return False 

66 if hasattr(self.file, "writable"): 

67 return self.file.writable() 

68 return "w" in getattr(self.file, "mode", "") 

69 

70 def seekable(self): 

71 if self.closed: 

72 return False 

73 if hasattr(self.file, "seekable"): 

74 return self.file.seekable() 

75 return True 

76 

77 def __iter__(self): 

78 return iter(self.file)