Show More
@@ -0,0 +1,1 b'' | |||||
|
1 | ``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 |
@@ -238,16 +238,22 b' def display(*objs, include=None, exclude=None, metadata=None, transient=None, di' | |||||
238 | want to use. Here is a list of the names of the special methods and the |
|
238 | want to use. Here is a list of the names of the special methods and the | |
239 | values they must return: |
|
239 | values they must return: | |
240 |
|
240 | |||
241 | - `_repr_html_`: return raw HTML as a string |
|
241 | - `_repr_html_`: return raw HTML as a string, or a tuple (see below). | |
242 | - `_repr_json_`: return a JSONable dict |
|
242 | - `_repr_json_`: return a JSONable dict, or a tuple (see below). | |
243 | - `_repr_jpeg_`: return raw JPEG data |
|
243 | - `_repr_jpeg_`: return raw JPEG data, or a tuple (see below). | |
244 | - `_repr_png_`: return raw PNG data |
|
244 | - `_repr_png_`: return raw PNG data, or a tuple (see below). | |
245 | - `_repr_svg_`: return raw SVG data as a string |
|
245 | - `_repr_svg_`: return raw SVG data as a string, or a tuple (see below). | |
246 |
- `_repr_latex_`: return LaTeX commands in a string surrounded by "$" |
|
246 | - `_repr_latex_`: return LaTeX commands in a string surrounded by "$", | |
|
247 | or a tuple (see below). | |||
247 | - `_repr_mimebundle_`: return a full mimebundle containing the mapping |
|
248 | - `_repr_mimebundle_`: return a full mimebundle containing the mapping | |
248 | from all mimetypes to data. |
|
249 | from all mimetypes to data. | |
249 | Use this for any mime-type not listed above. |
|
250 | Use this for any mime-type not listed above. | |
250 |
|
251 | |||
|
252 | The above functions may also return the object's metadata alonside the | |||
|
253 | data. If the metadata is available, the functions will return a tuple | |||
|
254 | containing the data and metadata, in that order. If there is no metadata | |||
|
255 | available, then the functions will return the data only. | |||
|
256 | ||||
251 | When you are directly writing your own classes, you can adapt them for |
|
257 | When you are directly writing your own classes, you can adapt them for | |
252 | display in IPython by following the above approach. But in practice, you |
|
258 | display in IPython by following the above approach. But in practice, you | |
253 | often need to work with existing classes that you can't easily modify. |
|
259 | often need to work with existing classes that you can't easily modify. | |
@@ -666,6 +672,11 b' class Pretty(TextDisplayObject):' | |||||
666 |
|
672 | |||
667 | class HTML(TextDisplayObject): |
|
673 | class HTML(TextDisplayObject): | |
668 |
|
674 | |||
|
675 | def __init__(self, data=None, url=None, filename=None, metadata=None): | |||
|
676 | if data and "<iframe " in data and "</iframe>" in data: | |||
|
677 | warnings.warn("Consider using IPython.display.IFrame instead") | |||
|
678 | super(HTML, self).__init__(data=data, url=url, filename=filename, metadata=metadata) | |||
|
679 | ||||
669 | def _repr_html_(self): |
|
680 | def _repr_html_(self): | |
670 | return self._data_and_metadata() |
|
681 | return self._data_and_metadata() | |
671 |
|
682 | |||
@@ -1256,7 +1267,8 b' class Image(DisplayObject):' | |||||
1256 |
|
1267 | |||
1257 | class Video(DisplayObject): |
|
1268 | class Video(DisplayObject): | |
1258 |
|
1269 | |||
1259 |
def __init__(self, data=None, url=None, filename=None, embed=False, |
|
1270 | def __init__(self, data=None, url=None, filename=None, embed=False, | |
|
1271 | mimetype=None, width=None, height=None): | |||
1260 | """Create a video object given raw data or an URL. |
|
1272 | """Create a video object given raw data or an URL. | |
1261 |
|
1273 | |||
1262 | When this object is returned by an input cell or passed to the |
|
1274 | When this object is returned by an input cell or passed to the | |
@@ -1288,6 +1300,12 b' class Video(DisplayObject):' | |||||
1288 | mimetype: unicode |
|
1300 | mimetype: unicode | |
1289 | Specify the mimetype for embedded videos. |
|
1301 | Specify the mimetype for embedded videos. | |
1290 | Default will be guessed from file extension, if available. |
|
1302 | Default will be guessed from file extension, if available. | |
|
1303 | width : int | |||
|
1304 | Width in pixels to which to constrain the video in HTML. | |||
|
1305 | If not supplied, defaults to the width of the video. | |||
|
1306 | height : int | |||
|
1307 | Height in pixels to which to constrain the video in html. | |||
|
1308 | If not supplied, defaults to the height of the video. | |||
1291 |
|
1309 | |||
1292 | Examples |
|
1310 | Examples | |
1293 | -------- |
|
1311 | -------- | |
@@ -1314,16 +1332,24 b' class Video(DisplayObject):' | |||||
1314 |
|
1332 | |||
1315 | self.mimetype = mimetype |
|
1333 | self.mimetype = mimetype | |
1316 | self.embed = embed |
|
1334 | self.embed = embed | |
|
1335 | self.width = width | |||
|
1336 | self.height = height | |||
1317 | super(Video, self).__init__(data=data, url=url, filename=filename) |
|
1337 | super(Video, self).__init__(data=data, url=url, filename=filename) | |
1318 |
|
1338 | |||
1319 | def _repr_html_(self): |
|
1339 | def _repr_html_(self): | |
|
1340 | width = height = '' | |||
|
1341 | if self.width: | |||
|
1342 | width = ' width="%d"' % self.width | |||
|
1343 | if self.height: | |||
|
1344 | height = ' height="%d"' % self.height | |||
|
1345 | ||||
1320 | # External URLs and potentially local files are not embedded into the |
|
1346 | # External URLs and potentially local files are not embedded into the | |
1321 | # notebook output. |
|
1347 | # notebook output. | |
1322 | if not self.embed: |
|
1348 | if not self.embed: | |
1323 | url = self.url if self.url is not None else self.filename |
|
1349 | url = self.url if self.url is not None else self.filename | |
1324 | output = """<video src="{0}" controls> |
|
1350 | output = """<video src="{0}" controls {1} {2}> | |
1325 | Your browser does not support the <code>video</code> element. |
|
1351 | Your browser does not support the <code>video</code> element. | |
1326 | </video>""".format(url) |
|
1352 | </video>""".format(url, width, height) | |
1327 | return output |
|
1353 | return output | |
1328 |
|
1354 | |||
1329 | # Embedded videos are base64-encoded. |
|
1355 | # Embedded videos are base64-encoded. | |
@@ -1342,10 +1368,10 b' class Video(DisplayObject):' | |||||
1342 | else: |
|
1368 | else: | |
1343 | b64_video = b2a_base64(video).decode('ascii').rstrip() |
|
1369 | b64_video = b2a_base64(video).decode('ascii').rstrip() | |
1344 |
|
1370 | |||
1345 | output = """<video controls> |
|
1371 | output = """<video controls {0} {1}> | |
1346 |
<source src="data:{ |
|
1372 | <source src="data:{2};base64,{3}" type="{2}"> | |
1347 | Your browser does not support the video tag. |
|
1373 | Your browser does not support the video tag. | |
1348 | </video>""".format(mimetype, b64_video) |
|
1374 | </video>""".format(width, height, mimetype, b64_video) | |
1349 | return output |
|
1375 | return output | |
1350 |
|
1376 | |||
1351 | def reload(self): |
|
1377 | def reload(self): |
@@ -20,10 +20,12 b" _indent_re = re.compile(r'^[ \\t]+')" | |||||
20 |
|
20 | |||
21 | def leading_indent(lines): |
|
21 | def leading_indent(lines): | |
22 | """Remove leading indentation. |
|
22 | """Remove leading indentation. | |
23 |
|
23 | |||
24 | If the first line starts with a spaces or tabs, the same whitespace will be |
|
24 | If the first line starts with a spaces or tabs, the same whitespace will be | |
25 | removed from each following line in the cell. |
|
25 | removed from each following line in the cell. | |
26 | """ |
|
26 | """ | |
|
27 | if not lines: | |||
|
28 | return lines | |||
27 | m = _indent_re.match(lines[0]) |
|
29 | m = _indent_re.match(lines[0]) | |
28 | if not m: |
|
30 | if not m: | |
29 | return lines |
|
31 | return lines | |
@@ -34,7 +36,7 b' def leading_indent(lines):' | |||||
34 |
|
36 | |||
35 | class PromptStripper: |
|
37 | class PromptStripper: | |
36 | """Remove matching input prompts from a block of input. |
|
38 | """Remove matching input prompts from a block of input. | |
37 |
|
39 | |||
38 | Parameters |
|
40 | Parameters | |
39 | ---------- |
|
41 | ---------- | |
40 | prompt_re : regular expression |
|
42 | prompt_re : regular expression | |
@@ -45,7 +47,7 b' class PromptStripper:' | |||||
45 | If no initial expression is given, prompt_re will be used everywhere. |
|
47 | If no initial expression is given, prompt_re will be used everywhere. | |
46 | Used mainly for plain Python prompts (``>>>``), where the continuation prompt |
|
48 | Used mainly for plain Python prompts (``>>>``), where the continuation prompt | |
47 | ``...`` is a valid Python expression in Python 3, so shouldn't be stripped. |
|
49 | ``...`` is a valid Python expression in Python 3, so shouldn't be stripped. | |
48 |
|
50 | |||
49 | If initial_re and prompt_re differ, |
|
51 | If initial_re and prompt_re differ, | |
50 | only initial_re will be tested against the first line. |
|
52 | only initial_re will be tested against the first line. | |
51 | If any prompt is found on the first two lines, |
|
53 | If any prompt is found on the first two lines, | |
@@ -59,6 +61,8 b' class PromptStripper:' | |||||
59 | return [self.prompt_re.sub('', l, count=1) for l in lines] |
|
61 | return [self.prompt_re.sub('', l, count=1) for l in lines] | |
60 |
|
62 | |||
61 | def __call__(self, lines): |
|
63 | def __call__(self, lines): | |
|
64 | if not lines: | |||
|
65 | return lines | |||
62 | if self.initial_re.match(lines[0]) or \ |
|
66 | if self.initial_re.match(lines[0]) or \ | |
63 | (len(lines) > 1 and self.prompt_re.match(lines[1])): |
|
67 | (len(lines) > 1 and self.prompt_re.match(lines[1])): | |
64 | return self._strip(lines) |
|
68 | return self._strip(lines) | |
@@ -72,7 +76,7 b' classic_prompt = PromptStripper(' | |||||
72 | ipython_prompt = PromptStripper(re.compile(r'^(In \[\d+\]: |\s*\.{3,}: ?)')) |
|
76 | ipython_prompt = PromptStripper(re.compile(r'^(In \[\d+\]: |\s*\.{3,}: ?)')) | |
73 |
|
77 | |||
74 | def cell_magic(lines): |
|
78 | def cell_magic(lines): | |
75 | if not lines[0].startswith('%%'): |
|
79 | if not lines or not lines[0].startswith('%%'): | |
76 | return lines |
|
80 | return lines | |
77 | if re.match('%%\w+\?', lines[0]): |
|
81 | if re.match('%%\w+\?', lines[0]): | |
78 | # This case will be handled by help_end |
|
82 | # This case will be handled by help_end | |
@@ -92,7 +96,7 b' def _find_assign_op(token_line):' | |||||
92 | for i, ti in enumerate(token_line): |
|
96 | for i, ti in enumerate(token_line): | |
93 | s = ti.string |
|
97 | s = ti.string | |
94 | if s == '=' and paren_level == 0: |
|
98 | if s == '=' and paren_level == 0: | |
95 |
return i |
|
99 | return i | |
96 | if s in '([{': |
|
100 | if s in '([{': | |
97 | paren_level += 1 |
|
101 | paren_level += 1 | |
98 | elif s in ')]}': |
|
102 | elif s in ')]}': | |
@@ -112,7 +116,7 b' def find_end_of_continued_line(lines, start_line: int):' | |||||
112 | return end_line |
|
116 | return end_line | |
113 |
|
117 | |||
114 | def assemble_continued_line(lines, start: Tuple[int, int], end_line: int): |
|
118 | def assemble_continued_line(lines, start: Tuple[int, int], end_line: int): | |
115 |
"""Assemble a single line from multiple continued line pieces |
|
119 | """Assemble a single line from multiple continued line pieces | |
116 |
|
120 | |||
117 | Continued lines are lines ending in ``\``, and the line following the last |
|
121 | Continued lines are lines ending in ``\``, and the line following the last | |
118 | ``\`` in the block. |
|
122 | ``\`` in the block. | |
@@ -202,7 +206,7 b' class MagicAssign(TokenTransformBase):' | |||||
202 | and (line[assign_ix+1].string == '%') \ |
|
206 | and (line[assign_ix+1].string == '%') \ | |
203 | and (line[assign_ix+2].type == tokenize.NAME): |
|
207 | and (line[assign_ix+2].type == tokenize.NAME): | |
204 | return cls(line[assign_ix+1].start) |
|
208 | return cls(line[assign_ix+1].start) | |
205 |
|
209 | |||
206 | def transform(self, lines: List[str]): |
|
210 | def transform(self, lines: List[str]): | |
207 | """Transform a magic assignment found by the ``find()`` classmethod. |
|
211 | """Transform a magic assignment found by the ``find()`` classmethod. | |
208 | """ |
|
212 | """ | |
@@ -212,12 +216,12 b' class MagicAssign(TokenTransformBase):' | |||||
212 | rhs = assemble_continued_line(lines, (start_line, start_col), end_line) |
|
216 | rhs = assemble_continued_line(lines, (start_line, start_col), end_line) | |
213 | assert rhs.startswith('%'), rhs |
|
217 | assert rhs.startswith('%'), rhs | |
214 | magic_name, _, args = rhs[1:].partition(' ') |
|
218 | magic_name, _, args = rhs[1:].partition(' ') | |
215 |
|
219 | |||
216 | lines_before = lines[:start_line] |
|
220 | lines_before = lines[:start_line] | |
217 | call = "get_ipython().run_line_magic({!r}, {!r})".format(magic_name, args) |
|
221 | call = "get_ipython().run_line_magic({!r}, {!r})".format(magic_name, args) | |
218 | new_line = lhs + call + '\n' |
|
222 | new_line = lhs + call + '\n' | |
219 | lines_after = lines[end_line+1:] |
|
223 | lines_after = lines[end_line+1:] | |
220 |
|
224 | |||
221 | return lines_before + [new_line] + lines_after |
|
225 | return lines_before + [new_line] + lines_after | |
222 |
|
226 | |||
223 |
|
227 | |||
@@ -464,7 +468,7 b' def make_tokens_by_line(lines):' | |||||
464 | pass |
|
468 | pass | |
465 | if not tokens_by_line[-1]: |
|
469 | if not tokens_by_line[-1]: | |
466 | tokens_by_line.pop() |
|
470 | tokens_by_line.pop() | |
467 |
|
471 | |||
468 | return tokens_by_line |
|
472 | return tokens_by_line | |
469 |
|
473 | |||
470 | def show_linewise_tokens(s: str): |
|
474 | def show_linewise_tokens(s: str): | |
@@ -501,12 +505,12 b' class TransformerManager:' | |||||
501 | EscapedCommand, |
|
505 | EscapedCommand, | |
502 | HelpEnd, |
|
506 | HelpEnd, | |
503 | ] |
|
507 | ] | |
504 |
|
508 | |||
505 | def do_one_token_transform(self, lines): |
|
509 | def do_one_token_transform(self, lines): | |
506 | """Find and run the transform earliest in the code. |
|
510 | """Find and run the transform earliest in the code. | |
507 |
|
511 | |||
508 | Returns (changed, lines). |
|
512 | Returns (changed, lines). | |
509 |
|
513 | |||
510 | This method is called repeatedly until changed is False, indicating |
|
514 | This method is called repeatedly until changed is False, indicating | |
511 | that all available transformations are complete. |
|
515 | that all available transformations are complete. | |
512 |
|
516 |
@@ -195,6 +195,14 b' def test_displayobject_repr():' | |||||
195 | j._show_mem_addr = False |
|
195 | j._show_mem_addr = False | |
196 | nt.assert_equal(repr(j), '<IPython.core.display.Javascript object>') |
|
196 | nt.assert_equal(repr(j), '<IPython.core.display.Javascript object>') | |
197 |
|
197 | |||
|
198 | @mock.patch('warnings.warn') | |||
|
199 | def test_encourage_iframe_over_html(m_warn): | |||
|
200 | display.HTML('<br />') | |||
|
201 | m_warn.assert_not_called() | |||
|
202 | ||||
|
203 | display.HTML('<iframe src="http://a.com"></iframe>') | |||
|
204 | m_warn.assert_called_with('Consider using IPython.display.IFrame instead') | |||
|
205 | ||||
198 | def test_progress(): |
|
206 | def test_progress(): | |
199 | p = display.ProgressBar(10) |
|
207 | p = display.ProgressBar(10) | |
200 | nt.assert_in('0/10',repr(p)) |
|
208 | nt.assert_in('0/10',repr(p)) |
@@ -103,6 +103,12 b' b) = zip?' | |||||
103 | [r"get_ipython().set_next_input('(a,\nb) = zip');get_ipython().run_line_magic('pinfo', 'zip')" + "\n"] |
|
103 | [r"get_ipython().set_next_input('(a,\nb) = zip');get_ipython().run_line_magic('pinfo', 'zip')" + "\n"] | |
104 | ) |
|
104 | ) | |
105 |
|
105 | |||
|
106 | def null_cleanup_transformer(lines): | |||
|
107 | """ | |||
|
108 | A cleanup transform that returns an empty list. | |||
|
109 | """ | |||
|
110 | return [] | |||
|
111 | ||||
106 | def check_make_token_by_line_never_ends_empty(): |
|
112 | def check_make_token_by_line_never_ends_empty(): | |
107 | """ |
|
113 | """ | |
108 | Check that not sequence of single or double characters ends up leading to en empty list of tokens |
|
114 | Check that not sequence of single or double characters ends up leading to en empty list of tokens | |
@@ -226,3 +232,7 b' def test_check_complete():' | |||||
226 | for k in short: |
|
232 | for k in short: | |
227 | cc(c+k) |
|
233 | cc(c+k) | |
228 |
|
234 | |||
|
235 | def test_null_cleanup_transformer(): | |||
|
236 | manager = ipt2.TransformerManager() | |||
|
237 | manager.cleanup_transforms.insert(0, null_cleanup_transformer) | |||
|
238 | nt.assert_is(manager.transform_cell(""), "") |
@@ -65,6 +65,21 b' There are also two more powerful display methods:' | |||||
65 | Displays the object as a side effect; the return value is ignored. If this |
|
65 | Displays the object as a side effect; the return value is ignored. If this | |
66 | is defined, all other display methods are ignored. |
|
66 | is defined, all other display methods are ignored. | |
67 |
|
67 | |||
|
68 | To customize how the REPL pretty-prints your object, add a `_repr_pretty_` | |||
|
69 | method to the class. The method should accept a pretty printer, and a boolean | |||
|
70 | that indicates whether the printer detected a cycle. The method should act on | |||
|
71 | the printer to produce your customized pretty output. Here is an example:: | |||
|
72 | ||||
|
73 | class MyObject(object): | |||
|
74 | ||||
|
75 | def _repr_pretty_(self, p, cycle): | |||
|
76 | if cycle: | |||
|
77 | p.text('MyObject(...)') | |||
|
78 | else: | |||
|
79 | p.text('MyObject[...]') | |||
|
80 | ||||
|
81 | For details, see :py:mod:`IPython.lib.pretty`. | |||
|
82 | ||||
68 | Formatters for third-party types |
|
83 | Formatters for third-party types | |
69 | -------------------------------- |
|
84 | -------------------------------- | |
70 |
|
85 |
General Comments 0
You need to be logged in to leave comments.
Login now