Skip to content

Commit

Permalink
enabling ruff FBT (flake8-boolean-trap) and addressing ScreenPyHQ#44
Browse files Browse the repository at this point in the history
  • Loading branch information
bandophahita committed Feb 7, 2024
1 parent ac934bf commit e169e1f
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 6 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ select = [
"ERA", # eradicate
"F", # Pyflakes
"FA", # flake8-future-annotations
# "FBT", # flake8-boolean-trap
"FBT", # flake8-boolean-trap
"FIX", # flake8-fixme
"FLY", # flynt
"I", # isort
Expand Down
6 changes: 5 additions & 1 deletion screenpy_selenium/actions/enter.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from screenpy.pacing import aside, beat
from selenium.common.exceptions import WebDriverException

from ..common import pos_args_deprecated
from ..speech_tools import KEY_NAMES

if TYPE_CHECKING:
Expand Down Expand Up @@ -152,7 +153,10 @@ def add_to_chain(
for key in self.following_keys:
send_keys(key)

def __init__(self: SelfEnter, text: str, mask: bool = False) -> None:
@pos_args_deprecated("mask")
def __init__(
self: SelfEnter, text: str, mask: bool = False # noqa: FBT001, FBT002
) -> None:
self.text = text
self.target = None
self.following_keys = []
Expand Down
8 changes: 7 additions & 1 deletion screenpy_selenium/actions/hold_down.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from screenpy.pacing import beat
from selenium.webdriver.common.keys import Keys

from ..common import pos_args_deprecated
from ..speech_tools import KEY_NAMES

if TYPE_CHECKING:
Expand Down Expand Up @@ -88,7 +89,12 @@ def add_to_chain(
msg = "HoldDown must be told what to hold down."
raise UnableToAct(msg)

def __init__(self: SelfHoldDown, key: str | None = None, lmb: bool = False) -> None:
@pos_args_deprecated("lmb")
def __init__(
self: SelfHoldDown,
key: str | None = None,
lmb: bool = False, # noqa: FBT001, FBT002
) -> None:
self.key = key
self.lmb = lmb
self.target = None
Expand Down
8 changes: 7 additions & 1 deletion screenpy_selenium/actions/release.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from screenpy.pacing import beat
from selenium.webdriver.common.keys import Keys

from ..common import pos_args_deprecated
from ..speech_tools import KEY_NAMES

if TYPE_CHECKING:
Expand Down Expand Up @@ -75,7 +76,12 @@ def add_to_chain(self: SelfRelease, _: Actor, the_chain: ActionChains) -> None:
msg = "Release must be told what to release."
raise UnableToAct(msg)

def __init__(self: SelfRelease, key: str | None = None, lmb: bool = False) -> None:
@pos_args_deprecated("lmb")
def __init__(
self: SelfRelease,
key: str | None = None,
lmb: bool = False, # noqa: FBT001, FBT002
) -> None:
self.key = key
self.lmb = lmb
self.description = "LEFT MOUSE BUTTON" if lmb else KEY_NAMES[key]
Expand Down
42 changes: 42 additions & 0 deletions screenpy_selenium/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""module to hold shared objects."""
from __future__ import annotations

import warnings
from functools import wraps
from typing import TYPE_CHECKING, Callable, ParamSpec, TypeVar

if TYPE_CHECKING:
P = ParamSpec("P")
T = TypeVar("T")
Function = Callable[P, T]


def pos_args_deprecated(*keywords: str) -> Function:
"""Warn users which positional arguments should be called via keyword."""

def deprecated(func: Function) -> Function:
argnames = func.__code__.co_varnames[: func.__code__.co_argcount]
i = min([argnames.index(kw) for kw in keywords])
kw_argnames = argnames[i:]

@wraps(func)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> Function:
# call the function first, to make sure the signature matches
ret_value = func(*args, **kwargs)

args_that_should_be_kw = args[i:]
if args_that_should_be_kw:
posargnames = ", ".join(kw_argnames)

msg = (
f"Warning: positional arguments `{posargnames}` for "
f"`{func.__qualname__}` are deprecated. "
f"Please use keyword arguments instead."
)
warnings.warn(msg, DeprecationWarning, stacklevel=2)

return ret_value

return wrapper

return deprecated
7 changes: 6 additions & 1 deletion screenpy_selenium/questions/selected.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
from screenpy.pacing import beat
from selenium.webdriver.support.ui import Select as SeleniumSelect

from ..common import pos_args_deprecated

if TYPE_CHECKING:
from screenpy import Actor

Expand Down Expand Up @@ -90,6 +92,9 @@ def answered_by(self: SelfSelected, the_actor: Actor) -> str | list[str]:
return [e.text for e in select.all_selected_options]
return select.first_selected_option.text

def __init__(self: SelfSelected, target: Target, multi: bool = False) -> None:
@pos_args_deprecated("multi")
def __init__(
self: SelfSelected, target: Target, multi: bool = False # noqa: FBT001, FBT002
) -> None:
self.target = target
self.multi = multi
7 changes: 6 additions & 1 deletion screenpy_selenium/questions/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

from screenpy.pacing import beat

from ..common import pos_args_deprecated

if TYPE_CHECKING:
from screenpy import Actor

Expand Down Expand Up @@ -70,6 +72,9 @@ def answered_by(self: SelfText, the_actor: Actor) -> str | list[str]:
return [e.text for e in self.target.all_found_by(the_actor)]
return self.target.found_by(the_actor).text

def __init__(self: SelfText, target: Target, multi: bool = False) -> None:
@pos_args_deprecated("multi")
def __init__(
self: SelfText, target: Target, multi: bool = False # noqa: FBT001, FBT002
) -> None:
self.target = target
self.multi = multi
40 changes: 40 additions & 0 deletions tests/test_actions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import warnings
from typing import cast
from unittest import mock

Expand Down Expand Up @@ -435,6 +436,19 @@ def new_method(self) -> bool:

assert SubEnter.the_text("blah").new_method() is True

def test_positional_arg_warns(self) -> None:
with pytest.warns(DeprecationWarning):
Enter("", True)

def test_keyword_arg_does_not_warn(self) -> None:
with warnings.catch_warnings():
warnings.simplefilter("error")
Enter.the_secret("")

with warnings.catch_warnings():
warnings.simplefilter("error")
Enter("", mask=True)


class TestEnter2FAToken:
def test_can_be_instantiated(self) -> None:
Expand Down Expand Up @@ -619,6 +633,19 @@ def new_method(self) -> bool:

assert SubHoldDown.left_mouse_button().new_method() is True

def test_positional_arg_warns(self) -> None:
with pytest.warns(DeprecationWarning):
HoldDown(Keys.LEFT_ALT, True)

def test_keyword_arg_does_not_warn(self) -> None:
with warnings.catch_warnings():
warnings.simplefilter("error")
HoldDown.left_mouse_button()

with warnings.catch_warnings():
warnings.simplefilter("error")
HoldDown(lmb=True)


class TestMoveMouse:
def test_can_be_instantiated(self) -> None:
Expand Down Expand Up @@ -884,6 +911,19 @@ def new_method(self) -> bool:

assert SubRelease.left_mouse_button().new_method() is True

def test_positional_arg_warns(self) -> None:
with pytest.warns(DeprecationWarning):
Release(Keys.LEFT_ALT, True)

def test_keyword_arg_does_not_warn(self) -> None:
with warnings.catch_warnings():
warnings.simplefilter("error")
Release.left_mouse_button()

with warnings.catch_warnings():
warnings.simplefilter("error")
Release(lmb=True)


class TestRespondToThePrompt:
def test_can_be_instantiated(self) -> None:
Expand Down
27 changes: 27 additions & 0 deletions tests/test_questions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import warnings
from unittest import mock

import pytest
Expand Down Expand Up @@ -309,6 +310,19 @@ def test_describe(self) -> None:
Selected(TARGET).describe() == f"The selected option(s) from the {TARGET}."
)

def test_positional_arg_warns(self) -> None:
with pytest.warns(DeprecationWarning):
Selected(TARGET, True)

def test_keyword_arg_does_not_warn(self) -> None:
with warnings.catch_warnings():
warnings.simplefilter("error")
Selected.options_from_the(TARGET)

with warnings.catch_warnings():
warnings.simplefilter("error")
Selected(TARGET, multi=True)


class TestText:
def test_can_be_instantiated(self) -> None:
Expand Down Expand Up @@ -379,3 +393,16 @@ def test_ask_for_text_of_the_alert(self, Tester: Actor) -> None:

def test_describe(self) -> None:
assert TextOfTheAlert().describe() == "The text of the alert."

def test_positional_arg_warns(self) -> None:
with pytest.warns(DeprecationWarning):
Text(TARGET, True)

def test_keyword_arg_does_not_warn(self) -> None:
with warnings.catch_warnings():
warnings.simplefilter("error")
Text.of_all(TARGET)

with warnings.catch_warnings():
warnings.simplefilter("error")
Text(TARGET, multi=True)

0 comments on commit e169e1f

Please sign in to comment.