dflook / python-minifier

Transform Python source code into its most compact representation
MIT License
583 stars 43 forks source link

Automatically strip type hints when needed #36

Closed greyblue9 closed 2 years ago

greyblue9 commented 2 years ago

Comments / feedback appreciated! Love your package. ❤

dflook commented 2 years ago

How is this different from the remove annotations transform?

greyblue9 commented 2 years ago

How is this different from the remove annotations transform?

Hi @dflook ,

Here is the code I am passing to minify, in its original form:

from collections.abc import Iterable, Iterator, Mapping, Sequence
from typing import ClassVar, Protocol, TypeVar

_T = TypeVar('_T')
_Node = TypeVar('_Node', bound='Node')

class VisitorFunc(Protocol[_Node, _T]):
    def __call__(self, node: _Node) -> _T: print(node)

VisitorMapping = Mapping[type[_Node], VisitorFunc[_Node, _T]]

class Node:
    def accept(
        self: _Node,
        visitor: VisitorFunc[_Node, _T] | VisitorMapping[_Node, _T]
    ) -> _T:
        if isinstance(visitor, Mapping):
            cls = self.__class__
            visitor_func = visitor.get(cls)
            if visitor_func is not None:
                return visitor_func(self)
        else:
            return visitor(self)

When I run the following code, I get a parse error with the latest master on python 2.7:

import python_minifier
python_minifier.minify(u"\nfrom collections.abc import Iterable, Iterator, Mapping, Sequence\nfrom typing import ClassVar, Protocol, TypeVar\n\n_T = TypeVar('_T')\n_Node = TypeVar('_Node', bound='Node')\n\nclass VisitorFunc(Protocol[_Node, _T]):\n    def __call__(self, node: _Node) -> _T: print(node)\n\nVisitorMapping = Mapping[type[_Node], VisitorFunc[_Node, _T]]\n\nclass Node:\n    def accept(\n        self: _Node,\n        visitor: VisitorFunc[_Node, _T] | VisitorMapping[_Node, _T]\n    ) -> _T:\n        if isinstance(visitor, Mapping):\n            cls = self.__class__\n            visitor_func = visitor.get(cls)\n            if visitor_func is not None:\n                return visitor_func(self)\n        else:\n            return visitor(self)\n", filename="printer.py", remove_annotations=True, remove_pass=True, remove_literal_statements=True, combine_imports=True, hoist_literals=True, rename_locals=True, preserve_locals=None, rename_globals=True, preserve_globals=None, remove_object_base=True, convert_posargs_to_args=True)

The error is:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "python_minifier/__init__.py", line 95, in minify
    module = ast.parse(source, filename)
  File "/data/data/com.termux/files/usr/lib/python2.7/ast.py", line 37, in parse
    return compile(source, filename, mode, PyCF_ONLY_AST)
  File "printer.py", line 9
    def __call__(self, node: _Node) -> _T: print(node)
                           ^
SyntaxError: invalid syntax

I guess it doesn't remove all annotations?

dflook commented 2 years ago

python-minifier needs to run using a version of python compatible with the source code you are trying to minify. If there are annotations in the source, you'll need to run python-minifer with Python 3+

greyblue9 commented 2 years ago

python-minifier needs to run using a version of python compatible with the source code you are trying to minify. If there are annotations in the source, you'll need to run python-minifer with Python 3+

Thanks for the explanation. And it does successfully minify on python 3.

May I ask why this restriction is there? If I remove all annotations, it works fine. Does remove_annotations a no-op on python 2?

dflook commented 2 years ago

python-minifier uses the built in ast module for parsing, so it can only support source that the running interpreter also supports.

remove_annotations will have no effect with Python 2, as there will never be any annotations to remove. If there were, you'd get a parse error instead.

greyblue9 commented 2 years ago

python-minifier uses the built in ast module for parsing, so it can only support source that the running interpreter also supports.

remove_annotations will have no effect with Python 2, as there will never be any annotations to remove. If there were, you'd get a parse error instead.

Gotcha, thanks for the clarifications.