ruff is faster and handle everything we had prior. isort configuration done based on the indication from https://github.com/astral-sh/ruff/issues/4670, previousely based on reorder-python-import (#11896) flake8-docstrings was a wrapper around pydocstyle (now archived) that explicitly asks to use ruff in https://github.com/PyCQA/pydocstyle/pull/658. flake8-typing-import is useful mainly for project that support python 3.7 and the one useful check will be implemented in https://github.com/astral-sh/ruff/issues/2302 We need to keep blacken-doc because ruff does not handle detection of python code inside .md and .rst. The direct link to the repo is now used to avoid a redirection. Manual fixes: - Lines that became too long - % formatting that was not done automatically - type: ignore that were moved around - noqa of hard to fix issues (UP031 generally) - fmt: off and fmt: on that is not really identical between black and ruff - autofix re-order in pre-commit from faster to slower Co-authored-by: Ran Benita <ran@unusedvar.com>
196 lines
5.3 KiB
Python
196 lines
5.3 KiB
Python
from typing import Callable
|
|
|
|
from _pytest.mark.expression import Expression
|
|
from _pytest.mark.expression import ParseError
|
|
import pytest
|
|
|
|
|
|
def evaluate(input: str, matcher: Callable[[str], bool]) -> bool:
|
|
return Expression.compile(input).evaluate(matcher)
|
|
|
|
|
|
def test_empty_is_false() -> None:
|
|
assert not evaluate("", lambda ident: False)
|
|
assert not evaluate("", lambda ident: True)
|
|
assert not evaluate(" ", lambda ident: False)
|
|
assert not evaluate("\t", lambda ident: False)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("expr", "expected"),
|
|
(
|
|
("true", True),
|
|
("true", True),
|
|
("false", False),
|
|
("not true", False),
|
|
("not false", True),
|
|
("not not true", True),
|
|
("not not false", False),
|
|
("true and true", True),
|
|
("true and false", False),
|
|
("false and true", False),
|
|
("true and true and true", True),
|
|
("true and true and false", False),
|
|
("true and true and not true", False),
|
|
("false or false", False),
|
|
("false or true", True),
|
|
("true or true", True),
|
|
("true or true or false", True),
|
|
("true and true or false", True),
|
|
("not true or true", True),
|
|
("(not true) or true", True),
|
|
("not (true or true)", False),
|
|
("true and true or false and false", True),
|
|
("true and (true or false) and false", False),
|
|
("true and (true or (not (not false))) and false", False),
|
|
),
|
|
)
|
|
def test_basic(expr: str, expected: bool) -> None:
|
|
matcher = {"true": True, "false": False}.__getitem__
|
|
assert evaluate(expr, matcher) is expected
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("expr", "expected"),
|
|
(
|
|
(" true ", True),
|
|
(" ((((((true)))))) ", True),
|
|
(" ( ((\t (((true))))) \t \t)", True),
|
|
("( true and (((false))))", False),
|
|
("not not not not true", True),
|
|
("not not not not not true", False),
|
|
),
|
|
)
|
|
def test_syntax_oddeties(expr: str, expected: bool) -> None:
|
|
matcher = {"true": True, "false": False}.__getitem__
|
|
assert evaluate(expr, matcher) is expected
|
|
|
|
|
|
def test_backslash_not_treated_specially() -> None:
|
|
r"""When generating nodeids, if the source name contains special characters
|
|
like a newline, they are escaped into two characters like \n. Therefore, a
|
|
user will never need to insert a literal newline, only \n (two chars). So
|
|
mark expressions themselves do not support escaping, instead they treat
|
|
backslashes as regular identifier characters."""
|
|
matcher = {r"\nfoo\n"}.__contains__
|
|
|
|
assert evaluate(r"\nfoo\n", matcher)
|
|
assert not evaluate(r"foo", matcher)
|
|
with pytest.raises(ParseError):
|
|
evaluate("\nfoo\n", matcher)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("expr", "column", "message"),
|
|
(
|
|
("(", 2, "expected not OR left parenthesis OR identifier; got end of input"),
|
|
(
|
|
" (",
|
|
3,
|
|
"expected not OR left parenthesis OR identifier; got end of input",
|
|
),
|
|
(
|
|
")",
|
|
1,
|
|
"expected not OR left parenthesis OR identifier; got right parenthesis",
|
|
),
|
|
(
|
|
") ",
|
|
1,
|
|
"expected not OR left parenthesis OR identifier; got right parenthesis",
|
|
),
|
|
(
|
|
"not",
|
|
4,
|
|
"expected not OR left parenthesis OR identifier; got end of input",
|
|
),
|
|
(
|
|
"not not",
|
|
8,
|
|
"expected not OR left parenthesis OR identifier; got end of input",
|
|
),
|
|
(
|
|
"(not)",
|
|
5,
|
|
"expected not OR left parenthesis OR identifier; got right parenthesis",
|
|
),
|
|
("and", 1, "expected not OR left parenthesis OR identifier; got and"),
|
|
(
|
|
"ident and",
|
|
10,
|
|
"expected not OR left parenthesis OR identifier; got end of input",
|
|
),
|
|
(
|
|
"ident and or",
|
|
11,
|
|
"expected not OR left parenthesis OR identifier; got or",
|
|
),
|
|
("ident ident", 7, "expected end of input; got identifier"),
|
|
),
|
|
)
|
|
def test_syntax_errors(expr: str, column: int, message: str) -> None:
|
|
with pytest.raises(ParseError) as excinfo:
|
|
evaluate(expr, lambda ident: True)
|
|
assert excinfo.value.column == column
|
|
assert excinfo.value.message == message
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"ident",
|
|
(
|
|
".",
|
|
"...",
|
|
":::",
|
|
"a:::c",
|
|
"a+-b",
|
|
r"\nhe\\l\lo\n\t\rbye",
|
|
"a/b",
|
|
"אבגד",
|
|
"aaאבגדcc",
|
|
"a[bcd]",
|
|
"1234",
|
|
"1234abcd",
|
|
"1234and",
|
|
"notandor",
|
|
"not_and_or",
|
|
"not[and]or",
|
|
"1234+5678",
|
|
"123.232",
|
|
"True",
|
|
"False",
|
|
"None",
|
|
"if",
|
|
"else",
|
|
"while",
|
|
),
|
|
)
|
|
def test_valid_idents(ident: str) -> None:
|
|
assert evaluate(ident, {ident: True}.__getitem__)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"ident",
|
|
(
|
|
"^",
|
|
"*",
|
|
"=",
|
|
"&",
|
|
"%",
|
|
"$",
|
|
"#",
|
|
"@",
|
|
"!",
|
|
"~",
|
|
"{",
|
|
"}",
|
|
'"',
|
|
"'",
|
|
"|",
|
|
";",
|
|
"←",
|
|
),
|
|
)
|
|
def test_invalid_idents(ident: str) -> None:
|
|
with pytest.raises(ParseError):
|
|
evaluate(ident, lambda ident: True)
|