Files
awesome-quant/scripts/readme_entries.py
T
2026-06-29 17:07:52 -03:00

118 lines
3.7 KiB
Python

"""Shared README entry parsing helpers for awesome-quant."""
from __future__ import annotations
import re
from dataclasses import dataclass
from pathlib import Path
from typing import Iterable
ENTRY_RE = re.compile(r"^\s*- \[(.*)\]\((.*)\) - (.*)$")
HEADING_RE = re.compile(r"^(#+) (.*)$")
BADGE_RE = re.compile(r"\s*!\[[^\]]*\]\([^)]*\)\s*")
LANGUAGE_PREFIX_RE = re.compile(r"^((?:`[^`]+`\s*)+)-\s*(.*)$")
GITHUB_LINK_RE = re.compile(r"\[GitHub\]\((https://github\.com/[\w-]+/[-\w.]+)\)")
MARKDOWN_URL_RE = re.compile(r"\[[^\]]+\]\(([^)]+)\)")
NO_LANGUAGE_REQUIRED_SECTIONS = {
"Commercial & Proprietary Services",
"Cross-Language Frameworks",
"Reproducing Works, Training & Books",
"Related Lists",
}
VALID_SECTIONS = [
"Numerical Libraries & Data Structures",
"Financial Instruments & Pricing",
"Technical Indicators",
"Trading & Backtesting",
"Portfolio Optimization & Risk Analysis",
"Factor Analysis",
"Sentiment Analysis & Alternative Data",
"Time Series Analysis",
"Market Data & Data Sources",
"Prediction Markets",
"Calendars & Market Hours",
"Visualization",
"Excel & Spreadsheet Integration",
"Quant Research Environments",
"Cross-Language Frameworks",
"Reproducing Works, Training & Books",
"Commercial & Proprietary Services",
"Related Lists",
]
@dataclass(frozen=True)
class ReadmeEntry:
line_number: int
raw_line: str
section: str
name: str
url: str
tail: str
languages: list[str]
description: str
@property
def markdown_urls(self) -> list[str]:
return MARKDOWN_URL_RE.findall(self.raw_line)
@property
def github_url(self) -> str:
if "github.com" in self.url:
return self.url
match = GITHUB_LINK_RE.search(self.description)
return match.group(1) if match else ""
def slugify(text: str) -> str:
"""Convert text to lowercase hyphen-separated slug."""
text = text.lower().strip()
text = re.sub(r"[&/]+", "-", text)
text = re.sub(r"[^\w\s-]", "", text)
text = re.sub(r"[\s_]+", "-", text)
text = re.sub(r"-+", "-", text)
return text.strip("-")
def extract_languages(description: str) -> tuple[list[str], str]:
"""Extract leading backtick language tags from an entry description."""
match = LANGUAGE_PREFIX_RE.match(description)
if not match:
return [], description
lang_str = match.group(1)
clean_description = match.group(2)
return re.findall(r"`([^`]+)`", lang_str), clean_description
def iter_readme_entries(path: str | Path) -> Iterable[ReadmeEntry]:
"""Yield parsed README entries with line numbers and current h2 section."""
current_section = ""
with Path(path).open("r", encoding="utf-8") as handle:
for line_number, raw_line in enumerate(handle, 1):
line = BADGE_RE.sub(" ", raw_line.rstrip("\n"))
heading = HEADING_RE.match(line)
if heading:
level, title = heading.groups()
if len(level) == 2 and title.strip() != "Contents":
current_section = title.strip()
continue
match = ENTRY_RE.match(line)
if not match:
continue
tail = match.group(3).strip()
languages, description = extract_languages(tail)
yield ReadmeEntry(
line_number=line_number,
raw_line=raw_line.rstrip("\n"),
section=current_section,
name=match.group(1).strip(),
url=match.group(2).strip(),
tail=tail,
languages=languages,
description=description.strip(),
)