pyracmon.testing.util API documentation

# Module `pyracmon.testing.util`

Expand source code

    from collections.abc import Callable
    from contextvars import ContextVar
    from datetime import datetime, date, time, timedelta
    from typing import Any, Optional
    from ..config import PyracmonConfiguration, default_config, contextualConfiguration

    config: ContextVar[PyracmonConfiguration] = ContextVar('config', default=contextualConfiguration(lambda: config, default_config()))

    def default_test_config() -> PyracmonConfiguration:
        return config.get()

    class Matcher:
        def __init__(self):
            self.invert = False

        def __invert__(self):
            self.invert = True
            return self

        def __and__(self, another):
            return CompositeMatcher(self, another, True)

        def __or__(self, another):
            return CompositeMatcher(self, another, False)

        def match(self, actual):
            return self._match(actual) ^ self.invert

        def _match(self, actual):
            raise NotImplementedError()

    class CompositeMatcher(Matcher):
        def __init__(self, m1, m2, and_=True):
            super().__init__()
            self.m1 = m1
            self.m2 = m2
            self.and_ = and_

        def _match(self, actual):
            if self.and_:
                return self.m1.match(actual) and self.m2.match(actual)
            else:
                return self.m1.match(actual) or self.m2.match(actual)

    class Near(Matcher):
        def __init__(self, expected, negative=None, positive=None, **kwargs):
            super().__init__()
            self.expected = expected
            self.negative = negative
            self.positive = positive
            self.kwargs = kwargs

        def _margin(self, t):
            neg = self.negative or 0
            pos = self.positive or 0

            if issubclass(t, (int, float)):
                return (self.expected + neg, self.expected + pos)
            elif issubclass(t, (datetime, date)):
                delta_args = {k:v for k, v in self.kwargs.items() if k in {
                    'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds',
                }}
                delta = timedelta(**delta_args)
                return (self.expected + neg * delta, self.expected + pos * delta)
            else:
                raise ValueError(f"Margin for {t} is not supported.")

        def _match(self, actual):
            if self.negative is None and self.positive is None:
                return actual == self.expected
            else:
                low, high = self._margin(type(actual))
                return low <= actual and actual <= high

    def near(expected: Any, negative: Optional[Any] = None, positive: Optional[Any] = None, **kwargs: Any) -> Matcher:
        """
        Creates a matcher to check actual value is in a range.

        Comparison between the value and given range depends on its type.

        Args:
            expected: Expected value.
            negative: Margin of negative direction.
            positive: Margin of positive direction.
            kwargs: Keyword arguments to create marginal values.
        Returns:
            Created matcher.
        """
        if isinstance(expected, datetime):
            if all(k not in kwargs for k in ('weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds')):
                kwargs.update(**default_test_config().timedelta_unit)
        return Near(expected, negative, positive, **kwargs)

    class let(Matcher):
        def __init__(self, pred: Callable[[Any], bool]) -> None:
            """
            Creates a matcher which applies the predicate function to actual value and checks its returning value is ``True`` .

            Args:
                pred: A predicate function.
            Returns:
                Created matcher.
            """
            super().__init__()
            self.pred = pred

        def _match(self, actual):
            return self.pred(actual)

    class one_of(Matcher):
        def __init__(self, *candidates) -> None:
            """
            Creates a matcher which checks whether the actual value matches one of candidate values.

            Args:
                candidates: Candidate values.
            Returns:
                Created matcher.
            """
            super().__init__()
            self.candidates = candidates

        def _match(self, actual):
            for c in self.candidates:
                if isinstance(c, Matcher):
                    if c.match(actual):
                        return True
                elif c == actual:
                    return True
            return False

## Functions

`def default_test_config() ‑> PyracmonConfiguration`
  Expand source code

      def default_test_config() -> PyracmonConfiguration:
          return config.get()

`def near(expected: Any, negative: Optional[Any] = None, positive: Optional[Any] = None, **kwargs: Any) ‑> Matcher`

  Creates a matcher to check actual value is in a range.

  Comparison between the value and given range depends on its type.

  ## Args

  `expected`
    Expected value.
  `negative`
    Margin of negative direction.
  `positive`
    Margin of positive direction.
  `kwargs`
    Keyword arguments to create marginal values.

  ## Returns

  Created matcher.

  Expand source code

      def near(expected: Any, negative: Optional[Any] = None, positive: Optional[Any] = None, **kwargs: Any) -> Matcher:
          """
          Creates a matcher to check actual value is in a range.

          Comparison between the value and given range depends on its type.

          Args:
              expected: Expected value.
              negative: Margin of negative direction.
              positive: Margin of positive direction.
              kwargs: Keyword arguments to create marginal values.
          Returns:
              Created matcher.
          """
          if isinstance(expected, datetime):
              if all(k not in kwargs for k in ('weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds')):
                  kwargs.update(**default_test_config().timedelta_unit)
          return Near(expected, negative, positive, **kwargs)

## Classes

`class CompositeMatcher (m1, m2, and_=True)`
  Expand source code

      class CompositeMatcher(Matcher):
          def __init__(self, m1, m2, and_=True):
              super().__init__()
              self.m1 = m1
              self.m2 = m2
              self.and_ = and_

          def _match(self, actual):
              if self.and_:
                  return self.m1.match(actual) and self.m2.match(actual)
              else:
                  return self.m1.match(actual) or self.m2.match(actual)

  ### Ancestors

  - Matcher
`class Matcher`
  Expand source code

      class Matcher:
          def __init__(self):
              self.invert = False

          def __invert__(self):
              self.invert = True
              return self

          def __and__(self, another):
              return CompositeMatcher(self, another, True)

          def __or__(self, another):
              return CompositeMatcher(self, another, False)

          def match(self, actual):
              return self._match(actual) ^ self.invert

          def _match(self, actual):
              raise NotImplementedError()

  ### Subclasses

  - CompositeMatcher
  - Near
  - let
  - one_of

  ### Methods

  `def match(self, actual)`
    Expand source code

        def match(self, actual):
            return self._match(actual) ^ self.invert

`class Near (expected, negative=None, positive=None, **kwargs)`
  Expand source code

      class Near(Matcher):
          def __init__(self, expected, negative=None, positive=None, **kwargs):
              super().__init__()
              self.expected = expected
              self.negative = negative
              self.positive = positive
              self.kwargs = kwargs

          def _margin(self, t):
              neg = self.negative or 0
              pos = self.positive or 0

              if issubclass(t, (int, float)):
                  return (self.expected + neg, self.expected + pos)
              elif issubclass(t, (datetime, date)):
                  delta_args = {k:v for k, v in self.kwargs.items() if k in {
                      'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds',
                  }}
                  delta = timedelta(**delta_args)
                  return (self.expected + neg * delta, self.expected + pos * delta)
              else:
                  raise ValueError(f"Margin for {t} is not supported.")

          def _match(self, actual):
              if self.negative is None and self.positive is None:
                  return actual == self.expected
              else:
                  low, high = self._margin(type(actual))
                  return low <= actual and actual <= high

  ### Ancestors

  - Matcher
`class let (pred: collections.abc.Callable[[typing.Any], bool])`

  Creates a matcher which applies the predicate function to actual value and checks its returning value is `True` .

  ## Args

  `pred`
    A predicate function.

  ## Returns

  Created matcher.

  Expand source code

      class let(Matcher):
          def __init__(self, pred: Callable[[Any], bool]) -> None:
              """
              Creates a matcher which applies the predicate function to actual value and checks its returning value is ``True`` .

              Args:
                  pred: A predicate function.
              Returns:
                  Created matcher.
              """
              super().__init__()
              self.pred = pred

          def _match(self, actual):
              return self.pred(actual)

  ### Ancestors

  - Matcher
`class one_of (*candidates)`

  Creates a matcher which checks whether the actual value matches one of candidate values.

  ## Args

  `candidates`
    Candidate values.

  ## Returns

  Created matcher.

  Expand source code

      class one_of(Matcher):
          def __init__(self, *candidates) -> None:
              """
              Creates a matcher which checks whether the actual value matches one of candidate values.

              Args:
                  candidates: Candidate values.
              Returns:
                  Created matcher.
              """
              super().__init__()
              self.candidates = candidates

          def _match(self, actual):
              for c in self.candidates:
                  if isinstance(c, Matcher):
                      if c.match(actual):
                          return True
                  elif c == actual:
                      return True
              return False

  ### Ancestors

  - Matcher