1
"""
2
Various general utilities used in the panel codebase.
3
"""
4 7
from __future__ import absolute_import, division, unicode_literals
5

6 7
import base64
7 7
import datetime as dt
8 7
import inspect
9 7
import json
10 7
import numbers
11 7
import os
12 7
import re
13 7
import sys
14 7
import urllib.parse as urlparse
15

16 7
from collections import defaultdict, OrderedDict
17 7
from contextlib import contextmanager
18 7
from datetime import datetime
19 7
from distutils.version import LooseVersion
20 7
from six import string_types
21

22 7
try:  # python >= 3.3
23 7
    from collections.abc import MutableSequence, MutableMapping
24 0
except ImportError:
25 0
    from collections import MutableSequence, MutableMapping
26

27 7
from html import escape # noqa
28

29 7
import bokeh
30 7
import param
31 7
import numpy as np
32

33 7
from bokeh.settings import settings
34

35 7
PANEL_DIR = os.path.abspath(os.path.dirname(__file__))
36

37 7
datetime_types = (np.datetime64, dt.datetime, dt.date)
38

39 7
if sys.version_info.major > 2:
40 7
    unicode = str
41

42 7
bokeh_version = LooseVersion(bokeh.__version__)
43

44 7
def isfile(path):
45
    """Safe version of os.path.isfile robust to path length issues on Windows"""
46 7
    try:
47 7
        return os.path.isfile(path)
48 0
    except ValueError: # path too long for Windows
49 0
        return False
50

51

52 7
def isurl(obj, formats):
53 7
    if not isinstance(obj, string_types):
54 0
        return False
55 7
    lower_string = obj.lower()
56 7
    return (
57
        lower_string.startswith('http://')
58
        or lower_string.startswith('https://')
59
    ) and (formats is None or any(lower_string.endswith('.'+fmt) for fmt in formats))
60

61

62 7
def is_dataframe(obj):
63 7
    if 'pandas' not in sys.modules:
64 0
        return False
65 7
    import pandas as pd
66 7
    return isinstance(obj, pd.DataFrame)
67

68

69 7
def hashable(x):
70 0
    if isinstance(x, MutableSequence):
71 0
        return tuple(x)
72 0
    elif isinstance(x, MutableMapping):
73 0
        return tuple([(k,v) for k,v in x.items()])
74
    else:
75 0
        return x
76

77

78 7
def isIn(obj, objs):
79
    """
80
    Checks if the object is in the list of objects safely.
81
    """
82 7
    for o in objs:
83 7
        if o is obj:
84 7
            return True
85 7
        try:
86 7
            if o == obj:
87 7
                return True
88 7
        except Exception:
89 7
            pass
90 7
    return False
91

92

93 7
def indexOf(obj, objs):
94
    """
95
    Returns the index of an object in a list of objects. Unlike the
96
    list.index method this function only checks for identity not
97
    equality.
98
    """
99 7
    for i, o in enumerate(objs):
100 7
        if o is obj:
101 7
            return i
102 7
        try:
103 7
            if o == obj:
104 7
                return i
105 7
        except Exception:
106 7
            pass
107 0
    raise ValueError('%s not in list' % obj)
108

109

110 7
def as_unicode(obj):
111
    """
112
    Safely casts any object to unicode including regular string
113
    (i.e. bytes) types in python 2.
114
    """
115 7
    if sys.version_info.major < 3 and isinstance(obj, str):
116 0
        obj = obj.decode('utf-8')
117 7
    return unicode(obj)
118

119

120 7
def param_name(name):
121
    """
122
    Removes the integer id from a Parameterized class name.
123
    """
124 7
    match = re.findall(r'\D+(\d{5,})', name)
125 7
    return name[:name.index(match[0])] if match else name
126

127

128 7
def unicode_repr(obj):
129
    """
130
    Returns a repr without the unicode prefix.
131
    """
132 7
    if sys.version_info.major == 2 and isinstance(obj, unicode):
133 0
        return repr(obj)[1:]
134 7
    return repr(obj)
135

136

137 7
def recursive_parameterized(parameterized, objects=None):
138
    """
139
    Recursively searches a Parameterized object for other Parmeterized
140
    objects.
141
    """
142 7
    objects = [] if objects is None else objects
143 7
    objects.append(parameterized)
144 7
    for _, p in parameterized.param.get_param_values():
145 7
        if isinstance(p, param.Parameterized) and not any(p is o for o in objects):
146 0
            recursive_parameterized(p, objects)
147 7
    return objects
148

149

150 7
def abbreviated_repr(value, max_length=25, natural_breaks=(',', ' ')):
151
    """
152
    Returns an abbreviated repr for the supplied object. Attempts to
153
    find a natural break point while adhering to the maximum length.
154
    """
155 7
    if isinstance(value, list):
156 7
        vrepr = '[' + ', '.join([abbreviated_repr(v) for v in value]) + ']'
157 7
    if isinstance(value, param.Parameterized):
158 0
        vrepr = type(value).__name__
159
    else:
160 7
        vrepr = repr(value)
161 7
    if len(vrepr) > max_length:
162
        # Attempt to find natural cutoff point
163 7
        abbrev = vrepr[max_length//2:]
164 7
        natural_break = None
165 7
        for brk in natural_breaks:
166 7
            if brk in abbrev:
167 7
                natural_break = abbrev.index(brk) + max_length//2
168 7
                break
169 7
        if natural_break and natural_break < max_length:
170 7
            max_length = natural_break + 1
171

172 7
        end_char = ''
173 7
        if isinstance(value, list):
174 7
            end_char = ']'
175 7
        elif isinstance(value, OrderedDict):
176 7
            end_char = '])'
177 7
        elif isinstance(value, (dict, set)):
178 7
            end_char = '}'
179 7
        return vrepr[:max_length+1] + '...' + end_char
180 7
    return vrepr
181

182

183 7
def param_reprs(parameterized, skip=None):
184
    """
185
    Returns a list of reprs for parameters on the parameterized object.
186
    Skips default and empty values.
187
    """
188 7
    cls = type(parameterized).__name__
189 7
    param_reprs = []
190 7
    for p, v in sorted(parameterized.param.get_param_values()):
191 7
        default = parameterized.param[p].default
192 7
        equal = v is default
193 7
        if not equal:
194 7
            try:
195 7
                equal = bool(v==default)
196 0
            except Exception:
197 0
                equal = False
198

199 7
        if equal: continue
200 7
        elif v is None: continue
201 7
        elif isinstance(v, string_types) and v == '': continue
202 7
        elif isinstance(v, list) and v == []: continue
203 7
        elif isinstance(v, dict) and v == {}: continue
204 7
        elif (skip and p in skip) or (p == 'name' and v.startswith(cls)): continue
205 7
        else: v = abbreviated_repr(v)
206 7
        param_reprs.append('%s=%s' % (p, v))
207 7
    return param_reprs
208

209

210 7
def full_groupby(l, key=lambda x: x):
211
    """
212
    Groupby implementation which does not require a prior sort
213
    """
214 7
    d = defaultdict(list)
215 7
    for item in l:
216 7
        d[key(item)].append(item)
217 7
    return d.items()
218

219

220 7
def get_method_owner(meth):
221
    """
222
    Returns the instance owning the supplied instancemethod or
223
    the class owning the supplied classmethod.
224
    """
225 7
    if inspect.ismethod(meth):
226 7
        if sys.version_info < (3,0):
227 0
            return meth.im_class if meth.im_self is None else meth.im_self
228
        else:
229 7
            return meth.__self__
230

231

232 7
def is_parameterized(obj):
233
    """
234
    Whether an object is a Parameterized class or instance.
235
    """
236 7
    return (isinstance(obj, param.Parameterized) or
237
            (isinstance(obj, type) and issubclass(obj, param.Parameterized)))
238

239

240 7
def isdatetime(value):
241
    """
242
    Whether the array or scalar is recognized datetime type.
243
    """
244 7
    if isinstance(value, np.ndarray):
245 7
        return (value.dtype.kind == "M" or
246
                (value.dtype.kind == "O" and len(value) and
247
                 isinstance(value[0], datetime_types)))
248 7
    elif isinstance(value, list):
249 7
        return all(isinstance(d, datetime_types) for d in value)
250
    else:
251 7
        return isinstance(value, datetime_types)
252

253 7
def value_as_datetime(value):
254
    """
255
    Retrieve the value tuple as a tuple of datetime objects.
256
    """
257 7
    if isinstance(value, numbers.Number):
258 7
        value = datetime.utcfromtimestamp(value / 1000)
259 7
    return value
260

261

262 7
def value_as_date(value):
263 7
    if isinstance(value, numbers.Number):
264 7
        value = datetime.utcfromtimestamp(value / 1000).date()
265 0
    elif isinstance(value, datetime):
266 0
        value = value.date()
267 7
    return value
268

269

270 7
def is_number(s):
271 7
    try:
272 7
        float(s)
273 0
        return True
274 7
    except ValueError:
275 7
        return False
276

277

278 7
def parse_query(query):
279
    """
280
    Parses a url query string, e.g. ?a=1&b=2.1&c=string, converting
281
    numeric strings to int or float types.
282
    """
283 7
    query = dict(urlparse.parse_qsl(query[1:]))
284 7
    for k, v in list(query.items()):
285 7
        if v.isdigit():
286 7
            query[k] = int(v)
287 7
        elif is_number(v):
288 0
            query[k] = float(v)
289 7
        elif v.startswith('[') or v.startswith('{'):
290 0
            query[k] = json.loads(v)
291 7
    return query
292

293

294 7
def base64url_encode(input):
295 0
    if isinstance(input, str):
296 0
        input = input.encode("utf-8")
297 0
    encoded = base64.urlsafe_b64encode(input).decode('ascii')
298
    # remove padding '=' chars that cause trouble
299 0
    return str(encoded.rstrip('='))
300

301

302 7
def base64url_decode(input):
303 0
    if isinstance(input, str):
304 0
        input = input.encode("ascii")
305

306 0
    rem = len(input) % 4
307

308 0
    if rem > 0:
309 0
        input += b"=" * (4 - rem)
310

311 0
    return base64.urlsafe_b64decode(input)
312

313

314 7
class classproperty(object):
315

316 7
    def __init__(self, f):
317 7
        self.f = f
318

319 7
    def __get__(self, obj, owner):
320 7
        return self.f(owner)
321

322

323 7
def url_path(url):
324 7
    return os.path.join(*os.path.join(*url.split('//')[1:]).split('/')[1:])
325

326

327 7
def bundled_files(model, file_type='javascript'):
328 7
    bdir = os.path.join(PANEL_DIR, 'dist', 'bundled', model.__name__.lower())
329 7
    name = model.__name__.lower()
330 7
    resources = settings.resources(default='server')
331 7
    files = []
332 7
    for url in getattr(model, f"__{file_type}_raw__", []):
333 7
        filepath = url_path(url)
334 7
        test_filepath = filepath.split('?')[0]
335 7
        if resources == 'server' and os.path.isfile(os.path.join(bdir, test_filepath)):
336 7
            files.append(f'/static/extensions/panel/bundled/{name}/{filepath}')
337
        else:
338 7
            files.append(url)
339 7
    return files
340

341

342
# This functionality should be contributed to param
343
# See https://github.com/holoviz/param/issues/379
344 7
@contextmanager
345 2
def edit_readonly(parameterized):
346
    """
347
    Temporarily set parameters on Parameterized object to readonly=False
348
    to allow editing them.
349
    """
350 7
    params = parameterized.param.objects("existing").values()
351 7
    readonlys = [p.readonly for p in params]
352 7
    constants = [p.constant for p in params]
353 7
    for p in params:
354 7
        p.readonly = False
355 7
        p.constant = False
356 7
    try:
357 7
        yield
358 0
    except Exception:
359 0
        raise
360
    finally:
361 7
        for (p, readonly) in zip(params, readonlys):
362 7
            p.readonly = readonly
363 7
        for (p, constant) in zip(params, constants):
364 7
            p.constant = constant

Read our documentation on viewing source code .

Loading