Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
mime.py211 linesDownload Raw Back to lexers
1"""2    pygments.lexers.mime3    ~~~~~~~~~~~~~~~~~~~~4 5    Lexer for Multipurpose Internet Mail Extensions (MIME) data.6 7    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.8    :license: BSD, see LICENSE for details.9"""10 11import re12 13from pygments.lexer import RegexLexer, include14from pygments.lexers import get_lexer_for_mimetype15from pygments.token import Text, Name, String, Operator, Comment, Other16from pygments.util import get_int_opt, ClassNotFound17 18__all__ = ["MIMELexer"]19 20 21class MIMELexer(RegexLexer):22    """23    Lexer for Multipurpose Internet Mail Extensions (MIME) data. This lexer is24    designed to process nested multipart data.25 26    It assumes that the given data contains both header and body (and is27    split at an empty line). If no valid header is found, then the entire data28    will be treated as body.29 30    Additional options accepted:31 32    `MIME-max-level`33        Max recursion level for nested MIME structure. Any negative number34        would treated as unlimited. (default: -1)35 36    `Content-Type`37        Treat the data as a specific content type. Useful when header is38        missing, or this lexer would try to parse from header. (default:39        `text/plain`)40 41    `Multipart-Boundary`42        Set the default multipart boundary delimiter. This option is only used43        when `Content-Type` is `multipart` and header is missing. This lexer44        would try to parse from header by default. (default: None)45 46    `Content-Transfer-Encoding`47        Treat the data as a specific encoding. Or this lexer would try to parse48        from header by default. (default: None)49    """50 51    name = "MIME"52    aliases = ["mime"]53    mimetypes = ["multipart/mixed",54                 "multipart/related",55                 "multipart/alternative"]56    url = 'https://en.wikipedia.org/wiki/MIME'57    version_added = '2.5'58 59    def __init__(self, **options):60        super().__init__(**options)61        self.boundary = options.get("Multipart-Boundary")62        self.content_transfer_encoding = options.get("Content_Transfer_Encoding")63        self.content_type = options.get("Content_Type", "text/plain")64        self.max_nested_level = get_int_opt(options, "MIME-max-level", -1)65 66    def get_header_tokens(self, match):67        field = match.group(1)68 69        if field.lower() in self.attention_headers:70            yield match.start(1), Name.Tag, field + ":"71            yield match.start(2), Text.Whitespace, match.group(2)72 73            pos = match.end(2)74            body = match.group(3)75            for i, t, v in self.get_tokens_unprocessed(body, ("root", field.lower())):76                yield pos + i, t, v77 78        else:79            yield match.start(), Comment, match.group()80 81    def get_body_tokens(self, match):82        pos_body_start = match.start()83        entire_body = match.group()84 85        # skip first newline86        if entire_body[0] == '\n':87            yield pos_body_start, Text.Whitespace, '\n'88            pos_body_start = pos_body_start + 189            entire_body = entire_body[1:]90 91        # if it is not a multipart92        if not self.content_type.startswith("multipart") or not self.boundary:93            for i, t, v in self.get_bodypart_tokens(entire_body):94                yield pos_body_start + i, t, v95            return96 97        # find boundary98        bdry_pattern = rf"^--{re.escape(self.boundary)}(--)?\n"99        bdry_matcher = re.compile(bdry_pattern, re.MULTILINE)100 101        # some data has prefix text before first boundary102        m = bdry_matcher.search(entire_body)103        if m:104            pos_part_start = pos_body_start + m.end()105            pos_iter_start = lpos_end = m.end()106            yield pos_body_start, Text, entire_body[:m.start()]107            yield pos_body_start + lpos_end, String.Delimiter, m.group()108        else:109            pos_part_start = pos_body_start110            pos_iter_start = 0111 112        # process tokens of each body part113        for m in bdry_matcher.finditer(entire_body, pos_iter_start):114            # bodypart115            lpos_start = pos_part_start - pos_body_start116            lpos_end = m.start()117            part = entire_body[lpos_start:lpos_end]118            for i, t, v in self.get_bodypart_tokens(part):119                yield pos_part_start + i, t, v120 121            # boundary122            yield pos_body_start + lpos_end, String.Delimiter, m.group()123            pos_part_start = pos_body_start + m.end()124 125        # some data has suffix text after last boundary126        lpos_start = pos_part_start - pos_body_start127        if lpos_start != len(entire_body):128            yield pos_part_start, Text, entire_body[lpos_start:]129 130    def get_bodypart_tokens(self, text):131        # return if:132        #  * no content133        #  * no content type specific134        #  * content encoding is not readable135        #  * max recurrsion exceed136        if not text.strip() or not self.content_type:137            return [(0, Other, text)]138 139        cte = self.content_transfer_encoding140        if cte and cte not in {"8bit", "7bit", "quoted-printable"}:141            return [(0, Other, text)]142 143        if self.max_nested_level == 0:144            return [(0, Other, text)]145 146        # get lexer147        try:148            lexer = get_lexer_for_mimetype(self.content_type)149        except ClassNotFound:150            return [(0, Other, text)]151 152        if isinstance(lexer, type(self)):153            lexer.max_nested_level = self.max_nested_level - 1154 155        return lexer.get_tokens_unprocessed(text)156 157    def store_content_type(self, match):158        self.content_type = match.group(1)159 160        prefix_len = match.start(1) - match.start(0)161        yield match.start(0), Text.Whitespace, match.group(0)[:prefix_len]162        yield match.start(1), Name.Label, match.group(2)163        yield match.end(2), String.Delimiter, '/'164        yield match.start(3), Name.Label, match.group(3)165 166    def get_content_type_subtokens(self, match):167        yield match.start(1), Text, match.group(1)168        yield match.start(2), Text.Whitespace, match.group(2)169        yield match.start(3), Name.Attribute, match.group(3)170        yield match.start(4), Operator, match.group(4)171        yield match.start(5), String, match.group(5)172 173        if match.group(3).lower() == "boundary":174            boundary = match.group(5).strip()175            if boundary[0] == '"' and boundary[-1] == '"':176                boundary = boundary[1:-1]177            self.boundary = boundary178 179    def store_content_transfer_encoding(self, match):180        self.content_transfer_encoding = match.group(0).lower()181        yield match.start(0), Name.Constant, match.group(0)182 183    attention_headers = {"content-type", "content-transfer-encoding"}184 185    tokens = {186        "root": [187            (r"^([\w-]+):( *)([\s\S]*?\n)(?![ \t])", get_header_tokens),188            (r"^$[\s\S]+", get_body_tokens),189        ],190        "header": [191            # folding192            (r"\n[ \t]", Text.Whitespace),193            (r"\n(?![ \t])", Text.Whitespace, "#pop"),194        ],195        "content-type": [196            include("header"),197            (198                r"^\s*((multipart|application|audio|font|image|model|text|video"199                r"|message)/([\w-]+))",200                store_content_type,201            ),202            (r'(;)((?:[ \t]|\n[ \t])*)([\w:-]+)(=)([\s\S]*?)(?=;|\n(?![ \t]))',203             get_content_type_subtokens),204            (r';[ \t]*\n(?![ \t])', Text, '#pop'),205        ],206        "content-transfer-encoding": [207            include("header"),208            (r"([\w-]+)", store_content_transfer_encoding),209        ],210    }211 
codekingpro/portable-devtools · Team Ai