holoviz / panel
1
"""
2
Various general utilities used in the panel codebase.
3
"""
4 7
import base64
5 7
import datetime as dt
6 7
import inspect
7 7
import json
8 7
import numbers
9 7
import os
10 7
import re
11 7
import sys
12 7
import urllib.parse as urlparse
13

14 7
from collections.abc import MutableSequence, MutableMapping
15 7
from collections import defaultdict, OrderedDict
16 7
from contextlib import contextmanager
17 7
from datetime import datetime
18 7
from distutils.version import LooseVersion
19 7
from html import escape # noqa
20 7
from importlib import import_module
21 7
from six import string_types
22

23 7
import bokeh
24 7
import param
25 7
import numpy as np
26

27 7
datetime_types = (np.datetime64, dt.datetime, dt.date)
28

29 7
if sys.version_info.major > 2:
30 7
    unicode = str
31

32 7
bokeh_version = LooseVersion(bokeh.__version__)
33

34

35 7
def isfile(path):
36
    """Safe version of os.path.isfile robust to path length issues on Windows"""
37 7
    try:
38 7
        return os.path.isfile(path)
39 0
    except ValueError: # path too long for Windows
40 0
        return False
41

42

43 7
def isurl(obj, formats):
44 7
    if not isinstance(obj, string_types):
45 0
        return False
46 7
    lower_string = obj.lower().split('?')[0].split('#')[0]
47 7
    return (
48
        lower_string.startswith('http://')
49
        or lower_string.startswith('https://')
50
    ) and (formats is None or any(lower_string.endswith('.'+fmt) for fmt in formats))
51

52

53 7
def is_dataframe(obj):
54 7
    if 'pandas' not in sys.modules:
55 0
        return False
56 7
    import pandas as pd
57 7
    return isinstance(obj, pd.DataFrame)
58

59

60 7
def is_series(obj):
61 7
    if 'pandas' not in sys.modules:
62 0
        return False
63 7
    import pandas as pd
64 7
    return isinstance(obj, pd.Series)
65

66

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

75

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

90

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

107

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

117

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

125

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

134

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

147

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

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

180

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

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

207

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

217

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

229

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

237

238 7
def isdatetime(value):
239
    """
240
    Whether the array or scalar is recognized datetime type.
241
    """
242 7
    if is_series(value) and len(value):
243 7
        return isinstance(value.iloc[0], datetime_types)
244 7
    elif 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
# This functionality should be contributed to param
328
# See https://github.com/holoviz/param/issues/379
329 7
@contextmanager
330 2
def edit_readonly(parameterized):
331
    """
332
    Temporarily set parameters on Parameterized object to readonly=False
333
    to allow editing them.
334
    """
335 7
    params = parameterized.param.objects("existing").values()
336 7
    readonlys = [p.readonly for p in params]
337 7
    constants = [p.constant for p in params]
338 7
    for p in params:
339 7
        p.readonly = False
340 7
        p.constant = False
341 7
    try:
342 7
        yield
343 0
    except Exception:
344 0
        raise
345
    finally:
346 7
        for (p, readonly) in zip(params, readonlys):
347 7
            p.readonly = readonly
348 7
        for (p, constant) in zip(params, constants):
349 7
            p.constant = constant
350

351

352 7
def lazy_load(module, model, notebook=False):
353 7
    if module in sys.modules:
354 7
        return getattr(sys.modules[module], model)
355 7
    if notebook:
356 0
        ext = module.split('.')[-1]
357 0
        param.main.param.warning(f'{model} was not imported on instantiation '
358
                                 'and may not render in a notebook. Restart '
359
                                 'the notebook kernel and ensure you load '
360
                                 'it as part of the extension using:'
361
                                 f'\n\npn.extension(\'{ext}\')\n')
362 7
    return getattr(import_module(module), model)
363

364

365 7
def updating(fn):
366 7
    def wrapped(self, *args, **kwargs):
367 7
        updating = self._updating
368 7
        self._updating = True
369 7
        try:
370 7
            fn(self, *args, **kwargs)
371
        finally:
372 7
            self._updating = updating
373 7
    return wrapped

Read our documentation on viewing source code .

Loading