Test Failed
Push — develop ( 720679...ba0508 )
by Dean
02:45
created

error_view()   A

Complexity

Conditions 1

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 15
rs 9.4285
c 1
b 0
f 0
cc 1
1
from core.logger import Logger
0 ignored issues
show
Bug introduced by
The name logger does not seem to exist in module core.
Loading history...
Configuration introduced by
The import core.logger could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
2
3
from plugin.core.constants import PLUGIN_PREFIX
0 ignored issues
show
Configuration introduced by
The import plugin.core.constants could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
4
from plugin.core.message import InterfaceMessages
0 ignored issues
show
Configuration introduced by
The import plugin.core.message could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
5
6
import base64
7
import cerealizer
0 ignored issues
show
Configuration introduced by
The import cerealizer could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
8
import functools
9
import hashlib
10
import inspect
11
import logging
12
import sys
13
import threading
14
import thread
0 ignored issues
show
Configuration introduced by
The import thread could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
15
import time
16
import urllib
17
18
log = Logger('core.helpers')
19
20
21
PY25 = sys.version_info[0] == 2 and sys.version_info[1] == 5
22
23
24
def try_convert(value, value_type, default=None):
25
    try:
26
        return value_type(value)
27
    except ValueError:
28
        return default
29
    except TypeError:
30
        return default
31
32
33
def add_attribute(target, source, key, value_type=str, func=None, target_key=None):
34
    if target_key is None:
35
        target_key = key
36
37
    value = try_convert(source.get(key, None), value_type)
38
39
    if value:
40
        target[target_key] = func(value) if func else value
41
42
43
def merge(a, b):
44
    a.update(b)
45
    return a
46
47
48
def all(items):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in all.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
49
    for item in items:
50
        if not item:
51
            return False
52
    return True
53
54
55
def any(items):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in any.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
56
    for item in items:
57
        if item:
58
            return True
59
60
    return False
61
62
63
def json_import():
64
    try:
65
        import simplejson as json
66
67
        log.info("Using 'simplejson' module for JSON serialization")
68
        return json, 'json'
69
    except ImportError:
0 ignored issues
show
Unused Code introduced by
This except handler seems to be unused and could be removed.

Except handlers which only contain pass and do not have an else clause can usually simply be removed:

try:
    raises_exception()
except:  # Could be removed
    pass
Loading history...
70
        pass
71
72
    # Try fallback to 'json' module
73
    try:
74
        import json
75
76
        log.info("Using 'json' module for JSON serialization")
77
        return json, 'json'
78
    except ImportError:
0 ignored issues
show
Unused Code introduced by
This except handler seems to be unused and could be removed.

Except handlers which only contain pass and do not have an else clause can usually simply be removed:

try:
    raises_exception()
except:  # Could be removed
    pass
Loading history...
79
        pass
80
81
    # Try fallback to 'demjson' module
82
    try:
83
        import demjson
84
85
        log.info("Using 'demjson' module for JSON serialization")
86
        return demjson, 'demjson'
87
    except ImportError:
88
        log.warn("Unable to find json module for serialization")
89
        raise Exception("Unable to find json module for serialization")
90
91
# Import json serialization module
92
JSON, JSON_MODULE = json_import()
93
94
95
# JSON serialization wrappers to simplejson/json or demjson
96
def json_decode(s):
97
    if JSON_MODULE == 'json':
98
        return JSON.loads(s)
99
100
    if JSON_MODULE == 'demjson':
101
        return JSON.decode(s)
0 ignored issues
show
Bug introduced by
The Module json does not seem to have a member named decode.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
102
103
    raise NotImplementedError()
104
105
106
def json_encode(obj):
107
    if JSON_MODULE == 'json':
108
        return JSON.dumps(obj)
109
110
    if JSON_MODULE == 'demjson':
111
        return JSON.encode(obj)
0 ignored issues
show
Bug introduced by
The Module json does not seem to have a member named encode.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
112
113
    raise NotImplementedError()
114
115
116
def str_format(s, *args, **kwargs):
117
    """Return a formatted version of S, using substitutions from args and kwargs.
118
119
    (Roughly matches the functionality of str.format but ensures compatibility with Python 2.5)
120
    """
121
122
    args = list(args)
123
124
    x = 0
125
    while x < len(s):
126
        # Skip non-start token characters
127
        if s[x] != '{':
128
            x += 1
129
            continue
130
131
        end_pos = s.find('}', x)
132
133
        # If end character can't be found, move to next character
134
        if end_pos == -1:
135
            x += 1
136
            continue
137
138
        name = s[x + 1:end_pos]
139
140
        # Ensure token name is alpha numeric
141
        if not name.isalnum():
142
            x += 1
143
            continue
144
145
        # Try find value for token
146
        value = args.pop(0) if args else kwargs.get(name)
147
148
        if value:
149
            value = str(value)
150
151
            # Replace token with value
152
            s = s[:x] + value + s[end_pos + 1:]
153
154
            # Update current position
155
            x = x + len(value) - 1
156
157
        x += 1
158
159
    return s
160
161
162
def str_pad(s, length, align='left', pad_char=' ', trim=False):
163
    if not s:
164
        return s
165
166
    if not isinstance(s, (str, unicode)):
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'unicode'
Loading history...
167
        s = str(s)
168
169
    if len(s) == length:
170
        return s
171
    elif len(s) > length and not trim:
172
        return s
173
174
    if align == 'left':
175
        if len(s) > length:
176
            return s[:length]
177
        else:
178
            return s + (pad_char * (length - len(s)))
179
    elif align == 'right':
180
        if len(s) > length:
181
            return s[len(s) - length:]
182
        else:
183
            return (pad_char * (length - len(s))) + s
184
    else:
185
        raise ValueError("Unknown align type, expected either 'left' or 'right'")
186
187
188
def pad_title(value):
189
    """Pad a title to 30 characters to force the 'details' view."""
190
    return str_pad(value, 30, pad_char=' ')
191
192
193
def total_seconds(span):
194
    return (span.microseconds + (span.seconds + span.days * 24 * 3600) * 1e6) / 1e6
195
196
197
def sum(values):
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in sum.

It is generally discouraged to redefine built-ins as this makes code very hard to read.

Loading history...
198
    result = 0
199
200
    for x in values:
201
        result = result + x
202
203
    return result
204
205
206
def timestamp():
207
    return str(time.time())
208
209
210
# <bound method type.start of <class 'Scrobbler'>>
211
RE_BOUND_METHOD = Regex(r"<bound method (type\.)?(?P<name>.*?) of <(class '(?P<class>.*?)')?")
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'Regex'
Loading history...
212
213
214
def get_func_name(obj):
215
    if inspect.ismethod(obj):
216
        match = RE_BOUND_METHOD.match(repr(obj))
217
218
        if match:
219
            cls = match.group('class')
220
            if not cls:
221
                return match.group('name')
222
223
            return '%s.%s' % (
224
                match.group('class'),
225
                match.group('name')
226
            )
227
228
    return None
229
230
231
def get_class_name(cls):
232
    if not inspect.isclass(cls):
233
        cls = getattr(cls, '__class__')
234
235
    return getattr(cls, '__name__')
236
237
238
def spawn(func, *args, **kwargs):
239
    thread_name = kwargs.pop('thread_name', None) or get_func_name(func)
240
241
    th = threading.Thread(target=thread_wrapper, name=thread_name, kwargs={
242
        'func': func,
243
        'args': args,
244
        'kwargs': kwargs,
245
        'thread_name': thread_name
246
    })
247
248
    try:
249
        th.start()
250
        log.debug("Spawned thread with name '%s'" % thread_name)
251
    except thread.error as ex:
252
        log.error('Unable to spawn thread: %s', ex, exc_info=True, extra={
253
            'data': {
254
                'active_count': threading.active_count()
255
            }
256
        })
257
        return None
258
259
    return th
260
261
262
def thread_wrapper(func, args=None, kwargs=None, thread_name=None):
263
    if args is None:
264
        args = ()
265
266
    if kwargs is None:
267
        kwargs = {}
268
269
    try:
270
        func(*args, **kwargs)
271
    except Exception as ex:
0 ignored issues
show
Best Practice introduced by
Catching very general exceptions such as Exception is usually not recommended.

Generally, you would want to handle very specific errors in the exception handler. This ensure that you do not hide other types of errors which should be fixed.

So, unless you specifically plan to handle any error, consider adding a more specific exception.

Loading history...
272
        log.error('Exception raised in thread "%s": %s', thread_name, ex, exc_info=True)
273
274
275
def schedule(func, seconds, *args, **kwargs):
276
    def schedule_sleep():
277
        time.sleep(seconds)
278
        func(*args, **kwargs)
279
280
    spawn(schedule_sleep)
281
282
283
def build_repr(obj, keys):
284
    key_part = ', '.join([
285
        ('%s: %s' % (key, repr(getattr(obj, key))))
286
        for key in keys
287
    ])
288
289
    cls = getattr(obj, '__class__')
290
291
    return '<%s %s>' % (getattr(cls, '__name__'), key_part)
292
293
294
def plural(value):
295
    if type(value) is list:
296
        value = len(value)
297
298
    if value == 1:
299
        return ''
300
301
    return 's'
302
303
304
def join_attributes(**kwargs):
305
    fragments = [
306
        (('%s: %s' % (key, value)) if value else None)
307
        for (key, value) in kwargs.items()
308
    ]
309
310
    return ', '.join([x for x in fragments if x])
311
312
313
def md5(value):
314
    # Generate MD5 hash of key
315
    m = hashlib.md5()
316
    m.update(value)
317
318
    return m.hexdigest()
319
320
321
def safe_encode(string):
322
    string = str(string)
323
    return base64.b64encode(string).replace('/', '@').replace('+', '*').replace('=', '_')
324
325
326
def pack(obj):
327
    serialized_obj = cerealizer.dumps(obj)
328
    encoded_string = safe_encode(serialized_obj)
329
    return urllib.quote(encoded_string)
0 ignored issues
show
Bug introduced by
The Module urllib does not seem to have a member named quote.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
330
331
332
def function_path(name, ext=None, **kwargs):
333
    return '%s/:/function/%s%s?%s' % (
334
        PLUGIN_PREFIX,
335
        name,
336
        ('.%s' % ext) if ext else '',
337
338
        urllib.urlencode({
0 ignored issues
show
Bug introduced by
The Module urllib does not seem to have a member named urlencode.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
339
            'function_args': pack(kwargs)
340
        })
341
    )
342
343
344
def redirect(path, **kwargs):
345
    location = PLUGIN_PREFIX + path
346
347
    if kwargs:
348
        location += '?' + urllib.urlencode(kwargs)
0 ignored issues
show
Bug introduced by
The Module urllib does not seem to have a member named urlencode.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
349
350
    try:
351
        request = Core.sandbox.context.request
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'Core'
Loading history...
352
353
        # Retrieve protocol
354
        protocol = request.protocol
355
356
        if request.host.endswith('.plex.direct:32400'):
357
            # Secure connection
358
            protocol = 'https'
359
360
        # Build URL
361
        if request and request.host and location[0] == "/":
362
            location = protocol + "://" + request.host + location
363
    except Exception as ex:
0 ignored issues
show
Best Practice introduced by
Catching very general exceptions such as Exception is usually not recommended.

Generally, you would want to handle very specific errors in the exception handler. This ensure that you do not hide other types of errors which should be fixed.

So, unless you specifically plan to handle any error, consider adding a more specific exception.

Loading history...
364
        log.warn('Redirect - %s', str(ex), exc_info=True)
365
366
    # Return redirect response
367
    return Redirect(location)
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'Redirect'
Loading history...
368
369
370
def catch_errors(func):
371
    @functools.wraps(func)
372
    def inner(*args, **kwargs):
373
        if InterfaceMessages.critical:
374
            return error_record_view(logging.CRITICAL, InterfaceMessages.record)
375
376
        try:
377
            return func(*args, **kwargs)
378
        except Exception as ex:
0 ignored issues
show
Best Practice introduced by
Catching very general exceptions such as Exception is usually not recommended.

Generally, you would want to handle very specific errors in the exception handler. This ensure that you do not hide other types of errors which should be fixed.

So, unless you specifically plan to handle any error, consider adding a more specific exception.

Loading history...
379
            if InterfaceMessages.critical:
380
                return error_record_view(logging.CRITICAL, InterfaceMessages.record)
381
382
            return error_view(
383
                'Exception',
384
                ex.message
0 ignored issues
show
Bug introduced by
The Instance of Exception does not seem to have a member named message.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
385
            )
386
387
    return inner
388
389
390
def error_record_view(level, record):
391
    # Retrieve level name
392
    if level == logging.CRITICAL:
393
        level_name = 'Critical Error'
394
    else:
395
        level_name = logging.getLevelName(level).capitalize()
396
397
    # Build error view
398
    if not record:
399
        return error_view(level_name)
400
401
    return error_view(
402
        level_name,
403
        record.message
404
    )
405
406
407
def error_view(title, message=None):
408
    oc = ObjectContainer(
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'ObjectContainer'
Loading history...
409
        title2=title,
410
        no_cache=True
411
    )
412
413
    oc.add(DirectoryObject(
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'DirectoryObject'
Loading history...
414
        key=PLUGIN_PREFIX,
415
        title=pad_title('%s: %s' % (
416
            title,
417
            message or 'Unknown'
418
        ))
419
    ))
420
421
    return oc
422