diff --git a/Tools/scripts/pypi-find-projects-containing-string.py b/Tools/scripts/pypi-find-projects-containing-string.py new file mode 100755 index 000000000000..59c9e1ca3abb --- /dev/null +++ b/Tools/scripts/pypi-find-projects-containing-string.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +# +# MAINTAINER: yuri@FreeBSD.org + +import argparse +import gzip +import json +import urllib.request + + +def filter_pypi_projects(search_term: str): + """Downloads all PyPI project names, handles compression, filters, and prints them sorted.""" + url = "https://pypi.org/simple/" + + # Request JSON format and explicitly allow gzip compression while providing a standard User-Agent + headers = { + "Accept": "application/vnd.pypi.simple.v1+json", + "Accept-Encoding": "gzip", + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) PyPiProjectFilter/1.0", + } + request = urllib.request.Request(url, headers=headers) + + try: + with urllib.request.urlopen(request) as response: + # Check if the content is gzipped and decompress safely + if response.info().get("Content-Encoding") == "gzip": + raw_data = gzip.decompress(response.read()) + else: + raw_data = response.read() + + data = json.loads(raw_data.decode("utf-8")) + + project_names = [project["name"] for project in data.get("projects", [])] + + # Case-insensitive partial matching + filtered_projects = [ + name for name in project_names if search_term.lower() in name.lower() + ] + + filtered_projects.sort() + + for project in filtered_projects: + print(project) + + except Exception as e: + print(f"Error fetching or processing PyPI data: {e}") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser( + description="Download, filter, and sort PyPI projects safely." + ) + parser.add_argument( + "search_term", + type=str, + help="The string used to filter the PyPI project names (case-insensitive).", + ) + + args = parser.parse_args() + filter_pypi_projects(args.search_term)