docs: def add(self, name, command_path=None, **params):
          """
          Add a command to the list of commands being watched.  "name"
          should be a unique value that can be used as a dictionary
          index.  It is typically, but not necessarily, the command
          name.  The value is returned when the command or modules for
          the command change and is used with the remove() method.
  
          "command_path" is a path to the command executable, which
          must be a python program.  If it includes no directory elements,
          the "path" param or the system PATH will be used to find the
          command's full path.
  
          If "command_path" is not specified, "name" will be used.  An
          exception is raised if the program file cannot be found.
      """
          log = self._getparam('log', self._discard, **params)
          module_path = self._getparam('module_path', sys.path, **params)
  
          if module_path:
              if type(module_path) is not list:
                  if module_path.find(os.pathsep) >= 0:
                      module_path = module_path.split(os.pathsep)
                  else:
                      module_path = [module_path]
          else:
              module_path = None
          log.debug("module_path: %s", str(module_path))
  
          if not command_path:
              command_path = name
  
          command = None
          if os.path.basename(command_path) == command_path:
              #  When the command has no directory elements (ie, the base name is the same as the name)
              #  search the system path.  Prefer a file that is both readable and executable but failing
              #  that, accept one that is only readable.
              #
              path_list = self._getparam('path', os.environ['PATH'], **params).split(os.pathsep)
              for dir in path_list:
                  path = os.path.join(dir, command_path)
                  try:
                      if os.access(path, os.R_OK|os.X_OK):
                          command = path
                          break
                  except:
                      continue
              if command is None:
                  for dir in path_list:
                      path = os.path.join(dir, command_path)
                      try:
                          if os.access(path, os.R_OK):
                              command = path
                              break
                      except:
                          continue
          if command is None and os.access(command_path, os.R_OK):
              command = command_path
          if command is None:
              log.error("failed to find %r", command_path)
              raise Exception("Could not locate command %r" % (command_path,))
          command = os.path.realpath(command)
  
          #  It would be nice if ModuleFinder() would retain state between runs so
          #  that it does not have to descend the tree for each command.  Unfortunately
          #  all state appears to be held in its "modules" attribute which then accumulates
          #  module references on each run_script() call.  That would wrongly attribute
          #  modules to subsequent scripts so we have to re-instantiate the class for
          #  each script.
          #
          finder = modulefinder.ModuleFinder(path=module_path)
          finder.run_script(command)
  
          #  If the name was used previously, remove all references
          #
          if name in self.names:
              log.debug("Removing existing entries for %r", name)
              self.remove(name)
          self.names[name] = command
  
          #  Build an inverted list of files and associated command names, starting
          #  with the command path itself.
          #
          rebuild = False
          if command in self.modules:
              if name in self.modules[command]:
                  log.debug("Name %r already present in %r", name, command)
              else:
                  log.debug("Command for %r added to %r", name, command)
                  self.modules[command].append(name)
          else:
              log.debug("Command %r added for %r", command, name)
              self.modules[command] = [name]
              rebuild = True
          for modname, mod in list(finder.modules.items()):
              path = mod.__file__
              if not path:
                  log.debug("Skipping module %r -- no __file__ in module", modname)
                  continue
              path = os.path.realpath(path)
              if path in self.modules:
                  if name in  self.modules[path]:
                      log.debug("Name %r already present in %r", name, command)
                  else:
                      log.debug("%r added to %r", name, path)
                      self.modules[path].append(name)
              else:
                  log.debug("%r added for %r", path, name)
                  self.modules[path] = [name]
                  rebuild = True
          if rebuild:
              self._build(name, **params)