Skip to content

Commit

Permalink
fix: used needle_samples to find some issues
Browse files Browse the repository at this point in the history
  • Loading branch information
15r10nk committed Oct 3, 2023
1 parent 347bb79 commit ae36dbc
Show file tree
Hide file tree
Showing 23 changed files with 173 additions and 56 deletions.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ data_file = "$TOP/.coverage"

[tool.black]
force-exclude = "tests/.*_samples"
skip_magic_trailing_comma = true

[tool.mypy]
exclude="tests/.*_samples"
129 changes: 103 additions & 26 deletions pysource_minimize/_minimize.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import copy
import os
import sys
from typing import List
from typing import Union

try:
from ast import unparse
Expand All @@ -27,6 +29,18 @@ def is_block(nodes):
)


def arguments(
node: Union[ast.FunctionDef, ast.AsyncFunctionDef, ast.Lambda]
) -> List[ast.arg]:
args = node.args
l = [*args.args, args.vararg, *args.kwonlyargs, args.kwarg]

if sys.version_info >= (3, 8):
l += args.posonlyargs

return [arg for arg in l if arg is not None]


class Minimizer:
def __init__(self, source, checker, progress_callback):
self.checker = checker
Expand Down Expand Up @@ -246,6 +260,12 @@ def minimize_expr(self, node):
if self.try_only(node, node.left):
self.minimize(node.left)
return

for comp in node.comparators:
if self.try_only(node, comp):
self.minimize(comp)
return

self.minimize_lists(
(node.ops, node.comparators), (lambda _: None, self.minimize)
)
Expand All @@ -254,20 +274,38 @@ def minimize_expr(self, node):
self.try_only_minimize(node, node.value, node.slice)

elif isinstance(node, ast.FormattedValue):
if (
isinstance(node.format_spec, ast.JoinedStr)
and len(node.format_spec.values) == 1
and self.try_only(node, node.format_spec.values[0])
):
self.minimize(node.format_spec.values[0])
return

self.try_none(node.format_spec)
self.minimize_expr(node.value)

elif isinstance(node, ast.JoinedStr):
if (
len(node.values) == 1
and isinstance(node.values[0], ast.FormattedValue)
and self.try_only(node, node.values[0].value)
):
self.minimize(node.values[0].value)
return

self.minimize(node.values)
# todo minimize values

elif isinstance(node, ast.Slice):
self.try_only_minimize(node, node.lower, node.upper, node.step)
elif isinstance(node, ast.Lambda):

if self.try_only_minimize(node, node.body):
return

self.minimize_args(node.args)
if self.minimize_args_of(node):
return

elif isinstance(node, ast.UnaryOp):
self.try_only_minimize(node, node.operand)
Expand Down Expand Up @@ -338,31 +376,30 @@ def minimize_expr(self, node):
node, (ast.ListComp, ast.SetComp, ast.GeneratorExp, ast.DictComp)
):
for gen in node.generators:
if self.try_only(node, gen.target):
self.minimize_expr(gen.target)
return

if self.try_only(node, gen.iter):
self.minimize_expr(gen.iter)
return

if isinstance(node, ast.DictComp):
if self.try_only(node, node.key):
self.minimize_expr(node.key)
return
for if_ in gen.ifs:
if self.try_only(node, if_):
self.minimize_expr(if_)
return

if self.try_only(node, node.value):
self.minimize_expr(node.value)
if isinstance(node, ast.DictComp):
if self.try_only_minimize(node, node.key, node.value):
return

self.minimize_expr(node.key)
self.minimize_expr(node.value)
else:
if self.try_only(node, node.elt):
self.minimize_expr(node.elt)
if self.try_only_minimize(node, node.elt):
return

self.minimize_expr(node.elt)
self.minimize_list(node.generators, self.minimize_comprehension, 1)

elif isinstance(node, ast.NamedExpr):
self.try_only_minimize(node, node.value)
self.try_only_minimize(node, node.target, node.value)
else:
raise TypeError(node) # Expr

Expand Down Expand Up @@ -410,7 +447,19 @@ def minimize_pattern(pattern):

minimize_pattern(c.pattern)

def minimize_args(self, args):
def minimize_args_of(self, func):
args = func.args

for child in [
*[arg.annotation for arg in arguments(func)],
*func.args.defaults,
*func.args.kw_defaults,
getattr(func, "returns", None),
]:
if child is not None and self.try_only(func, child):
self.minimize(child)
return True

def minimize_arg(arg: ast.arg):
if arg.annotation is not None and not self.try_none(arg.annotation):
self.minimize(arg.annotation)
Expand All @@ -435,6 +484,8 @@ def minimize_arg(arg: ast.arg):
if args.kwarg is not None and not self.try_none(args.kwarg):
minimize_arg(args.kwarg)

return False

def minimize_stmt(self, node):
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
if self.try_only_minimize(node, node.decorator_list):
Expand All @@ -460,7 +511,8 @@ def minimize_stmt(self, node):
if self.try_only(node, node.body):
return

self.minimize_args(node.args)
if self.minimize_args_of(node):
return
if sys.version_info >= (3, 12):
self.minimize_list(node.type_params, self.minimize_type_param)

Expand Down Expand Up @@ -499,26 +551,30 @@ def minimize_stmt(self, node):
self.try_only_minimize(node, node.value)

elif isinstance(node, ast.Delete):
if len(node.targets) == 1 and self.try_only(node, node.targets[0]):
self.minimize(node.targets[0])
return

self.minimize_list(node.targets, self.minimize, 1)

elif isinstance(node, ast.Assign):
self.try_only_minimize(node, node.value, node.targets)

# todo minimize targets

elif isinstance(node, ast.AugAssign):
self.minimize(node.target)
self.try_only_minimize(node, node.value)
# todo minimize target
self.try_only_minimize(node, node.target, node.value)

elif isinstance(node, ast.AnnAssign):

if node.value is None or not self.try_node(
node, ast.Assign(targets=[node.target], value=node.value)
):
self.try_only_minimize(node, node.value, node.annotation)
# todo minimize target
self.try_only_minimize(node, node.target, node.value, node.annotation)

elif isinstance(node, (ast.For, ast.AsyncFor)):
if self.try_only(node, node.target):
self.minimize(node.target)
return

self.minimize_list(node.body, self.minimize_stmt)
body = self.get_ast(node)
if not any(
Expand Down Expand Up @@ -548,21 +604,42 @@ def minimize_stmt(self, node):
self.try_only_minimize(node, node.test, node.body, node.orelse)

elif isinstance(node, (ast.With, ast.AsyncWith)):

if self.try_only_minimize(node, node.body):
return

for item in node.items:
if self.try_only(node, item.context_expr):
self.minimize(item.context_expr)
return

if item.optional_vars is not None and self.try_only(
node, item.optional_vars
):
self.minimize(item.optional_vars)
return

def minimize_item(item: ast.withitem):
self.minimize(item.context_expr)
self.minimize_optional(item.optional_vars)

self.minimize_list(node.items, minimize_item, minimal=1)

self.minimize_list(node.body, self.minimize_stmt)

elif py310 and isinstance(node, ast.Match):
self.minimize(node.subject)
if self.try_only_minimize(node, node.subject):
return

for case_ in node.cases:
for e in [case_.guard, case_.body]:
if e is not None and self.try_only(node, e):
self.minimize(e)
return

if isinstance(case_.pattern, ast.MatchValue):
if self.try_only(node, case_.pattern.value):
self.minimize(case_.pattern.value)
return

self.minimize_list(node.cases, self.minimize_match_case, 1)

elif isinstance(node, ast.Raise):
Expand Down
13 changes: 6 additions & 7 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,23 @@
import pytest


@pytest.fixture(params=range(100))
@pytest.fixture(params=range(0))
def seed():
return random.randrange(0, 100000000)



def pytest_addoption(parser, pluginmanager):
parser.addoption(
"--generate-samples",action="store_true"
, help="Config file to use, defaults to %(default)s",
"--generate-samples",
action="store_true",
help="Config file to use, defaults to %(default)s",
)


def pytest_sessionfinish(session, exitstatus):
if exitstatus==0 and session.config.option.generate_samples:
if exitstatus == 0 and session.config.option.generate_samples:
from .test_needle import generate_needle

generate_needle()


# teardown_stuff

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def name_3() -> needle_17597:
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
needle_17597 ^= name_4
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lambda *, name_5=needle_17597: name_5
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
match needle_17597:
case name_1.name_3:
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
name_2 <= needle_17597
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
f'{needle_17597!s}'
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
async def name_0(*, name_2=needle_17597):
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[name_5 for needle_17597 in name_3]
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
match name_3:
case needle_17597.name_5:
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def name_5(name_4: needle_17597, /):
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
f'{1!s:{needle_17597}}'
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(name_3 for name_1 in name_5 if needle_17597)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
with name_0 as needle_17597:
pass
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
needle_17597: name_1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
del needle_17597
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
with name_4:
needle_17597
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(needle_17597 := name_0)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
for needle_17597 in name_5:
pass
Loading

0 comments on commit ae36dbc

Please sign in to comment.