Repository: streamlit/streamlit

Issue:
Support for `_repr_html_` property on objects
From a user post here: https://discuss.streamlit.io/t/look-for-html-repr-on-objects-generally-a-method-called-repr-html/1939

> I have just started looking into streamlit after working on and using Panel 1. I am not sure if I missed this, but I was expecting an object with a _repr_html_ method to be automatically renderable with streamlit.
> 
> If streamlit looked for that method, it would be easy for other libraries to make themselves renderable. Additionally, many libraries already have html reprs since they are renderable in notebooks. See this blog post 2 for examples of libraries that comply with this defacto standard.

This seems like a good thing to add alongside `st.iframe` (re #686)


Discussion:
Thanks for adding this. Don't know why I put it in the discourse in the first place :)

Resolving patch:
diff --git a/lib/streamlit/elements/write.py b/lib/streamlit/elements/write.py
--- a/lib/streamlit/elements/write.py
+++ b/lib/streamlit/elements/write.py
@@ -43,7 +43,7 @@ def write(self, *args, **kwargs):
 
         1. You can pass in multiple arguments, all of which will be written.
         2. Its behavior depends on the input types as follows.
-        3. It returns None, so it's "slot" in the App cannot be reused.
+        3. It returns None, so its "slot" in the App cannot be reused.
 
         Parameters
         ----------
@@ -60,7 +60,6 @@ def write(self, *args, **kwargs):
             - write(func)       : Displays information about a function.
             - write(module)     : Displays information about the module.
             - write(dict)       : Displays dict in an interactive widget.
-            - write(obj)        : The default is to print str(obj).
             - write(mpl_fig)    : Displays a Matplotlib figure.
             - write(altair)     : Displays an Altair chart.
             - write(keras)      : Displays a Keras model.
@@ -68,6 +67,8 @@ def write(self, *args, **kwargs):
             - write(plotly_fig) : Displays a Plotly figure.
             - write(bokeh_fig)  : Displays a Bokeh figure.
             - write(sympy_expr) : Prints SymPy expression using LaTeX.
+            - write(htmlable)   : Prints _repr_html_() for the object if available.
+            - write(obj)        : Prints str(obj) if otherwise unknown.
 
         unsafe_allow_html : bool
             This is a keyword-only argument that defaults to False.
@@ -217,6 +218,11 @@ def flush_buffer():
             elif type_util.is_pydeck(arg):
                 flush_buffer()
                 self.dg.pydeck_chart(arg)
+            elif hasattr(arg, "_repr_html_"):
+                self.dg.markdown(
+                    arg._repr_html_(),
+                    unsafe_allow_html=True,
+                )
             else:
                 string_buffer.append("`%s`" % str(arg).replace("`", "\\`"))
 


Test patch:
diff --git a/lib/tests/streamlit/write_test.py b/lib/tests/streamlit/write_test.py
--- a/lib/tests/streamlit/write_test.py
+++ b/lib/tests/streamlit/write_test.py
@@ -39,6 +39,20 @@ class StreamlitWriteTest(unittest.TestCase):
     trying to check is that the right st.* method gets called
     """
 
+    def test_repr_html(self):
+        """Test st.write with an object that defines _repr_html_."""
+
+        class FakeHTMLable(object):
+            def _repr_html_(self):
+                return "<strong>hello world</strong>"
+
+        with patch("streamlit.delta_generator.DeltaGenerator.markdown") as p:
+            st.write(FakeHTMLable())
+
+            p.assert_called_once_with(
+                "<strong>hello world</strong>", unsafe_allow_html=True
+            )
+
     def test_string(self):
         """Test st.write with a string."""
         with patch("streamlit.delta_generator.DeltaGenerator.markdown") as p: