1
1
# encoding: utf-8
2
2
import hashlib
3
+ import json
3
4
import logging
4
5
from copy import copy
5
6
from slimurl import URL
16
17
log = logging .getLogger (__name__ )
17
18
18
19
20
+ def async_retrying (number , exceptions = (Exception ,)):
21
+ def decorator (func ):
22
+ @coroutine
23
+ def wrap (* args , ** kwargs ):
24
+ last_exc = None
25
+ for i in range (number ):
26
+ try :
27
+ raise Return ((yield func (* args , ** kwargs )))
28
+ except Return :
29
+ raise
30
+ except exceptions as e :
31
+ log .exception ("Error on attempt: %r" , i )
32
+ last_exc = e
33
+
34
+ if last_exc :
35
+ raise last_exc
36
+ return wrap
37
+ return decorator
38
+
39
+
19
40
def normalize_package_name (name ):
20
41
return name .lower ().replace ("_" , "-" ).replace ("." , "-" )
21
42
@@ -26,19 +47,20 @@ class PYPIClient(object):
26
47
THREAD_POOL = None
27
48
INDEX = None
28
49
XMLRPC = None
50
+ RPC_URL = None
29
51
LOCK = None
30
52
31
53
@classmethod
32
54
def configure (cls , backend , thread_pool ):
33
55
cls .CLIENT = AsyncHTTPClient (io_loop = IOLoop .current ())
34
56
cls .BACKEND = backend
35
57
cls .THREAD_POOL = thread_pool
36
- cls .XMLRPC = ServerProxy (
37
- str (copy (backend )(path = "/pypi" )),
38
- )
58
+ cls .RPC_URL = copy (backend )(path = "/pypi" )
59
+ cls .XMLRPC = ServerProxy (str (cls .RPC_URL ))
39
60
cls .LOCK = Lock ()
40
61
41
62
@classmethod
63
+ @async_retrying (5 )
42
64
@coroutine
43
65
@Cache (HOUR , files_cache = True , ignore_self = True )
44
66
def packages (cls ):
@@ -54,6 +76,7 @@ def packages(cls):
54
76
raise Return (index )
55
77
56
78
@classmethod
79
+ @async_retrying (5 )
57
80
@coroutine
58
81
@Cache (4 * HOUR , files_cache = True , ignore_self = True )
59
82
def search (cls , names , descriptions , operator = "or" ):
@@ -62,6 +85,7 @@ def search(cls, names, descriptions, operator="or"):
62
85
raise Return (result )
63
86
64
87
@classmethod
88
+ @async_retrying (5 )
65
89
@coroutine
66
90
def exists (cls , name ):
67
91
try :
@@ -76,6 +100,7 @@ def exists(cls, name):
76
100
raise Return (True )
77
101
78
102
@classmethod
103
+ @async_retrying (5 )
79
104
@coroutine
80
105
def find_real_name (cls , name ):
81
106
if not options .pypi_proxy :
@@ -92,6 +117,7 @@ def find_real_name(cls, name):
92
117
raise Return (real_name )
93
118
94
119
@classmethod
120
+ @async_retrying (5 )
95
121
@coroutine
96
122
@Cache (4 * HOUR , files_cache = True , ignore_self = True )
97
123
def releases (cls , name ):
@@ -119,15 +145,20 @@ def releases(cls, name):
119
145
raise Return (set (res ))
120
146
121
147
@classmethod
148
+ @async_retrying (5 )
122
149
@coroutine
123
150
@Cache (MONTH , files_cache = True , ignore_self = True )
124
151
def release_data (cls , name , version ):
125
- info , files = yield [
126
- cls .XMLRPC .release_data (str (name ), str (version )),
127
- cls .XMLRPC .release_urls (str (name ), str (version ))
128
- ]
152
+ url = copy (cls .RPC_URL )
153
+ url .path_append (str (name ), str (version ), 'json' )
154
+ log .info ("Gathering info %s" , url )
155
+
156
+ response = json .loads ((yield cls .CLIENT .fetch (str (url ))).body )
157
+ info = response ['info' ]
158
+ files = response ['urls' ]
129
159
130
160
download_url = info .get ('download_url' )
161
+
131
162
if download_url and not files :
132
163
try :
133
164
url = URL (download_url )
0 commit comments