cfb-data 0.4.1__py3-none-any.whl

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.
Files changed (127) hide show
  1. cfb_data/__init__.py +234 -0
  2. cfb_data/_dataframes.py +279 -0
  3. cfb_data/_executor.py +129 -0
  4. cfb_data/_parquet.py +197 -0
  5. cfb_data/_request_rules.py +41 -0
  6. cfb_data/_requests.py +36 -0
  7. cfb_data/_tabular.py +676 -0
  8. cfb_data/_transport.py +452 -0
  9. cfb_data/adjusted_metrics/__init__.py +29 -0
  10. cfb_data/adjusted_metrics/models/__init__.py +1 -0
  11. cfb_data/adjusted_metrics/models/pydantic/__init__.py +29 -0
  12. cfb_data/adjusted_metrics/models/pydantic/requests.py +45 -0
  13. cfb_data/adjusted_metrics/models/pydantic/responses.py +88 -0
  14. cfb_data/adjusted_metrics/resource.py +220 -0
  15. cfb_data/base/__init__.py +6 -0
  16. cfb_data/base/types.py +112 -0
  17. cfb_data/betting/__init__.py +15 -0
  18. cfb_data/betting/models/__init__.py +1 -0
  19. cfb_data/betting/models/pydantic/__init__.py +6 -0
  20. cfb_data/betting/models/pydantic/requests.py +35 -0
  21. cfb_data/betting/models/pydantic/responses.py +62 -0
  22. cfb_data/betting/resource.py +91 -0
  23. cfb_data/client.py +305 -0
  24. cfb_data/coaches/__init__.py +57 -0
  25. cfb_data/coaches/models/__init__.py +1 -0
  26. cfb_data/coaches/models/pydantic/__init__.py +57 -0
  27. cfb_data/coaches/models/pydantic/requests.py +75 -0
  28. cfb_data/coaches/models/pydantic/responses.py +261 -0
  29. cfb_data/coaches/resource.py +216 -0
  30. cfb_data/conferences/__init__.py +23 -0
  31. cfb_data/conferences/models/__init__.py +1 -0
  32. cfb_data/conferences/models/pydantic/__init__.py +23 -0
  33. cfb_data/conferences/models/pydantic/requests.py +69 -0
  34. cfb_data/conferences/models/pydantic/responses.py +64 -0
  35. cfb_data/conferences/resource.py +175 -0
  36. cfb_data/draft/__init__.py +19 -0
  37. cfb_data/draft/models/__init__.py +1 -0
  38. cfb_data/draft/models/pydantic/__init__.py +12 -0
  39. cfb_data/draft/models/pydantic/requests.py +18 -0
  40. cfb_data/draft/models/pydantic/responses.py +65 -0
  41. cfb_data/draft/resource.py +132 -0
  42. cfb_data/drives/__init__.py +13 -0
  43. cfb_data/drives/models/__init__.py +1 -0
  44. cfb_data/drives/models/pydantic/__init__.py +17 -0
  45. cfb_data/drives/models/pydantic/requests.py +42 -0
  46. cfb_data/drives/models/pydantic/responses.py +46 -0
  47. cfb_data/drives/resource.py +94 -0
  48. cfb_data/enums.py +93 -0
  49. cfb_data/errors.py +234 -0
  50. cfb_data/games/__init__.py +42 -0
  51. cfb_data/games/models/__init__.py +1 -0
  52. cfb_data/games/models/pydantic/__init__.py +106 -0
  53. cfb_data/games/models/pydantic/requests.py +266 -0
  54. cfb_data/games/models/pydantic/responses.py +495 -0
  55. cfb_data/games/resource.py +486 -0
  56. cfb_data/info/__init__.py +25 -0
  57. cfb_data/info/models/__init__.py +1 -0
  58. cfb_data/info/models/pydantic/__init__.py +23 -0
  59. cfb_data/info/models/pydantic/requests.py +18 -0
  60. cfb_data/info/models/pydantic/responses.py +103 -0
  61. cfb_data/info/resource.py +88 -0
  62. cfb_data/metrics/__init__.py +53 -0
  63. cfb_data/metrics/models/__init__.py +1 -0
  64. cfb_data/metrics/models/pydantic/__init__.py +49 -0
  65. cfb_data/metrics/models/pydantic/requests.py +121 -0
  66. cfb_data/metrics/models/pydantic/responses.py +182 -0
  67. cfb_data/metrics/resource.py +371 -0
  68. cfb_data/players/__init__.py +44 -0
  69. cfb_data/players/models/__init__.py +1 -0
  70. cfb_data/players/models/pydantic/__init__.py +41 -0
  71. cfb_data/players/models/pydantic/requests.py +70 -0
  72. cfb_data/players/models/pydantic/responses.py +171 -0
  73. cfb_data/players/resource.py +258 -0
  74. cfb_data/playoffs/__init__.py +41 -0
  75. cfb_data/playoffs/models/__init__.py +1 -0
  76. cfb_data/playoffs/models/pydantic/__init__.py +37 -0
  77. cfb_data/playoffs/models/pydantic/requests.py +30 -0
  78. cfb_data/playoffs/models/pydantic/responses.py +173 -0
  79. cfb_data/playoffs/resource.py +149 -0
  80. cfb_data/plays/__init__.py +43 -0
  81. cfb_data/plays/models/__init__.py +1 -0
  82. cfb_data/plays/models/pydantic/__init__.py +35 -0
  83. cfb_data/plays/models/pydantic/requests.py +85 -0
  84. cfb_data/plays/models/pydantic/responses.py +231 -0
  85. cfb_data/plays/resource.py +249 -0
  86. cfb_data/py.typed +0 -0
  87. cfb_data/rankings/__init__.py +16 -0
  88. cfb_data/rankings/models/__init__.py +1 -0
  89. cfb_data/rankings/models/pydantic/__init__.py +6 -0
  90. cfb_data/rankings/models/pydantic/requests.py +36 -0
  91. cfb_data/rankings/models/pydantic/responses.py +42 -0
  92. cfb_data/rankings/resource.py +89 -0
  93. cfb_data/ratings/__init__.py +59 -0
  94. cfb_data/ratings/models/__init__.py +1 -0
  95. cfb_data/ratings/models/pydantic/__init__.py +55 -0
  96. cfb_data/ratings/models/pydantic/requests.py +87 -0
  97. cfb_data/ratings/models/pydantic/responses.py +215 -0
  98. cfb_data/ratings/resource.py +342 -0
  99. cfb_data/recruiting/__init__.py +26 -0
  100. cfb_data/recruiting/models/__init__.py +1 -0
  101. cfb_data/recruiting/models/pydantic/__init__.py +23 -0
  102. cfb_data/recruiting/models/pydantic/requests.py +69 -0
  103. cfb_data/recruiting/models/pydantic/responses.py +70 -0
  104. cfb_data/recruiting/resource.py +180 -0
  105. cfb_data/retry.py +49 -0
  106. cfb_data/stats/__init__.py +69 -0
  107. cfb_data/stats/models/__init__.py +1 -0
  108. cfb_data/stats/models/pydantic/__init__.py +65 -0
  109. cfb_data/stats/models/pydantic/requests.py +167 -0
  110. cfb_data/stats/models/pydantic/responses.py +291 -0
  111. cfb_data/stats/resource.py +400 -0
  112. cfb_data/teams/__init__.py +40 -0
  113. cfb_data/teams/models/__init__.py +1 -0
  114. cfb_data/teams/models/pydantic/__init__.py +26 -0
  115. cfb_data/teams/models/pydantic/requests.py +96 -0
  116. cfb_data/teams/models/pydantic/responses.py +116 -0
  117. cfb_data/teams/resource.py +270 -0
  118. cfb_data/venues/__init__.py +6 -0
  119. cfb_data/venues/models/__init__.py +1 -0
  120. cfb_data/venues/models/pydantic/__init__.py +5 -0
  121. cfb_data/venues/models/pydantic/responses.py +24 -0
  122. cfb_data/venues/resource.py +51 -0
  123. cfb_data-0.4.1.dist-info/METADATA +414 -0
  124. cfb_data-0.4.1.dist-info/RECORD +127 -0
  125. cfb_data-0.4.1.dist-info/WHEEL +5 -0
  126. cfb_data-0.4.1.dist-info/licenses/LICENSE +21 -0
  127. cfb_data-0.4.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,414 @@
1
+ Metadata-Version: 2.4
2
+ Name: cfb-data
3
+ Version: 0.4.1
4
+ Summary: Async validated CollegeFootballData access for pandas and Polars
5
+ Author: Ryan Anderson
6
+ License-Expression: MIT
7
+ Project-URL: Documentation, https://ryanpaulanderson.github.io/cfb-data/
8
+ Project-URL: Repository, https://github.com/ryanpaulanderson/cfb-data
9
+ Project-URL: Issues, https://github.com/ryanpaulanderson/cfb-data/issues
10
+ Classifier: Development Status :: 2 - Pre-Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Requires-Python: >=3.12
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: aiohttp<4,>=3.13
19
+ Requires-Dist: pandas<4,>=3.0
20
+ Requires-Dist: pydantic<3,>=2.12
21
+ Requires-Dist: pyarrow<26,>=25
22
+ Provides-Extra: polars
23
+ Requires-Dist: polars<2,>=1.43.2; extra == "polars"
24
+ Provides-Extra: dev
25
+ Requires-Dist: build<2,>=1.5; extra == "dev"
26
+ Requires-Dist: furo>=2025.12.19; extra == "dev"
27
+ Requires-Dist: mypy<3,>=2.3; extra == "dev"
28
+ Requires-Dist: myst-parser<6,>=5.1; extra == "dev"
29
+ Requires-Dist: pre-commit<5,>=4.5; extra == "dev"
30
+ Requires-Dist: pyarrow-stubs<21,>=20.0.0.20260625; extra == "dev"
31
+ Requires-Dist: pytest<10,>=9; extra == "dev"
32
+ Requires-Dist: pytest-asyncio<2,>=1.3; extra == "dev"
33
+ Requires-Dist: ruff<0.17,>=0.15.22; extra == "dev"
34
+ Requires-Dist: sphinx<10,>=9.1; extra == "dev"
35
+ Requires-Dist: twine<8,>=7; extra == "dev"
36
+ Dynamic: license-file
37
+
38
+ # College Football Data Python Toolkit
39
+
40
+ `cfb-data` 0.4.1 is an asynchronous, validated client for the public
41
+ [CollegeFootballData API](https://collegefootballdata.com/) REST endpoint
42
+ groups. It returns eager pandas DataFrames by default and can return the same
43
+ logical tables as Polars DataFrames. Irreducibly nested analytical responses
44
+ and operational account metadata return validated models instead.
45
+
46
+ Read the complete [documentation](https://ryanpaulanderson.github.io/cfb-data/)
47
+ for the getting-started guide, request rules and allowed values, namespace
48
+ contracts, result schemas, and generated Python API reference.
49
+
50
+ The request path is explicit:
51
+
52
+ ```text
53
+ HTTP → Pydantic models → logical schema → canonical Arrow table → DataFrame
54
+ ```
55
+
56
+ Malformed upstream data never reaches a DataFrame. Choosing Polars changes the
57
+ concrete return type and native nested representation, not endpoint names,
58
+ validation, retry behavior, columns, row order, logical values, or the
59
+ canonical storage schema.
60
+
61
+ ## Installation
62
+
63
+ pandas and PyArrow are included in the default installation:
64
+
65
+ ```sh
66
+ python -m pip install cfb-data
67
+ ```
68
+
69
+ Install the optional Polars backend with:
70
+
71
+ ```sh
72
+ python -m pip install "cfb-data[polars]"
73
+ ```
74
+
75
+ Python 3.12 and 3.13 are supported. DataFrames are eager; Polars
76
+ `LazyFrame` results are not part of the 0.4.1 contract.
77
+
78
+ ## Authentication and lifecycle
79
+
80
+ Set a CollegeFootballData API key in the environment:
81
+
82
+ ```sh
83
+ export CFBD_API_KEY="..."
84
+ ```
85
+
86
+ Every client is one-shot and must own its reusable HTTP session through
87
+ `async with`:
88
+
89
+ ```python
90
+ import asyncio
91
+
92
+ from cfb_data import CFBDClient
93
+
94
+
95
+ async def main() -> None:
96
+ async with CFBDClient() as client:
97
+ games = await client.games.list(year=2024, team="Michigan")
98
+ drives = await client.drives.list(year=2024, team="Michigan")
99
+ plays = await client.plays.list(year=2024, week=1, team="Michigan")
100
+ stats = await client.stats.team_season(year=2024, team="Michigan")
101
+ ratings = await client.ratings.elo(year=2024, team="Michigan")
102
+ players = await client.players.search(
103
+ search_term="Edwards", year=2024, team="Michigan"
104
+ )
105
+ teams = await client.teams.fbs(year=2024)
106
+ draft_picks = await client.draft.picks(year=2024, school="Michigan")
107
+ playoff = await client.playoffs.cfp(year=2024)
108
+ adjusted = await client.adjusted_metrics.team_season(
109
+ year=2024, team="Michigan"
110
+ )
111
+ account = await client.info.account()
112
+
113
+ print(games.head())
114
+ print(drives.head())
115
+ print(plays.head())
116
+ print(stats.head())
117
+ print(ratings.head())
118
+ print(players.head())
119
+ print(teams.head())
120
+ print(draft_picks.head())
121
+ print(playoff.champion)
122
+ print(adjusted.head())
123
+ print(account.tier_name)
124
+
125
+
126
+ asyncio.run(main())
127
+ ```
128
+
129
+ An explicit non-empty `api_key` takes precedence over `CFBD_API_KEY`. Passing
130
+ an empty explicit value is a configuration error and does not fall back to the
131
+ environment. Calls before context entry, after exit, during a nested entry, or
132
+ after attempted re-entry raise `CFBDClientStateError`.
133
+
134
+ For Polars, select only the backend:
135
+
136
+ ```python
137
+ from cfb_data import CFBDClient
138
+
139
+ async with CFBDClient(dataframe_backend="polars") as client:
140
+ calendar = await client.games.calendar(year=2024)
141
+ ```
142
+
143
+ Type checkers infer `pandas.DataFrame` for the default client and
144
+ `polars.DataFrame` when the literal backend is `"polars"`.
145
+
146
+ ## Endpoints
147
+
148
+ Each method accepts either one positional request model or explicit snake-case
149
+ keyword filters. The styles are mutually exclusive:
150
+
151
+ ```python
152
+ from cfb_data import CFBDClient, GamesRequest, SeasonType
153
+
154
+ request = GamesRequest(year=2024, season_type=SeasonType.regular)
155
+
156
+ async with CFBDClient() as client:
157
+ by_model = await client.games.list(request)
158
+ by_keywords = await client.games.list(year=2024, season_type="regular")
159
+ ```
160
+
161
+ Unknown filters and invalid combinations fail before HTTP. The public Python
162
+ name is always `game_id`; request serialization maps it to upstream `id` or
163
+ `gameId` as required.
164
+
165
+ | Method | Request model | Result |
166
+ | --- | --- | --- |
167
+ | `client.games.list` | `GamesRequest` | `Game` rows as selected frame |
168
+ | `client.games.records` | `RecordsRequest` | `TeamRecords` rows as selected frame |
169
+ | `client.games.calendar` | `CalendarRequest` | `CalendarWeek` rows as selected frame |
170
+ | `client.games.scoreboard` | `ScoreboardRequest` | `ScoreboardGame` rows as selected frame |
171
+ | `client.games.media` | `GameMediaRequest` | `GameMedia` rows as selected frame |
172
+ | `client.games.weather` | `GameWeatherRequest` | `GameWeather` rows as selected frame |
173
+ | `client.games.player_stats` | `PlayerGameStatsRequest` | `PlayerGameStats` rows as selected frame |
174
+ | `client.games.team_stats` | `TeamGameStatsRequest` | `TeamGameStats` rows as selected frame |
175
+ | `client.games.advanced_box_score` | `AdvancedBoxScoreRequest` | `AdvancedBoxScore` model |
176
+ | `client.drives.list` | `DrivesRequest` | `Drive` rows as selected frame |
177
+ | `client.plays.list` | `PlaysRequest` | `Play` rows as selected frame |
178
+ | `client.plays.types` | None | `PlayType` rows as selected frame |
179
+ | `client.plays.stats` | `PlayStatsRequest` | `PlayStat` rows as selected frame |
180
+ | `client.plays.stat_types` | None | `PlayStatType` rows as selected frame |
181
+ | `client.plays.live` | `LivePlaysRequest` | `LiveGame` model |
182
+ | `client.venues.list` | None | `Venue` rows as selected frame |
183
+ | `client.conferences.list` | `ConferencesRequest` | `Conference` rows as selected frame |
184
+ | `client.conferences.changes` | `ConferenceChangesRequest` | `TeamConferenceChange` rows as selected frame |
185
+ | `client.conferences.affiliations` | `ConferenceAffiliationsRequest` | `TeamConferenceAffiliation` rows as selected frame |
186
+ | `client.teams.list` | `TeamsRequest` | `Team` rows as selected frame |
187
+ | `client.teams.fbs` | `FBSTeamsRequest` | `Team` rows as selected frame |
188
+ | `client.teams.matchup` | `TeamMatchupRequest` | one `Matchup` row as selected frame |
189
+ | `client.teams.ats` | `TeamATSRequest` | `TeamATS` rows as selected frame |
190
+ | `client.teams.roster` | `RosterRequest` | `RosterPlayer` rows as selected frame |
191
+ | `client.teams.talent` | `TalentRequest` | `TeamTalent` rows as selected frame |
192
+ | `client.stats.player_season` | `PlayerSeasonStatsRequest` | `PlayerStat` rows as selected frame |
193
+ | `client.stats.player_season_success` | `PlayerSeasonSuccessRequest` | `PlayerSeasonSuccessRate` rows as selected frame |
194
+ | `client.stats.player_game_success` | `PlayerGameSuccessRequest` | `PlayerGameSuccessRate` rows as selected frame |
195
+ | `client.stats.team_season` | `TeamSeasonStatsRequest` | `TeamStat` rows as selected frame |
196
+ | `client.stats.categories` | None | `StatCategory` rows as selected frame |
197
+ | `client.stats.advanced_season` | `AdvancedSeasonStatsRequest` | `AdvancedSeasonStat` rows as selected frame |
198
+ | `client.stats.advanced_game` | `AdvancedGameStatsRequest` | `AdvancedGameStat` rows as selected frame |
199
+ | `client.stats.game_havoc` | `GameHavocRequest` | `GameHavocStats` rows as selected frame |
200
+ | `client.metrics.predicted_points` | `PredictedPointsRequest` | `PredictedPointsValue` rows as selected frame |
201
+ | `client.metrics.team_season_ppa` | `TeamSeasonPPARequest` | `TeamSeasonPredictedPointsAdded` rows as selected frame |
202
+ | `client.metrics.team_game_ppa` | `TeamGamePPARequest` | `TeamGamePredictedPointsAdded` rows as selected frame |
203
+ | `client.metrics.player_game_ppa` | `PlayerGamePPARequest` | `PlayerGamePredictedPointsAdded` rows as selected frame |
204
+ | `client.metrics.player_season_ppa` | `PlayerSeasonPPARequest` | `PlayerSeasonPredictedPointsAdded` rows as selected frame |
205
+ | `client.metrics.win_probability` | `WinProbabilityRequest` | `PlayWinProbability` rows as selected frame |
206
+ | `client.metrics.pregame_win_probability` | `PregameWinProbabilityRequest` | `PregameWinProbability` rows as selected frame |
207
+ | `client.metrics.field_goal_expected_points` | None | `FieldGoalExpectedPoints` rows as selected frame |
208
+ | `client.ratings.core` | `CoreRatingsRequest` | `TeamCoreRating` rows as selected frame |
209
+ | `client.ratings.sp` | `SPRatingsRequest` | `TeamSP` rows as selected frame |
210
+ | `client.ratings.conference_sp` | `ConferenceSPRatingsRequest` | `ConferenceSP` rows as selected frame |
211
+ | `client.ratings.srs` | `SRSRatingsRequest` | `TeamSRS` rows as selected frame |
212
+ | `client.ratings.expanded_srs` | `ExpandedSRSRatingsRequest` | `ExpandedTeamSRS` rows as selected frame |
213
+ | `client.ratings.elo` | `EloRatingsRequest` | `TeamElo` rows as selected frame |
214
+ | `client.ratings.fpi` | `FPIRatingsRequest` | `TeamFPI` rows as selected frame |
215
+ | `client.players.search` | `PlayerSearchRequest` | `PlayerSearchResult` rows as selected frame |
216
+ | `client.players.usage` | `PlayerUsageRequest` | `PlayerUsage` rows as selected frame |
217
+ | `client.players.season_overview` | `PlayerSeasonOverviewRequest` | one `PlayerSeasonOverview` row as selected frame |
218
+ | `client.players.returning_production` | `ReturningProductionRequest` | `ReturningProduction` rows as selected frame |
219
+ | `client.players.transfer_portal` | `TransferPortalRequest` | `PlayerTransfer` rows as selected frame |
220
+ | `client.rankings.list` | `RankingsRequest` | `PollWeek` rows as selected frame |
221
+ | `client.betting.lines` | `BettingLinesRequest` | `BettingGame` rows as selected frame |
222
+ | `client.recruiting.players` | `RecruitingPlayersRequest` | `Recruit` rows as selected frame |
223
+ | `client.recruiting.teams` | `RecruitingTeamsRequest` | `TeamRecruitingRanking` rows as selected frame |
224
+ | `client.recruiting.groups` | `RecruitingGroupsRequest` | `AggregatedTeamRecruiting` rows as selected frame |
225
+ | `client.coaches.list` | `CoachesRequest` | `Coach` rows as selected frame |
226
+ | `client.coaches.profile` | `CoachProfileRequest` | one `CoachProfile` row as selected frame |
227
+ | `client.coaches.seasons` | `CoachSeasonsRequest` | `DetailedCoachSeason` rows as selected frame |
228
+ | `client.coaches.tenures` | `CoachTenuresRequest` | `CoachTenure` rows as selected frame |
229
+ | `client.draft.teams` | None | `DraftTeam` rows as selected frame |
230
+ | `client.draft.positions` | None | `DraftPosition` rows as selected frame |
231
+ | `client.draft.picks` | `DraftPicksRequest` | `DraftPick` rows as selected frame |
232
+ | `client.playoffs.cfp` | `CfpPlayoffRequest` | `CfpPlayoff` model |
233
+ | `client.playoffs.participants` | `CfpParticipantsRequest` | `PlayoffParticipant` rows as selected frame |
234
+ | `client.playoffs.games` | `CfpGamesRequest` | `PlayoffMatchup` rows as selected frame |
235
+ | `client.adjusted_metrics.team_season` | `AdjustedTeamMetricsRequest` | `AdjustedTeamMetrics` rows as selected frame |
236
+ | `client.adjusted_metrics.player_passing` | `AdjustedPlayerPassingRequest` | `PlayerWeightedEPA` rows as selected frame |
237
+ | `client.adjusted_metrics.player_rushing` | `AdjustedPlayerRushingRequest` | `PlayerWeightedEPA` rows as selected frame |
238
+ | `client.adjusted_metrics.kicker_paar` | `KickerPAARRequest` | `KickerPAAR` rows as selected frame |
239
+ | `client.info.account` | None | `UserInfo` model |
240
+ | `client.info.usage` | `InfoUsageRequest` | `UserUsage` model |
241
+
242
+ Request models and shared `StrEnum` values are exported from `cfb_data` and
243
+ their supported domain namespaces. Enum fields also accept their documented
244
+ string values.
245
+
246
+ Raw JSON and general validated-model return modes are intentionally excluded.
247
+ Advanced box score, live plays, and the complete CFP bracket return models
248
+ because their nested sections do not form one natural table. Info returns
249
+ models because account and usage metadata are operational objects rather than
250
+ analytical tables. The upstream live-plays route requires Patreon Tier 2
251
+ access; all Adjusted Metrics routes require Patreon Tier 1 access.
252
+
253
+ Team matchup, player season overview, and coach profile are one-row frames
254
+ containing nested columns. Rankings preserve polls and ranks, betting preserves
255
+ provider lines, and coach summaries preserve seasons as nested values. pandas
256
+ represents nested values as `object`; Polars represents them as native `Struct`
257
+ and `List[Struct]` values.
258
+
259
+ ## DataFrame contract
260
+
261
+ Response model field order defines exact snake-case column order. API row order
262
+ and row count are preserved. Conversion never flattens, explodes, indexes by
263
+ ID, or drops rows, and an empty response produces a correctly typed empty
264
+ frame.
265
+
266
+ pandas uses:
267
+
268
+ - `int64`, `float64`, and `bool` for required scalars;
269
+ - `Int64`, `Float64`, and `boolean` for nullable scalars;
270
+ - pandas `string` and `datetime64[ns, UTC]` dtypes;
271
+ - `object` columns for nested structs and lists;
272
+ - `object` for the explicitly heterogeneous Stats `stat_value` scalar;
273
+ - a normal `RangeIndex`.
274
+
275
+ Polars uses strict `Int64`, `Float64`, `Boolean`, `String`, UTC `Datetime`,
276
+ `Struct`, `List`, and the explicit heterogeneous `Object` Stats scalar. This
277
+ means nested values have the same logical content while remaining Python
278
+ mappings/lists in pandas and native nested columns in Polars. The
279
+ `client.stats.team_season` `stat_value` column preserves each upstream string,
280
+ integer, or float without coercion and is `object`/`Object` in both backends.
281
+
282
+ All response timestamps must include a timezone. Validation normalizes them to
283
+ UTC before conversion.
284
+
285
+ ## Arrow and Parquet contract
286
+
287
+ Every tabular response is represented as one explicit Arrow table before
288
+ pandas or Polars materialization. The Arrow schema is derived from the same
289
+ Pydantic annotations and retains ordered struct fields, typed list elements,
290
+ nullability, and UTC timestamps even for empty or all-null responses.
291
+
292
+ The internal versioned Parquet codec uses that schema directly. It stores
293
+ standard nested Parquet values plus cfb-data storage version, row-model
294
+ identity, logical-schema digest, and writer-version metadata. The heterogeneous
295
+ Stats scalar is stored losslessly as a tagged struct and decoded back to its
296
+ original string, integer, or float value for DataFrames.
297
+
298
+ Direct pandas and Polars Parquet methods are not the cfb-data persistence
299
+ compatibility contract: pandas object inference and Polars `Object` columns do
300
+ not provide identical empty- and mixed-value behavior. Version 0.2.0 keeps the
301
+ codec internal so future caching can use it without prematurely committing to
302
+ a public save/load API. See
303
+ [`ADR 0003`](docs/architecture/0003-canonical-arrow-parquet.md) for the format,
304
+ validation, and compatibility decisions.
305
+
306
+ ## Retries and errors
307
+
308
+ The default immutable `RetryPolicy` makes at most three total safe GET
309
+ attempts. It retries connection failures, timeouts, truncated payloads, and
310
+ HTTP `408`, `429`, `500`, `502`, `503`, and `504` with capped exponential
311
+ full-jitter backoff. Set `RetryPolicy(max_attempts=1)` to disable retries.
312
+
313
+ Valid numeric and HTTP-date `Retry-After` values are honored up to 90 seconds.
314
+ A longer requested delay fails immediately and remains available as
315
+ `retry_after_seconds` on the HTTP error. Redirects are disabled, TLS
316
+ verification remains enabled, every attempt has a finite timeout, and
317
+ cancellation is preserved.
318
+
319
+ ```python
320
+ from cfb_data import CFBDClient, RetryPolicy
321
+
322
+ policy = RetryPolicy(
323
+ max_attempts=4,
324
+ base_delay_seconds=0.25,
325
+ max_backoff_seconds=4.0,
326
+ max_retry_after_seconds=20.0,
327
+ )
328
+
329
+ async with CFBDClient(retry_policy=policy) as client:
330
+ scoreboard = await client.games.scoreboard()
331
+ ```
332
+
333
+ All library exceptions derive from `CFBDError`. Public subclasses distinguish
334
+ configuration, optional dependencies, client state, request validation,
335
+ timeouts and transport, HTTP/authentication/authorization/rate-limit/server
336
+ responses, response decoding and validation, and DataFrame conversion. Error
337
+ text and debug retry events include only safe endpoint/status/attempt metadata,
338
+ never credentials, query parameters, response payloads, or secrets.
339
+
340
+ ## Migrating from 0.1.x
341
+
342
+ The inherited raw, validation, and pandas client families were removed rather
343
+ than retained as compatibility wrappers.
344
+
345
+ Raw client calls:
346
+
347
+ ```python
348
+ # Before: CFBDGamesAPI(...).make_request("/games", {"year": 2024})
349
+ async with CFBDClient() as client:
350
+ games = await client.games.list(year=2024)
351
+ ```
352
+
353
+ Validated-model client calls:
354
+
355
+ ```python
356
+ # Before: CFBDGamesValidationAPI(...).make_request("/calendar", {"year": 2024})
357
+ async with CFBDClient() as client:
358
+ calendar_frame = await client.games.calendar(year=2024)
359
+ ```
360
+
361
+ pandas client calls:
362
+
363
+ ```python
364
+ # Before: CFBDDrivesPandasAPI(...).make_request("/drives", {"year": 2024})
365
+ async with CFBDClient() as client:
366
+ drives_frame = await client.drives.list(year=2024)
367
+ ```
368
+
369
+ There is no public generic path router or `make_request(path, params)` method.
370
+ Use the typed namespace method and either its request model or keyword filters.
371
+
372
+ ## Datasets and workflows
373
+
374
+ Version 0.4.1 does not expose `client.datasets` or `client.workflows`. The
375
+ accepted architecture reserves two higher layers:
376
+
377
+ - datasets compose validated endpoint results and validated subdatasets
378
+ through joins into one validated tabular row model, converting only the
379
+ final table;
380
+ - workflows orchestrate endpoints, datasets, and broader control flow and may
381
+ return multiple artifacts.
382
+
383
+ See
384
+ [`docs/architecture/0001-validated-models-before-dataframes.md`](docs/architecture/0001-validated-models-before-dataframes.md)
385
+ for the decision and extension boundaries.
386
+
387
+ ## Development
388
+
389
+ ```sh
390
+ git clone https://github.com/ryanpaulanderson/cfb-data.git
391
+ cd cfb-data
392
+ make install
393
+ make hooks
394
+ make format
395
+ make docs
396
+ make check
397
+ ```
398
+
399
+ `make install` creates `.venv` and installs `.[dev,polars]`, giving
400
+ contributors the complete Arrow/Parquet and two-backend test contract.
401
+ `make check` runs Ruff, strict mypy, a warning-free Sphinx build, and pytest
402
+ under the same contract as CI. `make docs` writes the local HTML site to
403
+ `docs/_build/html`. Package metadata and all dependency groups live only in
404
+ `pyproject.toml`.
405
+
406
+ See [`CONTRIBUTING.md`](CONTRIBUTING.md) and
407
+ [`docs/project-status.md`](docs/project-status.md) for repository and release
408
+ status.
409
+
410
+ ## License
411
+
412
+ This project is licensed under the MIT License. See [`LICENSE`](LICENSE).
413
+
414
+ This project is not affiliated with CollegeFootballData.com.
@@ -0,0 +1,127 @@
1
+ cfb_data/__init__.py,sha256=BxhcTZuD-sy1nA-ghMtkejb-WUAPJft4eCY6XYzhWkM,6354
2
+ cfb_data/_dataframes.py,sha256=oa2JkVmLVIT4p9n5vW5z4jErQCGHXp9SW9bWA-m4Bmk,9643
3
+ cfb_data/_executor.py,sha256=BqQ2EG8cQ1Ol5gKf_w6y85kR0jzuzzyzvWENGg1nR7I,4421
4
+ cfb_data/_parquet.py,sha256=qamr2gmMzPJQyxUg6xTFg3DCM4FNGKdHO3f5T8Vi0dc,6535
5
+ cfb_data/_request_rules.py,sha256=HSjDjgVQX7hUqp9KOP4EIwKhZ1T3gq6hoFS-oJZRasg,1347
6
+ cfb_data/_requests.py,sha256=Bo8EfNtiMsxoSjGAL6pmfOMXhLeEjMd2f-ySjRVyL7s,1148
7
+ cfb_data/_tabular.py,sha256=BO67ANPM3K2b2EtzM-XsTGzpkjt2T9xxCTeWVljTXrc,24584
8
+ cfb_data/_transport.py,sha256=DHpgApAFlASSf9UQzmg4L94ltR2M1MGIv5H_rJqZi74,15986
9
+ cfb_data/client.py,sha256=8hKhdbQM-PUpYOvgyY1Qu45rZLvOmI10EMOgDd1b0KQ,10454
10
+ cfb_data/enums.py,sha256=cTCMTKGoUKb9tISA1t4bJJxpOIeb8OVw5IC2zUFQ7Q0,1912
11
+ cfb_data/errors.py,sha256=JghNqo4ak5F1A5nFUialag1gMCAIZuncCMIY60fxdns,7108
12
+ cfb_data/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ cfb_data/retry.py,sha256=nIchHeIxNuJPtpaaLVJJiMZvjyTSjgkGmb_ALaYefcw,1891
14
+ cfb_data/adjusted_metrics/__init__.py,sha256=oBo7V-8K0CtTOrzxhsFpT-GkHvuAFZf5vnWXJEMfhes,788
15
+ cfb_data/adjusted_metrics/resource.py,sha256=JBjNBDavbqRi0u59k060TGEUCrLmhi3kRdeZlLzDck8,7275
16
+ cfb_data/adjusted_metrics/models/__init__.py,sha256=ORoaejZAf0t4VqAt9MgcFKEc15N1UeGeoxnwswuRnZY,46
17
+ cfb_data/adjusted_metrics/models/pydantic/__init__.py,sha256=W5PipY-hD6xDGXds2eFU3sDqLky0Bc7F_exH2aL_V_o,724
18
+ cfb_data/adjusted_metrics/models/pydantic/requests.py,sha256=orsm8XccCDEmXrNSqvQd17VLTxNeoKTbe0e_p7iHiT0,1360
19
+ cfb_data/adjusted_metrics/models/pydantic/responses.py,sha256=Khz9Qzn--SQiMoAkkBvhw7-U3jXU2CVkkaWyrQG27SU,2714
20
+ cfb_data/base/__init__.py,sha256=cEP9JVmQC0qPXrakNlK0H4DEBcH1GrmcTDSp_OGSDYg,240
21
+ cfb_data/base/types.py,sha256=qHGRFnpXTxkNi5km3YB1E4gcUrQzAciKVVulcQ_KZhw,4193
22
+ cfb_data/betting/__init__.py,sha256=_xqGYpWTMYSCjLaajZyxtGjyW-D25yNvedkjIB9JDUM,373
23
+ cfb_data/betting/resource.py,sha256=2gBYb7T5Q1RGsSH4YP22Q5-GLROR4ozrh8l7md0yDm0,2822
24
+ cfb_data/betting/models/__init__.py,sha256=GkWv7YInUtXvsxNLE66oZ8UMtEk0IL2IEJYDKwoA74w,37
25
+ cfb_data/betting/models/pydantic/__init__.py,sha256=clIw-EstJpJ8O-JOA4jwWsbUrebFLqsHuTFpASHePv4,210
26
+ cfb_data/betting/models/pydantic/requests.py,sha256=otQRbdn8lTOQeM3doeEYqeaMnVykbHxnJMGi3xRixNQ,1250
27
+ cfb_data/betting/models/pydantic/responses.py,sha256=vnb3ZBUVd9236zGSmQQvQ_UshVTZmhwzcxmmbBDIj4A,2366
28
+ cfb_data/coaches/__init__.py,sha256=CTJ3QsKADZb9fE2h7hv-miUO_hi9clOYF8XJ3elO9dI,1251
29
+ cfb_data/coaches/resource.py,sha256=Bmm-Y638SV0lcRb8YsCtUtqLf6ZPDEX6sclfpZu-sdc,7295
30
+ cfb_data/coaches/models/__init__.py,sha256=StKXGXQUEjx2rCr_vNlZsQ27EUEp2yICcbelk0WbxAc,37
31
+ cfb_data/coaches/models/pydantic/__init__.py,sha256=tWjiI4z1RBzr9xXGncBBpX1bWE6yZeFr2rJYrQP1avQ,1203
32
+ cfb_data/coaches/models/pydantic/requests.py,sha256=QJzvIZLeIejLZTlvJ1dzPIl8nw7XS_qH6VsYkus7HuU,2662
33
+ cfb_data/coaches/models/pydantic/responses.py,sha256=SrVzlSqxwGL5rXcy2HRP28cPfsHIUuTsALDDN9k4JAA,8372
34
+ cfb_data/conferences/__init__.py,sha256=UmSolOBlpnWfRtmldwGXQGcgqq0EN_OMzmEzDA4ZoMw,587
35
+ cfb_data/conferences/resource.py,sha256=rZbR87Mua1_bzxW18uHgglQSJgrSEEaddXzCYiQL1oI,6007
36
+ cfb_data/conferences/models/__init__.py,sha256=PIPY44zSC-06koHYdvKjoUo9pJVa6Htpi5QRyvYIqvU,71
37
+ cfb_data/conferences/models/pydantic/__init__.py,sha256=EqR1oVmVK4Ro0WiHFizxJZw3lDmv_sZilI7p_ymSVG0,523
38
+ cfb_data/conferences/models/pydantic/requests.py,sha256=s1ciwPsfE1kkxg_plb5VhKdRC2q4mCue5FMSEUVc08Q,2405
39
+ cfb_data/conferences/models/pydantic/responses.py,sha256=dLdaJilFXuKTHHt0W0rADPX4UJouEVlheg6zEpPjwp8,2275
40
+ cfb_data/draft/__init__.py,sha256=ztGfTtg94i47fjNuVV7CbBmw8x6SFKbxHzRmI38jWcI,371
41
+ cfb_data/draft/resource.py,sha256=wqUNobf7NVU2-UH3vl8NJXqsT4sLW063xLj8F6NZ4Wg,4198
42
+ cfb_data/draft/models/__init__.py,sha256=g8SWQW-hBben5QljgnrGjFPkUOYS5sUVcijokDfKkG4,35
43
+ cfb_data/draft/models/pydantic/__init__.py,sha256=ERyZRs7mP7SfUJ8YpewcvxT-1wSDHp_jIRRGT2nmLco,297
44
+ cfb_data/draft/models/pydantic/requests.py,sha256=ZUHr-0z2cUavzv2k-U0qSi7Mu1ReyfWDtLlHi8IGVFg,614
45
+ cfb_data/draft/models/pydantic/responses.py,sha256=C9pYFxJVXF0FiYg97UpYLDUney2Ve0thhX9gEO7KqLQ,2045
46
+ cfb_data/drives/__init__.py,sha256=YkO8ryYL_-n8semoDyaC2C3I2EnC3AVmqogd9MKgcu4,309
47
+ cfb_data/drives/resource.py,sha256=ljFP02JVzxbr1GJsG7lNRI1J5nU9Z8mo--FyvL-LyW4,2967
48
+ cfb_data/drives/models/__init__.py,sha256=zNS4tm7Kv_CHncIISJQJkFFxR7yKM8COpY2Z0xr-1P8,66
49
+ cfb_data/drives/models/pydantic/__init__.py,sha256=c3C7ecaOVcRXLPhyAQJFOD-a_mTodCxNwtj-MuRb5HQ,252
50
+ cfb_data/drives/models/pydantic/requests.py,sha256=0cvF_p44nFXokaAxyppEe9j2buUU7HcL9hvRxwPvclk,1469
51
+ cfb_data/drives/models/pydantic/responses.py,sha256=uc4K4xA3lyDNabzVxc98I5IeyhfkcUr5pYqPHNHXxF8,1862
52
+ cfb_data/games/__init__.py,sha256=ejvy_MTq_FSwBpUo18gW_LmRGvKLWnCqYmUJJ_471uA,929
53
+ cfb_data/games/resource.py,sha256=7Tl-tCyDnM9UztekMi4eb1HMwk39j7B3pe4SR93sDq4,15565
54
+ cfb_data/games/models/__init__.py,sha256=UbRK4VG2yxNwb6EdQW72d4VDOi2WvgYNUbUFMT2q3bg,65
55
+ cfb_data/games/models/pydantic/__init__.py,sha256=fBe_3Ly7IVcw8xu3_BoyQBY-T8lCND8hMtPQzqFZbCc,2267
56
+ cfb_data/games/models/pydantic/requests.py,sha256=eoLVmy-OdoigCwdHvSNAOWp15CV1-vozAXhz2TbbBX8,9472
57
+ cfb_data/games/models/pydantic/responses.py,sha256=OJ7Dk2fmt6YBEy67fMlBkUdjh3YThiXvHgLlt4iyaqQ,15679
58
+ cfb_data/info/__init__.py,sha256=m-7w9BtgTw0hV4z_vBveYtnGkVboopYKuvVIo7I58Rc,518
59
+ cfb_data/info/resource.py,sha256=7n1ln5VAD6zfdj0YBYXRdODBaGjXDaTKBe2uGD-R3gw,2809
60
+ cfb_data/info/models/__init__.py,sha256=wRnl8xYMnB9QDiuQoi_m-uDmdoiugpjAiAeO-73cO7w,34
61
+ cfb_data/info/models/pydantic/__init__.py,sha256=ua97nhr-6ZnOvdJIhIVqHejcv-334ZayXDdS26fy5Rc,467
62
+ cfb_data/info/models/pydantic/requests.py,sha256=8iyDKxYa4iqoAYKJh8BjA2jUnt3PrtUTZwvy5im2zIA,508
63
+ cfb_data/info/models/pydantic/responses.py,sha256=4ZxzDNL5Lvk4QP85L3OORJ8rC9mGYhEJf_2kXvcLIEk,3190
64
+ cfb_data/metrics/__init__.py,sha256=iRmhVN5G9JI-0G5xa7Ibxx-LPfxJU1_AfwBESEL7MBY,1436
65
+ cfb_data/metrics/resource.py,sha256=ISQAox9TeiInR_L6da_FRNzekucxXdyYiUKFfZtpG2c,13208
66
+ cfb_data/metrics/models/__init__.py,sha256=hDQUgZ-e7PtbF0Goh-nDpEqck_un7wrOQ16nuEQhfA0,37
67
+ cfb_data/metrics/models/pydantic/__init__.py,sha256=EVY3cbHWTQ0CbinufwQcTmaw0TSjKm_B0YQiHkudCLc,1293
68
+ cfb_data/metrics/models/pydantic/requests.py,sha256=bNlmtOE3XEzr9VdF15oYC5nH0mNpbwhcaph3PZVLvjY,4445
69
+ cfb_data/metrics/models/pydantic/responses.py,sha256=5GnHucwr8AFLOVvW3BkX1_KxH-ZBNIEpwfJ1NmzalKk,5581
70
+ cfb_data/players/__init__.py,sha256=FJ1v2jMF0BtT0uiAhFGnmYyHCipMj2BPYSXzXrH4dvI,1142
71
+ cfb_data/players/resource.py,sha256=nMoCM-f3Vove5wnKiWkpADkZ1PjO34bQ_8rWMkfKB-M,8640
72
+ cfb_data/players/models/__init__.py,sha256=5KsiXR-e03w14aYV7sTC72iFp0ntpv2Mal3M8OGLSPQ,37
73
+ cfb_data/players/models/pydantic/__init__.py,sha256=DzkREOYbLyPWWPjWNfOx8K6g-qQ8_WG34fq22EqKzgQ,1019
74
+ cfb_data/players/models/pydantic/requests.py,sha256=dUgxObrKSmepVr1XiwQd4R2ccMUW43D77rouk5p-kWg,2284
75
+ cfb_data/players/models/pydantic/responses.py,sha256=i_WJNdDa8MdvMOiL0zvsL5rOtIMNlsDMWo9pgkrirzE,5451
76
+ cfb_data/playoffs/__init__.py,sha256=IbtDqssw79DLCvKemSjeGg4bwMivYq2-SbK9AsOiD2k,926
77
+ cfb_data/playoffs/resource.py,sha256=WLFK3he9gXPCOOuCXvNH8xWRLkWSqkbCCntslK-ToS4,5181
78
+ cfb_data/playoffs/models/__init__.py,sha256=3rY5elKYmVHsn_wBx5SmnXJHDMjYiL48eO9tcRW_Kvs,38
79
+ cfb_data/playoffs/models/pydantic/__init__.py,sha256=k4lrzqL01yav96l_RznnUWB9k_JPVOYajGR7VpcOpyI,859
80
+ cfb_data/playoffs/models/pydantic/requests.py,sha256=MTcn0I4wXItUNLUDmxLo9SXxMVauefYmgkrjJJFggU8,823
81
+ cfb_data/playoffs/models/pydantic/responses.py,sha256=TAoEkOm-HXXquf2MI5zRu2lZx4EeMwLesVkXMUe3pvU,5252
82
+ cfb_data/plays/__init__.py,sha256=Cvn5yud3s9oDujyY5lbS333KP-nJsX6WA269w4s077U,784
83
+ cfb_data/plays/resource.py,sha256=X5Ri__-sEN8RzYZqYzLfXs1LO4Oxii95Iao9wPkQcZ8,7716
84
+ cfb_data/plays/models/__init__.py,sha256=i8J21Arwhufqdvbhk0V6baD1erYS2ltBGbbN9xltKoI,65
85
+ cfb_data/plays/models/pydantic/__init__.py,sha256=5wbFVzRLG53EV73B9V1-KA-1me3HSy9BIZWpzvc1Rxg,620
86
+ cfb_data/plays/models/pydantic/requests.py,sha256=eb3H-Ao3y5QbaVegJTNz6HjrszacBRsqTweesM3qt4U,3022
87
+ cfb_data/plays/models/pydantic/responses.py,sha256=xwuKxw6kHysPIHnDBvBU0Oiec_IuJcwVmfLJL3MWA28,7795
88
+ cfb_data/rankings/__init__.py,sha256=13eCPE52NsOYAxIfz55R_LmKP8BxoEW9rbg_sL1zE4c,374
89
+ cfb_data/rankings/resource.py,sha256=RBhniiijzVP1z4qrjPk8nV6JtGe3MOa40UIdvYUk3sI,2764
90
+ cfb_data/rankings/models/__init__.py,sha256=gWMN4gItPT_57Y_G_StmNwpBzUsDE1h7Et_He24arXg,38
91
+ cfb_data/rankings/models/pydantic/__init__.py,sha256=m4iWZB26Q3nI3Zm5ENRkQunCgFeS_om-srUawjanoPQ,211
92
+ cfb_data/rankings/models/pydantic/requests.py,sha256=Ql5QGPw0yUfzx6Di75LXZR92vM9qwd9yRRghyHC-x0k,1187
93
+ cfb_data/rankings/models/pydantic/responses.py,sha256=K6ijK09hDlLsBEEIqP7Cbm9V1hUPwi7qqfgPraMJeZ4,1129
94
+ cfb_data/ratings/__init__.py,sha256=7AgqFq3-zC3OWdGEv4ZtuJlIeuT7Fnqqy1T987Y8uYs,1254
95
+ cfb_data/ratings/resource.py,sha256=N3m7RdvhLcDbsmMv6Fimfev1HiyA1TFQNPzGL5gTUzQ,11085
96
+ cfb_data/ratings/models/__init__.py,sha256=JtestZyT6BVfSWm4zvTM6wT0COG2Tl37RITfIlS-mEM,37
97
+ cfb_data/ratings/models/pydantic/__init__.py,sha256=sAfzeVobQHeLxed_0Pxj3mo2hbDt1ZblX-yzTxl50WY,1111
98
+ cfb_data/ratings/models/pydantic/requests.py,sha256=XoPgqp6CIYoPIo18m53YgBeq3SbtWrifjMMlt1srkU8,2689
99
+ cfb_data/ratings/models/pydantic/responses.py,sha256=XR6nSaZb2qbpSm266DCsNO5-1fHspOdSALw7Vk8lhtI,5895
100
+ cfb_data/recruiting/__init__.py,sha256=9DIbmp3iC53oICf5dZdRrcN1yDZcgmWFADioy7cZRvI,635
101
+ cfb_data/recruiting/resource.py,sha256=QMVPuc7BB-E1hmiJfSjMTHe6IEjK5ooT5cjPxeigibw,6081
102
+ cfb_data/recruiting/models/__init__.py,sha256=dIS78gD08l70PbdqyzOIrnmGWqSaSamnrxDRyxdDyzE,40
103
+ cfb_data/recruiting/models/pydantic/__init__.py,sha256=D8NdLSeOBGfTb3mr9IgKBKRuSZt5kU-BznBjKVrM46Q,502
104
+ cfb_data/recruiting/models/pydantic/requests.py,sha256=hIC44-Ee6LnXoPwIWY2aUFqBmoHJEl0nObR7XKLE7bk,2358
105
+ cfb_data/recruiting/models/pydantic/responses.py,sha256=btyIL4cVnNH7KnMyP1IB9evzI92ObG1yjLUpCTgXQIg,2060
106
+ cfb_data/stats/__init__.py,sha256=Et8PDRLlj4k1tifZGB8gIS3Y85Wamr7DfX8GH21uweA,1648
107
+ cfb_data/stats/resource.py,sha256=7Z53fcrAT_rRmyIkalcdRs7zghHT4ToxxJfxDTDThj0,13785
108
+ cfb_data/stats/models/__init__.py,sha256=RU-zyAmEvhzaBKon6KDd9AP8-c2tCfoN_dzZ8__WM5M,65
109
+ cfb_data/stats/models/pydantic/__init__.py,sha256=1oAoDQGJX10hs7rRi2b9CM1G7SFM40N-Fca7rvfGdps,1509
110
+ cfb_data/stats/models/pydantic/requests.py,sha256=TP5D5YCF2bZJbsnCLfykVaajuRP1FeNPs7LVf6osuvY,6828
111
+ cfb_data/stats/models/pydantic/responses.py,sha256=5XcBvlFP6WLhA89EKRR7_U7AL_kXMFTZ000x5F-rDmc,9213
112
+ cfb_data/teams/__init__.py,sha256=IUu8U2FRVtPGAjp5Nh7_MEOwfITjG3aZMEWbVhXkdPk,813
113
+ cfb_data/teams/resource.py,sha256=itpeQ3V1jOtu3ReYq0q2X7VKMLUGWqCEtJcb3F53vJE,9371
114
+ cfb_data/teams/models/__init__.py,sha256=KJ9r29Xj-ihbp0fiKvodYQEUpC-3z-CqlidZUPxznr0,65
115
+ cfb_data/teams/models/pydantic/__init__.py,sha256=1GIUh9yZRYmcvuPAI_PYZqz7DW39a_Q_SCZLeK3EYCM,530
116
+ cfb_data/teams/models/pydantic/requests.py,sha256=H0b6XqaNFDL9wsiEBRsy-RRnGj39DIlaQAQuVVvNzLM,2923
117
+ cfb_data/teams/models/pydantic/responses.py,sha256=1aF1T3K_X6-AQjYlBzW8RojIbF3cHkKA04AuIHfJXsc,3930
118
+ cfb_data/venues/__init__.py,sha256=eO3Ftsuq_ejC0HZgDLsGpolnPCl3zYHhDTKk-7vtaVc,178
119
+ cfb_data/venues/resource.py,sha256=wQUWkscEtkoQqTs9wHpesoV2m8UPpcjhsbpCtg7q43E,1573
120
+ cfb_data/venues/models/__init__.py,sha256=0OzEb8xdUKhL5wDrHDFJT7ilORPhUDrG3C7nWUFngZk,66
121
+ cfb_data/venues/models/pydantic/__init__.py,sha256=DVGa1-gifFoDosfBGrqsinDr_OmdJClUX_eJebnDvnE,102
122
+ cfb_data/venues/models/pydantic/responses.py,sha256=JiI4MhYGQR8eKXEPkRRwFagskvLDCqkqD48ORm8NhDA,738
123
+ cfb_data-0.4.1.dist-info/licenses/LICENSE,sha256=cN9Oh5Soiwysh9CwfVqXCw8fCNahyG2ufMJyxpPnMXo,1075
124
+ cfb_data-0.4.1.dist-info/METADATA,sha256=RSIHt-xfGqgtAhPfZGisFTU9ihI5Tvd2_x8uQvDWlek,19537
125
+ cfb_data-0.4.1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
126
+ cfb_data-0.4.1.dist-info/top_level.txt,sha256=_6spK328eDT0T86WKk2KObWGrkJfgBGmG10SeL0j_2k,9
127
+ cfb_data-0.4.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-2026 Ryan Anderson
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.
@@ -0,0 +1 @@
1
+ cfb_data