fix: pkg inventory parser — filter self-refs, parse optional-dependencies
All checks were successful
CI / skinny-install (aco) (push) Successful in 51s
CI / skinny-install (api) (push) Successful in 30s
CI / skinny-install (bcda) (push) Successful in 25s
CI / skinny-install (bib) (push) Successful in 26s
CI / skinny-install (bls) (push) Successful in 23s
CI / skinny-install (ccw) (push) Successful in 24s
CI / skinny-install (cli) (push) Successful in 24s
CI / skinny-install (cms) (push) Successful in 21s
CI / skinny-install (conf) (push) Successful in 25s
CI / skinny-install (pfs) (push) Successful in 27s
CI / skinny-install (rex) (push) Successful in 25s
CI / lint-test (push) Successful in 6m6s
Deploy / build-scan-report (push) Successful in 3m18s
All checks were successful
CI / skinny-install (aco) (push) Successful in 51s
CI / skinny-install (api) (push) Successful in 30s
CI / skinny-install (bcda) (push) Successful in 25s
CI / skinny-install (bib) (push) Successful in 26s
CI / skinny-install (bls) (push) Successful in 23s
CI / skinny-install (ccw) (push) Successful in 24s
CI / skinny-install (cli) (push) Successful in 24s
CI / skinny-install (cms) (push) Successful in 21s
CI / skinny-install (conf) (push) Successful in 25s
CI / skinny-install (pfs) (push) Successful in 27s
CI / skinny-install (rex) (push) Successful in 25s
CI / lint-test (push) Successful in 6m6s
Deploy / build-scan-report (push) Successful in 3m18s
The package mirror sync failed because: 1. `_parse_pyproject_deps` included `stack[all]` from [dependency-groups] as an external package — `pip download` can't resolve a local project 2. Optional-dependency extras (perf, aco, api, etc.) were silently skipped because `]` inside `stack[conf]` terminated the regex early Fixes: - `_is_self_ref()` filters out any spec named "stack" - Balanced-bracket parser for optional-dependencies blocks handles nested `[extras]` inside dependency specs - Manifest now correctly captures all 28 unique PyPI deps including 6 OpenTelemetry packages + psutil from the perf extra
This commit is contained in:
292
data/pkg-manifest.json
Normal file
292
data/pkg-manifest.json
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
{
|
||||||
|
"apt": {},
|
||||||
|
"apk": {},
|
||||||
|
"pypi": {
|
||||||
|
"project_prod": [
|
||||||
|
{
|
||||||
|
"name": "pydantic",
|
||||||
|
"version": ">=2.0.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pydantic",
|
||||||
|
"version": ">=2.0.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "httpx",
|
||||||
|
"version": ">=0.28.1",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "duckdb",
|
||||||
|
"version": ">=1.0.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "narwhals",
|
||||||
|
"version": ">=2.17.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sqlglot",
|
||||||
|
"version": ">=26.0.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pyarrow",
|
||||||
|
"version": ">=23.0.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "duckdb",
|
||||||
|
"version": ">=1.0.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fastapi",
|
||||||
|
"version": ">=0.135.1",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "uvicorn",
|
||||||
|
"version": ">=0.41.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "httpx",
|
||||||
|
"version": ">=0.28.1",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pyjwt",
|
||||||
|
"version": ">=2.12.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "cryptography",
|
||||||
|
"version": ">=46.0.5",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "httpx",
|
||||||
|
"version": ">=0.28.1",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fsspec",
|
||||||
|
"version": ">=2024.1.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "narwhals",
|
||||||
|
"version": ">=2.17.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pyarrow",
|
||||||
|
"version": ">=23.0.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pydantic",
|
||||||
|
"version": ">=2.0.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pydantic",
|
||||||
|
"version": ">=2.0.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "typer",
|
||||||
|
"version": ">=0.24.1",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "uvicorn",
|
||||||
|
"version": ">=0.41.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "narwhals",
|
||||||
|
"version": ">=2.17.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pydantic",
|
||||||
|
"version": ">=2.0.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "duckdb",
|
||||||
|
"version": ">=1.0.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "narwhals",
|
||||||
|
"version": ">=2.17.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "narwhals",
|
||||||
|
"version": ">=2.17.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pyarrow",
|
||||||
|
"version": ">=23.0.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fsspec",
|
||||||
|
"version": ">=2024.1.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "opentelemetry-api",
|
||||||
|
"version": ">=1.25.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "opentelemetry-sdk",
|
||||||
|
"version": ">=1.25.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "opentelemetry-exporter-otlp-proto-grpc",
|
||||||
|
"version": ">=1.25.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "opentelemetry-exporter-prometheus",
|
||||||
|
"version": ">=0.46b0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "opentelemetry-instrumentation-fastapi",
|
||||||
|
"version": ">=0.46b0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "opentelemetry-semantic-conventions",
|
||||||
|
"version": ">=0.46b0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "psutil",
|
||||||
|
"version": ">=5.9.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "databricks-cli",
|
||||||
|
"version": ">=0.18.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "databricks-sdk",
|
||||||
|
"version": ">=0.85.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "boto3",
|
||||||
|
"version": ">=1.35.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "s3fs",
|
||||||
|
"version": ">=2026.2.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "google-cloud-storage",
|
||||||
|
"version": ">=2.18.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "gcsfs",
|
||||||
|
"version": ">=2024.1.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "azure-storage-blob",
|
||||||
|
"version": ">=12.23.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "adlfs",
|
||||||
|
"version": ">=2024.1.0",
|
||||||
|
"extras": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "uv-build",
|
||||||
|
"version": ">=0.7,<1",
|
||||||
|
"extras": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"project_dev": [],
|
||||||
|
"notebook": [],
|
||||||
|
"dockerfile_adhoc": []
|
||||||
|
},
|
||||||
|
"npm": [
|
||||||
|
{
|
||||||
|
"name": "@docusaurus/core",
|
||||||
|
"version": "^3.7.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "@docusaurus/preset-classic",
|
||||||
|
"version": "^3.7.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "@easyops-cn/docusaurus-search-local",
|
||||||
|
"version": "^0.55.1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "@mdx-js/react",
|
||||||
|
"version": "^3.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "clsx",
|
||||||
|
"version": "^2.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "prism-react-renderer",
|
||||||
|
"version": "^2.3.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "react",
|
||||||
|
"version": "^18.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "react-dom",
|
||||||
|
"version": "^18.0.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "@docusaurus/module-type-aliases",
|
||||||
|
"version": "^3.7.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "@docusaurus/types",
|
||||||
|
"version": "^3.7.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "typescript",
|
||||||
|
"version": "~5.6.0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ci_tools": [
|
||||||
|
{
|
||||||
|
"name": "uv",
|
||||||
|
"version": "latest"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "crane",
|
||||||
|
"version": "latest"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "trivy",
|
||||||
|
"version": "latest"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"base_images": {}
|
||||||
|
}
|
||||||
@@ -158,25 +158,73 @@ def _parse_ci_curl_tools(text: str) -> list[dict]:
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
def _is_self_ref(spec: str) -> bool:
|
||||||
|
"""Check if a dependency spec is a self-reference (e.g. stack[aco])."""
|
||||||
|
name = re.split(r"[\[>=<~!]", spec)[0].strip().lower().replace("_", "-")
|
||||||
|
return name == "stack"
|
||||||
|
|
||||||
|
|
||||||
def _parse_pyproject_deps(text: str) -> tuple[list[dict], list[dict]]:
|
def _parse_pyproject_deps(text: str) -> tuple[list[dict], list[dict]]:
|
||||||
"""Parse dependencies from pyproject.toml without a TOML library."""
|
"""Parse dependencies from pyproject.toml without a TOML library.
|
||||||
|
|
||||||
|
Extracts:
|
||||||
|
- [project] dependencies (prod)
|
||||||
|
- [project.optional-dependencies] all extras (prod)
|
||||||
|
- [dependency-groups] dev (dev)
|
||||||
|
- [build-system] requires (prod)
|
||||||
|
|
||||||
|
Filters out self-references (stack[*]) which can't be downloaded.
|
||||||
|
"""
|
||||||
prod: list[dict] = []
|
prod: list[dict] = []
|
||||||
dev: list[dict] = []
|
dev: list[dict] = []
|
||||||
|
|
||||||
# prod deps
|
# prod deps — [project] dependencies
|
||||||
m = re.search(r"^dependencies\s*=\s*\[(.*?)\]", text, re.MULTILINE | re.DOTALL)
|
m = re.search(r"^dependencies\s*=\s*\[(.*?)\]", text, re.MULTILINE | re.DOTALL)
|
||||||
if m:
|
if m:
|
||||||
for line in m.group(1).splitlines():
|
for line in m.group(1).splitlines():
|
||||||
line = line.strip().strip(",").strip('"').strip("'")
|
line = line.strip().strip(",").strip('"').strip("'")
|
||||||
if line and not line.startswith("#"):
|
if line and not line.startswith("#") and not _is_self_ref(line):
|
||||||
prod.append(_parse_pypi_spec(line))
|
prod.append(_parse_pypi_spec(line))
|
||||||
|
|
||||||
# dev deps
|
# optional deps — [project.optional-dependencies] all sections
|
||||||
|
# Extract the section content, then parse each "name = [...]" block.
|
||||||
|
# Can't use simple .*? because extras brackets (e.g. stack[conf])
|
||||||
|
# contain ] which terminates the match early.
|
||||||
|
opt_section = re.search(
|
||||||
|
r"\[project\.optional-dependencies\](.*?)(?:\n\[|\Z)",
|
||||||
|
text,
|
||||||
|
re.DOTALL,
|
||||||
|
)
|
||||||
|
if opt_section:
|
||||||
|
# Match each key = [ ... ] block by finding balanced brackets
|
||||||
|
for m in re.finditer(
|
||||||
|
r"^(\w+)\s*=\s*\[",
|
||||||
|
opt_section.group(1),
|
||||||
|
re.MULTILINE,
|
||||||
|
):
|
||||||
|
start = m.end()
|
||||||
|
rest = opt_section.group(1)[start:]
|
||||||
|
# Find closing ] that isn't inside a string bracket
|
||||||
|
depth = 1
|
||||||
|
i = 0
|
||||||
|
while i < len(rest) and depth > 0:
|
||||||
|
if rest[i] == "[":
|
||||||
|
depth += 1
|
||||||
|
elif rest[i] == "]":
|
||||||
|
depth -= 1
|
||||||
|
i += 1
|
||||||
|
block = rest[: i - 1] if depth == 0 else rest
|
||||||
|
for line in block.splitlines():
|
||||||
|
line = line.strip().strip(",").strip('"').strip("'")
|
||||||
|
if line and not line.startswith("#") and not _is_self_ref(line):
|
||||||
|
prod.append(_parse_pypi_spec(line))
|
||||||
|
|
||||||
|
# dev deps — [dependency-groups] dev
|
||||||
m = re.search(r"dev\s*=\s*\[(.*?)\]", text, re.MULTILINE | re.DOTALL)
|
m = re.search(r"dev\s*=\s*\[(.*?)\]", text, re.MULTILINE | re.DOTALL)
|
||||||
if m:
|
if m:
|
||||||
for line in m.group(1).splitlines():
|
for line in m.group(1).splitlines():
|
||||||
line = line.strip().strip(",").strip('"').strip("'")
|
line = line.strip().strip(",").strip('"').strip("'")
|
||||||
if line and not line.startswith("#"):
|
if line and not line.startswith("#") and not _is_self_ref(line):
|
||||||
dev.append(_parse_pypi_spec(line))
|
dev.append(_parse_pypi_spec(line))
|
||||||
|
|
||||||
# build-system requires
|
# build-system requires
|
||||||
@@ -188,7 +236,7 @@ def _parse_pyproject_deps(text: str) -> tuple[list[dict], list[dict]]:
|
|||||||
if m:
|
if m:
|
||||||
for line in m.group(1).splitlines():
|
for line in m.group(1).splitlines():
|
||||||
line = line.strip().strip(",").strip('"').strip("'")
|
line = line.strip().strip(",").strip('"').strip("'")
|
||||||
if line and not line.startswith("#"):
|
if line and not line.startswith("#") and not _is_self_ref(line):
|
||||||
prod.append(_parse_pypi_spec(line))
|
prod.append(_parse_pypi_spec(line))
|
||||||
|
|
||||||
return prod, dev
|
return prod, dev
|
||||||
|
|||||||
Reference in New Issue
Block a user