Repository: ipython/ipython

Issue:
HTML vs Iframe
I was again the witness of people miss-using HTML to display IFrames, and having issues. 

Could we detect in  HTML the usage of `<iframe>....</iframe>` and warn user that there is an iframe object ? 


Discussion:

Patch:
diff --git a/IPython/core/display.py b/IPython/core/display.py
index 7128d0f9b..84c18e51c 100644
--- a/IPython/core/display.py
+++ b/IPython/core/display.py
@@ -238,22 +238,16 @@ def display(*objs, include=None, exclude=None, metadata=None, transient=None, di
     want to use. Here is a list of the names of the special methods and the
     values they must return:
 
-      - `_repr_html_`: return raw HTML as a string, or a tuple (see below).
-      - `_repr_json_`: return a JSONable dict, or a tuple (see below).
-      - `_repr_jpeg_`: return raw JPEG data, or a tuple (see below).
-      - `_repr_png_`: return raw PNG data, or a tuple (see below).
-      - `_repr_svg_`: return raw SVG data as a string, or a tuple (see below).
-      - `_repr_latex_`: return LaTeX commands in a string surrounded by "$",
-                        or a tuple (see below).
+      - `_repr_html_`: return raw HTML as a string
+      - `_repr_json_`: return a JSONable dict
+      - `_repr_jpeg_`: return raw JPEG data
+      - `_repr_png_`: return raw PNG data
+      - `_repr_svg_`: return raw SVG data as a string
+      - `_repr_latex_`: return LaTeX commands in a string surrounded by "$".
       - `_repr_mimebundle_`: return a full mimebundle containing the mapping
                              from all mimetypes to data.
                              Use this for any mime-type not listed above.
 
-    The above functions may also return the object's metadata alonside the
-    data.  If the metadata is available, the functions will return a tuple
-    containing the data and metadata, in that order.  If there is no metadata
-    available, then the functions will return the data only.
-
     When you are directly writing your own classes, you can adapt them for
     display in IPython by following the above approach. But in practice, you
     often need to work with existing classes that you can't easily modify.
@@ -673,7 +667,19 @@ def _repr_pretty_(self, pp, cycle):
 class HTML(TextDisplayObject):
 
     def __init__(self, data=None, url=None, filename=None, metadata=None):
-        if data and "<iframe " in data and "</iframe>" in data:
+        def warn():
+            if not data:
+                return False
+
+            #
+            # Avoid calling lower() on the entire data, because it could be a
+            # long string and we're only interested in its beginning and end.
+            #
+            prefix = data[:10].lower()
+            suffix = data[-10:].lower()
+            return prefix.startswith("<iframe ") and suffix.endswith("</iframe>")
+
+        if warn():
             warnings.warn("Consider using IPython.display.IFrame instead")
         super(HTML, self).__init__(data=data, url=url, filename=filename, metadata=metadata)
 
@@ -1267,8 +1273,7 @@ def _find_ext(self, s):
 
 class Video(DisplayObject):
 
-    def __init__(self, data=None, url=None, filename=None, embed=False,
-                 mimetype=None, width=None, height=None):
+    def __init__(self, data=None, url=None, filename=None, embed=False, mimetype=None):
         """Create a video object given raw data or an URL.
 
         When this object is returned by an input cell or passed to the
@@ -1300,12 +1305,6 @@ def __init__(self, data=None, url=None, filename=None, embed=False,
         mimetype: unicode
             Specify the mimetype for embedded videos.
             Default will be guessed from file extension, if available.
-        width : int
-            Width in pixels to which to constrain the video in HTML.
-            If not supplied, defaults to the width of the video.
-        height : int
-            Height in pixels to which to constrain the video in html.
-            If not supplied, defaults to the height of the video.
 
         Examples
         --------
@@ -1332,24 +1331,16 @@ def __init__(self, data=None, url=None, filename=None, embed=False,
 
         self.mimetype = mimetype
         self.embed = embed
-        self.width = width
-        self.height = height
         super(Video, self).__init__(data=data, url=url, filename=filename)
 
     def _repr_html_(self):
-        width = height = ''
-        if self.width:
-            width = ' width="%d"' % self.width
-        if self.height:
-            height = ' height="%d"' % self.height
-
         # External URLs and potentially local files are not embedded into the
         # notebook output.
         if not self.embed:
             url = self.url if self.url is not None else self.filename
-            output = """<video src="{0}" controls {1} {2}>
+            output = """<video src="{0}" controls>
       Your browser does not support the <code>video</code> element.
-    </video>""".format(url, width, height)
+    </video>""".format(url)
             return output
 
         # Embedded videos are base64-encoded.
@@ -1368,10 +1359,10 @@ def _repr_html_(self):
         else:
             b64_video = b2a_base64(video).decode('ascii').rstrip()
 
-        output = """<video controls {0} {1}>
- <source src="data:{2};base64,{3}" type="{2}">
+        output = """<video controls>
+ <source src="data:{0};base64,{1}" type="{0}">
  Your browser does not support the video tag.
- </video>""".format(width, height, mimetype, b64_video)
+ </video>""".format(mimetype, b64_video)
         return output
 
     def reload(self):
diff --git a/IPython/core/inputtransformer2.py b/IPython/core/inputtransformer2.py
index 0a0110039..1abd2cf95 100644
--- a/IPython/core/inputtransformer2.py
+++ b/IPython/core/inputtransformer2.py
@@ -20,12 +20,10 @@
 
 def leading_indent(lines):
     """Remove leading indentation.
-
+    
     If the first line starts with a spaces or tabs, the same whitespace will be
     removed from each following line in the cell.
     """
-    if not lines:
-        return lines
     m = _indent_re.match(lines[0])
     if not m:
         return lines
@@ -36,7 +34,7 @@ def leading_indent(lines):
 
 class PromptStripper:
     """Remove matching input prompts from a block of input.
-
+    
     Parameters
     ----------
     prompt_re : regular expression
@@ -47,7 +45,7 @@ class PromptStripper:
         If no initial expression is given, prompt_re will be used everywhere.
         Used mainly for plain Python prompts (``>>>``), where the continuation prompt
         ``...`` is a valid Python expression in Python 3, so shouldn't be stripped.
-
+    
     If initial_re and prompt_re differ,
     only initial_re will be tested against the first line.
     If any prompt is found on the first two lines,
@@ -61,8 +59,6 @@ def _strip(self, lines):
         return [self.prompt_re.sub('', l, count=1) for l in lines]
 
     def __call__(self, lines):
-        if not lines:
-            return lines
         if self.initial_re.match(lines[0]) or \
                 (len(lines) > 1 and self.prompt_re.match(lines[1])):
             return self._strip(lines)
@@ -76,7 +72,7 @@ def __call__(self, lines):
 ipython_prompt = PromptStripper(re.compile(r'^(In \[\d+\]: |\s*\.{3,}: ?)'))
 
 def cell_magic(lines):
-    if not lines or not lines[0].startswith('%%'):
+    if not lines[0].startswith('%%'):
         return lines
     if re.match('%%\w+\?', lines[0]):
         # This case will be handled by help_end
@@ -96,7 +92,7 @@ def _find_assign_op(token_line):
     for i, ti in enumerate(token_line):
         s = ti.string
         if s == '=' and paren_level == 0:
-            return i
+            return i 
         if s in '([{':
             paren_level += 1
         elif s in ')]}':
@@ -116,7 +112,7 @@ def find_end_of_continued_line(lines, start_line: int):
     return end_line
 
 def assemble_continued_line(lines, start: Tuple[int, int], end_line: int):
-    """Assemble a single line from multiple continued line pieces
+    """Assemble a single line from multiple continued line pieces 
 
     Continued lines are lines ending in ``\``, and the line following the last
     ``\`` in the block.
@@ -206,7 +202,7 @@ def find(cls, tokens_by_line):
                     and (line[assign_ix+1].string == '%') \
                     and (line[assign_ix+2].type == tokenize.NAME):
                 return cls(line[assign_ix+1].start)
-
+    
     def transform(self, lines: List[str]):
         """Transform a magic assignment found by the ``find()`` classmethod.
         """
@@ -216,12 +212,12 @@ def transform(self, lines: List[str]):
         rhs = assemble_continued_line(lines, (start_line, start_col), end_line)
         assert rhs.startswith('%'), rhs
         magic_name, _, args = rhs[1:].partition(' ')
-
+        
         lines_before = lines[:start_line]
         call = "get_ipython().run_line_magic({!r}, {!r})".format(magic_name, args)
         new_line = lhs + call + '\n'
         lines_after = lines[end_line+1:]
-
+        
         return lines_before + [new_line] + lines_after
 
 
@@ -468,7 +464,7 @@ def make_tokens_by_line(lines):
         pass
     if not tokens_by_line[-1]:
         tokens_by_line.pop()
-
+    
     return tokens_by_line
 
 def show_linewise_tokens(s: str):
@@ -505,12 +501,12 @@ def __init__(self):
             EscapedCommand,
             HelpEnd,
         ]
-
+    
     def do_one_token_transform(self, lines):
         """Find and run the transform earliest in the code.
-
+        
         Returns (changed, lines).
-
+        
         This method is called repeatedly until changed is False, indicating
         that all available transformations are complete.
 
diff --git a/IPython/extensions/autoreload.py b/IPython/extensions/autoreload.py
index ca6be10f3..306bb8bd2 100644
--- a/IPython/extensions/autoreload.py
+++ b/IPython/extensions/autoreload.py
@@ -338,7 +338,7 @@ def __call__(self):
         return self.obj
 
 
-def superreload(module, reload=reload, old_objects=None):
+def superreload(module, reload=reload, old_objects={}):
     """Enhanced version of the builtin reload function.
 
     superreload remembers objects previously in the module, and
@@ -348,8 +348,6 @@ def superreload(module, reload=reload, old_objects=None):
     - clears the module's namespace before reloading
 
     """
-    if old_objects is None:
-        old_objects = {}
 
     # collect old objects in the module
     for name, obj in list(module.__dict__.items()):
diff --git a/docs/source/config/integrating.rst b/docs/source/config/integrating.rst
index ded720fee..1da496070 100644
--- a/docs/source/config/integrating.rst
+++ b/docs/source/config/integrating.rst
@@ -65,21 +65,6 @@ There are also two more powerful display methods:
       Displays the object as a side effect; the return value is ignored. If this
       is defined, all other display methods are ignored.
 
-To customize how the REPL pretty-prints your object, add a `_repr_pretty_`
-method to the class.  The method should accept a pretty printer, and a boolean
-that indicates whether the printer detected a cycle.  The method should act on
-the printer to produce your customized pretty output.  Here is an example::
-
-    class MyObject(object):
-
-        def _repr_pretty_(self, p, cycle):
-            if cycle:
-                p.text('MyObject(...)')
-            else:
-                p.text('MyObject[...]')
-
-For details, see :py:mod:`IPython.lib.pretty`.
-
 Formatters for third-party types
 --------------------------------
 
diff --git a/docs/source/whatsnew/pr/video-width-height.rst b/docs/source/whatsnew/pr/video-width-height.rst
deleted file mode 100644
index 84757f1b4..000000000
--- a/docs/source/whatsnew/pr/video-width-height.rst
+++ /dev/null
@@ -1,1 +0,0 @@
-``IPython.display.Video`` now supports ``width`` and ``height`` arguments, allowing a custom width and height to be set instead of using the video's width and height
\ No newline at end of file


Test patch:
diff --git a/IPython/core/tests/test_display.py b/IPython/core/tests/test_display.py
index 7c1f978b4..1fed51127 100644
--- a/IPython/core/tests/test_display.py
+++ b/IPython/core/tests/test_display.py
@@ -197,12 +197,22 @@ def test_displayobject_repr():
 
 @mock.patch('warnings.warn')
 def test_encourage_iframe_over_html(m_warn):
+    display.HTML()
+    m_warn.assert_not_called()
+
     display.HTML('<br />')
     m_warn.assert_not_called()
 
+    display.HTML('<html><p>Lots of content here</p><iframe src="http://a.com"></iframe>')
+    m_warn.assert_not_called()
+
     display.HTML('<iframe src="http://a.com"></iframe>')
     m_warn.assert_called_with('Consider using IPython.display.IFrame instead')
 
+    m_warn.reset_mock()
+    display.HTML('<IFRAME SRC="http://a.com"></IFRAME>')
+    m_warn.assert_called_with('Consider using IPython.display.IFrame instead')
+
 def test_progress():
     p = display.ProgressBar(10)
     nt.assert_in('0/10',repr(p))
diff --git a/IPython/core/tests/test_inputtransformer2.py b/IPython/core/tests/test_inputtransformer2.py
index 77df22c2c..f78a0b371 100644
--- a/IPython/core/tests/test_inputtransformer2.py
+++ b/IPython/core/tests/test_inputtransformer2.py
@@ -101,12 +101,6 @@
 [r"get_ipython().set_next_input('(a,\nb) = zip');get_ipython().run_line_magic('pinfo', 'zip')" + "\n"]
 )
 
-def null_cleanup_transformer(lines):
-    """
-    A cleanup transform that returns an empty list.
-    """
-    return []
-
 def check_make_token_by_line_never_ends_empty():
     """
     Check that not sequence of single or double characters ends up leading to en empty list of tokens
@@ -221,7 +215,3 @@ def test_check_complete():
         for k in short:
             cc(c+k)
 
-def test_null_cleanup_transformer():
-    manager = ipt2.TransformerManager()
-    manager.cleanup_transforms.insert(0, null_cleanup_transformer)
-    nt.assert_is(manager.transform_cell(""), "")