From 7e9b9f061b2bce9f46a5c3b13990f4e22d290907 Mon Sep 17 00:00:00 2001 From: jbjd Date: Sat, 17 Jan 2026 20:49:28 -0600 Subject: [PATCH 1/3] Improvement: No longer turn all type hints to int --- .../parser/config.py | 2 +- .../parser/skipper.py | 19 ++--- tests/parser/test_class.py | 10 ++- tests/parser/test_slots.py | 4 +- tests/parser/test_type_hints.py | 74 +++++++++++++++++++ 5 files changed, 91 insertions(+), 18 deletions(-) create mode 100644 tests/parser/test_type_hints.py diff --git a/personal_python_ast_optimizer/parser/config.py b/personal_python_ast_optimizer/parser/config.py index 559d12b..e99a01b 100644 --- a/personal_python_ast_optimizer/parser/config.py +++ b/personal_python_ast_optimizer/parser/config.py @@ -10,7 +10,7 @@ class TypeHintsToSkip(Enum): NONE = 0 - # ALL might be unsafe, NamedTuple or TypedDict for example + # ALL might be unsafe, NamedTuple for example ALL = 1 # Should be safe in most cases ALL_BUT_CLASS_VARS = 2 diff --git a/personal_python_ast_optimizer/parser/skipper.py b/personal_python_ast_optimizer/parser/skipper.py index 07c3b2e..234d4e5 100644 --- a/personal_python_ast_optimizer/parser/skipper.py +++ b/personal_python_ast_optimizer/parser/skipper.py @@ -422,18 +422,15 @@ def visit_AnnAssign(self, node: ast.AnnAssign) -> ast.AST | None: parsed_node: ast.AnnAssign = self.generic_visit(node) # type: ignore - if self.token_types_config.skip_type_hints: - if ( - not parsed_node.value - and self._node_context == _NodeContext.CLASS - and self.token_types_config.skip_type_hints - == TypeHintsToSkip.ALL_BUT_CLASS_VARS - ): - parsed_node.annotation = ast.Name("int") - elif parsed_node.value is None: + if self.token_types_config.skip_type_hints and ( + self._node_context != _NodeContext.CLASS + or self.token_types_config.skip_type_hints + != TypeHintsToSkip.ALL_BUT_CLASS_VARS + ): + if parsed_node.value is None: return None - else: - return ast.Assign([parsed_node.target], parsed_node.value) + + return ast.Assign([parsed_node.target], parsed_node.value) return parsed_node diff --git a/tests/parser/test_class.py b/tests/parser/test_class.py index 368063d..b030d9f 100644 --- a/tests/parser/test_class.py +++ b/tests/parser/test_class.py @@ -19,19 +19,21 @@ class Foo(): def test_class_preserves_type_hints(): before_and_after = BeforeAndAfter( """ +import some_type class SomeTuple(): '''A tuple, wow!''' - thing1: str + thing1: some_type thing2: int def a(): class B: thing3: None return B""", - """class SomeTuple: -\tthing1:int;thing2:int + """import some_type +class SomeTuple: +\tthing1:some_type;thing2:int \tdef a(): -\t\tclass B:thing3:int +\t\tclass B:thing3:None \t\treturn B""", ) run_minifier_and_assert_correct( diff --git a/tests/parser/test_slots.py b/tests/parser/test_slots.py index be8d9a4..c187d9d 100644 --- a/tests/parser/test_slots.py +++ b/tests/parser/test_slots.py @@ -8,9 +8,9 @@ def test_remove_dup_slots_tuple(): run_minifier_and_assert_correct(before_and_after) -def test_remove_dup_slots__list_annotation(): +def test_remove_dup_slots_list_annotation(): before_and_after = BeforeAndAfter( - "class A:__slots__: list = ['a', 'b', 'a']", "class A:__slots__=['a','b']" + "class A:__slots__: list = ['a', 'b', 'a']", "class A:__slots__:list=['a','b']" ) run_minifier_and_assert_correct(before_and_after) diff --git a/tests/parser/test_type_hints.py b/tests/parser/test_type_hints.py new file mode 100644 index 0000000..9c5bf10 --- /dev/null +++ b/tests/parser/test_type_hints.py @@ -0,0 +1,74 @@ +from personal_python_ast_optimizer.parser.config import ( + TokenTypesConfig, + TypeHintsToSkip, +) +from tests.utils import BeforeAndAfter, run_minifier_and_assert_correct + + +def test_removes_type_hints_all_but_class_var(): + before_and_after = BeforeAndAfter( + """ +import some_type + +a: some_type + +def b(): + c: int = 3 + print(c) + +class C: + a: str +""", + "def b():c=3;print(c)\nclass C:a:str", + ) + + run_minifier_and_assert_correct(before_and_after) + + +def test_remove_no_type_hints(): + before_and_after = BeforeAndAfter( + """ +import some_type + +a: some_type + +def b(): + c: int = 3 + print(c) + +class C: + a: str +""", + """import some_type +a:some_type +def b():c:int=3;print(c) +class C:a:str""", + ) + + run_minifier_and_assert_correct( + before_and_after, + token_types_config=TokenTypesConfig(skip_type_hints=TypeHintsToSkip.NONE), + ) + + +def test_remove_all_type_hints(): + before_and_after = BeforeAndAfter( + """ +import some_type + +a: some_type + +def b(): + c: int = 3 + print(c) + +class C: + a: str +""", + "def b():c=3;print(c)\nclass C:pass", + ) + + run_minifier_and_assert_correct( + before_and_after, + token_types_config=TokenTypesConfig(skip_type_hints=TypeHintsToSkip.ALL), + ) From 161b995df39ed69a9fb72bd960092db13b20972d Mon Sep 17 00:00:00 2001 From: jbjd Date: Sat, 17 Jan 2026 20:49:42 -0600 Subject: [PATCH 2/3] version bump --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index f3b5af3..5e32542 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -6.1.1 +6.1.2 From b48c2cd849aa478ee64a0860c997cd806514adb6 Mon Sep 17 00:00:00 2001 From: jbjd Date: Sat, 17 Jan 2026 20:52:07 -0600 Subject: [PATCH 3/3] More readable condition --- personal_python_ast_optimizer/parser/skipper.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/personal_python_ast_optimizer/parser/skipper.py b/personal_python_ast_optimizer/parser/skipper.py index 234d4e5..b5955ca 100644 --- a/personal_python_ast_optimizer/parser/skipper.py +++ b/personal_python_ast_optimizer/parser/skipper.py @@ -422,10 +422,10 @@ def visit_AnnAssign(self, node: ast.AnnAssign) -> ast.AST | None: parsed_node: ast.AnnAssign = self.generic_visit(node) # type: ignore - if self.token_types_config.skip_type_hints and ( - self._node_context != _NodeContext.CLASS - or self.token_types_config.skip_type_hints - != TypeHintsToSkip.ALL_BUT_CLASS_VARS + if self.token_types_config.skip_type_hints == TypeHintsToSkip.ALL or ( + self.token_types_config.skip_type_hints + == TypeHintsToSkip.ALL_BUT_CLASS_VARS + and self._node_context != _NodeContext.CLASS ): if parsed_node.value is None: return None