Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 22 additions & 13 deletions personal_python_ast_optimizer/futures.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
futures_to_mandatory_version: dict[str, tuple[int, int]] = {
"nested_scopes": (2, 2),
"generators": (2, 3),
"with_statement": (2, 6),
"division": (3, 0),
"absolute_import": (3, 0),
"print_function": (3, 0),
"unicode_literals": (3, 0),
"generator_stop": (3, 7),
}
class Futures:

__slots__ = ("name", "mandatory_version")

def __init__(self, name: str, mandatory_version: tuple[int, int]) -> None:
self.name: str = name
self.mandatory_version: tuple[int, int] = mandatory_version


futures_to_mandatory_version: list[Futures] = [
Futures("nested_scopes", (2, 2)),
Futures("generators", (2, 3)),
Futures("with_statement", (2, 6)),
Futures("division", (3, 0)),
Futures("absolute_import", (3, 0)),
Futures("print_function", (3, 0)),
Futures("unicode_literals", (3, 0)),
Futures("generator_stop", (3, 7)),
]


def get_unneeded_futures(python_version: tuple[int, int]) -> list[str]:
"""Returns list of __future__ imports that are unneeded in provided
python version"""
unneeded_futures: list[str] = [
future
for future, mandatory_version in futures_to_mandatory_version.items()
if python_version >= mandatory_version
future.name
for future in futures_to_mandatory_version
if python_version >= future.mandatory_version
]

return unneeded_futures
12 changes: 4 additions & 8 deletions personal_python_ast_optimizer/parser/minifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def _yield_updated_text(self, text_iter: Iterable[str]) -> Iterator[str]:
yield text.strip()
elif text in comparison_and_conjunctions:
yield self._get_space_before_write() + text[1:]
elif text:
elif text != "":
yield text

def visit_node(
Expand Down Expand Up @@ -227,24 +227,20 @@ def _write_decorators(
self.traverse(deco)

def _last_char_is(self, char_to_check: str) -> bool:
return bool(self._source) and self._source[-1][-1:] == char_to_check
return bool(self._source) and self._source[-1][-1] == char_to_check

def _get_space_before_write(self) -> str:
return (
""
if not self._source
or self._source[-1][-1:] in chars_that_dont_need_whitespace
or self._source[-1][-1] in chars_that_dont_need_whitespace
else " "
)

def _get_line_splitter(self) -> Literal["", "\n", ";"]:
"""Get character that starts the next line of code with the shortest
possible whitespace. Either a new line, semicolon, or nothing."""
if (
len(self._source) > 0
and self._source[-1] == ":"
and self.can_write_body_in_one_line
):
if self._source and self._source[-1] == ":" and self.can_write_body_in_one_line:
return ""

if (
Expand Down
11 changes: 0 additions & 11 deletions personal_python_ast_optimizer/parser/skipper.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,6 @@ def _combine_imports(body: list) -> None:
body[:] = new_body

def visit_Module(self, node: ast.Module) -> ast.AST:
if not self._has_code_to_skip():
return node

if self.token_types_config.skip_dangling_expressions:
skip_dangling_expressions(node)

Expand Down Expand Up @@ -573,14 +570,6 @@ def _use_version_optimization(self, min_version: tuple[int, int]) -> bool:
else self.target_python_version >= min_version
)

def _has_code_to_skip(self) -> bool:
return (
self.target_python_version is not None
or self.optimizations_config.has_code_to_skip()
or self.tokens_config.has_code_to_skip()
or self.token_types_config.has_code_to_skip()
)

def _should_skip_function_assign(self, node: ast.Assign | ast.AnnAssign) -> bool:
return (
isinstance(node.value, ast.Call)
Expand Down
27 changes: 18 additions & 9 deletions personal_python_ast_optimizer/parser/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@


def exclude_imports(node: ast.Import | ast.ImportFrom, exlcudes: Iterable[str]) -> None:
node.names = [
alias for alias in node.names if (alias.asname or alias.name) not in exlcudes
]
if exlcudes:
node.names = [
alias
for alias in node.names
if (alias.asname or alias.name) not in exlcudes
]


def filter_imports(node: ast.Import | ast.ImportFrom, filter: Iterable[str]) -> None:
Expand Down Expand Up @@ -51,18 +54,24 @@ def skip_dangling_expressions(
def skip_base_classes(
node: ast.ClassDef, classes_to_ignore: Iterable[str] | TokensToSkip
) -> None:
node.bases = [
base for base in node.bases if getattr(base, "id", "") not in classes_to_ignore
]
if classes_to_ignore:
node.bases = [
base
for base in node.bases
if getattr(base, "id", "") not in classes_to_ignore
]


def skip_decorators(
node: ast.ClassDef | ast.FunctionDef | ast.AsyncFunctionDef,
decorators_to_ignore: Iterable[str] | TokensToSkip,
) -> None:
node.decorator_list = [
n for n in node.decorator_list if get_node_name(n) not in decorators_to_ignore
]
if decorators_to_ignore:
node.decorator_list = [
n
for n in node.decorator_list
if get_node_name(n) not in decorators_to_ignore
]


def remove_duplicate_slots(
Expand Down
2 changes: 1 addition & 1 deletion version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
5.2.1
5.2.2