##// END OF EJS Templates
Merge branch 'ellisonbg-payload' into trunk
Brian Granger -
r3289:7ac6be05 merge
parent child Browse files
Show More
@@ -0,0 +1,122 b''
1 # -*- coding: utf-8 -*-
2 """Top-level display functions for displaying object in different formats.
3
4 Authors:
5
6 * Brian Granger
7 """
8
9 #-----------------------------------------------------------------------------
10 # Copyright (C) 2008-2010 The IPython Development Team
11 #
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
14 #-----------------------------------------------------------------------------
15
16 #-----------------------------------------------------------------------------
17 # Imports
18 #-----------------------------------------------------------------------------
19
20 #-----------------------------------------------------------------------------
21 # Main functions
22 #-----------------------------------------------------------------------------
23
24 def display(*objs, **kwargs):
25 """Display a Python object in all frontends.
26
27 By default all representations will be computed and sent to the frontends.
28 Frontends can decide which representation is used and how.
29
30 Parameters
31 ----------
32 objs : tuple of objects
33 The Python objects to display.
34 include : list or tuple, optional
35 A list of format type strings (MIME types) to include in the
36 format data dict. If this is set *only* the format types included
37 in this list will be computed.
38 exclude : list or tuple, optional
39 A list of format type string (MIME types) to exclue in the format
40 data dict. If this is set all format types will be computed,
41 except for those included in this argument.
42 """
43 include = kwargs.get('include')
44 exclude = kwargs.get('exclude')
45
46 from IPython.core.interactiveshell import InteractiveShell
47 inst = InteractiveShell.instance()
48 format = inst.display_formatter.format
49 publish = inst.display_pub.publish
50
51 for obj in objs:
52 format_dict = format(obj, include=include, exclude=exclude)
53 publish('IPython.core.display.display', format_dict)
54
55
56 def display_pretty(*objs):
57 """Display the pretty (default) representation of an object.
58
59 Parameters
60 ----------
61 objs : tuple of objects
62 The Python objects to display.
63 """
64 display(*objs, include=['text/plain'])
65
66
67 def display_html(*objs):
68 """Display the HTML representation of an object.
69
70 Parameters
71 ----------
72 objs : tuple of objects
73 The Python objects to display.
74 """
75 display(*objs, include=['text/plain','text/html'])
76
77
78 def display_svg(*objs):
79 """Display the SVG representation of an object.
80
81 Parameters
82 ----------
83 objs : tuple of objects
84 The Python objects to display.
85 """
86 display(*objs, include=['text/plain','image/svg+xml'])
87
88
89 def display_png(*objs):
90 """Display the PNG representation of an object.
91
92 Parameters
93 ----------
94 objs : tuple of objects
95 The Python objects to display.
96 """
97 display(*objs, include=['text/plain','image/png'])
98
99
100 def display_latex(*objs):
101 """Display the LaTeX representation of an object.
102
103 Parameters
104 ----------
105 objs : tuple of objects
106 The Python objects to display.
107 """
108 display(*objs, include=['text/plain','text/latex'])
109
110
111 def display_json(*objs):
112 """Display the JSON representation of an object.
113
114 Parameters
115 ----------
116 objs : tuple of objects
117 The Python objects to display.
118 """
119 display(*objs, include=['text/plain','application/json'])
120
121
122
@@ -0,0 +1,145 b''
1 """An interface for publishing rich data to frontends.
2
3 There are two components of the display system:
4
5 * Display formatters, which take a Python object and compute the
6 representation of the object in various formats (text, HTML, SVg, etc.).
7 * The display publisher that is used to send the representation data to the
8 various frontends.
9
10 This module defines the logic display publishing. The display publisher uses
11 the ``display_data`` message type that is defined in the IPython messaging
12 spec.
13
14 Authors:
15
16 * Brian Granger
17 """
18
19 #-----------------------------------------------------------------------------
20 # Copyright (C) 2008-2010 The IPython Development Team
21 #
22 # Distributed under the terms of the BSD License. The full license is in
23 # the file COPYING, distributed as part of this software.
24 #-----------------------------------------------------------------------------
25
26 #-----------------------------------------------------------------------------
27 # Imports
28 #-----------------------------------------------------------------------------
29
30 from __future__ import print_function
31
32 from IPython.config.configurable import Configurable
33
34 #-----------------------------------------------------------------------------
35 # Main payload class
36 #-----------------------------------------------------------------------------
37
38 class DisplayPublisher(Configurable):
39 """A traited class that publishes display data to frontends.
40
41 Instances of this class are created by the main IPython object and should
42 be accessed there.
43 """
44
45 def _validate_data(self, source, data, metadata=None):
46 """Validate the display data.
47
48 Parameters
49 ----------
50 source : str
51 The fully dotted name of the callable that created the data, like
52 :func:`foo.bar.my_formatter`.
53 data : dict
54 The formata data dictionary.
55 metadata : dict
56 Any metadata for the data.
57 """
58
59 if not isinstance(source, str):
60 raise TypeError('source must be a str, got: %r' % source)
61 if not isinstance(data, dict):
62 raise TypeError('data must be a dict, got: %r' % data)
63 if metadata is not None:
64 if not isinstance(metadata, dict):
65 raise TypeError('metadata must be a dict, got: %r' % data)
66
67 def publish(self, source, data, metadata=None):
68 """Publish data and metadata to all frontends.
69
70 See the ``display_data`` message in the messaging documentation for
71 more details about this message type.
72
73 The following MIME types are currently implemented:
74
75 * text/plain
76 * text/html
77 * text/latex
78 * application/json
79 * image/png
80 * immage/svg+xml
81
82 Parameters
83 ----------
84 source : str
85 A string that give the function or method that created the data,
86 such as 'IPython.core.page'.
87 data : dict
88 A dictionary having keys that are valid MIME types (like
89 'text/plain' or 'image/svg+xml') and values that are the data for
90 that MIME type. The data itself must be a JSON'able data
91 structure. Minimally all data should have the 'text/plain' data,
92 which can be displayed by all frontends. If more than the plain
93 text is given, it is up to the frontend to decide which
94 representation to use.
95 metadata : dict
96 A dictionary for metadata related to the data. This can contain
97 arbitrary key, value pairs that frontends can use to interpret
98 the data.
99 """
100 from IPython.utils import io
101 # The default is to simply write the plain text data using io.Term.
102 if data.has_key('text/plain'):
103 print(data['text/plain'], file=io.Term.cout)
104
105
106 def publish_display_data(self, source, data, metadata=None):
107 """Publish data and metadata to all frontends.
108
109 See the ``display_data`` message in the messaging documentation for
110 more details about this message type.
111
112 The following MIME types are currently implemented:
113
114 * text/plain
115 * text/html
116 * text/latex
117 * application/json
118 * image/png
119 * immage/svg+xml
120
121 Parameters
122 ----------
123 source : str
124 A string that give the function or method that created the data,
125 such as 'IPython.core.page'.
126 data : dict
127 A dictionary having keys that are valid MIME types (like
128 'text/plain' or 'image/svg+xml') and values that are the data for
129 that MIME type. The data itself must be a JSON'able data
130 structure. Minimally all data should have the 'text/plain' data,
131 which can be displayed by all frontends. If more than the plain
132 text is given, it is up to the frontend to decide which
133 representation to use.
134 metadata : dict
135 A dictionary for metadata related to the data. This can contain
136 arbitrary key, value pairs that frontends can use to interpret
137 the data.
138 """
139 from IPython.core.interactiveshell import InteractiveShell
140 InteractiveShell.instance().display_pub.publish(
141 source,
142 data,
143 metadata
144 )
145
@@ -0,0 +1,65 b''
1 """A print function that pretty prints sympy Basic objects.
2
3 Authors:
4 * Brian Granger
5 """
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2011 The IPython Development Team
8 #
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
12
13 #-----------------------------------------------------------------------------
14 # Imports
15 #-----------------------------------------------------------------------------
16
17 from IPython.lib.latextools import latex_to_png
18
19 from sympy import pretty, latex
20
21 #-----------------------------------------------------------------------------
22 # Definitions of magic functions for use with IPython
23 #-----------------------------------------------------------------------------
24
25 def print_basic_unicode(o, p, cycle):
26 """A function to pretty print sympy Basic objects."""
27 if cycle:
28 return p.text('Basic(...)')
29 out = pretty(o, use_unicode=True)
30 if '\n' in out:
31 p.text(u'\n')
32 p.text(out)
33
34
35 def print_png(o):
36 """A funciton to display sympy expression using LaTex -> PNG."""
37 s = latex(o, mode='inline')
38 # mathtext does not understand certain latex flags, so we try to replace
39 # them with suitable subs.
40 s = s.replace('\\operatorname','')
41 s = s.replace('\\overline', '\\bar')
42 png = latex_to_png(s, encode=True)
43 return png
44
45 _loaded = False
46
47
48 def load_ipython_extension(ip):
49 """Load the extension in IPython."""
50 global _loaded
51 if not _loaded:
52 plaintext_formatter = ip.display_formatter.formatters['text/plain']
53 plaintext_formatter.for_type_by_name(
54 'sympy.core.basic', 'Basic', print_basic_unicode
55 )
56 plaintext_formatter.for_type_by_name(
57 'sympy.matrices.matrices', 'Matrix', print_basic_unicode
58 )
59
60 png_formatter = ip.display_formatter.formatters['image/png']
61 png_formatter.for_type_by_name(
62 'sympy.core.basic', 'Basic', print_png
63 )
64 _loaded = True
65
@@ -0,0 +1,62 b''
1 # -*- coding: utf-8 -*-
2 """Tools for handling LaTeX.
3
4 Authors:
5
6 * Brian Granger
7 """
8 #-----------------------------------------------------------------------------
9 # Copyright (c) 2010, IPython Development Team.
10 #
11 # Distributed under the terms of the Modified BSD License.
12 #
13 # The full license is in the file COPYING.txt, distributed with this software.
14 #-----------------------------------------------------------------------------
15
16 #-----------------------------------------------------------------------------
17 # Imports
18 #-----------------------------------------------------------------------------
19
20 from StringIO import StringIO
21 from base64 import encodestring
22
23 #-----------------------------------------------------------------------------
24 # Tools
25 #-----------------------------------------------------------------------------
26
27
28 def latex_to_png(s, encode=True):
29 """Render a LaTeX string to PNG using matplotlib.mathtext.
30
31 Parameters
32 ----------
33 s : str
34 The raw string containing valid inline LaTeX.
35 encode : bool, optional
36 Should the PNG data bebase64 encoded to make it JSON'able.
37 """
38 from matplotlib import mathtext
39
40 mt = mathtext.MathTextParser('bitmap')
41 f = StringIO()
42 mt.to_png(f, s, fontsize=12)
43 bin_data = f.getvalue()
44 if encode:
45 bin_data = encodestring(bin_data)
46 return bin_data
47
48 _data_uri_template_png = """<img src="data:image/png;base64,%s" alt=%s />"""
49
50 def latex_to_html(s, alt='image'):
51 """Render LaTeX to HTML with embedded PNG data using data URIs.
52
53 Parameters
54 ----------
55 s : str
56 The raw string containing valid inline LateX.
57 alt : str
58 The alt text to use for the HTML.
59 """
60 base64_data = latex_to_png(s, encode=True)
61 return _data_uri_template_png % (base64_data, alt)
62
@@ -81,8 +81,6 b' c = get_config()'
81 81
82 82 # c.InteractiveShell.pdb = False
83 83
84 # c.InteractiveShell.pprint = True
85
86 84 # c.InteractiveShell.prompt_in1 = 'In [\#]: '
87 85 # c.InteractiveShell.prompt_in2 = ' .\D.: '
88 86 # c.InteractiveShell.prompt_out = 'Out[\#]: '
@@ -129,6 +127,12 b' c = get_config()'
129 127 # c.InteractiveShell.xmode = 'Context'
130 128
131 129 #-----------------------------------------------------------------------------
130 # Formatter and display options
131 #-----------------------------------------------------------------------------
132
133 # c.PlainTextFormatter.pprint = True
134
135 #-----------------------------------------------------------------------------
132 136 # PrefilterManager options
133 137 #-----------------------------------------------------------------------------
134 138
@@ -15,7 +15,15 b" f, g, h = map(Function, 'fgh')"
15 15 # You have to make sure that attributes that are containers already
16 16 # exist before using them. Simple assigning a new list will override
17 17 # all previous values.
18
18 19 if hasattr(c.Global, 'exec_lines'):
19 20 c.Global.exec_lines.append(lines)
20 21 else:
21 22 c.Global.exec_lines = [lines]
23
24 # Load the sympy_printing extension to enable nice printing of sympy expr's.
25 if hasattr(c.Global, 'extensions'):
26 c.Global.extensions.append('sympy_printing')
27 else:
28 c.Global.extensions = ['sympy_printing']
29
@@ -1,10 +1,13 b''
1 1 # -*- coding: utf-8 -*-
2 2 """Displayhook for IPython.
3 3
4 This defines a callable class that IPython uses for `sys.displayhook`.
5
4 6 Authors:
5 7
6 8 * Fernando Perez
7 9 * Brian Granger
10 * Robert Kern
8 11 """
9 12
10 13 #-----------------------------------------------------------------------------
@@ -27,7 +30,6 b' import IPython.utils.generics'
27 30 import IPython.utils.io
28 31 from IPython.utils.traitlets import Instance, List
29 32 from IPython.utils.warn import warn
30 from IPython.core.formatters import DefaultFormatter
31 33
32 34 #-----------------------------------------------------------------------------
33 35 # Main displayhook class
@@ -54,19 +56,6 b' class DisplayHook(Configurable):'
54 56
55 57 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
56 58
57 # The default formatter.
58 default_formatter = Instance('IPython.core.formatters.FormatterABC')
59 def _default_formatter_default(self):
60 # FIXME: backwards compatibility for the InteractiveShell.pprint option?
61 return DefaultFormatter(config=self.config)
62
63 # Any additional FormatterABC instances we use.
64 # FIXME: currently unused.
65 extra_formatters = List(config=True)
66
67 # Each call to the In[] prompt raises it by 1, even the first.
68 #prompt_count = Int(0)
69
70 59 def __init__(self, shell=None, cache_size=1000,
71 60 colors='NoColor', input_sep='\n',
72 61 output_sep='\n', output_sep2='',
@@ -185,36 +174,63 b' class DisplayHook(Configurable):'
185 174 pass
186 175
187 176 def write_output_prompt(self):
188 """Write the output prompt."""
177 """Write the output prompt.
178
179 The default implementation simply writes the prompt to
180 ``io.Term.cout``.
181 """
189 182 # Use write, not print which adds an extra space.
190 183 IPython.utils.io.Term.cout.write(self.output_sep)
191 184 outprompt = str(self.prompt_out)
192 185 if self.do_full_cache:
193 186 IPython.utils.io.Term.cout.write(outprompt)
194 187
195 def compute_result_repr(self, result):
196 """Compute and return the repr of the object to be displayed.
197
198 This method only compute the string form of the repr and should NOT
199 actually print or write that to a stream.
188 def compute_format_data(self, result):
189 """Compute format data of the object to be displayed.
190
191 The format data is a generalization of the :func:`repr` of an object.
192 In the default implementation the format data is a :class:`dict` of
193 key value pair where the keys are valid MIME types and the values
194 are JSON'able data structure containing the raw data for that MIME
195 type. It is up to frontends to determine pick a MIME to to use and
196 display that data in an appropriate manner.
197
198 This method only computes the format data for the object and should
199 NOT actually print or write that to a stream.
200
201 Parameters
202 ----------
203 result : object
204 The Python object passed to the display hook, whose format will be
205 computed.
206
207 Returns
208 -------
209 format_data : dict
210 A :class:`dict` whose keys are valid MIME types and values are
211 JSON'able raw data for that MIME type. It is recommended that
212 all return values of this should always include the "text/plain"
213 MIME type representation of the object.
200 214 """
201 result_repr = self.default_formatter(result)
202 extra_formats = []
203 for f in self.extra_formatters:
204 try:
205 data = f(result)
206 except Exception:
207 # FIXME: log the exception.
208 continue
209 if data is not None:
210 extra_formats.append((f.id, f.format, data))
215 return self.shell.display_formatter.format(result)
211 216
212 return result_repr, extra_formats
217 def write_format_data(self, format_dict):
218 """Write the format data dict to the frontend.
213 219
214 def write_result_repr(self, result_repr, extra_formats):
220 This default version of this method simply writes the plain text
221 representation of the object to ``io.Term.cout``. Subclasses should
222 override this method to send the entire `format_dict` to the
223 frontends.
224
225 Parameters
226 ----------
227 format_dict : dict
228 The format dict for the object passed to `sys.displayhook`.
229 """
215 230 # We want to print because we want to always make sure we have a
216 231 # newline, even if all the prompt separators are ''. This is the
217 232 # standard IPython behavior.
233 result_repr = format_dict['text/plain']
218 234 if '\n' in result_repr:
219 235 # So that multi-line strings line up with the left column of
220 236 # the screen, instead of having the output prompt mess up
@@ -278,8 +294,8 b' class DisplayHook(Configurable):'
278 294 if result is not None and not self.quiet():
279 295 self.start_displayhook()
280 296 self.write_output_prompt()
281 result_repr, extra_formats = self.compute_result_repr(result)
282 self.write_result_repr(result_repr, extra_formats)
297 format_dict = self.compute_format_data(result)
298 self.write_format_data(format_dict)
283 299 self.update_user_ns(result)
284 300 self.log_output(result)
285 301 self.finish_displayhook()
@@ -300,5 +316,6 b' class DisplayHook(Configurable):'
300 316 if '_' not in __builtin__.__dict__:
301 317 self.shell.user_ns.update({'_':None,'__':None, '___':None})
302 318 import gc
303 gc.collect() # xxx needed?
319 # TODO: Is this really needed?
320 gc.collect()
304 321
This diff has been collapsed as it changes many lines, (515 lines changed) Show them Hide them
@@ -1,36 +1,11 b''
1 1 # -*- coding: utf-8 -*-
2 """Displayhook formatters.
2 """Display formatters.
3 3
4 The DefaultFormatter is always present and may be configured from the
5 ipython_config.py file. For example, to add a pretty-printer for a numpy.dtype
6 object::
7 4
8 def dtype_pprinter(obj, p, cycle):
9 if cycle:
10 return p.text('dtype(...)')
11 if hasattr(obj, 'fields'):
12 if obj.fields is None:
13 p.text(repr(obj))
14 else:
15 p.begin_group(7, 'dtype([')
16 for i, field in enumerate(obj.descr):
17 if i > 0:
18 p.text(',')
19 p.breakable()
20 p.pretty(field)
21 p.end_group(7, '])')
5 Authors:
22 6
23 c.DefaultFormatter.deferred_pprinters = {
24 ('numpy', 'dtype'): dtype_pprinter,
25 }
26
27 The deferred_pprinters dictionary is the preferred way to configure these
28 pretty-printers. This allows you to define the pretty-printer without needing to
29 import the type itself. The dictionary maps (modulename, typename) pairs to
30 a function.
31
32 See the `IPython.external.pretty` documentation for how to write
33 pretty-printer functions.
7 * Robert Kern
8 * Brian Granger
34 9 """
35 10 #-----------------------------------------------------------------------------
36 11 # Copyright (c) 2010, IPython Development Team.
@@ -40,9 +15,14 b' pretty-printer functions.'
40 15 # The full license is in the file COPYING.txt, distributed with this software.
41 16 #-----------------------------------------------------------------------------
42 17
18 #-----------------------------------------------------------------------------
19 # Imports
20 #-----------------------------------------------------------------------------
21
43 22 # Stdlib imports
44 23 import abc
45 from cStringIO import StringIO
24 # We must use StringIO, as cStringIO doesn't handle unicode properly.
25 from StringIO import StringIO
46 26
47 27 # Our own imports
48 28 from IPython.config.configurable import Configurable
@@ -51,19 +31,315 b' from IPython.utils.traitlets import Bool, Dict, Int, Str'
51 31
52 32
53 33 #-----------------------------------------------------------------------------
54 # Classes and functions
34 # The main DisplayFormatter class
35 #-----------------------------------------------------------------------------
36
37
38 class DisplayFormatter(Configurable):
39
40 # When set to true only the default plain text formatter will be used.
41 plain_text_only = Bool(False, config=True)
42
43 # A dict of formatter whose keys are format types (MIME types) and whose
44 # values are subclasses of BaseFormatter.
45 formatters = Dict(config=True)
46 def _formatters_default(self):
47 """Activate the default formatters."""
48 formatter_classes = [
49 PlainTextFormatter,
50 HTMLFormatter,
51 SVGFormatter,
52 PNGFormatter,
53 LatexFormatter,
54 JSONFormatter
55 ]
56 d = {}
57 for cls in formatter_classes:
58 f = cls(config=self.config)
59 d[f.format_type] = f
60 return d
61
62 def format(self, obj, include=None, exclude=None):
63 """Return a format data dict for an object.
64
65 By default all format types will be computed.
66
67 The following MIME types are currently implemented:
68
69 * text/plain
70 * text/html
71 * text/latex
72 * application/json
73 * image/png
74 * immage/svg+xml
75
76 Parameters
77 ----------
78 obj : object
79 The Python object whose format data will be computed.
80 include : list or tuple, optional
81 A list of format type strings (MIME types) to include in the
82 format data dict. If this is set *only* the format types included
83 in this list will be computed.
84 exclude : list or tuple, optional
85 A list of format type string (MIME types) to exclue in the format
86 data dict. If this is set all format types will be computed,
87 except for those included in this argument.
88
89 Returns
90 -------
91 format_dict : dict
92 A dictionary of key/value pairs, one or each format that was
93 generated for the object. The keys are the format types, which
94 will usually be MIME type strings and the values and JSON'able
95 data structure containing the raw data for the representation in
96 that format.
97 """
98 format_dict = {}
99
100 # If plain text only is active
101 if self.plain_text_only:
102 formatter = self.formatters['text/plain']
103 try:
104 data = formatter(obj)
105 except:
106 # FIXME: log the exception
107 raise
108 if data is not None:
109 format_dict['text/plain'] = data
110 return format_dict
111
112 for format_type, formatter in self.formatters.items():
113 if include is not None:
114 if format_type not in include:
115 continue
116 if exclude is not None:
117 if format_type in exclude:
118 continue
119 try:
120 data = formatter(obj)
121 except:
122 # FIXME: log the exception
123 raise
124 if data is not None:
125 format_dict[format_type] = data
126 return format_dict
127
128 @property
129 def format_types(self):
130 """Return the format types (MIME types) of the active formatters."""
131 return self.formatters.keys()
132
133
134 #-----------------------------------------------------------------------------
135 # Formatters for specific format types (text, html, svg, etc.)
55 136 #-----------------------------------------------------------------------------
56 137
57 class DefaultFormatter(Configurable):
138
139 class FormatterABC(object):
140 """ Abstract base class for Formatters.
141
142 A formatter is a callable class that is responsible for computing the
143 raw format data for a particular format type (MIME type). For example,
144 an HTML formatter would have a format type of `text/html` and would return
145 the HTML representation of the object when called.
146 """
147 __metaclass__ = abc.ABCMeta
148
149 # The format type of the data returned, usually a MIME type.
150 format_type = 'text/plain'
151
152 # Is the formatter enabled...
153 enabled = True
154
155 @abc.abstractmethod
156 def __call__(self, obj):
157 """Return a JSON'able representation of the object.
158
159 If the object cannot be formatted by this formatter, then return None
160 """
161 try:
162 return repr(obj)
163 except TypeError:
164 return None
165
166
167 class BaseFormatter(Configurable):
168 """A base formatter class that is configurable.
169
170 This formatter should usually be used as the base class of all formatters.
171 It is a traited :class:`Configurable` class and includes an extensible
172 API for users to determine how their objects are formatted. The following
173 logic is used to find a function to format an given object.
174
175 1. The object is introspected to see if it has a method with the name
176 :attr:`print_method`. If is does, that object is passed to that method
177 for formatting.
178 2. If no print method is found, three internal dictionaries are consulted
179 to find print method: :attr:`singleton_printers`, :attr:`type_printers`
180 and :attr:`deferred_printers`.
181
182 Users should use these dictionaries to register functions that will be
183 used to compute the format data for their objects (if those objects don't
184 have the special print methods). The easiest way of using these
185 dictionaries is through the :meth:`for_type` and :meth:`for_type_by_name`
186 methods.
187
188 If no function/callable is found to compute the format data, ``None`` is
189 returned and this format type is not used.
190 """
191
192 format_type = Str('text/plain')
193
194 enabled = Bool(True, config=True)
195
196 print_method = Str('__repr__')
197
198 # The singleton printers.
199 # Maps the IDs of the builtin singleton objects to the format functions.
200 singleton_printers = Dict(config=True)
201 def _singleton_printers_default(self):
202 return {}
203
204 # The type-specific printers.
205 # Map type objects to the format functions.
206 type_printers = Dict(config=True)
207 def _type_printers_default(self):
208 return {}
209
210 # The deferred-import type-specific printers.
211 # Map (modulename, classname) pairs to the format functions.
212 deferred_printers = Dict(config=True)
213 def _deferred_printers_default(self):
214 return {}
215
216 def __call__(self, obj):
217 """Compute the format for an object."""
218 if self.enabled:
219 obj_id = id(obj)
220 try:
221 obj_class = getattr(obj, '__class__', None) or type(obj)
222 if hasattr(obj_class, self.print_method):
223 printer = getattr(obj_class, self.print_method)
224 return printer(obj)
225 try:
226 printer = self.singleton_printers[obj_id]
227 except (TypeError, KeyError):
228 pass
229 else:
230 return printer(obj)
231 for cls in pretty._get_mro(obj_class):
232 if cls in self.type_printers:
233 return self.type_printers[cls](obj)
234 else:
235 printer = self._in_deferred_types(cls)
236 if printer is not None:
237 return printer(obj)
238 return None
239 except Exception:
240 pass
241 else:
242 return None
243
244 def for_type(self, typ, func):
245 """Add a format function for a given type.
246
247 Parameters
248 -----------
249 typ : class
250 The class of the object that will be formatted using `func`.
251 func : callable
252 The callable that will be called to compute the format data. The
253 call signature of this function is simple, it must take the
254 object to be formatted and return the raw data for the given
255 format. Subclasses may use a different call signature for the
256 `func` argument.
257 """
258 oldfunc = self.type_printers.get(typ, None)
259 if func is not None:
260 # To support easy restoration of old printers, we need to ignore
261 # Nones.
262 self.type_printers[typ] = func
263 return oldfunc
264
265 def for_type_by_name(self, type_module, type_name, func):
266 """Add a format function for a type specified by the full dotted
267 module and name of the type, rather than the type of the object.
268
269 Parameters
270 ----------
271 type_module : str
272 The full dotted name of the module the type is defined in, like
273 ``numpy``.
274 type_name : str
275 The name of the type (the class name), like ``dtype``
276 func : callable
277 The callable that will be called to compute the format data. The
278 call signature of this function is simple, it must take the
279 object to be formatted and return the raw data for the given
280 format. Subclasses may use a different call signature for the
281 `func` argument.
282 """
283 key = (type_module, type_name)
284 oldfunc = self.deferred_printers.get(key, None)
285 if func is not None:
286 # To support easy restoration of old printers, we need to ignore
287 # Nones.
288 self.deferred_printers[key] = func
289 return oldfunc
290
291 def _in_deferred_types(self, cls):
292 """
293 Check if the given class is specified in the deferred type registry.
294
295 Returns the printer from the registry if it exists, and None if the
296 class is not in the registry. Successful matches will be moved to the
297 regular type registry for future use.
298 """
299 mod = getattr(cls, '__module__', None)
300 name = getattr(cls, '__name__', None)
301 key = (mod, name)
302 printer = None
303 if key in self.deferred_printers:
304 # Move the printer over to the regular registry.
305 printer = self.deferred_printers.pop(key)
306 self.type_printers[cls] = printer
307 return printer
308
309
310 class PlainTextFormatter(BaseFormatter):
58 311 """ The default pretty-printer.
312
313 This uses :mod:`IPython.external.pretty` to compute the format data of
314 the object. If the object cannot be pretty printed, :func:`repr` is used.
315 See the documentation of :mod:`IPython.external.pretty` for details on
316 how to write pretty printers. Here is a simple example::
317
318 def dtype_pprinter(obj, p, cycle):
319 if cycle:
320 return p.text('dtype(...)')
321 if hasattr(obj, 'fields'):
322 if obj.fields is None:
323 p.text(repr(obj))
324 else:
325 p.begin_group(7, 'dtype([')
326 for i, field in enumerate(obj.descr):
327 if i > 0:
328 p.text(',')
329 p.breakable()
330 p.pretty(field)
331 p.end_group(7, '])')
59 332 """
60 333
61 # The ID of the formatter.
62 id = Str('default')
334 # The format type of data returned.
335 format_type = Str('text/plain')
63 336
64 # The kind of data returned.
65 # This is often, but not always a MIME type.
66 format = Str('text/plain')
337 # This subclass ignores this attribute as it always need to return
338 # something.
339 enabled = Bool(True, config=False)
340
341 # Look for a __pretty__ methods to use for pretty printing.
342 print_method = Str('__pretty__')
67 343
68 344 # Whether to pretty-print or not.
69 345 pprint = Bool(True, config=True)
@@ -77,93 +353,152 b' class DefaultFormatter(Configurable):'
77 353 # The newline character.
78 354 newline = Str('\n', config=True)
79 355
80 # The singleton prettyprinters.
81 # Maps the IDs of the builtin singleton objects to the format functions.
82 singleton_pprinters = Dict(config=True)
83 def _singleton_pprinters_default(self):
356 # Use the default pretty printers from IPython.external.pretty.
357 def _singleton_printers_default(self):
84 358 return pretty._singleton_pprinters.copy()
85 359
86 # The type-specific prettyprinters.
87 # Map type objects to the format functions.
88 type_pprinters = Dict(config=True)
89 def _type_pprinters_default(self):
360 def _type_printers_default(self):
90 361 return pretty._type_pprinters.copy()
91 362
92 # The deferred-import type-specific prettyprinters.
93 # Map (modulename, classname) pairs to the format functions.
94 deferred_pprinters = Dict(config=True)
95 def _deferred_pprinters_default(self):
363 def _deferred_printers_default(self):
96 364 return pretty._deferred_type_pprinters.copy()
97 365
98 366 #### FormatterABC interface ####
99 367
100 368 def __call__(self, obj):
101 """ Format the object.
102 """
369 """Compute the pretty representation of the object."""
103 370 if not self.pprint:
104 371 try:
105 372 return repr(obj)
106 373 except TypeError:
107 374 return ''
108 375 else:
376 # This uses use StringIO, as cStringIO doesn't handle unicode.
109 377 stream = StringIO()
110 378 printer = pretty.RepresentationPrinter(stream, self.verbose,
111 379 self.max_width, self.newline,
112 singleton_pprinters=self.singleton_pprinters,
113 type_pprinters=self.type_pprinters,
114 deferred_pprinters=self.deferred_pprinters)
380 singleton_pprinters=self.singleton_printers,
381 type_pprinters=self.type_printers,
382 deferred_pprinters=self.deferred_printers)
115 383 printer.pretty(obj)
116 384 printer.flush()
117 385 return stream.getvalue()
118 386
119 387
120 #### DefaultFormatter interface ####
388 class HTMLFormatter(BaseFormatter):
389 """An HTML formatter.
121 390
122 def for_type(self, typ, func):
123 """
124 Add a pretty printer for a given type.
391 To define the callables that compute the HTML representation of your
392 objects, define a :meth:`__html__` method or use the :meth:`for_type`
393 or :meth:`for_type_by_name` methods to register functions that handle
394 this.
125 395 """
126 oldfunc = self.type_pprinters.get(typ, None)
127 if func is not None:
128 # To support easy restoration of old pprinters, we need to ignore
129 # Nones.
130 self.type_pprinters[typ] = func
131 return oldfunc
396 format_type = Str('text/html')
132 397
133 def for_type_by_name(self, type_module, type_name, func):
398 print_method = Str('__html__')
399
400
401 class SVGFormatter(BaseFormatter):
402 """An SVG formatter.
403
404 To define the callables that compute the SVG representation of your
405 objects, define a :meth:`__svg__` method or use the :meth:`for_type`
406 or :meth:`for_type_by_name` methods to register functions that handle
407 this.
134 408 """
135 Add a pretty printer for a type specified by the module and name of
136 a type rather than the type object itself.
409 format_type = Str('image/svg+xml')
410
411 print_method = Str('__svg__')
412
413
414 class PNGFormatter(BaseFormatter):
415 """A PNG formatter.
416
417 To define the callables that compute the PNG representation of your
418 objects, define a :meth:`__png__` method or use the :meth:`for_type`
419 or :meth:`for_type_by_name` methods to register functions that handle
420 this. The raw data should be the base64 encoded raw png data.
137 421 """
138 key = (type_module, type_name)
139 oldfunc = self.deferred_pprinters.get(key, None)
140 if func is not None:
141 # To support easy restoration of old pprinters, we need to ignore
142 # Nones.
143 self.deferred_pprinters[key] = func
144 return oldfunc
422 format_type = Str('image/png')
145 423
424 print_method = Str('__png__')
146 425
147 class FormatterABC(object):
148 """ Abstract base class for Formatters.
426
427 class LatexFormatter(BaseFormatter):
428 """A LaTeX formatter.
429
430 To define the callables that compute the LaTeX representation of your
431 objects, define a :meth:`__latex__` method or use the :meth:`for_type`
432 or :meth:`for_type_by_name` methods to register functions that handle
433 this.
149 434 """
150 __metaclass__ = abc.ABCMeta
435 format_type = Str('text/latex')
151 436
152 # The ID of the formatter.
153 id = 'abstract'
437 print_method = Str('__latex__')
154 438
155 # The kind of data returned.
156 format = 'text/plain'
157 439
158 @abc.abstractmethod
159 def __call__(self, obj):
160 """ Return a JSONable representation of the object.
440 class JSONFormatter(BaseFormatter):
441 """A JSON string formatter.
161 442
162 If the object cannot be formatted by this formatter, then return None
443 To define the callables that compute the JSON string representation of
444 your objects, define a :meth:`__json__` method or use the :meth:`for_type`
445 or :meth:`for_type_by_name` methods to register functions that handle
446 this.
163 447 """
164 try:
165 return repr(obj)
166 except TypeError:
167 return None
448 format_type = Str('application/json')
449
450 print_method = Str('__json__')
451
452
453 FormatterABC.register(BaseFormatter)
454 FormatterABC.register(PlainTextFormatter)
455 FormatterABC.register(HTMLFormatter)
456 FormatterABC.register(SVGFormatter)
457 FormatterABC.register(PNGFormatter)
458 FormatterABC.register(LatexFormatter)
459 FormatterABC.register(JSONFormatter)
460
461
462 def format_display_data(obj, include=None, exclude=None):
463 """Return a format data dict for an object.
464
465 By default all format types will be computed.
466
467 The following MIME types are currently implemented:
468
469 * text/plain
470 * text/html
471 * text/latex
472 * application/json
473 * image/png
474 * immage/svg+xml
475
476 Parameters
477 ----------
478 obj : object
479 The Python object whose format data will be computed.
480
481 Returns
482 -------
483 format_dict : dict
484 A dictionary of key/value pairs, one or each format that was
485 generated for the object. The keys are the format types, which
486 will usually be MIME type strings and the values and JSON'able
487 data structure containing the raw data for the representation in
488 that format.
489 include : list or tuple, optional
490 A list of format type strings (MIME types) to include in the
491 format data dict. If this is set *only* the format types included
492 in this list will be computed.
493 exclude : list or tuple, optional
494 A list of format type string (MIME types) to exclue in the format
495 data dict. If this is set all format types will be computed,
496 except for those included in this argument.
497 """
498 from IPython.core.interactiveshell import InteractiveShell
168 499
169 FormatterABC.register(DefaultFormatter)
500 InteractiveShell.instance().display_formatter.format(
501 obj,
502 include,
503 exclude
504 )
@@ -156,33 +156,6 b' class CommandChainDispatcher:'
156 156 return iter(self.chain)
157 157
158 158
159 def result_display(self,arg):
160 """ Default display hook.
161
162 Called for displaying the result to the user.
163 """
164
165 if self.pprint:
166 try:
167 out = pformat(arg)
168 except:
169 # Work around possible bugs in pformat
170 out = repr(arg)
171 if '\n' in out:
172 # So that multi-line strings line up with the left column of
173 # the screen, instead of having the output prompt mess up
174 # their first line.
175 IPython.utils.io.Term.cout.write('\n')
176 print >>IPython.utils.io.Term.cout, out
177 else:
178 # By default, the interactive prompt uses repr() to display results,
179 # so we should honor this. Users who'd rather use a different
180 # mechanism can easily override this hook.
181 print >>IPython.utils.io.Term.cout, repr(arg)
182 # the default display hook doesn't manipulate the value to put in history
183 return None
184
185
186 159 def input_prefilter(self,line):
187 160 """ Default input prefilter
188 161
@@ -41,9 +41,11 b' from IPython.core.builtin_trap import BuiltinTrap'
41 41 from IPython.core.compilerop import CachingCompiler
42 42 from IPython.core.display_trap import DisplayTrap
43 43 from IPython.core.displayhook import DisplayHook
44 from IPython.core.displaypub import DisplayPublisher
44 45 from IPython.core.error import TryNext, UsageError
45 46 from IPython.core.extensions import ExtensionManager
46 47 from IPython.core.fakemodule import FakeModule, init_fakemod_dict
48 from IPython.core.formatters import DisplayFormatter
47 49 from IPython.core.history import HistoryManager
48 50 from IPython.core.inputsplitter import IPythonInputSplitter
49 51 from IPython.core.logger import Logger
@@ -149,7 +151,10 b' class InteractiveShell(Configurable, Magic):'
149 151 default_value=get_default_colors(), config=True)
150 152 debug = CBool(False, config=True)
151 153 deep_reload = CBool(False, config=True)
154 display_formatter = Instance(DisplayFormatter)
152 155 displayhook_class = Type(DisplayHook)
156 display_pub_class = Type(DisplayPublisher)
157
153 158 exit_now = CBool(False)
154 159 # Monotonically increasing execution counter
155 160 execution_count = Int(1)
@@ -167,7 +172,6 b' class InteractiveShell(Configurable, Magic):'
167 172 config=True)
168 173 pdb = CBool(False, config=True)
169 174
170 pprint = CBool(True, config=True)
171 175 profile = Str('', config=True)
172 176 prompt_in1 = Str('In [\\#]: ', config=True)
173 177 prompt_in2 = Str(' .\\D.: ', config=True)
@@ -284,6 +288,8 b' class InteractiveShell(Configurable, Magic):'
284 288 self.init_io()
285 289 self.init_traceback_handlers(custom_exceptions)
286 290 self.init_prompts()
291 self.init_display_formatter()
292 self.init_display_pub()
287 293 self.init_displayhook()
288 294 self.init_reload_doctest()
289 295 self.init_magics()
@@ -481,6 +487,12 b' class InteractiveShell(Configurable, Magic):'
481 487 # will initialize that object and all prompt related information.
482 488 pass
483 489
490 def init_display_formatter(self):
491 self.display_formatter = DisplayFormatter(config=self.config)
492
493 def init_display_pub(self):
494 self.display_pub = self.display_pub_class(config=self.config)
495
484 496 def init_displayhook(self):
485 497 # Initialize displayhook, set in/out prompts and printing system
486 498 self.displayhook = self.displayhook_class(
@@ -2424,12 +2424,12 b' Defaulting color scheme to \'NoColor\'"""'
2424 2424 else:
2425 2425 shell.inspector.set_active_scheme('NoColor')
2426 2426
2427 def magic_Pprint(self, parameter_s=''):
2427 def magic_pprint(self, parameter_s=''):
2428 2428 """Toggle pretty printing on/off."""
2429
2430 self.shell.pprint = 1 - self.shell.pprint
2429 ptformatter = self.shell.display_formatter.formatters['text/plain']
2430 ptformatter.pprint = bool(1 - ptformatter.pprint)
2431 2431 print 'Pretty printing has been turned', \
2432 ['OFF','ON'][self.shell.pprint]
2432 ['OFF','ON'][ptformatter.pprint]
2433 2433
2434 2434 def magic_Exit(self, parameter_s=''):
2435 2435 """Exit IPython."""
@@ -3163,6 +3163,8 b' Defaulting color scheme to \'NoColor\'"""'
3163 3163 shell = self.shell
3164 3164 oc = shell.displayhook
3165 3165 meta = shell.meta
3166 disp_formatter = self.shell.display_formatter
3167 ptformatter = disp_formatter.formatters['text/plain']
3166 3168 # dstore is a data store kept in the instance metadata bag to track any
3167 3169 # changes we make, so we can undo them later.
3168 3170 dstore = meta.setdefault('doctest_mode',Struct())
@@ -3170,12 +3172,13 b' Defaulting color scheme to \'NoColor\'"""'
3170 3172
3171 3173 # save a few values we'll need to recover later
3172 3174 mode = save_dstore('mode',False)
3173 save_dstore('rc_pprint',shell.pprint)
3175 save_dstore('rc_pprint',ptformatter.pprint)
3174 3176 save_dstore('xmode',shell.InteractiveTB.mode)
3175 3177 save_dstore('rc_separate_out',shell.separate_out)
3176 3178 save_dstore('rc_separate_out2',shell.separate_out2)
3177 3179 save_dstore('rc_prompts_pad_left',shell.prompts_pad_left)
3178 3180 save_dstore('rc_separate_in',shell.separate_in)
3181 save_dstore('rc_plain_text_only',disp_formatter.plain_text_only)
3179 3182
3180 3183 if mode == False:
3181 3184 # turn on
@@ -3191,7 +3194,8 b' Defaulting color scheme to \'NoColor\'"""'
3191 3194 oc.prompt1.pad_left = oc.prompt2.pad_left = \
3192 3195 oc.prompt_out.pad_left = False
3193 3196
3194 shell.pprint = False
3197 ptformatter.pprint = False
3198 disp_formatter.plain_text_only = True
3195 3199
3196 3200 shell.magic_xmode('Plain')
3197 3201 else:
@@ -3208,7 +3212,8 b' Defaulting color scheme to \'NoColor\'"""'
3208 3212 oc.prompt1.pad_left = oc.prompt2.pad_left = \
3209 3213 oc.prompt_out.pad_left = dstore.rc_prompts_pad_left
3210 3214
3211 shell.pprint = dstore.rc_pprint
3215 ptformatter.pprint = dstore.rc_pprint
3216 disp_formatter.plain_text_only = dstore.rc_plain_text_only
3212 3217
3213 3218 shell.magic_xmode(dstore.xmode)
3214 3219
@@ -441,6 +441,31 b' We have provided as magics ``%less`` to page files (aliased to ``%more``),'
441 441 most common commands you'd want to call in your subshell and that would cause
442 442 problems if invoked via ``!cmd``, but you need to be aware of this limitation.
443 443
444 Display
445 =======
446
447 The IPython console can now display objects in a variety of formats, including
448 HTML, PNG and SVG. This is accomplished using the display functions in
449 ``IPython.core.display``::
450
451 In [4]: from IPython.core.display import display, display_html
452
453 In [5]: from IPython.core.display import display_png, display_svg
454
455 Python objects can simply be passed to these functions and the appropriate
456 representations will be displayed in the console as long as the objects know
457 how to compute those representations. The easiest way of teaching objects how
458 to format themselves in various representations is to define special methods
459 such as: ``__html``, ``__svg__`` and ``__png__``. IPython's display formatters
460 can also be given custom formatter functions for various types::
461
462 In [6]: ip = get_ipython()
463
464 In [7]: html_formatter = ip.display_formatter.formatters['text/html']
465
466 In [8]: html_formatter.for_type(Foo, foo_to_html)
467
468 For further details, see ``IPython.core.formatters``.
444 469
445 470 Inline matplotlib graphics
446 471 ==========================
@@ -448,10 +473,14 b' Inline matplotlib graphics'
448 473 The IPython console is capable of displaying matplotlib figures inline, in SVG
449 474 format. If started with the ``--pylab inline`` flag, then all figures are
450 475 rendered inline automatically. If started with ``--pylab`` or ``--pylab <your
451 backend>``, then a GUI backend will be used, but the ``pastefig()`` function is
452 added to the global and ``plt`` namespaces. You can paste any figure that is
453 currently open in a window with this function; type ``pastefig?`` for
454 additional details."""
476 backend>``, then a GUI backend will be used, but IPython's ``display()`` and
477 ``getfigs()`` functions can be used to view plots inline::
478
479 In [9]: display(*getfigs()) # display all figures inline
480
481 In[10]: display(*getfigs(1,2)) # display figures 1 and 2 inline
482 """
483
455 484
456 485 quick_guide = """\
457 486 ? -> Introduction and overview of IPython's features.
@@ -352,7 +352,7 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):'
352 352 """ Handle display hook output.
353 353 """
354 354 if not self._hidden and self._is_from_this_session(msg):
355 self._append_plain_text(msg['content']['data'] + '\n')
355 self._append_plain_text(msg['content']['data']['text/plain'] + '\n')
356 356
357 357 def _handle_stream(self, msg):
358 358 """ Handle stdout, stderr, and stdin.
@@ -179,9 +179,39 b' class IPythonWidget(FrontendWidget):'
179 179 if not self._hidden and self._is_from_this_session(msg):
180 180 content = msg['content']
181 181 prompt_number = content['execution_count']
182 data = content['data']
183 if data.has_key('text/html'):
182 184 self._append_plain_text(self.output_sep)
183 185 self._append_html(self._make_out_prompt(prompt_number))
184 self._append_plain_text(content['data']+self.output_sep2)
186 html = data['text/html']
187 self._append_plain_text('\n')
188 self._append_html(html + self.output_sep2)
189 elif data.has_key('text/plain'):
190 self._append_plain_text(self.output_sep)
191 self._append_html(self._make_out_prompt(prompt_number))
192 text = data['text/plain']
193 self._append_plain_text(text + self.output_sep2)
194
195 def _handle_display_data(self, msg):
196 """ The base handler for the ``display_data`` message.
197 """
198 # For now, we don't display data from other frontends, but we
199 # eventually will as this allows all frontends to monitor the display
200 # data. But we need to figure out how to handle this in the GUI.
201 if not self._hidden and self._is_from_this_session(msg):
202 source = msg['content']['source']
203 data = msg['content']['data']
204 metadata = msg['content']['metadata']
205 # In the regular IPythonWidget, we simply print the plain text
206 # representation.
207 if data.has_key('text/html'):
208 html = data['text/html']
209 self._append_html(html)
210 elif data.has_key('text/plain'):
211 text = data['text/plain']
212 self._append_plain_text(text)
213 # This newline seems to be needed for text and html output.
214 self._append_plain_text(u'\n')
185 215
186 216 def _started_channels(self):
187 217 """ Reimplemented to make a history request.
@@ -444,7 +474,7 b' class IPythonWidget(FrontendWidget):'
444 474 else:
445 475 self._page(item['text'], html=False)
446 476
447 #------ Trait change handlers ---------------------------------------------
477 #------ Trait change handlers --------------------------------------------
448 478
449 479 def _style_sheet_changed(self):
450 480 """ Set the style sheets of the underlying widgets.
@@ -1,6 +1,7 b''
1 1 # System library imports
2 2 import os
3 3 import re
4 from base64 import decodestring
4 5 from PyQt4 import QtCore, QtGui
5 6
6 7 # Local imports
@@ -58,15 +59,85 b' class RichIPythonWidget(IPythonWidget):'
58 59 return menu
59 60
60 61 #---------------------------------------------------------------------------
62 # 'BaseFrontendMixin' abstract interface
63 #---------------------------------------------------------------------------
64
65 def _handle_pyout(self, msg):
66 """ Overridden to handle rich data types, like SVG.
67 """
68 if not self._hidden and self._is_from_this_session(msg):
69 content = msg['content']
70 prompt_number = content['execution_count']
71 data = content['data']
72 if data.has_key('image/svg+xml'):
73 self._append_plain_text(self.output_sep)
74 self._append_html(self._make_out_prompt(prompt_number))
75 # TODO: try/except this call.
76 self._append_svg(data['image/svg+xml'])
77 self._append_html(self.output_sep2)
78 elif data.has_key('image/png'):
79 self._append_plain_text(self.output_sep)
80 self._append_html(self._make_out_prompt(prompt_number))
81 # This helps the output to look nice.
82 self._append_plain_text('\n')
83 # TODO: try/except these calls
84 png = decodestring(data['image/png'])
85 self._append_png(png)
86 self._append_html(self.output_sep2)
87 else:
88 # Default back to the plain text representation.
89 return super(RichIPythonWidget, self)._handle_pyout(msg)
90
91 def _handle_display_data(self, msg):
92 """ Overridden to handle rich data types, like SVG.
93 """
94 if not self._hidden and self._is_from_this_session(msg):
95 source = msg['content']['source']
96 data = msg['content']['data']
97 metadata = msg['content']['metadata']
98 # Try to use the svg or html representations.
99 # FIXME: Is this the right ordering of things to try?
100 if data.has_key('image/svg+xml'):
101 svg = data['image/svg+xml']
102 # TODO: try/except this call.
103 self._append_svg(svg)
104 elif data.has_key('image/png'):
105 # TODO: try/except these calls
106 # PNG data is base64 encoded as it passes over the network
107 # in a JSON structure so we decode it.
108 png = decodestring(data['image/png'])
109 self._append_png(png)
110 else:
111 # Default back to the plain text representation.
112 return super(RichIPythonWidget, self)._handle_display_data(msg)
113
114 #---------------------------------------------------------------------------
61 115 # 'FrontendWidget' protected interface
62 116 #---------------------------------------------------------------------------
63 117
64 118 def _process_execute_payload(self, item):
65 119 """ Reimplemented to handle matplotlib plot payloads.
66 120 """
121 # TODO: remove this as all plot data is coming back through the
122 # display_data message type.
67 123 if item['source'] == self._payload_source_plot:
68 124 if item['format'] == 'svg':
69 125 svg = item['data']
126 self._append_svg(svg)
127 return True
128 else:
129 # Add other plot formats here!
130 return False
131 else:
132 return super(RichIPythonWidget, self)._process_execute_payload(item)
133
134 #---------------------------------------------------------------------------
135 # 'RichIPythonWidget' protected interface
136 #---------------------------------------------------------------------------
137
138 def _append_svg(self, svg):
139 """ Append raw svg data to the widget.
140 """
70 141 try:
71 142 image = svg_to_image(svg)
72 143 except ValueError:
@@ -79,16 +150,21 b' class RichIPythonWidget(IPythonWidget):'
79 150 cursor.insertBlock()
80 151 cursor.insertImage(format)
81 152 cursor.insertBlock()
82 return True
83 else:
84 # Add other plot formats here!
85 return False
86 else:
87 return super(RichIPythonWidget, self)._process_execute_payload(item)
88 153
89 #---------------------------------------------------------------------------
90 # 'RichIPythonWidget' protected interface
91 #---------------------------------------------------------------------------
154 def _append_png(self, png):
155 """ Append raw svg data to the widget.
156 """
157 try:
158 image = QtGui.QImage()
159 image.loadFromData(png, 'PNG')
160 except ValueError:
161 self._append_plain_text('Received invalid plot data.')
162 else:
163 format = self._add_image(image)
164 cursor = self._get_end_cursor()
165 cursor.insertBlock()
166 cursor.insertImage(format)
167 cursor.insertBlock()
92 168
93 169 def _add_image(self, image):
94 170 """ Adds the specified QImage to the document and returns a
@@ -101,6 +101,9 b' class QtSubSocketChannel(SocketChannelQObject, SubSocketChannel):'
101 101 # Emitted when a message of type 'pyerr' is received.
102 102 pyerr_received = QtCore.pyqtSignal(object)
103 103
104 # Emitted when a message of type 'display_data' is received
105 display_data_received = QtCore.pyqtSignal(object)
106
104 107 # Emitted when a crash report message is received from the kernel's
105 108 # last-resort sys.excepthook.
106 109 crash_received = QtCore.pyqtSignal(object)
@@ -117,7 +120,6 b' class QtSubSocketChannel(SocketChannelQObject, SubSocketChannel):'
117 120 """
118 121 # Emit the generic signal.
119 122 self.message_received.emit(msg)
120
121 123 # Emit signals for specialized message types.
122 124 msg_type = msg['msg_type']
123 125 signal = getattr(self, msg_type + '_received', None)
@@ -188,10 +188,10 b' class IPAppConfigLoader(BaseAppConfigLoader):'
188 188 action='store_false', dest='InteractiveShell.pdb',
189 189 help="Disable auto calling the pdb debugger after every exception.")
190 190 paa('--pprint',
191 action='store_true', dest='InteractiveShell.pprint',
191 action='store_true', dest='PlainTextFormatter.pprint',
192 192 help="Enable auto pretty printing of results.")
193 193 paa('--no-pprint',
194 action='store_false', dest='InteractiveShell.pprint',
194 action='store_false', dest='PlainTextFormatter.pprint',
195 195 help="Disable auto auto pretty printing of results.")
196 196 paa('--prompt-in1','-pi1',
197 197 type=str, dest='InteractiveShell.prompt_in1',
@@ -443,7 +443,7 b' class IPythonApp(Application):'
443 443 if hasattr(config.Global, 'classic'):
444 444 if config.Global.classic:
445 445 config.InteractiveShell.cache_size = 0
446 config.InteractiveShell.pprint = 0
446 config.PlainTextFormatter.pprint = 0
447 447 config.InteractiveShell.prompt_in1 = '>>> '
448 448 config.InteractiveShell.prompt_in2 = '... '
449 449 config.InteractiveShell.prompt_out = ''
@@ -88,7 +88,7 b' def _result_list_printer(obj, p, cycle):'
88 88 # ResultList is a list subclass and will use the default pretty printer.
89 89 # This overrides that to use the __repr__ of ResultList.
90 90 ip = get_ipython()
91 ip.displayhook.default_formatter.for_type_by_name(
91 ip.display_formatter.formatters['text/plain'].for_type_by_name(
92 92 'IPython.kernel.multiengineclient', 'ResultList', _result_list_printer
93 93 )
94 94
@@ -19,6 +19,8 b' Authors'
19 19 # Imports
20 20 #-----------------------------------------------------------------------------
21 21
22 from cStringIO import StringIO
23
22 24 from IPython.utils.decorators import flag_calls
23 25
24 26 # If user specifies a GUI, that dictates the backend, otherwise we read the
@@ -31,7 +33,107 b" backends = {'tk': 'TkAgg',"
31 33 'inline' : 'module://IPython.zmq.pylab.backend_inline'}
32 34
33 35 #-----------------------------------------------------------------------------
34 # Main classes and functions
36 # Matplotlib utilities
37 #-----------------------------------------------------------------------------
38
39
40 def getfigs(*fig_nums):
41 """Get a list of matplotlib figures by figure numbers.
42
43 If no arguments are given, all available figures are returned. If the
44 argument list contains references to invalid figures, a warning is printed
45 but the function continues pasting further figures.
46
47 Parameters
48 ----------
49 figs : tuple
50 A tuple of ints giving the figure numbers of the figures to return.
51 """
52 from matplotlib._pylab_helpers import Gcf
53 if not fig_nums:
54 fig_managers = Gcf.get_all_fig_managers()
55 return [fm.canvas.figure for fm in fig_managers]
56 else:
57 figs = []
58 for num in fig_nums:
59 f = Gcf.figs.get(num)
60 if f is None:
61 print('Warning: figure %s not available.' % num)
62 figs.append(f.canvas.figure)
63 return figs
64
65
66 def figsize(sizex, sizey):
67 """Set the default figure size to be [sizex, sizey].
68
69 This is just an easy to remember, convenience wrapper that sets::
70
71 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
72 """
73 import matplotlib
74 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
75
76
77 def figure_to_svg(fig):
78 """Convert a figure to svg for inline display."""
79 fc = fig.get_facecolor()
80 ec = fig.get_edgecolor()
81 fig.set_facecolor('white')
82 fig.set_edgecolor('white')
83 try:
84 string_io = StringIO()
85 fig.canvas.print_figure(string_io, format='svg')
86 svg = string_io.getvalue()
87 finally:
88 fig.set_facecolor(fc)
89 fig.set_edgecolor(ec)
90 return svg
91
92
93 # We need a little factory function here to create the closure where
94 # safe_execfile can live.
95 def mpl_runner(safe_execfile):
96 """Factory to return a matplotlib-enabled runner for %run.
97
98 Parameters
99 ----------
100 safe_execfile : function
101 This must be a function with the same interface as the
102 :meth:`safe_execfile` method of IPython.
103
104 Returns
105 -------
106 A function suitable for use as the ``runner`` argument of the %run magic
107 function.
108 """
109
110 def mpl_execfile(fname,*where,**kw):
111 """matplotlib-aware wrapper around safe_execfile.
112
113 Its interface is identical to that of the :func:`execfile` builtin.
114
115 This is ultimately a call to execfile(), but wrapped in safeties to
116 properly handle interactive rendering."""
117
118 import matplotlib
119 import matplotlib.pylab as pylab
120
121 #print '*** Matplotlib runner ***' # dbg
122 # turn off rendering until end of script
123 is_interactive = matplotlib.rcParams['interactive']
124 matplotlib.interactive(False)
125 safe_execfile(fname,*where,**kw)
126 matplotlib.interactive(is_interactive)
127 # make rendering call now, if the user tried to do it
128 if pylab.draw_if_interactive.called:
129 pylab.draw()
130 pylab.draw_if_interactive.called = False
131
132 return mpl_execfile
133
134
135 #-----------------------------------------------------------------------------
136 # Code for initializing matplotlib and importing pylab
35 137 #-----------------------------------------------------------------------------
36 138
37 139
@@ -111,7 +213,7 b' def import_pylab(user_ns, backend, import_all=True, shell=None):'
111 213 # function that will pick up the results for display. This can only be
112 214 # done with access to the real shell object.
113 215 if backend == backends['inline']:
114 from IPython.zmq.pylab.backend_inline import flush_svg, figsize
216 from IPython.zmq.pylab.backend_inline import flush_svg
115 217 from matplotlib import pyplot
116 218 shell.register_post_execute(flush_svg)
117 219 # The typical default figure size is too large for inline use. We
@@ -120,11 +222,22 b' def import_pylab(user_ns, backend, import_all=True, shell=None):'
120 222 # Add 'figsize' to pyplot and to the user's namespace
121 223 user_ns['figsize'] = pyplot.figsize = figsize
122 224 shell.user_ns_hidden['figsize'] = figsize
123 else:
124 from IPython.zmq.pylab.backend_inline import pastefig
125 from matplotlib import pyplot
126 # Add 'paste' to pyplot and to the user's namespace
127 user_ns['pastefig'] = pyplot.pastefig = pastefig
225
226 # The old pastefig function has been replaced by display
227 # Always add this svg formatter so display works.
228 from IPython.zmq.pylab.backend_inline import figure_to_svg
229 from IPython.core.display import display, display_svg
230 svg_formatter = shell.display_formatter.formatters['image/svg+xml']
231 svg_formatter.for_type_by_name(
232 'matplotlib.figure','Figure',figure_to_svg
233 )
234 # Add display and display_png to the user's namespace
235 user_ns['display'] = display
236 shell.user_ns_hidden['display'] = display
237 user_ns['display_svg'] = display_svg
238 shell.user_ns_hidden['display_svg'] = display_svg
239 user_ns['getfigs'] = getfigs
240 shell.user_ns_hidden['getfigs'] = getfigs
128 241
129 242 if import_all:
130 243 s = ("from matplotlib.pylab import *\n"
@@ -165,44 +278,3 b' For more information, type \'help(pylab)\'.""" % backend'
165 278
166 279 return gui
167 280
168 # We need a little factory function here to create the closure where
169 # safe_execfile can live.
170 def mpl_runner(safe_execfile):
171 """Factory to return a matplotlib-enabled runner for %run.
172
173 Parameters
174 ----------
175 safe_execfile : function
176 This must be a function with the same interface as the
177 :meth:`safe_execfile` method of IPython.
178
179 Returns
180 -------
181 A function suitable for use as the ``runner`` argument of the %run magic
182 function.
183 """
184
185 def mpl_execfile(fname,*where,**kw):
186 """matplotlib-aware wrapper around safe_execfile.
187
188 Its interface is identical to that of the :func:`execfile` builtin.
189
190 This is ultimately a call to execfile(), but wrapped in safeties to
191 properly handle interactive rendering."""
192
193 import matplotlib
194 import matplotlib.pylab as pylab
195
196 #print '*** Matplotlib runner ***' # dbg
197 # turn off rendering until end of script
198 is_interactive = matplotlib.rcParams['interactive']
199 matplotlib.interactive(False)
200 safe_execfile(fname,*where,**kw)
201 matplotlib.interactive(is_interactive)
202 # make rendering call now, if the user tried to do it
203 if pylab.draw_if_interactive.called:
204 pylab.draw()
205 pylab.draw_if_interactive.called = False
206
207 return mpl_execfile
208
@@ -2,15 +2,6 b''
2 2 """Generic functions for extending IPython.
3 3
4 4 See http://cheeseshop.python.org/pypi/simplegeneric.
5
6 Here is an example from IPython.utils.text::
7
8 def print_lsstring(arg):
9 "Prettier (non-repr-like) and more informative printer for LSString"
10 print "LSString (.p, .n, .l, .s available). Value:"
11 print arg
12
13 print_lsstring = result_display.when_type(LSString)(print_lsstring)
14 5 """
15 6
16 7 #-----------------------------------------------------------------------------
@@ -388,8 +388,6 b' class Struct(dict):'
388 388 inv_conflict_solve_user[func] = inv_conflict_solve_user[name]
389 389 del inv_conflict_solve_user[name]
390 390 conflict_solve.update(self.__dict_invert(inv_conflict_solve_user))
391 #print 'merge. conflict_solve: '; pprint(conflict_solve) # dbg
392 #print '*'*50,'in merger. conflict_solver:'; pprint(conflict_solve)
393 391 for key in data_dict:
394 392 if key not in self:
395 393 self[key] = data_dict[key]
@@ -91,6 +91,8 b' class Kernel(Configurable):'
91 91 self.shell = ZMQInteractiveShell.instance()
92 92 self.shell.displayhook.session = self.session
93 93 self.shell.displayhook.pub_socket = self.pub_socket
94 self.shell.display_pub.session = self.session
95 self.shell.display_pub.pub_socket = self.pub_socket
94 96
95 97 # TMP - hack while developing
96 98 self.shell._reply_content = None
@@ -194,6 +196,7 b' class Kernel(Configurable):'
194 196
195 197 # Set the parent message of the display hook and out streams.
196 198 shell.displayhook.set_parent(parent)
199 shell.display_pub.set_parent(parent)
197 200 sys.stdout.set_parent(parent)
198 201 sys.stderr.set_parent(parent)
199 202
@@ -6,99 +6,39 b''
6 6 from __future__ import print_function
7 7
8 8 # Standard library imports
9 from cStringIO import StringIO
10 9
11 # System library imports.
12 10 import matplotlib
13 11 from matplotlib.backends.backend_svg import new_figure_manager
14 12 from matplotlib._pylab_helpers import Gcf
15 13
16 14 # Local imports.
17 from backend_payload import add_plot_payload
15 from IPython.core.displaypub import publish_display_data
16 from IPython.lib.pylabtools import figure_to_svg
18 17
19 18 #-----------------------------------------------------------------------------
20 19 # Functions
21 20 #-----------------------------------------------------------------------------
22 21
23 def show(close=True):
22 def show(close=False):
24 23 """Show all figures as SVG payloads sent to the IPython clients.
25 24
26 25 Parameters
27 26 ----------
28 27 close : bool, optional
29 28 If true, a ``plt.close('all')`` call is automatically issued after
30 sending all the SVG figures.
29 sending all the SVG figures. If this is set, the figures will entirely
30 removed from the internal list of figures.
31 31 """
32 32 for figure_manager in Gcf.get_all_fig_managers():
33 send_svg_canvas(figure_manager.canvas)
33 send_svg_figure(figure_manager.canvas.figure)
34 34 if close:
35 35 matplotlib.pyplot.close('all')
36 36
37
37 38 # This flag will be reset by draw_if_interactive when called
38 39 show._draw_called = False
39 40
40 41
41 def figsize(sizex, sizey):
42 """Set the default figure size to be [sizex, sizey].
43
44 This is just an easy to remember, convenience wrapper that sets::
45
46 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
47 """
48 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
49
50
51 def pastefig(*figs):
52 """Paste one or more figures into the console workspace.
53
54 If no arguments are given, all available figures are pasted. If the
55 argument list contains references to invalid figures, a warning is printed
56 but the function continues pasting further figures.
57
58 Parameters
59 ----------
60 figs : tuple
61 A tuple that can contain any mixture of integers and figure objects.
62 """
63 if not figs:
64 show(close=False)
65 else:
66 fig_managers = Gcf.get_all_fig_managers()
67 fig_index = dict( [(fm.canvas.figure, fm.canvas) for fm in fig_managers]
68 + [ (fm.canvas.figure.number, fm.canvas) for fm in fig_managers] )
69
70 for fig in figs:
71 canvas = fig_index.get(fig)
72 if canvas is None:
73 print('Warning: figure %s not available.' % fig)
74 else:
75 send_svg_canvas(canvas)
76
77
78 def send_svg_canvas(canvas):
79 """Draw the current canvas and send it as an SVG payload.
80 """
81 # Set the background to white instead so it looks good on black. We store
82 # the current values to restore them at the end.
83 fc = canvas.figure.get_facecolor()
84 ec = canvas.figure.get_edgecolor()
85 canvas.figure.set_facecolor('white')
86 canvas.figure.set_edgecolor('white')
87 try:
88 add_plot_payload('svg', svg_from_canvas(canvas))
89 finally:
90 canvas.figure.set_facecolor(fc)
91 canvas.figure.set_edgecolor(ec)
92
93
94 def svg_from_canvas(canvas):
95 """ Return a string containing the SVG representation of a FigureCanvasSvg.
96 """
97 string_io = StringIO()
98 canvas.print_figure(string_io, format='svg')
99 return string_io.getvalue()
100
101
102 42 def draw_if_interactive():
103 43 """
104 44 Is called after every pylab drawing command
@@ -115,5 +55,19 b' def flush_svg():'
115 55 prior code execution, there had been any calls to draw_if_interactive.
116 56 """
117 57 if show._draw_called:
118 show(close=True)
58 # Show is called with the default close=False here, otherwise, the
59 # Figure will be closed and not available for future plotting.
60 show()
119 61 show._draw_called = False
62
63
64 def send_svg_figure(fig):
65 """Draw the current figure and send it as an SVG payload.
66 """
67 svg = figure_to_svg(fig)
68 publish_display_data(
69 'IPython.zmq.pylab.backend_inline.send_svg_figure',
70 'Matplotlib Plot',
71 {'image/svg+xml' : svg}
72 )
73
@@ -26,6 +26,7 b' from IPython.core.interactiveshell import ('
26 26 )
27 27 from IPython.core import page
28 28 from IPython.core.displayhook import DisplayHook
29 from IPython.core.displaypub import DisplayPublisher
29 30 from IPython.core.macro import Macro
30 31 from IPython.core.payloadpage import install_payload_page
31 32 from IPython.utils import io
@@ -48,6 +49,7 b' install_payload_page()'
48 49 #-----------------------------------------------------------------------------
49 50
50 51 class ZMQDisplayHook(DisplayHook):
52 """A displayhook subclass that publishes data using ZeroMQ."""
51 53
52 54 session = Instance(Session)
53 55 pub_socket = Instance('zmq.Socket')
@@ -65,9 +67,8 b' class ZMQDisplayHook(DisplayHook):'
65 67 if self.do_full_cache:
66 68 self.msg['content']['execution_count'] = self.prompt_count
67 69
68 def write_result_repr(self, result_repr, extra_formats):
69 self.msg['content']['data'] = result_repr
70 self.msg['content']['extra_formats'] = extra_formats
70 def write_format_data(self, format_dict):
71 self.msg['content']['data'] = format_dict
71 72
72 73 def finish_displayhook(self):
73 74 """Finish up all displayhook activities."""
@@ -75,10 +76,37 b' class ZMQDisplayHook(DisplayHook):'
75 76 self.msg = None
76 77
77 78
79 class ZMQDisplayPublisher(DisplayPublisher):
80 """A display publisher that publishes data using a ZeroMQ PUB socket."""
81
82 session = Instance(Session)
83 pub_socket = Instance('zmq.Socket')
84 parent_header = Dict({})
85
86 def set_parent(self, parent):
87 """Set the parent for outbound messages."""
88 self.parent_header = extract_header(parent)
89
90 def publish(self, source, data, metadata=None):
91 if metadata is None:
92 metadata = {}
93 self._validate_data(source, data, metadata)
94 content = {}
95 content['source'] = source
96 content['data'] = data
97 content['metadata'] = metadata
98 self.session.send(
99 self.pub_socket, u'display_data', content,
100 parent=self.parent_header
101 )
102
103
78 104 class ZMQInteractiveShell(InteractiveShell):
79 105 """A subclass of InteractiveShell for ZMQ."""
80 106
81 107 displayhook_class = Type(ZMQDisplayHook)
108 display_pub_class = Type(ZMQDisplayPublisher)
109
82 110 keepkernel_on_exit = None
83 111
84 112 def init_environment(self):
@@ -178,6 +206,8 b' class ZMQInteractiveShell(InteractiveShell):'
178 206
179 207 # Shorthands
180 208 shell = self.shell
209 disp_formatter = self.shell.display_formatter
210 ptformatter = disp_formatter.formatters['text/plain']
181 211 # dstore is a data store kept in the instance metadata bag to track any
182 212 # changes we make, so we can undo them later.
183 213 dstore = shell.meta.setdefault('doctest_mode', Struct())
@@ -185,16 +215,19 b' class ZMQInteractiveShell(InteractiveShell):'
185 215
186 216 # save a few values we'll need to recover later
187 217 mode = save_dstore('mode', False)
188 save_dstore('rc_pprint', shell.pprint)
218 save_dstore('rc_pprint', ptformatter.pprint)
219 save_dstore('rc_plain_text_only',disp_formatter.plain_text_only)
189 220 save_dstore('xmode', shell.InteractiveTB.mode)
190 221
191 222 if mode == False:
192 223 # turn on
193 shell.pprint = False
224 ptformatter.pprint = False
225 disp_formatter.plain_text_only = True
194 226 shell.magic_xmode('Plain')
195 227 else:
196 228 # turn off
197 shell.pprint = dstore.rc_pprint
229 ptformatter.pprint = dstore.rc_pprint
230 disp_formatter.plain_text_only = dstore.rc_plain_text_only
198 231 shell.magic_xmode(dstore.xmode)
199 232
200 233 # Store new mode and inform on console
@@ -700,6 +700,39 b" socket with the names 'stdin' and 'stdin_reply'. This will allow other clients"
700 700 to monitor/display kernel interactions and possibly replay them to their user
701 701 or otherwise expose them.
702 702
703 Display Data
704 ------------
705
706 This type of message is used to bring back data that should be diplayed (text,
707 html, svg, etc.) in the frontends. This data is published to all frontends.
708 Each message can have multiple representations of the data; it is up to the
709 frontend to decide which to use and how. A single message should contain all
710 possible representations of the same information. Each representation should
711 be a JSON'able data structure, and should be a valid MIME type.
712
713 Some questions remain about this design:
714
715 * Do we use this message type for pyout/displayhook? Probably not, because
716 the displayhook also has to handle the Out prompt display. On the other hand
717 we could put that information into the metadata secion.
718
719 Message type: ``display_data``::
720
721 content = {
722
723 # Who create the data
724 'source' : str,
725
726 # The data dict contains key/value pairs, where the kids are MIME
727 # types and the values are the raw data of the representation in that
728 # format. The data dict must minimally contain the ``text/plain``
729 # MIME type which is used as a backup representation.
730 'data' : dict,
731
732 # Any metadata that describes the data
733 'metadata' : dict
734 }
735
703 736 Python inputs
704 737 -------------
705 738
@@ -708,8 +741,7 b' These messages are the re-broadcast of the ``execute_request``.'
708 741 Message type: ``pyin``::
709 742
710 743 content = {
711 # Source code to be executed, one or more lines
712 'code' : str
744 'code' : str # Source code to be executed, one or more lines
713 745 }
714 746
715 747 Python outputs
@@ -740,20 +772,18 b' any JSON object and depends on the format. It is often, but not always a string.'
740 772 Message type: ``pyout``::
741 773
742 774 content = {
743 # The data is typically the repr() of the object. It should be displayed
744 # as monospaced text.
745 'data' : str,
746 775
747 776 # The counter for this execution is also provided so that clients can
748 777 # display it, since IPython automatically creates variables called _N
749 778 # (for prompt N).
750 779 'execution_count' : int,
751 780
752 # Any extra formats.
753 # The tuples are of the form (ID, type, data).
754 'extra_formats' : [
755 [str, str, object]
756 ]
781 # The data dict contains key/value pairs, where the kids are MIME
782 # types and the values are the raw data of the representation in that
783 # format. The data dict must minimally contain the ``text/plain``
784 # MIME type which is used as a backup representation.
785 'data' : dict,
786
757 787 }
758 788
759 789 Python errors
@@ -214,12 +214,6 b' def find_data_files():'
214 214 (pjoin(docdirbase, 'extensions'), igridhelpfiles),
215 215 ] + manual_files + example_files
216 216
217 ## import pprint # dbg
218 ## print('*'*80)
219 ## print('data files')
220 ## pprint.pprint(data_files)
221 ## print('*'*80)
222
223 217 return data_files
224 218
225 219
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now