python-minifier
python-minifier copied to clipboard
wish: remove attribute annotations not generating code
var: str
is a legal annotation but it creates no executable code. Would be nice to remove these.
I am using python-minifier for asserting that a commit only changes annotations but not real code. Very helpful for annotating old source code. Or would there be a better tool for this?
Hi @wrohdewald, that's an interesting use of python-minifier!
A statement like var: str is an assignment without a value, but it puts the namevar in the local scope the same way var: str = 'foo' would do. You're right it has no effect when executed, but it's presence can affect the behaviour of a program.
Consider this program:
var = 'hello'
def func():
var: str
print(var)
When func() is executed it should raise UnboundLocalError because var is in the function namespace but has no value. If we removed the var: str statement it would print 'hello' instead.
What python-minifier should be doing is replacing the annotation value with '0', e.g
var = 'hello'
def func():
var: 0
print(var)
which should preserve the behaviour but make the annotation shorter.
OK, I see why this cannot be safely removed - at least not without a special option. Shortening is not much of a help - the diff output I have to read does not get shorter.
OTOH - what about code under if TYPE_CHECKING - if and only if TYPE_CHECKING is the original typing.TYPE_CHECKING - I see no reason why this could not be safely removed.
Like in
from typing import TYPE_CHECKING
b = cast(int, 1)
reveal_type(b)
if TYPE_CHECKING:
a = 5
assert 'b' in globals()
assert 'a' not in globals()
expecting
b=1
assert'b'in globals()
assert'a'not in globals()
typing.cast() would be another candidate. reveal_type() too - it is only recognized by mypy.
Hi @wrohdewald, that's a good idea
This works for me, but I do not know enough about ast to finish this. Like when somebody renames or redefines TYPE_CHECKING or cast()
class RemoveAnnotations(SuiteTransformer):
...
def visit_If(self, node):
if hasattr(node.test, 'id') and node.test.id == 'TYPE_CHECKING':
return ast.Pass()
return node
def visit_ImportFrom(self, node):
if node.module == 'typing':
return ast.Pass()
return node
def visit_Call(self, node):
if hasattr(node, 'func'):
if hasattr(node.func, 'id'):
if node.func.id == 'cast' and len(node.args) == 2:
return node.args[1]
return node