lektor_ng.filecontents

src/lektor_ng/filecontents.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import base64
import codecs
import hashlib
import mimetypes
import os

from lektor_ng.utils import deprecated


class FileContents:
    @deprecated(name="FileContents", version="3.4.0")
    def __init__(self, filename):
        self.filename = filename
        self._md5 = None
        self._sha1 = None
        self._integrity = None
        self._mimetype = mimetypes.guess_type(filename)[0] or "application/octet-stream"

    @property
    def sha1(self):
        self._ensure_hashes()
        return self._sha1

    @property
    def md5(self):
        self._ensure_hashes()
        return self._md5

    @property
    def integrity(self):
        self._ensure_hashes()
        return self._integrity

    @property
    def mimetype(self):
        return self._mimetype

    @property
    def bytes(self):
        try:
            return os.stat(self.filename).st_size
        except OSError:
            return 0

    def as_data_url(self, mediatype=None):
        if mediatype is None:
            mediatype = self.mimetype
        return f"data:{mediatype};base64,{self.as_base64()}"

    def as_text(self):
        with self.open() as f:
            return f.read()

    def as_bytes(self):
        with self.open("rb") as f:
            return f.read()

    def as_base64(self):
        return base64.b64encode(self.as_bytes())

    def open(self, mode="r", encoding=None):
        if mode == "rb":
            return open(self.filename, "rb")
        if mode != "r":
            raise TypeError("Can only open files for reading")
        # pylint: disable=deprecated-method
        return codecs.open(self.filename, encoding=encoding or "utf-8")

    def _ensure_hashes(self):
        if self._md5 is not None:
            return
        with self.open("rb") as f:
            md5 = hashlib.md5()
            sha1 = hashlib.sha1()
            sha384 = hashlib.sha384()
            while 1:
                chunk = f.read(16384)
                if not chunk:
                    break
                md5.update(chunk)
                sha1.update(chunk)
                sha384.update(chunk)
            self._md5 = md5.hexdigest()
            self._sha1 = sha1.hexdigest()
            self._integrity = "sha384-" + base64.b64encode(sha384.digest()).decode("ascii")

    def __repr__(self):
        return f"<FileContents {self.filename!r} md5={self.md5!r}>"