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.
Completed
Pull Request — master (#87)
by Gonzalo
01:34
created

_ManagerAPI   B

Complexity

Total Complexity 37

Size/Duplication

Total Lines 260
Duplicated Lines 0 %

Importance

Changes 9
Bugs 0 Features 0
Metric Value
c 9
b 0
f 0
dl 0
loc 260
rs 8.6
wmc 37
1
# -*- coding: utf-8 -*-
2
# -----------------------------------------------------------------------------
3
# Copyright © 2015- The Spyder Development Team
4
# Copyright © 2014-2015 Gonzalo Peña-Castellanos (@goanpeca)
5
#
6
# Licensed under the terms of the MIT License
7
# -----------------------------------------------------------------------------
8
"""API for using the api (anaconda-client, downloads and conda)."""
9
10
# Standard library imports
11
import json
12
import os
13
import tempfile
14
15
# Third party imports
16
from qtpy.QtCore import QObject, Signal
17
18
# Local imports
19
from conda_manager.api.client_api import ClientAPI
20
from conda_manager.api.conda_api import CondaAPI
21
from conda_manager.api.download_api import DownloadAPI, RequestsDownloadAPI
22
23
24
class _ManagerAPI(QObject):
25
    """Anaconda Manager API process worker."""
26
27
    sig_repodata_updated = Signal(object)
28
    sig_repodata_errored = Signal()
29
30
    def __init__(self):
31
        """Anaconda Manager API process worker."""
32
        super(_ManagerAPI, self).__init__()
33
34
        # API's
35
        self._conda_api = CondaAPI()
36
        self._client_api = ClientAPI()
37
        self._download_api = DownloadAPI(load_rc_func=self._conda_api.load_rc)
38
        self._requests_download_api = RequestsDownloadAPI(
39
            load_rc_func=self._conda_api.load_rc)
40
        self.ROOT_PREFIX = self._conda_api.ROOT_PREFIX
41
42
        # Vars
43
        self._checking_repos = None
44
        self._data_directory = None
45
        self._files_downloaded = None
46
        self._repodata_files = None
47
        self._valid_repos = None
48
49
        # Expose some methods for convenient access. Methods return a worker
50
        self.conda_create = self._conda_api.create
51
        self.conda_create_yaml = self._conda_api.create_from_yaml
52
        self.conda_clone = self._conda_api.clone_environment
53
        self.conda_dependencies = self._conda_api.dependencies
54
        self.conda_get_condarc_channels = self._conda_api.get_condarc_channels
55
        self.conda_install = self._conda_api.install
56
        self.conda_remove = self._conda_api.remove
57
        self.conda_terminate = self._conda_api.terminate_all_processes
58
        self.conda_config_add = self._conda_api.config_add
59
        self.conda_config_remove = self._conda_api.config_remove
60
        self.pip_list = self._conda_api.pip_list
61
        self.pip_remove = self._conda_api.pip_remove
62
63
        # No workers are returned for these methods
64
        self.conda_clear_lock = self._conda_api.clear_lock
65
        self.conda_environment_exists = self._conda_api.environment_exists
66
        self.conda_get_envs = self._conda_api.get_envs
67
        self.conda_linked = self._conda_api.linked
68
        self.conda_get_prefix_envname = self._conda_api.get_prefix_envname
69
        self.conda_package_version = self._conda_api.package_version
70
        self.conda_platform = self._conda_api.get_platform
71
72
        # These download methods return a worker
73
        get_api_info = self._requests_download_api.get_api_info
74
        is_valid_url = self._requests_download_api.is_valid_api_url
75
        is_valid_channel = self._requests_download_api.is_valid_channel
76
        terminate = self._requests_download_api.terminate
77
        self.download_requests = self._requests_download_api.download
78
        self.download_async = self._download_api.download
79
        self.download_async_terminate = self._download_api.terminate
80
        self.download_is_valid_url = self._requests_download_api.is_valid_url
81
        self.download_is_valid_api_url = is_valid_url
82
        self.download_get_api_info = lambda: get_api_info(
83
            self._client_api.get_api_url())
84
        self.download_is_valid_channel = is_valid_channel
85
        self.download_requests_terminate = terminate
86
87
        # These client methods return a worker
88
        self.client_store_token = self._client_api.store_token
89
        self.client_remove_token = self._client_api.remove_token
90
        self.client_login = self._client_api.login
91
        self.client_logout = self._client_api.logout
92
        self.client_load_repodata = self._client_api.load_repodata
93
        self.client_prepare_packages_data = self._client_api.prepare_model_data
94
        self.client_user = self._client_api.user
95
        self.client_domain = self._client_api.domain
96
        self.client_set_domain = self._client_api.set_domain
97
        self.client_packages = self._client_api.packages
98
        self.client_multi_packages = self._client_api.multi_packages
99
        self.client_organizations = self._client_api.organizations
100
        self.client_load_token = self._client_api.load_token
101
        self.client_get_api_url = self._client_api.get_api_url
102
        self.client_set_api_url = self._client_api.set_api_url
103
104
    # --- Helper methods
105
    # -------------------------------------------------------------------------
106
    def _set_repo_urls_from_channels(self, channels):
107
        """
108
        Convert a channel into a normalized repo name including.
109
110
        Channels are assumed in normalized url form.
111
        """
112
        repos = []
113
        sys_platform = self._conda_api.get_platform()
114
115
        for channel in channels:
116
            url = '{0}/{1}/repodata.json.bz2'.format(channel, sys_platform)
117
            repos.append(url)
118
119
        return repos
120
121
    def _check_repos(self, repos):
122
        """Check if repodata urls are valid."""
123
        self._checking_repos = []
124
        self._valid_repos = []
125
126
        for repo in repos:
127
            worker = self.download_is_valid_url(repo)
128
            worker.sig_finished.connect(self._repos_checked)
129
            worker.repo = repo
130
            self._checking_repos.append(repo)
131
132
    def _repos_checked(self, worker, output, error):
133
        """Callback for _check_repos."""
134
        if worker.repo in self._checking_repos:
135
            self._checking_repos.remove(worker.repo)
136
137
        if output:
138
            self._valid_repos.append(worker.repo)
139
140
        if len(self._checking_repos) == 0:
141
            self._download_repodata(self._valid_repos)
142
143
    def _repo_url_to_path(self, repo):
144
        """Convert a `repo` url to a file path for local storage."""
145
        repo = repo.replace('http://', '')
146
        repo = repo.replace('https://', '')
147
        repo = repo.replace('/', '_')
148
149
        return os.sep.join([self._data_directory, repo])
150
151
    def _download_repodata(self, checked_repos):
152
        """Dowload repodata."""
153
        self._files_downloaded = []
154
        self._repodata_files = []
155
        self.__counter = -1
156
157
        if checked_repos:
158
            for repo in checked_repos:
159
                path = self._repo_url_to_path(repo)
160
                self._files_downloaded.append(path)
161
                self._repodata_files.append(path)
162
                worker = self.download_async(repo, path)
163
                worker.url = repo
164
                worker.path = path
165
                worker.sig_finished.connect(self._repodata_downloaded)
166
        else:
167
            # Empty, maybe there is no internet connection
168
            # Load information from conda-meta and save that file
169
            path = self._get_repodata_from_meta()
170
            self._repodata_files = [path]
171
            self._repodata_downloaded()
172
173
    def _get_repodata_from_meta(self):
174
        """Generate repodata from local meta files."""
175
        path = os.sep.join([self.ROOT_PREFIX, 'conda-meta'])
176
        packages = os.listdir(path)
177
        meta_repodata = {}
178
        for pkg in packages:
179
            if pkg.endswith('.json'):
180
                filepath = os.sep.join([path, pkg])
181
                with open(filepath, 'r') as f:
182
                    data = json.load(f)
183
184
                if 'files' in data:
185
                    data.pop('files')
186
                if 'icondata' in data:
187
                    data.pop('icondata')
188
189
                name = pkg.replace('.json', '')
190
                meta_repodata[name] = data
191
192
        meta_repodata_path = os.sep.join([self._data_directory,
193
                                          'offline.json'])
194
        repodata = {'info': [],
195
                    'packages': meta_repodata}
196
197
        with open(meta_repodata_path, 'w') as f:
198
            json.dump(repodata, f, sort_keys=True,
199
                      indent=4, separators=(',', ': '))
200
201
        return meta_repodata_path
202
203
    def _repodata_downloaded(self, worker=None, output=None, error=None):
204
        """Callback for _download_repodata."""
205
        if worker:
206
            self._files_downloaded.remove(worker.path)
207
208
            if worker.path in self._files_downloaded:
209
                self._files_downloaded.remove(worker.path)
210
211
        if len(self._files_downloaded) == 0:
212
            self.sig_repodata_updated.emit(list(set(self._repodata_files)))
213
214
    # --- Public API
215
    # -------------------------------------------------------------------------
216
    def repodata_files(self, channels=None):
217
        """
218
        Return the repodata paths based on `channels` and the `data_directory`.
219
220
        There is no check for validity here.
221
        """
222
        if channels is None:
223
            channels = self.conda_get_condarc_channels()
224
225
        repodata_urls = self._set_repo_urls_from_channels(channels)
226
227
        repopaths = []
228
229
        for repourl in repodata_urls:
230
            fullpath = os.sep.join([self._repo_url_to_path(repourl)])
231
            repopaths.append(fullpath)
232
233
        return repopaths
234
235
    def set_data_directory(self, data_directory):
236
        """Set the directory where repodata and metadata are stored."""
237
        self._data_directory = data_directory
238
239
    def update_repodata(self, channels=None):
240
        """Update repodata from channels or use condarc channels if None."""
241
        norm_channels = self.conda_get_condarc_channels(channels=channels,
242
                                                        normalize=True)
243
        repodata_urls = self._set_repo_urls_from_channels(norm_channels)
244
        self._check_repos(repodata_urls)
245
246
    def update_metadata(self):
247
        """
248
        Update the metadata available for packages in repo.continuum.io.
249
250
        Returns a download worker.
251
        """
252
        if self._data_directory is None:
253
            raise Exception('Need to call `api.set_data_directory` first.')
254
255
        metadata_url = 'https://repo.continuum.io/pkgs/metadata.json'
256
        filepath = os.sep.join([self._data_directory, 'metadata.json'])
257
        worker = self.download_requests(metadata_url, filepath)
258
        return worker
259
260
    def check_valid_channel(self,
261
                            channel,
262
                            conda_url='https://conda.anaconda.org'):
263
        """Check if channel is valid."""
264
        if channel.startswith('https://') or channel.startswith('http://'):
265
            url = channel
266
        else:
267
            url = "{0}/{1}".format(conda_url, channel)
268
269
        if url[-1] == '/':
270
            url = url[:-1]
271
        plat = self.conda_platform()
272
        repodata_url = "{0}/{1}/{2}".format(url, plat, 'repodata.json')
273
        worker = self.download_is_valid_url(repodata_url)
274
        worker.url = url
275
        return worker
276
277
278
MANAGER_API = None
279
280
281
def ManagerAPI():
282
    """Manager API threaded worker."""
283
    global MANAGER_API
284
285
    if MANAGER_API is None:
286
        MANAGER_API = _ManagerAPI()
287
288
    return MANAGER_API
289
290
291
# --- Local testing
292
# -----------------------------------------------------------------------------
293
def finished(worker, output, error):  # pragma: no cover
294
    """Print information on test finished."""
295
    print(worker, output, error)
296
297
298
def download_finished(url, path):  # pragma: no cover
299
    """Print information on downlaod finished."""
300
    print(url, path)
301
302
303
def repodata_updated(repos):  # pragma: no cover
304
    """Print information on repodata updated."""
305
    print(repos)
306
307
308
def test():  # pragma: no cover
309
    """Main local test."""
310
    from conda_manager.utils.qthelpers import qapplication
311
312
    app = qapplication()
313
    api = ManagerAPI()
314
    api.sig_repodata_updated.connect(repodata_updated)
315
    data_directory = tempfile.mkdtemp()
316
    api.set_data_directory(data_directory)
317
    worker = api.update_metadata()
318
    worker.sig_download_finished.connect(download_finished)
319
    api.update_repodata()
320
    app.exec_()
321
322
323
if __name__ == '__main__':  # pragma: no cover
324
    test()
325