git: 28beaaa5e3a0 - main - Tools/scripts: Add pypi-find-projects-containing-string.py

From: Yuri Victorovich <yuri_at_FreeBSD.org>
Date: Wed, 15 Jul 2026 15:15:00 UTC
The branch main has been updated by yuri:

URL: https://cgit.FreeBSD.org/ports/commit/?id=28beaaa5e3a0e85459108f0e54fd52aec7494030

commit 28beaaa5e3a0e85459108f0e54fd52aec7494030
Author:     Yuri Victorovich <yuri@FreeBSD.org>
AuthorDate: 2026-07-15 15:11:05 +0000
Commit:     Yuri Victorovich <yuri@FreeBSD.org>
CommitDate: 2026-07-15 15:14:58 +0000

    Tools/scripts: Add pypi-find-projects-containing-string.py
    
    This is a useful script allowing to find all Python projects
    containing a given string.
    
    Usage example:
    $ pypi-find-projects-containing-string.py freebsd
---
 .../pypi-find-projects-containing-string.py        | 60 ++++++++++++++++++++++
 1 file changed, 60 insertions(+)

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)