inspect_conda_package()   F
last analyzed

Complexity

Conditions 15

Size

Total Lines 91

Duplication

Lines 0
Ratio 0 %

Importance

Changes 7
Bugs 0 Features 2
Metric Value
cc 15
c 7
b 0
f 2
dl 0
loc 91
rs 2

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, about, 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.index is deprecated and only packages built with older
91
                # versions of conda-build contain that file.
92
                recipe = tar.extractfile(info)
93
                recipe = json.loads(recipe.read().decode())
94
                about = recipe.pop('about', {})
95
            elif info.name == 'info/about.json':
96
                # recipe.json is deprecated and only packages build with older
97
                # versions of conda-build contain that file.
98
                about = tar.extractfile(info)
99
                about = json.loads(about.read().decode())
100
            elif info.name == 'info/has_prefix':
101
                has_prefix = True
102
            if index is not None and about != {}:
103
                break
104
        else:
105
            if index is None:
106
                raise TypeError("info/index.json required in conda package")
107
108
    # Load icon defined in the index.json and file exists inside info folder
109
    fileobj.seek(0)
110
    icon_b64 = None
111
    icon_path = index.get('icon')
112
    if icon_path:
113
        tar = tarfile.open(filename, fileobj=fileobj, mode="r|bz2")
114
        for info in tar:
115
            if info.name == 'info/{0}'.format(icon_path):
116
                icon_data = tar.extractfile(info).read()
117
                f, temp_path = tempfile.mkstemp()
118
                with open(temp_path, 'wb') as f:
119
                    f.write(icon_data)
120
                icon_b64 = data_uri_from(temp_path)
121
                break
122
123
    subdir = get_subdir(index)
124
125
    machine = index['arch']
126
    operatingsystem = os_map.get(index['platform'], index['platform'])
127
128
    package_data = {
129
        'name': index.pop('name'),
130
        # TODO: this info should be removed and moved to release
131
        'summary': about.get('summary', ''),
132
        'description': about.get('description', ''),
133
        'license': about.get('license'),
134
        'license_url': about.get('license_url'),
135
        'license_family': about.get('license_family'),
136
        'dev_url': about.get('dev_url'),
137
        'doc_url': about.get('doc_url'),
138
        'home': about.get('home'),
139
        'source_git_url': about.get('source_git_url'),
140
    }
141
    release_data = {
142
        'version': index.pop('version'),
143
        'home_page': about.get('home'),
144
        'description': about.get('description', ''),
145
        'summary': about.get('summary', ''),
146
        'dev_url': about.get('dev_url'),
147
        'doc_url': about.get('doc_url'),
148
        'home': about.get('home'),
149
        'source_git_url': about.get('source_git_url'),
150
        'source_git_tag': about.get('source_git_tag'),
151
        'icon': icon_b64,
152
        'license': about.get('license'),
153
        'license_url': about.get('license_url'),
154
        'license_family': about.get('license_family'),
155
    }
156
    file_data = {
157
        'basename': '%s/%s' % (subdir, path.basename(filename)),
158
        'attrs': {
159
            'operatingsystem': operatingsystem,
160
            'machine': machine,
161
            'target-triplet': '%s-any-%s' % (machine, operatingsystem),
162
            'has_prefix': has_prefix
163
        }
164
    }
165
166
    file_data['attrs'].update(index)
167
    conda_depends = index.get('depends', index.get('requires', []))
168
    file_data['dependencies'] = transform_conda_deps(conda_depends)
169
170
    return package_data, release_data, file_data
171
172
173
def main():
174
    filename = sys.argv[1]
175
    with open(filename) as fileobj:
176
        package_data, release_data, file_data = inspect_conda_package(filename, fileobj)
177
    pprint(package_data)
178
    print('--')
179
    pprint(release_data)
180
    print('--')
181
    pprint(file_data)
182
183
if __name__ == '__main__':
184
    main()
185