We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Add tooltip to st.metric(0 to provide more information on a KPI

### Problem

It would help our business users to have a bit more description available on KPIs.

### Solution

**MVP:** Could we add a tooltip to the _st.metric()_ function?   That would provide a lot of drill down flexibility, background description and the ability to use f-strings to include more detailed information



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lib/streamlit/elements/metric.py`
Content:
```
1 # Copyright 2018-2022 Streamlit Inc.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #    http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 from textwrap import dedent
16 from typing import cast, TYPE_CHECKING, Union
17 from typing_extensions import TypeAlias, Literal
18 
19 import attr
20 from streamlit.errors import StreamlitAPIException
21 from streamlit.proto.Metric_pb2 import Metric as MetricProto
22 
23 from .utils import clean_text
24 
25 if TYPE_CHECKING:
26     import numpy as np
27 
28     from streamlit.delta_generator import DeltaGenerator
29 
30 
31 Value: TypeAlias = Union["np.integer", "np.floating", float, str, None]
32 Delta: TypeAlias = Union[float, str, None]
33 DeltaColor: TypeAlias = Literal["normal", "inverse", "off"]
34 
35 
36 @attr.s(auto_attribs=True, slots=True, frozen=True)
37 class MetricColorAndDirection:
38     color: "MetricProto.MetricColor.ValueType"
39     direction: "MetricProto.MetricDirection.ValueType"
40 
41 
42 class MetricMixin:
43     def metric(
44         self,
45         label: str,
46         value: Value,
47         delta: Delta = None,
48         delta_color: DeltaColor = "normal",
49     ) -> "DeltaGenerator":
50         """Display a metric in big bold font, with an optional indicator of how the metric changed.
51 
52         Tip: If you want to display a large number, it may be a good idea to
53         shorten it using packages like `millify <https://github.com/azaitsev/millify>`_
54         or `numerize <https://github.com/davidsa03/numerize>`_. E.g. ``1234`` can be
55         displayed as ``1.2k`` using ``st.metric("Short number", millify(1234))``.
56 
57         Parameters
58         ----------
59         label : str
60             The header or Title for the metric
61         value : int, float, str, or None
62              Value of the metric. None is rendered as a long dash.
63         delta : int, float, str, or None
64             Indicator of how the metric changed, rendered with an arrow below
65             the metric. If delta is negative (int/float) or starts with a minus
66             sign (str), the arrow points down and the text is red; else the
67             arrow points up and the text is green. If None (default), no delta
68             indicator is shown.
69         delta_color : str
70              If "normal" (default), the delta indicator is shown as described
71              above. If "inverse", it is red when positive and green when
72              negative. This is useful when a negative change is considered
73              good, e.g. if cost decreased. If "off", delta is  shown in gray
74              regardless of its value.
75 
76         Example
77         -------
78         >>> st.metric(label="Temperature", value="70 °F", delta="1.2 °F")
79 
80         .. output::
81             https://share.streamlit.io/streamlit/docs/main/python/api-examples-source/metric.example1.py
82             height: 210px
83 
84         ``st.metric`` looks especially nice in combination with ``st.columns``:
85 
86         >>> col1, col2, col3 = st.columns(3)
87         >>> col1.metric("Temperature", "70 °F", "1.2 °F")
88         >>> col2.metric("Wind", "9 mph", "-8%")
89         >>> col3.metric("Humidity", "86%", "4%")
90 
91         .. output::
92             https://share.streamlit.io/streamlit/docs/main/python/api-examples-source/metric.example2.py
93             height: 210px
94 
95         The delta indicator color can also be inverted or turned off:
96 
97         >>> st.metric(label="Gas price", value=4, delta=-0.5,
98         ...     delta_color="inverse")
99         >>>
100         >>> st.metric(label="Active developers", value=123, delta=123,
101         ...     delta_color="off")
102 
103         .. output::
104             https://share.streamlit.io/streamlit/docs/main/python/api-examples-source/metric.example3.py
105             height: 320px
106 
107         """
108         metric_proto = MetricProto()
109         metric_proto.body = self.parse_value(value)
110         metric_proto.label = self.parse_label(label)
111         metric_proto.delta = self.parse_delta(delta)
112 
113         color_and_direction = self.determine_delta_color_and_direction(
114             cast(DeltaColor, clean_text(delta_color)), delta
115         )
116         metric_proto.color = color_and_direction.color
117         metric_proto.direction = color_and_direction.direction
118 
119         return self.dg._enqueue("metric", metric_proto)
120 
121     @staticmethod
122     def parse_label(label: str) -> str:
123         if not isinstance(label, str):
124             raise TypeError(
125                 f"'{str(label)}' is of type {str(type(label))}, which is not an accepted type."
126                 " label only accepts: str. Please convert the label to an accepted type."
127             )
128         return label
129 
130     @staticmethod
131     def parse_value(value: Value) -> str:
132         if value is None:
133             return "—"
134         if isinstance(value, int) or isinstance(value, float) or isinstance(value, str):
135             return str(value)
136         elif hasattr(value, "item"):
137             # Add support for numpy values (e.g. int16, float64, etc.)
138             try:
139                 # Item could also be just a variable, so we use try, except
140                 if isinstance(value.item(), float) or isinstance(value.item(), int):
141                     return str(value.item())
142             except Exception:
143                 pass
144 
145         raise TypeError(
146             f"'{str(value)}' is of type {str(type(value))}, which is not an accepted type."
147             " value only accepts: int, float, str, or None."
148             " Please convert the value to an accepted type."
149         )
150 
151     @staticmethod
152     def parse_delta(delta: Delta) -> str:
153         if delta is None or delta == "":
154             return ""
155         if isinstance(delta, str):
156             return dedent(delta)
157         elif isinstance(delta, int) or isinstance(delta, float):
158             return str(delta)
159         else:
160             raise TypeError(
161                 f"'{str(delta)}' is of type {str(type(delta))}, which is not an accepted type."
162                 " delta only accepts: int, float, str, or None."
163                 " Please convert the value to an accepted type."
164             )
165 
166     def determine_delta_color_and_direction(
167         self,
168         delta_color: DeltaColor,
169         delta: Delta,
170     ) -> MetricColorAndDirection:
171         if delta_color not in {"normal", "inverse", "off"}:
172             raise StreamlitAPIException(
173                 f"'{str(delta_color)}' is not an accepted value. delta_color only accepts: "
174                 "'normal', 'inverse', or 'off'"
175             )
176 
177         if delta is None or delta == "":
178             return MetricColorAndDirection(
179                 color=MetricProto.MetricColor.GRAY,
180                 direction=MetricProto.MetricDirection.NONE,
181             )
182 
183         if self.is_negative(delta):
184             if delta_color == "normal":
185                 cd_color = MetricProto.MetricColor.RED
186             elif delta_color == "inverse":
187                 cd_color = MetricProto.MetricColor.GREEN
188             else:
189                 cd_color = MetricProto.MetricColor.GRAY
190             cd_direction = MetricProto.MetricDirection.DOWN
191         else:
192             if delta_color == "normal":
193                 cd_color = MetricProto.MetricColor.GREEN
194             elif delta_color == "inverse":
195                 cd_color = MetricProto.MetricColor.RED
196             else:
197                 cd_color = MetricProto.MetricColor.GRAY
198             cd_direction = MetricProto.MetricDirection.UP
199 
200         return MetricColorAndDirection(
201             color=cd_color,
202             direction=cd_direction,
203         )
204 
205     @staticmethod
206     def is_negative(delta: Delta) -> bool:
207         return dedent(str(delta)).startswith("-")
208 
209     @property
210     def dg(self) -> "DeltaGenerator":
211         return cast("DeltaGenerator", self)
212
```

Path: `e2e/scripts/st_tooltips.py`
Content:
```
1 # Copyright 2018-2022 Streamlit Inc.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #    http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 import streamlit as st
16 from datetime import datetime
17 
18 default_tooltip = """
19 This is a really long tooltip.
20 
21 Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut ut turpis vitae
22 justo ornare venenatis a vitae leo. Donec mollis ornare ante, eu ultricies
23 tellus ornare eu. Donec eros risus, ultrices ut eleifend vel, auctor eu turpis.
24 In consectetur erat vel ante accumsan, a egestas urna aliquet. Nullam eget
25 sapien eget diam euismod eleifend. Nulla purus enim, finibus ut velit eu,
26 malesuada dictum nulla. In non arcu et risus maximus fermentum eget nec ante.
27 """.strip()
28 
29 leading_indent_code_tooltip = """
30 Code:
31 
32     This
33     is
34     a
35     code
36     block!"""
37 
38 leading_indent_regular_text_tooltip = """
39 This is a regular text block!
40 Test1
41 Test2
42 
43 """
44 
45 indented_code_tooltip = """
46 Code:
47 
48     for i in range(10):
49         x = i * 10
50         print(x)
51     """
52 
53 no_indent_tooltip = "thisisatooltipwithnoindents. It has some spaces but no idents."
54 
55 st.text_input("some input text", "default text", help=default_tooltip)
56 st.number_input("number input", value=1, help=leading_indent_code_tooltip)
57 st.checkbox("some checkbox", help=leading_indent_regular_text_tooltip)
58 st.radio("best animal", ("tiger", "giraffe", "bear"), 0, help=indented_code_tooltip)
59 st.selectbox("selectbox", ("a", "b", "c"), 0, help=default_tooltip)
60 st.time_input("time", datetime(2019, 7, 6, 21, 15), help=leading_indent_code_tooltip)
61 st.date_input(
62     "date", datetime(2019, 7, 6, 21, 15), help=leading_indent_regular_text_tooltip
63 )
64 st.slider("slider", 0, 100, 50, help=indented_code_tooltip)
65 st.color_picker("color picker", help=no_indent_tooltip)
66 st.file_uploader("file uploader", help=default_tooltip)
67 st.multiselect(
68     "multiselect", ["a", "b", "c"], ["a", "b"], help=leading_indent_code_tooltip
69 )
70 st.text_area("textarea", help=leading_indent_regular_text_tooltip)
71 st.select_slider("selectslider", options=["a", "b", "c"], help=indented_code_tooltip)
72 st.button("some button", help=no_indent_tooltip)
73
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/e2e/scripts/st_tooltips.py b/e2e/scripts/st_tooltips.py
--- a/e2e/scripts/st_tooltips.py
+++ b/e2e/scripts/st_tooltips.py
@@ -70,3 +70,4 @@
 st.text_area("textarea", help=leading_indent_regular_text_tooltip)
 st.select_slider("selectslider", options=["a", "b", "c"], help=indented_code_tooltip)
 st.button("some button", help=no_indent_tooltip)
+st.metric("some metric", value=500, help=no_indent_tooltip)
diff --git a/lib/streamlit/elements/metric.py b/lib/streamlit/elements/metric.py
--- a/lib/streamlit/elements/metric.py
+++ b/lib/streamlit/elements/metric.py
@@ -13,7 +13,7 @@
 # limitations under the License.
 
 from textwrap import dedent
-from typing import cast, TYPE_CHECKING, Union
+from typing import cast, TYPE_CHECKING, Union, Optional
 from typing_extensions import TypeAlias, Literal
 
 import attr
@@ -46,6 +46,7 @@
         value: Value,
         delta: Delta = None,
         delta_color: DeltaColor = "normal",
+        help: Optional[str] = None,
     ) -> "DeltaGenerator":
         """Display a metric in big bold font, with an optional indicator of how the metric changed.
 
@@ -72,6 +73,8 @@
              negative. This is useful when a negative change is considered
              good, e.g. if cost decreased. If "off", delta is  shown in gray
              regardless of its value.
+        help : str
+            An optional tooltip that gets displayed next to the metric label.
 
         Example
         -------
@@ -109,6 +112,8 @@
         metric_proto.body = self.parse_value(value)
         metric_proto.label = self.parse_label(label)
         metric_proto.delta = self.parse_delta(delta)
+        if help is not None:
+            metric_proto.help = dedent(help)
 
         color_and_direction = self.determine_delta_color_and_direction(
             cast(DeltaColor, clean_text(delta_color)), delta

{"golden_diff": "diff --git a/e2e/scripts/st_tooltips.py b/e2e/scripts/st_tooltips.py\n--- a/e2e/scripts/st_tooltips.py\n+++ b/e2e/scripts/st_tooltips.py\n@@ -70,3 +70,4 @@\n st.text_area(\"textarea\", help=leading_indent_regular_text_tooltip)\n st.select_slider(\"selectslider\", options=[\"a\", \"b\", \"c\"], help=indented_code_tooltip)\n st.button(\"some button\", help=no_indent_tooltip)\n+st.metric(\"some metric\", value=500, help=no_indent_tooltip)\ndiff --git a/lib/streamlit/elements/metric.py b/lib/streamlit/elements/metric.py\n--- a/lib/streamlit/elements/metric.py\n+++ b/lib/streamlit/elements/metric.py\n@@ -13,7 +13,7 @@\n # limitations under the License.\n \n from textwrap import dedent\n-from typing import cast, TYPE_CHECKING, Union\n+from typing import cast, TYPE_CHECKING, Union, Optional\n from typing_extensions import TypeAlias, Literal\n \n import attr\n@@ -46,6 +46,7 @@\n         value: Value,\n         delta: Delta = None,\n         delta_color: DeltaColor = \"normal\",\n+        help: Optional[str] = None,\n     ) -> \"DeltaGenerator\":\n         \"\"\"Display a metric in big bold font, with an optional indicator of how the metric changed.\n \n@@ -72,6 +73,8 @@\n              negative. This is useful when a negative change is considered\n              good, e.g. if cost decreased. If \"off\", delta is  shown in gray\n              regardless of its value.\n+        help : str\n+            An optional tooltip that gets displayed next to the metric label.\n \n         Example\n         -------\n@@ -109,6 +112,8 @@\n         metric_proto.body = self.parse_value(value)\n         metric_proto.label = self.parse_label(label)\n         metric_proto.delta = self.parse_delta(delta)\n+        if help is not None:\n+            metric_proto.help = dedent(help)\n \n         color_and_direction = self.determine_delta_color_and_direction(\n             cast(DeltaColor, clean_text(delta_color)), delta\n", "issue": "Add tooltip to st.metric(0 to provide more information on a KPI\n\r\n### Problem\r\n\r\nIt would help our business users to have a bit more description available on KPIs.\r\n\r\n### Solution\r\n\r\n**MVP:** Could we add a tooltip to the _st.metric()_ function?   That would provide a lot of drill down flexibility, background description and the ability to use f-strings to include more detailed information\r\n\r\n\n", "before_files": [{"content": "# Copyright 2018-2022 Streamlit Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom textwrap import dedent\nfrom typing import cast, TYPE_CHECKING, Union\nfrom typing_extensions import TypeAlias, Literal\n\nimport attr\nfrom streamlit.errors import StreamlitAPIException\nfrom streamlit.proto.Metric_pb2 import Metric as MetricProto\n\nfrom .utils import clean_text\n\nif TYPE_CHECKING:\n    import numpy as np\n\n    from streamlit.delta_generator import DeltaGenerator\n\n\nValue: TypeAlias = Union[\"np.integer\", \"np.floating\", float, str, None]\nDelta: TypeAlias = Union[float, str, None]\nDeltaColor: TypeAlias = Literal[\"normal\", \"inverse\", \"off\"]\n\n\n@attr.s(auto_attribs=True, slots=True, frozen=True)\nclass MetricColorAndDirection:\n    color: \"MetricProto.MetricColor.ValueType\"\n    direction: \"MetricProto.MetricDirection.ValueType\"\n\n\nclass MetricMixin:\n    def metric(\n        self,\n        label: str,\n        value: Value,\n        delta: Delta = None,\n        delta_color: DeltaColor = \"normal\",\n    ) -> \"DeltaGenerator\":\n        \"\"\"Display a metric in big bold font, with an optional indicator of how the metric changed.\n\n        Tip: If you want to display a large number, it may be a good idea to\n        shorten it using packages like `millify <https://github.com/azaitsev/millify>`_\n        or `numerize <https://github.com/davidsa03/numerize>`_. E.g. ``1234`` can be\n        displayed as ``1.2k`` using ``st.metric(\"Short number\", millify(1234))``.\n\n        Parameters\n        ----------\n        label : str\n            The header or Title for the metric\n        value : int, float, str, or None\n             Value of the metric. None is rendered as a long dash.\n        delta : int, float, str, or None\n            Indicator of how the metric changed, rendered with an arrow below\n            the metric. If delta is negative (int/float) or starts with a minus\n            sign (str), the arrow points down and the text is red; else the\n            arrow points up and the text is green. If None (default), no delta\n            indicator is shown.\n        delta_color : str\n             If \"normal\" (default), the delta indicator is shown as described\n             above. If \"inverse\", it is red when positive and green when\n             negative. This is useful when a negative change is considered\n             good, e.g. if cost decreased. If \"off\", delta is  shown in gray\n             regardless of its value.\n\n        Example\n        -------\n        >>> st.metric(label=\"Temperature\", value=\"70 \u00b0F\", delta=\"1.2 \u00b0F\")\n\n        .. output::\n            https://share.streamlit.io/streamlit/docs/main/python/api-examples-source/metric.example1.py\n            height: 210px\n\n        ``st.metric`` looks especially nice in combination with ``st.columns``:\n\n        >>> col1, col2, col3 = st.columns(3)\n        >>> col1.metric(\"Temperature\", \"70 \u00b0F\", \"1.2 \u00b0F\")\n        >>> col2.metric(\"Wind\", \"9 mph\", \"-8%\")\n        >>> col3.metric(\"Humidity\", \"86%\", \"4%\")\n\n        .. output::\n            https://share.streamlit.io/streamlit/docs/main/python/api-examples-source/metric.example2.py\n            height: 210px\n\n        The delta indicator color can also be inverted or turned off:\n\n        >>> st.metric(label=\"Gas price\", value=4, delta=-0.5,\n        ...     delta_color=\"inverse\")\n        >>>\n        >>> st.metric(label=\"Active developers\", value=123, delta=123,\n        ...     delta_color=\"off\")\n\n        .. output::\n            https://share.streamlit.io/streamlit/docs/main/python/api-examples-source/metric.example3.py\n            height: 320px\n\n        \"\"\"\n        metric_proto = MetricProto()\n        metric_proto.body = self.parse_value(value)\n        metric_proto.label = self.parse_label(label)\n        metric_proto.delta = self.parse_delta(delta)\n\n        color_and_direction = self.determine_delta_color_and_direction(\n            cast(DeltaColor, clean_text(delta_color)), delta\n        )\n        metric_proto.color = color_and_direction.color\n        metric_proto.direction = color_and_direction.direction\n\n        return self.dg._enqueue(\"metric\", metric_proto)\n\n    @staticmethod\n    def parse_label(label: str) -> str:\n        if not isinstance(label, str):\n            raise TypeError(\n                f\"'{str(label)}' is of type {str(type(label))}, which is not an accepted type.\"\n                \" label only accepts: str. Please convert the label to an accepted type.\"\n            )\n        return label\n\n    @staticmethod\n    def parse_value(value: Value) -> str:\n        if value is None:\n            return \"\u2014\"\n        if isinstance(value, int) or isinstance(value, float) or isinstance(value, str):\n            return str(value)\n        elif hasattr(value, \"item\"):\n            # Add support for numpy values (e.g. int16, float64, etc.)\n            try:\n                # Item could also be just a variable, so we use try, except\n                if isinstance(value.item(), float) or isinstance(value.item(), int):\n                    return str(value.item())\n            except Exception:\n                pass\n\n        raise TypeError(\n            f\"'{str(value)}' is of type {str(type(value))}, which is not an accepted type.\"\n            \" value only accepts: int, float, str, or None.\"\n            \" Please convert the value to an accepted type.\"\n        )\n\n    @staticmethod\n    def parse_delta(delta: Delta) -> str:\n        if delta is None or delta == \"\":\n            return \"\"\n        if isinstance(delta, str):\n            return dedent(delta)\n        elif isinstance(delta, int) or isinstance(delta, float):\n            return str(delta)\n        else:\n            raise TypeError(\n                f\"'{str(delta)}' is of type {str(type(delta))}, which is not an accepted type.\"\n                \" delta only accepts: int, float, str, or None.\"\n                \" Please convert the value to an accepted type.\"\n            )\n\n    def determine_delta_color_and_direction(\n        self,\n        delta_color: DeltaColor,\n        delta: Delta,\n    ) -> MetricColorAndDirection:\n        if delta_color not in {\"normal\", \"inverse\", \"off\"}:\n            raise StreamlitAPIException(\n                f\"'{str(delta_color)}' is not an accepted value. delta_color only accepts: \"\n                \"'normal', 'inverse', or 'off'\"\n            )\n\n        if delta is None or delta == \"\":\n            return MetricColorAndDirection(\n                color=MetricProto.MetricColor.GRAY,\n                direction=MetricProto.MetricDirection.NONE,\n            )\n\n        if self.is_negative(delta):\n            if delta_color == \"normal\":\n                cd_color = MetricProto.MetricColor.RED\n            elif delta_color == \"inverse\":\n                cd_color = MetricProto.MetricColor.GREEN\n            else:\n                cd_color = MetricProto.MetricColor.GRAY\n            cd_direction = MetricProto.MetricDirection.DOWN\n        else:\n            if delta_color == \"normal\":\n                cd_color = MetricProto.MetricColor.GREEN\n            elif delta_color == \"inverse\":\n                cd_color = MetricProto.MetricColor.RED\n            else:\n                cd_color = MetricProto.MetricColor.GRAY\n            cd_direction = MetricProto.MetricDirection.UP\n\n        return MetricColorAndDirection(\n            color=cd_color,\n            direction=cd_direction,\n        )\n\n    @staticmethod\n    def is_negative(delta: Delta) -> bool:\n        return dedent(str(delta)).startswith(\"-\")\n\n    @property\n    def dg(self) -> \"DeltaGenerator\":\n        return cast(\"DeltaGenerator\", self)\n", "path": "lib/streamlit/elements/metric.py"}, {"content": "# Copyright 2018-2022 Streamlit Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport streamlit as st\nfrom datetime import datetime\n\ndefault_tooltip = \"\"\"\nThis is a really long tooltip.\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Ut ut turpis vitae\njusto ornare venenatis a vitae leo. Donec mollis ornare ante, eu ultricies\ntellus ornare eu. Donec eros risus, ultrices ut eleifend vel, auctor eu turpis.\nIn consectetur erat vel ante accumsan, a egestas urna aliquet. Nullam eget\nsapien eget diam euismod eleifend. Nulla purus enim, finibus ut velit eu,\nmalesuada dictum nulla. In non arcu et risus maximus fermentum eget nec ante.\n\"\"\".strip()\n\nleading_indent_code_tooltip = \"\"\"\nCode:\n\n    This\n    is\n    a\n    code\n    block!\"\"\"\n\nleading_indent_regular_text_tooltip = \"\"\"\nThis is a regular text block!\nTest1\nTest2\n\n\"\"\"\n\nindented_code_tooltip = \"\"\"\nCode:\n\n    for i in range(10):\n        x = i * 10\n        print(x)\n    \"\"\"\n\nno_indent_tooltip = \"thisisatooltipwithnoindents. It has some spaces but no idents.\"\n\nst.text_input(\"some input text\", \"default text\", help=default_tooltip)\nst.number_input(\"number input\", value=1, help=leading_indent_code_tooltip)\nst.checkbox(\"some checkbox\", help=leading_indent_regular_text_tooltip)\nst.radio(\"best animal\", (\"tiger\", \"giraffe\", \"bear\"), 0, help=indented_code_tooltip)\nst.selectbox(\"selectbox\", (\"a\", \"b\", \"c\"), 0, help=default_tooltip)\nst.time_input(\"time\", datetime(2019, 7, 6, 21, 15), help=leading_indent_code_tooltip)\nst.date_input(\n    \"date\", datetime(2019, 7, 6, 21, 15), help=leading_indent_regular_text_tooltip\n)\nst.slider(\"slider\", 0, 100, 50, help=indented_code_tooltip)\nst.color_picker(\"color picker\", help=no_indent_tooltip)\nst.file_uploader(\"file uploader\", help=default_tooltip)\nst.multiselect(\n    \"multiselect\", [\"a\", \"b\", \"c\"], [\"a\", \"b\"], help=leading_indent_code_tooltip\n)\nst.text_area(\"textarea\", help=leading_indent_regular_text_tooltip)\nst.select_slider(\"selectslider\", options=[\"a\", \"b\", \"c\"], help=indented_code_tooltip)\nst.button(\"some button\", help=no_indent_tooltip)\n", "path": "e2e/scripts/st_tooltips.py"}], "after_files": [{"content": "# Copyright 2018-2022 Streamlit Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom textwrap import dedent\nfrom typing import cast, TYPE_CHECKING, Union, Optional\nfrom typing_extensions import TypeAlias, Literal\n\nimport attr\nfrom streamlit.errors import StreamlitAPIException\nfrom streamlit.proto.Metric_pb2 import Metric as MetricProto\n\nfrom .utils import clean_text\n\nif TYPE_CHECKING:\n    import numpy as np\n\n    from streamlit.delta_generator import DeltaGenerator\n\n\nValue: TypeAlias = Union[\"np.integer\", \"np.floating\", float, str, None]\nDelta: TypeAlias = Union[float, str, None]\nDeltaColor: TypeAlias = Literal[\"normal\", \"inverse\", \"off\"]\n\n\n@attr.s(auto_attribs=True, slots=True, frozen=True)\nclass MetricColorAndDirection:\n    color: \"MetricProto.MetricColor.ValueType\"\n    direction: \"MetricProto.MetricDirection.ValueType\"\n\n\nclass MetricMixin:\n    def metric(\n        self,\n        label: str,\n        value: Value,\n        delta: Delta = None,\n        delta_color: DeltaColor = \"normal\",\n        help: Optional[str] = None,\n    ) -> \"DeltaGenerator\":\n        \"\"\"Display a metric in big bold font, with an optional indicator of how the metric changed.\n\n        Tip: If you want to display a large number, it may be a good idea to\n        shorten it using packages like `millify <https://github.com/azaitsev/millify>`_\n        or `numerize <https://github.com/davidsa03/numerize>`_. E.g. ``1234`` can be\n        displayed as ``1.2k`` using ``st.metric(\"Short number\", millify(1234))``.\n\n        Parameters\n        ----------\n        label : str\n            The header or Title for the metric\n        value : int, float, str, or None\n             Value of the metric. None is rendered as a long dash.\n        delta : int, float, str, or None\n            Indicator of how the metric changed, rendered with an arrow below\n            the metric. If delta is negative (int/float) or starts with a minus\n            sign (str), the arrow points down and the text is red; else the\n            arrow points up and the text is green. If None (default), no delta\n            indicator is shown.\n        delta_color : str\n             If \"normal\" (default), the delta indicator is shown as described\n             above. If \"inverse\", it is red when positive and green when\n             negative. This is useful when a negative change is considered\n             good, e.g. if cost decreased. If \"off\", delta is  shown in gray\n             regardless of its value.\n        help : str\n            An optional tooltip that gets displayed next to the metric label.\n\n        Example\n        -------\n        >>> st.metric(label=\"Temperature\", value=\"70 \u00b0F\", delta=\"1.2 \u00b0F\")\n\n        .. output::\n            https://share.streamlit.io/streamlit/docs/main/python/api-examples-source/metric.example1.py\n            height: 210px\n\n        ``st.metric`` looks especially nice in combination with ``st.columns``:\n\n        >>> col1, col2, col3 = st.columns(3)\n        >>> col1.metric(\"Temperature\", \"70 \u00b0F\", \"1.2 \u00b0F\")\n        >>> col2.metric(\"Wind\", \"9 mph\", \"-8%\")\n        >>> col3.metric(\"Humidity\", \"86%\", \"4%\")\n\n        .. output::\n            https://share.streamlit.io/streamlit/docs/main/python/api-examples-source/metric.example2.py\n            height: 210px\n\n        The delta indicator color can also be inverted or turned off:\n\n        >>> st.metric(label=\"Gas price\", value=4, delta=-0.5,\n        ...     delta_color=\"inverse\")\n        >>>\n        >>> st.metric(label=\"Active developers\", value=123, delta=123,\n        ...     delta_color=\"off\")\n\n        .. output::\n            https://share.streamlit.io/streamlit/docs/main/python/api-examples-source/metric.example3.py\n            height: 320px\n\n        \"\"\"\n        metric_proto = MetricProto()\n        metric_proto.body = self.parse_value(value)\n        metric_proto.label = self.parse_label(label)\n        metric_proto.delta = self.parse_delta(delta)\n        if help is not None:\n            metric_proto.help = dedent(help)\n\n        color_and_direction = self.determine_delta_color_and_direction(\n            cast(DeltaColor, clean_text(delta_color)), delta\n        )\n        metric_proto.color = color_and_direction.color\n        metric_proto.direction = color_and_direction.direction\n\n        return self.dg._enqueue(\"metric\", metric_proto)\n\n    @staticmethod\n    def parse_label(label: str) -> str:\n        if not isinstance(label, str):\n            raise TypeError(\n                f\"'{str(label)}' is of type {str(type(label))}, which is not an accepted type.\"\n                \" label only accepts: str. Please convert the label to an accepted type.\"\n            )\n        return label\n\n    @staticmethod\n    def parse_value(value: Value) -> str:\n        if value is None:\n            return \"\u2014\"\n        if isinstance(value, int) or isinstance(value, float) or isinstance(value, str):\n            return str(value)\n        elif hasattr(value, \"item\"):\n            # Add support for numpy values (e.g. int16, float64, etc.)\n            try:\n                # Item could also be just a variable, so we use try, except\n                if isinstance(value.item(), float) or isinstance(value.item(), int):\n                    return str(value.item())\n            except Exception:\n                pass\n\n        raise TypeError(\n            f\"'{str(value)}' is of type {str(type(value))}, which is not an accepted type.\"\n            \" value only accepts: int, float, str, or None.\"\n            \" Please convert the value to an accepted type.\"\n        )\n\n    @staticmethod\n    def parse_delta(delta: Delta) -> str:\n        if delta is None or delta == \"\":\n            return \"\"\n        if isinstance(delta, str):\n            return dedent(delta)\n        elif isinstance(delta, int) or isinstance(delta, float):\n            return str(delta)\n        else:\n            raise TypeError(\n                f\"'{str(delta)}' is of type {str(type(delta))}, which is not an accepted type.\"\n                \" delta only accepts: int, float, str, or None.\"\n                \" Please convert the value to an accepted type.\"\n            )\n\n    def determine_delta_color_and_direction(\n        self,\n        delta_color: DeltaColor,\n        delta: Delta,\n    ) -> MetricColorAndDirection:\n        if delta_color not in {\"normal\", \"inverse\", \"off\"}:\n            raise StreamlitAPIException(\n                f\"'{str(delta_color)}' is not an accepted value. delta_color only accepts: \"\n                \"'normal', 'inverse', or 'off'\"\n            )\n\n        if delta is None or delta == \"\":\n            return MetricColorAndDirection(\n                color=MetricProto.MetricColor.GRAY,\n                direction=MetricProto.MetricDirection.NONE,\n            )\n\n        if self.is_negative(delta):\n            if delta_color == \"normal\":\n                cd_color = MetricProto.MetricColor.RED\n            elif delta_color == \"inverse\":\n                cd_color = MetricProto.MetricColor.GREEN\n            else:\n                cd_color = MetricProto.MetricColor.GRAY\n            cd_direction = MetricProto.MetricDirection.DOWN\n        else:\n            if delta_color == \"normal\":\n                cd_color = MetricProto.MetricColor.GREEN\n            elif delta_color == \"inverse\":\n                cd_color = MetricProto.MetricColor.RED\n            else:\n                cd_color = MetricProto.MetricColor.GRAY\n            cd_direction = MetricProto.MetricDirection.UP\n\n        return MetricColorAndDirection(\n            color=cd_color,\n            direction=cd_direction,\n        )\n\n    @staticmethod\n    def is_negative(delta: Delta) -> bool:\n        return dedent(str(delta)).startswith(\"-\")\n\n    @property\n    def dg(self) -> \"DeltaGenerator\":\n        return cast(\"DeltaGenerator\", self)\n", "path": "lib/streamlit/elements/metric.py"}, {"content": "# Copyright 2018-2022 Streamlit Inc.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport streamlit as st\nfrom datetime import datetime\n\ndefault_tooltip = \"\"\"\nThis is a really long tooltip.\n\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Ut ut turpis vitae\njusto ornare venenatis a vitae leo. Donec mollis ornare ante, eu ultricies\ntellus ornare eu. Donec eros risus, ultrices ut eleifend vel, auctor eu turpis.\nIn consectetur erat vel ante accumsan, a egestas urna aliquet. Nullam eget\nsapien eget diam euismod eleifend. Nulla purus enim, finibus ut velit eu,\nmalesuada dictum nulla. In non arcu et risus maximus fermentum eget nec ante.\n\"\"\".strip()\n\nleading_indent_code_tooltip = \"\"\"\nCode:\n\n    This\n    is\n    a\n    code\n    block!\"\"\"\n\nleading_indent_regular_text_tooltip = \"\"\"\nThis is a regular text block!\nTest1\nTest2\n\n\"\"\"\n\nindented_code_tooltip = \"\"\"\nCode:\n\n    for i in range(10):\n        x = i * 10\n        print(x)\n    \"\"\"\n\nno_indent_tooltip = \"thisisatooltipwithnoindents. It has some spaces but no idents.\"\n\nst.text_input(\"some input text\", \"default text\", help=default_tooltip)\nst.number_input(\"number input\", value=1, help=leading_indent_code_tooltip)\nst.checkbox(\"some checkbox\", help=leading_indent_regular_text_tooltip)\nst.radio(\"best animal\", (\"tiger\", \"giraffe\", \"bear\"), 0, help=indented_code_tooltip)\nst.selectbox(\"selectbox\", (\"a\", \"b\", \"c\"), 0, help=default_tooltip)\nst.time_input(\"time\", datetime(2019, 7, 6, 21, 15), help=leading_indent_code_tooltip)\nst.date_input(\n    \"date\", datetime(2019, 7, 6, 21, 15), help=leading_indent_regular_text_tooltip\n)\nst.slider(\"slider\", 0, 100, 50, help=indented_code_tooltip)\nst.color_picker(\"color picker\", help=no_indent_tooltip)\nst.file_uploader(\"file uploader\", help=default_tooltip)\nst.multiselect(\n    \"multiselect\", [\"a\", \"b\", \"c\"], [\"a\", \"b\"], help=leading_indent_code_tooltip\n)\nst.text_area(\"textarea\", help=leading_indent_regular_text_tooltip)\nst.select_slider(\"selectslider\", options=[\"a\", \"b\", \"c\"], help=indented_code_tooltip)\nst.button(\"some button\", help=no_indent_tooltip)\nst.metric(\"some metric\", value=500, help=no_indent_tooltip)\n", "path": "e2e/scripts/st_tooltips.py"}]}