about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/azure/ai/ml/entities/_inputs_outputs/input.py
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/azure/ai/ml/entities/_inputs_outputs/input.py
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are here HEAD master
Diffstat (limited to '.venv/lib/python3.12/site-packages/azure/ai/ml/entities/_inputs_outputs/input.py')
-rw-r--r--.venv/lib/python3.12/site-packages/azure/ai/ml/entities/_inputs_outputs/input.py547
1 files changed, 547 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/azure/ai/ml/entities/_inputs_outputs/input.py b/.venv/lib/python3.12/site-packages/azure/ai/ml/entities/_inputs_outputs/input.py
new file mode 100644
index 00000000..4a945108
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/azure/ai/ml/entities/_inputs_outputs/input.py
@@ -0,0 +1,547 @@
+# ---------------------------------------------------------
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# ---------------------------------------------------------
+
+# pylint: disable=redefined-builtin
+# disable redefined-builtin to use type/min/max as argument name
+
+import math
+from inspect import Parameter
+from typing import Any, Dict, List, Optional, Union, overload
+
+from typing_extensions import Literal
+
+from azure.ai.ml.constants._component import ComponentParameterTypes, IOConstants
+from azure.ai.ml.entities._assets.intellectual_property import IntellectualProperty
+from azure.ai.ml.exceptions import (
+    ErrorCategory,
+    ErrorTarget,
+    UserErrorException,
+    ValidationErrorType,
+    ValidationException,
+)
+
+from .base import _InputOutputBase
+from .utils import _get_param_with_standard_annotation, _remove_empty_values
+
+
+class Input(_InputOutputBase):  # pylint: disable=too-many-instance-attributes
+    """Initialize an Input object.
+
+    :keyword type: The type of the data input. Accepted values are
+        'uri_folder', 'uri_file', 'mltable', 'mlflow_model', 'custom_model', 'integer', 'number', 'string', and
+        'boolean'. Defaults to 'uri_folder'.
+    :paramtype type: str
+    :keyword path: The path to the input data. Paths can be local paths, remote data uris, or a registered AzureML asset
+        ID.
+    :paramtype path: Optional[str]
+    :keyword mode: The access mode of the data input. Accepted values are:
+        * 'ro_mount': Mount the data to the compute target as read-only,
+        * 'download': Download the data to the compute target,
+        * 'direct': Pass in the URI as a string to be accessed at runtime
+    :paramtype mode: Optional[str]
+    :keyword path_on_compute: The access path of the data input for compute
+    :paramtype path_on_compute: Optional[str]
+    :keyword default: The default value of the input. If a default is set, the input data will be optional.
+    :paramtype default: Union[str, int, float, bool]
+    :keyword min: The minimum value for the input. If a value smaller than the minimum is passed to the job, the job
+        execution will fail.
+    :paramtype min: Union[int, float]
+    :keyword max: The maximum value for the input. If a value larger than the maximum is passed to a job, the job
+        execution will fail.
+    :paramtype max: Union[int, float]
+    :keyword optional: Specifies if the input is optional.
+    :paramtype optional: Optional[bool]
+    :keyword description: Description of the input
+    :paramtype description: Optional[str]
+    :keyword datastore: The datastore to upload local files to.
+    :paramtype datastore: str
+    :keyword intellectual_property: Intellectual property for the input.
+    :paramtype intellectual_property: IntellectualProperty
+    :raises ~azure.ai.ml.exceptions.ValidationException: Raised if Input cannot be successfully validated.
+        Details will be provided in the error message.
+
+    .. admonition:: Example:
+
+        .. literalinclude:: ../samples/ml_samples_misc.py
+            :start-after: [START create_inputs_outputs]
+            :end-before: [END create_inputs_outputs]
+            :language: python
+            :dedent: 8
+            :caption: Creating a CommandJob with two inputs.
+    """
+
+    _EMPTY = Parameter.empty
+    _IO_KEYS = [
+        "path",
+        "type",
+        "mode",
+        "path_on_compute",
+        "description",
+        "default",
+        "min",
+        "max",
+        "enum",
+        "optional",
+        "datastore",
+    ]
+
+    @overload
+    def __init__(
+        self,
+        *,
+        type: str,
+        path: Optional[str] = None,
+        mode: Optional[str] = None,
+        optional: Optional[bool] = None,
+        description: Optional[str] = None,
+        **kwargs: Any,
+    ) -> None:
+        """"""
+
+    @overload
+    def __init__(
+        self,
+        *,
+        type: Literal["number"] = "number",
+        default: Optional[float] = None,
+        min: Optional[float] = None,
+        max: Optional[float] = None,
+        optional: Optional[bool] = None,
+        description: Optional[str] = None,
+        **kwargs: Any,
+    ) -> None:
+        """Initialize a number input.
+
+        :keyword type: The type of the data input. Can only be set to "number".
+        :paramtype type: str
+        :keyword default: The default value of the input. If a default is set, the input data will be optional.
+        :paramtype default: Union[str, int, float, bool]
+        :keyword min: The minimum value for the input. If a value smaller than the minimum is passed to the job, the job
+            execution will fail.
+        :paramtype min: Optional[float]
+        :keyword max: The maximum value for the input. If a value larger than the maximum is passed to a job, the job
+            execution will fail.
+        :paramtype max: Optional[float]
+        :keyword optional: Specifies if the input is optional.
+        :paramtype optional: bool
+        :keyword description: Description of the input
+        :paramtype description: str
+        :raises ~azure.ai.ml.exceptions.ValidationException: Raised if Input cannot be successfully validated.
+            Details will be provided in the error message.
+        """
+
+    @overload
+    def __init__(
+        self,
+        *,
+        type: Literal["integer"] = "integer",
+        default: Optional[int] = None,
+        min: Optional[int] = None,
+        max: Optional[int] = None,
+        optional: Optional[bool] = None,
+        description: Optional[str] = None,
+        **kwargs: Any,
+    ) -> None:
+        """Initialize an integer input.
+
+        :keyword type: The type of the data input. Can only be set to "integer".
+        :paramtype type: str
+        :keyword default: The default value of the input. If a default is set, the input data will be optional.
+        :paramtype default: Union[str, int, float, bool]
+        :keyword min: The minimum value for the input. If a value smaller than the minimum is passed to the job, the job
+            execution will fail.
+        :paramtype min: Optional[int]
+        :keyword max: The maximum value for the input. If a value larger than the maximum is passed to a job, the job
+            execution will fail.
+        :paramtype max: Optional[int]
+        :keyword optional: Specifies if the input is optional.
+        :paramtype optional: bool
+        :keyword description: Description of the input
+        :paramtype description: str
+        """
+
+    @overload
+    def __init__(
+        self,
+        *,
+        type: Literal["string"] = "string",
+        default: Optional[str] = None,
+        optional: Optional[bool] = None,
+        description: Optional[str] = None,
+        path: Optional[str] = None,
+        **kwargs: Any,
+    ) -> None:
+        """Initialize a string input.
+
+        :keyword type: The type of the data input. Can only be set to "string".
+        :paramtype type: str
+        :keyword default: The default value of this input. When a `default` is set, the input will be optional.
+        :paramtype default: str
+        :keyword optional: Determine if this input is optional.
+        :paramtype optional: bool
+        :keyword description: Description of the input.
+        :paramtype description: str
+        :raises ~azure.ai.ml.exceptions.ValidationException: Raised if Input cannot be successfully validated.
+            Details will be provided in the error message.
+        """
+
+    @overload
+    def __init__(
+        self,
+        *,
+        type: Literal["boolean"] = "boolean",
+        default: Optional[bool] = None,
+        optional: Optional[bool] = None,
+        description: Optional[str] = None,
+        **kwargs: Any,
+    ) -> None:
+        """Initialize a bool input.
+
+        :keyword type: The type of the data input. Can only be set to "boolean".
+        :paramtype type: str
+        :keyword path: The path to the input data. Paths can be local paths, remote data uris, or a registered AzureML
+            asset id.
+        :paramtype path: str
+        :keyword default: The default value of the input. If a default is set, the input data will be optional.
+        :paramtype default: Union[str, int, float, bool]
+        :keyword optional: Specifies if the input is optional.
+        :paramtype optional: bool
+        :keyword description: Description of the input
+        :paramtype description: str
+        :raises ~azure.ai.ml.exceptions.ValidationException: Raised if Input cannot be successfully validated.
+            Details will be provided in the error message.
+        """
+
+    def __init__(
+        self,
+        *,
+        type: str = "uri_folder",
+        path: Optional[str] = None,
+        mode: Optional[str] = None,
+        path_on_compute: Optional[str] = None,
+        default: Optional[Union[str, int, float, bool]] = None,
+        optional: Optional[bool] = None,
+        min: Optional[Union[int, float]] = None,
+        max: Optional[Union[int, float]] = None,
+        enum: Any = None,
+        description: Optional[str] = None,
+        datastore: Optional[str] = None,
+        **kwargs: Any,
+    ) -> None:
+        super(Input, self).__init__(type=type)
+        # As an annotation, it is not allowed to initialize the _port_name.
+        self._port_name = None
+        self.description = description
+        self.path: Any = None
+
+        if path is not None and not isinstance(path, str):
+            # this logic will make dsl data binding expression working in the same way as yaml
+            # it's written to handle InputOutputBase, but there will be loop import if we import InputOutputBase here
+            self.path = str(path)
+        else:
+            self.path = path
+        self.path_on_compute = path_on_compute
+        self.mode = None if self._is_primitive_type else mode
+        self._update_default(default)
+        self.optional = optional
+        # set the flag to mark if the optional=True is inferred by us.
+        self._is_inferred_optional = False
+        self.min = min
+        self.max = max
+        self.enum = enum
+        self.datastore = datastore
+        intellectual_property = kwargs.pop("intellectual_property", None)
+        if intellectual_property:
+            self._intellectual_property = (
+                intellectual_property
+                if isinstance(intellectual_property, IntellectualProperty)
+                else IntellectualProperty(**intellectual_property)
+            )
+        # normalize properties like ["default", "min", "max", "optional"]
+        self._normalize_self_properties()
+
+        self._validate_parameter_combinations()
+
+    @property
+    def _allowed_types(self) -> Any:
+        if self._multiple_types:
+            return None
+        return IOConstants.PRIMITIVE_STR_2_TYPE.get(self.type)
+
+    @property
+    def _is_primitive_type(self) -> bool:
+        if self._multiple_types:
+            # note: we suppose that no primitive type will be included when there are multiple types
+            return False
+        return self.type in IOConstants.PRIMITIVE_STR_2_TYPE
+
+    @property
+    def _multiple_types(self) -> bool:
+        """Returns True if this input has multiple types.
+
+        Currently, there are two scenarios that need to check this property:
+        1. before `in` as it may throw exception; there will be `in` operation for validation/transformation.
+        2. `str()` of list is not ideal, so we need to manually create its string result.
+
+        :return: Whether this input has multiple types
+        :rtype: bool
+        """
+        return isinstance(self.type, list)
+
+    def _is_literal(self) -> bool:
+        """Whether this input is a literal
+
+        Override this function as `self.type` can be list and not hashable for operation `in`.
+
+        :return: Whether is a literal
+        :rtype: bool
+        """
+        return not self._multiple_types and super(Input, self)._is_literal()
+
+    def _is_enum(self) -> bool:
+        """Whether input is an enum
+
+        :return: True if the input is enum.
+        :rtype: bool
+        """
+        res: bool = self.type == ComponentParameterTypes.STRING and self.enum
+        return res
+
+    def _to_dict(self) -> Dict:
+        """Convert the Input object to a dict.
+
+        :return: Dictionary representation of Input
+        :rtype: Dict
+        """
+        keys = self._IO_KEYS
+        result = {key: getattr(self, key) for key in keys}
+        res: dict = _remove_empty_values(result)
+        return res
+
+    def _parse(self, val: Any) -> Union[int, float, bool, str, Any]:
+        """Parse value passed from command line.
+
+        :param val: The input value
+        :type val: T
+        :return: The parsed value.
+        :rtype: Union[int, float, bool, str, T]
+        """
+        if self.type == "integer":
+            return int(float(val))  # backend returns 10.0,for integer, parse it to float before int
+        if self.type == "number":
+            return float(val)
+        if self.type == "boolean":
+            lower_val = str(val).lower()
+            if lower_val not in {"true", "false"}:
+                msg = "Boolean parameter '{}' only accept True/False, got {}."
+                raise ValidationException(
+                    message=msg.format(self._port_name, val),
+                    no_personal_data_message=msg.format("[self._port_name]", "[val]"),
+                    error_category=ErrorCategory.USER_ERROR,
+                    target=ErrorTarget.PIPELINE,
+                    error_type=ValidationErrorType.INVALID_VALUE,
+                )
+            return lower_val == "true"
+        if self.type == "string":
+            return val if isinstance(val, str) else str(val)
+        return val
+
+    def _parse_and_validate(self, val: Any) -> Union[int, float, bool, str, Any]:
+        """Parse the val passed from the command line and validate the value.
+
+        :param val: The input string value from the command line.
+        :type val: T
+        :return: The parsed value, an exception will be raised if the value is invalid.
+        :rtype: Union[int, float, bool, str, T]
+        """
+        if self._is_primitive_type:
+            val = self._parse(val) if isinstance(val, str) else val
+            self._validate_or_throw(val)
+        return val
+
+    def _update_name(self, name: Any) -> None:
+        self._port_name = name
+
+    def _update_default(self, default_value: Any) -> None:
+        """Update provided default values.
+
+        :param default_value: The default value of the Input
+        :type default_value: Any
+        """
+        name = "" if not self._port_name else f"{self._port_name!r} "
+        msg_prefix = f"Default value of Input {name}"
+
+        if not self._is_primitive_type and default_value is not None:
+            msg = f"{msg_prefix}cannot be set: Non-primitive type Input has no default value."
+            raise UserErrorException(msg)
+        if isinstance(default_value, float) and not math.isfinite(default_value):
+            # Since nan/inf cannot be stored in the backend, just ignore them.
+            # logger.warning("Float default value %r is not allowed, ignored." % default_value)
+            return
+        # pylint: disable=pointless-string-statement
+        """Update provided default values.
+        Here we need to make sure the type of default value is allowed or it could be parsed..
+        """
+        if default_value is not None:
+            if type(default_value) not in IOConstants.PRIMITIVE_TYPE_2_STR:
+                msg = (
+                    f"{msg_prefix}cannot be set: type must be one of "
+                    f"{list(IOConstants.PRIMITIVE_TYPE_2_STR.values())}, got '{type(default_value)}'."
+                )
+                raise UserErrorException(msg)
+
+            if not isinstance(default_value, self._allowed_types):
+                try:
+                    default_value = self._parse(default_value)
+                # return original validation exception which is custom defined if raised by self._parse
+                except ValidationException as e:
+                    raise e
+                except Exception as e:
+                    msg = f"{msg_prefix}cannot be parsed, got '{default_value}', type = {type(default_value)!r}."
+                    raise UserErrorException(msg) from e
+        self.default = default_value
+
+    def _validate_or_throw(self, value: Any) -> None:
+        """Validate input parameter value, throw exception if not as expected.
+
+        It will throw exception if validate failed, otherwise do nothing.
+
+        :param value: A value to validate
+        :type value: Any
+        """
+        if not self.optional and value is None:
+            msg = "Parameter {} cannot be None since it is not optional."
+            raise ValidationException(
+                message=msg.format(self._port_name),
+                no_personal_data_message=msg.format("[self._port_name]"),
+                error_category=ErrorCategory.USER_ERROR,
+                target=ErrorTarget.PIPELINE,
+                error_type=ValidationErrorType.INVALID_VALUE,
+            )
+        if self._allowed_types and value is not None:
+            if not isinstance(value, self._allowed_types):
+                msg = "Unexpected data type for parameter '{}'. Expected {} but got {}."
+                raise ValidationException(
+                    message=msg.format(self._port_name, self._allowed_types, type(value)),
+                    no_personal_data_message=msg.format("[_port_name]", self._allowed_types, type(value)),
+                    error_category=ErrorCategory.USER_ERROR,
+                    target=ErrorTarget.PIPELINE,
+                    error_type=ValidationErrorType.INVALID_VALUE,
+                )
+        # for numeric values, need extra check for min max value
+        if not self._multiple_types and self.type in ("integer", "number"):
+            if self.min is not None and value < self.min:
+                msg = "Parameter '{}' should not be less than {}."
+                raise ValidationException(
+                    message=msg.format(self._port_name, self.min),
+                    no_personal_data_message=msg.format("[_port_name]", self.min),
+                    error_category=ErrorCategory.USER_ERROR,
+                    target=ErrorTarget.PIPELINE,
+                    error_type=ValidationErrorType.INVALID_VALUE,
+                )
+            if self.max is not None and value > self.max:
+                msg = "Parameter '{}' should not be greater than {}."
+                raise ValidationException(
+                    message=msg.format(self._port_name, self.max),
+                    no_personal_data_message=msg.format("[_port_name]", self.max),
+                    error_category=ErrorCategory.USER_ERROR,
+                    target=ErrorTarget.PIPELINE,
+                    error_type=ValidationErrorType.INVALID_VALUE,
+                )
+
+    def _get_python_builtin_type_str(self) -> str:
+        """Get python builtin type for current input in string, eg: str.
+
+        Return yaml type if not available.
+
+        :return: The name of the input type
+        :rtype: str
+        """
+        if self._multiple_types:
+            return "[" + ", ".join(self.type) + "]"
+        if self._is_primitive_type:
+            res_primitive_type: str = IOConstants.PRIMITIVE_STR_2_TYPE[self.type].__name__
+            return res_primitive_type
+        res: str = self.type
+        return res
+
+    def _validate_parameter_combinations(self) -> None:
+        """Validate different parameter combinations according to type."""
+        parameters = ["type", "path", "mode", "default", "min", "max"]
+        parameters_dict: dict = {key: getattr(self, key, None) for key in parameters}
+        type = parameters_dict.pop("type")
+
+        # validate parameter combination
+        if not self._multiple_types and type in IOConstants.INPUT_TYPE_COMBINATION:
+            valid_parameters = IOConstants.INPUT_TYPE_COMBINATION[type]
+            for key, value in parameters_dict.items():
+                if key not in valid_parameters and value is not None:
+                    msg = "Invalid parameter for '{}' Input, parameter '{}' should be None but got '{}'"
+                    raise ValidationException(
+                        message=msg.format(type, key, value),
+                        no_personal_data_message=msg.format("[type]", "[parameter]", "[parameter_value]"),
+                        error_category=ErrorCategory.USER_ERROR,
+                        target=ErrorTarget.PIPELINE,
+                        error_type=ValidationErrorType.INVALID_VALUE,
+                    )
+
+    def _simple_parse(self, value: Any, _type: Any = None) -> Any:
+        if self._multiple_types:
+            return value
+        if _type is None:
+            _type = self.type
+        if _type in IOConstants.PARAM_PARSERS:
+            return IOConstants.PARAM_PARSERS[_type](value)
+        return value
+
+    def _normalize_self_properties(self) -> None:
+        # parse value from string to its original type. eg: "false" -> False
+        for key in ["min", "max"]:
+            if getattr(self, key) is not None:
+                origin_value = getattr(self, key)
+                new_value = self._simple_parse(origin_value)
+                setattr(self, key, new_value)
+        if self.optional:
+            self.optional = self._simple_parse(getattr(self, "optional", "false"), _type="boolean")
+
+    @classmethod
+    def _get_input_by_type(cls, t: type, optional: Any = None) -> Optional["Input"]:
+        if t in IOConstants.PRIMITIVE_TYPE_2_STR:
+            return cls(type=IOConstants.PRIMITIVE_TYPE_2_STR[t], optional=optional)
+        return None
+
+    @classmethod
+    def _get_default_unknown_input(cls, optional: Optional[bool] = None) -> "Input":
+        # Set type as None here to avoid schema validation failed
+        res: Input = cls(type=None, optional=optional)  # type: ignore
+        return res
+
+    @classmethod
+    def _get_param_with_standard_annotation(cls, func: Any) -> Dict:
+        return _get_param_with_standard_annotation(func, is_func=True)
+
+    def _to_rest_object(self) -> Dict:
+        # this is for component rest object when using Input as component inputs, as for job input usage,
+        # rest object is generated by extracting Input's properties, see details in to_rest_dataset_literal_inputs()
+        result = self._to_dict()
+        # parse string -> String, integer -> Integer, etc.
+        if result["type"] in IOConstants.TYPE_MAPPING_YAML_2_REST:
+            result["type"] = IOConstants.TYPE_MAPPING_YAML_2_REST[result["type"]]
+        return result
+
+    @classmethod
+    def _map_from_rest_type(cls, _type: Union[str, List]) -> Union[str, List]:
+        # this is for component rest object when using Input as component inputs
+        reversed_data_type_mapping = {v: k for k, v in IOConstants.TYPE_MAPPING_YAML_2_REST.items()}
+        # parse String -> string, Integer -> integer, etc
+        if not isinstance(_type, list) and _type in reversed_data_type_mapping:
+            res: str = reversed_data_type_mapping[_type]
+            return res
+        return _type
+
+    @classmethod
+    def _from_rest_object(cls, obj: Dict) -> "Input":
+        obj["type"] = cls._map_from_rest_type(obj["type"])
+
+        return cls(**obj)