Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add schema links to error messages resolution section #26646

Merged
merged 13 commits into from
Oct 11, 2022
56 changes: 48 additions & 8 deletions sdk/ml/azure-ai-ml/azure/ai/ml/_exception_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,32 +154,32 @@ def format_errors_and_resolutions_sections(entity_type: str, error_types: Dict[s
if error_types[ValidationErrorType.INVALID_VALUE]:
errors += f"\n{count}) One or more fields are invalid"
resolutions += f"Double-check that all specified parameters are of the correct types and formats \
prescribed by the {entity_type} schema.\n"
prescribed by the {entity_type} schema."
count += 1
if error_types[ValidationErrorType.UNKNOWN_FIELD]:
errors += f"\n{count}) A least one unrecognized parameter is specified"
resolutions += f"Remove any parameters not prescribed by the {entity_type} schema.\n"
resolutions += f"Remove any parameters not prescribed by the {entity_type} schema."
count += 1
if error_types[ValidationErrorType.MISSING_FIELD]:
errors += f"\n{count}) At least one required parameter is missing"
resolutions += f"Ensure all parameters required by the {entity_type} schema are specified.\n"
resolutions += f"Ensure all parameters required by the {entity_type} schema are specified."
count += 1
if error_types[ValidationErrorType.FILE_OR_FOLDER_NOT_FOUND]:
errors += f"\n{count}) One or more files or folders do not exist.\n"
resolutions += "Double-check the directory paths you provided and enter the correct paths.\n"
resolutions += "Double-check the directory paths you provided and enter the correct paths."
count += 1
if error_types[ValidationErrorType.CANNOT_SERIALIZE]:
errors += f"\n{count}) One or more fields cannot be serialized.\n"
resolutions += f"Double-check that all specified parameters are of the correct types and formats \
prescribed by the {entity_type} schema.\n"
prescribed by the {entity_type} schema."
count += 1
if error_types[ValidationErrorType.CANNOT_PARSE]:
errors += f"\n{count}) YAML file cannot be parsed.\n"
resolutions += "Double-check your YAML file for syntax and formatting errors.\n"
resolutions += "Double-check your YAML file for syntax and formatting errors."
count += 1
if error_types[ValidationErrorType.RESOURCE_NOT_FOUND]:
errors += f"\n{count}) Resource was not found.\n"
resolutions += "Double-check that the resource has been specified correctly and that you have access to it.\n"
resolutions += "Double-check that the resource has been specified correctly and that you have access to it."
count += 1

return errors, resolutions
Expand All @@ -191,10 +191,50 @@ def format_create_validation_error(
"""
Formats a detailed error message for validation errors.
"""
from azure.ai.ml.entities._util import REF_DOC_ERROR_MESSAGE_MAP
from azure.ai.ml._schema.assets.data import DataSchema
from azure.ai.ml._schema._datastore import (
AzureBlobSchema,
AzureDataLakeGen1Schema,
AzureDataLakeGen2Schema,
AzureFileSchema,
)
from azure.ai.ml._schema.job import CommandJobSchema
from azure.ai.ml._schema._sweep import SweepJobSchema
from azure.ai.ml._schema.assets.environment import EnvironmentSchema
from azure.ai.ml._schema.assets.model import ModelSchema

entity_type, details = get_entity_type(error)
error_types, details = format_details_section(error, details, entity_type)
errors, resolutions = format_errors_and_resolutions_sections(entity_type, error_types)
description = YAML_CREATION_ERROR_DESCRIPTION.format(entity_type=entity_type) if yaml_operation else ""

if yaml_operation:
description = YAML_CREATION_ERROR_DESCRIPTION.format(entity_type=entity_type)

if entity_type == ErrorTarget.MODEL:
schema_type = ModelSchema
elif entity_type == ErrorTarget.DATA:
schema_type = DataSchema
elif entity_type == ErrorTarget.COMMAND_JOB:
schema_type = CommandJobSchema
elif entity_type == ErrorTarget.SWEEP_JOB:
schema_type = SweepJobSchema
elif entity_type in [ErrorTarget.BLOB_DATASTORE, ErrorTarget.DATASTORE]:
schema_type = AzureBlobSchema
elif entity_type == ErrorTarget.GEN1_DATASTORE:
schema_type = AzureDataLakeGen1Schema
elif entity_type == ErrorTarget.GEN2_DATASTORE:
schema_type = AzureDataLakeGen2Schema
elif entity_type == ErrorTarget.FILE_DATASTORE:
schema_type = AzureFileSchema
elif entity_type == ErrorTarget.ENVIRONMENT:
schema_type = EnvironmentSchema

resolutions += " " + REF_DOC_ERROR_MESSAGE_MAP.get(schema_type, "")
else:
description = ""

resolutions += "\n"
formatted_error = SCHEMA_VALIDATION_ERROR_TEMPLATE.format(
description=description,
error_msg=errors,
Expand Down
2 changes: 1 addition & 1 deletion sdk/ml/azure-ai-ml/azure/ai/ml/constants/_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@
EXPERIMENTAL_LINK_MESSAGE = (
"and may change at any time. Please see https://aka.ms/azuremlexperimental for more information."
)
REF_DOC_YAML_SCHEMA_ERROR_MSG_FORMAT = "For a more detailed breakdown of the {} schema, please see: {}."
REF_DOC_YAML_SCHEMA_ERROR_MSG_FORMAT = "Visit this link to refer to the {} schema if needed: {}."
STORAGE_AUTH_MISMATCH_ERROR = "AuthorizationPermissionMismatch"
SWEEP_JOB_BEST_CHILD_RUN_ID_PROPERTY_NAME = "best_child_run_id"
BATCH_JOB_CHILD_RUN_NAME = "batchscoring"
Expand Down
4 changes: 4 additions & 0 deletions sdk/ml/azure-ai-ml/azure/ai/ml/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ class ErrorTarget:
ONLINE_ENDPOINT = "OnlineEndpoint"
ASSET = "Asset"
DATASTORE = "Datastore"
BLOB_DATASTORE = "BlobDatastore"
FILE_DATASTORE = "FileDatastore"
GEN1_DATASTORE = "Gen1Datastore"
GEN2_DATASTORE = "Gen2Datastore"
WORKSPACE = "Workspace"
COMPUTE = "Compute"
DEPLOYMENT = "Deployment"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@
DatasetVersionDetails,
)
from azure.ai.ml._scope_dependent_operations import OperationConfig, OperationScope
from azure.ai.ml.constants._common import AssetTypes
from azure.ai.ml.constants._common import (
REF_DOC_YAML_SCHEMA_ERROR_MSG_FORMAT,
AssetTypes,
YAMLRefDocLinks,
YAMLRefDocSchemaNames,
)
from azure.ai.ml.entities._assets import Data
from azure.ai.ml.entities._assets._artifacts.artifact import ArtifactStorageInfo
from azure.ai.ml.exceptions import ErrorTarget
Expand Down Expand Up @@ -170,13 +175,17 @@ def test_create_or_update_missing_path(self, mock_data_operations: DataOperation
Expect to raise ValidationException for missing path
"""
name = "random_name"
data = Data(name=name, version="1", description="this is an mltable dataset", type=AssetTypes.MLTABLE)
data1 = Data(name=name, version="1", description="this is an mltable dataset", type=AssetTypes.MLTABLE)
diondrapeck marked this conversation as resolved.
Show resolved Hide resolved

with pytest.raises(Exception) as ex:
mock_data_operations.create_or_update(data)
mock_data_operations.create_or_update(data1)
assert "At least one required parameter is missing" in str(ex.value)
mock_data_operations._operation.create_or_update.assert_not_called()

with pytest.raises(Exception) as ex:
load_data("tests/test_configs/dataset/data_missing_path_test.yml")
assert REF_DOC_YAML_SCHEMA_ERROR_MSG_FORMAT.format(YAMLRefDocSchemaNames.DATA, YAMLRefDocLinks.DATA) in str(ex.value)

@patch("azure.ai.ml.operations._data_operations.read_local_mltable_metadata_contents")
@patch("azure.ai.ml.operations._data_operations.read_remote_mltable_metadata_contents")
@patch("azure.ai.ml.operations._data_operations.download_mltable_metadata_schema")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,11 @@ def link_file_path(
link_file_name = "link_file_rand_name.txt"
link_file = Path(os.path.join(os.path.abspath(storage_test_directory), link_file_name))

os.symlink(target_file_path, link_file)
try:
os.symlink(target_file_path, link_file)
except FileExistsError:
pass

assert os.path.islink(link_file)
link_file = convert_windows_path_to_unix(link_file)
yield link_file
Expand Down