• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hivesolutions / admin-scripts / #613635369

21 Nov 2023 12:44PM UTC coverage: 42.144% (-0.08%) from 42.222%
#613635369

Pull #2

travis-ci

Pull Request #2: feat: add python 3.12

684 of 1623 relevant lines covered (42.14%)

2.95 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

14.06
/src/admin_scripts/extra/js.py
1
#!/usr/bin/python
2
# -*- coding: utf-8 -*-
3

4
# Hive Administration Scripts
5
# Copyright (c) 2008-2025 Hive Solutions Lda.
6
#
7
# This file is part of Hive Administration Scripts.
8
#
9
# Hive Administration Scripts is free software: you can redistribute it and/or modify
10
# it under the terms of the Apache License as published by the Apache
11
# Foundation, either version 2.0 of the License, or (at your option) any
12
# later version.
13
#
14
# Hive Administration Scripts is distributed in the hope that it will be useful,
15
# but WITHOUT ANY WARRANTY; without even the implied warranty of
16
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
# Apache License for more details.
18
#
19
# You should have received a copy of the Apache License along with
20
# Hive Administration Scripts. If not, see <http://www.apache.org/licenses/>.
21

22
__author__ = "João Magalhães <joamag@hive.pt>"
7✔
23
""" The author(s) of the module """
24

25
__copyright__ = "Copyright (c) 2008-2025 Hive Solutions Lda."
7✔
26
""" The copyright for the module """
27

28
__license__ = "Apache License, Version 2.0"
7✔
29
""" The license for the module """
30

31
import legacy
7✔
32

33

34
class JavascriptMinify(object):
7✔
35
    """
36
    Class used to minify javascript code.
37
    The minification is done using a classic
7✔
38
    parsing approach.
39
    """
40

7✔
41
    def _out_a(self):
42
        """
7✔
43
        Outputs the "first" value to the output
44
        file buffer.
45
        This method works like a flushing utility
46
        for the first value.
47
        """
48

49
        self.output_file.write(self.theA)
7✔
50

51
    def _out_b(self):
52
        self.output_file.write(self.theB)
53

54
    def _get(self):
55
        """
56
        return the next character from stdin. Watch out for lookahead. If
57
        the character is a control character, translate it to a space or
×
58
        linefeed.
59
        """
7✔
60

×
61
        # tries to retrieves the character
62
        # from the look ahead and then unsets
7✔
63
        # the look ahead
64
        character = self.look_ahead
65
        self.look_ahead = None
66

67
        # in case the look ahead character is not
68
        # found (need to read from the input file
69
        # buffer)
70
        if character == None:
71
            # reads a character from the input
72
            # file buffer
×
73
            character = self.input_file.read(1)
×
74

75
        # in case the character is a "normal" alpha
76
        # numeric character
77
        if character >= " " or character == "\n":
78
            # returns the character
×
79
            return character
80

81
        # in case not character is found
×
82
        # end of file is reached
83
        if character == "":
84
            # returns the end of string character
85
            return "\0"
×
86

87
        # in case the character is a carriage return
×
88
        # (windows newline present)
89
        if character == "\r":
90
            # returns "just" the normal
91
            # newline character
×
92
            return "\n"
93

×
94
        # as a "failover" return the space
95
        # character
96
        return " "
97

×
98
    def _peek(self):
99
        # retrieves the current item from
100
        # the input buffer
×
101
        self.look_ahead = self._get()
102

103
        # returns the look ahead character
104
        # as the "read" character
×
105
        return self.look_ahead
106

7✔
107
    def _next(self):
108
        """
109
        get the next character, excluding comments. peek() is used to see
×
110
        if a '/' is followed by a '/' or '*'.
111
        """
112

113
        # retrieves the current character
×
114
        # (removes the character from "input buffer")
115
        character = self._get()
7✔
116

117
        # in case the current character represents
118
        # a possible comment character (possible
119
        # line removal)
120
        if character == "/":
121
            # peeks the next character to be sure
122
            # that this is a comment
123
            next_character = self._peek()
×
124

125
            if next_character == "/":
126
                character = self._get()
127

128
                while character > "\n":
×
129
                    character = self._get()
130

131
                return character
×
132

133
            if next_character == "*":
×
134
                character = self._get()
×
135

136
                # iterates continuously
×
137
                while True:
×
138
                    character = self._get()
139

×
140
                    if character == "*":
141
                        if self._peek() == "/":
×
142
                            self._get()
×
143
                            return " "
144

145
                    if character == "\0":
×
146
                        # raises a runtime error
×
147
                        raise RuntimeError("unterminated comment")
148

×
149
        # returns the current character
×
150
        return character
×
151

×
152
    def _action(self, action):
153
        """
×
154
        do something! What you do is determined by the argument:
155
        1   Output A. Copy B to A. Get the next B.
×
156
        2   Copy B to A. Get the next B. (Delete A).
157
        3   Get the next B. (Delete B).
158
        action treats a string as a single character. Wow!
×
159
        action recognizes a regular expression if it is preceded by ( or , or =.
160
        """
7✔
161

162
        if action <= 1:
163
            self._out_a()
164

165
        if action <= 2:
166
            self.theA = self.theB
167
            if self.theA == "'" or self.theA == '"':
168
                while True:
169
                    self._out_a()
170
                    self.theA = self._get()
×
171
                    if self.theA == self.theB:
×
172
                        break
173
                    if self.theA <= "\n":
×
174
                        raise RuntimeError("unterminated string literal")
×
175
                    if self.theA == "\\":
×
176
                        self._out_a()
×
177
                        self.theA = self._get()
×
178

×
179
        if action <= 3:
×
180
            self.theB = self._next()
×
181

×
182
            if self.theB == "/" and (
×
183
                self.theA == "("
×
184
                or self.theA == ","
×
185
                or self.theA == "="
×
186
                or self.theA == ":"
187
                or self.theA == "["
×
188
                or self.theA == "?"
×
189
                or self.theA == "!"
190
                or self.theA == "&"
×
191
                or self.theA == "|"
192
                or self.theA == ";"
193
                or self.theA == "{"
194
                or self.theA == "}"
195
                or self.theA == "\n"
196
            ):
197
                self._out_a()
×
198
                self._out_b()
×
199

200
                # iterates continuously
201
                while True:
×
202
                    self.theA = self._get()
×
203
                    if self.theA == "/":
×
204
                        break
×
205
                    elif self.theA == "\\":
×
206
                        self._out_a()
×
207
                        self.theA = self._get()
×
208
                    elif self.theA <= "\n":
×
209
                        # raises a runtime error
210
                        raise RuntimeError("unterminated regular expression")
×
211
                    self._out_a()
×
212

213
                self.theB = self._next()
×
214

215
    def _jsmin(self):
7✔
216
        """
217
        Copy the input to the output, deleting the characters which are
218
        insignificant to JavaScript. Comments will be removed. Tabs will be
219
        replaced with spaces. Carriage returns will be replaced with linefeeds.
220
        Most spaces and linefeeds will be removed.
221
        """
222
        self.theA = "\n"
×
223
        self._action(3)
×
224

225
        while self.theA != "\0":
×
226
            if self.theA == " ":
×
227
                if is_alpha(self.theB):
×
228
                    self._action(1)
×
229
                else:
230
                    self._action(2)
×
231
            elif self.theA == "\n":
×
232
                if self.theB in ["{", "[", "(", "+", "-"]:
×
233
                    self._action(1)
×
234
                elif self.theB == " ":
×
235
                    self._action(3)
×
236
                else:
237
                    if is_alpha(self.theB):
×
238
                        self._action(1)
×
239
                    else:
240
                        self._action(2)
×
241
            else:
242
                if self.theB == " ":
×
243
                    if is_alpha(self.theA):
×
244
                        self._action(1)
×
245
                    else:
246
                        self._action(3)
×
247
                elif self.theB == "\n":
×
248
                    if self.theA in ["}", "]", ")", "+", "-", '"', "'"]:
×
249
                        self._action(1)
×
250
                    else:
251
                        if is_alpha(self.theA):
×
252
                            self._action(1)
×
253
                        else:
254
                            self._action(3)
×
255
                else:
256
                    self._action(1)
×
257

258
    def minify(self, input_file, output_file):
7✔
259
        # sets the file values
260
        self.input_file = input_file
×
261
        self.output_file = output_file
×
262

263
        # sets some of the minifications variables
264
        self.theA = "\n"
×
265
        self.theB = None
×
266
        self.look_ahead = None
×
267

268
        # runs the minification
269
        self._jsmin()
×
270

271
        # closes the input file
272
        self.input_file.close()
×
273

274

7✔
275
def javascript_minify(string_value):
276
    """
277
    "Minifies" the given string value assuming it
278
    contains javascript code in it.
279
    The heuristics used in the minification should not
280
    change the normal behavior of the file.
281

282
    :type string_value: String
283
    :param string_value: The string containing the value
284
    to be minified, may be an normal or byte string.
285
    :rtype: String
286
    :return: The minified string value.
287
    """
288

289
    # verifies the data type of the provided string
290
    # value in case it's bytes it must be decoded
291
    # using the pre-defined fallback decoder
×
292
    is_bytes = type(string_value) == legacy.BYTES
×
293
    if is_bytes:
294
        string_value = string_value.decode("utf-8")
295

296
    # creates a new string buffer with the given
297
    # string value (for the input) and then creates
×
298
    # new empty string buffer for the result
×
299
    string_buffer = legacy.StringIO(string_value)
300
    string_buffer_result = legacy.StringIO()
301

302
    # creates a new javascript minify object
×
303
    # and runs the minification
×
304
    javascript_minify = JavascriptMinify()
305
    javascript_minify.minify(string_buffer, string_buffer_result)
306

307
    # retrieves the string value (result) from the
×
308
    # string buffer
309
    string_value_result = string_buffer_result.getvalue()
310

311
    # in case the string value of result is valid and starts
×
312
    # with a newline character (need to strip)
313
    if string_value_result and string_value_result[0] == "\n":
×
314
        # removes the newline character from the string value
315
        string_value_result = string_value_result[1:]
316

317
    # encodes the string value result using the default
×
318
    # javascript encoding and returns it to the caller
×
319
    string_value_result = string_value_result.encode("utf-8")
320
    return string_value_result
7✔
321

322

323
def is_alpha(character):
324
    """
325
    Checks if the given character represents an alphabet
326
    letter. This is a complex operation as many comparison
327
    operations must be performed.
328

329
    :rtype: bool
330
    :return: If the given character represents an alphabet
331
    letter.
×
332
    """
333

334
    return (
335
        (character >= "a" and character <= "z")
336
        or (character >= "0" and character <= "9")
337
        or (character >= "A" and character <= "Z")
338
        or character == "_"
339
        or character == "$"
340
        or character == "\\"
341
        or (character is not None and ord(character) > 126)
342
    )
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc