def number_input(
            self,
            label,
            min_value=None,
            max_value=None,
            value=NoValue(),
            step=None,
            format=None,
            key=None,
            help=None,
    ):
        """Display a numeric input widget.

        Parameters
        ----------
        label : str
            A short label explaining to the user what this input is for.
        min_value : int or float or None
            The minimum permitted value.
            If None, there will be no minimum.
        max_value : int or float or None
            The maximum permitted value.
            If None, there will be no maximum.
        value : int or float or None
            The value of this widget when it first renders.
            Defaults to min_value, or 0.0 if min_value is None
        step : int or float or None
            The stepping interval.
            Defaults to 1 if the value is an int, 0.01 otherwise.
            If the value is not specified, the format parameter will be used.
        format : str or None
            A printf-style format string controlling how the interface should
            display numbers. Output must be purely numeric. This does not impact
            the return value. Valid formatters: %d %e %f %g %i %u
        key : str
            An optional string to use as the unique key for the widget.
            If this is omitted, a key will be generated for the widget
            based on its content. Multiple widgets of the same type may
            not share the same key.
        help : str
            A tooltip that gets displayed next to the input.

        Returns
        -------
        int or float
            The current value of the numeric input widget. The return type
            will match the data type of the value parameter.

        Example
        -------
        >>> number = st.number_input('Insert a number')
        >>> st.write('The current number is ', number)
        """

        # Ensure that all arguments are of the same type.
        args = [min_value, max_value, value, step]

        int_args = all(
            isinstance(a, (numbers.Integral, type(None), NoValue))
            for a in args)

        float_args = all(
            isinstance(a, (float, type(None), NoValue)) for a in args)

        if not int_args and not float_args:
            raise StreamlitAPIException(
                "All numerical arguments must be of the same type."
                f"\n`value` has {type(value).__name__} type."
                f"\n`min_value` has {type(min_value).__name__} type."
                f"\n`max_value` has {type(max_value).__name__} type."
                f"\n`step` has {type(step).__name__} type.")

        if isinstance(value, NoValue):
            if min_value is not None:
                value = min_value
            elif int_args and float_args:
                value = 0.0  # if no values are provided, defaults to float
            elif int_args:
                value = 0
            else:
                value = 0.0

        int_value = isinstance(value, numbers.Integral)
        float_value = isinstance(value, float)

        if value is None:
            raise StreamlitAPIException(
                "Default value for number_input should be an int or a float.")
        else:
            if format is None:
                format = "%d" if int_value else "%0.2f"

            # Warn user if they format an int type as a float or vice versa.
            if format in ["%d", "%u", "%i"] and float_value:
                import streamlit as st

                st.warning("Warning: NumberInput value below has type float,"
                           f" but format {format} displays as integer.")
            elif format[-1] == "f" and int_value:
                import streamlit as st

                st.warning(
                    "Warning: NumberInput value below has type int so is"
                    f" displayed as int despite format string {format}.")

        if step is None:
            step = 1 if int_value else 0.01

        try:
            float(format % 2)
        except (TypeError, ValueError):
            raise StreamlitAPIException(
                "Format string for st.number_input contains invalid characters: %s"
                % format)

        # Ensure that the value matches arguments' types.
        all_ints = int_value and int_args

        if (min_value and min_value > value) or (max_value
                                                 and max_value < value):
            raise StreamlitAPIException(
                "The default `value` of %(value)s "
                "must lie between the `min_value` of %(min)s "
                "and the `max_value` of %(max)s, inclusively." % {
                    "value": value,
                    "min": min_value,
                    "max": max_value
                })

        # Bounds checks. JSNumber produces human-readable exceptions that
        # we simply re-package as StreamlitAPIExceptions.
        try:
            if all_ints:
                if min_value is not None:
                    JSNumber.validate_int_bounds(min_value, "`min_value`")
                if max_value is not None:
                    JSNumber.validate_int_bounds(max_value, "`max_value`")
                if step is not None:
                    JSNumber.validate_int_bounds(step, "`step`")
                JSNumber.validate_int_bounds(value, "`value`")
            else:
                if min_value is not None:
                    JSNumber.validate_float_bounds(min_value, "`min_value`")
                if max_value is not None:
                    JSNumber.validate_float_bounds(max_value, "`max_value`")
                if step is not None:
                    JSNumber.validate_float_bounds(step, "`step`")
                JSNumber.validate_float_bounds(value, "`value`")
        except JSNumberBoundsException as e:
            raise StreamlitAPIException(str(e))

        number_input_proto = NumberInputProto()
        number_input_proto.data_type = (NumberInputProto.INT if all_ints else
                                        NumberInputProto.FLOAT)
        number_input_proto.label = label
        number_input_proto.default = value
        number_input_proto.form_id = current_form_id(self.dg)
        if help is not None:
            number_input_proto.help = help

        if min_value is not None:
            number_input_proto.min = min_value
            number_input_proto.has_min = True

        if max_value is not None:
            number_input_proto.max = max_value
            number_input_proto.has_max = True

        if step is not None:
            number_input_proto.step = step

        if format is not None:
            number_input_proto.format = format

        ui_value = register_widget("number_input",
                                   number_input_proto,
                                   user_key=key)

        return_value = ui_value if ui_value is not None else value
        return self.dg._enqueue("number_input", number_input_proto,
                                return_value)
Exemple #2
0
    def _number_input(
        self,
        label: str,
        min_value: Optional[Number] = None,
        max_value: Optional[Number] = None,
        value: Union[NoValue, Number, None] = NoValue(),
        step: Optional[Number] = None,
        format: Optional[str] = None,
        key: Optional[Key] = None,
        help: Optional[str] = None,
        on_change: Optional[WidgetCallback] = None,
        args: Optional[WidgetArgs] = None,
        kwargs: Optional[WidgetKwargs] = None,
        *,  # keyword-only arguments:
        disabled: bool = False,
        ctx: Optional[ScriptRunContext] = None,
    ) -> Number:
        key = to_key(key)
        check_callback_rules(self.dg, on_change)
        check_session_state_rules(
            default_value=None if isinstance(value, NoValue) else value,
            key=key)

        # Ensure that all arguments are of the same type.
        number_input_args = [min_value, max_value, value, step]

        int_args = all(
            isinstance(a, (numbers.Integral, type(None), NoValue))
            for a in number_input_args)

        float_args = all(
            isinstance(a, (float, type(None), NoValue))
            for a in number_input_args)

        if not int_args and not float_args:
            raise StreamlitAPIException(
                "All numerical arguments must be of the same type."
                f"\n`value` has {type(value).__name__} type."
                f"\n`min_value` has {type(min_value).__name__} type."
                f"\n`max_value` has {type(max_value).__name__} type."
                f"\n`step` has {type(step).__name__} type.")

        if isinstance(value, NoValue):
            if min_value is not None:
                value = min_value
            elif int_args and float_args:
                value = 0.0  # if no values are provided, defaults to float
            elif int_args:
                value = 0
            else:
                value = 0.0

        int_value = isinstance(value, numbers.Integral)
        float_value = isinstance(value, float)

        if value is None:
            raise StreamlitAPIException(
                "Default value for number_input should be an int or a float.")
        else:
            if format is None:
                format = "%d" if int_value else "%0.2f"

            # Warn user if they format an int type as a float or vice versa.
            if format in ["%d", "%u", "%i"] and float_value:
                import streamlit as st

                st.warning("Warning: NumberInput value below has type float,"
                           f" but format {format} displays as integer.")
            elif format[-1] == "f" and int_value:
                import streamlit as st

                st.warning(
                    "Warning: NumberInput value below has type int so is"
                    f" displayed as int despite format string {format}.")

        if step is None:
            step = 1 if int_value else 0.01

        try:
            float(format % 2)
        except (TypeError, ValueError):
            raise StreamlitAPIException(
                "Format string for st.number_input contains invalid characters: %s"
                % format)

        # Ensure that the value matches arguments' types.
        all_ints = int_value and int_args

        if (min_value and min_value > value) or (max_value
                                                 and max_value < value):
            raise StreamlitAPIException(
                "The default `value` of %(value)s "
                "must lie between the `min_value` of %(min)s "
                "and the `max_value` of %(max)s, inclusively." % {
                    "value": value,
                    "min": min_value,
                    "max": max_value
                })

        # Bounds checks. JSNumber produces human-readable exceptions that
        # we simply re-package as StreamlitAPIExceptions.
        try:
            if all_ints:
                if min_value is not None:
                    JSNumber.validate_int_bounds(min_value,
                                                 "`min_value`")  # type: ignore
                if max_value is not None:
                    JSNumber.validate_int_bounds(max_value,
                                                 "`max_value`")  # type: ignore
                if step is not None:
                    JSNumber.validate_int_bounds(step,
                                                 "`step`")  # type: ignore
                JSNumber.validate_int_bounds(value, "`value`")  # type: ignore
            else:
                if min_value is not None:
                    JSNumber.validate_float_bounds(min_value, "`min_value`")
                if max_value is not None:
                    JSNumber.validate_float_bounds(max_value, "`max_value`")
                if step is not None:
                    JSNumber.validate_float_bounds(step, "`step`")
                JSNumber.validate_float_bounds(value, "`value`")
        except JSNumberBoundsException as e:
            raise StreamlitAPIException(str(e))

        number_input_proto = NumberInputProto()
        number_input_proto.data_type = (NumberInputProto.INT if all_ints else
                                        NumberInputProto.FLOAT)
        number_input_proto.label = label
        number_input_proto.default = value
        number_input_proto.form_id = current_form_id(self.dg)
        number_input_proto.disabled = disabled
        if help is not None:
            number_input_proto.help = dedent(help)

        if min_value is not None:
            number_input_proto.min = min_value
            number_input_proto.has_min = True

        if max_value is not None:
            number_input_proto.max = max_value
            number_input_proto.has_max = True

        if step is not None:
            number_input_proto.step = step

        if format is not None:
            number_input_proto.format = format

        def deserialize_number_input(ui_value, widget_id=""):
            return ui_value if ui_value is not None else value

        current_value, set_frontend_value = register_widget(
            "number_input",
            number_input_proto,
            user_key=key,
            on_change_handler=on_change,
            args=args,
            kwargs=kwargs,
            deserializer=deserialize_number_input,
            serializer=lambda x: x,
            ctx=ctx,
        )

        if set_frontend_value:
            number_input_proto.value = current_value
            number_input_proto.set_value = True

        self.dg._enqueue("number_input", number_input_proto)
        return cast(Number, current_value)