7
7
from python_minifier import minify
8
8
from python_minifier .transforms .remove_annotations_options import RemoveAnnotationsOptions
9
9
10
- # Python 2.7 compatibility for UTF-8 file writing
11
- if sys .version_info [0 ] == 2 :
12
- import codecs
13
- def open_utf8 (filename , mode ):
14
- return codecs .open (filename , mode , encoding = 'utf-8' )
15
- else :
16
- def open_utf8 (filename , mode ):
17
- return open (filename , mode , encoding = 'utf-8' )
18
10
19
- def safe_stdout_write (text ):
20
- """Write text to stdout with proper encoding handling."""
21
- try :
22
- sys .stdout .write (text )
23
- except UnicodeEncodeError :
24
- # Fallback: encode to UTF-8 and write to stdout.buffer (Python 3) or sys.stdout (Python 2)
25
- if sys .version_info [0 ] >= 3 and hasattr (sys .stdout , 'buffer' ):
26
- sys .stdout .buffer .write (text .encode ('utf-8' ))
27
- else :
28
- # Python 2.7 or no buffer attribute - write UTF-8 encoded bytes
29
- sys .stdout .write (text .encode ('utf-8' ))
11
+ class MinificationNotBeneficialError (Exception ):
12
+ """Raised when minification results in larger output than the original."""
13
+ pass
14
+
15
+ def stdout_write_bytes (data ):
16
+ """Write bytes to stdout with proper Python 2.7/3.x compatibility."""
17
+ if sys .version_info >= (3 , 0 ):
18
+ sys .stdout .buffer .write (data )
19
+ else :
20
+ sys .stdout .write (data )
30
21
31
22
32
23
if sys .version_info >= (3 , 8 ):
@@ -72,12 +63,23 @@ def main():
72
63
if len (args .path ) == 1 and args .path [0 ] == '-' :
73
64
# minify stdin
74
65
source = sys .stdin .buffer .read () if sys .version_info >= (3 , 0 ) else sys .stdin .read ()
75
- minified = do_minify (source , 'stdin' , args )
66
+ try :
67
+ minified = do_minify (source , 'stdin' , args )
68
+ except MinificationNotBeneficialError :
69
+ # Use original source when minification isn't beneficial
70
+ if args .output :
71
+ with open (args .output , 'wb' ) as f :
72
+ f .write (source )
73
+ else :
74
+ # Write original source to stdout
75
+ stdout_write_bytes (source )
76
+ return
77
+
76
78
if args .output :
77
- with open_utf8 (args .output , 'w ' ) as f :
79
+ with open (args .output , 'wb ' ) as f :
78
80
f .write (minified )
79
81
else :
80
- safe_stdout_write (minified )
82
+ stdout_write_bytes (minified )
81
83
82
84
else :
83
85
# minify source paths
@@ -88,16 +90,30 @@ def main():
88
90
with open (path , 'rb' ) as f :
89
91
source = f .read ()
90
92
91
- minified = do_minify (source , path , args )
93
+ try :
94
+ minified = do_minify (source , path , args )
95
+ except MinificationNotBeneficialError :
96
+ # Use original source when minification isn't beneficial
97
+ if args .in_place :
98
+ # File is already the original, no need to write
99
+ pass
100
+ elif args .output :
101
+ # Write original source to output
102
+ with open (args .output , 'wb' ) as f :
103
+ f .write (source )
104
+ else :
105
+ # Write original source to stdout
106
+ stdout_write_bytes (source )
107
+ continue
92
108
93
109
if args .in_place :
94
- with open_utf8 (path , 'w ' ) as f :
110
+ with open (path , 'wb ' ) as f :
95
111
f .write (minified )
96
112
elif args .output :
97
- with open_utf8 (args .output , 'w ' ) as f :
113
+ with open (args .output , 'wb ' ) as f :
98
114
f .write (minified )
99
115
else :
100
- safe_stdout_write (minified )
116
+ stdout_write_bytes (minified )
101
117
102
118
103
119
def parse_args ():
@@ -301,6 +317,15 @@ def error(os_error):
301
317
302
318
303
319
def do_minify (source , filename , minification_args ):
320
+ """Minify Python source code with size-based fallback.
321
+
322
+ :param bytes source: Source code as bytes (from file 'rb' or stdin.buffer)
323
+ :param str filename: Filename for error reporting
324
+ :param argparse.Namespace minification_args: CLI arguments for minification options
325
+ :returns: Minified source code as UTF-8 bytes
326
+ :rtype: bytes
327
+ :raises MinificationNotBeneficialError: When minified output is larger than original
328
+ """
304
329
305
330
preserve_globals = []
306
331
if minification_args .preserve_globals :
@@ -329,7 +354,7 @@ def do_minify(source, filename, minification_args):
329
354
remove_class_attribute_annotations = minification_args .remove_class_attribute_annotations ,
330
355
)
331
356
332
- return minify (
357
+ minified_result = minify (
333
358
source ,
334
359
filename = filename ,
335
360
combine_imports = minification_args .combine_imports ,
@@ -351,6 +376,19 @@ def do_minify(source, filename, minification_args):
351
376
constant_folding = minification_args .constant_folding
352
377
)
353
378
379
+ # Encode minified result to bytes for comparison and output
380
+ minified_bytes = minified_result .encode ('utf-8' )
381
+
382
+ # Check if environment variable forces minified output
383
+ if os .environ .get ('PYMINIFY_FORCE_BEST_EFFORT' ):
384
+ return minified_bytes
385
+
386
+ # Compare byte lengths for accurate size comparison
387
+ if len (minified_bytes ) > len (source ):
388
+ raise MinificationNotBeneficialError ("Minified output is longer than original" )
389
+
390
+ return minified_bytes
391
+
354
392
355
393
if __name__ == '__main__' :
356
394
main ()
0 commit comments