docstr: data: it has three keys: trial_job_id, event, hyper_params
       - trial_job_id: the id generated by training service
       - event: the job's state
       - hyper_params: the hyperparameters generated and returned by tuner
docstr_tok: ["data",":","it","has","three","keys",":","trial_job_id",",","event",",","hyper_params","-","trial_job_id",":","the","id","generated","by","training","service","-","event",":","the","job","'s","state","-","hyper_params",":","the","hyperparameters","generated","and","returned","by","tuner"]
code: def handle_trial_end(self, data):
      """
      data: it has three keys: trial_job_id, event, hyper_params
           - trial_job_id: the id generated by training service
           - event: the job's state
           - hyper_params: the hyperparameters generated and returned by tuner
      """
      id_ = load(data["hyper_params"])["parameter_id"]
      if self.is_created_in_previous_exp(id_):
          # The end of the recovered trial is ignored
          return
      trial_job_id = data["trial_job_id"]
      _ended_trials.add(trial_job_id)
      if trial_job_id in _trial_history:
          _trial_history.pop(trial_job_id)
          if self.assessor is not None:
              self.assessor.trial_end(
                  trial_job_id, data["event"] == "SUCCEEDED"
              )
      if self.tuner is not None:
          self.tuner.trial_end(id_, data["event"] == "SUCCEEDED")
  
imports: import logging
  from collections import defaultdict
  from nni import NoMoreTrialError
  from nni.assessor import AssessResult
  from .common import multi_phase_enabled
  from .env_vars import dispatcher_env_vars
  from .msg_dispatcher_base import MsgDispatcherBase
  from .tuner_command_channel import CommandType
  from ..common.serializer import dump, load
  from ..utils import MetricType
label_desc: Use image node_id 11 for calling the MsgDispatcher obj's underlying member method code with example usage: obj.handle_trial_end(data) without return types