text: # -*- coding: utf-8 -*-
  """Base module containing Base and helper classes for all of EasyVision algorithms.
  
  """
  
  from abc import ABCMeta, abstractmethod, abstractproperty
  from datetime import datetime
  
  try:
      from functools import lru_cache
  except ImportError:
      import functools
  
      class lru_cache(object):
          """Simple lru_cache analog for python 2.7 as python 3.x has it builtin"""
          def __init__(self, *args, **kwargs):
              self._cache = {}
  
          def __call__(self, func):
              @functools.wraps(func)
              def wrapper(*args, **kwargs):
                  arg = "".join(str(i) for i in args)
                  kwarg = "".join("{}={}".format(k, v) for k, v in kwargs.items())
                  key = (arg, kwarg)
                  if key in self._cache:
                      return self._cache[key]
  
                  result = func(*args, **kwargs)
                  self._cache[key] = result
                  return result
              return wrapper
  
  
  class NamedTupleExtendHelper(object):
      """NamedTupleExtendedHelper is a helper Mixin style class that enables to extend namedtuple derived classes with fields
  
      Implements:
          __repr__
              Builds a string representation of the object
          _make
              Make a new object from a sequence or iterable
          _replace
              Return a new object replacing specified fields with new values
  
      example
  
      .. code-block:: python
  
          MyBase = namedtuple('MyBase', 'a b')
  
          class MyNamedTuple(MyBase):
              _fields = MyBase._fields + ('c')
              __slots__ = ()
  
              def __new__(cls, *args, **kwargs):
                  return super(MyNamedTuple, self).__new__(*args, **kwargs)
  
              c = property(itemgetter(2), doc="Alias for field number 3")
      """
  
      def __repr__(self):
          return "{}({})".format(self.__class__.__name__, ", ".join("%s=%r" % field for field in zip(self._fields, self)))
  
      @classmethod
      def _make(cls, iterable, new=None, len=len):
          'Make a new object from a sequence or iterable'
          result = cls.__new__(cls, *iterable)
          if len(result) != len(cls._fields):
              raise TypeError('Expected %d arguments, got %d' % (len(cls._fields), len(result)))
          return result
  
      def _replace(self, **kwds):
          'Return a new object replacing specified fields with new values'
          result = self._make(map(kwds.pop, self._fields, self))
          if kwds:
              raise ValueError('Got unexpected field names: %r' % kwds.keys())
          return result
  
  
  # compatible with Python 2 *and* 3:
  ABC = ABCMeta('ABC', (object,), {'__slots__': ()})
  
  
  class FPSCounter(object):
      """Simple FPS Counter descriptor.
  
      setting a datetime object or None to this descriptor will update fps.
      getting this descriptor will return calculated fps.
      deleting this descriptor will reset the counter
      """
      def __init__(self, num_frames=30):
          self._frames = 0
          self._num_frames = num_frames
          self.fps = 0
          self._last_now = datetime.now()
  
      def update(self, now):
          if now is None:
              now = datetime.now()
          assert(isinstance(now, datetime))
          if self._frames % self._num_frames == 0:
              if self._frames != 0:
                  self.fps = self._num_frames / (now - self._last_now).total_seconds()
              self._last_now = now
          self._frames += 1
  
      def reset(self):
          self._frames = 0
          self.fps = 0
  
  
  class EasyVisionBase(ABC):
      """EasyVisionBase is an abstract class for all EasyVision algorithms
      Contains simple setup/release, debug/display_results, setup/release and context functionality.
  
      Uses __slots__ to conserve space, so derived classes can choose to continue using __slots__.
  
      Implements these:
          __enter__
              Implements a context manager
          __exit__
              Implements a context manager
          __next__
              Implements an iterator for Python 3.7
          __iter__
              Implements an iterator
          __len__
              Allows to receive a preliminary number of frames available
          __getattr__
              Catches {ClassName}_fps and returns current fps
          update_fps
              Updates fps counter
          name
              Returns Derived Class name
  
      Abstract methods:
          next
              For Python 2.7 compatibility. Implements iterator.
          description
              Must return a brief description of the algorithm.
          setup
              Must allocate resources.
          release
              Must deallocate resources.
  
      Optional methods:
          debug_changed
              Being called when debug property value is changed
          display_results_changed
              Being called when display_results property value is changed
  
      """
  
      __slots__ = ('_debug', '_display_results', '__setup_called', '__fps_counter')
  
      def __init__(self, debug=False, display_results=False, fps_num_frames=30, *args, **kwargs):
          """Initializes the instance. super must be called after all the derived class
          initialization is complete.
  
          :param debug: Indicates whether this algorithm should output debug info
          :param display_results: Indicates whether this algorithm should output results
          """
          self._debug = False
          self._display_results = False
          self.__setup_called = False
          self.debug = debug
          self.display_results = display_results
          self.__fps_counter = FPSCounter(fps_num_frames)
          super(EasyVisionBase, self).__init__()
  
      def __enter__(self):
          """Makes derived class into a context manager. Will call setup()."""
          self.setup()
          return self
  
      def __exit__(self, type, value, traceback):
          """Makes derived class into a context manager. Will call release()."""
          self.release()
  
      def __next__(self):
          """For Python 3.x support"""
          return self.next()
  
      def __iter__(self):
          """Makes derived class into iterable."""
          return self
  
      def __getattr__(self, name):
          if name == "%s_fps" % self.__class__.__name__:
              return self.__fps_counter.fps
          else:
              raise AttributeError("Unsupported dynamic attribute: %s" % name)
  
      def update_fps(self, now=None):
          """Updates FPS counter
  
          :param now: optional datetime object. If set to None will use datetime.now() instead.
          :return: None
          """
          self.__fps_counter.update(now)
  
      @abstractmethod
      def __len__(self):
          """Abstract method. Used for len(MyAlgorithm). Should return the number of images/frames/computations available.
          Will return negative numbers or zero for freerun capturing devices such as video camera.
          """
          pass
  
      @abstractmethod
      def next(self):
          """Abstract method. Legacy method for Python 2.7"""
          assert(self.__setup_called)
  
      @abstractmethod
      def setup(self):
          """Setup method that should be called before any call to __next__/capture/compute.
  
          This method should be used for resource allocation, algorithm initialization, opening of devices, etc.
          All derived classes must implement this method and call it using super after all the initialization is complete.
          This is being handled if using the algorithm with "with" statement.
          """
          assert(not self.__setup_called)
          self.__setup_called = True
          self.__fps_counter.reset()
  
      @abstractmethod
      def release(self):
          """Release method that should be called in order to release allocated resources.
  
          This method should be used for resource deallocation and cleaning up.
          All derived classes must implement this method and call it using super after all the deallocation is complete.
          This is being handled if using the algorithm with "with" statement.
          """
          assert(self.__setup_called)
          self.__setup_called = False
  
      @property
      def name(self):
          """Returns the name of the class."""
          return self.__class__.__name__
  
      @abstractproperty
      def description(self):
          """Abstract property. Should return a brief description of the algorithm."""
          pass
  
      @property
      def debug(self):
          """Property specifying whether to perform some outpuf for debugging purposes"""
          return self._debug
  
      @property
      def display_results(self):
          """Property specifying whether to display intermediate results"""
          return self._display_results
  
      @debug.setter
      def debug(self, value):
          """This property will call debug_changed if it finds one in the derived class."""
          lastdebug, self._debug = self._debug, value
          if lastdebug != value and hasattr(self, 'debug_changed'):
              self.debug_changed(lastdebug, value)
  
      @display_results.setter
      def display_results(self, value):
          """This property will call display_results_changed if it finds one in the derived class."""
          lastdisplay, self._display_results = self._display_results, value
          if lastdisplay != value and hasattr(self, 'display_results_changed'):
              self.display_results_changed(lastdisplay, value)
  