예제 #1
0
def gdaltest_other_skipfails(node, capture, filename):
    """
    Replaces a generic call to `return 'skip'` or `return 'fail'`,
    with a `pytest.skip()` or `pytest.fail()`.

    If there's a call to `gdal.post_reason()` immediately preceding
    the return statement, uses that reason as the argument to the
    skip/fail function.
    Ignores/preserves print() calls between the two.

    Examples:

        gdal.post_reason('foo')
        print('everything is broken')
        return 'fail'
        -->
            print('everything is broken')
            pytest.fail('foo')


        gdal.post_reason('foo')
        return 'skip'
        -->
            pytest.skip('foo')


        return 'skip'
        -->
            pytest.skip()
    """
    if flags["debug"]:
        print(f"expression: {capture}")

    returntype = string_value(capture["returntype"])
    if returntype not in ("skip", "fail"):
        return

    reason = None
    if capture.get('post_reason_call'):
        # Remove the gdal.post_reason() statement altogether. Preserve whitespace
        reason = capture["reason"].clone()
        prefix = capture["post_reason_call"].prefix
        next_node = capture["post_reason_call"].next_sibling
        capture["post_reason_call"].remove()
        next_node.prefix = prefix

    # Replace the return statement with a call to pytest.skip() or pytest.fail().
    # Include the reason message if there was one.
    replacement = Attr(kw("pytest", prefix=capture["return_call"].prefix),
                       kw(returntype, prefix="")) + [ArgList(listify(reason))]

    # Adds a 'import pytest' if there wasn't one already
    touch_import(None, "pytest", node)

    capture["return_call"].replace(replacement)
예제 #2
0
def handle_assert_regex(node, capture, arguments):
    """
    self.assertRegex(text, pattern, msg)
    --> assert re.search(pattern, text), msg

    self.assertNotRegex(text, pattern, msg)
    --> assert not re.search(pattern, text), msg

    """
    function_name = capture["function_name"].value
    invert = function_name in INVERT_FUNCTIONS
    function_name = SYNONYMS.get(function_name, function_name)
    num_arguments = ARGUMENTS[function_name]

    if len(arguments) not in (num_arguments, num_arguments + 1):
        # Not sure what this is. Leave it alone.
        return None

    if len(arguments) == num_arguments:
        message = None
    else:
        message = arguments.pop()
        if message.type == syms.argument:
            # keyword argument (e.g. `msg=abc`)
            message = message.children[2].clone()

    arguments[0].prefix = " "
    arguments[1].prefix = ""

    # Adds a 'import re' if there wasn't one already
    touch_import(None, "re", node)

    op_tokens = []
    if invert:
        op_tokens.append(keyword("not"))

    assert_test_nodes = [
        Node(
            syms.power,
            op_tokens + Attr(keyword("re"), keyword("search", prefix="")) +
            [ArgList([arguments[1], Comma(), arguments[0]])],
        )
    ]
    return Assert(assert_test_nodes,
                  message.clone() if message else None,
                  prefix=node.prefix)
예제 #3
0
def rewrite_print(node, capture, filename):
    """Given a print node, rewrite to a logger.debug"""

    params = capture["function_parameters"]
    # Args is either a Leaf or an arglist Node here
    arglist = params.children[1]

    # Extract the arguments from this list
    if isinstance(arglist, Node) and arglist.type == syms.arglist:
        args = [x for x in arglist.children if not x.type == token.COMMA]
        # Remove kwargs for now
        non_kwargs = [x for x in args if not x.type == syms.argument]
        # Read out things like sep here
        sep = " "

        if not len(non_kwargs) == len(args):
            first_leaf = next(node.leaves())
            print(
                "Warning: {}:{} Not handling keyword argument loggers".format(
                    filename, first_leaf.lineno))
            return None

        # If more than one child, we need to construct a new joiner string
        if len(non_kwargs) > 1:
            # Instead of placing a new, modify the first if it's a string
            if arglist.children[0].type == token.STRING:
                value = arglist.children[0].value
                last_char = value[-1]
                new_end = sep + sep.join(["%s"] * (len(non_kwargs) - 1))
                arglist.children[0].value = value[:-1] + new_end + last_char
            else:
                arglist.insert_child(
                    0, String('"' + sep.join(["%s"] * len(non_kwargs)) + '"'))
                arglist.insert_child(1, Comma())
                arglist.children[2].prefix = " " + arglist.children[2].prefix

    # Use the possibly rewritten parameters in the new call
    new_node = Attr(Name("logger"), Name("debug"))
    new_node[0].prefix = node.children[0].prefix
    node.children = new_node + [params]
예제 #4
0
def modify_dict_literal(node: LN, capture: Capture,
                        filename: Filename) -> Optional[LN]:
    toks = iter(capture.get("body"))
    items = []
    prefix = ""
    while True:
        try:
            tok = next(toks)
            if tok.type == TOKEN.DOUBLESTAR:
                body = next(toks).clone()
                body.prefix = prefix + tok.prefix + body.prefix
                items.append(body)
            else:
                colon = next(toks)
                value = next(toks).clone()
                value.prefix = colon.prefix + value.prefix
                if items and isinstance(items[-1], list):
                    items[-1].append(TupleNode(tok, value))
                else:
                    items.append([TupleNode(tok, value)])
            comma = next(toks)
            prefix = comma.prefix
        except StopIteration:
            break
    listitems = []
    for item in items:
        if listitems:
            listitems.extend([Space(), Plus(), Space()])
        if isinstance(item, list):
            listitems.append(ListNode(*item))
        else:
            call = Node(SYMBOL.test, [*Attr(item, Name("items")), ArgList([])])
            listitems.append(call)
    args = listitems
    if len(listitems) > 1:
        args = [Node(SYMBOL.arith_expr, args)]
    args.append(String(node.children[-1].prefix))
    return Call(Name("dict"), args, prefix=node.prefix)
예제 #5
0
    def transform(self, node, results):
        # First, find the sys import. We'll just hope it's global scope.
        if "sys_import" in results:
            if self.sys_import is None:
                self.sys_import = results["sys_import"]
            return

        func = results["func"].clone()
        func.prefix = ""
        register = pytree.Node(syms.power, Attr(Name("atexit"), Name("register")))
        call = Call(register, [func], node.prefix)
        node.replace(call)

        if self.sys_import is None:
            # That's interesting.
            self.warning(
                node,
                "Can't find sys import; Please add an atexit "
                "import at the top of your file.",
            )
            return

        # Now add an atexit import after the sys import.
        names = self.sys_import.children[1]
        if names.type == syms.dotted_as_names:
            names.append_child(Comma())
            names.append_child(Name("atexit", " "))
        else:
            containing_stmt = self.sys_import.parent
            position = containing_stmt.children.index(self.sys_import)
            stmt_container = containing_stmt.parent
            new_import = pytree.Node(
                syms.import_name, [Name("import"), Name("atexit", " ")]
            )
            new = pytree.Node(syms.simple_stmt, [new_import])
            containing_stmt.insert_child(position + 1, Newline())
            containing_stmt.insert_child(position + 2, new)
예제 #6
0
 def test_returns(self):
     attr = Attr(Name("a"), Name("b"))
     self.assertEqual(type(attr), list)
예제 #7
0
    def test(self):
        call = parse("foo()", strip_levels=2)

        self.assertStr(Attr(Name("a"), Name("b")), "a.b")
        self.assertStr(Attr(call, Name("b")), "foo().b")
예제 #8
0
def assertalmostequal_to_assert(node, capture, arguments):
    function_name = capture["function_name"].value
    invert = function_name in INVERT_FUNCTIONS
    function_name = SYNONYMS.get(function_name, function_name)

    nargs = len(arguments)
    if nargs < 2 or nargs > 5:
        return None

    def get_kwarg_value(index, name):
        idx = 0
        for arg in arguments:
            if arg.type == syms.argument:
                if arg.children[0].value == name:
                    return arg.children[2].clone()
            else:
                if idx == index:
                    return arg.clone()
                idx += 1
        return None

    first = get_kwarg_value(0, "first")
    second = get_kwarg_value(1, "second")

    if first is None or second is None:
        # Not sure what this is, leave it alone
        return

    places = get_kwarg_value(2, "places")
    msg = get_kwarg_value(3, "msg")
    delta = get_kwarg_value(4, "delta")

    if delta is not None:
        try:
            abs_delta = float(delta.value)
        except ValueError:
            # this should be a number, give up.
            return
    else:
        if places is None:
            places = 7
        else:
            try:
                places = int(places.value)
            except (ValueError, AttributeError):
                # this should be an int, give up.
                return
        abs_delta = "1e-%d" % places

    arguments[1].prefix = ""
    if invert:
        op_token = Leaf(TOKEN.NOTEQUAL, "!=", prefix=" ")
    else:
        op_token = Leaf(TOKEN.EQEQUAL, "==", prefix=" ")
    assert_test_nodes = [
        Node(
            syms.comparison,
            [
                arguments[0],
                op_token,
                Node(
                    syms.power,
                    Attr(keyword("pytest"), keyword("approx", prefix="")) + [
                        ArgList([
                            arguments[1],
                            Comma(),
                            KeywordArg(keyword("abs"),
                                       Leaf(TOKEN.NUMBER, abs_delta)),
                        ])
                    ],
                ),
            ],
        )
    ]
    # Adds a 'import pytest' if there wasn't one already
    touch_import(None, "pytest", node)

    return Assert(assert_test_nodes,
                  msg.clone() if msg else None,
                  prefix=node.prefix)
예제 #9
0
def assertmethod_to_assert(node, capture, arguments):
    """
    self.assertEqual(foo, bar, msg)
    --> assert foo == bar, msg

    self.assertTrue(foo, msg)
    --> assert foo, msg

    self.assertIsNotNone(foo, msg)
    --> assert foo is not None, msg

    .. etc
    """
    function_name = capture["function_name"].value
    invert = function_name in INVERT_FUNCTIONS
    function_name = SYNONYMS.get(function_name, function_name)
    num_arguments = ARGUMENTS[function_name]

    if len(arguments) not in (num_arguments, num_arguments + 1):
        # Not sure what this is. Leave it alone.
        return None

    if len(arguments) == num_arguments:
        message = None
    else:
        message = arguments.pop()
        if message.type == syms.argument:
            # keyword argument (e.g. `msg=abc`)
            message = message.children[2].clone()

    if function_name == "assertIsInstance":
        arguments[0].prefix = ""
        assert_test_nodes = [
            Call(keyword("isinstance"),
                 [arguments[0], Comma(), arguments[1]])
        ]
        if invert:
            assert_test_nodes.insert(0, keyword("not"))
    elif function_name == "assertAlmostEqual":
        arguments[1].prefix = ""
        # TODO: insert the `import pytest` at the top of the file
        if invert:
            op_token = Leaf(TOKEN.NOTEQUAL, "!=", prefix=" ")
        else:
            op_token = Leaf(TOKEN.EQEQUAL, "==", prefix=" ")
        assert_test_nodes = [
            Node(
                syms.comparison,
                [
                    arguments[0],
                    op_token,
                    Node(
                        syms.power,
                        Attr(keyword("pytest"), keyword("approx", prefix="")) +
                        [
                            ArgList([
                                arguments[1],
                                Comma(),
                                KeywordArg(keyword("abs"),
                                           Leaf(TOKEN.NUMBER, "1e-7")),
                            ])
                        ],
                    ),
                ],
            )
        ]
        # Adds a 'import pytest' if there wasn't one already
        touch_import(None, "pytest", node)

    else:
        op_tokens = OPERATORS[function_name]
        if not isinstance(op_tokens, list):
            op_tokens = [op_tokens]
        op_tokens = [o.clone() for o in op_tokens]

        if invert:
            if not op_tokens:
                op_tokens.append(keyword("not"))
            elif op_tokens[0].type == TOKEN.NAME and op_tokens[0].value == "is":
                op_tokens[0] = Node(
                    syms.comp_op,
                    [keyword("is"), keyword("not")], prefix=" ")
            elif op_tokens[0].type == TOKEN.NAME and op_tokens[0].value == "in":
                op_tokens[0] = Node(
                    syms.comp_op,
                    [keyword("not"), keyword("in")], prefix=" ")
            elif op_tokens[0].type == TOKEN.EQEQUAL:
                op_tokens[0] = Leaf(TOKEN.NOTEQUAL, "!=", prefix=" ")

        if num_arguments == 2:
            # a != b, etc.
            assert_test_nodes = [arguments[0]] + op_tokens + [arguments[1]]
        elif function_name == "assertTrue":
            assert_test_nodes = op_tokens + [arguments[0]]
            # not a
        elif function_name == "assertIsNone":
            # a is not None
            assert_test_nodes = [arguments[0]] + op_tokens
    return Assert(assert_test_nodes,
                  message.clone() if message else None,
                  prefix=node.prefix)
예제 #10
0
        def encapsulate_transform(
            node: LN, capture: Capture, filename: Filename
        ) -> None:
            if "attr_assignment" in capture:
                leaf = capture["attr_name"]
                leaf.replace(Name(new_name, prefix=leaf.prefix))

                if make_property:
                    # TODO: capture and use type annotation from original assignment

                    class_node = get_class(node)
                    suite = find_first(class_node, SYMBOL.suite)
                    assert isinstance(suite, Node)
                    indent_node = find_first(suite, TOKEN.INDENT)
                    assert isinstance(indent_node, Leaf)
                    indent = indent_node.value

                    getter = Node(
                        SYMBOL.decorated,
                        [
                            Node(
                                SYMBOL.decorator,
                                [
                                    Leaf(TOKEN.AT, "@"),
                                    Name("property"),
                                    Leaf(TOKEN.NEWLINE, "\n"),
                                ],
                            ),
                            Node(
                                SYMBOL.funcdef,
                                [
                                    Name("def", indent),
                                    Name(old_name, prefix=" "),
                                    Node(
                                        SYMBOL.parameters,
                                        [LParen(), Name("self"), RParen()],
                                    ),
                                    Leaf(TOKEN.COLON, ":"),
                                    Node(
                                        SYMBOL.suite,
                                        [
                                            Newline(),
                                            Leaf(TOKEN.INDENT, indent.value + "    "),
                                            Node(
                                                SYMBOL.simple_stmt,
                                                [
                                                    Node(
                                                        SYMBOL.return_stmt,
                                                        [
                                                            Name("return"),
                                                            Node(
                                                                SYMBOL.power,
                                                                Attr(
                                                                    Name("self"),
                                                                    Name(new_name),
                                                                ),
                                                                prefix=" ",
                                                            ),
                                                        ],
                                                    ),
                                                    Newline(),
                                                ],
                                            ),
                                            Leaf(TOKEN.DEDENT, "\n" + indent),
                                        ],
                                    ),
                                ],
                                prefix=indent,
                            ),
                        ],
                    )

                    setter = Node(
                        SYMBOL.decorated,
                        [
                            Node(
                                SYMBOL.decorator,
                                [
                                    Leaf(TOKEN.AT, "@"),
                                    Node(
                                        SYMBOL.dotted_name,
                                        [Name(old_name), Dot(), Name("setter")],
                                    ),
                                    Leaf(TOKEN.NEWLINE, "\n"),
                                ],
                            ),
                            Node(
                                SYMBOL.funcdef,
                                [
                                    Name("def", indent),
                                    Name(old_name, prefix=" "),
                                    Node(
                                        SYMBOL.parameters,
                                        [
                                            LParen(),
                                            Node(
                                                SYMBOL.typedargslist,
                                                [
                                                    Name("self"),
                                                    Comma(),
                                                    Name("value", prefix=" "),
                                                ],
                                            ),
                                            RParen(),
                                        ],
                                    ),
                                    Leaf(TOKEN.COLON, ":"),
                                    Node(
                                        SYMBOL.suite,
                                        [
                                            Newline(),
                                            Leaf(TOKEN.INDENT, indent + "    "),
                                            Node(
                                                SYMBOL.simple_stmt,
                                                [
                                                    Node(
                                                        SYMBOL.expr_stmt,
                                                        [
                                                            Node(
                                                                SYMBOL.power,
                                                                Attr(
                                                                    Name("self"),
                                                                    Name(new_name),
                                                                ),
                                                            ),
                                                            Leaf(
                                                                TOKEN.EQUAL,
                                                                "=",
                                                                prefix=" ",
                                                            ),
                                                            Name("value", prefix=" "),
                                                        ],
                                                    ),
                                                    Newline(),
                                                ],
                                            ),
                                            Leaf(TOKEN.DEDENT, "\n" + indent),
                                        ],
                                    ),
                                ],
                                prefix=indent,
                            ),
                        ],
                    )

                    suite.insert_child(-1, getter)
                    suite.insert_child(-1, setter)

                    prev = find_previous(getter, TOKEN.DEDENT, recursive=True)
                    curr = find_last(setter, TOKEN.DEDENT, recursive=True)
                    assert isinstance(prev, Leaf) and isinstance(curr, Leaf)
                    prev.prefix, curr.prefix = curr.prefix, prev.prefix
                    prev.value, curr.value = curr.value, prev.value
예제 #11
0
def gdaltest_fail_reason_to_assert(node, capture, filename):
    """
    Converts an entire if statement into an assertion.

    if x == y:
        print("a")
        gdal.post_reason('b')
        print("c")
        return 'fail'

    -->
        assert x != y, 'foo'

    Uses post_reason(x) or print(x) as valid reasons.
    Prefers post_reason(x).
    Ignores reasons that are sub-expressions of the condition
    (ie, will ignore `x` as a reason in the above example.)

    Attempts to strip out low-value print/post_reason calls.
    If they can't all be stripped out, falls back to using an if statement with pytest.fail():

        if x == y:
            print("a", a)
            pytest.fail("b")
    """
    if flags["debug"]:
        print(f"expression: {capture}")

    returntype = string_value(capture["returntype"])
    if returntype != "fail":
        # only handle fails for now, tackle others later
        return

    condition = capture["condition"]
    condition_leaves = {
        n.value
        for n in condition.leaves() if n.type in (TOKEN.NAME, TOKEN.STRING)
    }

    candidates = []
    # Find a reason. Prefer post_reason reasons rather than print statements.
    for stmt in capture['reason_candidates']:
        power = stmt.children[0]
        if power.children[0].value == 'print':
            candidates.append(power.children[1].children[1])

    for stmt in capture['reason_candidates']:
        power = stmt.children[0]
        if power.children[0].value == 'gdaltest':
            candidates.append(power.children[2].children[1])
        elif power.children[0].value == 'post_reason':
            candidates.append(power.children[1].children[1])

    candidates.reverse()

    IGNORE_REASON_STRING = re.compile(
        r'(got %.* expected .*)|(.* returned %.* instead of.*)', re.I)
    reason = None
    for i, candidate in reversed(list(enumerate(candidates[:]))):
        # Don't include reasons that are actually expressions used in the comparison itself.
        # These are already printed by pytest in the event of the assertion failing
        candidate_leaves = {
            n.value
            for n in candidate.leaves() if n.type in (TOKEN.NAME, TOKEN.STRING)
        }
        if candidate_leaves.issubset(condition_leaves):
            # Reason is just made up of expressions already used in the comparison; remove it
            safe_remove_from_suite(candidate.parent.parent.parent)
            candidates.pop(i)
            continue

        if any(leaf.type == TOKEN.STRING
               and IGNORE_REASON_STRING.match(string_value(leaf))
               for leaf in candidate.leaves()):
            # looks kind of useless too; pytest will output the got/expected values.
            safe_remove_from_suite(candidate.parent.parent.parent)
            candidates.pop(i)
            continue
        # Keep this reason.
        reason = candidate

    if reason:
        # remove the winning reason node from the tree
        safe_remove_from_suite(reason.parent.parent.parent)
        reason.remove()
        candidates.remove(reason)

    if not candidates:
        # all print/post_reason calls were removed.
        # So we can convert the if statement to an assert.
        if reason:
            reason = parenthesize_if_not_already(reason.clone())
        assertion = Assert(
            [parenthesize_if_multiline(invert_condition(condition))],
            reason,
            prefix=node.prefix,
        )
        if flags["debug"]:
            print(f"Replacing:\n\t{node}")
            print(f"With: {assertion}")
            print()

        replace_if_with_assert(node, capture['dedent'], assertion)
    else:
        # At least one print statement remains.
        # We need to keep the if statement, and use a `pytest.fail(reason)`.
        replacement = Attr(
            kw("pytest", prefix=capture["return_call"].prefix),
            kw(returntype, prefix=""),
        ) + [ArgList(listify(reason))]

        # Adds a 'import pytest' if there wasn't one already
        touch_import(None, "pytest", node)

        capture["return_call"].replace(replacement)