docs: def find_rule_classes(extra_path):
      """
      Searches a given directory or python module for rule classes. This is done by
      adding the directory path to the python path, importing the modules and then finding
      any Rule class in those modules.
  
      :param extra_path: absolute directory or file path to search for rule classes
      :return: The list of rule classes that are found in the given directory or module
      """
  
      files = []
      modules = []
  
      if os.path.isfile(extra_path):
          files = [os.path.basename(extra_path)]
          directory = os.path.dirname(extra_path)
      elif os.path.isdir(extra_path):
          files = os.listdir(extra_path)
          directory = extra_path
      else:
          raise UserRuleError(u"Invalid extra-path: {0}".format(extra_path))
  
      # Filter out files that are not python modules
      for filename in files:
          if fnmatch.fnmatch(filename, '*.py'):
              modules.append(os.path.splitext(filename)[0])
  
      # No need to continue if there are no modules specified
      if not modules:
          return []
  
      # Append the extra rules path to python path so that we can import them
      sys.path.append(directory)
  
      # Find all the rule classes in the found python files
      rule_classes = []
      for module in modules:
          # Import the module
          try:
              importlib.import_module(module)
          except Exception as e:
              raise UserRuleError(u"Error while importing extra-path module '{0}': {1}".format(module, ustr(e)))
  
          # Find all rule classes in the module. We do this my inspecting all members of the module and checking
          # 1) is it a class, if not, skip
          # 2) is the parent path the current module. If not, we are dealing with an imported class, skip
          # 3) is it a subclass of rule
          rule_classes.extend([clazz for _, clazz in inspect.getmembers(sys.modules[module])
                               if
                               inspect.isclass(clazz) and  # check isclass to ensure clazz.__module__ exists
                               clazz.__module__ == module and  # ignore imported classes
                               (issubclass(clazz, rules.LineRule) or issubclass(clazz, rules.CommitRule))])
  
          # validate that the rule classes are valid user-defined rules
          for rule_class in rule_classes:
              assert_valid_rule_class(rule_class)
  
      return rule_classes