diff --git a/src/python_minifier/__main__.py b/src/python_minifier/__main__.py index ec7c2ab7..18a4e5cd 100644 --- a/src/python_minifier/__main__.py +++ b/src/python_minifier/__main__.py @@ -7,6 +7,7 @@ from python_minifier import minify from python_minifier.transforms.remove_annotations_options import RemoveAnnotationsOptions + if sys.version_info >= (3, 8): from importlib import metadata @@ -270,9 +271,9 @@ def error(os_error): for path_arg in args.path: if os.path.isdir(path_arg): - for root, dirs, files in os.walk(path_arg, onerror=error, followlinks=True): + for root, _dirs, files in os.walk(path_arg, onerror=error, followlinks=True): for file in files: - if file.endswith('.py') or file.endswith('.pyw'): + if file.endswith(('.py', '.pyw')): yield os.path.join(root, file) else: yield path_arg diff --git a/src/python_minifier/ast_compat.py b/src/python_minifier/ast_compat.py index da7f5a11..b52a167b 100644 --- a/src/python_minifier/ast_compat.py +++ b/src/python_minifier/ast_compat.py @@ -8,6 +8,7 @@ from ast import * + # Ideally we don't import anything else if 'TypeAlias' in globals(): diff --git a/src/python_minifier/ast_printer.py b/src/python_minifier/ast_printer.py index 1b9c020e..c8f588b1 100644 --- a/src/python_minifier/ast_printer.py +++ b/src/python_minifier/ast_printer.py @@ -14,6 +14,7 @@ from python_minifier.util import is_ast_node + INDENT = ' ' # The field name that can be omitted for each node diff --git a/src/python_minifier/expression_printer.py b/src/python_minifier/expression_printer.py index d9cd685b..c4a0202a 100644 --- a/src/python_minifier/expression_printer.py +++ b/src/python_minifier/expression_printer.py @@ -629,7 +629,8 @@ def visit_arguments(self, node): def visit_arg(self, node): if isinstance(node, ast.Name): # Python 2 uses Name nodes - return self.visit_Name(node) + self.visit_Name(node) + return self.printer.identifier(node.arg) diff --git a/src/python_minifier/f_string.py b/src/python_minifier/f_string.py index ae505d73..91a50330 100644 --- a/src/python_minifier/f_string.py +++ b/src/python_minifier/f_string.py @@ -36,7 +36,7 @@ def is_correct_ast(self, code): c = ast.parse(code, 'FString candidate', mode='eval') compare_ast(self.node, c.body) return True - except Exception as e: + except Exception: return False def complete_debug_specifier(self, partial_specifier_candidates, value_node): @@ -79,12 +79,12 @@ def candidates(self): # Maybe! try: debug_specifier_candidates = [x + '{' + v.s for x in candidates] - except Exception as e: + except Exception: continue try: candidates = [x + self.str_for(v.s, quote) for x in candidates] - except Exception as e: + except Exception: continue elif isinstance(v, ast.FormattedValue): try: @@ -93,15 +93,14 @@ def candidates(self): x + y for x in candidates for y in FormattedValue(v, nested_allowed, self.pep701).get_candidates() ] + completed debug_specifier_candidates = [] - except Exception as e: + except Exception: continue else: raise RuntimeError('Unexpected JoinedStr value') actual_candidates += ['f' + quote + x + quote for x in candidates] - actual_candidates = filter(self.is_correct_ast, actual_candidates) - return actual_candidates + return filter(self.is_correct_ast, actual_candidates) def str_for(self, s, quote): return s.replace('{', '{{').replace('}', '}}') diff --git a/src/python_minifier/ministring.py b/src/python_minifier/ministring.py index 8aef9472..5cf517ea 100644 --- a/src/python_minifier/ministring.py +++ b/src/python_minifier/ministring.py @@ -32,7 +32,7 @@ def __str__(self): try: eval(self.quote + s + self.quote) - except (UnicodeDecodeError, UnicodeEncodeError) as e: + except (UnicodeDecodeError, UnicodeEncodeError): if self.safe_mode: raise @@ -63,7 +63,7 @@ def to_short(self): } for c in self._s: - if c in escaped.keys(): + if c in escaped: s += escaped[c] else: if self.safe_mode: @@ -95,7 +95,7 @@ def to_long(self): } for c in self._s: - if c in escaped.keys(): + if c in escaped: s += escaped[c] else: if self.safe_mode: diff --git a/src/python_minifier/module_printer.py b/src/python_minifier/module_printer.py index 32d2e084..d2804447 100644 --- a/src/python_minifier/module_printer.py +++ b/src/python_minifier/module_printer.py @@ -244,7 +244,7 @@ def visit_ImportFrom(self, node): assert isinstance(node, ast.ImportFrom) self.printer.keyword('from') - for i in range(node.level): + for _i in range(node.level): self.printer.delimiter('.') if node.module is not None: self.printer.identifier(node.module) diff --git a/src/python_minifier/rename/binding.py b/src/python_minifier/rename/binding.py index 7dde4095..5a4eb959 100644 --- a/src/python_minifier/rename/binding.py +++ b/src/python_minifier/rename/binding.py @@ -273,7 +273,7 @@ def should_rename(self, new_name): """ - raise NotImplementedError() + raise NotImplementedError def rename(self, new_name): """ @@ -283,7 +283,7 @@ def rename(self, new_name): """ - raise NotImplementedError() + raise NotImplementedError class NameBinding(Binding): diff --git a/src/python_minifier/rename/mapper.py b/src/python_minifier/rename/mapper.py index 2f921e91..215b60cb 100644 --- a/src/python_minifier/rename/mapper.py +++ b/src/python_minifier/rename/mapper.py @@ -161,12 +161,11 @@ def add_parent(node, parent=None, namespace=None): if is_ast_node(node, 'Nonlocal'): namespace.nonlocal_names.update(node.names) - if isinstance(node, ast.Name): - if isinstance(namespace, ast.ClassDef): - if isinstance(node.ctx, ast.Load): - namespace.nonlocal_names.add(node.id) - elif isinstance(node.ctx, ast.Store) and isinstance(node.parent, ast.AugAssign): - namespace.nonlocal_names.add(node.id) + if isinstance(node, ast.Name) and isinstance(namespace, ast.ClassDef): + if isinstance(node.ctx, ast.Load): + namespace.nonlocal_names.add(node.id) + elif isinstance(node.ctx, ast.Store) and isinstance(node.parent, ast.AugAssign): + namespace.nonlocal_names.add(node.id) for child in ast.iter_child_nodes(node): add_parent(child, parent=node, namespace=namespace) diff --git a/src/python_minifier/rename/rename_literals.py b/src/python_minifier/rename/rename_literals.py index c5167918..3c67a688 100644 --- a/src/python_minifier/rename/rename_literals.py +++ b/src/python_minifier/rename/rename_literals.py @@ -155,18 +155,18 @@ def namespace_path(self, node): """ - l = [] + path = [] while True: namespace = self.nearest_function_namespace(node) - l.insert(0, namespace) + path.insert(0, namespace) if isinstance(namespace, ast.Module): break node = namespace - return l + return path def common_path(self, n1_path, n2_path): @@ -217,8 +217,7 @@ def visit_JoinedStr(self, node): if is_ast_node(v, ast.Str): # Can't hoist this! continue - else: - self.visit(v) + self.visit(v) def visit_NameConstant(self, node): self.get_binding(node.value, node).add_reference(node) @@ -242,7 +241,7 @@ def visit_Assign(self, node): for target in node.targets: if is_ast_node(target, ast.Name) and target.id == '__slots__': # This is a __slots__ assignment, don't hoist the literals - return + return None return self.generic_visit(node) diff --git a/src/python_minifier/rename/renamer.py b/src/python_minifier/rename/renamer.py index 9e9e6b54..2d8aafd9 100644 --- a/src/python_minifier/rename/renamer.py +++ b/src/python_minifier/rename/renamer.py @@ -55,7 +55,7 @@ def reservation_scope(namespace, binding): """ - namespaces = set([namespace]) + namespaces = {namespace} for node in binding.references: while node is not namespace: @@ -112,7 +112,7 @@ def available_name(self): def __call__(self, module): assert isinstance(module, ast.Module) - for namespace, binding in sorted_bindings(module): + for _namespace, binding in sorted_bindings(module): if binding.allow_rename: binding.new_name = self.available_name() @@ -154,6 +154,8 @@ def available_name(self, reservation_scope, prefix=''): if self.is_available(prefix + name, reservation_scope): return prefix + name + return None + def is_available(self, name, reservation_scope): """ Is a name unreserved in a reservation scope @@ -165,11 +167,7 @@ def is_available(self, name, reservation_scope): """ - for namespace in reservation_scope: - if name in namespace.assigned_names: - return False - - return True + return all(name not in namespace.assigned_names for namespace in reservation_scope) def __call__(self, module, prefix_globals, reserved_globals=None): assert isinstance(module, ast.Module) @@ -184,6 +182,26 @@ def __call__(self, module, prefix_globals, reserved_globals=None): for name in reserved_globals: module.assigned_names.add(name) + def should_rename(binding, name, scope): + if binding.should_rename(name): + return True + + # It's no longer efficient to do this rename + + if isinstance(binding, NameBinding): + # Check that the original name is still available + + if binding.reserved == binding.name: + # We already reserved it (this is probably an arg) + return False + + if not self.is_available(binding.name, scope): + # The original name has already been assigned to another binding, + # so we need to rename this anyway. + return True + + return False + for namespace, binding in sorted_bindings(module): scope = reservation_scope(namespace, binding) @@ -194,27 +212,7 @@ def __call__(self, module, prefix_globals, reserved_globals=None): else: name = self.available_name(scope) - def should_rename(): - if binding.should_rename(name): - return True - - # It's no longer efficient to do this rename - - if isinstance(binding, NameBinding): - # Check that the original name is still available - - if binding.reserved == binding.name: - # We already reserved it (this is probably an arg) - return False - - if not self.is_available(binding.name, scope): - # The original name has already been assigned to another binding, - # so we need to rename this anyway. - return True - - return False - - if should_rename(): + if should_rename(binding, name, scope): binding.rename(name) else: # Any existing name will become reserved @@ -228,3 +226,5 @@ def should_rename(): def rename(module, prefix_globals=False, preserved_globals=None): NameAssigner()(module, prefix_globals, preserved_globals) + + diff --git a/src/python_minifier/transforms/constant_folding.py b/src/python_minifier/transforms/constant_folding.py index 7d1e1b21..f8d3c7cc 100644 --- a/src/python_minifier/transforms/constant_folding.py +++ b/src/python_minifier/transforms/constant_folding.py @@ -70,7 +70,7 @@ def visit_BinOp(self, node): try: folded_expression = unparse_expression(new_node) folded_value = safe_eval(folded_expression) - except Exception as e: + except Exception: # This can happen if the value is too large to be represented as a literal # or if the value is unparsed as nan, inf or -inf - which are not valid python literals return node @@ -107,11 +107,11 @@ def equal_value_and_type(a, b): def safe_eval(expression): - globals = {} - locals = {} + empty_globals = {} + empty_locals = {} # This will return the value, or could raise an exception - return eval(expression, globals, locals) + return eval(expression, empty_globals, empty_locals) def unparse_expression(node): diff --git a/src/python_minifier/transforms/remove_annotations.py b/src/python_minifier/transforms/remove_annotations.py index 02804a24..274d7ea8 100644 --- a/src/python_minifier/transforms/remove_annotations.py +++ b/src/python_minifier/transforms/remove_annotations.py @@ -78,14 +78,14 @@ def is_dataclass_field(node): if len(node.parent.decorator_list) == 0: return False - for node in node.parent.decorator_list: - if isinstance(node, ast.Name) and node.id == 'dataclass': + for decorator_node in node.parent.decorator_list: + if isinstance(decorator_node, ast.Name) and decorator_node.id == 'dataclass': return True - elif isinstance(node, ast.Attribute) and node.attr == 'dataclass': + elif isinstance(decorator_node, ast.Attribute) and decorator_node.attr == 'dataclass': return True - elif isinstance(node, ast.Call) and isinstance(node.func, ast.Name) and node.func.id == 'dataclass': + elif isinstance(decorator_node, ast.Call) and isinstance(decorator_node.func, ast.Name) and decorator_node.func.id == 'dataclass': return True - elif isinstance(node, ast.Call) and isinstance(node.func, ast.Attribute) and node.func.attr == 'dataclass': + elif isinstance(decorator_node, ast.Call) and isinstance(decorator_node.func, ast.Attribute) and decorator_node.func.attr == 'dataclass': return True return False @@ -102,10 +102,10 @@ def is_typing_sensitive(node): tricky_types = ['NamedTuple', 'TypedDict'] - for node in node.parent.bases: - if isinstance(node, ast.Name) and node.id in tricky_types: + for base_node in node.parent.bases: + if isinstance(base_node, ast.Name) and base_node.id in tricky_types: return True - elif isinstance(node, ast.Attribute) and node.attr in tricky_types: + elif isinstance(base_node, ast.Attribute) and base_node.attr in tricky_types: return True return False diff --git a/src/python_minifier/transforms/remove_annotations_options.pyi b/src/python_minifier/transforms/remove_annotations_options.pyi index aea089c5..3110542d 100644 --- a/src/python_minifier/transforms/remove_annotations_options.pyi +++ b/src/python_minifier/transforms/remove_annotations_options.pyi @@ -13,8 +13,6 @@ class RemoveAnnotationsOptions: ): ... - def __repr__(self) -> str: ... - def __nonzero__(self) -> bool: ... def __bool__(self) -> bool: ... diff --git a/src/python_minifier/transforms/remove_exception_brackets.py b/src/python_minifier/transforms/remove_exception_brackets.py index 1a7dc41e..fedc59b1 100644 --- a/src/python_minifier/transforms/remove_exception_brackets.py +++ b/src/python_minifier/transforms/remove_exception_brackets.py @@ -14,6 +14,7 @@ from python_minifier.rename.binding import BuiltinBinding + # These are always exceptions, in every version of python builtin_exceptions = [ 'SyntaxError', 'Exception', 'ValueError', 'BaseException', 'MemoryError', 'RuntimeError', 'DeprecationWarning', 'UnicodeEncodeError', 'KeyError', 'LookupError', 'TypeError', 'BufferError', @@ -80,7 +81,8 @@ def _remove_empty_call(binding): for name_node in binding.references: # For this to be a builtin, all references must be name nodes as it is not defined anywhere - assert isinstance(name_node, ast.Name) and isinstance(name_node.ctx, ast.Load) + assert isinstance(name_node, ast.Name) + assert isinstance(name_node.ctx, ast.Load) if not isinstance(name_node.parent, ast.Call): # This is not a call diff --git a/src/python_minifier/transforms/remove_literal_statements.py b/src/python_minifier/transforms/remove_literal_statements.py index 89d28189..9283421d 100644 --- a/src/python_minifier/transforms/remove_literal_statements.py +++ b/src/python_minifier/transforms/remove_literal_statements.py @@ -6,9 +6,8 @@ def find_doc(node): - if isinstance(node, ast.Attribute): - if node.attr == '__doc__': - raise ValueError('__doc__ found!') + if isinstance(node, ast.Attribute) and node.attr == '__doc__': + raise ValueError('__doc__ found!') for child in ast.iter_child_nodes(node): find_doc(child) @@ -18,7 +17,7 @@ def _doc_in_module(module): try: find_doc(module) return False - except: + except Exception: return True diff --git a/src/python_minifier/transforms/remove_posargs.py b/src/python_minifier/transforms/remove_posargs.py index a5e72d52..3c2f9e30 100644 --- a/src/python_minifier/transforms/remove_posargs.py +++ b/src/python_minifier/transforms/remove_posargs.py @@ -2,10 +2,9 @@ def remove_posargs(node): - if isinstance(node, ast.arguments): - if hasattr(node, 'posonlyargs'): - node.args = node.posonlyargs + node.args - node.posonlyargs = [] + if isinstance(node, ast.arguments) and hasattr(node, 'posonlyargs'): + node.args = node.posonlyargs + node.args + node.posonlyargs = [] for child in ast.iter_child_nodes(node): remove_posargs(child) diff --git a/src/python_minifier/transforms/suite_transformer.py b/src/python_minifier/transforms/suite_transformer.py index 0b0982a7..d1a8c9ad 100644 --- a/src/python_minifier/transforms/suite_transformer.py +++ b/src/python_minifier/transforms/suite_transformer.py @@ -13,7 +13,7 @@ def visit(self, node): def generic_visit(self, node): """Called if no explicit visitor function exists for a node.""" - for field, value in ast.iter_fields(node): + for _field, value in ast.iter_fields(node): if isinstance(value, list): for item in value: if isinstance(item, ast.AST): diff --git a/src/python_minifier/util.py b/src/python_minifier/util.py index d1ea6252..b8299d6d 100644 --- a/src/python_minifier/util.py +++ b/src/python_minifier/util.py @@ -23,9 +23,9 @@ def is_ast_node(node, types): actual_types = [] for node_type in types: if isinstance(node_type, str): - node_type = getattr(ast, node_type, None) - if node_type is not None: - actual_types.append(node_type) + actual_type = getattr(ast, node_type, None) + if actual_type is not None: + actual_types.append(actual_type) else: actual_types.append(node_type)