1
"""
2
Pane class which render various markup languages including HTML,
3
Markdown, and also regular strings.
4
"""
5 7
from __future__ import absolute_import, division, unicode_literals
6

7 7
import json
8 7
import textwrap
9

10 7
from six import string_types
11

12 7
import param
13

14 7
from ..models import HTML as _BkHTML, JSON as _BkJSON
15 7
from ..util import escape
16 7
from ..viewable import Layoutable
17 7
from .base import PaneBase
18

19

20 7
class DivPaneBase(PaneBase):
21
    """
22
    Baseclass for Panes which render HTML inside a Bokeh Div.
23
    See the documentation for Bokeh Div for more detail about
24
    the supported options like style and sizing_mode.
25
    """
26

27 7
    style = param.Dict(default=None, doc="""
28
        Dictionary of CSS property:value pairs to apply to this Div.""")
29

30 7
    _bokeh_model = _BkHTML
31

32 7
    _rename = {'object': 'text'}
33

34 7
    _updates = True
35

36 7
    __abstract = True
37

38 7
    def _get_properties(self):
39 7
        return {p : getattr(self, p) for p in list(Layoutable.param) + ['style']
40
                if getattr(self, p) is not None}
41

42 7
    def _get_model(self, doc, root=None, parent=None, comm=None):
43 7
        model = self._bokeh_model(**self._get_properties())
44 7
        if root is None:
45 7
            root = model
46 7
        self._models[root.ref['id']] = (model, parent)
47 7
        return model
48

49 7
    def _update(self, ref=None, model=None):
50 7
        model.update(**self._get_properties())
51

52

53 7
class HTML(DivPaneBase):
54
    """
55
    HTML panes wrap HTML text in a Panel HTML model. The
56
    provided object can either be a text string, or an object that
57
    has a `_repr_html_` method that can be called to get the HTML
58
    text string.  The height and width can optionally be specified, to
59
    allow room for whatever is being wrapped.
60
    """
61

62
    # Priority is dependent on the data type
63 7
    priority = None
64

65 7
    @classmethod
66 2
    def applies(cls, obj):
67 7
        module, name = getattr(obj, '__module__', ''), type(obj).__name__
68 7
        if ((any(m in module for m in ('pandas', 'dask')) and
69
            name in ('DataFrame', 'Series')) or hasattr(obj, '_repr_html_')):
70 7
            return 0.2
71 7
        elif isinstance(obj, string_types):
72 7
            return None
73
        else:
74 7
            return False
75

76 7
    def _get_properties(self):
77 7
        properties = super(HTML, self)._get_properties()
78 7
        text = '' if self.object is None else self.object
79 7
        if hasattr(text, '_repr_html_'):
80 0
            text = text._repr_html_()
81 7
        return dict(properties, text=escape(text))
82

83

84 7
class DataFrame(HTML):
85
    """
86
    DataFrame renders pandas, dask and streamz DataFrame types using
87
    their custom HTML repr. In the case of a streamz DataFrame the
88
    rendered data will update periodically.
89

90
    """
91

92 7
    bold_rows = param.Boolean(default=True, doc="""
93
        Make the row labels bold in the output.""")
94

95 7
    border = param.Integer(default=0, doc="""
96
        A ``border=border`` attribute is included in the opening
97
        `<table>` tag.""")
98

99 7
    classes = param.List(default=['panel-df'], doc="""
100
        CSS class(es) to apply to the resulting html table.""")
101

102 7
    col_space = param.ClassSelector(default=None, class_=(str, int), doc="""
103
        The minimum width of each column in CSS length units. An int
104
        is assumed to be px units.""")
105

106 7
    decimal = param.String(default='.', doc="""
107
        Character recognized as decimal separator, e.g. ',' in Europe.""")
108

109 7
    float_format = param.Callable(default=None, doc="""
110
        Formatter function to apply to columns' elements if they are
111
        floats. The result of this function must be a unicode string.""")
112

113 7
    formatters = param.ClassSelector(default=None, class_=(dict, list), doc="""
114
        Formatter functions to apply to columns' elements by position
115
        or name. The result of each function must be a unicode string.""")
116

117 7
    header = param.Boolean(default=True, doc="""
118
        Whether to print column labels.""")
119

120 7
    index = param.Boolean(default=True, doc="""
121
        Whether to print index (row) labels.""")
122

123 7
    index_names = param.Boolean(default=True, doc="""
124
        Prints the names of the indexes.""")
125

126 7
    justify = param.ObjectSelector(default=None, allow_None=True, objects=[
127
        'left', 'right', 'center', 'justify', 'justify-all', 'start',
128
        'end', 'inherit', 'match-parent', 'initial', 'unset'], doc="""
129
        How to justify the column labels.""")
130

131 7
    max_rows = param.Integer(default=None, doc="""
132
        Maximum number of rows to display.""")
133

134 7
    max_cols = param.Integer(default=None, doc="""
135
        Maximum number of columns to display.""")
136

137 7
    na_rep = param.String(default='NaN', doc="""
138
        String representation of NAN to use.""")
139

140 7
    render_links = param.Boolean(default=False, doc="""
141
        Convert URLs to HTML links.""")
142

143 7
    show_dimensions = param.Boolean(default=False, doc="""
144
        Display DataFrame dimensions (number of rows by number of
145
        columns).""")
146

147 7
    sparsify = param.Boolean(default=True, doc="""
148
        Set to False for a DataFrame with a hierarchical index to
149
        print every multi-index key at each row.""")
150

151 7
    _object = param.Parameter(default=None, doc="""Hidden parameter.""")
152

153 7
    _dask_params = ['max_rows']
154

155 7
    _rerender_params = [
156
        'object', '_object', 'bold_rows', 'border', 'classes',
157
        'col_space', 'decimal', 'float_format', 'formatters',
158
        'header', 'index', 'index_names', 'justify', 'max_rows',
159
        'max_cols', 'na_rep', 'render_links', 'show_dimensions',
160
        'sparsify', 'sizing_mode'
161
    ]
162

163 7
    def __init__(self, object=None, **params):
164 7
        super(DataFrame, self).__init__(object, **params)
165 7
        self._stream = None
166 7
        self._setup_stream()
167

168 7
    @classmethod
169 2
    def applies(cls, obj):
170 7
        module = getattr(obj, '__module__', '')
171 7
        name = type(obj).__name__
172 7
        if (any(m in module for m in ('pandas', 'dask', 'streamz')) and
173
            name in ('DataFrame', 'Series', 'Random', 'DataFrames', 'Seriess')):
174 7
            return 0.3
175
        else:
176 7
            return False
177

178 7
    def _set_object(self, object):
179 0
        self._object = object
180

181 7
    @param.depends('object', watch=True)
182 2
    def _setup_stream(self):
183 7
        if not self._models or not hasattr(self.object, 'stream'):
184 7
            return
185 7
        elif self._stream:
186 7
            self._stream.destroy()
187 7
            self._stream = None
188 7
        self._stream = self.object.stream.latest().rate_limit(0.5).gather()
189 7
        self._stream.sink(self._set_object)
190

191 7
    def _get_model(self, doc, root=None, parent=None, comm=None):
192 7
        model = super(DataFrame, self)._get_model(doc, root, parent, comm)
193 7
        self._setup_stream()
194 7
        return model
195

196 7
    def _cleanup(self, model):
197 7
        super(DataFrame, self)._cleanup(model)
198 7
        if not self._models and self._stream:
199 7
            self._stream.destroy()
200 7
            self._stream = None
201

202 7
    def _get_properties(self):
203 7
        properties = DivPaneBase._get_properties(self)
204 7
        if self._stream:
205 7
            df = self._object
206
        else:
207 7
            df = self.object
208 7
        if hasattr(df, 'to_frame'):
209 7
            df = df.to_frame()
210

211 7
        module = getattr(df, '__module__', '')
212 7
        if hasattr(df, 'to_html'):
213 7
            if 'dask' in module:
214 0
                html = df.to_html(max_rows=self.max_rows).replace('border="1"', '')
215
            else:
216 7
                kwargs = {p: getattr(self, p) for p in self._rerender_params
217
                          if p not in DivPaneBase.param and p != '_object'}
218 7
                html = df.to_html(**kwargs)
219
        else:
220 7
            html = ''
221 7
        return dict(properties, text=escape(html))
222

223

224 7
class Str(DivPaneBase):
225
    """
226
    A Str pane renders any object for which `str()` can be called,
227
    escaping any HTML markup and then wrapping the resulting string in
228
    a bokeh Div model.  Set to a low priority because generally one
229
    will want a better representation, but allows arbitrary objects to
230
    be used as a Pane (numbers, arrays, objects, etc.).
231
    """
232

233 7
    priority = 0
234

235 7
    _target_transforms = {'object': """JSON.stringify(value).replace(/,/g, ", ").replace(/:/g, ": ")"""}
236

237 7
    _bokeh_model = _BkHTML
238

239 7
    @classmethod
240 2
    def applies(cls, obj):
241 7
        return True
242

243 7
    def _get_properties(self):
244 7
        properties = super(Str, self)._get_properties()
245 7
        if self.object is None:
246 7
            text = ''
247
        else:
248 7
            text = '<pre>'+str(self.object)+'</pre>'
249 7
        return dict(properties, text=escape(text))
250

251

252 7
class Markdown(DivPaneBase):
253
    """
254
    A Markdown pane renders the markdown markup language to HTML and
255
    displays it inside a bokeh Div model. It has no explicit
256
    priority since it cannot be easily be distinguished from a
257
    standard string, therefore it has to be invoked explicitly.
258
    """
259

260 7
    dedent = param.Boolean(default=True, doc="""
261
        Whether to dedent common whitespace across all lines.""")
262

263 7
    extensions = param.List(default=[
264
        "extra", "smarty", "codehilite"], doc="""
265
        Markdown extension to apply when transforming markup.""")
266

267
    # Priority depends on the data type
268 7
    priority = None
269

270 7
    _target_transforms = {'object': None}
271

272 7
    _rerender_params = ['object', 'dedent', 'extensions', 'css_classes']
273

274 7
    @classmethod
275 2
    def applies(cls, obj):
276 7
        if hasattr(obj, '_repr_markdown_'):
277 0
            return 0.3
278 7
        elif isinstance(obj, string_types):
279 7
            return 0.1
280
        else:
281 7
            return False
282

283 7
    def _get_properties(self):
284 7
        import markdown
285 7
        data = self.object
286 7
        if data is None:
287 7
            data = ''
288 7
        elif not isinstance(data, string_types):
289 0
            data = data._repr_markdown_()
290 7
        if self.dedent:
291 7
            data = textwrap.dedent(data)
292 7
        properties = super(Markdown, self)._get_properties()
293 7
        properties['style'] = properties.get('style', {})
294 7
        css_classes = properties.pop('css_classes', []) + ['markdown']
295 7
        html = markdown.markdown(data, extensions=self.extensions,
296
                                 output_format='html5')
297 7
        return dict(properties, text=escape(html), css_classes=css_classes)
298

299

300

301 7
class JSON(DivPaneBase):
302

303 7
    depth = param.Integer(default=1, bounds=(-1, None), doc="""
304
        Depth to which the JSON tree will be expanded on initialization.""")
305

306 7
    encoder = param.ClassSelector(class_=json.JSONEncoder, is_instance=False, doc="""
307
        Custom JSONEncoder class used to serialize objects to JSON string.""")
308

309 7
    hover_preview = param.Boolean(default=False, doc="""
310
        Whether to display a hover preview for collapsed nodes.""")
311

312 7
    margin = param.Parameter(default=(5, 20, 5, 5), doc="""
313
        Allows to create additional space around the component. May
314
        be specified as a two-tuple of the form (vertical, horizontal)
315
        or a four-tuple (top, right, bottom, left).""")
316

317 7
    theme = param.ObjectSelector(default="dark", objects=["light", "dark"], doc="""
318
        Whether the JSON tree view is expanded by default.""")
319

320 7
    priority = None
321

322 7
    _applies_kw = True
323 7
    _bokeh_model = _BkJSON
324 7
    _rename = {"name": None, "object": "text", "encoder": None}
325

326 7
    _rerender_params = ['object', 'depth', 'encoder', 'hover_preview', 'theme']
327

328 7
    @classmethod
329 2
    def applies(cls, obj, **params):
330 7
        if isinstance(obj, (list, dict)):
331 7
            try:
332 7
                json.dumps(obj, cls=params.get('encoder', cls.encoder))
333 7
            except Exception:
334 7
                return False
335
            else:
336 7
                return 0.1
337 7
        elif isinstance(obj, string_types):
338 7
            return 0
339
        else:
340 7
            return None
341

342 7
    def _get_properties(self):
343 7
        properties = super(JSON, self)._get_properties()
344 7
        if isinstance(self.object, string_types):
345 7
            text = self.object
346
        else:
347 7
            text = json.dumps(self.object or {}, cls=self.encoder)
348 7
        depth = None if self.depth < 0 else self.depth
349 7
        return dict(text=text, theme=self.theme, depth=depth,
350
                    hover_preview=self.hover_preview, **properties)

Read our documentation on viewing source code .

Loading