New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

python-minifier

Package Overview
Dependencies
Maintainers
1
Versions
28
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

python-minifier - pypi Package Compare versions

Comparing version
2.11.2
to
2.11.3
+82
src/python_minifier/ast_annotation/__init__.py
"""
This module provides utilities for annotating Abstract Syntax Tree (AST) nodes with parent references.
"""
import ast
class _NoParent(ast.AST):
"""A placeholder class used to indicate that a node has no parent."""
def __repr__(self):
# type: () -> str
return 'NoParent()'
def add_parent(node, parent=_NoParent()):
# type: (ast.AST, ast.AST) -> None
"""
Recursively adds a parent reference to each node in the AST.
>>> tree = ast.parse('a = 1')
>>> add_parent(tree)
>>> get_parent(tree.body[0]) == tree
True
:param node: The current AST node.
:param parent: The parent :class:`ast.AST` node.
"""
node._parent = parent # type: ignore[attr-defined]
for child in ast.iter_child_nodes(node):
add_parent(child, node)
def get_parent(node):
# type: (ast.AST) -> ast.AST
"""
Retrieves the parent of the given AST node.
>>> tree = ast.parse('a = 1')
>>> add_parent(tree)
>>> get_parent(tree.body[0]) == tree
True
:param node: The AST node whose parent is to be retrieved.
:return: The parent AST node.
:raises ValueError: If the node has no parent.
"""
if not hasattr(node, '_parent') or isinstance(node._parent, _NoParent): # type: ignore[attr-defined]
raise ValueError('Node has no parent')
return node._parent # type: ignore[attr-defined]
def set_parent(node, parent):
# type: (ast.AST, ast.AST) -> None
"""
Replace the parent of the given AST node.
Create a simple AST:
>>> tree = ast.parse('a = func()')
>>> add_parent(tree)
>>> isinstance(tree.body[0], ast.Assign) and isinstance(tree.body[0].value, ast.Call)
True
>>> assign = tree.body[0]
>>> call = tree.body[0].value
>>> get_parent(call) == assign
True
Replace the parent of the call node:
>>> tree.body[0] = call
>>> set_parent(call, tree)
>>> get_parent(call) == tree
True
>>> from python_minifier.ast_printer import print_ast
>>> print(print_ast(tree))
Module(body=[
Call(Name('func'))
])
:param node: The AST node whose parent is to be set.
:param parent: The parent AST node.
"""
node._parent = parent # type: ignore[attr-defined]
import sys
import pytest
from helpers import assert_namespace_tree
def test_namedexpr_in_module():
if sys.version_info < (3, 8):
pytest.skip('Test is for >= python3.8 only')
source = '''
(a := 1)
'''
expected_namespaces = '''
+ Module
- NameBinding(name='a', allow_rename=True) <references=1>
'''
assert_namespace_tree(source, expected_namespaces)
def test_namedexpr_in_function():
if sys.version_info < (3, 8):
pytest.skip('Test is for >= python3.8 only')
source = '''
def test():
(a := 1)
lambda x: (x := 1)
'''
expected_namespaces = '''
+ Module
- NameBinding(name='test', allow_rename=True) <references=1>
+ Function test
- NameBinding(name='a', allow_rename=True) <references=1>
+ Lambda
- NameBinding(name='x', allow_rename=False) <references=2>
'''
assert_namespace_tree(source, expected_namespaces)
def test_namedexpr_in_listcomp_if_nonlocal():
if sys.version_info < (3, 8):
pytest.skip('Test is for >= python3.8 only')
source = '''
def f(arg, /):
nonlocal x
print([x for y in range(10) if (x := y // 2) & 1])
print(arg, arg)
'''
expected_namespaces = '''
+ Module
- NameBinding(name='f', allow_rename=True) <references=1>
- BuiltinBinding(name='print', allow_rename=True) <references=2>
- BuiltinBinding(name='range', allow_rename=True) <references=1>
- NameBinding(name='x', allow_rename=False) <references=3>
+ Function f
- nonlocal x
- NameBinding(name='arg', allow_rename=True) <references=3>
+ ListComp
- NameBinding(name='y', allow_rename=True) <references=2>
'''
assert_namespace_tree(source, expected_namespaces)
def test_namedexpr_in_listcomp_if_global():
if sys.version_info < (3, 8):
pytest.skip('Test is for >= python3.8 only')
source = '''
def f2():
def f(arg, /):
global x
print([x for y in range(10) if (x := y // 2) & 1])
print(arg, arg)
'''
expected_namespaces = '''
+ Module
- NameBinding(name='f2', allow_rename=True) <references=1>
- BuiltinBinding(name='print', allow_rename=True) <references=2>
- BuiltinBinding(name='range', allow_rename=True) <references=1>
- NameBinding(name='x', allow_rename=True) <references=3>
+ Function f2
- NameBinding(name='f', allow_rename=True) <references=1>
+ Function f
- global x
- NameBinding(name='arg', allow_rename=True) <references=3>
+ ListComp
- NameBinding(name='y', allow_rename=True) <references=2>
'''
assert_namespace_tree(source, expected_namespaces)
def test_namedexpr_in_listcomp_if():
if sys.version_info < (3, 8):
pytest.skip('Test is for >= python3.8 only')
source = '''
def f(arg, /):
print([x for y in range(10) if (x := y // 2) & 1])
print(arg, arg)
'''
expected_namespaces = '''
+ Module
- NameBinding(name='f', allow_rename=True) <references=1>
- BuiltinBinding(name='print', allow_rename=True) <references=2>
- BuiltinBinding(name='range', allow_rename=True) <references=1>
+ Function f
- NameBinding(name='arg', allow_rename=True) <references=3>
- NameBinding(name='x', allow_rename=True) <references=2>
+ ListComp
- NameBinding(name='y', allow_rename=True) <references=2>
'''
assert_namespace_tree(source, expected_namespaces)
def test_namedexpr_in_listcomp_body():
if sys.version_info < (3, 8):
pytest.skip('Test is for >= python3.8 only')
source = '''
def f(arg, /):
print([(x := y // 2) for _ in range(x)])
print(arg, arg)
'''
expected_namespaces = '''
+ Module
- NameBinding(name='f', allow_rename=True) <references=1>
- BuiltinBinding(name='print', allow_rename=True) <references=2>
- BuiltinBinding(name='range', allow_rename=True) <references=1>
- NameBinding(name='y', allow_rename=False) <references=1>
+ Function f
- NameBinding(name='arg', allow_rename=True) <references=3>
- NameBinding(name='x', allow_rename=True) <references=2>
+ ListComp
- NameBinding(name='_', allow_rename=True) <references=1>
'''
assert_namespace_tree(source, expected_namespaces)
def test_namedexpr_in_dictcomp_body():
if sys.version_info < (3, 8):
pytest.skip('Test is for >= python3.8 only')
source = '''
{i: (x := i // 2) for i in range(1)}
'''
expected_namespaces = '''
+ Module
- BuiltinBinding(name='range', allow_rename=True) <references=1>
- NameBinding(name='x', allow_rename=True) <references=1>
+ DictComp
- NameBinding(name='i', allow_rename=True) <references=3>
'''
assert_namespace_tree(source, expected_namespaces)
def test_namedexpr_in_dictcomp_if():
if sys.version_info < (3, 8):
pytest.skip('Test is for >= python3.8 only')
source = '''
{x: y for y in range(1) if (x := y // 2)}
'''
expected_namespaces = '''
+ Module
- BuiltinBinding(name='range', allow_rename=True) <references=1>
- NameBinding(name='x', allow_rename=True) <references=2>
+ DictComp
- NameBinding(name='y', allow_rename=True) <references=3>
'''
assert_namespace_tree(source, expected_namespaces)
def test_namedexpr_in_setcomp_body():
if sys.version_info < (3, 8):
pytest.skip('Test is for >= python3.8 only')
source = '''
{(x := y // 2) for y in range(1)}
'''
expected_namespaces = '''
+ Module
- BuiltinBinding(name='range', allow_rename=True) <references=1>
- NameBinding(name='x', allow_rename=True) <references=1>
+ SetComp
- NameBinding(name='y', allow_rename=True) <references=2>
'''
assert_namespace_tree(source, expected_namespaces)
def test_namedexpr_in_setcomp_if():
if sys.version_info < (3, 8):
pytest.skip('Test is for >= python3.8 only')
source = '''
{x for y in range(1) if (x := y // 2)}
'''
expected_namespaces = '''
+ Module
- BuiltinBinding(name='range', allow_rename=True) <references=1>
- NameBinding(name='x', allow_rename=True) <references=2>
+ SetComp
- NameBinding(name='y', allow_rename=True) <references=2>
'''
assert_namespace_tree(source, expected_namespaces)
def test_namedexpr_in_generatorexp_body():
if sys.version_info < (3, 8):
pytest.skip('Test is for >= python3.8 only')
source = '''
((x := y // 2) for y in range(1))
'''
expected_namespaces = '''
+ Module
- BuiltinBinding(name='range', allow_rename=True) <references=1>
- NameBinding(name='x', allow_rename=True) <references=1>
+ GeneratorExp
- NameBinding(name='y', allow_rename=True) <references=2>
'''
assert_namespace_tree(source, expected_namespaces)
def test_namedexpr_in_generatorexp_if():
if sys.version_info < (3, 8):
pytest.skip('Test is for >= python3.8 only')
source = '''
(x for y in range(1) if (x := y // 2))
'''
expected_namespaces = '''
+ Module
- BuiltinBinding(name='range', allow_rename=True) <references=1>
- NameBinding(name='x', allow_rename=True) <references=2>
+ GeneratorExp
- NameBinding(name='y', allow_rename=True) <references=2>
'''
assert_namespace_tree(source, expected_namespaces)
import sys
import pytest
import ast
from python_minifier.util import is_constant_node
def test_type_nodes():
assert is_constant_node(ast.Str('a'), ast.Str)
if hasattr(ast, 'Bytes'):
assert is_constant_node(ast.Bytes(b'a'), ast.Bytes)
assert is_constant_node(ast.Num(1), ast.Num)
assert is_constant_node(ast.Num(0), ast.Num)
if hasattr(ast, 'NameConstant'):
assert is_constant_node(ast.NameConstant(True), ast.NameConstant)
assert is_constant_node(ast.NameConstant(False), ast.NameConstant)
assert is_constant_node(ast.NameConstant(None), ast.NameConstant)
else:
assert is_constant_node(ast.Name(id='True', ctx=ast.Load()), ast.Name)
assert is_constant_node(ast.Name(id='False', ctx=ast.Load()), ast.Name)
assert is_constant_node(ast.Name(id='None', ctx=ast.Load()), ast.Name)
assert is_constant_node(ast.Ellipsis(), ast.Ellipsis)
def test_constant_nodes():
# only test on python 3.8+
if sys.version_info < (3, 8):
pytest.skip('Constant not available')
assert is_constant_node(ast.Constant('a'), ast.Str)
assert is_constant_node(ast.Constant(b'a'), ast.Bytes)
assert is_constant_node(ast.Constant(1), ast.Num)
assert is_constant_node(ast.Constant(0), ast.Num)
assert is_constant_node(ast.Constant(True), ast.NameConstant)
assert is_constant_node(ast.Constant(False), ast.NameConstant)
assert is_constant_node(ast.Constant(None), ast.NameConstant)
assert is_constant_node(ast.Constant(ast.literal_eval('...')), ast.Ellipsis)
+1
-1
Metadata-Version: 2.1
Name: python_minifier
Version: 2.11.2
Version: 2.11.3
Summary: Transform Python source code into it's most compact representation

@@ -5,0 +5,0 @@ Home-page: https://github.com/dflook/python-minifier

@@ -31,3 +31,3 @@ import os.path

python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, <3.14',
version='2.11.2',
version='2.11.3',

@@ -34,0 +34,0 @@ classifiers=[

Metadata-Version: 2.1
Name: python_minifier
Version: 2.11.2
Version: 2.11.3
Summary: Transform Python source code into it's most compact representation

@@ -5,0 +5,0 @@ Home-page: https://github.com/dflook/python-minifier

@@ -23,2 +23,3 @@ LICENSE

src/python_minifier.egg-info/zip-safe
src/python_minifier/ast_annotation/__init__.py
src/python_minifier/rename/__init__.py

@@ -51,2 +52,3 @@ src/python_minifier/rename/bind_names.py

test/test_bind_names.py
test/test_bind_names_namedexpr.py
test/test_bind_names_python2.py

@@ -65,3 +67,3 @@ test/test_bind_names_python312.py

test/test_import.py
test/test_is_ast_node.py
test/test_is_constant_node.py
test/test_iterable_unpacking.py

@@ -68,0 +70,0 @@ test/test_match.py

@@ -7,17 +7,18 @@ """

import python_minifier.ast_compat as ast
import re
import python_minifier.ast_compat as ast
from python_minifier.ast_annotation import add_parent
from python_minifier.ast_compare import CompareError, compare_ast
from python_minifier.module_printer import ModulePrinter
from python_minifier.rename import (
rename_literals,
add_namespace,
allow_rename_globals,
allow_rename_locals,
bind_names,
resolve_names,
rename,
allow_rename_globals,
allow_rename_locals,
add_namespace,
rename_literals,
resolve_names
)
from python_minifier.transforms.combine_imports import CombineImports

@@ -29,4 +30,4 @@ from python_minifier.transforms.constant_folding import FoldConstants

from python_minifier.transforms.remove_debug import RemoveDebug
from python_minifier.transforms.remove_exception_brackets import remove_no_arg_exception_call
from python_minifier.transforms.remove_explicit_return_none import RemoveExplicitReturnNone
from python_minifier.transforms.remove_exception_brackets import remove_no_arg_exception_call
from python_minifier.transforms.remove_literal_statements import RemoveLiteralStatements

@@ -120,2 +121,3 @@ from python_minifier.transforms.remove_object_base import RemoveObject

add_parent(module)
add_namespace(module)

@@ -204,2 +206,3 @@

def _find_shebang(source):

@@ -221,2 +224,3 @@ """

def unparse(module):

@@ -223,0 +227,0 @@ """

import ast
from typing import List, Text, AnyStr, Optional, Any, Union
from typing import Any, AnyStr, List, Optional, Text, Union
from .transforms.remove_annotations_options import RemoveAnnotationsOptions as RemoveAnnotationsOptions
class UnstableMinification(RuntimeError):
def __init__(self, exception: Any, source: Any, minified: Any): ...
def minify(

@@ -31,4 +34,6 @@ source: AnyStr,

def unparse(module: ast.Module) -> Text: ...
def awslambda(

@@ -35,0 +40,0 @@ source: AnyStr,

@@ -10,4 +10,6 @@ from __future__ import print_function

if sys.version_info >= (3, 8):
from importlib import metadata
try:

@@ -18,3 +20,4 @@ version = metadata.version('python-minifier')

else:
from pkg_resources import get_distribution, DistributionNotFound
from pkg_resources import DistributionNotFound, get_distribution
try:

@@ -175,3 +178,3 @@ version = get_distribution('python_minifier').version

action='store_false',
help='Preserve any shebang line from the source',
help='Disable preserving any shebang line from the source',
dest='preserve_shebang',

@@ -182,3 +185,3 @@ )

action='store_true',
help='Remove assert statements',
help='Enable removing assert statements',
dest='remove_asserts',

@@ -189,3 +192,3 @@ )

action='store_true',
help='Remove conditional statements that test __debug__ is True',
help='Enable removing conditional statements that test __debug__ is True',
dest='remove_debug',

@@ -196,3 +199,3 @@ )

action='store_false',
help='Replace explicit return None with a bare return',
help='Disable replacing explicit return None with a bare return',
dest='remove_explicit_return_none',

@@ -277,5 +280,5 @@ )

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)

@@ -282,0 +285,0 @@ else:

import python_minifier.ast_compat as ast
from python_minifier.util import is_ast_node
class CompareError(RuntimeError):

@@ -21,3 +19,3 @@ """

if hasattr(node, 'namespace'):
if is_ast_node(node.namespace, (ast.FunctionDef, ast.ClassDef, 'AsyncFunctionDef')):
if isinstance(node.namespace, (ast.FunctionDef, ast.ClassDef, ast.AsyncFunctionDef)):
return self.namespace(node.namespace) + '.' + node.namespace.name

@@ -24,0 +22,0 @@ elif isinstance(node.namespace, ast.Module):

@@ -11,2 +11,3 @@ """

# Ideally we don't import anything else

@@ -20,2 +21,3 @@

# These classes are redefined from the ones in ast that complain about deprecation

@@ -28,2 +30,3 @@ # They will continue to work once they are removed from ast

class Bytes(Constant): # type: ignore[no-redef]

@@ -33,2 +36,3 @@ def __new__(cls, s, *args, **kwargs):

class Num(Constant): # type: ignore[no-redef]

@@ -38,2 +42,3 @@ def __new__(cls, n, *args, **kwargs):

class NameConstant(Constant): # type: ignore[no-redef]

@@ -43,4 +48,37 @@ def __new__(cls, *args, **kwargs):

class Ellipsis(Constant): # type: ignore[no-redef]
def __new__(cls, *args, **kwargs):
return Constant(value=literal_eval('...'), *args, **kwargs)
# Create a dummy class for missing AST nodes
for _node_type in [
'AnnAssign',
'AsyncFor',
'AsyncFunctionDef',
'AsyncFunctionDef',
'AsyncWith',
'Bytes',
'Constant',
'DictComp',
'Exec',
'ListComp',
'MatchAs',
'MatchMapping',
'MatchStar',
'NameConstant',
'NamedExpr',
'Nonlocal',
'ParamSpec',
'SetComp',
'Starred',
'TryStar',
'TypeVar',
'TypeVarTuple',
'YieldFrom',
'arg',
'withitem',
]:
if _node_type not in globals():
globals()[_node_type] = type(_node_type, (AST,), {})

@@ -15,4 +15,5 @@ """

from python_minifier.util import is_ast_node
from python_minifier.util import is_constant_node
INDENT = ' '

@@ -67,2 +68,3 @@

def is_literal(node, field):

@@ -72,12 +74,12 @@ if hasattr(ast, 'Constant') and isinstance(node, ast.Constant) and field == 'value':

if is_ast_node(node, ast.Num) and field == 'n':
if is_constant_node(node, ast.Num) and field == 'n':
return True
if is_ast_node(node, ast.Str) and field == 's':
if is_constant_node(node, ast.Str) and field == 's':
return True
if is_ast_node(node, 'Bytes') and field == 's':
if is_constant_node(node, ast.Bytes) and field == 's':
return True
if is_ast_node(node, 'NameConstant') and field == 'value':
if is_constant_node(node, ast.NameConstant) and field == 'value':
return True

@@ -87,2 +89,3 @@

def print_ast(node):

@@ -89,0 +92,0 @@ if not isinstance(node, ast.AST):

@@ -1,7 +0,7 @@

import python_minifier.ast_compat as ast
import sys
from python_minifier.util import is_ast_node
import python_minifier.ast_compat as ast
from python_minifier.token_printer import TokenPrinter, Delimiter
from python_minifier.token_printer import Delimiter, TokenPrinter
from python_minifier.util import is_constant_node

@@ -72,3 +72,3 @@

# Make sure the Num get the precedence of the USub operator in this case.
if sys.version_info < (3, 0) and is_ast_node(node, ast.Num):
if sys.version_info < (3, 0) and is_constant_node(node, ast.Num):
if str(node.n)[0] == '-':

@@ -160,3 +160,3 @@ return self.precedences['USub']

if 0 < self.precedence(datum) <=7:
if 0 < self.precedence(datum) <= 7:
self.printer.delimiter('(')

@@ -213,3 +213,3 @@ self._expression(datum)

if sys.version_info < (3, 0) and isinstance(node.op, ast.USub) and is_ast_node(node.operand, ast.Num):
if sys.version_info < (3, 0) and isinstance(node.op, ast.USub) and is_constant_node(node.operand, ast.Num):
# For: -(1), which is parsed as a UnaryOp(USub, Num(1)).

@@ -434,3 +434,3 @@ # Without this special case it would be printed as -1

if (value_precedence != 0 and (attr_precedence > value_precedence)) or is_ast_node(node.value, ast.Num):
if (value_precedence != 0 and (attr_precedence > value_precedence)) or is_constant_node(node.value, ast.Num):
self.printer.delimiter('(')

@@ -469,3 +469,3 @@ self._expression(node.value)

self.visit_ExtSlice(node.slice)
elif is_ast_node(node.slice, ast.Ellipsis):
elif is_constant_node(node.slice, ast.Ellipsis):
self.visit_Ellipsis(node)

@@ -637,3 +637,4 @@ elif sys.version_info >= (3, 9) and isinstance(node.slice, ast.Tuple):

# Python 2 uses Name nodes
return self.visit_Name(node)
self.visit_Name(node)
return

@@ -657,3 +658,3 @@ self.printer.identifier(node.arg)

def _expression(self, expression):
if is_ast_node(expression, (ast.Yield, 'YieldFrom')):
if isinstance(expression, (ast.Yield, ast.YieldFrom)):
self.printer.delimiter('(')

@@ -666,3 +667,3 @@ self._yield_expr(expression)

self.printer.delimiter(')')
elif is_ast_node(expression, 'NamedExpr'):
elif isinstance(expression, ast.NamedExpr):
self.printer.delimiter('(')

@@ -675,7 +676,7 @@ self.visit_NamedExpr(expression)

def _testlist(self, test):
if is_ast_node(test, (ast.Yield, 'YieldFrom')):
if isinstance(test, (ast.Yield, ast.YieldFrom)):
self.printer.delimiter('(')
self._yield_expr(test)
self.printer.delimiter(')')
elif is_ast_node(test, 'NamedExpr'):
elif isinstance(test, ast.NamedExpr):
self.printer.delimiter('(')

@@ -682,0 +683,0 @@ self.visit_NamedExpr(test)

@@ -9,13 +9,13 @@ """

import python_minifier.ast_compat as ast
import copy
import re
import python_minifier.ast_compat as ast
from python_minifier import UnstableMinification
from python_minifier.ast_compare import CompareError
from python_minifier.ast_compare import compare_ast
from python_minifier.ast_compare import CompareError, compare_ast
from python_minifier.expression_printer import ExpressionPrinter
from python_minifier.ministring import MiniString
from python_minifier.token_printer import TokenTypes
from python_minifier.util import is_ast_node
from python_minifier.util import is_constant_node

@@ -40,3 +40,3 @@

return True
except Exception as e:
except Exception:
return False

@@ -75,3 +75,3 @@

for v in self.node.values:
if is_ast_node(v, ast.Str):
if is_constant_node(v, ast.Str):

@@ -85,3 +85,3 @@ # Could this be used as a debug specifier?

debug_specifier_candidates = [x + '{' + v.s for x in candidates]
except Exception as e:
except Exception:
continue

@@ -91,3 +91,3 @@

candidates = [x + self.str_for(v.s, quote) for x in candidates]
except Exception as e:
except Exception:
continue

@@ -101,3 +101,3 @@ elif isinstance(v, ast.FormattedValue):

debug_specifier_candidates = []
except Exception as e:
except Exception:
continue

@@ -109,4 +109,3 @@ else:

actual_candidates = filter(self.is_correct_ast, actual_candidates)
return actual_candidates
return filter(self.is_correct_ast, actual_candidates)

@@ -280,3 +279,3 @@ def str_for(self, s, quote):

raise ValueError('Couldn\'t find a quote')
raise ValueError("Couldn't find a quote")

@@ -360,3 +359,3 @@ def _literals(self):

for v in self.node.values:
if is_ast_node(v, ast.Str):
if is_constant_node(v, ast.Str):
candidates = [x + self.str_for(v.s) for x in candidates]

@@ -409,3 +408,3 @@ elif isinstance(v, ast.FormattedValue):

raise ValueError('Couldn\'t find a quote')
raise ValueError("Couldn't find a quote")

@@ -412,0 +411,0 @@ def _literals(self):

@@ -35,3 +35,3 @@ BACKSLASH = '\\'

eval(self.quote + s + self.quote)
except (UnicodeDecodeError, UnicodeEncodeError) as e:
except (UnicodeDecodeError, UnicodeEncodeError):
if self.safe_mode:

@@ -67,3 +67,3 @@ raise

for c in self._s:
if c in escaped.keys():
if c in escaped:
s += escaped[c]

@@ -100,3 +100,3 @@ else:

for c in self._s:
if c in escaped.keys():
if c in escaped:
s += escaped[c]

@@ -103,0 +103,0 @@ else:

@@ -1,7 +0,7 @@

import python_minifier.ast_compat as ast
import sys
import python_minifier.ast_compat as ast
from .expression_printer import ExpressionPrinter
from .token_printer import Delimiter
from .util import is_ast_node

@@ -58,3 +58,3 @@

if is_ast_node(node.value, (ast.Yield, 'YieldFrom')):
if isinstance(node.value, (ast.Yield, ast.YieldFrom)):
self._yield_expr(node.value)

@@ -86,3 +86,3 @@ else:

# Yield nodes that are the sole node on the right hand side of an assignment do not need parens
if is_ast_node(node.value, (ast.Yield, 'YieldFrom')):
if isinstance(node.value, (ast.Yield, ast.YieldFrom)):
self._yield_expr(node.value)

@@ -102,3 +102,3 @@ else:

# Yield nodes that are the sole node on the right hand side of an assignment do not need parens
if is_ast_node(node.value, (ast.Yield, 'YieldFrom')):
if isinstance(node.value, (ast.Yield, ast.YieldFrom)):
self._yield_expr(node.value)

@@ -149,3 +149,3 @@ else:

if isinstance(node.value, ast.Tuple):
if sys.version_info < (3, 8) and [n for n in node.value.elts if is_ast_node(n, 'Starred')]:
if sys.version_info < (3, 8) and [n for n in node.value.elts if isinstance(n, ast.Starred)]:
self.printer.delimiter('(')

@@ -250,3 +250,3 @@ self._testlist(node.value)

self.printer.keyword('from')
for i in range(node.level):
for _i in range(node.level):
self.printer.delimiter('.')

@@ -334,3 +334,3 @@ if node.module is not None:

def visit_For(self, node, is_async=False):
assert isinstance(node, ast.For) or (hasattr(ast, 'AsyncFor') and isinstance(node, ast.AsyncFor))
assert isinstance(node, (ast.For, ast.AsyncFor))

@@ -371,3 +371,3 @@ self.printer.newline()

def visit_Try(self, node, star=False):
assert is_ast_node(node, (ast.Try, 'TryStar'))
assert isinstance(node, (ast.Try, ast.TryStar))

@@ -450,3 +450,3 @@ self.printer.newline()

def visit_With(self, node, is_async=False):
assert is_ast_node(node, (ast.With, 'AsyncWith'))
assert isinstance(node, (ast.With, ast.AsyncWith))

@@ -480,3 +480,3 @@ self.printer.newline()

def visit_withitem(self, node):
assert is_ast_node(node, ('withitem', ast.With))
assert isinstance(node, (ast.withitem, ast.With))

@@ -490,3 +490,3 @@ self._expression(node.context_expr)

def visit_FunctionDef(self, node, is_async=False):
assert is_ast_node(node, (ast.FunctionDef, 'AsyncFunctionDef'))
assert isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))

@@ -683,3 +683,3 @@ self.printer.newline()

self.printer.identifier(kwd)
self.printer.identifier(kwd)
self.printer.delimiter('=')

@@ -873,2 +873,1 @@

statements[node.__class__.__name__](node)

@@ -6,2 +6,2 @@ from python_minifier.rename.bind_names import bind_names

from python_minifier.rename.resolve_names import resolve_names
from python_minifier.rename.util import allow_rename_locals, allow_rename_globals
from python_minifier.rename.util import allow_rename_globals, allow_rename_locals
import python_minifier.ast_compat as ast
from python_minifier.rename.binding import NameBinding
from python_minifier.rename.util import arg_rename_in_place, get_global_namespace, get_nonlocal_namespace, builtins
from python_minifier.rename.util import arg_rename_in_place, builtins, get_global_namespace
from python_minifier.transforms.suite_transformer import NodeVisitor

@@ -183,2 +183,3 @@

def bind_names(module):

@@ -185,0 +186,0 @@ """

import python_minifier.ast_compat as ast
from python_minifier.rename.util import arg_rename_in_place, insert
from python_minifier.util import is_ast_node

@@ -108,7 +107,7 @@

arg_rename = True
elif is_ast_node(node, (ast.ClassDef, ast.FunctionDef, 'AsyncFunctionDef')):
elif isinstance(node, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)):
pass
elif isinstance(node, ast.ExceptHandler):
pass
elif is_ast_node(node, (ast.Global, 'Nonlocal')):
elif isinstance(node, (ast.Global, ast.Nonlocal)):
pass

@@ -123,18 +122,18 @@ elif isinstance(node, ast.alias):

pass
elif is_ast_node(node, 'arg'):
elif isinstance(node, ast.arg):
if not arg_rename_in_place(node):
arg_rename = True
elif is_ast_node(node, 'MatchAs'):
elif isinstance(node, ast.MatchAs):
if node.name is None:
additional_bytes += 4 # ' as '
elif is_ast_node(node, 'MatchStar'):
elif isinstance(node, ast.MatchStar):
pass
elif is_ast_node(node, 'MatchMapping'):
elif isinstance(node, ast.MatchMapping):
pass
elif is_ast_node(node, 'TypeVar'):
elif isinstance(node, ast.TypeVar):
pass
elif is_ast_node(node, 'TypeVarTuple'):
elif isinstance(node, ast.TypeVarTuple):
pass
elif is_ast_node(node, 'ParamSpec'):
elif isinstance(node, ast.ParamSpec):
pass

@@ -165,7 +164,7 @@

elif is_ast_node(node, (ast.ClassDef, ast.FunctionDef, 'AsyncFunctionDef')):
elif isinstance(node, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)):
pass
elif isinstance(node, ast.ExceptHandler):
pass
elif is_ast_node(node, (ast.Global, 'Nonlocal')):
elif isinstance(node, (ast.Global, ast.Nonlocal)):
pass

@@ -178,3 +177,3 @@ elif isinstance(node, ast.alias):

pass
elif is_ast_node(node, 'arg'):
elif isinstance(node, ast.arg):
if not arg_rename_in_place(node):

@@ -184,13 +183,13 @@ mentions += 1

elif is_ast_node(node, 'MatchAs'):
elif isinstance(node, ast.MatchAs):
pass
elif is_ast_node(node, 'MatchStar'):
elif isinstance(node, ast.MatchStar):
pass
elif is_ast_node(node, 'MatchMapping'):
elif isinstance(node, ast.MatchMapping):
pass
elif is_ast_node(node, 'TypeVar'):
elif isinstance(node, ast.TypeVar):
pass
elif is_ast_node(node, 'TypeVarTuple'):
elif isinstance(node, ast.TypeVarTuple):
pass
elif is_ast_node(node, 'ParamSpec'):
elif isinstance(node, ast.ParamSpec):
pass

@@ -218,7 +217,7 @@

arg_rename = True
elif is_ast_node(node, (ast.ClassDef, ast.FunctionDef, 'AsyncFunctionDef')):
elif isinstance(node, (ast.ClassDef, ast.FunctionDef, ast.AsyncFunctionDef)):
mentions += 1
elif isinstance(node, ast.ExceptHandler):
mentions += 1
elif is_ast_node(node, (ast.Global, 'Nonlocal')):
elif isinstance(node, (ast.Global, ast.Nonlocal)):
mentions += len([n for n in node.names if n == self._name])

@@ -232,16 +231,16 @@ elif isinstance(node, ast.alias):

mentions += 1
elif is_ast_node(node, 'arg'):
elif isinstance(node, ast.arg):
arg_rename = True
elif is_ast_node(node, 'MatchAs'):
elif isinstance(node, ast.MatchAs):
mentions += 1
elif is_ast_node(node, 'MatchStar'):
elif isinstance(node, ast.MatchStar):
mentions += 1
elif is_ast_node(node, 'MatchMapping'):
elif isinstance(node, ast.MatchMapping):
mentions += 1
elif is_ast_node(node, 'TypeVar'):
elif isinstance(node, ast.TypeVar):
mentions += 1
elif is_ast_node(node, 'TypeVarTuple'):
elif isinstance(node, ast.TypeVarTuple):
mentions += 1
elif is_ast_node(node, 'ParamSpec'):
elif isinstance(node, ast.ParamSpec):
mentions += 1

@@ -283,3 +282,3 @@

raise NotImplementedError()
raise NotImplementedError

@@ -294,3 +293,3 @@ def rename(self, new_name):

raise NotImplementedError()
raise NotImplementedError

@@ -374,3 +373,3 @@

elif is_ast_node(node, (ast.FunctionDef, 'AsyncFunctionDef')):
elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
node.name = new_name

@@ -384,3 +383,3 @@ elif isinstance(node, ast.ClassDef):

node.asname = new_name
elif is_ast_node(node, 'arg'):
elif isinstance(node, ast.arg):

@@ -398,3 +397,3 @@ if arg_rename_in_place(node):

node.name = new_name
elif is_ast_node(node, (ast.Global, 'Nonlocal')):
elif isinstance(node, (ast.Global, ast.Nonlocal)):
node.names = [new_name if n == self._name else n for n in node.names]

@@ -413,13 +412,13 @@ elif isinstance(node, ast.arguments):

elif is_ast_node(node, 'MatchAs'):
elif isinstance(node, ast.MatchAs):
node.name = new_name
elif is_ast_node(node, 'MatchStar'):
elif isinstance(node, ast.MatchStar):
node.name = new_name
elif is_ast_node(node, 'MatchMapping'):
elif isinstance(node, ast.MatchMapping):
node.rest = new_name
elif is_ast_node(node, 'TypeVar'):
elif isinstance(node, ast.TypeVar):
node.name = new_name
elif is_ast_node(node, 'TypeVarTuple'):
elif isinstance(node, ast.TypeVarTuple):
node.name = new_name
elif is_ast_node(node, 'ParamSpec'):
elif isinstance(node, ast.ParamSpec):
node.name = new_name

@@ -508,2 +507,2 @@

return False
return False

@@ -6,44 +6,43 @@ """

import python_minifier.ast_compat as ast
from python_minifier.ast_annotation import get_parent
from python_minifier.rename.util import is_namespace
from python_minifier.util import is_ast_node
def add_parent_to_arguments(arguments, func):
arguments.parent = func
arguments.namespace = func
for arg in getattr(arguments, 'posonlyargs', []) + arguments.args:
add_parent(arg, arguments, func)
add_parent(arg, func)
if hasattr(arg, 'annotation') and arg.annotation is not None:
add_parent(arg.annotation, arguments, func.namespace)
add_parent(arg.annotation, func.namespace)
if hasattr(arguments, 'kwonlyargs'):
for arg in arguments.kwonlyargs:
add_parent(arg, arguments, func)
add_parent(arg, func)
if arg.annotation is not None:
add_parent(arg.annotation, arguments, func.namespace)
add_parent(arg.annotation, func.namespace)
for node in arguments.kw_defaults:
if node is not None:
add_parent(node, arguments, func.namespace)
add_parent(node, func.namespace)
for node in arguments.defaults:
add_parent(node, arguments, func.namespace)
add_parent(node, func.namespace)
if arguments.vararg:
if hasattr(arguments, 'varargannotation') and arguments.varargannotation is not None:
add_parent(arguments.varargannotation, arguments, func.namespace)
add_parent(arguments.varargannotation, func.namespace)
elif isinstance(arguments.vararg, str):
pass
else:
add_parent(arguments.vararg, arguments, func)
add_parent(arguments.vararg, func)
if arguments.kwarg:
if hasattr(arguments, 'kwargannotation') and arguments.kwargannotation is not None:
add_parent(arguments.kwargannotation, arguments, func.namespace)
add_parent(arguments.kwargannotation, func.namespace)
elif isinstance(arguments.kwarg, str):
pass
else:
add_parent(arguments.kwarg, arguments, func)
add_parent(arguments.kwarg, func)

@@ -60,13 +59,13 @@

for node in functiondef.body:
add_parent(node, parent=functiondef, namespace=functiondef)
add_parent(node, namespace=functiondef)
for node in functiondef.decorator_list:
add_parent(node, parent=functiondef, namespace=functiondef.namespace)
add_parent(node, namespace=functiondef.namespace)
if hasattr(functiondef, 'type_params') and functiondef.type_params is not None:
for node in functiondef.type_params:
add_parent(node, parent=functiondef, namespace=functiondef.namespace)
add_parent(node, namespace=functiondef.namespace)
if hasattr(functiondef, 'returns') and functiondef.returns is not None:
add_parent(functiondef.returns, parent=functiondef, namespace=functiondef.namespace)
add_parent(functiondef.returns, namespace=functiondef.namespace)

@@ -80,54 +79,68 @@

for node in classdef.bases:
add_parent(node, parent=classdef, namespace=classdef.namespace)
add_parent(node, namespace=classdef.namespace)
if hasattr(classdef, 'keywords'):
for node in classdef.keywords:
add_parent(node, parent=classdef, namespace=classdef.namespace)
add_parent(node, namespace=classdef.namespace)
if hasattr(classdef, 'starargs') and classdef.starargs is not None:
add_parent(classdef.starargs, parent=classdef, namespace=classdef.namespace)
add_parent(classdef.starargs, namespace=classdef.namespace)
if hasattr(classdef, 'kwargs') and classdef.kwargs is not None:
add_parent(classdef.kwargs, parent=classdef, namespace=classdef.namespace)
add_parent(classdef.kwargs, namespace=classdef.namespace)
for node in classdef.body:
add_parent(node, parent=classdef, namespace=classdef)
add_parent(node, namespace=classdef)
for node in classdef.decorator_list:
add_parent(node, parent=classdef, namespace=classdef.namespace)
add_parent(node, namespace=classdef.namespace)
if hasattr(classdef, 'type_params') and classdef.type_params is not None:
for node in classdef.type_params:
add_parent(node, parent=classdef, namespace=classdef.namespace)
add_parent(node, namespace=classdef.namespace)
def add_parent_to_comprehension(node, namespace):
assert is_ast_node(node, (ast.GeneratorExp, 'SetComp', 'DictComp', 'ListComp'))
assert isinstance(node, (ast.GeneratorExp, ast.SetComp, ast.DictComp, ast.ListComp))
if hasattr(node, 'elt'):
add_parent(node.elt, parent=node, namespace=node)
add_parent(node.elt, namespace=node)
elif hasattr(node, 'key'):
add_parent(node.key, parent=node, namespace=node)
add_parent(node.value, parent=node, namespace=node)
add_parent(node.key, namespace=node)
add_parent(node.value, namespace=node)
iter_namespace = namespace
for generator in node.generators:
generator.parent = node
generator.namespace = node
add_parent(generator.target, parent=generator, namespace=node)
add_parent(generator.iter, parent=generator, namespace=iter_namespace)
iter_namespace = node
add_parent(generator.target, namespace=node)
add_parent(generator.iter, namespace=iter_namespace)
for if_ in generator.ifs:
add_parent(if_, parent=generator, namespace=node)
add_parent(if_, namespace=node)
iter_namespace = node
def add_parent(node, parent=None, namespace=None):
def namedexpr_namespace(node):
"""
Add a parent attribute to child nodes
Get the namespace for a NamedExpr target
"""
if not isinstance(node, (ast.ListComp, ast.DictComp, ast.SetComp, ast.GeneratorExp)):
return node
return namedexpr_namespace(node.namespace)
def add_parent_to_namedexpr(node):
assert isinstance(node, ast.NamedExpr)
add_parent(node.target, namespace=namedexpr_namespace(node.namespace))
add_parent(node.value, namespace=node.namespace)
def add_parent(node, namespace=None):
"""
Add a namespace attribute to child nodes
:param node: The tree to add parent and namespace properties to
:param node: The tree to add namespace properties to
:type node: :class:`ast.AST`
:param parent: The parent node of this node
:type parent: :class:`ast.AST`
:param namespace: The namespace Node that this node is in

@@ -138,3 +151,2 @@ :type namespace: ast.Lambda or ast.Module or ast.FunctionDef or ast.AsyncFunctionDef or ast.ClassDef or ast.DictComp or ast.SetComp or ast.ListComp or ast.Generator

node.parent = parent if parent is not None else node
node.namespace = namespace if namespace is not None else node

@@ -147,9 +159,9 @@

if is_ast_node(node, (ast.FunctionDef, 'AsyncFunctionDef')):
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
add_parent_to_functiondef(node)
elif is_ast_node(node, (ast.GeneratorExp, 'SetComp', 'DictComp', 'ListComp')):
elif isinstance(node, (ast.GeneratorExp, ast.SetComp, ast.DictComp, ast.ListComp)):
add_parent_to_comprehension(node, namespace=namespace)
elif isinstance(node, ast.Lambda):
add_parent_to_arguments(node.args, func=node)
add_parent(node.body, parent=node, namespace=node)
add_parent(node.body, namespace=node)
elif isinstance(node, ast.ClassDef):

@@ -159,3 +171,3 @@ add_parent_to_classdef(node)

for child in ast.iter_child_nodes(node):
add_parent(child, parent=node, namespace=node)
add_parent(child, namespace=node)

@@ -166,14 +178,18 @@ return

namespace.global_names.update(node.names)
if is_ast_node(node, 'Nonlocal'):
if isinstance(node, ast.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(get_parent(node), ast.AugAssign):
namespace.nonlocal_names.add(node.id)
if isinstance(node, ast.NamedExpr):
# NamedExpr is 'special'
add_parent_to_namedexpr(node)
return
for child in ast.iter_child_nodes(node):
add_parent(child, parent=node, namespace=namespace)
add_parent(child, namespace=namespace)

@@ -180,0 +196,0 @@

import python_minifier.ast_compat as ast
from python_minifier.ast_annotation import get_parent, set_parent

@@ -6,8 +7,8 @@ from python_minifier.rename.binding import Binding

from python_minifier.transforms.suite_transformer import NodeVisitor
from python_minifier.util import is_ast_node
from python_minifier.util import is_constant_node
def replace(old_node, new_node):
parent = old_node.parent
new_node.parent = parent
parent = get_parent(old_node)
set_parent(new_node, parent)
new_node.namespace = old_node.namespace

@@ -47,3 +48,3 @@

def value(self):
if is_ast_node(self._value_node, (ast.Str, 'Bytes')):
if is_constant_node(self._value_node, (ast.Str, ast.Bytes)):
return self._value_node.s

@@ -89,2 +90,3 @@ else:

class HoistedValue(object):

@@ -138,3 +140,3 @@ """

if is_ast_node(node.namespace, (ast.FunctionDef, ast.Module, 'AsyncFunctionDef')):
if isinstance(node.namespace, (ast.FunctionDef, ast.Module, ast.AsyncFunctionDef)):
return node.namespace

@@ -161,7 +163,7 @@ return self.nearest_function_namespace(node.namespace)

l = []
path = []
while True:
namespace = self.nearest_function_namespace(node)
l.insert(0, namespace)
path.insert(0, namespace)

@@ -173,3 +175,3 @@ if isinstance(namespace, ast.Module):

return l
return path

@@ -210,3 +212,3 @@ def common_path(self, n1_path, n2_path):

if isinstance(node.parent, ast.Expr):
if isinstance(get_parent(node), ast.Expr):
# This is literal statement

@@ -223,7 +225,6 @@ # The RemoveLiteralStatements transformer must have left it here, so ignore it.

for v in node.values:
if is_ast_node(v, ast.Str):
if is_constant_node(v, ast.Str):
# Can't hoist this!
continue
else:
self.visit(v)
self.visit(v)

@@ -246,13 +247,14 @@ def visit_NameConstant(self, node):

if not is_ast_node(node.namespace, ast.ClassDef):
if not isinstance(node.namespace, ast.ClassDef):
return self.generic_visit(node)
for target in node.targets:
if is_ast_node(target, ast.Name) and target.id == '__slots__':
if isinstance(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)
def rename_literals(module):
HoistLiterals()(module)

@@ -58,3 +58,3 @@ import python_minifier.ast_compat as ast

namespaces = set([namespace])
namespaces = {namespace}

@@ -116,3 +116,3 @@ for node in binding.references:

for namespace, binding in sorted_bindings(module):
for _namespace, binding in sorted_bindings(module):
if binding.allow_rename:

@@ -159,2 +159,4 @@ binding.new_name = self.available_name()

return None
def is_available(self, name, reservation_scope):

@@ -171,8 +173,4 @@ """

for namespace in reservation_scope:
if name in namespace.assigned_names:
return False
return all(name not in namespace.assigned_names for namespace in reservation_scope)
return True
def __call__(self, module, prefix_globals, reserved_globals=None):

@@ -191,2 +189,22 @@ assert isinstance(module, ast.Module)

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):

@@ -202,23 +220,3 @@ scope = reservation_scope(namespace, binding)

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)

@@ -225,0 +223,0 @@ else:

import python_minifier.ast_compat as ast
from python_minifier.rename.binding import BuiltinBinding, NameBinding
from python_minifier.rename.util import get_global_namespace, get_nonlocal_namespace, builtins
from python_minifier.util import is_ast_node
from python_minifier.rename.util import builtins, get_global_namespace, get_nonlocal_namespace

@@ -37,2 +36,3 @@

def get_binding_disallow_class_namespace_rename(name, namespace):

@@ -47,2 +47,3 @@ binding = get_binding(name, namespace)

def resolve_names(node):

@@ -70,3 +71,3 @@ """

elif is_ast_node(node, (ast.FunctionDef, 'AsyncFunctionDef')) and node.name in node.namespace.nonlocal_names:
elif isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) and node.name in node.namespace.nonlocal_names:
binding = get_binding_disallow_class_namespace_rename(node.name, node.namespace)

@@ -97,11 +98,11 @@ binding.add_reference(node)

elif is_ast_node(node, 'Nonlocal'):
elif isinstance(node, ast.Nonlocal):
for name in node.names:
get_binding_disallow_class_namespace_rename(name, node.namespace).add_reference(node)
elif is_ast_node(node, ('MatchAs', 'MatchStar')) and node.name in node.namespace.nonlocal_names:
elif isinstance(node, (ast.MatchAs, ast.MatchStar)) and node.name in node.namespace.nonlocal_names:
get_binding_disallow_class_namespace_rename(node.name, node.namespace).add_reference(node)
elif is_ast_node(node, 'MatchMapping') and node.rest in node.namespace.nonlocal_names:
elif isinstance(node, ast.MatchMapping) and node.rest in node.namespace.nonlocal_names:
get_binding_disallow_class_namespace_rename(node.rest, node.namespace).add_reference(node)
elif is_ast_node(node, 'Exec'):
elif isinstance(node, ast.Exec):
get_global_namespace(node).tainted = True

@@ -108,0 +109,0 @@

@@ -1,7 +0,8 @@

import python_minifier.ast_compat as ast
import sys
from python_minifier.util import is_ast_node
import python_minifier.ast_compat as ast
from python_minifier.util import is_constant_node
def create_is_namespace():

@@ -35,2 +36,3 @@

def get_global_namespace(node):

@@ -129,3 +131,3 @@ """

if (isinstance(node, ast.ImportFrom) and node.module == '__future__') or (
isinstance(node, ast.Expr) and is_ast_node(node.value, ast.Str)
isinstance(node, ast.Expr) and is_constant_node(node.value, ast.Str)
):

@@ -169,3 +171,3 @@ pass

elif is_ast_node(node, (ast.AugAssign, 'AnnAssign')):
elif isinstance(node, (ast.AugAssign, ast.AnnAssign)):
if isinstance(node.target, ast.Name) and node.target.id == '__all__':

@@ -184,3 +186,3 @@ return True

for el in node.value.elts:
if is_ast_node(el, ast.Str):
if is_constant_node(el, ast.Str):
names.append(el.s)

@@ -187,0 +189,0 @@

@@ -6,2 +6,3 @@ """Tools for assembling python code from tokens."""

class TokenTypes(object):

@@ -19,2 +20,3 @@ NoToken = 0

class Delimiter(object):

@@ -79,2 +81,3 @@ def __init__(self, terminal_printer, delimiter=',', add_parens=False):

class TokenPrinter(object):

@@ -81,0 +84,0 @@ """

@@ -33,3 +33,2 @@ import python_minifier.ast_compat as ast

def _combine_import_from(self, node_list, parent):

@@ -36,0 +35,0 @@

@@ -1,10 +0,13 @@

import python_minifier.ast_compat as ast
import math
import sys
import python_minifier.ast_compat as ast
from python_minifier.ast_annotation import get_parent
from python_minifier.ast_compare import compare_ast
from python_minifier.expression_printer import ExpressionPrinter
from python_minifier.transforms.suite_transformer import SuiteTransformer
from python_minifier.util import is_ast_node
from python_minifier.util import is_constant_node
class FoldConstants(SuiteTransformer):

@@ -25,5 +28,5 @@ """

# We don't try to fold strings or bytes, since they have probably been arranged this way to make the source shorter and we are unlikely to beat that
if not is_ast_node(node.left, (ast.Num, 'NameConstant')):
if not is_constant_node(node.left, (ast.Num, ast.NameConstant)):
return node
if not is_ast_node(node.right, (ast.Num, 'NameConstant')):
if not is_constant_node(node.right, (ast.Num, ast.NameConstant)):
return node

@@ -72,3 +75,3 @@

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

@@ -96,4 +99,5 @@ # or if the value is unparsed as nan, inf or -inf - which are not valid python literals

# New representation is shorter and has the same value, so use it
return self.add_child(new_node, node.parent, node.namespace)
return self.add_child(new_node, get_parent(node), node.namespace)
def equal_value_and_type(a, b):

@@ -108,11 +112,13 @@ if type(a) != type(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):
expression_printer = ExpressionPrinter()
return expression_printer(node)
return expression_printer(node)

@@ -29,3 +29,5 @@ class RemoveAnnotationsOptions(object):

def __repr__(self):
return 'RemoveAnnotationsOptions(remove_variable_annotations=%r, remove_return_annotations=%r, remove_argument_annotations=%r, remove_class_attribute_annotations=%r)' % (self.remove_variable_annotations, self.remove_return_annotations, self.remove_argument_annotations, self.remove_class_attribute_annotations)
return 'RemoveAnnotationsOptions(remove_variable_annotations=%r, remove_return_annotations=%r, remove_argument_annotations=%r, remove_class_attribute_annotations=%r)' % (
self.remove_variable_annotations, self.remove_return_annotations, self.remove_argument_annotations, self.remove_class_attribute_annotations
)

@@ -32,0 +34,0 @@ def __nonzero__(self):

class RemoveAnnotationsOptions:
remove_variable_annotations: bool

@@ -8,11 +7,13 @@ remove_return_annotations: bool

def __init__(self,
remove_variable_annotations: bool = ...,
remove_return_annotations: bool = ...,
remove_argument_annotations: bool = ...,
remove_class_attribute_annotations: bool = ...):
def __init__(
self,
remove_variable_annotations: bool = ...,
remove_return_annotations: bool = ...,
remove_argument_annotations: bool = ...,
remove_class_attribute_annotations: bool = ...
):
...
def __repr__(self) -> str: ...
def __nonzero__(self) -> bool: ...
def __bool__(self) -> bool: ...

@@ -1,4 +0,6 @@

import python_minifier.ast_compat as ast
import sys
import python_minifier.ast_compat as ast
from python_minifier.ast_annotation import get_parent
from python_minifier.transforms.remove_annotations_options import RemoveAnnotationsOptions

@@ -74,16 +76,16 @@ from python_minifier.transforms.suite_transformer import SuiteTransformer

if not isinstance(node.parent, ast.ClassDef):
if not isinstance(get_parent(node), ast.ClassDef):
return False
if len(node.parent.decorator_list) == 0:
if len(get_parent(node).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 get_parent(node).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

@@ -97,6 +99,6 @@

if not isinstance(node.parent, ast.ClassDef):
if not isinstance(get_parent(node), ast.ClassDef):
return False
if len(node.parent.bases) == 0:
if len(get_parent(node).bases) == 0:
return False

@@ -106,6 +108,6 @@

for node in node.parent.bases:
if isinstance(node, ast.Name) and node.id in tricky_types:
for base_node in get_parent(node).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

@@ -116,3 +118,3 @@

# is this a class attribute or a variable?
if isinstance(node.parent, ast.ClassDef):
if isinstance(get_parent(node), ast.ClassDef):
if not self._options.remove_class_attribute_annotations:

@@ -127,3 +129,3 @@ return node

elif node.value:
return self.add_child(ast.Assign([node.target], node.value), parent=node.parent, namespace=node.namespace)
return self.add_child(ast.Assign([node.target], node.value), parent=get_parent(node), namespace=node.namespace)
else:

@@ -134,3 +136,3 @@ # Valueless annotations cause the interpreter to treat the variable as a local.

node.annotation = self.add_child(ast.Num(0), parent=node.parent, namespace=node.namespace)
node.annotation = self.add_child(ast.Num(0), parent=get_parent(node), namespace=node.namespace)
return node
import python_minifier.ast_compat as ast
from python_minifier.transforms.suite_transformer import SuiteTransformer
from python_minifier.util import is_ast_node

@@ -18,3 +17,3 @@

def suite(self, node_list, parent):
without_assert = [self.visit(a) for a in filter(lambda n: not is_ast_node(n, ast.Assert), node_list)]
without_assert = [self.visit(a) for a in filter(lambda n: not isinstance(n, ast.Assert), node_list)]

@@ -21,0 +20,0 @@ if len(without_assert) == 0:

@@ -1,6 +0,7 @@

import python_minifier.ast_compat as ast
import sys
import python_minifier.ast_compat as ast
from python_minifier.transforms.suite_transformer import SuiteTransformer
from python_minifier.util import is_ast_node
from python_minifier.util import is_constant_node

@@ -21,3 +22,3 @@

return node.id == 'True'
elif is_ast_node(node, 'NameConstant'):
elif is_constant_node(node, ast.NameConstant):
return node.value

@@ -24,0 +25,0 @@ return None

@@ -11,7 +11,10 @@ """

import python_minifier.ast_compat as ast
import sys
import python_minifier.ast_compat as ast
from python_minifier.ast_annotation import get_parent, set_parent
from python_minifier.rename.binding import BuiltinBinding
# These are always exceptions, in every version of python

@@ -77,2 +80,3 @@ builtin_exceptions = [

def _remove_empty_call(binding):

@@ -83,13 +87,14 @@ assert isinstance(binding, BuiltinBinding)

# 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):
if not isinstance(get_parent(name_node), ast.Call):
# This is not a call
continue
call_node = name_node.parent
call_node = get_parent(name_node)
if not isinstance(call_node.parent, ast.Raise):
if not isinstance(get_parent(call_node), ast.Raise):
# This is not a raise statement
continue
raise_node = call_node.parent
raise_node = get_parent(call_node)

@@ -107,3 +112,3 @@ if len(call_node.args) > 0 or len(call_node.keywords) > 0:

raise_node.cause = name_node
name_node.parent = raise_node
set_parent(name_node, raise_node)

@@ -110,0 +115,0 @@

@@ -1,6 +0,7 @@

import python_minifier.ast_compat as ast
import sys
import python_minifier.ast_compat as ast
from python_minifier.transforms.suite_transformer import SuiteTransformer
from python_minifier.util import is_ast_node
from python_minifier.util import is_constant_node

@@ -20,3 +21,3 @@

elif sys.version_info >= (3, 4) and is_ast_node(node.value, 'NameConstant') and node.value.value is None:
elif sys.version_info >= (3, 4) and is_constant_node(node.value, ast.NameConstant) and node.value.value is None:
node.value = None

@@ -27,3 +28,3 @@

def visit_FunctionDef(self, node):
assert is_ast_node(node, (ast.FunctionDef, 'AsyncFunctionDef'))
assert isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))

@@ -30,0 +31,0 @@ node.body = [self.visit(a) for a in node.body]

import python_minifier.ast_compat as ast
from python_minifier.transforms.suite_transformer import SuiteTransformer
from python_minifier.util import is_ast_node
from python_minifier.util import is_constant_node

@@ -9,5 +9,4 @@

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!')

@@ -22,3 +21,3 @@ for child in ast.iter_child_nodes(node):

return False
except:
except Exception:
return True

@@ -52,3 +51,3 @@

return is_ast_node(node.value, (ast.Num, ast.Str, 'NameConstant', 'Bytes'))
return is_constant_node(node.value, (ast.Num, ast.Str, ast.NameConstant, ast.Bytes))

@@ -55,0 +54,0 @@ def suite(self, node_list, parent):

@@ -1,4 +0,5 @@

import python_minifier.ast_compat as ast
import sys
import python_minifier.ast_compat as ast
from python_minifier.transforms.suite_transformer import SuiteTransformer

@@ -5,0 +6,0 @@

import python_minifier.ast_compat as ast
from python_minifier.transforms.suite_transformer import SuiteTransformer
from python_minifier.util import is_ast_node

@@ -18,3 +17,3 @@

def suite(self, node_list, parent):
without_pass = [self.visit(a) for a in filter(lambda n: not is_ast_node(n, ast.Pass), node_list)]
without_pass = [self.visit(a) for a in filter(lambda n: not isinstance(n, ast.Pass), node_list)]

@@ -21,0 +20,0 @@ if len(without_pass) == 0:

@@ -5,6 +5,5 @@ import python_minifier.ast_compat as ast

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 = []

@@ -11,0 +10,0 @@ for child in ast.iter_child_nodes(node):

import python_minifier.ast_compat as ast
from python_minifier.ast_annotation import get_parent, add_parent as add_node_parent
from python_minifier.rename.mapper import add_parent
from python_minifier.util import is_ast_node

@@ -16,3 +16,3 @@

"""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):

@@ -189,5 +189,5 @@ for item in value:

if is_ast_node(node, (ast.FunctionDef, ast.Module, 'AsyncFunctionDef')):
if isinstance(node, (ast.FunctionDef, ast.Module, ast.AsyncFunctionDef)):
return node
return nearest_function_namespace(node.parent)
return nearest_function_namespace(get_parent(node))

@@ -197,3 +197,4 @@ if namespace is None:

add_parent(child, parent=parent, namespace=namespace)
add_node_parent(child, parent=parent)
add_parent(child, namespace=namespace)
return child
import python_minifier.ast_compat as ast
def is_ast_node(node, types):
def is_constant_node(node, types):
"""
Is a node one of the specified node types
A node type may be an actual ast class, or a string naming one.
types is a single node type or an iterable of many.
A node type may be an actual ast class or a tuple of many.
If a node_type specified a specific Constant type (Str, Bytes, Num etc),
If types includes a specific Constant type (Str, Bytes, Num etc),
returns true for Constant nodes of the correct type.

@@ -22,25 +22,19 @@

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)
else:
actual_types.append(node_type)
assert not isinstance(node_type, str)
if isinstance(node, tuple(actual_types)):
if isinstance(node, types):
return True
if hasattr(ast, 'Constant') and isinstance(node, ast.Constant):
if isinstance(node, ast.Constant):
if type(node.value) in [type(None), type(True), type(False)]:
return ast.NameConstant in actual_types
return ast.NameConstant in types
elif isinstance(node.value, (int, float, complex)):
return ast.Num in actual_types
return ast.Num in types
elif isinstance(node.value, str):
return ast.Str in actual_types
return ast.Str in types
elif isinstance(node.value, bytes):
return ast.Bytes in actual_types
return ast.Bytes in types
elif node.value == Ellipsis:
return ast.Ellipsis in actual_types
return ast.Ellipsis in types
else:

@@ -47,0 +41,0 @@ raise RuntimeError('Unknown Constant value %r' % type(node.value))

import ast
import sys
import pytest
from python_minifier import unparse
from python_minifier.ast_compare import compare_ast
def test_pep():

@@ -15,5 +18,5 @@ if sys.version_info < (3, 8):

if self._is_special and (ans := self._check_nans(context=context)):
return ans
return ans
results = [(x, y, x/y) for x in input_data if (y := f(x)) > 0]
stuff = [[y := f(x), x/y] for x in range(5)]
stuff = [[y := f(x), x/y] for x in range(5)]
'''

@@ -20,0 +23,0 @@

import ast
import sys
import pytest
from python_minifier import unparse
from python_minifier.ast_compare import compare_ast
def test_await_fstring():

@@ -8,0 +11,0 @@ if sys.version_info < (3, 7):

@@ -7,2 +7,3 @@ import sys

def test_module_namespace():

@@ -9,0 +10,0 @@ if sys.version_info >= (3, 0):

@@ -7,2 +7,3 @@ import sys

def test_class_typevar_default():

@@ -25,2 +26,3 @@ if sys.version_info < (3, 12):

def test_function_typevar_default():

@@ -43,2 +45,3 @@ if sys.version_info < (3, 12):

def test_alias_typevar_default():

@@ -61,2 +64,3 @@ if sys.version_info < (3, 12):

def test_class_typevartuple_default():

@@ -79,2 +83,3 @@ if sys.version_info < (3, 12):

def test_function_typevartuple_default():

@@ -97,2 +102,3 @@ if sys.version_info < (3, 12):

def test_alias_typevartuple_default():

@@ -115,2 +121,3 @@ if sys.version_info < (3, 12):

def test_class_paramspec_default():

@@ -133,2 +140,3 @@ if sys.version_info < (3, 12):

def test_function_paramspec_default():

@@ -151,2 +159,3 @@ if sys.version_info < (3, 12):

def test_alias_paramspec_default():

@@ -167,2 +176,2 @@ if sys.version_info < (3, 12):

assert_namespace_tree(source, expected_namespaces)
assert_namespace_tree(source, expected_namespaces)

@@ -7,2 +7,3 @@ import sys

def test_class_typevar_default():

@@ -26,2 +27,3 @@ if sys.version_info < (3, 13):

def test_function_typevar_default():

@@ -47,2 +49,3 @@ if sys.version_info < (3, 13):

def test_alias_typevar_default():

@@ -68,2 +71,3 @@ if sys.version_info < (3, 13):

def test_class_typevartuple_default():

@@ -90,2 +94,3 @@ if sys.version_info < (3, 13):

def test_function_typevartuple_default():

@@ -113,2 +118,3 @@ if sys.version_info < (3, 13):

def test_alias_typevartuple_default():

@@ -134,2 +140,3 @@ if sys.version_info < (3, 13):

def test_class_paramspec_default():

@@ -156,2 +163,3 @@ if sys.version_info < (3, 13):

def test_function_paramspec_default():

@@ -179,2 +187,3 @@ if sys.version_info < (3, 13):

def test_alias_paramspec_default():

@@ -198,2 +207,2 @@ if sys.version_info < (3, 13):

assert_namespace_tree(source, expected_namespaces)
assert_namespace_tree(source, expected_namespaces)

@@ -7,2 +7,3 @@ import sys

def test_module_namespace():

@@ -9,0 +10,0 @@ if sys.version_info < (3, 3) or sys.version_info > (3, 4):

@@ -24,2 +24,3 @@ import sys

def test_lambda_namespace():

@@ -47,2 +48,3 @@ if sys.version_info < (3, 4):

def test_function_namespace():

@@ -81,2 +83,3 @@ if sys.version_info < (3, 4):

def test_async_function_namespace():

@@ -115,2 +118,3 @@ if sys.version_info < (3, 5):

# region generator namespace

@@ -136,2 +140,3 @@

def test_multi_generator_namespace():

@@ -158,2 +163,3 @@ if sys.version_info < (3, 4):

def test_multi_generator_namespace_2():

@@ -183,2 +189,3 @@ if sys.version_info < (3, 4):

def test_nested_generator():

@@ -209,2 +216,3 @@ if sys.version_info < (3, 4):

def test_nested_generator_2():

@@ -231,2 +239,3 @@ if sys.version_info < (3, 4):

# endregion

@@ -255,2 +264,3 @@

def test_multi_setcomp_namespace():

@@ -277,2 +287,3 @@ if sys.version_info < (3, 4):

def test_multi_setcomp_namespace_2():

@@ -302,2 +313,3 @@ if sys.version_info < (3, 4):

def test_nested_setcomp():

@@ -328,2 +340,3 @@ if sys.version_info < (3, 4):

def test_nested_setcomp_2():

@@ -350,2 +363,3 @@ if sys.version_info < (3, 4):

# endregion

@@ -373,2 +387,3 @@

def test_multi_listcomp_namespace():

@@ -395,2 +410,3 @@ if sys.version_info < (3, 4):

def test_multi_listcomp_namespace_2():

@@ -420,2 +436,3 @@ if sys.version_info < (3, 4):

def test_nested_listcomp():

@@ -446,2 +463,3 @@ if sys.version_info < (3, 4):

def test_nested_listcomp_2():

@@ -468,2 +486,3 @@ if sys.version_info < (3, 4):

# endregion

@@ -491,2 +510,3 @@

def test_multi_dictcomp_namespace():

@@ -513,2 +533,3 @@ if sys.version_info < (3, 4):

def test_multi_dictcomp_namespace_2():

@@ -538,2 +559,3 @@ if sys.version_info < (3, 4):

def test_nested_dictcomp():

@@ -564,2 +586,3 @@ if sys.version_info < (3, 4):

def test_nested_dictcomp_2():

@@ -586,2 +609,3 @@ if sys.version_info < (3, 4):

# endregion

@@ -638,4 +662,4 @@

MyOtherName = OhALongName
func()

@@ -658,2 +682,3 @@ '''

def test_class_namespace_nonlocal_name_allow_rename():

@@ -679,2 +704,3 @@ source = '''

def test_class_namespace_undefined_nonlocal_name_disallow_rename():

@@ -697,2 +723,3 @@ source = '''

def test_class_namespace_nonlocal_builtin_name_allow_rename():

@@ -716,2 +743,3 @@ source = '''

def test_class_namespace_nonlocal_builtin_name_disallow_rename():

@@ -759,2 +787,3 @@ source = '''

def test_class_namespace_nonlocal_name_disallow_rename():

@@ -781,3 +810,4 @@ source = '''

def test_class_namespace_undefined_nonlocal_name_disallow_rename():
def test_class_namespace_undefined_nonlocal_name_disallow_rename_2():
source = '''

@@ -800,2 +830,3 @@ class LazyList:

def test_class_namespace_nonlocal_import_as_disallow_rename():

@@ -823,2 +854,3 @@ """import os as MyNonLocal"""

def test_class_namespace_undefined_nonlocal_import_as_disallow_rename():

@@ -843,2 +875,3 @@ source = '''

def test_class_namespace_nonlocal_import_from_as_disallow_rename():

@@ -865,2 +898,3 @@ source = '''

def test_class_namespace_undefined_nonlocal_import_from_as_disallow_rename():

@@ -885,2 +919,3 @@ source = '''

def test_class_namespace_nonlocal_import_disallow_rename():

@@ -907,2 +942,3 @@ source = '''

def test_class_namespace_undefined_nonlocal_import_disallow_rename():

@@ -927,2 +963,3 @@ source = '''

def test_class_namespace_nonlocal_import_from_disallow_rename():

@@ -949,2 +986,3 @@ source = '''

def test_class_namespace_undefined_nonlocal_import_from_disallow_rename():

@@ -969,2 +1007,3 @@ source = '''

def test_class_namespace_nonlocal_dotted_import_disallow_rename():

@@ -991,2 +1030,3 @@ source = '''

def test_class_namespace_undefined_nonlocal_dotted_import_disallow_rename():

@@ -1011,2 +1051,3 @@ source = '''

def test_class_namespace_nonlocal_func_disallow_rename():

@@ -1034,2 +1075,3 @@ source = '''

def test_class_namespace_undefined_nonlocal_func_disallow_rename():

@@ -1055,2 +1097,3 @@ source = '''

def test_class_namespace_nonlocal_async_func_disallow_rename():

@@ -1081,2 +1124,3 @@ if sys.version_info < (3, 5):

def test_class_namespace_undefined_nonlocal_async_func_disallow_rename():

@@ -1105,2 +1149,3 @@ if sys.version_info < (3, 5):

def test_class_namespace_nonlocal_classdef_disallow_rename():

@@ -1128,2 +1173,3 @@ source = '''

def test_class_namespace_undefined_nonlocal_classdef_disallow_rename():

@@ -1176,2 +1222,3 @@ source = '''

def test_class_namespace_undefined_nonlocal_except_disallow_rename():

@@ -1184,3 +1231,3 @@ source = '''

except Exception as MyNonLocal:
pass
pass

@@ -1210,3 +1257,3 @@ '''

Blah = "Hello"
class LazyList:

@@ -1232,2 +1279,3 @@ MyAttribute = MyNonLocal

def test_class_namespace_undefined_match_disallow_rename():

@@ -1243,3 +1291,3 @@ if sys.version_info < (3, 10):

case MyNonLocal:
pass
pass
'''

@@ -1260,2 +1308,3 @@

def test_class_namespace_nonlocal_match_star_disallow_rename():

@@ -1289,2 +1338,3 @@ if sys.version_info < (3, 10):

def test_class_namespace_undefined_match_star_disallow_rename():

@@ -1300,3 +1350,3 @@ if sys.version_info < (3, 10):

case [*MyNonLocal]:
pass
pass
'''

@@ -1317,2 +1367,3 @@

def test_class_namespace_nonlocal_match_mapping_disallow_rename():

@@ -1346,2 +1397,3 @@ if sys.version_info < (3, 10):

def test_class_namespace_undefined_match_mapping_disallow_rename():

@@ -1357,3 +1409,3 @@ if sys.version_info < (3, 10):

case {**MyNonLocal}:
pass
pass
'''

@@ -1374,2 +1426,3 @@

def test_class_namespace_nonlocal_disallow_rename():

@@ -1396,2 +1449,3 @@ if sys.version_info < (3, 0):

def test_class_namespace_undefined_nonlocal_disallow_rename():

@@ -1398,0 +1452,0 @@ if sys.version_info < (3, 0):

import ast
from helpers import print_namespace
from python_minifier import add_namespace
from python_minifier.rename import bind_names, resolve_names
from python_minifier.ast_annotation import add_parent
from python_minifier.ast_compare import compare_ast
from python_minifier.rename import add_namespace, bind_names, resolve_names
from python_minifier.transforms.combine_imports import CombineImports
from python_minifier.ast_compare import compare_ast
def combine_imports(module):
add_parent(module)
add_namespace(module)

@@ -14,5 +17,7 @@ CombineImports()(module)

def assert_namespace_tree(source, expected_tree):
tree = ast.parse(source)
add_parent(tree)
add_namespace(tree)

@@ -28,2 +33,3 @@ CombineImports()(tree)

def test_import():

@@ -34,3 +40,2 @@ source = '''import builtins

expected_ast = ast.parse(expected)

@@ -40,2 +45,3 @@ actual_ast = combine_imports(ast.parse(source))

def test_import_as():

@@ -51,3 +57,2 @@ source = '''import builtins

expected_ast = ast.parse(expected)

@@ -63,3 +68,3 @@ actual_ast = combine_imports(ast.parse(source))

from collections import abc'''
expected = '''from builtins import dir, help
expected = '''from builtins import dir, help
import collections

@@ -72,2 +77,3 @@ from collections import abc'''

def test_import_in_function():

@@ -77,3 +83,3 @@ source = '''def test():

import builtins
return None

@@ -108,2 +114,3 @@ '''

def test_import_as_class_namespace():

@@ -125,2 +132,3 @@

def test_import_class_namespace():

@@ -142,2 +150,3 @@

def test_from_import_class_namespace():

@@ -159,2 +168,3 @@

def test_from_import_as_class_namespace():

@@ -175,2 +185,1 @@

assert_namespace_tree(source, expected_namespaces)

@@ -9,5 +9,6 @@ import ast

def test_listcomp_regression_2_7():
if sys.version_info >= (3, 0):
pytest.skip('ListComp doesn\'t create a new namespace in python < 3.0')
pytest.skip("ListComp doesn't create a new namespace in python < 3.0")

@@ -21,2 +22,3 @@ source = '''

def test_listcomp_regression():

@@ -47,2 +49,3 @@ if sys.version_info < (3, 0):

def test_generator_expression():

@@ -63,2 +66,3 @@ source = '''

def test_generator_expression_multiple_for():

@@ -83,2 +87,3 @@ source = '''

def test_generator_expression_nested_for():

@@ -85,0 +90,0 @@ source = '''

import ast
import sys
import pytest
from python_minifier import unparse
from python_minifier.ast_compare import compare_ast
def test_pep():

@@ -11,3 +14,3 @@ if sys.version_info < (3, 9):

source = """
source = '''
buttons = [QPushButton(f'Button {i}') for i in range(10)]

@@ -59,3 +62,3 @@

"""
'''

@@ -62,0 +65,0 @@ expected_ast = ast.parse(source)

import ast
import sys
import pytest
from python_minifier import unparse
from python_minifier.ast_compare import compare_ast
def test_dict_expanson():

@@ -8,0 +11,0 @@ if sys.version_info < (3, 5):

@@ -6,5 +6,6 @@ import ast

from python_minifier import add_namespace
from python_minifier.ast_annotation import add_parent
from python_minifier.ast_compare import compare_ast
from python_minifier.rename import add_namespace
from python_minifier.transforms.constant_folding import FoldConstants
from python_minifier.ast_compare import compare_ast

@@ -14,2 +15,3 @@

module = ast.parse(source)
add_parent(module)
add_namespace(module)

@@ -19,2 +21,3 @@ FoldConstants()(module)

def run_test(source, expected):

@@ -29,48 +32,51 @@ try:

@pytest.mark.parametrize('source,expected', [
('True | True', 'True'),
('True | False', 'True'),
('False | True', 'True'),
('False | False', 'False'),
('True & True', 'True'),
('True & False', 'False'),
('False & True', 'False'),
('False & False', 'False'),
('True ^ True', 'False'),
('True ^ False', 'True'),
('False ^ True', 'True'),
('False ^ False', 'False'),
('(True | True) | True', 'True'),
('(True | True) | False', 'True'),
('(True | False) | True', 'True'),
('(True | False) | False', 'True'),
('(False | True) | True', 'True'),
('(False | True) | False', 'True'),
('(False | False) | True', 'True'),
('(False | False) | False', 'False'),
('(True | True) & True', 'True'),
('(True | True) & False', 'False'),
('(True | False) & True', 'True'),
('(True | False) & False', 'False'),
('(False | True) & True', 'True'),
('(False | True) & False', 'False'),
('(False | False) & True', 'False'),
('(False | False) & False', 'False'),
('(True | True) ^ True', 'False'),
('(True | True) ^ False', 'True'),
('(True | False) ^ True', 'False'),
('(True | False) ^ False', 'True'),
('(False | True) ^ True', 'False'),
('(False | True) ^ False', 'True'),
('(False | False) ^ True', 'True'),
('(False | False) ^ False', 'False'),
('True | (True | True)', 'True'),
('True | (True | False)', 'True'),
('True | (False | True)', 'True'),
('True | (False | False)', 'True'),
('False | (True | True)', 'True'),
('False | (True | False)', 'True'),
('False | (False | True)', 'True'),
('False | (False | False)', 'False'),
])
@pytest.mark.parametrize(
('source', 'expected'), [
('True | True', 'True'),
('True | False', 'True'),
('False | True', 'True'),
('False | False', 'False'),
('True & True', 'True'),
('True & False', 'False'),
('False & True', 'False'),
('False & False', 'False'),
('True ^ True', 'False'),
('True ^ False', 'True'),
('False ^ True', 'True'),
('False ^ False', 'False'),
('(True | True) | True', 'True'),
('(True | True) | False', 'True'),
('(True | False) | True', 'True'),
('(True | False) | False', 'True'),
('(False | True) | True', 'True'),
('(False | True) | False', 'True'),
('(False | False) | True', 'True'),
('(False | False) | False', 'False'),
('(True | True) & True', 'True'),
('(True | True) & False', 'False'),
('(True | False) & True', 'True'),
('(True | False) & False', 'False'),
('(False | True) & True', 'True'),
('(False | True) & False', 'False'),
('(False | False) & True', 'False'),
('(False | False) & False', 'False'),
('(True | True) ^ True', 'False'),
('(True | True) ^ False', 'True'),
('(True | False) ^ True', 'False'),
('(True | False) ^ False', 'True'),
('(False | True) ^ True', 'False'),
('(False | True) ^ False', 'True'),
('(False | False) ^ True', 'True'),
('(False | False) ^ False', 'False'),
('True | (True | True)', 'True'),
('True | (True | False)', 'True'),
('True | (False | True)', 'True'),
('True | (False | False)', 'True'),
('False | (True | True)', 'True'),
('False | (True | False)', 'True'),
('False | (False | True)', 'True'),
('False | (False | False)', 'False'),
]
)
def test_bool(source, expected):

@@ -88,21 +94,24 @@ """

@pytest.mark.parametrize('source,expected', [
('10 + 10', '20'),
('10 + 0', '10'),
('0 + 10', '10'),
('10 + 10 + 5', '25'),
('10 - 5 + 5', '10'),
('10 * 10', '100'),
('10 * 10 * 10', '1000'),
('(10 * 10) // 10', '10'),
('(2 * 10) // (2+2)', '5'),
('8>>2', '2'),
('8<<2', '32'),
('0xff^0x0f', '0xf0'),
('0xf0&0xff', '0xf0'),
('0xf0|0x0f', '0xff'),
('10%2', '0'),
('10%3', '1'),
('10-100', '-90')
])
@pytest.mark.parametrize(
('source', 'expected'), [
('10 + 10', '20'),
('10 + 0', '10'),
('0 + 10', '10'),
('10 + 10 + 5', '25'),
('10 - 5 + 5', '10'),
('10 * 10', '100'),
('10 * 10 * 10', '1000'),
('(10 * 10) // 10', '10'),
('(2 * 10) // (2+2)', '5'),
('8>>2', '2'),
('8<<2', '32'),
('0xff^0x0f', '0xf0'),
('0xf0&0xff', '0xf0'),
('0xf0|0x0f', '0xff'),
('10%2', '0'),
('10%3', '1'),
('10-100', '-90')
]
)
def test_int(source, expected):

@@ -115,10 +124,13 @@ """

@pytest.mark.parametrize('source,expected', [
('10/10', '10/10'),
('5+5/10', '5+5/10'),
('2*5/10', '10/10'),
('2/5*10', '2/5*10'),
('2**5', '2**5'),
('5@6', '5@6'),
])
@pytest.mark.parametrize(
('source', 'expected'), [
('10/10', '10/10'),
('5+5/10', '5+5/10'),
('2*5/10', '10/10'),
('2/5*10', '2/5*10'),
('2**5', '2**5'),
('5@6', '5@6'),
]
)
def test_int_not_eval(source, expected):

@@ -131,8 +143,11 @@ """

@pytest.mark.parametrize('source,expected', [
('"Hello" + "World"', '"Hello" + "World"'),
('"Hello" * 5', '"Hello" * 5'),
('b"Hello" + b"World"', 'b"Hello" + b"World"'),
('b"Hello" * 5', 'b"Hello" * 5'),
])
@pytest.mark.parametrize(
('source', 'expected'), [
('"Hello" + "World"', '"Hello" + "World"'),
('"Hello" * 5', '"Hello" * 5'),
('b"Hello" + b"World"', 'b"Hello" + b"World"'),
('b"Hello" * 5', 'b"Hello" * 5'),
]
)
def test_not_eval(source, expected):

@@ -143,2 +158,2 @@ """

run_test(source, expected)
run_test(source, expected)

@@ -10,12 +10,13 @@ import ast

@pytest.mark.parametrize('statement', [
'f"{1=!r:.4}"',
'f"{1=:.4}"',
'f"{1=!s:.4}"',
'f"{1=:.4}"',
'f"{1}"',
'f"{1=}"',
'f"{1=!s}"',
'f"{1=!a}"'
])
@pytest.mark.parametrize(
'statement', [
'f"{1=!r:.4}"',
'f"{1=:.4}"',
'f"{1=!s:.4}"',
'f"{1}"',
'f"{1=}"',
'f"{1=!s}"',
'f"{1=!a}"'
]
)
def test_fstring_statement(statement):

@@ -27,2 +28,3 @@ if sys.version_info < (3, 8):

def test_pep0701():

@@ -41,3 +43,3 @@ if sys.version_info < (3, 12):

statement = """
statement = '''
f"This is the playlist: {", ".join([

@@ -48,7 +50,7 @@ 'Take me back to Eden', # My, my, those eyes like fire

])}"
"""
'''
assert unparse(ast.parse(statement)) == 'f"This is the playlist: {", ".join(["Take me back to Eden","Alkaline","Ascensionism"])}"'
#statement = '''print(f"This is the playlist: {"\N{BLACK HEART SUIT}".join(songs)}")'''
#assert unparse(ast.parse(statement)) == statement
# statement = '''print(f"This is the playlist: {"\N{BLACK HEART SUIT}".join(songs)}")'''
# assert unparse(ast.parse(statement)) == statement

@@ -83,4 +85,4 @@ statement = '''f"Magic wand: {bag["wand"]}"'''

#statement = '''f"{"":*^{1:{1:{1}}}}"'''
#assert unparse(ast.parse(statement)) == statement
# statement = '''f"{"":*^{1:{1:{1}}}}"'''
# assert unparse(ast.parse(statement)) == statement
# SyntaxError: f-string: expressions nested too deeply

@@ -96,2 +98,3 @@

def test_fstring_empty_str():

@@ -98,0 +101,0 @@ if sys.version_info < (3, 6):

import ast
import sys
import pytest
from python_minifier import unparse
from python_minifier.ast_compare import compare_ast
def test_type_statement():

@@ -11,3 +14,2 @@ if sys.version_info < (3, 12):

source = '''

@@ -22,2 +24,3 @@ type Point = tuple[float, float]

def test_function_generic():

@@ -37,2 +40,3 @@ if sys.version_info < (3, 12):

def test_class_generic():

@@ -281,2 +285,1 @@ if sys.version_info < (3, 12):

unparse(expected_ast)

@@ -7,13 +7,24 @@ import ast

from python_minifier import unparse
from python_minifier.ast_annotation import add_parent
from python_minifier.ast_compare import compare_ast
from python_minifier.ast_printer import print_ast
from python_minifier.rename import add_namespace, bind_names, resolve_names, rename, rename_literals, allow_rename_locals, allow_rename_globals
from python_minifier.rename import (
add_namespace,
allow_rename_globals,
allow_rename_locals,
bind_names,
rename,
rename_literals,
resolve_names
)
def hoist(source):
module = ast.parse(source)
add_parent(module)
add_namespace(module)
bind_names(module)
resolve_names(module)
allow_rename_locals(module, False)
allow_rename_globals(module, False)
allow_rename_locals(module, rename_locals=False)
allow_rename_globals(module, rename_globals=False)
rename_literals(module)

@@ -24,2 +35,3 @@ rename(module)

def test_nohoist_single_usage():

@@ -38,2 +50,3 @@ source = '''

def test_hoist_multiple_usage():

@@ -55,2 +68,3 @@ source = '''

def test_no_hoist_multiple_small():

@@ -71,2 +85,3 @@ source = '''

def test_hoist_multiple_small():

@@ -98,2 +113,3 @@ source = '''

def test_hoist_insert_after_docstring():

@@ -140,2 +156,3 @@ source = '''

def test_hoist_insert_after_future_docstring():

@@ -163,2 +180,3 @@ source = '''

def test_hoist_bytes():

@@ -186,2 +204,3 @@ source = '''

def test_hoist_after_multiple_future():

@@ -211,2 +230,3 @@ source = '''

def test_hoist_class():

@@ -230,2 +250,3 @@ source = '''

def test_hoist_from_class_to_func():

@@ -251,2 +272,3 @@ source = '''

def test_hoist_from_func_to_func():

@@ -274,2 +296,3 @@ source = '''

def test_hoist_in_func():

@@ -295,2 +318,3 @@ source = '''

def test_hoist_over_class():

@@ -318,2 +342,3 @@ source = '''

def test_hoist_from_generator():

@@ -326,3 +351,3 @@ source = '''

for i in (a for a in 'World'):
pass
pass
return 'World'

@@ -341,3 +366,3 @@ def c():

for i in (a for a in B):
pass
pass
return B

@@ -352,2 +377,3 @@ def c():

def test_hoist_from_listcomp():

@@ -360,3 +386,3 @@ source = '''

for i in [a for a in 'World']:
pass
pass
return 'World'

@@ -375,3 +401,3 @@ def c():

for i in [a for a in B]:
pass
pass
return B

@@ -397,3 +423,3 @@ def c():

for i in {a: a for a in 'World'}:
pass
pass
return 'World'

@@ -412,3 +438,3 @@ def c():

for i in {a: a for a in B}:
pass
pass
return B

@@ -423,2 +449,3 @@ def c():

def test_hoist_from_setcomp():

@@ -433,3 +460,3 @@ if sys.version_info < (2, 7):

pass
return 'World'
return 'World'
def c():

@@ -445,3 +472,3 @@ return 'World'

for i in {a for a in B + B + A}:
pass
pass
return A

@@ -456,2 +483,3 @@ def c():

def test_hoist_from_lambda():

@@ -479,2 +507,3 @@ source = '''

def test_hoisted_types_py3():

@@ -523,2 +552,3 @@ if sys.version_info < (3, 0):

def test_no_hoist_slots():

@@ -525,0 +555,0 @@ source = '''

@@ -8,24 +8,26 @@ import ast

@pytest.mark.parametrize('statement', [
'import a',
'import a,b',
'import a as b',
'import a as b,c as d',
'import a.b',
'import a.b.c',
'import a.b.c as d',
'import a.b.c as d,e as f',
'from a import A',
'from.import A',
'from.import*',
'from..import A,B',
'from.a import A',
'from...a import A',
'from...a import*',
'from a import A as B',
'from a import A as B,C as D',
'from.a.b import A',
'from....a.b.c import A',
])
@pytest.mark.parametrize(
'statement', [
'import a',
'import a,b',
'import a as b',
'import a as b,c as d',
'import a.b',
'import a.b.c',
'import a.b.c as d',
'import a.b.c as d,e as f',
'from a import A',
'from.import A',
'from.import*',
'from..import A,B',
'from.a import A',
'from...a import A',
'from...a import*',
'from a import A as B',
'from a import A as B,C as D',
'from.a.b import A',
'from....a.b.c import A',
]
)
def test_import_statement(statement):
assert unparse(ast.parse(statement)) == statement
import ast
import sys
import pytest
from python_minifier import unparse
from python_minifier.ast_compare import compare_ast
def test_return():

@@ -8,0 +11,0 @@ if sys.version_info < (3, 0):

@@ -6,11 +6,20 @@ import ast

from python_minifier import (
add_namespace, bind_names, resolve_names, allow_rename_locals, allow_rename_globals,
compare_ast, rename as do_rename, CompareError, unparse, rename_literals
from python_minifier import unparse
from python_minifier.ast_annotation import add_parent
from python_minifier.ast_compare import CompareError, compare_ast
from python_minifier.rename import (
add_namespace,
allow_rename_globals,
allow_rename_locals,
bind_names,
rename,
rename_literals,
resolve_names
)
def rename(source, locals, globals):
def do_rename(source, allow_locals, allow_globals):
# This will raise if the source file can't be parsed
module = ast.parse(source, 'test_match_rename')
add_parent(module)
add_namespace(module)

@@ -20,7 +29,7 @@ bind_names(module)

allow_rename_locals(module, locals)
allow_rename_globals(module, globals)
allow_rename_locals(module, allow_locals)
allow_rename_globals(module, allow_globals)
rename_literals(module)
do_rename(module)
rename(module)

@@ -50,5 +59,5 @@ return module

case hello: pass
match 'hello' + 'hello':
case 'hello': pass
case 'hello': pass
'''

@@ -65,7 +74,7 @@ expected = '''

match B + B:
case 'hello': pass
case 'hello': pass
'''
expected_ast = ast.parse(expected)
actual_ast = rename(source, locals=False, globals=True)
actual_ast = do_rename(source, allow_locals=False, allow_globals=True)
assert_code(expected_ast, actual_ast)

@@ -88,3 +97,3 @@

case None: pass
match 'hello' + 'hello' + 'hello':

@@ -104,9 +113,9 @@ case 'hello': pass

case None: pass
match B + B + B:
case 'hello': pass
case 'hello': pass
'''
expected_ast = ast.parse(expected)
actual_ast = rename(source, locals=True, globals=False)
actual_ast = do_rename(source, allow_locals=True, allow_globals=False)
assert_code(expected_ast, actual_ast)

@@ -127,4 +136,4 @@

case None if hello + hello + hello + hello: pass
case None if expensive_rename: pass
case 'hello' if 'hello' + 'hello' + 'hello' + 'hello': pass
case None if expensive_rename: pass
case 'hello' if 'hello' + 'hello' + 'hello' + 'hello': pass
'''

@@ -141,7 +150,7 @@ expected = '''

case None if expensive_rename: pass
case 'hello' if B + B + B + B: pass
case 'hello' if B + B + B + B: pass
'''
expected_ast = ast.parse(expected)
actual_ast = rename(source, locals=True, globals=False)
actual_ast = do_rename(source, allow_locals=True, allow_globals=False)
assert_code(expected_ast, actual_ast)

@@ -158,3 +167,3 @@

case None if sausage + sausage + sausage + sausage: pass
case 'hello' if 'hello' + 'hello' + 'hello' + 'hello': pass
case 'hello' if 'hello' + 'hello' + 'hello' + 'hello': pass

@@ -165,4 +174,4 @@ def func(expensive_rename):

case None if hello + hello + hello + hello: pass
case None if expensive_rename: pass
case 'hello' if 'hello' + 'hello' + 'hello' + 'hello': pass
case None if expensive_rename: pass
case 'hello' if 'hello' + 'hello' + 'hello' + 'hello': pass
'''

@@ -175,3 +184,3 @@

case None if B + B + B + B: pass
case 'hello' if A + A + A + A: pass
case 'hello' if A + A + A + A: pass

@@ -182,8 +191,8 @@ def C(expensive_rename):

case None if hello + hello + hello + hello: pass
case None if expensive_rename: pass
case 'hello' if A + A + A + A: pass
case None if expensive_rename: pass
case 'hello' if A + A + A + A: pass
'''
expected_ast = ast.parse(expected)
actual_ast = rename(source, locals=False, globals=True)
actual_ast = do_rename(source, allow_locals=False, allow_globals=True)
assert_code(expected_ast, actual_ast)

@@ -205,7 +214,7 @@

case None: hello + hello + hello + hello
case None: expensive_rename
case None: expensive_rename
'''
expected = '''
sausage=0
sausage=0
match None:

@@ -218,7 +227,7 @@ case None: sausage + sausage + sausage + sausage

case None: A + A + A + A
case None: expensive_rename
case None: expensive_rename
'''
expected_ast = ast.parse(expected)
actual_ast = rename(source, locals=True, globals=False)
actual_ast = do_rename(source, allow_locals=True, allow_globals=False)
assert_code(expected_ast, actual_ast)

@@ -232,3 +241,3 @@

source = '''
sausage=0
sausage=0
match None:

@@ -241,3 +250,3 @@ case None: sausage + sausage + sausage + sausage

case None: hello + hello + hello + hello
case None: expensive_rename
case None: expensive_rename
'''

@@ -254,7 +263,7 @@

case None: hello + hello + hello + hello
case None: expensive_rename
case None: expensive_rename
'''
expected_ast = ast.parse(expected)
actual_ast = rename(source, locals=False, globals=True)
actual_ast = do_rename(source, allow_locals=False, allow_globals=True)
assert_code(expected_ast, actual_ast)

@@ -276,3 +285,3 @@

case [True, True, True, True]: pass
case [False, False, False, False]: pass
case [False, False, False, False]: pass

@@ -285,7 +294,7 @@ def func(expensive_rename):

case Global(d, e) if d: e
case Local(f, g) if f: g
case Local(f, g) if f: g
case ['hello', 'hello', 'hello', 'hello']: pass
case [None, None, None, None]: pass
case [True, True, True, True]: pass
case [False, False, False, False]: pass
case [False, False, False, False]: pass
'''

@@ -302,3 +311,3 @@

case [True, True, True, True]: pass
case [False, False, False, False]: pass
case [False, False, False, False]: pass

@@ -315,7 +324,7 @@ def func(expensive_rename):

case [True, True, True, True]: pass
case [False, False, False, False]: pass
case [False, False, False, False]: pass
'''
expected_ast = ast.parse(expected)
actual_ast = rename(source, locals=True, globals=False)
actual_ast = do_rename(source, allow_locals=True, allow_globals=False)
assert_code(expected_ast, actual_ast)

@@ -336,3 +345,3 @@

case [True, True, True, True]: pass
case [False, False, False, False]: pass
case [False, False, False, False]: pass

@@ -345,7 +354,7 @@ def func(expensive_rename):

case Global(d, e) if d: e
case Local(f, g) if f: g
case Local(f, g) if f: g
case ['hello', 'hello', 'hello', 'hello']: pass
case [None, None, None, None]: pass
case [True, True, True, True]: pass
case [False, False, False, False]: pass
case [False, False, False, False]: pass
'''

@@ -361,3 +370,3 @@

case [True, True, True, True]: pass
case [False, False, False, False]: pass
case [False, False, False, False]: pass

@@ -374,7 +383,7 @@ def D(expensive_rename):

case [True, True, True, True]: pass
case [False, False, False, False]: pass
case [False, False, False, False]: pass
'''
expected_ast = ast.parse(expected)
actual_ast = rename(source, locals=False, globals=True)
actual_ast = do_rename(source, allow_locals=False, allow_globals=True)
assert_code(expected_ast, actual_ast)
import ast
import sys
import pytest
from python_minifier import unparse
from python_minifier.ast_compare import compare_ast
def test_pep635_unparse():

@@ -17,7 +20,7 @@ if sys.version_info < (3, 10):

pass
match node:
case BinOp("+", a, BinOp("*", b, c)):
pass
pass
match json_pet:

@@ -30,3 +33,3 @@ case {"type": "cat", "name": name, "pattern": pattern}:

raise ValueError("Not a suitable pet")
def sort(seq):

@@ -59,3 +62,3 @@ match seq:

return Num(value)
def simplify(expr):

@@ -99,3 +102,3 @@ match expr:

return sum(a) / len(a)
def is_closed(sequence):

@@ -109,3 +112,3 @@ match sequence:

return False
def handle_reply(reply):

@@ -137,2 +140,3 @@ match reply:

def test_pep646_unparse():

@@ -305,2 +309,3 @@ if sys.version_info < (3, 10):

def test_match_unparse():

@@ -316,6 +321,6 @@ if sys.version_info < (3, 10):

case _:pass
match a:
case 0|(0|0): pass
case (0|0)|0: pass
case (0|0)|0: pass
case 0|0|0: pass

@@ -328,3 +333,3 @@

case [action, obj]:pass
case {**rest}: pass
case {**rest}: pass
'''

@@ -331,0 +336,0 @@

@@ -5,2 +5,3 @@ import itertools

def test_name_generator():

@@ -7,0 +8,0 @@

@@ -7,2 +7,3 @@ import sys

def test_nonlocal_name():

@@ -20,3 +21,3 @@ if sys.version_info < (3, 0):

inner()
rename_me = False
rename_me = False
outer()

@@ -52,3 +53,3 @@ return rename_me

return 1
patch()

@@ -71,2 +72,3 @@ return f()

def test_nonlocal_import():

@@ -93,3 +95,3 @@ if sys.version_info < (3, 0):

exec(test_code, {}, unminified_locals)
assert unminified_locals['result'] == True
assert unminified_locals['result'] is True

@@ -100,4 +102,5 @@ minified = python_minifier.minify(test_code, rename_locals=True)

exec(minified, {}, minified_locals)
assert minified_locals['result'] == True
assert minified_locals['result'] is True
def test_nonlocal_import_alias():

@@ -124,3 +127,3 @@ if sys.version_info < (3, 0):

exec(test_code, {}, unminified_locals)
assert unminified_locals['result'] == True
assert unminified_locals['result'] is True

@@ -131,2 +134,2 @@ minified = python_minifier.minify(test_code, rename_locals=True)

exec(minified, {}, minified_locals)
assert minified_locals['result'] == True
assert minified_locals['result'] is True
import ast
import sys
import pytest
from python_minifier import unparse

@@ -8,2 +10,3 @@ from python_minifier.ast_compare import compare_ast

def test_pep():

@@ -37,2 +40,3 @@ if sys.version_info < (3, 8):

def test_convert():

@@ -84,2 +88,2 @@ if sys.version_info < (3, 8):

actual_ast = unparse(remove_posargs(ast.parse(source)))
compare_ast(expected_ast, ast.parse(actual_ast))
compare_ast(expected_ast, ast.parse(actual_ast))
import ast
import sys
import pytest
from python_minifier import add_namespace, RemoveAnnotationsOptions
from python_minifier import RemoveAnnotationsOptions
from python_minifier.ast_annotation import add_parent
from python_minifier.ast_compare import compare_ast
from python_minifier.rename import add_namespace
from python_minifier.transforms.remove_annotations import RemoveAnnotations
from python_minifier.ast_compare import compare_ast
def remove_annotations(source, **kwargs):
module = ast.parse(source)
add_parent(module)
add_namespace(module)

@@ -15,2 +20,3 @@ RemoveAnnotations(RemoveAnnotationsOptions(**kwargs))(module)

def test_AnnAssign():

@@ -36,9 +42,12 @@

expected_ast = ast.parse(expected)
actual_ast = remove_annotations(source,
remove_variable_annotations=True,
remove_return_annotations=False,
remove_argument_annotations=False,
remove_class_attribute_annotations=False)
actual_ast = remove_annotations(
source,
remove_variable_annotations=True,
remove_return_annotations=False,
remove_argument_annotations=False,
remove_class_attribute_annotations=False
)
compare_ast(expected_ast, actual_ast)
def test_FunctionDef():

@@ -55,7 +64,9 @@ if sys.version_info < (3,):

expected_ast = ast.parse(expected)
actual_ast = remove_annotations(source,
remove_variable_annotations=False,
remove_return_annotations=True,
remove_argument_annotations=True,
remove_class_attribute_annotations=False)
actual_ast = remove_annotations(
source,
remove_variable_annotations=False,
remove_return_annotations=True,
remove_argument_annotations=True,
remove_class_attribute_annotations=False
)
compare_ast(expected_ast, actual_ast)

@@ -70,7 +81,9 @@

expected_ast = ast.parse(expected)
actual_ast = remove_annotations(source,
remove_variable_annotations=False,
remove_return_annotations=False,
remove_argument_annotations=True,
remove_class_attribute_annotations=False)
actual_ast = remove_annotations(
source,
remove_variable_annotations=False,
remove_return_annotations=False,
remove_argument_annotations=True,
remove_class_attribute_annotations=False
)
compare_ast(expected_ast, actual_ast)

@@ -91,5 +104,6 @@

remove_class_attribute_annotations=False
)
)
compare_ast(expected_ast, actual_ast)
def test_AsyncFunctionDef():

@@ -105,9 +119,12 @@ if sys.version_info < (3, 6):

expected_ast = ast.parse(expected)
actual_ast = remove_annotations(source,
remove_variable_annotations=True,
remove_return_annotations=True,
remove_argument_annotations=True,
remove_class_attribute_annotations=False)
actual_ast = remove_annotations(
source,
remove_variable_annotations=True,
remove_return_annotations=True,
remove_argument_annotations=True,
remove_class_attribute_annotations=False
)
compare_ast(expected_ast, actual_ast)
def test_AnnAssign_novalue():

@@ -129,9 +146,12 @@ if sys.version_info < (3, 6):

expected_ast = ast.parse(expected)
actual_ast = remove_annotations(source,
remove_variable_annotations=True,
remove_return_annotations=False,
remove_argument_annotations=False,
remove_class_attribute_annotations=False)
actual_ast = remove_annotations(
source,
remove_variable_annotations=True,
remove_return_annotations=False,
remove_argument_annotations=False,
remove_class_attribute_annotations=False
)
compare_ast(expected_ast, actual_ast)
def test_class_attributes():

@@ -155,7 +175,9 @@ if sys.version_info < (3, 6):

expected_ast = ast.parse(expected)
actual_ast = remove_annotations(source,
remove_variable_annotations=False,
remove_return_annotations=False,
remove_argument_annotations=False,
remove_class_attribute_annotations=True)
actual_ast = remove_annotations(
source,
remove_variable_annotations=False,
remove_return_annotations=False,
remove_argument_annotations=False,
remove_class_attribute_annotations=True
)
compare_ast(expected_ast, actual_ast)

@@ -195,9 +217,12 @@

expected_ast = ast.parse(expected)
actual_ast = remove_annotations(source,
remove_variable_annotations=False,
remove_return_annotations=False,
remove_argument_annotations=False,
remove_class_attribute_annotations=True)
actual_ast = remove_annotations(
source,
remove_variable_annotations=False,
remove_return_annotations=False,
remove_argument_annotations=False,
remove_class_attribute_annotations=True
)
compare_ast(expected_ast, actual_ast)
def test_remove_dataclass():

@@ -223,7 +248,9 @@ if sys.version_info < (3, 6):

expected_ast = ast.parse(expected)
actual_ast = remove_annotations(source,
remove_variable_annotations=False,
remove_return_annotations=False,
remove_argument_annotations=False,
remove_class_attribute_annotations=True)
actual_ast = remove_annotations(
source,
remove_variable_annotations=False,
remove_return_annotations=False,
remove_argument_annotations=False,
remove_class_attribute_annotations=True
)
compare_ast(expected_ast, actual_ast)

@@ -252,9 +279,12 @@

expected_ast = ast.parse(expected)
actual_ast = remove_annotations(source,
remove_variable_annotations=False,
remove_return_annotations=False,
remove_argument_annotations=False,
remove_class_attribute_annotations=True)
actual_ast = remove_annotations(
source,
remove_variable_annotations=False,
remove_return_annotations=False,
remove_argument_annotations=False,
remove_class_attribute_annotations=True
)
compare_ast(expected_ast, actual_ast)
def test_remove():

@@ -283,7 +313,9 @@ if sys.version_info < (3, 6):

expected_ast = ast.parse(expected)
actual_ast = remove_annotations(source,
remove_variable_annotations=False,
remove_return_annotations=False,
remove_argument_annotations=False,
remove_class_attribute_annotations=True)
actual_ast = remove_annotations(
source,
remove_variable_annotations=False,
remove_return_annotations=False,
remove_argument_annotations=False,
remove_class_attribute_annotations=True
)
compare_ast(expected_ast, actual_ast)

@@ -317,6 +349,6 @@

mysecondfile: str
class Dummy(HypedDict):
myfield: 0
mysecondfile: 0
mysecondfile: 0

@@ -326,14 +358,16 @@ class Dummy(typing.TypedDict):

mysecondfile: str
class Dummy(typing.TypedDic):
myfield: 0
mysecondfile: 0
'''
expected_ast = ast.parse(expected)
actual_ast = remove_annotations(source,
remove_variable_annotations=False,
remove_return_annotations=False,
remove_argument_annotations=False,
remove_class_attribute_annotations=True)
actual_ast = remove_annotations(
source,
remove_variable_annotations=False,
remove_return_annotations=False,
remove_argument_annotations=False,
remove_class_attribute_annotations=True
)
compare_ast(expected_ast, actual_ast)
import ast
from python_minifier import add_namespace, bind_names, resolve_names
from python_minifier.ast_annotation import add_parent
from python_minifier.ast_compare import compare_ast
from python_minifier.rename import add_namespace, bind_names, resolve_names
from python_minifier.transforms.remove_asserts import RemoveAsserts
from python_minifier.ast_compare import compare_ast
def remove_asserts(source):
module = ast.parse(source, 'remove_asserts')
add_parent(module)
add_namespace(module)

@@ -14,2 +18,3 @@ bind_names(module)

def test_remove_assert_empty_module():

@@ -23,2 +28,3 @@ source = 'assert False'

def test_remove_assert_module():

@@ -36,2 +42,3 @@ source = '''import collections

def test_remove_if_empty():

@@ -47,2 +54,3 @@ source = '''if True:

def test_remove_if_line():

@@ -56,10 +64,11 @@ source = '''if True: assert False'''

def test_remove_suite():
source = '''if True:
source = '''if True:
assert False
a=1
assert False
a=1
assert False
return None'''
expected = '''if True:
a=1
a=1
return None'''

@@ -71,2 +80,3 @@

def test_remove_from_class():

@@ -92,2 +102,3 @@ source = '''class A:

def test_remove_from_class_empty():

@@ -103,2 +114,3 @@ source = '''class A:

def test_remove_from_class_func_empty():

@@ -105,0 +117,0 @@ source = '''class A:

@@ -5,4 +5,5 @@ import ast

from python_minifier import add_namespace, bind_names, resolve_names
from python_minifier.ast_annotation import add_parent
from python_minifier.ast_compare import compare_ast
from python_minifier.rename import add_namespace, bind_names, resolve_names
from python_minifier.transforms.remove_debug import RemoveDebug

@@ -14,2 +15,3 @@

add_parent(module)
add_namespace(module)

@@ -54,9 +56,9 @@ bind_names(module)

def test_remove_suite():
source = '''if True:
source = '''if True:
if __debug__: pass
a=1
if __debug__: pass
a=1
if __debug__: pass
return None'''
expected = '''if True:
a=1
a=1
return None'''

@@ -114,8 +116,10 @@

@pytest.mark.parametrize("condition", [
'__debug__',
'__debug__ is True',
'__debug__ is not False',
'__debug__ == True'
])
@pytest.mark.parametrize(
'condition', [
'__debug__',
'__debug__ is True',
'__debug__ is not False',
'__debug__ == True'
]
)
def test_remove_truthy_debug(condition):

@@ -129,3 +133,3 @@ source = '''

print(value)
print(value)
'''

@@ -136,3 +140,3 @@

print(value)
print(value)
'''

@@ -145,11 +149,13 @@

@pytest.mark.parametrize("condition", [
'not __debug__',
'__debug__ is False',
'__debug__ is not True',
'__debug__ == False',
'not __debug__ is True',
'not __debug__ is not False',
'not __debug__ == True'
])
@pytest.mark.parametrize(
'condition', [
'not __debug__',
'__debug__ is False',
'__debug__ is not True',
'__debug__ == False',
'not __debug__ is True',
'not __debug__ is not False',
'not __debug__ == True'
]
)
def test_no_remove_falsy_debug(condition):

@@ -163,3 +169,3 @@ source = '''

print(value)
print(value)
'''

@@ -166,0 +172,0 @@

@@ -6,4 +6,5 @@ import ast

from python_minifier.ast_annotation import add_parent
from python_minifier.ast_compare import compare_ast
from python_minifier.rename import add_namespace, bind_names, resolve_names
from python_minifier.ast_compare import compare_ast
from python_minifier.transforms.remove_exception_brackets import remove_no_arg_exception_call

@@ -15,2 +16,3 @@

add_parent(module)
add_namespace(module)

@@ -34,2 +36,3 @@ bind_names(module)

def test_zero_division_error_brackets():

@@ -47,2 +50,3 @@ """This is a buitin so remove the brackets"""

def test_builtin_with_arg():

@@ -60,2 +64,3 @@ """This has an arg so dont' remove the brackets"""

def test_one_division_error_brackets():

@@ -73,2 +78,3 @@ """This is not a builtin so don't remove the brackets even though it's not defined in the module"""

def test_redefined():

@@ -97,2 +103,3 @@ """This is usually a builtin, but don't remove brackets if it's been redefined"""

def test_raise_from():

@@ -110,2 +117,3 @@ """This is a builtin so remove the brackets"""

def test_raise_from_only():

@@ -123,2 +131,3 @@ """This is a builtin so remove the brackets"""

def test_raise_from_arg():

@@ -136,2 +145,3 @@ """This is a builtin so remove the brackets"""

def test_raise_builtin_in_class():

@@ -155,2 +165,3 @@ """This is a builtin so remove the brackets"""

def test_raise_redefined_builtin_in_class():

@@ -176,2 +187,2 @@ """This was a builtin at some point, but it was redefined so don't remove the brackets"""

actual_ast = remove_brackets(source)
compare_ast(expected_ast, actual_ast)
compare_ast(expected_ast, actual_ast)
import ast
from python_minifier import add_namespace, bind_names, resolve_names
from python_minifier.ast_annotation import add_parent
from python_minifier.ast_compare import compare_ast
from python_minifier.rename import add_namespace, bind_names, resolve_names
from python_minifier.transforms.remove_literal_statements import RemoveLiteralStatements
from python_minifier.ast_compare import compare_ast
def remove_literals(source):
module = ast.parse(source, 'test_remove_literal_statements')
add_parent(module)
add_namespace(module)

@@ -15,2 +18,3 @@ bind_names(module)

def test_remove_literal_num():

@@ -24,2 +28,3 @@ source = '213'

def test_remove_literal_str():

@@ -33,2 +38,3 @@ source = '"hello"'

def test_complex():

@@ -35,0 +41,0 @@ source = '''

import ast
import pytest
import sys
import pytest
from python_minifier.ast_compare import compare_ast
from python_minifier.transforms.remove_object_base import RemoveObject
from python_minifier.ast_compare import compare_ast

@@ -70,2 +71,1 @@

compare_ast(expected_ast, actual_ast)
import ast
from python_minifier import add_namespace, bind_names, resolve_names
from python_minifier.ast_annotation import add_parent
from python_minifier.ast_compare import compare_ast
from python_minifier.rename import add_namespace, bind_names, resolve_names
from python_minifier.transforms.remove_pass import RemovePass
from python_minifier.ast_compare import compare_ast
def remove_literals(source):
module = ast.parse(source, 'remove_literals')
add_parent(module)
add_namespace(module)

@@ -14,2 +18,3 @@ bind_names(module)

def test_remove_pass_empty_module():

@@ -23,2 +28,3 @@ source = 'pass'

def test_remove_pass_module():

@@ -36,2 +42,3 @@ source = '''import collections

def test_remove_if_empty():

@@ -47,2 +54,3 @@ source = '''if True:

def test_remove_if_line():

@@ -56,10 +64,11 @@ source = '''if True: pass'''

def test_remove_suite():
source = '''if True:
source = '''if True:
pass
a=1
pass
a=1
pass
return None'''
expected = '''if True:
a=1
a=1
return None'''

@@ -71,2 +80,3 @@

def test_remove_from_class():

@@ -92,2 +102,3 @@ source = '''class A:

def test_remove_from_class_empty():

@@ -103,2 +114,3 @@ source = '''class A:

def test_remove_from_class_func_empty():

@@ -105,0 +117,0 @@ source = '''class A:

@@ -8,13 +8,13 @@ """

import ast
import sys
import pytest
from python_minifier import unparse
from python_minifier.ast_annotation import add_parent
from python_minifier.ast_compare import CompareError, compare_ast
from python_minifier.rename import add_namespace, allow_rename_globals, allow_rename_locals, bind_names, rename, resolve_names
from python_minifier import add_namespace, bind_names, resolve_names, allow_rename_locals, allow_rename_globals, \
compare_ast, rename, CompareError, unparse
def do_rename(source):
# This will raise if the source file can't be parsed
module = ast.parse(source, 'test_rename_bultins')
add_parent(module)
add_namespace(module)

@@ -24,4 +24,4 @@ bind_names(module)

allow_rename_locals(module, True)
allow_rename_globals(module, True)
allow_rename_locals(module, rename_locals=True)
allow_rename_globals(module, rename_globals=True)

@@ -80,2 +80,3 @@ rename(module)

def test_rename_local_builtin():

@@ -104,2 +105,3 @@ source = '''

def test_no_rename_local_assigned_builtin():

@@ -106,0 +108,0 @@ source = '''

@@ -12,4 +12,6 @@ """

from python_minifier import add_namespace, bind_names, resolve_names, allow_rename_locals, allow_rename_globals, \
compare_ast, rename, CompareError, unparse
from python_minifier import unparse
from python_minifier.ast_annotation import add_parent
from python_minifier.ast_compare import CompareError, compare_ast
from python_minifier.rename import add_namespace, allow_rename_globals, allow_rename_locals, bind_names, rename, resolve_names

@@ -21,2 +23,3 @@

module = ast.parse(source, 'test_rename_locals')
add_parent(module)
add_namespace(module)

@@ -26,4 +29,4 @@ bind_names(module)

allow_rename_locals(module, True)
allow_rename_globals(module, False)
allow_rename_locals(module, rename_locals=True)
allow_rename_globals(module, rename_globals=False)

@@ -34,2 +37,3 @@ rename(module)

def assert_code(expected_ast, actual_ast):

@@ -58,2 +62,3 @@ try:

def test_rename_multiple_definitions():

@@ -78,2 +83,3 @@ source = '''

def test_rename_self_cls_in_place():

@@ -89,23 +95,23 @@ source = '''

def mystatic(cls): return cls
@staticmethod
@staticmethod
def mystatic(asdfghjkl): return asdfghjkl
@staticmethod
@staticmethod
def mystatic(): return 'No cls'
@classmethod
def mystatic(cls): return cls
@classmethod
@classmethod
def mystatic(asdfghjkl): return asdfghjkl
@classmethod
@classmethod
def mystatic(): return 'No cls'
@unknown_decorator
def unknown(self): return self
@unknown_decorator
@unknown_decorator
def unknown(qwertyuiop): return qwertyuiop
@unknown_decorator
@unknown_decorator
def unknown(self, arg): return self, arg
@unknown_decorator
def unknown(): return 'No arg'
@unknown_decorator
def unknown(): return 'No arg'
'''

@@ -121,22 +127,22 @@ expected = '''

def mystatic(cls): return cls
@staticmethod
@staticmethod
def mystatic(asdfghjkl): return asdfghjkl
@staticmethod
@staticmethod
def mystatic(): return 'No cls'
@classmethod
def mystatic(A): return A
@classmethod
@classmethod
def mystatic(A): return A
@classmethod
@classmethod
def mystatic(): return 'No cls'
@unknown_decorator
def unknown(self): return self
@unknown_decorator
@unknown_decorator
def unknown(qwertyuiop): return qwertyuiop
@unknown_decorator
@unknown_decorator
def unknown(self, arg): return self, arg
@unknown_decorator
def unknown(): return 'No arg'
@unknown_decorator
def unknown(): return 'No arg'
'''

@@ -153,6 +159,6 @@

def samename(arg, arg2, *asd, **asd): return asd
class TestClass():
def mymethod(self, arg, *args, **kwargs): return args, kwargs
@classmethod

@@ -164,8 +170,8 @@ def mymethod(cls, arg, *args, **kwargs): return args, kwargs

def samename(arg, arg2, *A, **A): return A
class TestClass():
def mymethod(C, arg, *A, **B): return A, B
@classmethod
def mymethod(C, arg, *A, **B): return A, B
def mymethod(C, arg, *A, **B): return A, B
'''

@@ -271,3 +277,3 @@

print(this_is_my_long_argument_name)
print(this_is_my_long_argument_name)
print(this_is_my_long_argument_name)
'''

@@ -285,2 +291,3 @@ expected = '''

def test_no_rename_single_char_arg():

@@ -292,14 +299,14 @@ source = '''

print(a)
print(a)
print(a)
print(a)
print(a)
print(a)
print(a)
print(a)
print(a)
print(a)
print(a)
print(a)
print(a)
print(a)
print(a)
print(a)
print(a)
print(a)
print(a)
print(a)
print(a)
print(a)
print(a)
print(a)
print(a)
'''

@@ -311,2 +318,3 @@

def test_rename_arg():

@@ -319,3 +327,3 @@ source = '''

print(aa)
print(aa)
print(aa)
'''

@@ -329,3 +337,3 @@ expected = '''

print(A)
print(A)
print(A)
'''

@@ -337,5 +345,6 @@

def test_no_rename_lambda_arg():
source = '''
lambda my_argument: f(my_argument + my_argument + my_argument)
lambda my_argument: f(my_argument + my_argument + my_argument)
'''

@@ -347,9 +356,10 @@

def test_rename_lambda_stararg():
source = '''
lambda *args, **kwargs: f(args, kwargs)
lambda *args, **kwargs: f(args, kwargs)
'''
expected = '''
lambda *A, **B: f(A, B)
lambda *A, **B: f(A, B)
'''

@@ -361,2 +371,3 @@

def test_python3_listcomp_scope():

@@ -367,7 +378,7 @@ if sys.version_info < (3, 0):

source = '''
[a for a in mylist]
[a for a in mylist]
'''
expected = '''
[A for A in mylist]
[A for A in mylist]
'''

@@ -379,2 +390,3 @@

def test_python2_listcomp_scope():

@@ -385,13 +397,13 @@ if sys.version_info >= (3, 0):

source = '''
[a for a in mylist]
[a for a in mylist]
def t():
a = True
[a for a in mylist]
[a for a in mylist]
'''
expected = '''
[a for a in mylist]
[a for a in mylist]
def t():
A = True
[A for A in mylist]
[A for A in mylist]
'''

@@ -403,2 +415,3 @@

def test_arg_rename():

@@ -416,6 +429,7 @@

def test_multiple_args():
source = '''
def f(hello, hello):
print(hello + hello)
print(hello + hello)
'''

@@ -422,0 +436,0 @@ expected = '''

@@ -6,2 +6,3 @@ import ast

def test_slice():

@@ -8,0 +9,0 @@ """AST for slices was changed in 3.9"""

import ast
import sys
import pytest
from python_minifier import unparse
from python_minifier.ast_compare import compare_ast
# There are bizarrely few examples of this, some in the PEP are even syntax errors

@@ -26,2 +29,3 @@

def test_pep696_2():

@@ -50,2 +54,3 @@ if sys.version_info < (3, 13):

def test_pep696_3():

@@ -68,2 +73,3 @@ if sys.version_info < (3, 13):

def test_example():

@@ -92,2 +98,2 @@ if sys.version_info < (3, 13):

actual_ast = unparse(expected_ast)
compare_ast(expected_ast, ast.parse(actual_ast))
compare_ast(expected_ast, ast.parse(actual_ast))
import sys
import pytest
import python_minifier.ast_compat as ast
from python_minifier.util import is_ast_node
def test_type_nodes():
assert is_ast_node(ast.Str('a'), ast.Str)
if hasattr(ast, 'Bytes'):
assert is_ast_node(ast.Bytes(b'a'), ast.Bytes)
assert is_ast_node(ast.Num(1), ast.Num)
assert is_ast_node(ast.Num(0), ast.Num)
if hasattr(ast, 'NameConstant'):
assert is_ast_node(ast.NameConstant(True), ast.NameConstant)
assert is_ast_node(ast.NameConstant(False), ast.NameConstant)
assert is_ast_node(ast.NameConstant(None), ast.NameConstant)
else:
assert is_ast_node(ast.Name(id='True', ctx=ast.Load()), ast.Name)
assert is_ast_node(ast.Name(id='False', ctx=ast.Load()), ast.Name)
assert is_ast_node(ast.Name(id='None', ctx=ast.Load()), ast.Name)
assert is_ast_node(ast.Ellipsis(), ast.Ellipsis)
def test_constant_nodes():
# only test on python 3.8+
if sys.version_info < (3, 8):
pytest.skip('Constant not available')
assert is_ast_node(ast.Constant('a'), ast.Str)
assert is_ast_node(ast.Constant(b'a'), ast.Bytes)
assert is_ast_node(ast.Constant(1), ast.Num)
assert is_ast_node(ast.Constant(0), ast.Num)
assert is_ast_node(ast.Constant(True), ast.NameConstant)
assert is_ast_node(ast.Constant(False), ast.NameConstant)
assert is_ast_node(ast.Constant(None), ast.NameConstant)
assert is_ast_node(ast.Constant(ast.literal_eval('...')), ast.Ellipsis)