Skip to content

Commit dff78b4

Browse files
committed
🎉 Initial commit
This is the inital commit of python-minifier. It currently supports Python 2.6-2.7 and Python 3.3-3.5. Includes a basic API and pyminify command. Further work is required to add support for Python 3.6+ before release.
0 parents  commit dff78b4

18 files changed

+2729
-0
lines changed

.circleci/Dockerfile

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
FROM fedora
2+
3+
# CircleCI required tools
4+
RUN dnf install -y \
5+
git \
6+
openssh \
7+
tar \
8+
gzip \
9+
gpg \
10+
ca-certificates
11+
12+
# Python versions
13+
RUN dnf install -y \
14+
python26 \
15+
python27 \
16+
python33 \
17+
python34 \
18+
python35 \
19+
python36 \
20+
python37 \
21+
pypy \
22+
pypy3
23+
24+
WORKDIR /tmp/work
25+
ENTRYPOINT ["/bin/bash"]

.circleci/config.yml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
version: 2
2+
jobs:
3+
test:
4+
docker:
5+
- image: danielflook/python-minifier-build
6+
steps:
7+
- checkout
8+
9+
- run:
10+
name: Set version statically
11+
command: |
12+
VERSION=$(python setup.py --version)
13+
sed -i "s/setup_requires=.*/version='$VERSION',/; s/use_scm_version=.*//" setup.py
14+
15+
- run:
16+
name: tox
17+
command: |
18+
pip install -r requirements-tox.txt
19+
tox -e py27,py34,py35,pypy
20+
21+
publish:
22+
docker:
23+
- image: danielflook/python-minifier-build
24+
steps:
25+
- checkout
26+
27+
- run:
28+
name: Set version statically
29+
command: |
30+
VERSION=$(python setup.py --version)
31+
sed -i "s/setup_requires=.*/version='$VERSION',/; s/use_scm_version=.*//" setup.py
32+
33+
- run:
34+
name: Add signing key
35+
command: |
36+
echo $SIGNING_KEY | base64 -d > private.key
37+
gpg --import private.key
38+
39+
- run:
40+
name: sdist
41+
command: |
42+
pip3 install --upgrade setuptools wheel twine pip
43+
python3 setup.py sdist bdist_wheel
44+
twine upload --sign dist/*
45+
46+
workflows:
47+
version: 2
48+
build:
49+
jobs:
50+
- test
51+
- publish:
52+
requires:
53+
- test

.gitignore

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.tox/
2+
.idea/
3+
__pycache__/
4+
*.py[cod]
5+
dist/
6+
downloads/
7+
eggs/
8+
.eggs/
9+
wheels/
10+
*.egg-info/
11+
*.egg
12+
venv/

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2018 Daniel Flook
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

MANIFEST.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
include *.txt
2+
include LICENSE
3+
include tox.ini

README.md

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# Python Minifier
2+
3+
Transforms Python source code into it's most compact representation.
4+
5+
python-minifier supports Python 2.6 to 2.7 and Python 3.3 to 3.5..
6+
7+
As an example, the following python source:
8+
9+
```python
10+
import ast
11+
12+
from python_minifier.miniprinter import MiniPrinter
13+
from python_minifier.ast_compare import AstCompare
14+
15+
class UnstableMinification(Exception):
16+
def __init__(self, original, minified, exception):
17+
self.original = original
18+
self.minified = minified
19+
self.exception = exception
20+
21+
def __str__(self):
22+
return str(self.exception)
23+
24+
def minify(source):
25+
26+
code = ast.parse(source)
27+
minifier = MiniPrinter()
28+
29+
minifier.visit(code)
30+
31+
try:
32+
# Check that the minified code is identical to the original
33+
minified_code = ast.parse(minifier.code)
34+
comparer = AstCompare()
35+
comparer.compare(code, minified_code)
36+
except Exception as exception:
37+
raise UnstableMinification(source, minifier.code, exception)
38+
39+
return minifier.code
40+
```
41+
42+
Becomes:
43+
44+
```python
45+
import ast
46+
from python_minifier.miniprinter import MiniPrinter
47+
from python_minifier.ast_compare import AstCompare
48+
class UnstableMinification(Exception):
49+
def __init__(self,original,minified,exception):
50+
self.original=original;self.minified=minified;self.exception=exception
51+
def __str__(self):return str(self.exception)
52+
def minify(source):
53+
code=ast.parse(source);minifier=MiniPrinter();minifier.visit(code)
54+
try:
55+
minified_code=ast.parse(minifier.code);comparer=AstCompare();comparer.compare(code,minified_code)
56+
except Exception as exception:
57+
raise UnstableMinification(source,minifier.code,exception)
58+
return minifier.code
59+
```
60+
61+
## Why?
62+
63+
AWS Cloudformation templates may have AWS lambda function source code embedded in them, but only if the function is less
64+
than 4KiB. I wrote this package so I could write python normally and still embed the module in a template.
65+
66+
## Installation
67+
68+
To install python-minifier use pip:
69+
70+
```bash
71+
$ pip install python-minifier
72+
```
73+
74+
Note that python-minifier depends on the python interpreter for parsing source code,
75+
so install using a version of python appropriate for your source.
76+
77+
python-minifier runs with and can minify code written for Python 2.6 to 2.7 and Python 3.3 to 3.5.
78+
79+
## Usage
80+
81+
To minify a source file, and write the minified module to stdout:
82+
83+
```bash
84+
$ pyminify hello.py
85+
```
86+
87+
There is also an API. The same example would look like:
88+
89+
```python
90+
import python_minifier
91+
92+
with open('hello.py') as f:
93+
print(python_minifier.minify(f.read()))
94+
```
95+
96+
## License
97+
98+
Available under the MIT License. Full text is in the [LICENSE](LICENSE) file.
99+
100+
Copyright 2018 Daniel Flook

requirements-tox.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
tox
2+
virtualenv<16.0.0

setup.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import os.path
2+
from setuptools import setup, find_packages
3+
4+
readme_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'README.md')
5+
with open(readme_path) as f:
6+
long_desc = f.read()
7+
8+
setup(
9+
name='python_minifier',
10+
description='Transform Python source code into it\'s most compact representation',
11+
author='Daniel Flook',
12+
author_email='daniel@flook.org',
13+
url='https://github.com/dflook/python-minifier',
14+
license='MIT',
15+
project_urls={
16+
'Issues': 'https://github.com/dflook/python-minifier/issues',
17+
'Say Thanks!': 'https://saythanks.io/to/dflook',
18+
},
19+
keywords='minify minifier',
20+
21+
use_scm_version=True,
22+
package_dir={'': 'src'},
23+
packages=find_packages('src'),
24+
long_description=long_desc,
25+
long_description_content_type='text/markdown',
26+
27+
python_requires='>=2.6, !=3.0.*, !=3.1.*, !=3.2.*, <3.6',
28+
setup_requires=['setuptools_scm'],
29+
30+
classifiers=[
31+
'Development Status :: 5 - Production/Stable',
32+
'License :: OSI Approved :: MIT License',
33+
'Programming Language :: Python',
34+
'Programming Language :: Python :: 3',
35+
'Programming Language :: Python :: 3.3',
36+
'Programming Language :: Python :: 3.4',
37+
'Programming Language :: Python :: 3.5',
38+
'Programming Language :: Python :: 2',
39+
'Programming Language :: Python :: 2.6',
40+
'Programming Language :: Python :: 2.7',
41+
'Programming Language :: Python :: Implementation :: CPython',
42+
'Programming Language :: Python :: Implementation :: PyPy',
43+
'Intended Audience :: Developers',
44+
'Topic :: Software Development'
45+
],
46+
47+
entry_points = {
48+
'console_scripts': ['pyminify=python_minifier.__main__:main']
49+
},
50+
51+
zip_safe=True
52+
)

src/python_minifier/__init__.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import ast
2+
3+
from python_minifier.ast_compare import AstComparer, CompareError
4+
from python_minifier.miniprinter import MiniPrinter
5+
6+
7+
class UnstableMinification(RuntimeError):
8+
def __init__(self, exception, source, minified):
9+
self.exception = exception
10+
self.source = source
11+
self.minified = minified
12+
13+
def __str__(self):
14+
return 'Minification was unstable! Please create an issue at https://github.com/dflook/python-minifier/issues'
15+
16+
17+
def minify(source, filename=None):
18+
"""
19+
Minify a python module
20+
21+
With the default arguments an exact representation of the input source is returned.
22+
23+
:param str source: The python module to minify
24+
:param str filename: The original source filename if known
25+
:rtype: str
26+
27+
"""
28+
29+
filename = filename or 'python_minifer.minify source'
30+
31+
# This will raise if the source file can't be parsed
32+
module = ast.parse(source, filename)
33+
34+
printer = MiniPrinter()
35+
printer(module)
36+
37+
try:
38+
minified_module = ast.parse(printer.code, 'python_minifier.minify output')
39+
except SyntaxError as syntax_error:
40+
raise UnstableMinification(syntax_error, source, printer.code)
41+
42+
try:
43+
comparer = AstComparer()
44+
comparer.compare(module, minified_module)
45+
except CompareError as compare_error:
46+
raise UnstableMinification(compare_error, source, printer.code)
47+
48+
return printer.code
49+
50+
51+
def awslambda(source, filename=None):
52+
"""
53+
Minify a python module for use as an AWS Lambda function
54+
55+
This returns a string suitable for embedding in a cloudformation template.
56+
57+
:param str source: The python module to minify
58+
:param str filename: The original source filename if known
59+
:rtype: str
60+
61+
"""
62+
63+
return minify(source, filename)

src/python_minifier/__main__.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from __future__ import print_function
2+
3+
import sys
4+
5+
from python_minifier import minify
6+
7+
8+
def main():
9+
if len(sys.argv) < 2:
10+
print('Usage: pyminify <PATH>')
11+
exit(-1)
12+
13+
with open(sys.argv[1], 'rb') as f:
14+
print(minify(f.read()))
15+
16+
17+
if __name__ == '__main__':
18+
main()

0 commit comments

Comments
 (0)