Repository: aws/sagemaker-python-sdk

Issue:
AttributeError: 'NoneType' object has no attribute '_create_model_request'
**Describe the bug**
Create a model without specifying sagemaker_session and you get this error

**To reproduce**
```
model = Model(
    image_uri = "246618743249.dkr.ecr.us-west-2.amazonaws.com/sagemaker-scikit-learn:0.23-1-cpu-py3",
    model_data = my_model_data_url,
    role = role_arn,
    source_dir = s3_url_to_targz,
    entry_point= "encoder.py"
)

model_step = CreateModelStep(
    name="createmodel",
    model = model,
    inputs = CreateModelInput(instance_type = "ml.m5.large")
)

pipeline = Pipeline(name="pipeline1", steps=[model_step])
pipeline.upsert(role_arn=role_arn)
pipeline.start()
```

if you add `sagemaker_session = Session()` to Model() it magically works

**Expected behavior**
work without specifying sagemaker_session

**Screenshots or logs**
```
    request_dict = self.model.sagemaker_session._create_model_request(
AttributeError: 'NoneType' object has no attribute '_create_model_request'
```

**System information**
A description of your system. Please provide:
- **SageMaker Python SDK version**: latest
- **Framework name (eg. PyTorch) or algorithm (eg. KMeans)**: any
- **Framework version**: any
- **Python version**: 3.8
- **CPU or GPU**: any
- **Custom Docker image (Y/N)**: N

**Additional context**
Add any other context about the problem here.

Hints from issue comments:
@mouhannadali: any update here?@timxieICN: Running into the same issue. Any update?@bernadinel: any updates ? got the same issue@davidbrochart: There is a method to set a default session:
https://github.com/aws/sagemaker-python-sdk/blob/410ab2c005a59941a4e56a7081d4dc3009244454/src/sagemaker/model.py#L559
But it doesn't seem to be called early enough.
Since it is accessed from another place:
https://github.com/aws/sagemaker-python-sdk/blob/410ab2c005a59941a4e56a7081d4dc3009244454/src/sagemaker/workflow/steps.py#L609
Maybe a solution would be for the `sagemaker_session` to be a `@property` of the model, so that it can be initialized lazily by calling `_init_sagemaker_session_if_does_not_exist()`?@pintaoz-aws: Fixed by https://github.com/aws/sagemaker-python-sdk/pull/5062

Solution patch:
diff --git a/src/sagemaker/experiments/_metrics.py b/src/sagemaker/experiments/_metrics.py
index 31dd679c..026e73e8 100644
--- a/src/sagemaker/experiments/_metrics.py
+++ b/src/sagemaker/experiments/_metrics.py
@@ -197,8 +197,8 @@ class _SyncMetricsSink(object):
             response = self._metrics_client.batch_put_metrics(**request)
             errors = response["Errors"] if "Errors" in response else None
             if errors:
-                message = errors[0]["Message"]
-                raise Exception(f'{len(errors)} errors with message "{message}"')
+                error_code = errors[0]["Code"]
+                raise Exception(f'{len(errors)} errors with error code "{error_code}"')
 
     def _construct_batch_put_metrics_request(self, batch):
         """Creates dictionary object used as request to metrics service."""
diff --git a/src/sagemaker/pipeline.py b/src/sagemaker/pipeline.py
index 1d1ece59..b36cd4e9 100644
--- a/src/sagemaker/pipeline.py
+++ b/src/sagemaker/pipeline.py
@@ -17,6 +17,8 @@ from typing import Callable, Optional, Dict, List, Union
 
 import sagemaker
 from sagemaker import ModelMetrics, Model
+from sagemaker import local
+from sagemaker import session
 from sagemaker.config import (
     ENDPOINT_CONFIG_KMS_KEY_ID_PATH,
     MODEL_VPC_CONFIG_PATH,
@@ -560,3 +562,16 @@ class PipelineModel(object):
             raise ValueError("The SageMaker model must be created before attempting to delete.")
 
         self.sagemaker_session.delete_model(self.name)
+
+    def _init_sagemaker_session_if_does_not_exist(self, instance_type=None):
+        """Set ``self.sagemaker_session`` to ``LocalSession`` or ``Session`` if it's not already.
+
+        The type of session object is determined by the instance type.
+        """
+        if self.sagemaker_session:
+            return
+
+        if instance_type in ("local", "local_gpu"):
+            self.sagemaker_session = local.LocalSession(sagemaker_config=self._sagemaker_config)
+        else:
+            self.sagemaker_session = session.Session(sagemaker_config=self._sagemaker_config)
diff --git a/src/sagemaker/session.py b/src/sagemaker/session.py
index c6a2014a..b2398e03 100644
--- a/src/sagemaker/session.py
+++ b/src/sagemaker/session.py
@@ -4347,11 +4347,59 @@ class Session(object):  # pylint: disable=too-many-public-methods
             if model_package_group_name is not None and not model_package_group_name.startswith(
                 "arn:"
             ):
-                _create_resource(
-                    lambda: self.sagemaker_client.create_model_package_group(
-                        ModelPackageGroupName=request["ModelPackageGroupName"]
+                is_model_package_group_present = False
+                try:
+                    model_package_groups_response = self.search(
+                        resource="ModelPackageGroup",
+                        search_expression={
+                            "Filters": [
+                                {
+                                    "Name": "ModelPackageGroupName",
+                                    "Value": request["ModelPackageGroupName"],
+                                    "Operator": "Equals",
+                                }
+                            ],
+                        },
+                    )
+                    if len(model_package_groups_response.get("Results")) > 0:
+                        is_model_package_group_present = True
+                except Exception:  # pylint: disable=W0703
+                    model_package_groups = []
+                    model_package_groups_response = self.sagemaker_client.list_model_package_groups(
+                        NameContains=request["ModelPackageGroupName"],
+                    )
+                    model_package_groups = (
+                        model_package_groups
+                        + model_package_groups_response["ModelPackageGroupSummaryList"]
+                    )
+                    next_token = model_package_groups_response.get("NextToken")
+
+                    while next_token is not None and next_token != "":
+                        model_package_groups_response = (
+                            self.sagemaker_client.list_model_package_groups(
+                                NameContains=request["ModelPackageGroupName"], NextToken=next_token
+                            )
+                        )
+                        model_package_groups = (
+                            model_package_groups
+                            + model_package_groups_response["ModelPackageGroupSummaryList"]
+                        )
+                        next_token = model_package_groups_response.get("NextToken")
+
+                    filtered_model_package_group = list(
+                        filter(
+                            lambda mpg: mpg.get("ModelPackageGroupName")
+                            == request["ModelPackageGroupName"],
+                            model_package_groups,
+                        )
+                    )
+                    is_model_package_group_present = len(filtered_model_package_group) > 0
+                if not is_model_package_group_present:
+                    _create_resource(
+                        lambda: self.sagemaker_client.create_model_package_group(
+                            ModelPackageGroupName=request["ModelPackageGroupName"]
+                        )
                     )
-                )
             if "SourceUri" in request and request["SourceUri"] is not None:
                 # Remove inference spec from request if the
                 # given source uri can lead to auto-population of it
diff --git a/src/sagemaker/utils.py b/src/sagemaker/utils.py
index c575b1ee..1a75a3a5 100644
--- a/src/sagemaker/utils.py
+++ b/src/sagemaker/utils.py
@@ -397,8 +397,7 @@ def download_folder(bucket_name, prefix, target, sagemaker_session):
         sagemaker_session (sagemaker.session.Session): a sagemaker session to
             interact with S3.
     """
-    boto_session = sagemaker_session.boto_session
-    s3 = boto_session.resource("s3", region_name=boto_session.region_name)
+    s3 = sagemaker_session.s3_resource
 
     prefix = prefix.lstrip("/")
 
diff --git a/src/sagemaker/workflow/steps.py b/src/sagemaker/workflow/steps.py
index a80b5440..f49e457b 100644
--- a/src/sagemaker/workflow/steps.py
+++ b/src/sagemaker/workflow/steps.py
@@ -645,6 +645,7 @@ class CreateModelStep(ConfigurableRetryStep):
             request_dict = self.step_args
         else:
             if isinstance(self.model, PipelineModel):
+                self.model._init_sagemaker_session_if_does_not_exist()
                 request_dict = self.model.sagemaker_session._create_model_request(
                     name="",
                     role=self.model.role,
@@ -653,6 +654,7 @@ class CreateModelStep(ConfigurableRetryStep):
                     enable_network_isolation=self.model.enable_network_isolation,
                 )
             else:
+                self.model._init_sagemaker_session_if_does_not_exist()
                 request_dict = self.model.sagemaker_session._create_model_request(
                     name="",
                     role=self.model.role,


Test patch:
diff --git a/tests/unit/test_session.py b/tests/unit/test_session.py
index d2d2c3bc..f873e9b1 100644
--- a/tests/unit/test_session.py
+++ b/tests/unit/test_session.py
@@ -5006,6 +5006,7 @@ def test_create_model_package_with_sagemaker_config_injection(sagemaker_session)
     domain = "COMPUTER_VISION"
     task = "IMAGE_CLASSIFICATION"
     sample_payload_url = "s3://test-bucket/model"
+    sagemaker_session.sagemaker_client.search.return_value = {"Results": []}
     sagemaker_session.create_model_package_from_containers(
         containers=containers,
         content_types=content_types,
@@ -5094,6 +5095,8 @@ def test_create_model_package_from_containers_with_source_uri_and_inference_spec
     skip_model_validation = "All"
     source_uri = "dummy-source-uri"
 
+    sagemaker_session.sagemaker_client.search.return_value = {"Results": []}
+
     created_versioned_mp_arn = (
         "arn:aws:sagemaker:us-west-2:123456789123:model-package/unit-test-package-version/1"
     )
@@ -5149,6 +5152,7 @@ def test_create_model_package_from_containers_with_source_uri_for_unversioned_mp
     approval_status = ("Approved",)
     skip_model_validation = "All"
     source_uri = "dummy-source-uri"
+    sagemaker_session.sagemaker_client.search.return_value = {"Results": []}
 
     with pytest.raises(
         ValueError,
@@ -5221,6 +5225,8 @@ def test_create_model_package_from_containers_with_source_uri_set_to_mp(sagemake
         return_value={"ModelPackageArn": created_versioned_mp_arn}
     )
 
+    sagemaker_session.sagemaker_client.search.return_value = {"Results": []}
+
     sagemaker_session.create_model_package_from_containers(
         model_package_group_name=model_package_group_name,
         containers=containers,
@@ -5443,6 +5449,7 @@ def test_create_model_package_from_containers_without_instance_types(sagemaker_s
     approval_status = ("Approved",)
     description = "description"
     customer_metadata_properties = {"key1": "value1"}
+    sagemaker_session.sagemaker_client.search.return_value = {"Results": []}
     sagemaker_session.create_model_package_from_containers(
         containers=containers,
         content_types=content_types,
@@ -5510,6 +5517,7 @@ def test_create_model_package_from_containers_with_one_instance_types(
     approval_status = ("Approved",)
     description = "description"
     customer_metadata_properties = {"key1": "value1"}
+    sagemaker_session.sagemaker_client.search.return_value = {"Results": []}
     sagemaker_session.create_model_package_from_containers(
         containers=containers,
         content_types=content_types,
@@ -7183,3 +7191,65 @@ def test_delete_hub_content_reference(sagemaker_session):
     }
 
     sagemaker_session.sagemaker_client.delete_hub_content_reference.assert_called_with(**request)
+
+
+def test_create_model_package_from_containers_to_create_mpg_if_not_present_without_search(
+    sagemaker_session,
+):
+    sagemaker_session.sagemaker_client.search.side_effect = Exception()
+    sagemaker_session.sagemaker_client.search.return_value = {}
+    sagemaker_session.sagemaker_client.list_model_package_groups.side_effect = [
+        {
+            "ModelPackageGroupSummaryList": [{"ModelPackageGroupName": "mock-mpg"}],
+            "NextToken": "NextToken",
+        },
+        {"ModelPackageGroupSummaryList": [{"ModelPackageGroupName": "mock-mpg-test"}]},
+    ]
+    sagemaker_session.create_model_package_from_containers(
+        source_uri="mock-source-uri", model_package_group_name="mock-mpg"
+    )
+    sagemaker_session.sagemaker_client.create_model_package_group.assert_not_called()
+    sagemaker_session.create_model_package_from_containers(
+        source_uri="mock-source-uri",
+        model_package_group_name="arn:aws:sagemaker:us-east-1:215995503607:model-package-group/mock-mpg",
+    )
+    sagemaker_session.sagemaker_client.create_model_package_group.assert_not_called()
+    sagemaker_session.sagemaker_client.list_model_package_groups.side_effect = [
+        {"ModelPackageGroupSummaryList": []}
+    ]
+    sagemaker_session.create_model_package_from_containers(
+        source_uri="mock-source-uri", model_package_group_name="mock-mpg"
+    )
+    sagemaker_session.sagemaker_client.create_model_package_group.assert_called_with(
+        ModelPackageGroupName="mock-mpg"
+    )
+
+
+def test_create_model_package_from_containers_to_create_mpg_if_not_present(sagemaker_session):
+    # with search api
+    sagemaker_session.sagemaker_client.search.return_value = {
+        "Results": [
+            {
+                "ModelPackageGroup": {
+                    "ModelPackageGroupName": "mock-mpg",
+                    "ModelPackageGroupArn": "arn:aws:sagemaker:us-west-2:123456789012:model-package-group/mock-mpg",
+                }
+            }
+        ]
+    }
+    sagemaker_session.create_model_package_from_containers(
+        source_uri="mock-source-uri", model_package_group_name="mock-mpg"
+    )
+    sagemaker_session.sagemaker_client.create_model_package_group.assert_not_called()
+    sagemaker_session.create_model_package_from_containers(
+        source_uri="mock-source-uri",
+        model_package_group_name="arn:aws:sagemaker:us-east-1:215995503607:model-package-group/mock-mpg",
+    )
+    sagemaker_session.sagemaker_client.create_model_package_group.assert_not_called()
+    sagemaker_session.sagemaker_client.search.return_value = {"Results": []}
+    sagemaker_session.create_model_package_from_containers(
+        source_uri="mock-source-uri", model_package_group_name="mock-mpg"
+    )
+    sagemaker_session.sagemaker_client.create_model_package_group.assert_called_with(
+        ModelPackageGroupName="mock-mpg"
+    )