# Path: selene/core/exceptions.py
# class TimeoutException(AssertionError):
#     def __init__(self, msg=None):
#         self.msg = msg
# 
#     def __str__(self):
#         exception_msg = "Message: %s\n" % self.msg
#         return exception_msg
# 
# Path: selene/core/wait.py
# class Wait(Generic[E]):
# 
#     # todo: provide the smallest possible timeout default, something like 1ms
#     def __init__(
#         self,
#         entity: E,
#         at_most: int,
#         or_fail_with: Optional[Callable[[TimeoutException], Exception]] = None,
#     ):
#         self._entity = entity
#         self._timeout = at_most
#         self._hook_failure = or_fail_with or identity
# 
#     def at_most(self, timeout: int) -> Wait[E]:
#         return Wait(self._entity, timeout, self._hook_failure)
# 
#     def or_fail_with(
#         self, hook_failure: Optional[Callable[[TimeoutException], Exception]]
#     ) -> Wait[E]:
# 
#         return Wait(self._entity, self._timeout, hook_failure)
# 
#     @property
#     def hook_failure(
#         self,
#     ) -> Optional[Callable[[TimeoutException], Exception]]:
#         # todo: hook_failure or failure_hook?
#         return self._hook_failure
# 
#     # todo: consider renaming to `def to(...)`, though will sound awkward when wait.to(condition)
#     def for_(self, fn: Callable[[E], R]) -> R:
#         finish_time = time.time() + self._timeout
# 
#         while True:
#             try:
#                 return fn(self._entity)
#             except Exception as reason:
#                 if time.time() > finish_time:
# 
#                     reason_message = str(reason)
# 
#                     reason_string = '{name}: {message}'.format(
#                         name=reason.__class__.__name__, message=reason_message
#                     )
#                     # todo: think on how can we improve logging failures in selene, e.g. reverse msg and stacktrace
#                     # stacktrace = getattr(reason, 'stacktrace', None)
#                     timeout = self._timeout
#                     entity = self._entity
# 
#                     failure = TimeoutException(
#                         f'''
# 
# Timed out after {timeout}s, while waiting for:
# {entity}.{fn}
# 
# Reason: {reason_string}'''
#                     )
# 
#                     raise self._hook_failure(failure)
# 
#     def until(self, fn: Callable[[E], R]) -> bool:
#         try:
#             self.for_(fn)
#             return True
#         except TimeoutException:
#             return False
# 
#     # todo: do we really need these aliases?
#     def command(self, description: str, fn: Callable[[E], None]) -> None:
#         self.for_(Command(description, fn))
# 
#     def query(self, description: str, fn: Callable[[E], R]) -> R:
#         return self.for_(Query(description, fn))


from typing import Callable, Optional
from selene.core.exceptions import TimeoutException
from selenium.webdriver.remote.webdriver import WebDriver
from selene.core.wait import Wait
              taking into account that it does not work for mobile
              while we want the core of Selene to be versatile as much as psbl.
              probably we should move it to support.shared.config only
        '''

    def as_dict(self, skip_empty=True):
        return {
            _strip_first_underscore(k): v
            for k, v in self.__dict__.items()
            if not (skip_empty and v is None) and not k.startswith('__')
        }

    def with_(self, config: Config = None, **config_as_kwargs) -> Config:
        return self.__class__(
            **{
                **self.as_dict(),
                **(config.as_dict() if config else {}),
                **config_as_kwargs,
            }
        )

    @property
    def driver(self) -> Optional[WebDriver]:
        return self._driver

    @property
    def timeout(self) -> int:
        return self._timeout

    @property

# Path: selene/core/exceptions.py
# class TimeoutException(AssertionError):
#     def __init__(self, msg=None):
#         self.msg = msg
# 
#     def __str__(self):
#         exception_msg = "Message: %s\n" % self.msg
#         return exception_msg
# 
# Path: selene/core/wait.py
# class Wait(Generic[E]):
# 
#     # todo: provide the smallest possible timeout default, something like 1ms
#     def __init__(
#         self,
#         entity: E,
#         at_most: int,
#         or_fail_with: Optional[Callable[[TimeoutException], Exception]] = None,
#     ):
#         self._entity = entity
#         self._timeout = at_most
#         self._hook_failure = or_fail_with or identity
# 
#     def at_most(self, timeout: int) -> Wait[E]:
#         return Wait(self._entity, timeout, self._hook_failure)
# 
#     def or_fail_with(
#         self, hook_failure: Optional[Callable[[TimeoutException], Exception]]
#     ) -> Wait[E]:
# 
#         return Wait(self._entity, self._timeout, hook_failure)
# 
#     @property
#     def hook_failure(
#         self,
#     ) -> Optional[Callable[[TimeoutException], Exception]]:
#         # todo: hook_failure or failure_hook?
#         return self._hook_failure
# 
#     # todo: consider renaming to `def to(...)`, though will sound awkward when wait.to(condition)
#     def for_(self, fn: Callable[[E], R]) -> R:
#         finish_time = time.time() + self._timeout
# 
#         while True:
#             try:
#                 return fn(self._entity)
#             except Exception as reason:
#                 if time.time() > finish_time:
# 
#                     reason_message = str(reason)
# 
#                     reason_string = '{name}: {message}'.format(
#                         name=reason.__class__.__name__, message=reason_message
#                     )
#                     # todo: think on how can we improve logging failures in selene, e.g. reverse msg and stacktrace
#                     # stacktrace = getattr(reason, 'stacktrace', None)
#                     timeout = self._timeout
#                     entity = self._entity
# 
#                     failure = TimeoutException(
#                         f'''
# 
# Timed out after {timeout}s, while waiting for:
# {entity}.{fn}
# 
# Reason: {reason_string}'''
#                     )
# 
#                     raise self._hook_failure(failure)
# 
#     def until(self, fn: Callable[[E], R]) -> bool:
#         try:
#             self.for_(fn)
#             return True
#         except TimeoutException:
#             return False
# 
#     # todo: do we really need these aliases?
#     def command(self, description: str, fn: Callable[[E], None]) -> None:
#         self.for_(Command(description, fn))
# 
#     def query(self, description: str, fn: Callable[[E], R]) -> R:
#         return self.for_(Query(description, fn))


# Path: selene/core/configuration.py
from typing import Callable, Optional
from selene.core.exceptions import TimeoutException
from selenium.webdriver.remote.webdriver import WebDriver
from selene.core.wait import Wait

              taking into account that it does not work for mobile
              while we want the core of Selene to be versatile as much as psbl.
              probably we should move it to support.shared.config only
        '''

    def as_dict(self, skip_empty=True):
        return {
            _strip_first_underscore(k): v
            for k, v in self.__dict__.items()
            if not (skip_empty and v is None) and not k.startswith('__')
        }

    def with_(self, config: Config = None, **config_as_kwargs) -> Config:
        return self.__class__(
            **{
                **self.as_dict(),
                **(config.as_dict() if config else {}),
                **config_as_kwargs,
            }
        )

    @property
    def driver(self) -> Optional[WebDriver]:
        return self._driver

    @property
    def timeout(self) -> int:
        return self._timeout

    @property

    def hook_wait_failure(self) -> Callable[[TimeoutException], Exception]: