# Buggy code
def _generate_compose_file(
    self, command, additional_volumes=None, additional_env_vars=None
):
    """Writes a config file describing a training/hosting  environment.

    This method generates a docker compose configuration file, it has an entry for each container
    that will be created (based on self.hosts). it calls
    :meth:~sagemaker.local_session.SageMakerContainer._create_docker_host to generate the config
    for each individual container.

    Args:
        command (str): either 'train' or 'serve'
        additional_volumes (list): a list of volumes that will be mapped to the containers
        additional_env_vars (dict): a dictionary with additional environment variables to be
            passed on to the containers.

    Returns: (dict) A dictionary representation of the configuration that was written.

    """
    boto_session = self.sagemaker_session.boto_session
    additional_env_vars = additional_env_vars or []
    additional_volumes = additional_volumes or {}
    environment = []
    optml_dirs = set()

    aws_creds = _aws_credentials(boto_session)
    if aws_creds is not None:
        environment.extend(aws_creds)

    additional_env_var_list = [
        "{}={}".format(k, v) for k, v in additional_env_vars.items()
    ]
    environment.extend(additional_env_var_list)

    if command == "train":
        optml_dirs = {"output", "output/data", "input"}

    services = {
        h: self._create_docker_host(
            h, environment, optml_dirs, command, additional_volumes
        )
        for h in self.hosts
    }

    content = {
        # Use version 2.3 as a minimum so that we can specify the runtime
        "version": "2.3",
        "services": services,
        "networks": {"sagemaker-local": {"name": "sagemaker-local"}},
    }

    docker_compose_path = os.path.join(self.container_root, DOCKER_COMPOSE_FILENAME)
    yaml_content = yaml.dump(content, default_flow_style=False)
    logger.info("docker compose file: \n{}".format(yaml_content))
    with open(docker_compose_path, "w") as f:
        f.write(yaml_content)

    return content


# 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 _generate_compose_file(
    self, command, additional_volumes=None, additional_env_vars=None
):
    """Writes a config file describing a training/hosting  environment.

    This method generates a docker compose configuration file, it has an entry for each container
    that will be created (based on self.hosts). it calls
    :meth:~sagemaker.local_session.SageMakerContainer._create_docker_host to generate the config
    for each individual container.

    Args:
        command (str): either 'train' or 'serve'
        additional_volumes (list): a list of volumes that will be mapped to the containers
        additional_env_vars (dict): a dictionary with additional environment variables to be
            passed on to the containers.

    Returns: (dict) A dictionary representation of the configuration that was written.

    """
    boto_session = self.sagemaker_session.boto_session
    additional_volumes = additional_volumes or []
    additional_env_vars = additional_env_vars or {}
    environment = []
    optml_dirs = set()

    aws_creds = _aws_credentials(boto_session)
    if aws_creds is not None:
        environment.extend(aws_creds)

    additional_env_var_list = [
        "{}={}".format(k, v) for k, v in additional_env_vars.items()
    ]
    environment.extend(additional_env_var_list)

    if command == "train":
        optml_dirs = {"output", "output/data", "input"}

    services = {
        h: self._create_docker_host(
            h, environment, optml_dirs, command, additional_volumes
        )
        for h in self.hosts
    }

    content = {
        # Use version 2.3 as a minimum so that we can specify the runtime
        "version": "2.3",
        "services": services,
        "networks": {"sagemaker-local": {"name": "sagemaker-local"}},
    }

    docker_compose_path = os.path.join(self.container_root, DOCKER_COMPOSE_FILENAME)
    yaml_content = yaml.dump(content, default_flow_style=False)
    logger.info("docker compose file: \n{}".format(yaml_content))
    with open(docker_compose_path, "w") as f:
        f.write(yaml_content)

    return content