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
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-23 11:16 -0600
1import os
2import pathlib
4from plain.exceptions import SuspiciousFileOperation
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}'")
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")
24 return name
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"::
32 class FileProxy(FileProxyMixin):
33 def __init__(self, file):
34 self.file = file
35 """
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)
52 @property
53 def closed(self):
54 return not self.file or self.file.closed
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
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", "")
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
77 def __iter__(self):
78 return iter(self.file)