Completed
Push — 0.5.3 ( 615661...6cbe30 )
by Felipe A.
01:11
created

SGMLCompressContext   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 52
Duplicated Lines 0 %

Importance

Changes 4
Bugs 1 Features 0
Metric Value
wmc 14
c 4
b 1
f 0
dl 0
loc 52
rs 10

3 Methods

Rating   Name   Duplication   Size   Complexity  
A transform_text() 0 6 4
B transform_tag() 0 11 6
A look() 0 9 4
1
import re
2
3
import jinja2
4
import jinja2.ext
5
import jinja2.lexer
6
7
from . import StateMachine
8
9
10
class SGMLCompressContext(StateMachine):
11
    re_whitespace = re.compile('[ \\t\\r\\n]+')
12
    block_tags = {}  # block content will be treated as literal text
13
    jumps = {  # state machine jumps
14
        'text': {
15
            '<': 'tag',
16
            '<!--': 'comment',
17
            '<![CDATA[': 'cdata',
18
            },
19
        'lit1': {'"': 'tag'},
20
        'lit2': ("'", 'tag'),
21
        'tag': {
22
            '>': 'text',
23
            '"': 'lit1',
24
            "'": 'lit2'
25
            },
26
        'comment': {'-->': 'text'},
27
        'cdata': {']]>': 'text'}
28
        }
29
    lineno = 0  # current token lineno
30
    skip_until_token = None  # inside token until this is met
31
    skip_until_text = None  # inside text until this is met
32
    current = 'text'
33
34
    def look(self, value, current, start):
35
        if self.skip_until_text and current == 'text':
36
            mark = self.skip_until_text
37
            index = value.find(mark, len(self.start))
38
            if -1 != index:
39
                return index, mark, current
40
            return len(value), '', None
41
        return super(SGMLCompressContext, self).look(
42
            value, current, start)
43
44
    def transform_tag(self, data, mark, next):
45
        tagstart = self.start == '<'
46
        data = self.re_whitespace.sub(' ', data[1:] if tagstart else data)
47
        if tagstart:
48
            data = data.lstrip() if next is None else data.strip()
49
            tagname = data.split(' ', 1)[0]
50
            self.skip_until_text = self.block_tags.get(tagname)
51
            return '<' + data
52
        elif next is None:
53
            return data.rstrip()
54
        return self.start if data.strip() == self.start else data
55
56
    def transform_text(self, data, mark, next):
57
        if not self.skip_until_text:
58
            return self.start if data.strip() == self.start else data
59
        elif next is not None:
60
            self.skip_until_text = None
61
        return data
62
63
64
class HTMLCompressContext(SGMLCompressContext):
65
    block_tags = {
66
        'textarea': '</textarea>',
67
        'pre': '</pre>',
68
        'script': '</script>',
69
        'style': '</style>',
70
        }
71
72
73
class HTMLCompress(jinja2.ext.Extension):
74
    context_class = HTMLCompressContext
75
    token_class = jinja2.lexer.Token
76
    block_tokens = {
77
        'variable_begin': 'variable_end',
78
        'block_begin': 'block_end'
79
        }
80
81
    def filter_stream(self, stream):
82
        transform = self.context_class()
83
        lineno = 0
84
        skip_until_token = None
85
        for token in stream:
86
            if skip_until_token:
87
                yield token
88
                if token.type == skip_until_token:
89
                    skip_until_token = None
90
                continue
91
92
            if token.type != 'data':
93
                for data in transform.finish():
94
                    yield self.token_class(lineno, 'data', data)
95
                yield token
96
                skip_until_token = self.block_tokens.get(token.type)
97
                continue
98
99
            if not transform.pending:
100
                lineno = token.lineno
101
102
            for data in transform.feed(token.value):
103
                yield self.token_class(lineno, 'data', data)
104
                lineno = token.lineno
105
106
        for data in transform.finish():
107
            yield self.token_class(lineno, 'data', data)
108