<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def _obtain_health_pills_at_step(self, events_directory, node_names, step):
 """Reads disk to obtain the health pills for a run at a specific step. This could be much slower than the alternative path of just returning all health pills sampled by the event multiplexer. It could take tens of minutes to complete this call for large graphs for big step values (in the thousands). Args: events_directory: The directory containing events for the desired run. node_names: A list of node names for which to retrieve health pills. step: The step to obtain health pills for. Returns: A dictionary mapping from node name to a list of health pill objects (see docs for _serve_health_pills_handler for properties of those objects). Raises: IOError: If no files with health pill events could be found. """

    # Obtain all files with debugger-related events.
    pattern = os.path.join(events_directory, _DEBUGGER_EVENTS_GLOB_PATTERN)
    file_paths = glob.glob(pattern)

    if not file_paths:
      raise IOError(
          'No events files found that matches the pattern %r.' % pattern)

    # Sort by name (and thus by timestamp).
    file_paths.sort()

    mapping = collections.defaultdict(list)
    node_name_set = frozenset(node_names)

    for file_path in file_paths:
      should_stop = self._process_health_pill_event(
          node_name_set, mapping, step, file_path)
      if should_stop:
        break

    return mapping