# 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
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from __future__ import annotations





def _strip_first_underscore(name: str) -> str:
    return name[1:] if name.startswith('_') else name


class Config:
    # todo: add hooks:
    #            hook_wait_command: Callable[[Command], None]
    #            hook_...
    #       consider impementing some hooks as generators...
    #       hook generally should allow to do some processing before and after e.g. command
    #       think on better name depending on hook type (generator vs standard fn)
    def __init__(
        self,
        driver: Optional[WebDriver] = None,

# 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

# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from __future__ import annotations





def _strip_first_underscore(name: str) -> str:
    return name[1:] if name.startswith('_') else name


class Config:
    # todo: add hooks:
    #            hook_wait_command: Callable[[Command], None]
    #            hook_...
    #       consider impementing some hooks as generators...
    #       hook generally should allow to do some processing before and after e.g. command
    #       think on better name depending on hook type (generator vs standard fn)
    def __init__(
        self,
        driver: Optional[WebDriver] = None,

        timeout: int = 4,