diff --git a/CHANGELOG.md b/CHANGELOG.md index ee2f832b..043724e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -212,8 +212,6 @@ should we even refactor out them from Condition and move to Match only? ### TODO: rename all conditions inside match.py so match can be fully used instead be + have #530 -### TODO: consider implementing either_res_or just for demo... - ### Deprecated conditions - `be.present` in favor of `be.present_in_dom` diff --git a/selene/common/fp.py b/selene/common/fp.py index eef2314d..181c3b74 100644 --- a/selene/common/fp.py +++ b/selene/common/fp.py @@ -20,8 +20,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. import functools -import inspect -from typing import TypeVar, Callable, Any, Tuple, Optional +from typing_extensions import TypeVar, Callable, Any, Tuple, Optional # T = TypeVar('T', bound=Callable[..., Any]) T = TypeVar('T') @@ -63,6 +62,23 @@ def identity(it: T) -> T: return it +# todo: what about decorator? like: +# > res, maybe_failure = _either(res=func, or_=Exception)(*args, **kwargs) +# over +# > res, maybe_failure = _either_res_or(Exception, func, *args, **kwargs) +# todo: support exception_type as tuple of exception types +# todo: make generic-aware version of this function, +# that knows the type of failure +# that knows the type of fn res +def _either_res_or( + exception_type, fn: Callable, /, *args, **kwargs +) -> Tuple[Any, Optional[Exception]]: + try: + return fn(*args, **kwargs), None + except exception_type as failure: + return ..., failure + + def pipe(*functions) -> Optional[Callable[[Any], Any]]: """ pipes functions one by one in the provided order diff --git a/selene/core/configuration.py b/selene/core/configuration.py index f61a6244..43ed2c20 100644 --- a/selene/core/configuration.py +++ b/selene/core/configuration.py @@ -1427,30 +1427,21 @@ def _inject_screenshot_and_page_source_pre_hooks(self, hook): # TODO: consider moving hooks to class methods accepting config as argument # or refactor somehow to eliminate all times defining hook fns def save_and_log_screenshot(error: TimeoutException) -> Exception: - path = None - failure_reason: WebDriverException | None = None - try: - # todo: consider changing _save_screenshot_strategy to be Either-like - # TODO: consider refactoring to be Either-monad-like - # so we can eliminate try..except clauses - # > path, maybe_failure = either_res_or( - # > WebDriverException - # > self._save_screenshot_strategy, - # > self, - # > ) - path = self._save_screenshot_strategy(self) # type: ignore - except WebDriverException as reason: - failure_reason = reason + # todo: consider changing _save_screenshot_strategy to be Either-like + # > path, maybe_failure = self._save_screenshot_strategy(self) + path, maybe_failure = fp._either_res_or( + WebDriverException, self._save_screenshot_strategy, self + ) return TimeoutException( error.msg # todo: should we just skip logging screenshot at all when failure? + '\nScreenshot: ' + ( self._format_path_as_uri(path) - if path and not failure_reason + if path and not maybe_failure else 'cannot be saved because of: {name}: {message}'.format( - name=failure_reason.__class__.__name__, - message=getattr(failure_reason, "msg", str(failure_reason)), + name=maybe_failure.__class__.__name__, + message=getattr(maybe_failure, "msg", str(maybe_failure)), ) ) ) @@ -1464,23 +1455,18 @@ def save_and_log_page_source(error: TimeoutException) -> Exception: else self._generate_filename(suffix='.html') ) - path = None - failure_reason: WebDriverException | None = None - try: - # TODO: consider refactoring to be Either-monad-like - # so we can eliminate try..except clauses - path = self._save_page_source_strategy(self, filename) - except WebDriverException as reason: - failure_reason = reason + path, maybe_failure = fp._either_res_or( + WebDriverException, self._save_screenshot_strategy, self + ) return TimeoutException( error.msg + '\nPageSource: ' + ( self._format_path_as_uri(path) - if path and not failure_reason + if path and not maybe_failure else 'cannot be saved because of: {name}: {message}'.format( - name=failure_reason.__class__.__name__, - message=getattr(failure_reason, "msg", str(failure_reason)), + name=maybe_failure.__class__.__name__, + message=getattr(maybe_failure, "msg", str(maybe_failure)), ) ) )