text: """From https://docs.python.org/3/howto/descriptor.html#custom-validators"""
  from abc import ABC, abstractmethod
  
  
  class Validator(ABC):
      def __set_name__(self, owner, name):
          # The owner is class where descriptor is used
          self.private_name = '_' + name
  
      def __get__(self, obj, objtype=None):
          # obj is instance of class that uses Validator()
          # objtype is the class that uses Validator()
          return getattr(obj, self.private_name)
  
      def __set__(self, obj, value):
          self.validate(value)
          setattr(obj, self.private_name, value)
  
      @abstractmethod
      def validate(self, value):
          pass
  
  
  class String(Validator):
      """Verifies that a value is a str. Optional size restriction and format."""
  
      def __init__(self, minsize=None, maxsize=None, predicate=None):
          self.minsize = minsize
          self.maxsize = maxsize
          self.predicate = predicate
  
      def validate(self, value):
          if not isinstance(value, str):
              raise TypeError(f'Expected {value!r} to be a string')
          if self.minsize is not None and len(value) < self.minsize:
              raise ValueError(
                  f'Expected {value!r} to be no smaller than {self.minsize}'
                  )
          if self.maxsize is not None and len(value) > self.maxsize:
              raise ValueError(
                  f'Expected {value!r} to be no bigger than {self.maxsize!r}'
                  )
          if self.predicate is not None and not self.predicate(value):
              raise ValueError(
                  f'Expected {self.predicate} to be true for {value!r}'
                  )
  
  
  class Number(Validator):
      """Verifies that a value is an int or float. Optional range limit."""
      def __init__(
            self, minvalue=None, maxvalue=None):
          self.minvalue = minvalue
          self.maxvalue = maxvalue
  
      def validate(self, value):
          if not isinstance(value, (int, float)):
              raise TypeError(f'Expected {value!r} to be an int or a float')
          if self.minvalue is not None and value < self.minvalue:
              raise ValueError(
                  f'Expected {value!r} to be at least {self.minvalue!r}'
                  )
          if self.maxvalue is not None and value > self.maxvalue:
              raise ValueError(
                  f'Expected {value!r} to be no more than {self.maxvalue!r}'
                  )
  
  
  class OneOf(Validator):
      """Verifies that a value is one of a restricted set of options"""
      def __init__(self, *options):
          self.options = set(options)
  
      def validate(self, value):
          if value not in self.options:
              raise ValueError(
                  f'Expected {value!r} to be one of {self.options!r}'
                  )
  