fungo 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fungo-1.0.0/.flake8 +8 -0
- fungo-1.0.0/.github/workflows/ci.yml +52 -0
- fungo-1.0.0/.gitignore +126 -0
- fungo-1.0.0/.pre-commit-config.yaml +26 -0
- fungo-1.0.0/CHANGELOG.md +42 -0
- fungo-1.0.0/CLAUDE.md +80 -0
- fungo-1.0.0/LICENSE +21 -0
- fungo-1.0.0/MIGRATION.md +41 -0
- fungo-1.0.0/PKG-INFO +420 -0
- fungo-1.0.0/README.md +386 -0
- fungo-1.0.0/pyproject.toml +75 -0
- fungo-1.0.0/src/fungo/__init__.py +44 -0
- fungo-1.0.0/src/fungo/cli.py +298 -0
- fungo-1.0.0/src/fungo/constants.py +392 -0
- fungo-1.0.0/src/fungo/exceptions.py +80 -0
- fungo-1.0.0/src/fungo/frame.py +55 -0
- fungo-1.0.0/src/fungo/http.py +235 -0
- fungo-1.0.0/src/fungo/lookup.py +186 -0
- fungo-1.0.0/src/fungo/mlb/__init__.py +215 -0
- fungo-1.0.0/src/fungo/mlb/constants.py +710 -0
- fungo-1.0.0/src/fungo/mlb/discovery.py +206 -0
- fungo-1.0.0/src/fungo/mlb/games.py +343 -0
- fungo-1.0.0/src/fungo/mlb/misc.py +604 -0
- fungo-1.0.0/src/fungo/mlb/people.py +220 -0
- fungo-1.0.0/src/fungo/mlb/stats.py +273 -0
- fungo-1.0.0/src/fungo/mlb/stats_api.py +124 -0
- fungo-1.0.0/src/fungo/mlb/teams.py +415 -0
- fungo-1.0.0/src/fungo/py.typed +0 -0
- fungo-1.0.0/src/fungo/statcast/__init__.py +144 -0
- fungo-1.0.0/src/fungo/statcast/leaderboards.py +2121 -0
- fungo-1.0.0/src/fungo/statcast/physics.py +395 -0
- fungo-1.0.0/src/fungo/statcast/search.py +492 -0
- fungo-1.0.0/tests/mlb/__init__.py +0 -0
- fungo-1.0.0/tests/mlb/test_mlb.py +987 -0
- fungo-1.0.0/tests/statcast/__init__.py +0 -0
- fungo-1.0.0/tests/statcast/test_leaderboards.py +871 -0
- fungo-1.0.0/tests/statcast/test_live.py +49 -0
- fungo-1.0.0/tests/statcast/test_physics.py +167 -0
- fungo-1.0.0/tests/statcast/test_search.py +393 -0
- fungo-1.0.0/tests/test_cli.py +266 -0
- fungo-1.0.0/tests/test_constants.py +96 -0
- fungo-1.0.0/tests/test_exceptions.py +46 -0
- fungo-1.0.0/tests/test_frame.py +97 -0
- fungo-1.0.0/tests/test_http.py +306 -0
- fungo-1.0.0/tests/test_lookup.py +242 -0
- fungo-1.0.0/tests/test_release_scaffolding.py +116 -0
- fungo-1.0.0/uv.lock +763 -0
fungo-1.0.0/.flake8
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
concurrency:
|
|
8
|
+
group: ci-${{ github.ref }}
|
|
9
|
+
cancel-in-progress: true
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
test:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
strategy:
|
|
15
|
+
fail-fast: false
|
|
16
|
+
matrix:
|
|
17
|
+
python-version: ["3.12", "3.13", "3.14"]
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Install uv
|
|
22
|
+
uses: astral-sh/setup-uv@v6
|
|
23
|
+
with:
|
|
24
|
+
enable-cache: true
|
|
25
|
+
|
|
26
|
+
- name: Install Python
|
|
27
|
+
run: uv python install ${{ matrix.python-version }}
|
|
28
|
+
|
|
29
|
+
- name: Sync dependencies
|
|
30
|
+
run: uv sync --python ${{ matrix.python-version }} --all-extras --dev
|
|
31
|
+
|
|
32
|
+
- name: Lint (ruff check)
|
|
33
|
+
run: uv run ruff check .
|
|
34
|
+
|
|
35
|
+
- name: Format check (ruff format)
|
|
36
|
+
run: uv run ruff format --check .
|
|
37
|
+
|
|
38
|
+
- name: Type-check (strict)
|
|
39
|
+
run: uv run mypy
|
|
40
|
+
|
|
41
|
+
- name: Test (offline) with coverage gate
|
|
42
|
+
run: uv run pytest --cov=fungo --cov-report=term-missing --cov-fail-under=100
|
|
43
|
+
|
|
44
|
+
- name: Build distribution
|
|
45
|
+
run: uv build
|
|
46
|
+
|
|
47
|
+
- name: Smoke installed wheel
|
|
48
|
+
run: |
|
|
49
|
+
python -m venv /tmp/fungo-smoke
|
|
50
|
+
/tmp/fungo-smoke/bin/python -m pip install dist/*.whl
|
|
51
|
+
/tmp/fungo-smoke/bin/python -c "import fungo; assert fungo.__version__ == '1.0.0'"
|
|
52
|
+
/tmp/fungo-smoke/bin/fungo --help
|
fungo-1.0.0/.gitignore
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# macOS
|
|
2
|
+
.DS_Store
|
|
3
|
+
.vscode
|
|
4
|
+
.idea
|
|
5
|
+
|
|
6
|
+
# Data
|
|
7
|
+
*.csv
|
|
8
|
+
!data/*/*.csv
|
|
9
|
+
dev/
|
|
10
|
+
|
|
11
|
+
# Byte-compiled / optimized / DLL files
|
|
12
|
+
__pycache__/
|
|
13
|
+
*.py[cod]
|
|
14
|
+
*$py.class
|
|
15
|
+
|
|
16
|
+
# C extensions
|
|
17
|
+
*.so
|
|
18
|
+
|
|
19
|
+
# Distribution / packaging
|
|
20
|
+
.Python
|
|
21
|
+
build/
|
|
22
|
+
develop-eggs/
|
|
23
|
+
dist/
|
|
24
|
+
downloads/
|
|
25
|
+
eggs/
|
|
26
|
+
.eggs/
|
|
27
|
+
lib/
|
|
28
|
+
lib64/
|
|
29
|
+
parts/
|
|
30
|
+
sdist/
|
|
31
|
+
var/
|
|
32
|
+
wheels/
|
|
33
|
+
share/python-wheels/
|
|
34
|
+
*.egg-info/
|
|
35
|
+
.installed.cfg
|
|
36
|
+
*.egg
|
|
37
|
+
MANIFEST
|
|
38
|
+
|
|
39
|
+
# PyInstaller
|
|
40
|
+
# Usually these files are written by a python script from a template
|
|
41
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
42
|
+
*.manifest
|
|
43
|
+
*.spec
|
|
44
|
+
|
|
45
|
+
# Installer logs
|
|
46
|
+
pip-log.txt
|
|
47
|
+
pip-delete-this-directory.txt
|
|
48
|
+
|
|
49
|
+
# Unit test / coverage reports
|
|
50
|
+
htmlcov/
|
|
51
|
+
.tox/
|
|
52
|
+
.nox/
|
|
53
|
+
.coverage
|
|
54
|
+
.coverage.*
|
|
55
|
+
.cache
|
|
56
|
+
nosetests.xml
|
|
57
|
+
coverage.xml
|
|
58
|
+
*.cover
|
|
59
|
+
.hypothesis/
|
|
60
|
+
.pytest_cache/
|
|
61
|
+
cassettes/
|
|
62
|
+
|
|
63
|
+
# Translations
|
|
64
|
+
*.mo
|
|
65
|
+
*.pot
|
|
66
|
+
|
|
67
|
+
# Django stuff:
|
|
68
|
+
*.log
|
|
69
|
+
local_settings.py
|
|
70
|
+
db.sqlite3
|
|
71
|
+
|
|
72
|
+
# Flask stuff:
|
|
73
|
+
instance/
|
|
74
|
+
.webassets-cache
|
|
75
|
+
|
|
76
|
+
# Scrapy stuff:
|
|
77
|
+
.scrapy
|
|
78
|
+
|
|
79
|
+
# Sphinx documentation
|
|
80
|
+
docs/_build/
|
|
81
|
+
|
|
82
|
+
# PyBuilder
|
|
83
|
+
target/
|
|
84
|
+
|
|
85
|
+
# Jupyter Notebook
|
|
86
|
+
.ipynb_checkpoints
|
|
87
|
+
|
|
88
|
+
# iPython
|
|
89
|
+
profile_default/
|
|
90
|
+
ipython_config.py
|
|
91
|
+
|
|
92
|
+
# pyenv
|
|
93
|
+
.python-version
|
|
94
|
+
|
|
95
|
+
# celery beat schedule file
|
|
96
|
+
celerybeat-schedule
|
|
97
|
+
|
|
98
|
+
# SageMath parsed files
|
|
99
|
+
*.sage.py
|
|
100
|
+
|
|
101
|
+
# Environments
|
|
102
|
+
.env
|
|
103
|
+
.venv
|
|
104
|
+
env/
|
|
105
|
+
venv/
|
|
106
|
+
ENV/
|
|
107
|
+
env.bak/
|
|
108
|
+
venv.bak/
|
|
109
|
+
|
|
110
|
+
# Spyder project settings
|
|
111
|
+
.spyderproject
|
|
112
|
+
.spyproject
|
|
113
|
+
|
|
114
|
+
# Rope project settings
|
|
115
|
+
.ropeproject
|
|
116
|
+
|
|
117
|
+
# mkdocs documentation
|
|
118
|
+
/site
|
|
119
|
+
|
|
120
|
+
# mypy
|
|
121
|
+
.mypy_cache/
|
|
122
|
+
.dmypy.json
|
|
123
|
+
dmypy.json
|
|
124
|
+
|
|
125
|
+
# Pyre type checker
|
|
126
|
+
.pyre/
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v5.0.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: check-added-large-files
|
|
6
|
+
- id: check-toml
|
|
7
|
+
types: [toml]
|
|
8
|
+
- id: check-yaml
|
|
9
|
+
types: [yaml]
|
|
10
|
+
- id: end-of-file-fixer
|
|
11
|
+
types: [text]
|
|
12
|
+
stages: [pre-commit, pre-push, manual]
|
|
13
|
+
- id: trailing-whitespace
|
|
14
|
+
types: [text]
|
|
15
|
+
stages: [pre-commit, pre-push, manual]
|
|
16
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
17
|
+
rev: v0.12.0
|
|
18
|
+
hooks:
|
|
19
|
+
- id: ruff
|
|
20
|
+
args: [--fix]
|
|
21
|
+
- id: ruff-format
|
|
22
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
23
|
+
rev: v1.16.0
|
|
24
|
+
hooks:
|
|
25
|
+
- id: mypy
|
|
26
|
+
pass_filenames: false
|
fungo-1.0.0/CHANGELOG.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format is based on
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
|
|
5
|
+
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [1.0.0]
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `fungo.mlb`, a typed pass-through wrapper surface for MLB Stats API
|
|
12
|
+
endpoints.
|
|
13
|
+
- Baseball Savant leaderboard registry with typed wrappers and explicit
|
|
14
|
+
season-parameter handling for bat-tracking boards.
|
|
15
|
+
- Statcast pitch-level search helpers for games, teams, matchups, and pitcher
|
|
16
|
+
arsenal aggregation.
|
|
17
|
+
- Nathan-style derived spin physics columns via `fungo.statcast.physics`.
|
|
18
|
+
- Chadwick Bureau player lookup and ID converters with local cache refresh.
|
|
19
|
+
- `fungo` CLI with lookup, search, leaderboard, and MLB subcommands.
|
|
20
|
+
- Optional pandas/polars DataFrame conversion through `fungo.to_frame`.
|
|
21
|
+
- `py.typed` marker for inline type annotations.
|
|
22
|
+
- GitHub Actions CI across Python 3.12-3.14 with ruff, mypy, offline pytest
|
|
23
|
+
coverage, package build, and wheel install smoke tests.
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- Packaged with Hatchling, uv, PEP 621 metadata, and runtime-only extras.
|
|
28
|
+
- The public API is organized around `fungo.statcast`, `fungo.mlb`, and
|
|
29
|
+
`fungo.lookup`.
|
|
30
|
+
- Live tests are opt-in; default test runs are fully offline.
|
|
31
|
+
|
|
32
|
+
### Removed
|
|
33
|
+
|
|
34
|
+
- Removed pre-1.0 experimental modules: `fungo.download`, `fungo.field`,
|
|
35
|
+
`fungo.query`, `fungo.statcast.analysis`, `fungo.statcast.base`,
|
|
36
|
+
`fungo.statcast.leaderboard`, and `fungo.statcast.query`.
|
|
37
|
+
|
|
38
|
+
### Notes
|
|
39
|
+
|
|
40
|
+
- `fungo.statcast.get_top_performers` raises `SavantError` because Baseball
|
|
41
|
+
Savant currently serves that page with an empty inline JSON payload and
|
|
42
|
+
renders the cards directly in HTML.
|
fungo-1.0.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## What this is
|
|
6
|
+
|
|
7
|
+
`fungo` acquires baseball data from public web sources: Baseball Savant (Statcast pitch-level search + ~42 leaderboards), the MLB Stats API (`statsapi.mlb.com`), and the Chadwick Bureau player-ID register.
|
|
8
|
+
|
|
9
|
+
The **core is stdlib-only** (no third-party runtime deps — `dependencies = []` in `pyproject.toml`). It returns **raw data**, not DataFrames:
|
|
10
|
+
|
|
11
|
+
- CSV endpoints (Statcast search, most leaderboards, lookup) return `list[dict]`; **every value is a string** (`""` for empty cells). Callers cast themselves.
|
|
12
|
+
- JSON endpoints (the MLB Stats API, HTML-backed Savant leaderboards) return the raw `dict` / `list` exactly as the source produced it.
|
|
13
|
+
|
|
14
|
+
DataFrame conversion is **optional and explicit** via `fungo.frame.to_frame(rows, backend="polars" | "pandas")`, behind extras. `rich` progress bars are also an opt-in extra. The library never writes to stdout unless `progress=True` is passed.
|
|
15
|
+
|
|
16
|
+
## Commands
|
|
17
|
+
|
|
18
|
+
- Env / install (Python `>=3.12`): `uv venv` then `uv pip install -e ".[dev]"`.
|
|
19
|
+
- Lint / format: `uv run ruff check .` and `uv run ruff format .` (line length 88; lint selects `E,F,W,I,UP,B,SIM,C4,RUF`).
|
|
20
|
+
- Types: `uv run mypy src` (strict mode; `files = ["src"]`).
|
|
21
|
+
- Tests: `uv run pytest`. Tests are **offline by default** — `addopts = "-m 'not live'"` deselects network tests. Live tests are marked `@pytest.mark.live`; run them with `uv run pytest -m live`. Run one test: `uv run pytest tests/statcast/test_leaderboards.py::test_name`.
|
|
22
|
+
|
|
23
|
+
### CLI
|
|
24
|
+
|
|
25
|
+
A single console script, `fungo`, with four subcommands (registered via `[project.scripts]` in `pyproject.toml`). `cli.py` has no `__main__` guard and there is no `__main__.py`, so **`python -m fungo` does not work** — invoke the installed `fungo` script.
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
fungo lookup [--name NAME | --mlbam ID] [--no-mlb-only] [-o OUT] [--format {csv,json}]
|
|
29
|
+
fungo search --start YYYY-MM-DD --end YYYY-MM-DD [--player-type pitcher|batter] [--player-id ID] [--level mlb|milb] [--<field>=val ...]
|
|
30
|
+
fungo leaderboard SLUG [--season YEARS] [--type T] [--player-id ID] [--<field>=val ...] | fungo leaderboard --list [--category C]
|
|
31
|
+
fungo mlb FUNCTION [--<field>=val ...] | fungo mlb --list
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
- `--format` defaults to `csv` for `lookup`/`search`/`leaderboard` and `json` for `mlb`. CSV rendering requires a `list[dict]`; non-tabular results error and ask for `--format json`.
|
|
35
|
+
- `search` and `leaderboard`/`mlb` accept arbitrary `--field=value` (or `--field value`) passthrough via `parse_known_args` → `_parse_extras`. **Only `search`** pipe-joins comma-separated values (`--pitch-type=FF,SL` → `FF|SL`, Savant's convention); the others pass values verbatim.
|
|
36
|
+
- `--season 2023,2024` becomes a `list[int]`, valid only for the bat-tracking season-array / camelCase boards (see below). Passing a multi-year value to any other (`int`-format) board raises `ValidationError` — `_emit_year` fails loud rather than silently sending a mangled `year=`.
|
|
37
|
+
|
|
38
|
+
## Architecture
|
|
39
|
+
|
|
40
|
+
Two layers: a **stdlib generic engine** and a **source-specific specialization** on top of it.
|
|
41
|
+
|
|
42
|
+
### Generic engine
|
|
43
|
+
|
|
44
|
+
- `http.py` — stdlib transport. `request_bytes` (urlencode with `safe="|"` so pipe-delimited params survive, `doseq=True` so list values become repeated keys, drops `None` values, exponential-backoff retry on 5xx/network, raises `RequestError` on 4xx or exhausted retries). `request_json` = `request_bytes` + JSON decode. `parse_csv` (BOM-aware via `utf-8-sig`, preserves empty strings). `map_concurrent` (bounded `ThreadPoolExecutor`, preserves input order, optional inter-submit `delay`, opt-in silent `rich` progress). Uses PEP-695 generics.
|
|
45
|
+
- `exceptions.py` — `FungoError` base; `RequestError`, `SavantError`, `MLBStatsError`, and `ValidationError(value, field_name, valid_values=None)` (also a `ValueError`; emits a `difflib` "did you mean?" suggestion). Reuse these — don't raise bare `ValueError`.
|
|
46
|
+
- `constants.py` — `PITCH_TYPES`, `TEAMS` (enriched club metadata), and resolvers `resolve_team` / `resolve_pitch_type` / `resolve_hand` (raise `ValidationError` on a miss).
|
|
47
|
+
- `lookup.py` — Chadwick register cross-reference (MLBAM ↔ FanGraphs ↔ Baseball-Reference ↔ Retrosheet). Downloads the ~6 MB register (16 shards) on first use and caches it under the stdlib user cache dir (`$XDG_CACHE_HOME` or `~/.cache` → `fungo/chadwick_people.csv`). `lookup(...)`, `refresh()`, and `mlbam_to_*` / `*_to_mlbam` converters.
|
|
48
|
+
- `frame.py` — optional `to_frame(rows, backend)`; imports the backend lazily and raises an actionable `ImportError` if the extra isn't installed.
|
|
49
|
+
|
|
50
|
+
### Statcast specialization (`statcast/`)
|
|
51
|
+
|
|
52
|
+
- `search.py` — `statcast_search(start_date, end_date, ...)` and the convenience wrappers `search_game`, `search_matchup`, `search_team`, plus `aggregate_pitcher_arsenal` / `get_pitcher_arsenal`. `fetch_csv` adds Savant-specific **HTML-vs-CSV detection**: Savant serves an HTML page with HTTP 200 (not a 4xx) when given params it can't fulfill — that's caught and raised as `SavantError`. `statcast_search` returns **raw rows**; it does NOT add physics columns.
|
|
53
|
+
- `leaderboards.py` — the `LEADERBOARDS` registry (~42 entries), the `year_format`-aware `fetch_leaderboard` + registry-checked `get_leaderboard`, ~33 typed CSV wrappers (`get_exit_velocity_barrels`, `get_expected_statistics`, `get_pitch_movement`, `get_poptime`, `get_catcher_framing`, …), 4 HTML-backed wrappers (`get_park_factors`, `get_hot_stove`, `get_top_performers`, `get_rolling_windows`, via `fetch_html_json`), and the introspection helpers `list_leaderboards` / `describe_leaderboard`. **To add or change a Statcast field, edit the registry dict in `leaderboards.py`** — that's the single source of truth.
|
|
54
|
+
- `physics.py` — Nathan (2021) spin-physics derived columns (stdlib `math` only). `add_spin_columns(rows)` is **opt-in** — call it explicitly on search output; nothing applies it automatically. `compute_row`, `axis_to_clock`, `DERIVED_COLUMNS`.
|
|
55
|
+
|
|
56
|
+
### MLB Stats API (`mlb/`)
|
|
57
|
+
|
|
58
|
+
A pure pass-through port (~90 functions across `stats_api`, `people`, `teams`, `games`, `stats`, `misc`, `discovery`, `constants`). Every function returns the raw JSON `dict`/`list`. `mlb_api(path, params)` is the low-level escape hatch; the typed functions (`get_person`, `get_schedule`, `get_roster`, `get_stats`, …) wrap specific endpoints. `mlb.__all__` lists every public function (the CLI's `fungo mlb --list` reads it).
|
|
59
|
+
|
|
60
|
+
## The two non-obvious things (the library's hard-won value)
|
|
61
|
+
|
|
62
|
+
### 1. Bat-tracking boards ignore `year=` (the `year_format` mechanism)
|
|
63
|
+
|
|
64
|
+
The three Hawk-Eye bat-tracking boards — `bat-tracking`, `bat-tracking/swing-path-attack-angle`, `bat-tracking/swing-timing-miss-distance` — **silently return the current season if you pass the standard `year=` param.** `_emit_year` in `leaderboards.py` branches per slug on the registry's `year_format`:
|
|
65
|
+
|
|
66
|
+
- `int` / `special` → `year=<str>` (`special`, e.g. active-spin, wants a composite `"2024_spin-based"` — build it with `active_spin_year`).
|
|
67
|
+
- `camelCase_season` → `seasonStart` + `seasonEnd`. A `(start, end)` tuple/list is a **contiguous range**; multi-year returns **one aggregated row per player across the span, with no year column** (`bat-tracking`, `swing-path-attack-angle`).
|
|
68
|
+
- `season_array` → repeated `season[]` keys (this is why `request_bytes` uses `doseq=True`). A list is a **true multi-select**; multi-year returns **one row per player per year, with a year column** (`swing-timing-miss-distance`).
|
|
69
|
+
|
|
70
|
+
The tuple-vs-list / range-vs-array distinction is the whole point — it dictates whether you get a year column. Also: `swing-timing-miss-distance` uses the plain `min` threshold, not `minSwings`, and `miss_distance` is in inches.
|
|
71
|
+
|
|
72
|
+
### 2. Day-chunking defends the ~30k-row cap
|
|
73
|
+
|
|
74
|
+
Savant silently truncates responses at ~30,000 rows (`MAX_ROWS_PER_QUERY`). `statcast_search` auto-chunks any range longer than 5 days into 1-day requests, fanned through `map_concurrent` (bounded pool, 1s politeness delay between submits), and flattens per-day results in date order. Pipe-delimited multi-value filters must **not** be URL-encoded — `request_bytes` sets `safe="|"` for exactly this.
|
|
75
|
+
|
|
76
|
+
## Conventions
|
|
77
|
+
|
|
78
|
+
- New sources follow the same shape: a stdlib fetch on `http.py`, source-specific quirks (HTML detection, param emission) isolated in the source module, raw `list[dict]`/JSON out, DataFrame conversion left to `to_frame`.
|
|
79
|
+
- Fail loud — let stdlib/transport exceptions propagate; raise the typed `ValidationError`/`SavantError`/`MLBStatsError`/`RequestError` for input and source problems rather than bare exceptions or silent fallbacks.
|
|
80
|
+
- Keep the core dependency-free; anything needing `polars`/`pandas`/`rich` is an optional extra, imported lazily at the call site.
|
fungo-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright © 2022 Josh Hejka
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
fungo-1.0.0/MIGRATION.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Fungo 1.0 Migration Notes
|
|
2
|
+
|
|
3
|
+
Fungo 1.0 is a cleanup release that stabilizes the package around explicit
|
|
4
|
+
subpackages instead of the older experimental module layout.
|
|
5
|
+
|
|
6
|
+
## Public Surface
|
|
7
|
+
|
|
8
|
+
Use these imports:
|
|
9
|
+
|
|
10
|
+
- `fungo.statcast` for Baseball Savant pitch search, leaderboards, and derived
|
|
11
|
+
spin physics.
|
|
12
|
+
- `fungo.mlb` for MLB Stats API pass-through wrappers.
|
|
13
|
+
- `fungo.lookup` for Chadwick Bureau player ID lookup.
|
|
14
|
+
- `fungo.to_frame` for optional pandas/polars conversion.
|
|
15
|
+
|
|
16
|
+
## Removed Pre-1.0 Modules
|
|
17
|
+
|
|
18
|
+
The following modules were removed:
|
|
19
|
+
|
|
20
|
+
- `fungo.download`
|
|
21
|
+
- `fungo.field`
|
|
22
|
+
- `fungo.query`
|
|
23
|
+
- `fungo.statcast.analysis`
|
|
24
|
+
- `fungo.statcast.base`
|
|
25
|
+
- `fungo.statcast.leaderboard`
|
|
26
|
+
- `fungo.statcast.query`
|
|
27
|
+
|
|
28
|
+
Use these replacements:
|
|
29
|
+
|
|
30
|
+
- `fungo.statcast.search` for pitch-level Savant search.
|
|
31
|
+
- `fungo.statcast.leaderboards` for Savant leaderboard wrappers.
|
|
32
|
+
- `fungo.statcast.physics` for derived spin/movement columns.
|
|
33
|
+
- `fungo.mlb` for MLB Stats API endpoints.
|
|
34
|
+
|
|
35
|
+
## Known Savant Limitation
|
|
36
|
+
|
|
37
|
+
`fungo.statcast.get_top_performers` raises `SavantError` in 1.0 because
|
|
38
|
+
Baseball Savant currently serves the page with an empty inline JSON payload
|
|
39
|
+
(`var data = {};`) and renders the cards directly in HTML. Returning `{}` would
|
|
40
|
+
hide upstream drift, so the wrapper fails loudly until Savant exposes a stable
|
|
41
|
+
machine-readable payload again.
|