8
8
from typing import Any
9
9
10
10
import aiohttp
11
- from aiohttp_xmlrpc .client import ServerProxy
12
11
13
12
import bandersnatch
14
13
from bandersnatch .config .proxy import get_aiohttp_proxy_kwargs , proxy_address_from_env
@@ -28,10 +27,6 @@ class StalePage(Exception):
28
27
"""We got a page back from PyPI that doesn't meet our expected serial."""
29
28
30
29
31
- class XmlRpcError (aiohttp .ClientError ):
32
- """Issue getting package listing from PyPI Repository"""
33
-
34
-
35
30
class Master :
36
31
def __init__ (
37
32
self ,
@@ -141,58 +136,47 @@ async def url_fetch(
141
136
fd .write (chunk )
142
137
143
138
@property
144
- def xmlrpc_url (self ) -> str :
145
- return f"{ self .url } /pypi"
146
-
147
- # TODO: Potentially make USER_AGENT more accessible from aiohttp-xmlrpc
148
- async def _gen_custom_headers (self ) -> dict [str , str ]:
149
- # Create dummy client so we can copy the USER_AGENT + prepend bandersnatch info
150
- dummy_client = ServerProxy (self .xmlrpc_url , loop = self .loop )
151
- custom_headers = {
152
- "User-Agent" : (
153
- f"bandersnatch { bandersnatch .__version__ } { dummy_client .USER_AGENT } "
154
- )
139
+ def simple_url (self ) -> str :
140
+ return f"{ self .url } /simple/"
141
+
142
+ # TODO: Potentially make USER_AGENT more accessible from aiohttp
143
+ @property
144
+ def _custom_headers (self ) -> dict [str , str ]:
145
+ return {
146
+ "User-Agent" : f"bandersnatch { bandersnatch .__version__ } " ,
147
+ # the simple API use headers to return JSON
148
+ "Accept" : "application/vnd.pypi.simple.v1+json" ,
155
149
}
156
- await dummy_client .close ()
157
- return custom_headers
158
-
159
- async def _gen_xmlrpc_client (self ) -> ServerProxy :
160
- custom_headers = await self ._gen_custom_headers ()
161
- client = ServerProxy (
162
- self .xmlrpc_url ,
163
- client = self .session ,
164
- loop = self .loop ,
165
- headers = custom_headers ,
166
- )
167
- return client
168
150
169
- # TODO: Add an async context manager to aiohttp-xmlrpc to replace this function
170
- async def rpc (self , method_name : str , serial : int = 0 ) -> Any :
171
- try :
172
- client = await self ._gen_xmlrpc_client ()
173
- method = getattr (client , method_name )
174
- if serial :
175
- return await method (serial )
176
- return await method ()
177
- except TimeoutError as te :
178
- logger .error (f"Call to { method_name } @ { self .xmlrpc_url } timed out: { te } " )
151
+ async def fetch_simple_index (self ) -> Any :
152
+ """Return a mapping of all project data from the PyPI Index API"""
153
+ logger .debug (f"Fetching simple JSON index from { self .simple_url } " )
154
+ async with self .session .get (
155
+ self .simple_url , headers = self ._custom_headers
156
+ ) as response :
157
+ simple_index = await response .json ()
158
+ return simple_index
179
159
180
160
async def all_packages (self ) -> Any :
181
- all_packages_with_serial = await self .rpc ("list_packages_with_serial" )
182
- if not all_packages_with_serial :
183
- raise XmlRpcError ("Unable to get full list of packages" )
184
- return all_packages_with_serial
161
+ """Return a mapping of all project names as {name: last_serial}"""
162
+ simple_index = await self .fetch_simple_index ()
163
+ if not simple_index :
164
+ return {}
165
+ all_packages = {
166
+ project ["name" ]: project ["_last-serial" ]
167
+ for project in simple_index ["projects" ]
168
+ }
169
+ logger .debug (f"Fetched #{ len (all_packages )} from simple JSON index" )
170
+ return all_packages
185
171
186
172
async def changed_packages (self , last_serial : int ) -> dict [str , int ]:
187
- changelog = await self .rpc ("changelog_since_serial" , last_serial )
188
- if changelog is None :
189
- changelog = []
190
-
191
- packages : dict [str , int ] = {}
192
- for package , _version , _time , _action , serial in changelog :
193
- if serial > packages .get (package , 0 ):
194
- packages [package ] = serial
195
- return packages
173
+ """Return a mapping of all project names changed since last serial as {name: last_serial}"""
174
+ all_packages = await self .all_packages ()
175
+ changed_packages = {
176
+ pkg : ser for pkg , ser in all_packages .items () if ser > last_serial
177
+ }
178
+ logger .debug (f"Fetched #{ len (changed_packages )} changed packages" )
179
+ return changed_packages
196
180
197
181
async def get_package_metadata (self , package_name : str , serial : int = 0 ) -> Any :
198
182
try :
0 commit comments