docs: def get_os_path_to_library(self, library_path, library_name, allow_user_interaction=True):
          """Find library_os_path of library
  
          This function retrieves the file system library_os_path of a library specified by a library_path and a 
          library_name. In case the library does not exist any more at its original location, the user has to specify 
          an alternative location.
  
          :param str library_path: The library_path of the library, that must be relative and within a library_root_path 
                               given in the config.yaml by LIBRARY_PATHS
          :param str library_name: The name of the library
          :param bool allow_user_interaction: Whether the user may be asked to specify library location
          :return: library_os_path within filesystem, library_path, library_name
          :rtype: str, str, str
          :raises rafcon.core.custom_exceptions.LibraryNotFoundException: if the cannot be found
          """
          original_path_and_name = os.path.join(library_path, library_name)
          library_path_root = library_path.split(os.sep)[0]
  
          if library_path.split(os.sep) and \
                  (library_path.startswith(os.sep) or library_path.endswith(os.sep) or os.sep + os.sep in library_path):
              raise LibraryNotFoundException("A library_path is not considered to start or end with {2} or to have two "
                                             "path separators {2}{2} in a row like '{0}' with library name {1}"
                                             "".format(library_path, library_name, os.sep))
  
          if not self._library_root_paths:
              raise LibraryNotFoundException("There are no libraries registered")
  
          # skip already skipped states
          if original_path_and_name in self._skipped_states or library_path_root in self._skipped_library_roots:
              # if an already skipped state shall be loaded again, directly raise the exception to jump over this state
              raise LibraryNotFoundException("Library '{0}' not found in subfolder {1}".format(library_name,
                                                                                               library_path))
  
          # replace already replaced states
          if original_path_and_name in self._replaced_libraries:
              new_library_os_path = self._replaced_libraries[original_path_and_name][0]
              new_library_path = self._replaced_libraries[original_path_and_name][1]
  
              # only show debug message if a state is automatically replaced by the appropriate library state
              # chosen by the user before
              if not self._replaced_libraries[original_path_and_name][2]:
                  logger.debug("The library with library_path \"{0}\" and name \"{1}\" "
                               "is automatically replaced by the library "
                               "with file system library_os_path \"{2}\" and library_path \"{3}\""
                               "".format(library_path, library_name, new_library_os_path, new_library_path))
              return new_library_os_path, new_library_path, library_name
  
          # a boolean to indicate if a state was regularly found or by the help of the user
          regularly_found = True
  
          library_os_path = self._get_library_os_path_from_library_dict_tree(library_path, library_name)
          while library_os_path is None:  # until the library is found or the user aborts
  
              regularly_found = False
              new_library_os_path = None
              if allow_user_interaction:
                  notice = "Cannot find library '{0}' in library_path '{1}' in any of the library root paths. " \
                           "Please check your library root paths configuration in config.yaml " \
                           "LIBRARY_PATHS and environment variable RAFCON_LIBRARY_PATH. " \
                           "If your library_path is correct and the library was moved, please " \
                           "select the new root/library_os_path folder of the library which should be situated within a "\
                           "loaded library_root_path. If not, please abort.".format(library_name, library_path)
                  interface.show_notice_func(notice)
                  new_library_os_path = interface.open_folder_func("Select root folder for library name '{0}'"
                                                                   "".format(original_path_and_name))
              if new_library_os_path is None:
                  # User clicked cancel => cancel library search
                  # If the library root path is existent (e.g. "generic") and only the specific library state is not (
                  # e.g. "generic/wait", then the state is added to the skipped states.
                  # If the library root path is not existing, we ignore the whole library, preventing the user from
                  # endless dialogs for each missing library state.
                  if library_path_root not in self.libraries:
                      self._skipped_library_roots.append(library_path_root)
                  else:
                      self._skipped_states.append(original_path_and_name)
                  raise LibraryNotFoundException("Library '{0}' not found in sub-folder {1}".format(library_name,
                                                                                                    library_path))
  
              if not os.path.exists(new_library_os_path):
                  logger.error('Specified library_os_path does not exist')
                  continue
  
              # check if valid library_path and library_name can be created
              library_path, library_name = self.get_library_path_and_name_for_os_path(new_library_os_path)
              if library_path is None:
                  logger.error("Specified library_os_path not within loaded library_root_path list or your config.yaml "
                               "file LIBRARY_PATHS or in the list of paths in environment variable RAFCON_LIBRARY_PATH")
                  continue  # Allow the user to change the directory
  
              # verification if library is also in library tree
              library_os_path = self._get_library_os_path_from_library_dict_tree(library_path, library_name)
              if library_os_path is not None:
                  assert library_os_path == new_library_os_path
  
          # save the replacement in order that for a future occurrence the correct library_os_path can be used,
          # without asking the user for the correct library_os_path
          self._replaced_libraries[original_path_and_name] = (library_os_path, library_path, regularly_found)
          return library_os_path, library_path, library_name