docs(great-docs): add a Great Docs site for sdv-py — executable guides + auto-introspected reference - #112
docs(great-docs): add a Great Docs site for sdv-py — executable guides + auto-introspected reference#112saiemgilani wants to merge 9 commits into
Conversation
Add a self-contained Quarto proof-of-concept under docs-quarto/ to compare against the Docusaurus docs the SportsDataverse uses. The PoC demonstrates Quarto's build-time executable Python cells: every cell runs real sportsdataverse code at render time and freezes the output (tidy polars / pandas DataFrames as HTML tables) into the static page. - _quarto.yml: minimal website project, jupyter: python3, execute freeze: auto - index.qmd: 7 executable cells (espn_nba_teams / espn_nfl_teams, polars + pandas round-trip) + inline computed expressions; renders clean (30 NBA / 32 NFL teams, real team names in the HTML) - README.md: honest Quarto-vs-Docusaurus write-up for these packages, with a recommendation (Quarto for the Python/R data packages; Docusaurus stays for the JS package) + a build-time "real output" injector hybrid - _freeze/ committed (cached executed output); rendered-preview.html + quarto-poc-render.png committed so the render is visible without a build - _site/ and .quarto/ gitignored (build artifacts)
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reached
More reviews will be available in 7 minutes and 25 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (23)
📝 WalkthroughWalkthroughAdds a complete Quarto documentation site under ChangesQuarto + Great Docs documentation site for sportsdataverse-py
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~30 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Extend the single-page PoC into a complete, navigable Quarto website that mirrors the production Docusaurus structure (Home / Guides / Reference / Architecture) with navbar + sidebar + overlay search. - Home (index.qmd): install tabs + executable surface tour with real frames. - Guides (guides/*.qmd): installation, teams & scoreboard, schedule & rosters, and the parser layer — all build-time-executed against live ESPN endpoints, with output frozen via execute: freeze: auto. - Reference: hand-written executable reference for MLB (4 fns) and NBA (3 fns) with real df output, plus _gen_reference.py emitting 21 MLB .qmd stubs from the endpoint YAML + live signatures (proves the codegen -> .qmd path). - Architecture (architecture.qmd): ESPN cross-league core, parser layer, and the codegen pipeline, with a build-time table computed from the real YAML. - quarto render -> 32 pages, no execution errors; real output verified in HTML. Tried Great Docs first (pip install great-docs): it installs but its package auto-discovery is non-viable against this package's dynamically-generated, submodule-namespaced 800-wrapper surface (init/scan/build time out or fail to resolve the dynamic names). Fell back to a clean Quarto website per the brief and documented the deviation. README.md rewritten as a full-conversion evaluation (effort estimate + per-package recommendation + executable vs schema-table reference comparison). Adds 3 screenshots + a self-contained rendered-preview.html so the result is viewable without building.
…scovery works) Build an actual Great Docs (Posit, v0.14.0) documentation site for the package, succeeding where the first pass fell back to plain Quarto. The blocker was never that the dynamic 800-wrapper surface "defeats" discovery — it was that Great Docs' auto-discovery validates every one of the ~2,769 import-time names through griffe's dynamic loader (~2s each = ~90 min), so init/build time out. The fix is the documented escape hatch: pin an explicit, curated `reference:` in great-docs.yml. `_create_api_sections_with_config()` prioritizes it over auto-discovery and skips the per-symbol validation loop, so the build runs in ~100s. Pair with `source.enabled: false` to drop the other unscoped griffe walk. What's committed: - great-docs.yml — module: sportsdataverse, dynamic: true, parser: google, curated reference (8 sports x 8 ESPN entry points + NFL loaders + PBP/ config classes = 95 documented symbols), slate navbar + sky content glow + dark-mode toggle + hero + GitHub widget. - user_guide/00-getting-started.qmd, 01-scoreboards-and-schedules.qmd — executable cells that run against the live ESPN API at build time and freeze real DataFrame output (espn_nba_teams() -> 30x14, a scoreboard with MIN @ BOS / Final/OT, schedule 500x70, build-time-computed prose). - assets/logo.png — hero logo + auto-generated favicon. - great-docs-preview/ — self-contained rendered-preview.html (opens with no build) + 3 screenshots (themed home, reference page, user guide). - GREAT-DOCS-NOTES.md — honest evaluation: what made auto-discovery work, the scope needed, and Great-Docs-autodoc vs the Docusaurus codegen reference; supersedes the "non-viable" conclusion in docs-quarto/README.md. Build: 19/19 steps, ~100s, great-docs/_site/index.html + 85 HTML pages. The ephemeral great-docs/ build dir is gitignored.
There was a problem hiding this comment.
Pull request overview
This PR adds an additive Great Docs (Quarto) documentation site for sportsdataverse-py, including a curated auto-introspected API reference and executable user-guide pages, while preserving the existing Docusaurus/codegen docs and retaining the prior docs-quarto/ evaluation site.
Changes:
- Introduces
great-docs.ymlwith an explicit curatedreference:allow-list to avoid Great Docs’ expensive dynamic auto-discovery over the package’s large dynamically-registered surface. - Adds executable Great Docs user guide pages under
user_guide/demonstrating live ESPN API calls rendered at build time. - Updates/extends the existing
docs-quarto/evaluation site (including a small generator and committed_freeze/execution outputs).
Reviewed changes
Copilot reviewed 26 out of 63 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
.gitignore |
Ignores Great Docs build artifacts and root-level Quarto cache dirs. |
GREAT-DOCS-NOTES.md |
Documents the working Great Docs approach and the curated-reference workaround. |
great-docs.yml |
Great Docs config: theming, source/changelog disablement, and curated reference: allow-list. |
user_guide/00-getting-started.qmd |
Executable Great Docs “Getting started” guide with live frames. |
user_guide/01-scoreboards-and-schedules.qmd |
Executable Great Docs guide for scoreboards/schedules + pandas example. |
docs-quarto/.gitignore |
Quarto project ignores; notes _freeze/ is committed. |
docs-quarto/_quarto.yml |
Quarto website configuration (nav/sidebar/theme/execution defaults). |
docs-quarto/_gen_reference.py |
Generator to emit Quarto .qmd reference stubs from endpoint YAML + live signatures. |
docs-quarto/README.md |
Extended Quarto/Great Docs evaluation write-up (now points to GREAT-DOCS-NOTES as superseding). |
docs-quarto/index.qmd |
Quarto site home page with executable cells. |
docs-quarto/architecture.qmd |
Architecture + codegen pipeline overview with executable YAML introspection snippet. |
docs-quarto/guides/index.qmd |
Guides landing page for the Quarto evaluation site. |
docs-quarto/guides/installation.qmd |
Executable install/import verification guide. |
docs-quarto/guides/teams-and-scoreboard.qmd |
Executable teams + past-dated scoreboard guide. |
docs-quarto/guides/schedule-and-rosters.qmd |
Executable team_id → schedule/roster walkthrough. |
docs-quarto/guides/parsed-dataframes.qmd |
Explains return_parsed / return_as_pandas with executable examples. |
docs-quarto/reference/index.qmd |
Reference landing page explaining handwritten vs generated stubs. |
docs-quarto/reference/mlb.qmd |
Handwritten executable MLB reference examples. |
docs-quarto/reference/nba.qmd |
Handwritten executable NBA reference examples. |
docs-quarto/reference/generated/index.qmd |
Index of generator-emitted MLB stubs. |
docs-quarto/reference/generated/calendar.qmd |
Generated stub for espn_mlb_calendar (currently inconsistent vs endpoint metadata). |
docs-quarto/reference/generated/conferences.qmd |
Generated stub for espn_mlb_conferences. |
docs-quarto/reference/generated/draft.qmd |
Generated stub for espn_mlb_draft. |
docs-quarto/reference/generated/injuries.qmd |
Generated stub for espn_mlb_injuries. |
docs-quarto/reference/generated/news.qmd |
Generated stub for espn_mlb_news. |
docs-quarto/reference/generated/scoreboard.qmd |
Generated stub for espn_mlb_scoreboard. |
docs-quarto/reference/generated/standings.qmd |
Generated stub for espn_mlb_standings. |
docs-quarto/reference/generated/statistics_league.qmd |
Generated stub for espn_mlb_statistics_league. |
docs-quarto/reference/generated/summary.qmd |
Generated stub for espn_mlb_summary. |
docs-quarto/reference/generated/team.qmd |
Generated stub for espn_mlb_team. |
docs-quarto/reference/generated/team_depthcharts.qmd |
Generated stub for espn_mlb_team_depthcharts. |
docs-quarto/reference/generated/team_history.qmd |
Generated stub for espn_mlb_team_history. |
docs-quarto/reference/generated/team_injuries.qmd |
Generated stub for espn_mlb_team_injuries. |
docs-quarto/reference/generated/team_leaders.qmd |
Generated stub for espn_mlb_team_leaders. |
docs-quarto/reference/generated/team_news.qmd |
Generated stub for espn_mlb_team_news. |
docs-quarto/reference/generated/team_record.qmd |
Generated stub for espn_mlb_team_record. |
docs-quarto/reference/generated/team_roster.qmd |
Generated stub for espn_mlb_team_roster. |
docs-quarto/reference/generated/team_schedule.qmd |
Generated stub for espn_mlb_team_schedule. |
docs-quarto/reference/generated/team_transactions.qmd |
Generated stub for espn_mlb_team_transactions. |
docs-quarto/reference/generated/teams_site.qmd |
Generated stub for espn_mlb_teams_site. |
docs-quarto/reference/generated/transactions.qmd |
Generated stub for espn_mlb_transactions. |
docs-quarto/_freeze/site_libs/clipboard/clipboard.min.js |
Generated/vendor JS captured under _freeze/. |
docs-quarto/_freeze/index/execute-results/html.json |
Frozen execution output for Quarto index. |
docs-quarto/_freeze/architecture/execute-results/html.json |
Frozen execution output for architecture page. |
docs-quarto/_freeze/guides/installation/execute-results/html.json |
Frozen execution output for install guide. |
docs-quarto/_freeze/guides/teams-and-scoreboard/execute-results/html.json |
Frozen execution output for teams/scoreboard guide. |
docs-quarto/_freeze/guides/schedule-and-rosters/execute-results/html.json |
Frozen execution output for schedule/rosters guide. |
docs-quarto/_freeze/guides/parsed-dataframes/execute-results/html.json |
Frozen execution output for parser-layer guide. |
docs-quarto/_freeze/reference/mlb/execute-results/html.json |
Frozen execution output for handwritten MLB reference. |
docs-quarto/_freeze/reference/nba/execute-results/html.json |
Frozen execution output for handwritten NBA reference. |
docs-quarto/_freeze/reference/generated/conferences/execute-results/html.json |
Frozen execution output for generated conferences stub. |
docs-quarto/_freeze/reference/generated/news/execute-results/html.json |
Frozen execution output for generated news stub. |
docs-quarto/_freeze/reference/generated/scoreboard/execute-results/html.json |
Frozen execution output for generated scoreboard stub. |
docs-quarto/_freeze/reference/generated/teams_site/execute-results/html.json |
Frozen execution output for generated teams_site stub. |
Comments suppressed due to low confidence (1)
docs-quarto/.gitignore:14
docs-quarto/_freeze/site_libs/contains generated/vendor assets (e.g. minified clipboard.js) that are not part of the executed-output cache and will churn with Quarto upgrades. If the goal is to commit only the executed results for reproducibility, it’s better to ignore_freeze/site_libs/and keep just theexecute-results/*.jsonartifacts under_freeze/.
# NOTE: _freeze/ IS committed on purpose — it is the cached executed output
# (the proof the cells ran against live data) and lets `quarto render` reuse
# results without re-hitting the live ESPN endpoints.
|
|
||
|
|
||
| def main() -> int: | ||
| import sportsdataverse.mlb as mlb # imported here so failures are clear |
|
|
||
| ```{python} | ||
| n = games.shape[0] | ||
| home_wins = games.filter(pl.col("home_winner") == True).shape[0] |
| teams_pd = espn_nba_scoreboard(dates=20240110, return_as_pandas=True) | ||
| print(type(teams_pd).__module__ + "." + type(teams_pd).__name__) | ||
| print(list(teams_pd.columns[:6])) |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs-quarto/_gen_reference.py`:
- Around line 62-88: The functions _summary, _param_table, and _returns_note in
the module lack complete type hints and the module is not included in mypy
validation. Import Callable and Any from typing at the top of the file, then add
full type hints: for _summary change the fn parameter from untyped to Callable,
for _param_table change ep from bare dict to a proper type (likely Any) and fn
from untyped to Callable, and for _returns_note change ep from bare dict to a
proper type (likely Any). Finally, append "docs-quarto/_gen_reference.py" to the
files list under [tool.mypy] in pyproject.toml.
In `@docs-quarto/architecture.qmd`:
- Around line 44-49: The "evaluation write-up" link in the callout block uses a
temporary PR branch reference (proto/quarto-docs) that will not exist after
merge. Update the link to use a stable reference by either replacing the branch
name with main in the GitHub URL (keeping the full path to
docs-quarto/README.md), or use a relative link to the local README.md file since
it exists in the same directory as architecture.qmd. Choose whichever approach
best fits the documentation's linking strategy.
In `@docs-quarto/reference/mlb.qmd`:
- Around line 130-158: The `espn_mlb_scoreboard` function documentation is
missing the `return_parsed` parameter that exists in the actual implementation.
Add `return_parsed: bool = True` as a parameter in the function signature (it
should be a keyword-only parameter after the asterisk and before
`return_as_pandas`). Update the return type from `pl.DataFrame` to `pl.DataFrame
| dict` to reflect that the return type depends on the `return_parsed` value.
Finally, add a new row to the parameters table documenting `return_parsed` with
type `bool`, default `True`, and a description explaining that when `True` it
returns a tidy frame and when `False` it returns raw `Dict` data.
In `@docs-quarto/reference/nba.qmd`:
- Around line 88-93: The `espn_nba_scoreboard` function documentation is missing
the `return_parsed` parameter and has an inaccurate return type annotation.
Update the function signature in the docstring to include `return_parsed: bool =
True` as a keyword-only parameter, and change the return type annotation from
`pl.DataFrame` to `pl.DataFrame | dict` to accurately reflect that it returns a
Dict when `return_parsed=False`. Add a new row to the Parameters table
documenting the `return_parsed` parameter with its default value and description
explaining it controls whether a parsed tidy frame or raw Dict is returned.
Finally, update the Returns section to clarify that it returns either a tidy
polars DataFrame or the raw ESPN Dict depending on the `return_parsed` parameter
value, and update the description text to note that this function is
parser-backed.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: b3f646e6-5741-4ab6-abe9-712a9c175a89
⛔ Files ignored due to path filters (34)
assets/logo.pngis excluded by!**/*.pngdocs-quarto/_freeze/reference/generated/conferences/execute-results/html.jsonis excluded by!**/generated/**docs-quarto/_freeze/reference/generated/news/execute-results/html.jsonis excluded by!**/generated/**docs-quarto/_freeze/reference/generated/scoreboard/execute-results/html.jsonis excluded by!**/generated/**docs-quarto/_freeze/reference/generated/teams_site/execute-results/html.jsonis excluded by!**/generated/**docs-quarto/_freeze/site_libs/clipboard/clipboard.min.jsis excluded by!**/*.min.jsdocs-quarto/quarto-generated-ref.pngis excluded by!**/*.pngdocs-quarto/quarto-guide.pngis excluded by!**/*.pngdocs-quarto/quarto-home.pngis excluded by!**/*.pngdocs-quarto/reference/generated/calendar.qmdis excluded by!**/generated/**docs-quarto/reference/generated/conferences.qmdis excluded by!**/generated/**docs-quarto/reference/generated/draft.qmdis excluded by!**/generated/**docs-quarto/reference/generated/index.qmdis excluded by!**/generated/**docs-quarto/reference/generated/injuries.qmdis excluded by!**/generated/**docs-quarto/reference/generated/news.qmdis excluded by!**/generated/**docs-quarto/reference/generated/scoreboard.qmdis excluded by!**/generated/**docs-quarto/reference/generated/standings.qmdis excluded by!**/generated/**docs-quarto/reference/generated/statistics_league.qmdis excluded by!**/generated/**docs-quarto/reference/generated/summary.qmdis excluded by!**/generated/**docs-quarto/reference/generated/team.qmdis excluded by!**/generated/**docs-quarto/reference/generated/team_depthcharts.qmdis excluded by!**/generated/**docs-quarto/reference/generated/team_history.qmdis excluded by!**/generated/**docs-quarto/reference/generated/team_injuries.qmdis excluded by!**/generated/**docs-quarto/reference/generated/team_leaders.qmdis excluded by!**/generated/**docs-quarto/reference/generated/team_news.qmdis excluded by!**/generated/**docs-quarto/reference/generated/team_record.qmdis excluded by!**/generated/**docs-quarto/reference/generated/team_roster.qmdis excluded by!**/generated/**docs-quarto/reference/generated/team_schedule.qmdis excluded by!**/generated/**docs-quarto/reference/generated/team_transactions.qmdis excluded by!**/generated/**docs-quarto/reference/generated/teams_site.qmdis excluded by!**/generated/**docs-quarto/reference/generated/transactions.qmdis excluded by!**/generated/**great-docs-preview/great-docs-home.pngis excluded by!**/*.pnggreat-docs-preview/great-docs-reference.pngis excluded by!**/*.pnggreat-docs-preview/great-docs-userguide.pngis excluded by!**/*.png
📒 Files selected for processing (29)
.gitignoreGREAT-DOCS-NOTES.mddocs-quarto/.gitignoredocs-quarto/README.mddocs-quarto/_freeze/architecture/execute-results/html.jsondocs-quarto/_freeze/guides/installation/execute-results/html.jsondocs-quarto/_freeze/guides/parsed-dataframes/execute-results/html.jsondocs-quarto/_freeze/guides/schedule-and-rosters/execute-results/html.jsondocs-quarto/_freeze/guides/teams-and-scoreboard/execute-results/html.jsondocs-quarto/_freeze/index/execute-results/html.jsondocs-quarto/_freeze/reference/mlb/execute-results/html.jsondocs-quarto/_freeze/reference/nba/execute-results/html.jsondocs-quarto/_gen_reference.pydocs-quarto/_quarto.ymldocs-quarto/architecture.qmddocs-quarto/guides/index.qmddocs-quarto/guides/installation.qmddocs-quarto/guides/parsed-dataframes.qmddocs-quarto/guides/schedule-and-rosters.qmddocs-quarto/guides/teams-and-scoreboard.qmddocs-quarto/index.qmddocs-quarto/reference/index.qmddocs-quarto/reference/mlb.qmddocs-quarto/reference/nba.qmddocs-quarto/rendered-preview.htmlgreat-docs-preview/rendered-preview.htmlgreat-docs.ymluser_guide/00-getting-started.qmduser_guide/01-scoreboards-and-schedules.qmd
| def _summary(fn) -> str: | ||
| doc = inspect.getdoc(fn) or "" | ||
| first = doc.strip().splitlines()[0] if doc.strip() else "" | ||
| # docstrings often start "espn_mlb_x - <summary>."; keep the human half. | ||
| if " - " in first: | ||
| first = first.split(" - ", 1)[1] | ||
| return first or "ESPN endpoint." | ||
|
|
||
|
|
||
| def _param_table(ep: dict, fn) -> str: | ||
| rows = [] | ||
| for kind in ("path_params", "extra_params"): | ||
| for p in ep.get(kind, []) or []: | ||
| required = "Y" if p.get("required") else "" | ||
| default = p.get("default", "") | ||
| # escape the pipe in union types (e.g. int|str) so it doesn't break | ||
| # the markdown table column boundaries. | ||
| ptype = str(p.get("type", "")).replace("|", "\\|") | ||
| rows.append(f"| `{p['name']}` | `{ptype}` | `{default}` | {required} |") | ||
| # always-present passthrough flags | ||
| rows.append("| `return_parsed` | `bool` | `True` | |") | ||
| rows.append("| `return_as_pandas` | `bool` | `False` | |") | ||
| header = "| Parameter | Type | Default | Required |\n|---|---|---|:---:|\n" | ||
| return header + "\n".join(rows) | ||
|
|
||
|
|
||
| def _returns_note(ep: dict) -> str: |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify mypy config includes/handles the new module path requirement from guidelines.
# Read-only checks only.
fd -HI '^pyproject\.toml$' -x sed -n '1,260p' {}
rg -n --no-heading '\[tool\.mypy\]|\[\[tool\.mypy\.overrides\]\]|files\s*=|module\s*=' pyproject.toml
rg -n --no-heading '_gen_reference|docs[-_]quarto' pyproject.tomlRepository: sportsdataverse/sportsdataverse-py
Length of output: 7715
🏁 Script executed:
# Read the complete [tool.mypy] section to see the files list
sed -n '260,350p' pyproject.tomlRepository: sportsdataverse/sportsdataverse-py
Length of output: 1578
🏁 Script executed:
# Check if docs-quarto/_gen_reference.py exists and read the target lines
if [ -f "docs-quarto/_gen_reference.py" ]; then
echo "=== File exists. Reading lines 60-90 ===";
sed -n '60,90p' docs-quarto/_gen_reference.py;
else
echo "File not found at docs-quarto/_gen_reference.py. Searching for it...";
find . -name "_gen_reference.py" 2>/dev/null;
fiRepository: sportsdataverse/sportsdataverse-py
Length of output: 1329
🏁 Script executed:
# Check if this is a new file and its status
git log --oneline --all -- docs-quarto/_gen_reference.py 2>/dev/null | head -5
git status docs-quarto/_gen_reference.py 2>/dev/nullRepository: sportsdataverse/sportsdataverse-py
Length of output: 236
🏁 Script executed:
# Read top of the file to see imports and confirm structure
head -n 60 docs-quarto/_gen_reference.pyRepository: sportsdataverse/sportsdataverse-py
Length of output: 2049
Add complete type hints for helper functions and append module to mypy files list.
Lines 62, 71, and 88 use partially/untyped parameters in a module that must meet the repo's strict type-hint standard. The module is not yet included in the mypy files list in pyproject.toml.
- Line 62:
fnparameter is untyped - Line 71:
epuses baredict;fnis untyped - Line 88:
epuses baredict
Add imports for Callable and Any, apply full type hints, then append "docs-quarto/_gen_reference.py" to the [tool.mypy] files list in pyproject.toml.
Suggested patch
from __future__ import annotations
import inspect
import pathlib
import warnings
+from collections.abc import Callable
+from typing import Any
...
-def _summary(fn) -> str:
+def _summary(fn: Callable[..., Any]) -> str:
doc = inspect.getdoc(fn) or ""
-def _param_table(ep: dict, fn) -> str:
+def _param_table(ep: dict[str, Any], fn: Callable[..., Any]) -> str:
rows = []
-def _returns_note(ep: dict) -> str:
+def _returns_note(ep: dict[str, Any]) -> str:
parser = ep.get("parser")In pyproject.toml, append to the [tool.mypy] files list:
files = [
"sportsdataverse/_deprecation.py",
...
"sportsdataverse/mlb/mlb_statcast_extra.py",
+ "docs-quarto/_gen_reference.py",
]🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs-quarto/_gen_reference.py` around lines 62 - 88, The functions _summary,
_param_table, and _returns_note in the module lack complete type hints and the
module is not included in mypy validation. Import Callable and Any from typing
at the top of the file, then add full type hints: for _summary change the fn
parameter from untyped to Callable, for _param_table change ep from bare dict to
a proper type (likely Any) and fn from untyped to Callable, and for
_returns_note change ep from bare dict to a proper type (likely Any). Finally,
append "docs-quarto/_gen_reference.py" to the files list under [tool.mypy] in
pyproject.toml.
Source: Coding guidelines
| ::: {.callout-important} | ||
| This dynamic, namespaced generation is exactly what makes auto-introspection | ||
| documentation tools struggle with this package — there is no static `__all__` of | ||
| flat names to discover. See the [evaluation write-up](https://github.com/sportsdataverse/sportsdataverse-py/blob/proto/quarto-docs/docs-quarto/README.md) | ||
| for how that shaped the Great-Docs result. | ||
| ::: |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Find the architecture.qmd file
find . -name "architecture.qmd" -type fRepository: sportsdataverse/sportsdataverse-py
Length of output: 109
🏁 Script executed:
# Check if docs-quarto directory exists and list its contents
ls -la docs-quarto/ 2>/dev/null | head -30Repository: sportsdataverse/sportsdataverse-py
Length of output: 1072
🏁 Script executed:
# Read lines 44-49 of the architecture.qmd file
if [ -f "docs-quarto/architecture.qmd" ]; then
sed -n '44,49p' docs-quarto/architecture.qmd
fiRepository: sportsdataverse/sportsdataverse-py
Length of output: 462
🏁 Script executed:
# Check if README.md exists in docs-quarto
ls -la docs-quarto/README.md 2>/dev/nullRepository: sportsdataverse/sportsdataverse-py
Length of output: 150
🏁 Script executed:
# Check all branches in the repository
git branch -a 2>/dev/null | head -20Repository: sportsdataverse/sportsdataverse-py
Length of output: 176
Update the GitHub link to use a stable branch reference.
The link on line 47 references the proto/quarto-docs branch, which is a temporary PR branch that will not exist after merge. The README.md file exists locally in the same directory, so update to either:
- Use the stable
mainbranch:
https://github.com/sportsdataverse/sportsdataverse-py/blob/main/docs-quarto/README.md
- Or use a relative link:
[evaluation write-up](./README.md)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs-quarto/architecture.qmd` around lines 44 - 49, The "evaluation write-up"
link in the callout block uses a temporary PR branch reference
(proto/quarto-docs) that will not exist after merge. Update the link to use a
stable reference by either replacing the branch name with main in the GitHub URL
(keeping the full path to docs-quarto/README.md), or use a relative link to the
local README.md file since it exists in the same directory as architecture.qmd.
Choose whichever approach best fits the documentation's linking strategy.
| ## `espn_mlb_scoreboard` | ||
|
|
||
| ```python | ||
| espn_mlb_scoreboard(dates: int | str | None = None, *, season_type: int | None = None, | ||
| return_as_pandas: bool = False, **kwargs) -> pl.DataFrame | ||
| ``` | ||
|
|
||
| The scoreboard for a given date (`YYYYMMDD`). | ||
|
|
||
| **Parameters** | ||
|
|
||
| | Parameter | Type | Default | Description | | ||
| |---|---|---|---| | ||
| | `dates` | `int \| str` | `None` | Date in `YYYYMMDD`; defaults to today. | | ||
| | `season_type` | `int` | `None` | ESPN season type (2 = regular, 3 = postseason). | | ||
| | `return_as_pandas` | `bool` | `False` | If `True`, return a pandas DataFrame. | | ||
|
|
||
| **Returns** — a tidy `polars.DataFrame`, one row per game on the slate. | ||
|
|
||
| **Example** | ||
|
|
||
| ```{python} | ||
| from sportsdataverse.mlb import espn_mlb_scoreboard | ||
|
|
||
| sb = espn_mlb_scoreboard(dates="20240615") | ||
| print("shape:", sb.shape) | ||
| keep = [c for c in ["id", "name", "short_name", "status_type_detail"] if c in sb.columns] | ||
| sb.select(keep).head(6) | ||
| ``` |
There was a problem hiding this comment.
Document the return_parsed parameter for espn_mlb_scoreboard.
The signature and parameters table for espn_mlb_scoreboard (lines 130–158) omit the return_parsed parameter. According to the context from sportsdataverse/mlb/mlb_espn_ext.py:150–198, the actual function signature includes return_parsed: bool = True, which controls whether to return a tidy DataFrame or raw JSON Dict. The documented return type should reflect this: pl.DataFrame | dict (depending on return_parsed), not just pl.DataFrame.
This is inconsistent with the documentation for the other three functions (espn_mlb_teams, espn_mlb_team_schedule, espn_mlb_team_roster), which correctly document return_parsed.
🔧 Suggested fix
Update the signature block and parameters table:
## `espn_mlb_scoreboard`
```python
-espn_mlb_scoreboard(dates: int | str | None = None, *, season_type: int | None = None,
- return_as_pandas: bool = False, **kwargs) -> pl.DataFrame
+espn_mlb_scoreboard(dates: int | str | None = None, *, season_type: int | None = None,
+ return_parsed: bool = True,
+ return_as_pandas: bool = False, **kwargs) -> pl.DataFrame | dictParameters
| Parameter | Type | Default | Description |
|---|---|---|---|
dates |
int | str |
None |
Date in YYYYMMDD; defaults to today. |
season_type |
int |
None |
ESPN season type (2 = regular, 3 = postseason). |
| + | return_parsed |
bool |
True |
return_as_pandas |
bool |
False |
If True, return a pandas DataFrame. |
</details>
<details>
<summary>🤖 Prompt for AI Agents</summary>
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @docs-quarto/reference/mlb.qmd around lines 130 - 158, The
espn_mlb_scoreboard function documentation is missing the return_parsed
parameter that exists in the actual implementation. Add return_parsed: bool = True as a parameter in the function signature (it should be a keyword-only
parameter after the asterisk and before return_as_pandas). Update the return
type from pl.DataFrame to pl.DataFrame | dict to reflect that the return
type depends on the return_parsed value. Finally, add a new row to the
parameters table documenting return_parsed with type bool, default True,
and a description explaining that when True it returns a tidy frame and when
False it returns raw Dict data.
</details>
<!-- fingerprinting:phantom:triton:mongoose -->
<!-- cr-comment:v1:556eeceab77c18fbdcebf51b -->
<!-- This is an auto-generated comment by CodeRabbit -->
| ## `espn_nba_scoreboard` | ||
|
|
||
| ```python | ||
| espn_nba_scoreboard(dates: int | str | None = None, *, | ||
| return_as_pandas: bool = False, **kwargs) -> pl.DataFrame | ||
| ``` |
There was a problem hiding this comment.
Incomplete function signature: return_parsed parameter missing and return type inaccurate.
The documented signature omits the return_parsed: bool = True parameter. When return_parsed=False, the function returns the raw Dict, not a pl.DataFrame. This is a breaking contract difference that should be surfaced in the documentation.
The full signature from the upstream function (Context snippet 3) is:
espn_nba_scoreboard(
dates: int | str | None = None,
week: int | None = None,
season_type: int | None = None,
groups: int | str | None = None,
limit: int = 500,
*,
return_parsed: bool = True, # <-- MISSING in docs
return_as_pandas: bool = False,
**kwargs,
) -> pl.DataFrame | dict📝 Suggested fix for scoreboard signature and returns block
## `espn_nba_scoreboard`
```python
-espn_nba_scoreboard(dates: int | str | None = None, *,
- return_as_pandas: bool = False, **kwargs) -> pl.DataFrame
+espn_nba_scoreboard(dates: int | str | None = None, *,
+ return_parsed: bool = True,
+ return_as_pandas: bool = False, **kwargs) -> pl.DataFrame | dict-The scoreboard for a given date (YYYYMMDD).
+The scoreboard for a given date (YYYYMMDD). Parser-backed, so return_parsed=True is the default.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
dates |
int | str |
None |
Date in YYYYMMDD; defaults to today. |
| + | return_parsed |
bool |
True |
return_as_pandas |
bool |
False |
If True, return a pandas DataFrame. |
-Returns — a tidy polars.DataFrame, one row per game on the slate.
+Returns — a tidy polars.DataFrame, one row per game on the slate; or the raw ESPN Dict when return_parsed=False.
</details>
<details>
<summary>🤖 Prompt for AI Agents</summary>
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @docs-quarto/reference/nba.qmd around lines 88 - 93, The
espn_nba_scoreboard function documentation is missing the return_parsed
parameter and has an inaccurate return type annotation. Update the function
signature in the docstring to include return_parsed: bool = True as a
keyword-only parameter, and change the return type annotation from
pl.DataFrame to pl.DataFrame | dict to accurately reflect that it returns a
Dict when return_parsed=False. Add a new row to the Parameters table
documenting the return_parsed parameter with its default value and description
explaining it controls whether a parsed tidy frame or raw Dict is returned.
Finally, update the Returns section to clarify that it returns either a tidy
polars DataFrame or the raw ESPN Dict depending on the return_parsed parameter
value, and update the description text to note that this function is
parser-backed.
</details>
<!-- fingerprinting:phantom:triton:mongoose -->
<!-- cr-comment:v1:a7084229291acd3fcb70fbf9 -->
<!-- This is an auto-generated comment by CodeRabbit -->
…, informational CI) Make the additive Great Docs site (great-docs.yml + user_guide/*.qmd) a first-class, reproducible part of the repo without touching the runtime package or the production Docusaurus codegen docs. - pyproject: add a `docs` optional-dependencies extra carrying great-docs (flows into `all`); marker-gated to python_version >= 3.11 so the package's requires-python = ">=3.9" runtime support is unchanged. Quarto noted as a separate SYSTEM install. Re-locked uv.lock. - CLAUDE.md: new "Great Docs site" subsection under the docs toolchain area — complementary-not-replacement framing, the curated reference allow-list rationale (~2,940 dynamic wrappers -> ~90-min walk; allow-list -> ~95s build), build/preview commands + Windows PYTHONUTF8/jupyter pins, gitignore + live-API notes. Doctoc TOC regenerated. - CONTRIBUTING.md: short Great Docs subsection (install via docs extra + Quarto, build/preview, gitignore + curated-reference notes). - README.md: brief mention of the alternative executable-guides site. - CHANGELOG.md: Unreleased Docs entry. - CI: .github/workflows/great-docs.yml builds the site + uploads _site as an artifact on changes to great-docs.yml / user_guide/** / assets/**. Informational and non-blocking (continue-on-error on the live-cell build step; job named "(informational)") so a flaky live ESPN render never blocks a merge. No deployment wired in.
…clude_in_header Port the Docusaurus brand surface into the Great Docs site head: - Plausible analytics (same src + data-domain as docs/docusaurus.config.ts) - Bungee / Chivo / Lato / Fira Mono + FiraCode CDN font stack (custom.css URLs) - the cyan glow @Keyframes, retargeted to Quarto/Bootstrap selectors Injected via include_in_header: [{file: assets/head.html}] in great-docs.yml. Verified in the built _site/index.html <head>: plausible.io, Bungee, and the text-shadow glow CSS all present.
Copy the Docusaurus favicon (docs/static/img/favicon.ico -> assets/favicon.ico, 15086 bytes byte-identical) and pin it via favicon: in great-docs.yml so the browser-tab icon matches. Document why we keep navbar_style: slate (the dark brand gradient the cyan glow sits on) rather than a solid navbar_color, and that the single brand logo serves both light/dark (no dark variant on the Docusaurus site either). Built _site/index.html links ./favicon.ico and renders the logo.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
assets/head.html (1)
21-25: ⚡ Quick winRemove the duplicate Fira Code source and keep one maintained provider.
Line 21 and Line 25 both load Fira Code from different CDNs. This adds an unnecessary request and introduces avoidable fragility (the RawGit CDN is legacy). Keep only one source (prefer the Google Fonts entry already present at Line 25).
Suggested diff
-<link rel="stylesheet" href="https://cdn.rawgit.com/tonsky/FiraCode/1.205/distr/fira_code.css"> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Chivo&display=swap"> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Lato&display=swap"> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Fira+Mono&display=swap"> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Fira+Code&display=swap"> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Bungee&display=swap">🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@assets/head.html` around lines 21 - 25, The head.html file contains duplicate Fira Code font declarations loading from two different CDNs. Remove the RawGit CDN link for Fira Code (https://cdn.rawgit.com/tonsky/FiraCode/1.205/distr/fira_code.css) and keep only the Google Fonts entry (https://fonts.googleapis.com/css2?family=Fira+Code&display=swap) which is a maintained provider. This eliminates unnecessary requests and removes the fragility of relying on the legacy RawGit CDN.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/great-docs.yml:
- Around line 39-67: Replace all mutable action version tags with full
40-character commit SHAs to prevent supply-chain attacks: update
actions/checkout@v5 to use its pinned SHA, astral-sh/setup-uv@v8.1.0 to use its
pinned SHA, quarto-dev/quarto-actions/setup@v2 to use its pinned SHA, and
actions/upload-artifact@v4 to use its pinned SHA. Additionally, add
persist-credentials: false to the actions/checkout step configuration since no
authenticated Git operations are performed in this workflow and the GITHUB_TOKEN
does not need to be available to subsequent steps.
In `@pyproject.toml`:
- Around line 104-114: The pyjanitor dependency declarations in pyproject.toml
currently have only a lower bound constraint (>=0.23.1) without an upper bound,
which could cause compatibility issues since pyjanitor 0.32.18+ requires pandas
3.x. Locate all three pyjanitor entries in the dependencies section of
pyproject.toml (referenced at lines 68, 96, and 134) and update each instance by
adding the upper bound constraint to read pyjanitor>=0.23.1,<0.32.18 instead of
just the lower bound.
---
Nitpick comments:
In `@assets/head.html`:
- Around line 21-25: The head.html file contains duplicate Fira Code font
declarations loading from two different CDNs. Remove the RawGit CDN link for
Fira Code (https://cdn.rawgit.com/tonsky/FiraCode/1.205/distr/fira_code.css) and
keep only the Google Fonts entry
(https://fonts.googleapis.com/css2?family=Fira+Code&display=swap) which is a
maintained provider. This eliminates unnecessary requests and removes the
fragility of relying on the legacy RawGit CDN.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 5e1dbbf5-0c19-4369-88d9-9814dd5f1309
⛔ Files ignored due to path filters (1)
uv.lockis excluded by!**/*.lock
📒 Files selected for processing (9)
.github/workflows/great-docs.ymlCHANGELOG.mdCLAUDE.mdCONTRIBUTING.mdREADME.mdassets/head.htmldocs/src/pages/CHANGELOG.mdgreat-docs.ymlpyproject.toml
✅ Files skipped from review due to trivial changes (3)
- README.md
- CHANGELOG.md
- docs/src/pages/CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (1)
- great-docs.yml
| - uses: actions/checkout@v5 | ||
| - name: Install uv | ||
| uses: astral-sh/setup-uv@v8.1.0 | ||
| with: | ||
| version: "latest" | ||
| enable-cache: true | ||
| cache-dependency-glob: "uv.lock" | ||
| - name: Set up Python | ||
| run: uv python install 3.13.2 | ||
| - name: Install Linux build deps for pyreadr sdist fallback | ||
| run: | | ||
| sudo apt-get update | ||
| sudo apt-get install -y libbz2-dev liblzma-dev | ||
| - name: Install project + docs extra | ||
| # The package itself (so `import sportsdataverse` works for live | ||
| # introspection / executable cells) plus the `docs` extra (great-docs). | ||
| run: uv sync --extra docs --extra all --frozen --python 3.13.2 | ||
| - name: Install Quarto (system dependency) | ||
| uses: quarto-dev/quarto-actions/setup@v2 | ||
| - name: Build Great Docs site | ||
| # Non-blocking: the user-guide cells hit the live ESPN API at build time, | ||
| # so a transient upstream outage must not red-fail the PR. | ||
| continue-on-error: true | ||
| env: | ||
| PYTHONUTF8: "1" | ||
| run: uv run --frozen great-docs build | ||
| - name: Upload rendered site | ||
| if: always() | ||
| uses: actions/upload-artifact@v4 |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n .github/workflows/great-docs.yml | sed -n '35,70p'Repository: sportsdataverse/sportsdataverse-py
Length of output: 1710
🌐 Web query:
GitHub Actions security best practices action pinning commit SHA
💡 Result:
Pinning GitHub Actions to a full-length (40-character) commit SHA is a foundational security best practice designed to protect workflows against supply chain attacks [1][2][3]. Why Pinning is Essential GitHub Actions referenced by tags (e.g., @v1) or branches (e.g., @main) are mutable, meaning the code they execute can be changed by anyone with write access to the action's repository [1][4][5]. If an action's repository or a maintainer's account is compromised, attackers can update these tags to point to malicious code, which would then be automatically executed by your workflows [2][6][3]. Pinning to an immutable, full-length commit SHA ensures that your workflows always execute the exact, verified code you intended, regardless of any future changes to the upstream repository [2][4][7]. Best Practices for Implementation - Use Full-Length SHAs: Always use the complete 40-character commit SHA [3][8]. Short SHAs create ambiguity and potential for collisions; full SHAs guarantee unique, deterministic references [8]. - Automate Updates: Manually updating SHAs is error-prone and burdensome [7]. Use automated dependency tools like Dependabot or Renovate to manage updates [6][9]. These tools can be configured to keep your SHAs current while maintaining the security benefits of immutability [6][3]. - Include Human-Readable Comments: When pinning, add a comment in your YAML file with the corresponding version tag (e.g., uses: actions/checkout@sha # v4.2.0) [2][3]. This provides context for human readers and allows automated tools to parse the intended version for future updates [3]. - Enforce via Policy: Administrators can enforce SHA pinning at the repository or organization level through GitHub's allowed actions policy [1][10][4]. When enabled, this policy causes any workflow attempting to use an unpinned (tag-based) action to fail, ensuring consistent security compliance across the organization [10][4][7]. - Audit Dependencies: Pinning only protects the specific action you reference. If that action internally calls other unpinned actions or fetches external dependencies (like Docker images or scripts), those remain potential attack vectors [4][6]. Regularly audit your dependency tree and use security tools like Scorecard, zizmor, or other dedicated scanners to identify insecure practices [4][11][9]. For organizations or projects looking to automate this, tools such as pinact, frizbee, and ratchet are specifically designed to convert existing tag-based references to commit SHAs across your workflow files [4][9].
Citations:
- 1: https://docs.github.com/en/actions/reference/security/secure-use
- 2: https://www.stepsecurity.io/blog/pinning-github-actions-for-enhanced-security-a-complete-guide
- 3: https://safeguard.sh/resources/blog/how-to-pin-github-actions-to-shas-correctly
- 4: https://blogs.eclipse.org/post/mika%C3%ABl-barbero/stop-trusting-mutable-references-how-eclipse-foundation-projects-should-harden
- 5: https://nearform.com/insights/why-you-should-pin-your-github-actions-by-commit-hash/
- 6: https://emmer.dev/blog/pin-your-github-actions-to-protect-against-mutability/
- 7: https://www.romainlespinasse.dev/posts/github-actions-commit-sha-pinning/
- 8: https://actsense.dev/vulnerabilities/short_hash_pinning/
- 9: https://www.cncf.io/blog/2026/05/04/securing-github-actions-ci-dependencies-recipe-card/
- 10: https://github.blog/changelog/2025-08-15-github-actions-policy-now-supports-blocking-and-sha-pinning-actions/
- 11: https://github.com/github/docs/blob/962a1c8dccb8c0f66548b324e5b921b5e4fbc3d6/content/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions.md
🌐 Web query:
GitHub Actions persist-credentials security implications
💡 Result:
In GitHub Actions, the persist-credentials input for the actions/checkout action controls whether the repository's authentication token (typically the GITHUB_TOKEN) is configured in the local Git environment [1][2][3]. When set to true (the default), this configuration allows subsequent steps in the same job to execute authenticated Git commands—such as git push or git fetch—without needing to manually handle the token [1][4]. Security Implications The primary security risk associated with persist-credentials: true is credential exposure [5][6]. Because the credentials are made available to the Git configuration, any malicious or compromised step, script, or third-party action executed later in the same job can potentially access and abuse these credentials to perform unauthorized API calls or modify the repository [7][5][8]. Specific risks include: - Artifact Leakage: If a workflow uploads the workspace (or its .git directory) as a build artifact, the persisted credentials stored within the Git configuration may be included in the artifact [6][8]. Anyone with access to download these artifacts could then extract the token [8]. - Privilege Abuse: Because the token is available throughout the job, an attacker can use it to perform any action authorized by the token's permissions, which could include modifying repository code or accessing protected assets [5][8]. Recent Improvements Historically, actions/checkout stored these credentials directly in .git/config [6][8]. However, recent versions of actions/checkout (starting with v6) have improved security by storing credentials in a separate file under $RUNNER_TEMP rather than directly in the repository's Git configuration [1][4]. While this change mitigates certain leakage vectors—such as accidental artifact inclusion—it remains best practice to disable this behavior unless it is explicitly required for your workflow's operations [5][8]. Recommendation Unless your workflow requires authenticated Git operations (e.g., pushing commits back to the repository), you should explicitly set persist-credentials: false in your actions/checkout step [5][6]: - uses: actions/checkout@v4 with: persist-credentials: false If you do require authentication, ensure that your GITHUB_TOKEN permissions are configured to follow the principle of least privilege [1][3].
Citations:
- 1: https://github.com/actions/checkout/?tab=readme-ov-file
- 2: https://github.com/actions/checkout/blob/main/action.yml
- 3: https://github.com/marketplace/actions/checkout?version=v3.6.0
- 4: https://github.com/actions/checkout/tree/v6.0.2
- 5: https://actsense.dev/vulnerabilities/unsafe_checkout/
- 6: https://yossarian.net/til/post/actions-checkout-can-leak-github-credentials/
- 7: Remove
persist-credentialsor change the default tofalseactions/checkout#485 - 8: https://sisaku-security.github.io/lint/docs/rules/artipacked/
Pin actions to commit SHAs and disable credential persistence in checkout.
Actions on lines 39, 41, 57, and 67 are referenced by mutable tags (@v5, @v8.1.0, @v2, @v4) instead of full commit SHAs, which exposes the workflow to supply-chain attacks if these repositories are compromised. Additionally, line 39 relies on the default persist-credentials: true, making the GITHUB_TOKEN available to all subsequent steps—unnecessary here since no authenticated Git operations are performed. Use full 40-character commit SHAs for all action steps and set persist-credentials: false on checkout.
Suggested diff pattern
- - uses: actions/checkout@v5
+ - uses: actions/checkout@<full_commit_sha>
+ with:
+ persist-credentials: false
...
- - name: Install uv
- uses: astral-sh/setup-uv@v8.1.0
+ - name: Install uv
+ uses: astral-sh/setup-uv@<full_commit_sha>
...
- - name: Install Quarto (system dependency)
- uses: quarto-dev/quarto-actions/setup@v2
+ - name: Install Quarto (system dependency)
+ uses: quarto-dev/quarto-actions/setup@<full_commit_sha>
...
- - name: Upload rendered site
- if: always()
- uses: actions/upload-artifact@v4
+ - name: Upload rendered site
+ if: always()
+ uses: actions/upload-artifact@<full_commit_sha>🧰 Tools
🪛 zizmor (1.25.2)
[warning] 39-39: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false
(artipacked)
[error] 39-39: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 41-41: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 57-57: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
[error] 67-67: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/great-docs.yml around lines 39 - 67, Replace all mutable
action version tags with full 40-character commit SHAs to prevent supply-chain
attacks: update actions/checkout@v5 to use its pinned SHA,
astral-sh/setup-uv@v8.1.0 to use its pinned SHA,
quarto-dev/quarto-actions/setup@v2 to use its pinned SHA, and
actions/upload-artifact@v4 to use its pinned SHA. Additionally, add
persist-credentials: false to the actions/checkout step configuration since no
authenticated Git operations are performed in this workflow and the GITHUB_TOKEN
does not need to be available to subsequent steps.
Source: Linters/SAST tools
| docs = [ | ||
| # Great Docs builds the alternative executable-guides + auto-introspected | ||
| # reference site (great-docs.yml + user_guide/*.qmd). It is COMPLEMENTARY to | ||
| # the production Docusaurus codegen docs, not a replacement. Docs-only — never | ||
| # ships to users. NOTE: Great Docs additionally requires Quarto, which is a | ||
| # SYSTEM install (not a pip package) — install it from <https://quarto.org>. | ||
| # Marker-gated to py>=3.11 (great-docs requires it); this keeps the package's | ||
| # `requires-python = ">=3.9"` runtime support untouched — on 3.9/3.10 the docs | ||
| # extra simply resolves without great-docs. | ||
| "great-docs>=0.14.0; python_version >= '3.11'", | ||
| ] |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Read-only verification: confirm the defensive pyjanitor upper bound exists.
rg -nP '^\s*"pyjanitor<0\.32\.18' pyproject.tomlRepository: sportsdataverse/sportsdataverse-py
Length of output: 60
🏁 Script executed:
rg -n 'pyjanitor' pyproject.tomlRepository: sportsdataverse/sportsdataverse-py
Length of output: 163
Add defensive pyjanitor<0.32.18 upper bound to all dependency declarations in pyproject.toml.
All three pyjanitor entries (lines 68, 96, 134) currently have only a lower bound (>=0.23.1). Per coding guidelines, the defensive upper bound must be present until pandas 3 is the project floor, since pyjanitor 0.32.18+ silently switched to pandas 3.x. Update each instance to "pyjanitor>=0.23.1,<0.32.18".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@pyproject.toml` around lines 104 - 114, The pyjanitor dependency declarations
in pyproject.toml currently have only a lower bound constraint (>=0.23.1)
without an upper bound, which could cause compatibility issues since pyjanitor
0.32.18+ requires pandas 3.x. Locate all three pyjanitor entries in the
dependencies section of pyproject.toml (referenced at lines 68, 96, and 134) and
update each instance by adding the upper bound constraint to read
pyjanitor>=0.23.1,<0.32.18 instead of just the lower bound.
Sources: Coding guidelines, Learnings
Convert docs/docs/tutorials/NN_*.md -> user_guide/NN-*.qmd via
tools/convert_tutorials_to_qmd.py (a build-time authoring tool, not shipped):
- Quarto frontmatter (title/description/jupyter), Docusaurus frontmatter dropped
- code fences -> {python} cells with #| eval: false so the build never hits the
live ESPN / native / odds / Statcast APIs (fast + non-flaky); each page carries
a callout pointing at the two executable guides for live output
- internal links rewritten to the Great Docs tree (tutorial cross-links + the
per-sport reference links collapse to ../reference/index.qmd)
Two explicit user_guide sidebar sections in great-docs.yml: 'User guide' (the
executable getting-started pair, renamed prefix-less so the navbar landing link
resolves) + 'Tutorials' (the 15 ported walkthroughs). Build: 17 user-guide pages,
3 warnings (pre-existing README/CONTRIBUTING), ~2m40s.
Replace the hand-curated reference: block in great-docs.yml with a generated one. New tools/codegen/great_docs_reference.py enumerates the high-value espn_<prefix>_<short> entry points per league from tools/codegen/endpoints/ leagues.yaml, emitting only names that are real importable top-level exports of sportsdataverse, grouped by sport (football/basketball/baseball/hockey/soccer/ cricket). The block is spliced between # >>> / # <<< markers so re-running refreshes it in place; the NFL-loaders + PBP/config tail sections are preserved. Wired into generate.py: python tools/codegen/generate.py --great-docs # regenerate python tools/codegen/generate.py --great-docs --check # drift guard (rc=1 if stale) The drift check also folds into the main --check gate (CI). Coverage jumps from 8 ESPN sports to 17 league sections — now auto-includes men's/women's college hockey, college baseball/softball, UFL/XFL/CFL, soccer, and cricket. Build: 175 reference pages, every generated symbol resolves (Great Docs introspects them live). splice() emits a single trailing newline so it agrees with end-of-file-fixer; sys.path shim resolves the local source.
Add a dismissable announcement: banner linking OUT to the interactive docs (https://py.sportsdataverse.org) — Great Docs is a static Quarto site with no analogue of the Docusaurus React code playground, so this is the one honest parity gap. Document the full parity work (brand head, codegen-generated reference + regen command, tutorials, the playground caveat) in GREAT-DOCS-NOTES.md and the CLAUDE.md Great Docs subsection.
Adds a real Great Docs documentation site for
sportsdataverse— a Quarto-based static site with an auto-introspected API reference and executable user-guide cells (real DataFrames frozen at build time). This is additive and evaluative: it does NOT replace the existing Docusaurus codegen docs; it stands up Great Docs as an option and documents the trade-offs.Did Great Docs auto-discovery actually work? — Yes
great-docs build→ 19/19 steps, ~95s, 85 HTML pages / 95 documented symbols (reproducible from clean).The real blocker (and why a first pass looked "non-viable"):
import sportsdataverseregisters ~2,940 names viamake_league_module(), and Great Docs' discovery validates every name with a ~2s griffeget_object()call → ~90-min walk → timeout. Discovery itself is fine; the O(n)×2s validation loop is the cost.The fix: pin an explicit curated
reference:allow-list ingreat-docs.yml. That takes the lightweight_categorize_referenced_objectspath and skips the validation loop, plussource.enabled: falseto drop the one other unscoped griffe walk. No blanketexcludeneeded — scoping is the allow-list.What's here
great-docs.yml—module: sportsdataverse,dynamic: true,parser: google,source.enabled: false; themenavbar_style: slate+content_style: sky+dark_mode_toggle+ hero/tagline + GitHub widget; a curatedreference:of 10 sections (8 sports × ESPN entry points + NFL loaders + PBP/config classes) = 95 symbols.user_guide/00-getting-started.qmd,01-scoreboards-and-schedules.qmd— executable cells with real build-time output (espn_nba_teams() → 30×14, a scoreboard table, schedule500×70, computed prose).great-docs-preview/— committedrendered-preview.html(opens with no build) + 3 screenshots (home / reference / user-guide) so the result is visible without building.GREAT-DOCS-NOTES.md— the honest write-up + the two Windows workarounds (PYTHONUTF8=1; ajupyter: python3frontmatter pin)..gitignore— the ephemeralgreat-docs/build dir.docs-quarto/is kept as the comparison point.Verdict (Great Docs vs the Docusaurus codegen)
Complementary. Great Docs wins on the curated public surface + executable guides (live DataFrames beat drift-prone schema tables; batteries-included theme/search/llms.txt). The Docusaurus codegen wins on full ~800-wrapper breadth + per-column returns tables (both YAML-driven). Python could run both.
Preview
Or open
great-docs-preview/rendered-preview.html.CI
Green (CodeRabbit, Vercel, codegen --check + tests, pytest on macOS/Ubuntu/Windows × py3.9/3.13).
Summary by CodeRabbit
Release Notes