exec("def square(x):\n return x * x\n")
assert 16 == square(4)  # noqa: F821

d = {}
exec("def square(x):\n return x * x\n", {}, d)
assert 16 == d["square"](4)

exec("assert 2 == x", {}, {"x": 2})
exec("assert 2 == x", {"x": 2}, {})
exec("assert 4 == x", {"x": 2}, {"x": 4})

exec("assert max(1, 2) == 2", {}, {})

exec("assert max(1, 5, square(5)) == 25", None)

# Local environment shouldn't replace global environment:
exec("assert max(1, 5, square(5)) == 25", None, {})


# Closures aren't available if local scope is replaced:
def g():
    seven = "seven"

    def f():
        try:
            exec("seven", None, {})
        except NameError:
            pass
        else:
            raise NameError("seven shouldn't be in scope")

    f()


g()

try:
    exec("", 1)
except TypeError:
    pass
else:
    raise TypeError("exec should fail unless globals is a dict or None")

g = globals()
g["x"] = 2
exec("x += 2")
assert x == 4  # noqa: F821
assert g["x"] == x  # noqa: F821

exec("del x")
assert "x" not in g

assert "g" in globals()
assert "g" in locals()
exec("assert 'g' in globals()")
exec("assert 'g' in locals()")
exec("assert 'g' not in globals()", {})
exec("assert 'g' not in locals()", {})

del g


def f():
    g = 1
    assert "g" not in globals()
    assert "g" in locals()
    exec("assert 'g' not in globals()")
    exec("assert 'g' in locals()")
    exec("assert 'g' not in globals()", {})
    exec("assert 'g' not in locals()", {})


f()
