We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Auto-Complete Password with Password Manager
### Summary

When filling a username/password combo from a password manager, it only fills the username, not the password, because the HTML password field has the property `autocomplete="new-password"`. Removing this property results in the expected behaviour.

### Steps to reproduce

Code snippet:

```python
import streamlit as st

sender_email = st.text_input('username', value='user', key='email')
sender_password = st.text_input('password', type='password', key='password')
if st.button('Login'):
    st.success('Logged in')
else:
    st.write('Not logged in')
```

1. Run the code
2. Notice that 1Password 7 displays for the user field the saved password and for the password field only suggests a new password
3. Fill saved password from the user field. Notice that the password field does not get filled.
4. Reload the page.
5. Remove the `autocomplete="new-password"` property with the web inspector.
6. Fill the password from the user field. Notice that both fields get filled (as expected).

**Expected behavior:**

The password manager fills both fields

**Actual behavior:**

The password manager only fills the user field.

### Is this a regression?

Don't know.

### Debug info

- Streamlit version: 0.79.0
- Python version: 3.9.2
- Using Conda? PipEnv? PyEnv? Pex? => Conda
- OS version: macOS 10.15.7
- Browser version: Tested with Safari 14.0.3 and Chrome 89.0.4389.90



Is there a workaround to remove this HTML property? Or does it required a code change within Streamlit?
For my tool the user needs to provide a API authentication for the backend (username/password), which will be stored in a password manager (1Password 7) in this case. It is inconvenient having to copy the password manually.

Thank you!


--- 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/text_widgets.py`
Content:
```
1 # Copyright 2018-2021 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 typing import cast
16 
17 import streamlit
18 from streamlit.errors import StreamlitAPIException
19 from streamlit.proto.TextArea_pb2 import TextArea as TextAreaProto
20 from streamlit.proto.TextInput_pb2 import TextInput as TextInputProto
21 from streamlit.state.widgets import register_widget
22 from .form import current_form_id
23 from .utils import check_callback_rules, check_session_state_rules
24 
25 
26 class TextWidgetsMixin:
27     def text_input(
28         self,
29         label,
30         value="",
31         max_chars=None,
32         key=None,
33         type="default",
34         help=None,
35         on_change=None,
36         args=None,
37         kwargs=None,
38     ):
39         """Display a single-line text input widget.
40 
41         Parameters
42         ----------
43         label : str
44             A short label explaining to the user what this input is for.
45         value : any
46             The text value of this widget when it first renders. This will be
47             cast to str internally.
48         max_chars : int or None
49             Max number of characters allowed in text input.
50         key : str
51             An optional string to use as the unique key for the widget.
52             If this is omitted, a key will be generated for the widget
53             based on its content. Multiple widgets of the same type may
54             not share the same key.
55         type : str
56             The type of the text input. This can be either "default" (for
57             a regular text input), or "password" (for a text input that
58             masks the user's typed value). Defaults to "default".
59         help : str
60             An optional tooltip that gets displayed next to the input.
61         on_change : callable
62             An optional callback invoked when this text_input's value changes.
63         args : tuple
64             An optional tuple of args to pass to the callback.
65         kwargs : dict
66             An optional dict of kwargs to pass to the callback.
67 
68         Returns
69         -------
70         str
71             The current value of the text input widget.
72 
73         Example
74         -------
75         >>> title = st.text_input('Movie title', 'Life of Brian')
76         >>> st.write('The current movie title is', title)
77 
78         """
79         check_callback_rules(self.dg, on_change)
80         check_session_state_rules(default_value=None if value == "" else value, key=key)
81 
82         text_input_proto = TextInputProto()
83         text_input_proto.label = label
84         text_input_proto.default = str(value)
85         text_input_proto.form_id = current_form_id(self.dg)
86         if help is not None:
87             text_input_proto.help = help
88 
89         if max_chars is not None:
90             text_input_proto.max_chars = max_chars
91 
92         if type == "default":
93             text_input_proto.type = TextInputProto.DEFAULT
94         elif type == "password":
95             text_input_proto.type = TextInputProto.PASSWORD
96         else:
97             raise StreamlitAPIException(
98                 "'%s' is not a valid text_input type. Valid types are 'default' and 'password'."
99                 % type
100             )
101 
102         def deserialize_text_input(ui_value) -> str:
103             return str(ui_value if ui_value is not None else value)
104 
105         current_value, set_frontend_value = register_widget(
106             "text_input",
107             text_input_proto,
108             user_key=key,
109             on_change_handler=on_change,
110             args=args,
111             kwargs=kwargs,
112             deserializer=deserialize_text_input,
113             serializer=lambda x: x,
114         )
115 
116         if set_frontend_value:
117             text_input_proto.value = current_value
118             text_input_proto.set_value = True
119 
120         self.dg._enqueue("text_input", text_input_proto)
121         return current_value
122 
123     def text_area(
124         self,
125         label,
126         value="",
127         height=None,
128         max_chars=None,
129         key=None,
130         help=None,
131         on_change=None,
132         args=None,
133         kwargs=None,
134     ):
135         """Display a multi-line text input widget.
136 
137         Parameters
138         ----------
139         label : str
140             A short label explaining to the user what this input is for.
141         value : any
142             The text value of this widget when it first renders. This will be
143             cast to str internally.
144         height : int or None
145             Desired height of the UI element expressed in pixels. If None, a
146             default height is used.
147         max_chars : int or None
148             Maximum number of characters allowed in text area.
149         key : str
150             An optional string to use as the unique key for the widget.
151             If this is omitted, a key will be generated for the widget
152             based on its content. Multiple widgets of the same type may
153             not share the same key.
154         help : str
155             An optional tooltip that gets displayed next to the textarea.
156         on_change : callable
157             An optional callback invoked when this text_area's value changes.
158         args : tuple
159             An optional tuple of args to pass to the callback.
160         kwargs : dict
161             An optional dict of kwargs to pass to the callback.
162 
163         Returns
164         -------
165         str
166             The current value of the text input widget.
167 
168         Example
169         -------
170         >>> txt = st.text_area('Text to analyze', '''
171         ...     It was the best of times, it was the worst of times, it was
172         ...     the age of wisdom, it was the age of foolishness, it was
173         ...     the epoch of belief, it was the epoch of incredulity, it
174         ...     was the season of Light, it was the season of Darkness, it
175         ...     was the spring of hope, it was the winter of despair, (...)
176         ...     ''')
177         >>> st.write('Sentiment:', run_sentiment_analysis(txt))
178 
179         """
180         check_callback_rules(self.dg, on_change)
181         check_session_state_rules(default_value=None if value == "" else value, key=key)
182 
183         text_area_proto = TextAreaProto()
184         text_area_proto.label = label
185         text_area_proto.default = str(value)
186         text_area_proto.form_id = current_form_id(self.dg)
187         if help is not None:
188             text_area_proto.help = help
189 
190         if height is not None:
191             text_area_proto.height = height
192 
193         if max_chars is not None:
194             text_area_proto.max_chars = max_chars
195 
196         def deserialize_text_area(ui_value) -> str:
197             return str(ui_value if ui_value is not None else value)
198 
199         current_value, set_frontend_value = register_widget(
200             "text_area",
201             text_area_proto,
202             user_key=key,
203             on_change_handler=on_change,
204             args=args,
205             kwargs=kwargs,
206             deserializer=deserialize_text_area,
207             serializer=lambda x: x,
208         )
209 
210         if set_frontend_value:
211             text_area_proto.value = current_value
212             text_area_proto.set_value = True
213 
214         self.dg._enqueue("text_area", text_area_proto)
215         return current_value
216 
217     @property
218     def dg(self) -> "streamlit.delta_generator.DeltaGenerator":
219         """Get our DeltaGenerator."""
220         return cast("streamlit.delta_generator.DeltaGenerator", self)
221
```
--- 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/lib/streamlit/elements/text_widgets.py b/lib/streamlit/elements/text_widgets.py
--- a/lib/streamlit/elements/text_widgets.py
+++ b/lib/streamlit/elements/text_widgets.py
@@ -32,6 +32,7 @@
         key=None,
         type="default",
         help=None,
+        autocomplete=None,
         on_change=None,
         args=None,
         kwargs=None,
@@ -58,6 +59,11 @@
             masks the user's typed value). Defaults to "default".
         help : str
             An optional tooltip that gets displayed next to the input.
+        autocomplete : str
+            An optional value that will be passed to the <input> element's
+            autocomplete property. If unspecified, this value will be set to
+            "new-password" for "password" inputs, and the empty string for
+            "default" inputs. For more details, see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete
         on_change : callable
             An optional callback invoked when this text_input's value changes.
         args : tuple
@@ -99,6 +105,12 @@
                 % type
             )
 
+        # Marshall the autocomplete param. If unspecified, this will be
+        # set to "new-password" for password inputs.
+        if autocomplete is None:
+            autocomplete = "new-password" if type == "password" else ""
+        text_input_proto.autocomplete = autocomplete
+
         def deserialize_text_input(ui_value) -> str:
             return str(ui_value if ui_value is not None else value)

{"golden_diff": "diff --git a/lib/streamlit/elements/text_widgets.py b/lib/streamlit/elements/text_widgets.py\n--- a/lib/streamlit/elements/text_widgets.py\n+++ b/lib/streamlit/elements/text_widgets.py\n@@ -32,6 +32,7 @@\n         key=None,\n         type=\"default\",\n         help=None,\n+        autocomplete=None,\n         on_change=None,\n         args=None,\n         kwargs=None,\n@@ -58,6 +59,11 @@\n             masks the user's typed value). Defaults to \"default\".\n         help : str\n             An optional tooltip that gets displayed next to the input.\n+        autocomplete : str\n+            An optional value that will be passed to the <input> element's\n+            autocomplete property. If unspecified, this value will be set to\n+            \"new-password\" for \"password\" inputs, and the empty string for\n+            \"default\" inputs. For more details, see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete\n         on_change : callable\n             An optional callback invoked when this text_input's value changes.\n         args : tuple\n@@ -99,6 +105,12 @@\n                 % type\n             )\n \n+        # Marshall the autocomplete param. If unspecified, this will be\n+        # set to \"new-password\" for password inputs.\n+        if autocomplete is None:\n+            autocomplete = \"new-password\" if type == \"password\" else \"\"\n+        text_input_proto.autocomplete = autocomplete\n+\n         def deserialize_text_input(ui_value) -> str:\n             return str(ui_value if ui_value is not None else value)\n", "issue": "Auto-Complete Password with Password Manager\n### Summary\r\n\r\nWhen filling a username/password combo from a password manager, it only fills the username, not the password, because the HTML password field has the property `autocomplete=\"new-password\"`. Removing this property results in the expected behaviour.\r\n\r\n### Steps to reproduce\r\n\r\nCode snippet:\r\n\r\n```python\r\nimport streamlit as st\r\n\r\nsender_email = st.text_input('username', value='user', key='email')\r\nsender_password = st.text_input('password', type='password', key='password')\r\nif st.button('Login'):\r\n    st.success('Logged in')\r\nelse:\r\n    st.write('Not logged in')\r\n```\r\n\r\n1. Run the code\r\n2. Notice that 1Password 7 displays for the user field the saved password and for the password field only suggests a new password\r\n3. Fill saved password from the user field. Notice that the password field does not get filled.\r\n4. Reload the page.\r\n5. Remove the `autocomplete=\"new-password\"` property with the web inspector.\r\n6. Fill the password from the user field. Notice that both fields get filled (as expected).\r\n\r\n**Expected behavior:**\r\n\r\nThe password manager fills both fields\r\n\r\n**Actual behavior:**\r\n\r\nThe password manager only fills the user field.\r\n\r\n### Is this a regression?\r\n\r\nDon't know.\r\n\r\n### Debug info\r\n\r\n- Streamlit version: 0.79.0\r\n- Python version: 3.9.2\r\n- Using Conda? PipEnv? PyEnv? Pex? => Conda\r\n- OS version: macOS 10.15.7\r\n- Browser version: Tested with Safari 14.0.3 and Chrome 89.0.4389.90\r\n\r\n\r\n\r\nIs there a workaround to remove this HTML property? Or does it required a code change within Streamlit?\r\nFor my tool the user needs to provide a API authentication for the backend (username/password), which will be stored in a password manager (1Password 7) in this case. It is inconvenient having to copy the password manually.\r\n\r\nThank you!\r\n\n", "before_files": [{"content": "# Copyright 2018-2021 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 typing import cast\n\nimport streamlit\nfrom streamlit.errors import StreamlitAPIException\nfrom streamlit.proto.TextArea_pb2 import TextArea as TextAreaProto\nfrom streamlit.proto.TextInput_pb2 import TextInput as TextInputProto\nfrom streamlit.state.widgets import register_widget\nfrom .form import current_form_id\nfrom .utils import check_callback_rules, check_session_state_rules\n\n\nclass TextWidgetsMixin:\n    def text_input(\n        self,\n        label,\n        value=\"\",\n        max_chars=None,\n        key=None,\n        type=\"default\",\n        help=None,\n        on_change=None,\n        args=None,\n        kwargs=None,\n    ):\n        \"\"\"Display a single-line text input widget.\n\n        Parameters\n        ----------\n        label : str\n            A short label explaining to the user what this input is for.\n        value : any\n            The text value of this widget when it first renders. This will be\n            cast to str internally.\n        max_chars : int or None\n            Max number of characters allowed in text input.\n        key : str\n            An optional string to use as the unique key for the widget.\n            If this is omitted, a key will be generated for the widget\n            based on its content. Multiple widgets of the same type may\n            not share the same key.\n        type : str\n            The type of the text input. This can be either \"default\" (for\n            a regular text input), or \"password\" (for a text input that\n            masks the user's typed value). Defaults to \"default\".\n        help : str\n            An optional tooltip that gets displayed next to the input.\n        on_change : callable\n            An optional callback invoked when this text_input's value changes.\n        args : tuple\n            An optional tuple of args to pass to the callback.\n        kwargs : dict\n            An optional dict of kwargs to pass to the callback.\n\n        Returns\n        -------\n        str\n            The current value of the text input widget.\n\n        Example\n        -------\n        >>> title = st.text_input('Movie title', 'Life of Brian')\n        >>> st.write('The current movie title is', title)\n\n        \"\"\"\n        check_callback_rules(self.dg, on_change)\n        check_session_state_rules(default_value=None if value == \"\" else value, key=key)\n\n        text_input_proto = TextInputProto()\n        text_input_proto.label = label\n        text_input_proto.default = str(value)\n        text_input_proto.form_id = current_form_id(self.dg)\n        if help is not None:\n            text_input_proto.help = help\n\n        if max_chars is not None:\n            text_input_proto.max_chars = max_chars\n\n        if type == \"default\":\n            text_input_proto.type = TextInputProto.DEFAULT\n        elif type == \"password\":\n            text_input_proto.type = TextInputProto.PASSWORD\n        else:\n            raise StreamlitAPIException(\n                \"'%s' is not a valid text_input type. Valid types are 'default' and 'password'.\"\n                % type\n            )\n\n        def deserialize_text_input(ui_value) -> str:\n            return str(ui_value if ui_value is not None else value)\n\n        current_value, set_frontend_value = register_widget(\n            \"text_input\",\n            text_input_proto,\n            user_key=key,\n            on_change_handler=on_change,\n            args=args,\n            kwargs=kwargs,\n            deserializer=deserialize_text_input,\n            serializer=lambda x: x,\n        )\n\n        if set_frontend_value:\n            text_input_proto.value = current_value\n            text_input_proto.set_value = True\n\n        self.dg._enqueue(\"text_input\", text_input_proto)\n        return current_value\n\n    def text_area(\n        self,\n        label,\n        value=\"\",\n        height=None,\n        max_chars=None,\n        key=None,\n        help=None,\n        on_change=None,\n        args=None,\n        kwargs=None,\n    ):\n        \"\"\"Display a multi-line text input widget.\n\n        Parameters\n        ----------\n        label : str\n            A short label explaining to the user what this input is for.\n        value : any\n            The text value of this widget when it first renders. This will be\n            cast to str internally.\n        height : int or None\n            Desired height of the UI element expressed in pixels. If None, a\n            default height is used.\n        max_chars : int or None\n            Maximum number of characters allowed in text area.\n        key : str\n            An optional string to use as the unique key for the widget.\n            If this is omitted, a key will be generated for the widget\n            based on its content. Multiple widgets of the same type may\n            not share the same key.\n        help : str\n            An optional tooltip that gets displayed next to the textarea.\n        on_change : callable\n            An optional callback invoked when this text_area's value changes.\n        args : tuple\n            An optional tuple of args to pass to the callback.\n        kwargs : dict\n            An optional dict of kwargs to pass to the callback.\n\n        Returns\n        -------\n        str\n            The current value of the text input widget.\n\n        Example\n        -------\n        >>> txt = st.text_area('Text to analyze', '''\n        ...     It was the best of times, it was the worst of times, it was\n        ...     the age of wisdom, it was the age of foolishness, it was\n        ...     the epoch of belief, it was the epoch of incredulity, it\n        ...     was the season of Light, it was the season of Darkness, it\n        ...     was the spring of hope, it was the winter of despair, (...)\n        ...     ''')\n        >>> st.write('Sentiment:', run_sentiment_analysis(txt))\n\n        \"\"\"\n        check_callback_rules(self.dg, on_change)\n        check_session_state_rules(default_value=None if value == \"\" else value, key=key)\n\n        text_area_proto = TextAreaProto()\n        text_area_proto.label = label\n        text_area_proto.default = str(value)\n        text_area_proto.form_id = current_form_id(self.dg)\n        if help is not None:\n            text_area_proto.help = help\n\n        if height is not None:\n            text_area_proto.height = height\n\n        if max_chars is not None:\n            text_area_proto.max_chars = max_chars\n\n        def deserialize_text_area(ui_value) -> str:\n            return str(ui_value if ui_value is not None else value)\n\n        current_value, set_frontend_value = register_widget(\n            \"text_area\",\n            text_area_proto,\n            user_key=key,\n            on_change_handler=on_change,\n            args=args,\n            kwargs=kwargs,\n            deserializer=deserialize_text_area,\n            serializer=lambda x: x,\n        )\n\n        if set_frontend_value:\n            text_area_proto.value = current_value\n            text_area_proto.set_value = True\n\n        self.dg._enqueue(\"text_area\", text_area_proto)\n        return current_value\n\n    @property\n    def dg(self) -> \"streamlit.delta_generator.DeltaGenerator\":\n        \"\"\"Get our DeltaGenerator.\"\"\"\n        return cast(\"streamlit.delta_generator.DeltaGenerator\", self)\n", "path": "lib/streamlit/elements/text_widgets.py"}], "after_files": [{"content": "# Copyright 2018-2021 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 typing import cast\n\nimport streamlit\nfrom streamlit.errors import StreamlitAPIException\nfrom streamlit.proto.TextArea_pb2 import TextArea as TextAreaProto\nfrom streamlit.proto.TextInput_pb2 import TextInput as TextInputProto\nfrom streamlit.state.widgets import register_widget\nfrom .form import current_form_id\nfrom .utils import check_callback_rules, check_session_state_rules\n\n\nclass TextWidgetsMixin:\n    def text_input(\n        self,\n        label,\n        value=\"\",\n        max_chars=None,\n        key=None,\n        type=\"default\",\n        help=None,\n        autocomplete=None,\n        on_change=None,\n        args=None,\n        kwargs=None,\n    ):\n        \"\"\"Display a single-line text input widget.\n\n        Parameters\n        ----------\n        label : str\n            A short label explaining to the user what this input is for.\n        value : any\n            The text value of this widget when it first renders. This will be\n            cast to str internally.\n        max_chars : int or None\n            Max number of characters allowed in text input.\n        key : str\n            An optional string to use as the unique key for the widget.\n            If this is omitted, a key will be generated for the widget\n            based on its content. Multiple widgets of the same type may\n            not share the same key.\n        type : str\n            The type of the text input. This can be either \"default\" (for\n            a regular text input), or \"password\" (for a text input that\n            masks the user's typed value). Defaults to \"default\".\n        help : str\n            An optional tooltip that gets displayed next to the input.\n        autocomplete : str\n            An optional value that will be passed to the <input> element's\n            autocomplete property. If unspecified, this value will be set to\n            \"new-password\" for \"password\" inputs, and the empty string for\n            \"default\" inputs. For more details, see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete\n        on_change : callable\n            An optional callback invoked when this text_input's value changes.\n        args : tuple\n            An optional tuple of args to pass to the callback.\n        kwargs : dict\n            An optional dict of kwargs to pass to the callback.\n\n        Returns\n        -------\n        str\n            The current value of the text input widget.\n\n        Example\n        -------\n        >>> title = st.text_input('Movie title', 'Life of Brian')\n        >>> st.write('The current movie title is', title)\n\n        \"\"\"\n        check_callback_rules(self.dg, on_change)\n        check_session_state_rules(default_value=None if value == \"\" else value, key=key)\n\n        text_input_proto = TextInputProto()\n        text_input_proto.label = label\n        text_input_proto.default = str(value)\n        text_input_proto.form_id = current_form_id(self.dg)\n        if help is not None:\n            text_input_proto.help = help\n\n        if max_chars is not None:\n            text_input_proto.max_chars = max_chars\n\n        if type == \"default\":\n            text_input_proto.type = TextInputProto.DEFAULT\n        elif type == \"password\":\n            text_input_proto.type = TextInputProto.PASSWORD\n        else:\n            raise StreamlitAPIException(\n                \"'%s' is not a valid text_input type. Valid types are 'default' and 'password'.\"\n                % type\n            )\n\n        # Marshall the autocomplete param. If unspecified, this will be\n        # set to \"new-password\" for password inputs.\n        if autocomplete is None:\n            autocomplete = \"new-password\" if type == \"password\" else \"\"\n        text_input_proto.autocomplete = autocomplete\n\n        def deserialize_text_input(ui_value) -> str:\n            return str(ui_value if ui_value is not None else value)\n\n        current_value, set_frontend_value = register_widget(\n            \"text_input\",\n            text_input_proto,\n            user_key=key,\n            on_change_handler=on_change,\n            args=args,\n            kwargs=kwargs,\n            deserializer=deserialize_text_input,\n            serializer=lambda x: x,\n        )\n\n        if set_frontend_value:\n            text_input_proto.value = current_value\n            text_input_proto.set_value = True\n\n        self.dg._enqueue(\"text_input\", text_input_proto)\n        return current_value\n\n    def text_area(\n        self,\n        label,\n        value=\"\",\n        height=None,\n        max_chars=None,\n        key=None,\n        help=None,\n        on_change=None,\n        args=None,\n        kwargs=None,\n    ):\n        \"\"\"Display a multi-line text input widget.\n\n        Parameters\n        ----------\n        label : str\n            A short label explaining to the user what this input is for.\n        value : any\n            The text value of this widget when it first renders. This will be\n            cast to str internally.\n        height : int or None\n            Desired height of the UI element expressed in pixels. If None, a\n            default height is used.\n        max_chars : int or None\n            Maximum number of characters allowed in text area.\n        key : str\n            An optional string to use as the unique key for the widget.\n            If this is omitted, a key will be generated for the widget\n            based on its content. Multiple widgets of the same type may\n            not share the same key.\n        help : str\n            An optional tooltip that gets displayed next to the textarea.\n        on_change : callable\n            An optional callback invoked when this text_area's value changes.\n        args : tuple\n            An optional tuple of args to pass to the callback.\n        kwargs : dict\n            An optional dict of kwargs to pass to the callback.\n\n        Returns\n        -------\n        str\n            The current value of the text input widget.\n\n        Example\n        -------\n        >>> txt = st.text_area('Text to analyze', '''\n        ...     It was the best of times, it was the worst of times, it was\n        ...     the age of wisdom, it was the age of foolishness, it was\n        ...     the epoch of belief, it was the epoch of incredulity, it\n        ...     was the season of Light, it was the season of Darkness, it\n        ...     was the spring of hope, it was the winter of despair, (...)\n        ...     ''')\n        >>> st.write('Sentiment:', run_sentiment_analysis(txt))\n\n        \"\"\"\n        check_callback_rules(self.dg, on_change)\n        check_session_state_rules(default_value=None if value == \"\" else value, key=key)\n\n        text_area_proto = TextAreaProto()\n        text_area_proto.label = label\n        text_area_proto.default = str(value)\n        text_area_proto.form_id = current_form_id(self.dg)\n        if help is not None:\n            text_area_proto.help = help\n\n        if height is not None:\n            text_area_proto.height = height\n\n        if max_chars is not None:\n            text_area_proto.max_chars = max_chars\n\n        def deserialize_text_area(ui_value) -> str:\n            return str(ui_value if ui_value is not None else value)\n\n        current_value, set_frontend_value = register_widget(\n            \"text_area\",\n            text_area_proto,\n            user_key=key,\n            on_change_handler=on_change,\n            args=args,\n            kwargs=kwargs,\n            deserializer=deserialize_text_area,\n            serializer=lambda x: x,\n        )\n\n        if set_frontend_value:\n            text_area_proto.value = current_value\n            text_area_proto.set_value = True\n\n        self.dg._enqueue(\"text_area\", text_area_proto)\n        return current_value\n\n    @property\n    def dg(self) -> \"streamlit.delta_generator.DeltaGenerator\":\n        \"\"\"Get our DeltaGenerator.\"\"\"\n        return cast(\"streamlit.delta_generator.DeltaGenerator\", self)\n", "path": "lib/streamlit/elements/text_widgets.py"}]}