upload_print_callback()   F
last analyzed

Complexity

Conditions 11

Size

Total Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
dl 0
loc 37
rs 3.1764
c 0
b 0
f 0

How to fix   Complexity   

Complexity

Complex classes like upload_print_callback() 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
from __future__ import print_function, absolute_import, unicode_literals
2
3
import base64
4
import json
5
import logging
6
import os
7
import sys
8
import time
9
10
from hashlib import md5
11
12
# re-export parse_version
13
from pkg_resources import parse_version as pv
14
from .spec import PackageSpec, package_specs, parse_specs
15
16
# Re-export config
17
from .config import (get_server_api, dirs, load_token, store_token,
18
                     remove_token, get_config, set_config, load_config,
19
                     get_binstar,
20
                     USER_CONFIG, USER_LOGDIR, SITE_CONFIG, DEFAULT_CONFIG)
21
22
from six.moves import input
23
24
25
logger = logging.getLogger('binstar')
26
27
28
def jencode(*E, **F):
29
    payload = dict(*E, **F)
30
    return json.dumps(payload), {'Content-Type': 'application/json'}
31
32
33
def compute_hash(fp, buf_size=8192, size=None, hash_algorithm=md5):
34
    hash_obj = hash_algorithm()
35
    spos = fp.tell()
36
    if size and size < buf_size:
37
        s = fp.read(size)
38
    else:
39
        s = fp.read(buf_size)
40
    while s:
41
        hash_obj.update(s)
42
        if size:
43
            size -= len(s)
44
            if size <= 0:
45
                break
46
        if size and size < buf_size:
47
            s = fp.read(size)
48
        else:
49
            s = fp.read(buf_size)
50
    hex_digest = hash_obj.hexdigest()
51
52
    b64encode = getattr(base64, 'encodebytes', base64.encodestring)
53
    base64_digest = b64encode(hash_obj.digest())
54
    if base64_digest[-1] == '\n':
55
        base64_digest = base64_digest[0:-1]
56
    # data_size based on bytes read.
57
    data_size = fp.tell() - spos
58
    fp.seek(spos)
59
    return (hex_digest, base64_digest, data_size)
60
61
62
class upload_in_chunks(object):
63
    def __init__(self, fd, chunksize=1 << 13):
64
        self.fd = fd
65
        self.chunksize = chunksize
66
        self.totalsize = os.fstat(fd.fileno()).st_size
67
        self.readsofar = 0
68
69
    def __iter__(self):
70
        sys.stderr.write('Progress:\n')
71
        while True:
72
            data = self.fd.read(self.chunksize)
73
            if not data:
74
                sys.stderr.write("\n")
75
                break
76
            self.readsofar += len(data)
77
            percent = self.readsofar * 1e2 / self.totalsize
78
            sys.stderr.write("\r{percent:3.0f}%".format(percent=percent))
79
            yield data
80
81
    def __len__(self):
82
        return self.totalsize
83
84
85
def upload_with_progress(fd):
86
    it = upload_in_chunks(fd)
87
    IterableToFileAdapter(it)
88
89
90
class IterableToFileAdapter(object):
91
    def __init__(self, iterable):
92
        self.iterator = iter(iterable)
93
        self.length = len(iterable)
94
95
    def read(self, size=-1):  # TBD: add buffer for `len(data) > size` case
96
        return next(self.iterator, b'')
97
98
    def __len__(self):
99
        return self.length
100
101
102
def bool_input(prompt, default=True):
103
    default_str = '[Y|n]' if default else '[y|N]'
104
    while 1:
105
        inpt = input('%s %s: ' % (prompt, default_str))
106
        if inpt.lower() in ['y', 'yes'] and not default:
107
            return True
108
        elif inpt.lower() in ['', 'n', 'no'] and not default:
109
            return False
110
        elif inpt.lower() in ['', 'y', 'yes']:
111
            return True
112
        elif inpt.lower() in ['n', 'no']:
113
            return False
114
        else:
115
            sys.stderr.write('please enter yes or no\n')
116
117
118
WAIT_SECONDS = 15
119
120
121
def upload_print_callback(args):
122
    start_time = time.time()
123
    if args.no_progress or args.log_level > logging.INFO:
124
125
        def callback(curr, total):
126
            perc = 100.0 * curr / total if total else 0
127
128
            if (time.time() - callback.last_output) > WAIT_SECONDS:
129
                print('| %.2f%% ' % (perc), end='')
130
                sys.stdout.flush()
131
                callback.last_output = time.time()
132
133
        callback.last_output = time.time()
134
135
        return callback
136
137
    def callback(curr, total):
138
        curr_time = time.time()
139
        time_delta = curr_time - start_time
140
141
        remain = total - curr
142
        if curr and remain:
143
            eta = 1.0 * time_delta / curr * remain / 60.0
144
        else:
145
            eta = 0
146
147
        curr_kb = curr // 1024
148
        total_kb = total // 1024
149
        perc = 100.0 * curr / total if total else 0
150
151
        msg = '\r uploaded %(curr_kb)i of %(total_kb)iKb: %(perc).2f%% ETA: %(eta).1f minutes'
152
        sys.stderr.write(msg % locals())
153
        sys.stderr.flush()
154
        if curr == total:
155
            sys.stderr.write('\n')
156
157
    return callback
158