Run pip-licenses and return structured package notices. Each entry: {name, version, license_id, text}
()
| 125 | |
| 126 | |
| 127 | def get_python_notices() -> list[dict]: |
| 128 | """Run pip-licenses and return structured package notices. |
| 129 | |
| 130 | Each entry: {name, version, license_id, text} |
| 131 | """ |
| 132 | print(" Running pip-licenses ...") |
| 133 | try: |
| 134 | result = subprocess.run( |
| 135 | [ |
| 136 | "uv", |
| 137 | "run", |
| 138 | "--with", |
| 139 | "pip-licenses", |
| 140 | "pip-licenses", |
| 141 | "--format=json", |
| 142 | "--with-license-file", |
| 143 | "--no-license-path", |
| 144 | ], |
| 145 | capture_output=True, |
| 146 | text=True, |
| 147 | check=True, |
| 148 | ) |
| 149 | except FileNotFoundError: |
| 150 | print(" WARNING: uv not found, skipping Python notices", file=sys.stderr) |
| 151 | return [] |
| 152 | except subprocess.CalledProcessError as e: |
| 153 | print(f" WARNING: pip-licenses failed: {e.stderr[:200]}", file=sys.stderr) |
| 154 | return [] |
| 155 | |
| 156 | packages: list[dict] = [] |
| 157 | for pkg in json.loads(result.stdout): |
| 158 | name = pkg.get("Name", "") |
| 159 | if name.lower() in OWN_PYTHON_PACKAGES: |
| 160 | continue |
| 161 | # Skip pip/setuptools/wheel (installer tools, not shipped deps) |
| 162 | if name.lower() in {"pip", "wheel"}: |
| 163 | continue |
| 164 | |
| 165 | packages.append( |
| 166 | { |
| 167 | "name": name, |
| 168 | "version": pkg.get("Version", ""), |
| 169 | "license_id": pkg.get("License", "Unknown"), |
| 170 | "text": (pkg.get("LicenseText") or "").rstrip(), |
| 171 | } |
| 172 | ) |
| 173 | |
| 174 | return sorted(packages, key=lambda p: p["name"].lower()) |
| 175 | |
| 176 | |
| 177 | # --------------------------------------------------------------------------- |