GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

Imagine   B
last analyzed

Complexity

Total Complexity 51

Size/Duplication

Total Lines 260
Duplicated Lines 0 %

Importance

Changes 8
Bugs 0 Features 0
Metric Value
c 8
b 0
f 0
dl 0
loc 260
rs 8.3206
wmc 51

11 Methods

Rating   Name   Duplication   Size   Complexity  
D add_filter_set() 0 34 8
C handle_request() 0 34 7
A _handle_adapter() 0 13 4
A init_app() 0 21 4
A _add_url_rule() 0 14 1
F _handle_filter_sets() 0 30 10
A clear_cache() 0 11 3
A __init__() 0 6 2
A _set_defaults() 0 22 1
F update_filter_set() 0 34 9
A remove_filter_set() 0 9 2

How to fix   Complexity   

Complex Class

Complex classes like Imagine often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
"""
2
Flask Imagine extension.
3
"""
4
from __future__ import unicode_literals
5
import logging
6
7
from io import BytesIO
8
from flask import current_app, abort, redirect
9
10
from flask.ext.imagine.adapters import *
11
from flask.ext.imagine.filters import *
12
13
from .helpers.regex_route import RegexConverter
14
15
LOGGER = logging.getLogger(__file__)
16
17
18
class Imagine(object):
19
    """
20
    Flask Imagine extension
21
    """
22
    _adapters = {
23
        'fs': ImagineFilesystemAdapter
24
    }
25
    _filters = {
26
        'autorotate': AutorotateFilter,
27
        'crop': CropFilter,
28
        'downscale': DownscaleFilter,
29
        'relative_resize': RelativeResizeFilter,
30
        'rotate': RotateFilter,
31
        'thumbnail': ThumbnailFilter,
32
        'upscale': UpscaleFilter,
33
        'watermark': WatermarkFilter
34
    }
35
36
    _filter_sets = {}
37
    _adapter = None
38
    _redirect_code = 302
39
40
    def __init__(self, app=None):
41
        """
42
        :param app: Flask application
43
        """
44
        if app is not None:
45
            self.init_app(app)
46
47
    def init_app(self, app):
48
        """
49
        :param app: Flask application
50
        """
51
        if not hasattr(app, 'extensions'):  # pragma: no cover
52
            app.extensions = {}
53
        app.extensions['imagine'] = self
54
55
        self._set_defaults(app)
56
57
        self._redirect_code = app.config['IMAGINE_CACHE_REDIRECT_CODE']
58
59
        if isinstance(app.config['IMAGINE_ADAPTERS'], dict):
60
            self._adapters.update(app.config['IMAGINE_ADAPTERS'])
61
        if isinstance(app.config['IMAGINE_FILTERS'], dict):
62
            self._filters.update(app.config['IMAGINE_FILTERS'])
63
64
        self._handle_adapter(app)
65
        self._handle_filter_sets(app)
66
67
        self._add_url_rule(app)
68
69
    @classmethod
70
    def _set_defaults(cls, app):
71
        """
72
        Set default configuration parameters
73
        :param app: Flask application
74
        :return: Flask application
75
        """
76
        app.config.setdefault('IMAGINE_URL', '/media/cache/resolve')
77
        app.config.setdefault('IMAGINE_NAME', 'imagine')
78
        app.config.setdefault('IMAGINE_CACHE_ENABLED', True)
79
        app.config.setdefault('IMAGINE_CACHE_REDIRECT_CODE', 302)
80
81
        app.config.setdefault('IMAGINE_ADAPTERS', {})
82
        app.config.setdefault('IMAGINE_FILTERS', {})
83
84
        app.config.setdefault('IMAGINE_ADAPTER', {
85
            'name': 'fs',
86
        })
87
88
        app.config.setdefault('IMAGINE_FILTER_SETS', {})
89
90
        return app
91
92
    def _handle_adapter(self, app):
93
        """
94
        Handle storage _adapter configuration
95
        :param app: Flask application
96
        """
97
        if 'IMAGINE_ADAPTER' in app.config \
98
                and 'name' in app.config['IMAGINE_ADAPTER'] \
99
                and app.config['IMAGINE_ADAPTER']['name'] in self._adapters.keys():
100
            self._adapter = self._adapters[app.config['IMAGINE_ADAPTER']['name']](
101
                **app.config['IMAGINE_ADAPTER']
102
            )
103
        else:
104
            raise ValueError('Unknown _adapter: %s' % str(app.config['IMAGINE_ADAPTER']))
105
106
    def _handle_filter_sets(self, app):
107
        """
108
        Handle filter sets
109
        :param app: Flask application
110
        """
111
        if 'IMAGINE_FILTER_SETS' in app.config and isinstance(app.config['IMAGINE_FILTER_SETS'], dict):
112
            for filter_name, filters_settings in app.config['IMAGINE_FILTER_SETS'].items():
113
                filter_set = []
114
                if isinstance(filters_settings, dict) and 'filters' in filters_settings:
115
                    for filter_type, filter_settings in filters_settings['filters'].items():
116
                        if filter_type in self._filters:
117
                            filter_item = self._filters[filter_type](**filter_settings)
118
                            if isinstance(filter_item, ImagineFilterInterface):
119
                                filter_set.append(filter_item)
120
                            else:
121
                                raise ValueError('Filter must be implement ImagineFilterInterface')
122
                        else:
123
                            raise ValueError('Unknown filter type: %s' % filter_type)
124
125
                    filter_config = {'filters': filter_set}
126
                    if 'cached' in filters_settings:
127
                        filter_config['cached'] = filters_settings['cached']
128
                    else:
129
                        filter_config['cached'] = app.config['IMAGINE_CACHE_ENABLED']
130
131
                    self._filter_sets.update({filter_name: filter_config})
132
                else:
133
                    raise ValueError('Wrong settings for filter: %s' % filter_name)
134
        else:
135
            raise ValueError('Filters configuration does not present')
136
137
    def _add_url_rule(self, app):
138
        """
139
        Add url rule for get filtered images
140
        :param app: Flask application
141
        :return: Flask application
142
        """
143
        app.url_map.converters['regex'] = RegexConverter
144
        app.add_url_rule(
145
            app.config['IMAGINE_URL'] + '/<regex("[^\/]+"):filter_name>/<path:path>',
146
            app.config['IMAGINE_NAME'],
147
            self.handle_request
148
        )
149
150
        return app
151
152
    def handle_request(self, filter_name, path):
153
        """
154
        Handle image request
155
        :param filter_name: filter_name
156
        :param path: image_path
157
        :return:
158
        """
159
        if filter_name in self._filter_sets:
160
            if self._filter_sets[filter_name]['cached']:
161
                cached_item_path = self._adapter.check_cached_item('%s/%s' % (filter_name, path))
162
                if cached_item_path:
163
                    return redirect(cached_item_path, self._redirect_code)
164
165
            resource = self._adapter.get_item(path)
166
167
            if resource:
168
                for filter_item in self._filter_sets[filter_name]['filters']:
169
                    resource = filter_item.apply(resource)
170
171
                if self._filter_sets[filter_name]['cached']:
172
                    return redirect(
173
                        self._adapter.create_cached_item('%s/%s' % (filter_name, path), resource),
174
                        self._redirect_code
175
                    )
176
                else:
177
                    output = BytesIO()
178
                    resource.save(output, format=str(resource.format))
179
                    return output.getvalue()
180
            else:
181
                LOGGER.warning('File "%s" not found.' % path)
182
                abort(404)
183
        else:
184
            LOGGER.warning('Filter "%s" not found.' % filter_name)
185
            abort(404)
186
187
    def clear_cache(self, path, filter_name=None):
188
        """
189
        Clear cache for resource path.
190
        :param path: str
191
        :param filter_name: str or None
192
        """
193
        if filter_name:
194
            self._adapter.remove_cached_item('%s/%s' % (filter_name, path))
195
        else:
196
            for filter_name in self._filter_sets:
197
                self._adapter.remove_cached_item('%s/%s' % (filter_name, path))
198
199
    def add_filter_set(self, filter_name, filter_set, cached=True):
200
        """
201
        Manual addition of filter set
202
        :param filter_name: str
203
        :param filter_set: list
204
        :param cached: bool
205
        """
206
        try:
207
            hash(filter_name)
208
        except TypeError as err:
209
            raise ValueError('Filter set name must be as instance of hashable type: %s' % str(err))
210
211
        if not isinstance(filter_set, list):
212
            raise ValueError('Filters must be a list.')
213
214
        if len(filter_set) == 0:
215
            raise ValueError('Filters count must be greater than 0.')
216
217
        for filter_instance in filter_set:
218
            if not isinstance(filter_instance, ImagineFilterInterface):
219
                raise ValueError('All filters must implement of ImagineFilterInterface.')
220
221
        if not isinstance(cached, bool):
222
            raise ValueError('Cached parameter must be a bool.')
223
224
        filter_config = {
225
            'filters': filter_set,
226
            'cached': cached
227
        }
228
229
        if filter_name not in self._filter_sets:
230
            self._filter_sets.update({filter_name: filter_config})
231
        else:
232
            raise ValueError('Duplicate filter set name.')
233
234
    def update_filter_set(self, filter_name, filter_set=None, cached=None):
235
        """
236
        Manual update of filter set
237
        :param filter_name: str
238
        :param filter_set: list
239
        :param cached: bool
240
        """
241
        try:
242
            hash(filter_name)
243
        except TypeError as err:
244
            raise ValueError('Filter set name must be as instance of hashable type: %s' % str(err))
245
246
        filter_config = self._filter_sets[filter_name]
247
248
        if filter_set is not None:
249
            if not isinstance(filter_set, list):
250
                raise ValueError('Filters must be a list.')
251
252
            if len(filter_set) == 0:
253
                raise ValueError('Filters count must be greater than 0.')
254
255
            for filter_instance in filter_set:
256
                if not isinstance(filter_instance, ImagineFilterInterface):
257
                    raise ValueError('All filters must implement of ImagineFilterInterface.')
258
259
            filter_config.update({'filters': filter_set})
260
261
        if cached is not None:
262
            if not isinstance(cached, bool):
263
                raise ValueError('Cached parameter must be a bool.')
264
265
            filter_config.update({'cached': cached})
266
267
        self._filter_sets.update({filter_name: filter_config})
268
269
    def remove_filter_set(self, filter_name):
270
        """
271
        Remove filter set by name
272
        :param filter_name: str
273
        """
274
        if filter_name in self._filter_sets:
275
            del self._filter_sets[filter_name]
276
        else:
277
            raise ValueError('Unknown filter set name.')
278
279
280
def imagine_cache_clear(path, filter_name=None):
281
    """
282
    Clear cache for resource path.
283
    :param path: str
284
    :param filter_name: str or None
285
    """
286
    self = current_app.extensions['imagine']
287
    self.clear_cache(path, filter_name)
288