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.
Test Failed
Pull Request — master (#38)
by
unknown
02:13
created

b2blaze.models.file_list   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 335
Duplicated Lines 0 %

Test Coverage

Coverage 10.12%

Importance

Changes 0
Metric Value
eloc 190
dl 0
loc 335
ccs 17
cts 168
cp 0.1012
rs 8.8
c 0
b 0
f 0
wmc 45

11 Methods

Rating   Name   Duplication   Size   Complexity  
A B2FileList.__init__() 0 10 1
A B2FileList._get_by_id() 0 12 2
C B2FileList.upload_large_file() 0 68 9
A B2FileList._get_by_name() 0 18 3
B B2FileList.all_file_versions() 0 52 7
A B2FileList.get_versions() 0 21 3
A B2FileList.all() 0 22 3
A B2FileList.get() 0 18 3
B B2FileList._update_files_list() 0 36 6
A B2FileList.upload() 0 32 4
A B2FileList.delete_all() 0 15 4

How to fix   Complexity   

Complexity

Complex classes like b2blaze.models.file_list 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
Copyright George Sibble 2018
3
"""
4
5 1
from .b2_file import B2File
6 1
from ..utilities import b2_url_encode, get_content_length, get_part_ranges, decode_error, RangeStream, StreamWithHashProgress
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (125/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
Unused Code introduced by
Unused decode_error imported from utilities
Loading history...
Unused Code introduced by
Unused StreamWithHashProgress imported from utilities
Loading history...
7 1
from ..b2_exceptions import B2Exception, B2FileNotFoundError
8 1
from multiprocessing.dummy import Pool as ThreadPool
0 ignored issues
show
introduced by
standard import "from multiprocessing.dummy import Pool as ThreadPool" should be placed before "from .b2_file import B2File"
Loading history...
9 1
from ..api import API
10
11 1
class B2FileList(object):
0 ignored issues
show
Documentation introduced by
Empty class docstring
Loading history...
12
    """
13
14
    """
15 1
    def __init__(self, connector, bucket):
16
        """
17
18
        :param connector:
19
        :param bucket:
20
        """
21
        self.connector = connector
22
        self.bucket = bucket
23
        self._files_by_name = {}
24
        self._files_by_id = {}
25
26 1
    def all(self, include_hidden=False, limit=None, prefix="", delimiter=None):
27
        """ Return an updated list of all files.
28
            (This does not include hidden files unless include_hidden flag set to True)
29
30
            Parameters:
31
                include_hidden:         (bool) Include hidden files
32
                limit:                  (int)  Limit number of file results
33
                prefix:                 (str)  Limit files to those with the given prefix (default: empty string)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (113/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
34
                delimiter:              (str)  Files returned will be limited to those within the top folder, or any one subfolder. (default: null)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (147/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
35
36
        """
37
        if not include_hidden:
38
            return self._update_files_list(retrieve=True, limit=limit, prefix=prefix, delimiter=delimiter)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (106/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
39
        else:
40
            results = self.all_file_versions(limit=limit)
41
            versions = results['file_versions']
42
            file_ids = results['file_ids']
43
            if versions:
44
                # Return only the first file from a given file with multiple versions
45
                files = [versions[f][0] for f in file_ids]
46
                return files
47
        return []   # Return empty set on no results
48
49 1
    def delete_all(self, confirm=False):
50
        """ Delete all files in the bucket. 
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
51
            Parameters:
52
                confirm:    (bool)  Safety check. Confirm deletion
53
        """ 
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
54
        if not confirm:
55
            raise Exception('This will delete all files! Pass confirm=True')
56
        
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
57
        all_files = self.all(include_hidden=True)
58
        try:
59
            for f in all_files:
0 ignored issues
show
Coding Style Naming introduced by
The name f does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
60
                f.delete_all_versions(confirm=True)
61
        except Exception as E:
0 ignored issues
show
Coding Style Naming introduced by
The name E does not conform to the variable naming conventions ((([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
62
            raise B2Exception.parse(E)
63
        return []
64
65
        
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
66 1
    def _update_files_list(self, retrieve=False, limit=None, prefix="", delimiter=None):
67
        """ Retrieve list of all files in bucket 
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
68
            Parameters:
69
                limit:      (int)  Max number of file results, default 10000
70
                retrieve:   (bool) Refresh local store. (default: false)
71
                prefix:     (str)  Limit files to those with the given prefix (default: empty string)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (101/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
72
                delimiter:  (str)  Files returned will be limited to those within the top folder, or any one subfolder. (default: null)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (135/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
73
        """
74
        path = API.list_all_files
75
        files = []
76
        new_files_to_retrieve = True
77
        params = {
78
            'bucketId': self.bucket.bucket_id,
79
            'maxFileCount': limit or 10000,
80
            'prefix': prefix,
81
            'delimiter': delimiter
82
        }
83
        while new_files_to_retrieve:
84
            response = self.connector.make_request(path=path, method='post', params=params)
85
            if response.status_code == 200:
86
                files_json = response.json()
87
                self._files_by_name = {}
88
                self._files_by_id = {}
89
                for file_json in files_json['files']:
90
                    new_file = B2File(connector=self.connector, parent_list=self, **file_json)
91
                    files.append(new_file)
92
                    self._files_by_name[file_json['fileName']] = new_file
93
                    self._files_by_id[file_json['fileId']] = new_file
94
                if files_json['nextFileName'] is None:
95
                    new_files_to_retrieve = False
96
                else:
97
                    params['startFileName'] = files_json['nextFileName']
98
            else:
99
                raise B2Exception.parse(response)
100
        if retrieve:
101
            return files
102
103
104 1
    def get(self, file_name=None, file_id=None):
105
        """ Get a file by file name or id.
106
            Required:
107
                file_name or file_id
108
109
            Parameters:
110
                file_name:          (str) File name 
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
111
                file_id:            (str) File ID 
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
112
        """
113
        if file_name:
114
            file = self._get_by_name(file_name)
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in file.

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

Loading history...
115
116
        elif file_id:
117
            file = self._get_by_id(file_id)
118
        else:
119
            raise ValueError('file_name or file_id must be passed')
120
        
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
121
        return file
122
123
124 1
    def get_versions(self, file_name=None, file_id=None, limit=None):
0 ignored issues
show
Unused Code introduced by
The argument limit seems to be unused.
Loading history...
125
        """ Return list of all the versions of one file in current bucket. 
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
126
            Required:
127
                file_id or file_name   (either)
128
129
            Params:
130
                file_id:            (str) File id
131
                file_name:          (str) File id
132
                limit:              (int) Limit number of results returned (optional)
133
134
            Returns:
135
                file_versions       (list) B2FileObject of all file versions
136
        """ 
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
137
        if file_name:
138
            file = self.get(file_name)
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in file.

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

Loading history...
139
140
        elif file_id:
141
            file = self.get(file_id=file_id)
142
        else:
143
            raise ValueError('Either file_id or file_name required for get_versions')
144
        return file.get_versions()
145
        
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
146
147 1
    def all_file_versions(self, limit=None):
148
        """ Return all the versions of all files in a given bucket.
149
150
            Params:
151
                limit:              (int) Limit number of results returned (optional). Defaults to 10000
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (104/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
152
153
            Returns dict: 
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
154
                'file_names':       (list) String filenames
155
                'file_ids':         (list) File IDs
156
                'file_versions':    (dict) b2blaze File objects, keyed by file name
157
        """ 
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
158
159
        path = API.list_file_versions
160
        file_versions = dict()
161
        file_names = []
162
        file_ids = []
163
        new_files_to_retrieve = True
164
        params = {
165
            'bucketId': self.bucket.bucket_id,
166
            'maxFileCount': 10000
167
        }
168
169
        # Limit files
170
        if limit:
171
            params['maxFileCount'] = limit
172
173
        while new_files_to_retrieve:
174
175
            response = self.connector.make_request(path=path, method='post', params=params)
176
            if response.status_code == 200:
177
                files_json = response.json()
178
                for file_json in files_json['files']:
179
                    new_file = B2File(connector=self.connector, parent_list=self, **file_json)
180
181
                    # Append file_id, file_name to lists
182
                    file_name, file_id = file_json['fileName'], file_json['fileId']
183
                    file_names.append(file_name)
184
                    file_ids.append(file_id)
185
                    
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
186
                    # Add file to list keyed by file_id
187
                    if file_id in file_versions:
188
                        file_versions[file_id].append(new_file)
189
                    else:
190
                        file_versions[file_id] = [new_file]
191
192
                if files_json['nextFileName'] is None:
193
                    new_files_to_retrieve = False
194
                else:
195
                    params['startFileName'] = files_json['nextFileName']
196
            else:
197
                raise B2Exception.parse(response)
198
        return {'file_names': file_names, 'file_versions': file_versions, 'file_ids': file_ids}
199
200
201 1
    def _get_by_name(self, file_name):
202
        """ Internal method, return single file by file name """ 
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
203
        path = API.list_all_files
204
        params = {
205
            'prefix': b2_url_encode(file_name),
206
            'bucketId': self.bucket.bucket_id
207
        }
208
209
        response = self.connector.make_request(path, method='post', params=params)
210
        file_json = response.json()
211
212
        # Handle errors and empty files
213
        if not response.status_code == 200:
214
            raise B2Exception.parse(response)
215
        if not len(file_json['files']) > 0:
0 ignored issues
show
Unused Code introduced by
Do not use len(SEQUENCE) as condition value
Loading history...
216
            raise B2FileNotFoundError('Filename {} not found'.format(file_name))
217
        else:
218
            return B2File(connector=self.connector, parent_list=self, **file_json['files'][0])
219
220 1
    def _get_by_id(self, file_id):
221
        """ Internal method, return single file by file id """ 
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
222
        path = API.file_info
223
        params = {
224
            'fileId': file_id
225
        }
226
        response = self.connector.make_request(path, method='post', params=params)
227
        if response.status_code == 200:
228
            file_json = response.json()
229
            return B2File(connector=self.connector, parent_list=self, **file_json)
230
        else:
231
            raise B2Exception.parse(response)
232
            
0 ignored issues
show
Coding Style introduced by
Trailing whitespace
Loading history...
233
234 1
    def upload(self, contents, file_name, mime_content_type=None, content_length=None, progress_listener=None):
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (111/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
best-practice introduced by
Too many arguments (6/5)
Loading history...
Unused Code introduced by
The argument mime_content_type seems to be unused.
Loading history...
235
        """
236
237
        :param contents:
238
        :param file_name:
239
        :param mime_content_type:
240
        :param content_length:
241
        :param progress_listener:
242
        :return:
243
        """
244
        if file_name[0] == '/':
245
            file_name = file_name[1:]
246
        get_upload_url_path = API.upload_url
247
        params = {
248
            'bucketId': self.bucket.bucket_id
249
        }
250
        upload_url_response = self.connector.make_request(path=get_upload_url_path, method='post', params=params)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (113/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
251
        if upload_url_response.status_code == 200:
252
            upload_url = upload_url_response.json().get('uploadUrl', None)
253
            auth_token = upload_url_response.json().get('authorizationToken', None)
254
            upload_response = self.connector.upload_file(file_contents=contents, file_name=file_name,
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (101/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
255
                                                         upload_url=upload_url, auth_token=auth_token,
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (102/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
256
                                                         content_length=content_length, progress_listener=progress_listener)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (124/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
257
            if upload_response.status_code == 200:
258
                new_file = B2File(connector=self.connector, parent_list=self, **upload_response.json())
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (103/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
259
                # Update file list after upload
260
                self._update_files_list()
261
                return new_file
262
            else:
263
                raise B2Exception.parse(upload_response)
264
        else:
265
            raise B2Exception.parse(upload_url_response)
266
267 1
    def upload_large_file(self, contents, file_name, part_size=None, num_threads=4,
0 ignored issues
show
best-practice introduced by
Too many arguments (8/5)
Loading history...
Comprehensibility introduced by
This function exceeds the maximum number of variables (19/15).
Loading history...
268
                          mime_content_type=None, content_length=None, progress_listener=None):
269
        """
270
271
        :param contents:
272
        :param file_name:
273
        :param part_size:
274
        :param num_threads:
275
        :param mime_content_type:
276
        :param content_length:
277
        :param progress_listener:
278
        :return:
279
        """
280
        if file_name[0] == '/':
281
            file_name = file_name[1:]
282
        if part_size == None:
0 ignored issues
show
introduced by
Comparison to None should be 'expr is None'
Loading history...
283
            part_size = self.connector.recommended_part_size
284
        if content_length == None:
0 ignored issues
show
introduced by
Comparison to None should be 'expr is None'
Loading history...
285
            content_length = get_content_length(contents)
286
        start_large_file_path = API.upload_large
287
        params = {
288
            'bucketId': self.bucket.bucket_id,
289
            'fileName': b2_url_encode(file_name),
290
            'contentType': mime_content_type or 'b2/x-auto'
291
        }
292
        large_file_response = self.connector.make_request(path=start_large_file_path, method='post', params=params)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (115/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
293
        if large_file_response.status_code == 200:
294
            file_id = large_file_response.json().get('fileId', None)
295
            get_upload_part_url_path = API.upload_large_part
296
            params = {
297
                'fileId': file_id
298
            }
299
            pool = ThreadPool(num_threads)
300
            def upload_part_worker(args):
0 ignored issues
show
Coding Style introduced by
This function should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
301
                part_number, part_range = args
302
                offset, content_length = part_range
303
                with open(contents.name, 'rb') as file:
0 ignored issues
show
Bug Best Practice introduced by
This seems to re-define the built-in file.

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

Loading history...
304
                    file.seek(offset)
305
                    stream = RangeStream(file, offset, content_length)
306
                    upload_part_url_response = self.connector.make_request(path=get_upload_part_url_path, method='post', params=params)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (135/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
307
                    if upload_part_url_response.status_code == 200:
308
                        upload_url = upload_part_url_response.json().get('uploadUrl')
309
                        auth_token = upload_part_url_response.json().get('authorizationToken')
310
                        upload_part_response = self.connector.upload_part(file_contents=stream, content_length=content_length,
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (126/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
311
                                                                          part_number=part_number, upload_url=upload_url,
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (121/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
312
                                                                          auth_token=auth_token, progress_listener=progress_listener)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (133/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
313
                        if upload_part_response.status_code == 200:
314
                            return upload_part_response.json().get('contentSha1', None)
315
                        else:
316
                            raise B2Exception.parse(upload_part_response)
317
                    else:
318
                        raise B2Exception.parse(upload_part_url_response)
319
            sha_list = pool.map(upload_part_worker, enumerate(get_part_ranges(content_length, part_size), 1))
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (109/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
320
            pool.close()
321
            pool.join()
322
            finish_large_file_path = API.upload_large_finish
323
            params = {
324
                'fileId': file_id,
325
                'partSha1Array': sha_list
326
            }
327
            finish_large_file_response = self.connector.make_request(path=finish_large_file_path, method='post', params=params)
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (127/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
328
            if finish_large_file_response.status_code == 200:
329
                new_file = B2File(connector=self.connector, parent_list=self, **finish_large_file_response.json())
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (114/100).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
330
                return new_file
331
            else:
332
                raise B2Exception.parse(finish_large_file_response)
333
        else:
334
            raise B2Exception.parse(large_file_response)
335