Skip to content

Commit 4da78d9

Browse files
committed
fix: Execution.create() 503 from credentials annotation typo and missing metadata store init
Fixes #6610. Two bugs in `metadata/execution.py` caused `Execution.create()` to fail with "503 Getting metadata from plugin failed with error: before_request": 1. The `credentials` parameter on both `create()` and `_create()` used `=` instead of `:` for the type annotation: `credentials=Optional[auth_credentials.Credentials]` instead of `credentials: Optional[auth_credentials.Credentials] = None`. This made the default value the `typing.Optional` type object itself (not `None`), which the gRPC auth stack tried to call `.before_request()` on. 2. `create()` skipped the `ensure_default_metadata_store_exists()` call that `Artifact.create()` makes, so standalone `Execution.create()` would also fail if no prior operation had initialized the metadata store. Both issues have been present since PR #1410 (June 2022). `artifact.py` and `context.py` both have the correct annotation.
1 parent 3c55f26 commit 4da78d9

2 files changed

Lines changed: 79 additions & 2 deletions

File tree

google/cloud/aiplatform/metadata/execution.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def create(
100100
metadata_store_id: str = "default",
101101
project: Optional[str] = None,
102102
location: Optional[str] = None,
103-
credentials=Optional[auth_credentials.Credentials],
103+
credentials: Optional[auth_credentials.Credentials] = None,
104104
) -> "Execution":
105105
"""
106106
Creates a new Metadata Execution.
@@ -149,6 +149,11 @@ def create(
149149
"aiplatform.metadata.execution.Execution.create"
150150
)
151151

152+
if metadata_store_id == "default":
153+
metadata_store._MetadataStore.ensure_default_metadata_store_exists(
154+
project=project, location=location, credentials=credentials
155+
)
156+
152157
return cls._create(
153158
resource_id=resource_id,
154159
schema_title=schema_title,
@@ -178,7 +183,7 @@ def _create(
178183
metadata_store_id: str = "default",
179184
project: Optional[str] = None,
180185
location: Optional[str] = None,
181-
credentials=Optional[auth_credentials.Credentials],
186+
credentials: Optional[auth_credentials.Credentials] = None,
182187
) -> "Execution":
183188
"""
184189
Creates a new Metadata Execution.

tests/unit/aiplatform/test_metadata_resources.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
from google.cloud.aiplatform.metadata import artifact
2929
from google.cloud.aiplatform.metadata import context
3030
from google.cloud.aiplatform.metadata import execution
31+
from google.cloud.aiplatform.metadata import metadata_store
3132
from google.cloud.aiplatform.metadata import utils as metadata_utils
3233
from google.cloud.aiplatform_v1 import (
3334
MetadataServiceClient,
@@ -1005,6 +1006,77 @@ def test_query_input_and_output_artifacts(
10051006
assert len(artifact_list) == 1
10061007
assert artifact_list[0]._gca_resource == expected_artifact
10071008

1009+
def test_create_credentials_default_is_none(self):
1010+
"""Verify the credentials parameter defaults to None, not a type object.
1011+
1012+
Regression test for https://github.com/googleapis/python-aiplatform/issues/6610.
1013+
The original code used ``credentials=Optional[...]`` (assignment) instead of
1014+
``credentials: Optional[...] = None`` (annotation with default). The ``=`` form
1015+
makes the default the ``typing.Optional`` type object itself, which causes a
1016+
503 "Getting metadata from plugin failed with error: before_request" when the
1017+
gRPC auth stack tries to call ``.before_request()`` on it.
1018+
"""
1019+
import inspect
1020+
1021+
sig = inspect.signature(execution.Execution.create)
1022+
default = sig.parameters["credentials"].default
1023+
assert default is None, (
1024+
f"Execution.create() credentials default should be None, got {default!r}. "
1025+
"Check that the annotation uses ':' (not '=') with a '= None' default."
1026+
)
1027+
1028+
sig_internal = inspect.signature(execution.Execution._create)
1029+
default_internal = sig_internal.parameters["credentials"].default
1030+
assert default_internal is None, (
1031+
f"Execution._create() credentials default should be None, got {default_internal!r}. "
1032+
"Check that the annotation uses ':' (not '=') with a '= None' default."
1033+
)
1034+
1035+
@pytest.mark.usefixtures("create_execution_mock", "get_execution_mock")
1036+
def test_create_calls_ensure_default_metadata_store_exists(
1037+
self, create_execution_mock
1038+
):
1039+
"""Verify Execution.create() initializes the default metadata store.
1040+
1041+
Regression test for https://github.com/googleapis/python-aiplatform/issues/6610.
1042+
Artifact.create() calls ensure_default_metadata_store_exists() before creating
1043+
the resource, but Execution.create() originally skipped this call, causing 503
1044+
credential plugin errors when no prior Artifact.create() or aiplatform.init()
1045+
had warmed up the metadata store.
1046+
"""
1047+
aiplatform.init(project=_TEST_PROJECT, location=_TEST_LOCATION)
1048+
1049+
with patch.object(
1050+
metadata_store._MetadataStore,
1051+
"ensure_default_metadata_store_exists",
1052+
) as ensure_store_mock:
1053+
execution.Execution.create(
1054+
schema_title=_TEST_SCHEMA_TITLE,
1055+
display_name=_TEST_DISPLAY_NAME,
1056+
metadata_store_id="default",
1057+
)
1058+
ensure_store_mock.assert_called_once_with(
1059+
project=None, location=None, credentials=None
1060+
)
1061+
1062+
@pytest.mark.usefixtures("create_execution_mock", "get_execution_mock")
1063+
def test_create_skips_ensure_for_non_default_metadata_store(
1064+
self, create_execution_mock
1065+
):
1066+
"""Verify ensure_default_metadata_store_exists is not called for non-default stores."""
1067+
aiplatform.init(project=_TEST_PROJECT, location=_TEST_LOCATION)
1068+
1069+
with patch.object(
1070+
metadata_store._MetadataStore,
1071+
"ensure_default_metadata_store_exists",
1072+
) as ensure_store_mock:
1073+
execution.Execution.create(
1074+
schema_title=_TEST_SCHEMA_TITLE,
1075+
display_name=_TEST_DISPLAY_NAME,
1076+
metadata_store_id=_TEST_METADATA_STORE,
1077+
)
1078+
ensure_store_mock.assert_not_called()
1079+
10081080

10091081
@pytest.mark.usefixtures("google_auth_mock")
10101082
class TestArtifact:

0 commit comments

Comments
 (0)