Completed
Pull Request — develop (#344)
by Gonzalo
01:27
created

inspect_conda_package()   F

Complexity

Conditions 15

Size

Total Lines 74

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
cc 15
dl 0
loc 74
rs 2.3086
c 4
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like inspect_conda_package() 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
2
3
# Standard library imports
4
from os import path
5
from pprint import pprint
6
import json
7
import re
8
import sys
9
import tarfile
10
import tempfile
11
12
# Local imports
13
from ..utils.notebook.data_uri import data_uri_from
14
15
16
os_map = {'osx': 'darwin', 'win': 'win32'}
17
specs_re = re.compile('^([=><]+)(.*)$')
18
19
20
def transform_conda_deps(deps):
21
    """
22
    Format dependencies into a common binstar format
23
    """
24
    depends = []
25
    for dep in deps:
26
        dep = dep.strip()
27
        name_spec = dep.split(' ', 1)
28
        if len(name_spec) == 1:
29
            name, = name_spec
30
            depends.append({'name':name, 'specs': []})
31
        elif len(name_spec) == 2:
32
            name, spec = name_spec
33
            if spec.endswith('*'):  # Star does nothing in semver
34
                spec = spec[:-1]
35
36
            match = specs_re.match(spec)
37
            if match:
38
                op, spec = match.groups()
39
            else:
40
                op = '=='
41
42
            depends.append({'name':name, 'specs': [[op, spec]]})
43
        elif len(name_spec) == 3:
44
            name, spec, build_str = name_spec
45
            if spec.endswith('*'):  # Star does nothing in semver
46
                spec = spec[:-1]
47
48
            match = specs_re.match(spec)
49
            if match:
50
                op, spec = match.groups()
51
            else:
52
                op = '=='
53
54
            depends.append({'name':name, 'specs': [['==', '%s+%s' % (spec, build_str)]]})
55
56
    return {'depends': depends}
57
58
59
def get_subdir(index):
60
    """
61
    Return the sub-directory given the index dictionary.  The return
62
    value is obtained in the following order:
63
64
    1. when the 'subdir' key exists, it's value is returned
65
    2. if the 'arch' is None, or does not exist, 'noarch' is returned
66
    3. otherwise, the return value is constructed from the 'platform' key
67
       and the 'arch' key (where 'x86' is replaced by '32',
68
       and 'x86_64' by '64')
69
    """
70
    try:
71
        return index['subdir']
72
    except KeyError:
73
        arch = index.get('arch')
74
        if arch is None:
75
            return 'noarch'
76
        intel_map = {'x86': '32', 'x86_64': '64'}
77
        return '%s-%s' % (index.get('platform'), intel_map.get(arch, arch))
78
79
80
def inspect_conda_package(filename, fileobj, *args, **kwargs):
81
82
    index, recipe, has_prefix = None, {}, False
83
84
    with tarfile.open(filename, fileobj=fileobj, mode="r|bz2") as tar:
85
        for info in tar:
86
            if info.name == 'info/index.json':
87
                index = tar.extractfile(info)
88
                index = json.loads(index.read().decode())
89
            elif info.name == 'info/recipe.json':
90
                recipe = tar.extractfile(info)
91
                recipe = json.loads(recipe.read().decode())
92
            elif info.name == 'info/has_prefix':
93
                has_prefix = True
94
            if index is not None and recipe != {}:
95
                break
96
        else:
97
            if index is None:
98
                raise TypeError("info/index.json required in conda package")
99
100
    # Load icon if defined in the recipe app section and file exists inside
101
    # recipe folder
102
    fileobj.seek(0)
103
    icon_b64 = None
104
    app = recipe.get('app')
105
    if app:
106
        icon_path = app.get('icon')
107
        if icon_path:
108
            tar = tarfile.open(filename, fileobj=fileobj, mode="r|bz2")
109
            for info in tar:
110
                if info.name == 'info/recipe/{0}'.format(icon_path):
111
                    icon_data = tar.extractfile(info).read()
112
                    f, temp_path = tempfile.mkstemp()
113
                    with open(temp_path, 'wb') as f:
114
                        f.write(icon_data)
115
                    icon_b64 = data_uri_from(temp_path)
116
                    break
117
118
    about = recipe.pop('about', {})
119
120
    subdir = get_subdir(index)
121
122
    machine = index['arch']
123
    operatingsystem = os_map.get(index['platform'], index['platform'])
124
125
    package_data = {
126
                    'name': index.pop('name'),
127
                    # Should this info be removed and moved to the release?
128
                    'summary': about.get('summary', ''),
129
                    'license': about.get('license'),
130
                    }
131
    release_data = {
132
                    'version': index.pop('version'),
133
                    'home_page': about.get('home'),
134
                    'description': '',
135
                    # Add summary and license as per release attributes?
136
                    # 'summary': about.get('summary', ''),
137
                    # 'license': about.get('license'),
138
                    'icon': icon_b64,
139
                    }
140
    file_data = {
141
                'basename': '%s/%s' % (subdir, path.basename(filename)),
142
                'attrs': {
143
                        'operatingsystem': operatingsystem,
144
                        'machine': machine,
145
                        'target-triplet': '%s-any-%s' % (machine, operatingsystem),
146
                        'has_prefix': has_prefix
147
                         }
148
                 }
149
150
    file_data['attrs'].update(index)
151
    conda_depends = index.get('depends', index.get('requires', []))
152
    file_data['dependencies'] = transform_conda_deps(conda_depends)
153
    return package_data, release_data, file_data
154
155
156
def main():
157
    filename = sys.argv[1]
158
    with open(filename) as fileobj:
159
        package_data, release_data, file_data = inspect_conda_package(filename, fileobj)
160
    pprint(package_data)
161
    print('--')
162
    pprint(release_data)
163
    print('--')
164
    pprint(file_data)
165
166
if __name__ == '__main__':
167
    main()
168