docs: def validator(names_or_instance, names=None, func=None):
      """Specify a callback function to fire on class validation OR property set
  
      This function has two modes of operation:
  
      1. Registering callback functions that validate Property values when
         they are set, before the change is saved to the HasProperties instance.
         This mode is very similar to the :code:`observer` function.
      2. Registering callback functions that fire only when the HasProperties
         :code:`validate` method is called. This allows for cross-validation
         of Properties that should only fire when all required Properties are
         set.
  
      **Mode 1:**
  
      Validator functions on a HasProperties class fire on set but before the
      observed Property or Properties have been changed (unlike observer
      functions that fire after the value has been changed).
  
      You can use this method as a decorator inside a HasProperties class
  
      .. code::
  
          @properties.validator('variable_name')
          def callback_function(self, change):
              print(change)
  
      or you can use it to register a function to a single HasProperties
      instance
  
      .. code::
  
          properties.validator(my_has_props, 'variable_name', callback_function)
  
      The variable name must refer to a Property name on the HasProperties
      class. A list of Property names may also be used; the same
      callback function will fire when any of these Properties change. Also,
      :class:`properties.everything <properties.utils.Sentinel>` may be
      specified instead of the variable name. In that case, the callback
      function will fire when any Property changes.
  
      The callback function must take two arguments. The first is the
      HasProperties instance; the second is the change notification dictionary.
      This dictionary contains:
  
      * 'name' - the name of the changed Property
      * 'previous' - the value of the Property prior to change (this will be
        :code:`properties.undefined` if the value was not previously set)
      * 'value' - the new value of the Property (this will be
        :code:`properties.undefined` if the value is deleted)
      * 'mode' - the mode of the change; for validators, this is 'validate'
  
      **Mode 2:**
  
      When used as a decorator without arguments (i.e. called directly on a
      HasProperties method), the decorated method is registered as a class
      validator. These methods execute only when :code:`validate()` is called
      on the HasProperties instance.
  
      .. code::
  
          @properties.validator
          def validation_method(self):
              print('validating instance of {}'.format(self.__class__))
  
      The decorated function must only take one argument, the HasProperties
      instance.
      """
  
      if names is None and func is None:
          if callable(names_or_instance):
              return ClassValidator(names_or_instance)
          return Observer(names_or_instance, 'validate')
      val = Observer(names, 'validate')(func)
      _set_listener(names_or_instance, val)
      return val