# Buggy code
def secondary_training_status_message(job_description, prev_description):
    """Returns a string contains start time and the secondary training job status message.

    Args:
        job_description: Returned response from DescribeTrainingJob call
        prev_description: Previous job description from DescribeTrainingJob call

    Returns:
        str: Job status string to be printed.

    """

    if (
        job_description is None
        or job_description.get("SecondaryStatusTransitions") is None
        or len(job_description.get("SecondaryStatusTransitions")) == 0
    ):
        return ""

    prev_description_secondary_transitions = (
        prev_description.get("SecondaryStatusTransitions")
        if prev_description is not None
        else None
    )
    prev_transitions_num = (
        len(prev_description["SecondaryStatusTransitions"])
        if prev_description_secondary_transitions is not None
        else 0
    )
    current_transitions = job_description["SecondaryStatusTransitions"]

    transitions_to_print = (
        current_transitions[-1:]
        if len(current_transitions) == prev_transitions_num
        else current_transitions[prev_transitions_num - len(current_transitions) :]
    )

    status_strs = []
    for transition in transitions_to_print:
        message = transition["StatusMessage"]
        time_str = datetime.utcfromtimestamp(
            time.mktime(job_description["LastModifiedTime"].timetuple())
        ).strftime("%Y-%m-%d %H:%M:%S")
        status_strs.append("{} {} - {}".format(time_str, transition["Status"], message))

    return "\n".join(status_strs)


# Traceback (TypeError)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-34-104847d86296> in <module>()
61     m.set_hyperparameters(**hyperparameters)
62
---> 63     m.fit({k:'file://'+v for k,v in inputs.items()})
64
65     predictor = m.deploy(1, 'local_gpu')

~/anaconda3/envs/mxnet_p36/lib/python3.6/site-packages/sagemaker/estimator.py in fit(self, inputs, wait, logs, job_name)
189         self._prepare_for_training(job_name=job_name)
190
--> 191         self.latest_training_job = _TrainingJob.start_new(self, inputs)
192         if wait:
193             self.latest_training_job.wait(logs=logs)

~/anaconda3/envs/mxnet_p36/lib/python3.6/site-packages/sagemaker/estimator.py in start_new(cls, estimator, inputs)
415                                           resource_config=config['resource_config'], vpc_config=config['vpc_config'],
416                                           hyperparameters=hyperparameters, stop_condition=config['stop_condition'],
--> 417                                           tags=estimator.tags)
418
419         return cls(estimator.sagemaker_session, estimator._current_job_name)

~/anaconda3/envs/mxnet_p36/lib/python3.6/site-packages/sagemaker/session.py in train(self, image, input_mode, input_config, role, job_name, output_config, resource_config, vpc_config, hyperparameters, stop_condition, tags)
276         LOGGER.info('Creating training-job with name: {}'.format(job_name))
277         LOGGER.debug('train request: {}'.format(json.dumps(train_request, indent=4)))
--> 278         self.sagemaker_client.create_training_job(**train_request)
279
280     def tune(self, job_name, strategy, objective_type, objective_metric_name,

~/anaconda3/envs/mxnet_p36/lib/python3.6/site-packages/sagemaker/local/local_session.py in create_training_job(self, TrainingJobName, AlgorithmSpecification, InputDataConfig, OutputDataConfig, ResourceConfig, **kwargs)
73         training_job = _LocalTrainingJob(container)
74         hyperparameters = kwargs['HyperParameters'] if 'HyperParameters' in kwargs else {}
---> 75         training_job.start(InputDataConfig, hyperparameters)
76
77         LocalSagemakerClient._training_jobs[TrainingJobName] = training_job

~/anaconda3/envs/mxnet_p36/lib/python3.6/site-packages/sagemaker/local/entities.py in start(self, input_data_config, hyperparameters)
58         self.state = self._TRAINING
59
---> 60         self.model_artifacts = self.container.train(input_data_config, hyperparameters)
61         self.end = datetime.datetime.now()
62         self.state = self._COMPLETED

~/anaconda3/envs/mxnet_p36/lib/python3.6/site-packages/sagemaker/local/image.py in train(self, input_data_config, hyperparameters)
109         training_env_vars = {
110             REGION_ENV_NAME: self.sagemaker_session.boto_region_name,
--> 111             TRAINING_JOB_NAME_ENV_NAME: json.loads(hyperparameters.get(sagemaker.model.JOB_NAME_PARAM_NAME)),
112         }
113         compose_data = self._generate_compose_file('train', additional_volumes=volumes,

~/anaconda3/envs/mxnet_p36/lib/python3.6/json/__init__.py in loads(s, encoding, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)
346         if not isinstance(s, (bytes, bytearray)):
347             raise TypeError('the JSON object must be str, bytes or bytearray, '
--> 348                             'not {!r}'.format(s.__class__.__name__))
349         s = s.decode(detect_encoding(s), 'surrogatepass')
350

TypeError: the JSON object must be str, bytes or bytearray, not 'NoneType'

# Fixed code
def secondary_training_status_message(job_description, prev_description):
    """Returns a string contains last modified time and the secondary training job status message.

    Args:
        job_description: Returned response from DescribeTrainingJob call
        prev_description: Previous job description from DescribeTrainingJob call

    Returns:
        str: Job status string to be printed.

    """

    if (
        job_description is None
        or job_description.get("SecondaryStatusTransitions") is None
        or len(job_description.get("SecondaryStatusTransitions")) == 0
    ):
        return ""

    prev_description_secondary_transitions = (
        prev_description.get("SecondaryStatusTransitions")
        if prev_description is not None
        else None
    )
    prev_transitions_num = (
        len(prev_description["SecondaryStatusTransitions"])
        if prev_description_secondary_transitions is not None
        else 0
    )
    current_transitions = job_description["SecondaryStatusTransitions"]

    if len(current_transitions) == prev_transitions_num:
        # Secondary status is not changed but the message changed.
        transitions_to_print = current_transitions[-1:]
    else:
        # Secondary status is changed we need to print all the entries.
        transitions_to_print = current_transitions[
            prev_transitions_num - len(current_transitions) :
        ]

    status_strs = []
    for transition in transitions_to_print:
        message = transition["StatusMessage"]
        time_str = datetime.utcfromtimestamp(
            time.mktime(job_description["LastModifiedTime"].timetuple())
        ).strftime("%Y-%m-%d %H:%M:%S")
        status_strs.append("{} {} - {}".format(time_str, transition["Status"], message))

    return "\n".join(status_strs)