Coverage for src/lektor_ng/filecontents.py: 43%

72 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-09-01 11:35 +0000

1import base64 

2import codecs 

3import hashlib 

4import mimetypes 

5import os 

6 

7from lektor_ng.utils import deprecated 

8 

9 

10class FileContents: 

11 @deprecated(name="FileContents", version="3.4.0") 

12 def __init__(self, filename): 

13 self.filename = filename 

14 self._md5 = None 

15 self._sha1 = None 

16 self._integrity = None 

17 self._mimetype = mimetypes.guess_type(filename)[0] or "application/octet-stream" 

18 

19 @property 

20 def sha1(self): 

21 self._ensure_hashes() 

22 return self._sha1 

23 

24 @property 

25 def md5(self): 

26 self._ensure_hashes() 

27 return self._md5 

28 

29 @property 

30 def integrity(self): 

31 self._ensure_hashes() 

32 return self._integrity 

33 

34 @property 

35 def mimetype(self): 

36 return self._mimetype 

37 

38 @property 

39 def bytes(self): 

40 try: 

41 return os.stat(self.filename).st_size 

42 except OSError: 

43 return 0 

44 

45 def as_data_url(self, mediatype=None): 

46 if mediatype is None: 

47 mediatype = self.mimetype 

48 return f"data:{mediatype};base64,{self.as_base64()}" 

49 

50 def as_text(self): 

51 with self.open() as f: 

52 return f.read() 

53 

54 def as_bytes(self): 

55 with self.open("rb") as f: 

56 return f.read() 

57 

58 def as_base64(self): 

59 return base64.b64encode(self.as_bytes()) 

60 

61 def open(self, mode="r", encoding=None): 

62 if mode == "rb": 

63 return open(self.filename, "rb") 

64 if mode != "r": 

65 raise TypeError("Can only open files for reading") 

66 # pylint: disable=deprecated-method 

67 return codecs.open(self.filename, encoding=encoding or "utf-8") 

68 

69 def _ensure_hashes(self): 

70 if self._md5 is not None: 

71 return 

72 with self.open("rb") as f: 

73 md5 = hashlib.md5() 

74 sha1 = hashlib.sha1() 

75 sha384 = hashlib.sha384() 

76 while 1: 

77 chunk = f.read(16384) 

78 if not chunk: 

79 break 

80 md5.update(chunk) 

81 sha1.update(chunk) 

82 sha384.update(chunk) 

83 self._md5 = md5.hexdigest() 

84 self._sha1 = sha1.hexdigest() 

85 self._integrity = "sha384-" + base64.b64encode(sha384.digest()).decode("ascii") 

86 

87 def __repr__(self): 

88 return f"<FileContents {self.filename!r} md5={self.md5!r}>"