2323import asyncio
2424import dataclasses
2525import pytz
26+ import xml .etree .ElementTree as ET
2627from bs4 import BeautifulSoup
27- from dateutil import parser
28+ from dateutil import parser as dateparser
2829from json import JSONDecodeError
2930from typing import Mapping
30- from typing import Tuple
3131from typing import Set
32+ from typing import List
3233from datetime import datetime
3334
3435from aiohttp import ClientSession
@@ -122,13 +123,19 @@ async def fetch(self, pkg, session):
122123 try :
123124 response = await session .request (method = "GET" , url = url )
124125 response = await response .json ()
125- for version in response ["releases" ]:
126- if response ["releases" ][version ]:
126+ for version , download_items in response ["releases" ].items ():
127+ if download_items :
128+ latest_download_item = max (
129+ download_items ,
130+ key = lambda download_item : dateparser .parse (
131+ download_item ["upload_time_iso_8601" ]
132+ ),
133+ )
127134 versions .add (
128135 Version (
129136 value = version ,
130- release_date = parser .parse (
131- response [ "releases" ][ version ][ - 1 ] ["upload_time_iso_8601" ]
137+ release_date = dateparser .parse (
138+ latest_download_item ["upload_time_iso_8601" ]
132139 ),
133140 )
134141 )
@@ -157,7 +164,8 @@ async def fetch(self, pkg, session):
157164 for version_info in response ["versions" ]:
158165 versions .add (
159166 Version (
160- value = version_info ["num" ], release_date = parser .parse (version_info ["updated_at" ])
167+ value = version_info ["num" ],
168+ release_date = dateparser .parse (version_info ["updated_at" ]),
161169 )
162170 )
163171
@@ -183,7 +191,8 @@ async def fetch(self, pkg, session):
183191 for release in response :
184192 versions .add (
185193 Version (
186- value = release ["number" ], release_date = parser .parse (release ["created_at" ])
194+ value = release ["number" ],
195+ release_date = dateparser .parse (release ["created_at" ]),
187196 )
188197 )
189198 except (ClientResponseError , JSONDecodeError ):
@@ -211,7 +220,7 @@ async def fetch(self, pkg, session):
211220 for version in response .get ("versions" , []):
212221 release_date = response .get ("time" , {}).get (version )
213222 if release_date :
214- release_date = parser .parse (release_date )
223+ release_date = dateparser .parse (release_date )
215224 versions .add (Version (value = version , release_date = release_date ))
216225 else :
217226 versions .add (Version (value = version , release_date = None ))
@@ -265,15 +274,12 @@ class MavenVersionAPI(VersionAPI):
265274 async def load_api (self , pkg_set ):
266275 async with client_session () as session :
267276 await asyncio .gather (
268- * [
269- self .fetch (pkg , session )
270- for pkg in pkg_set
271- if pkg not in self .cache and "camel" not in pkg
272- ]
277+ * [self .fetch (pkg , session ) for pkg in pkg_set if pkg not in self .cache ]
273278 )
274279
275280 async def fetch (self , pkg , session ) -> None :
276- endpoint = self .artifact_url (pkg )
281+ artifact_comps = pkg .split (":" )
282+ endpoint = self .artifact_url (artifact_comps )
277283 try :
278284 resp = await session .request (method = "GET" , url = endpoint )
279285 resp = await resp .read ()
@@ -282,15 +288,11 @@ async def fetch(self, pkg, session) -> None:
282288 self .cache [pkg ] = set ()
283289 return
284290
285- soup = BeautifulSoup (resp , features = "lxml" )
286- try :
287- self .cache [pkg ] = self .extract_versions (soup )
288- except :
289- raise
291+ xml_resp = ET .ElementTree (ET .fromstring (resp .decode ("utf-8" )))
292+ self .cache [pkg ] = self .extract_versions (xml_resp )
290293
291294 @staticmethod
292- def artifact_url (pkg : str ) -> str :
293- artifact_comps = pkg .split (":" )
295+ def artifact_url (artifact_comps : List [str ]) -> str :
294296 base_url = "https://repo1.maven.org/maven2/{}"
295297 try :
296298 group_id , artifact_id = artifact_comps
@@ -306,35 +308,19 @@ def artifact_url(pkg: str) -> str:
306308 raise
307309
308310 group_url = group_id .replace ("." , "/" )
309- suffix = group_url + "/" + artifact_id + "/"
311+ suffix = group_url + "/" + artifact_id + "/" + "maven-metadata.xml"
310312 endpoint = base_url .format (suffix )
311313
312314 return endpoint
313315
314316 @staticmethod
315- def extract_versions (soup : BeautifulSoup ) -> Set [Version ]:
316- pre_tag = soup .find ("pre" )
317- prev_tag = None
318- versions = set ()
319- for i , atag in enumerate (pre_tag ):
320- if atag .name == "a" and i != 0 :
321- prev_tag = atag
322- elif prev_tag :
323- text_groups = atag .split ()
324- if text_groups [- 1 ] != "-" :
325- break
326- date = " " .join (text_groups [:- 1 ])
327- if date != "-" :
328- versions .add (
329- Version (
330- value = prev_tag .text [:- 1 ],
331- release_date = parser .parse (date ).replace (tzinfo = pytz .UTC ),
332- )
333- )
334- else :
335- versions .add (Version (value = prev_tag .text [:- 1 ], release_date = None ))
317+ def extract_versions (xml_response : ET .ElementTree ) -> Set [str ]:
318+ all_versions = set ()
319+ for child in xml_response .getroot ().iter ():
320+ if child .tag == "version" :
321+ all_versions .add (Version (child .text ))
336322
337- return versions
323+ return all_versions
338324
339325
340326class NugetVersionAPI (VersionAPI ):
@@ -368,7 +354,7 @@ def extract_versions(resp: dict) -> Set[str]:
368354 all_versions .add (
369355 Version (
370356 value = entry ["catalogEntry" ]["version" ],
371- release_date = parser .parse (entry ["catalogEntry" ]["published" ]),
357+ release_date = dateparser .parse (entry ["catalogEntry" ]["published" ]),
372358 )
373359 )
374360 # FIXME: json response for YamlDotNet.Signed triggers this exception.
@@ -412,15 +398,16 @@ def extract_versions(resp: dict, pkg_name: str) -> Set[str]:
412398 for version in resp ["packages" ][pkg_name ]:
413399 if "dev" in version :
414400 continue
401+
402+ # This if statement ensures, that all_versions contains only released versions
403+ # See https://github.com/composer/composer/blob/44a4429978d1b3c6223277b875762b2930e83e8c/doc/articles/versions.md#tags # nopep8
404+ # for explanation of removing 'v'
415405 all_versions .add (
416406 Version (
417- value = version .replace ("v" , " " ),
418- release_date = parser .parse (resp ["packages" ][pkg_name ][version ]["time" ]),
407+ value = version .lstrip ("v" ),
408+ release_date = dateparser .parse (resp ["packages" ][pkg_name ][version ]["time" ]),
419409 )
420410 )
421- # This if statement ensures, that all_versions contains only released versions
422- # See https://github.com/composer/composer/blob/44a4429978d1b3c6223277b875762b2930e83e8c/doc/articles/versions.md#tags # nopep8
423- # for explanation of removing 'v'
424411 return all_versions
425412
426413
@@ -440,8 +427,10 @@ async def load_api(self, repo_set):
440427 )
441428
442429 async def fetch (self , owner_repo : str , endpoint = None ) -> None :
443- # owner_repo is a string of format "{repo_owner}/{repo_name}"
444- # Example value of owner_repo = "nexB/scancode-toolkit"
430+ """
431+ owner_repo is a string of format "{repo_owner}/{repo_name}"
432+ Example value of owner_repo = "nexB/scancode-toolkit"
433+ """
445434 if owner_repo not in self .cache :
446435 self .cache [owner_repo ] = set ()
447436
@@ -456,12 +445,13 @@ async def fetch(self, owner_repo: str, endpoint=None) -> None:
456445 version = None
457446 for links in release_entry .find_all ("a" ):
458447 if f"/{ owner_repo } /releases/tag/" in links ["href" ].lower ():
459- version = links ["href" ].split ("/" )[- 1 ]
448+ prefix , _slash , version = links ["href" ].rpartition ("/" )
449+ version = version .lstrip ("v" )
460450 break
461451
462452 release_date = release_entry .find ("relative-time" )["datetime" ]
463453 self .cache [owner_repo ].add (
464- Version (value = version , release_date = parser .parse (release_date ))
454+ Version (value = version , release_date = dateparser .parse (release_date ))
465455 )
466456
467457 url = None
@@ -492,7 +482,8 @@ async def fetch(self, pkg, session):
492482 for release in response ["releases" ]:
493483 versions .add (
494484 Version (
495- value = release ["version" ], release_date = parser .parse (release ["inserted_at" ])
485+ value = release ["version" ],
486+ release_date = dateparser .parse (release ["inserted_at" ]),
496487 )
497488 )
498489 except (ClientResponseError , JSONDecodeError ):
0 commit comments