Skip to content

Commit 76d98f0

Browse files
Mlaz-codeclaude
andauthored
feat(types): nested refs + TeamRef metadata (0.3.0 / 0.3.1) (#7)
## Summary Two related additions to the type model, both additive and non-breaking: **0.3.0 — nested refs** - New types: `TeamRef`, `SportRef`, `EntityRef`, `Team` - Existing odds + opportunity rows extend with optional `home`, `away`, `sport_ref`, `league_ref`, `market_ref`, `sportsbook_ref` - `ArbitrageLeg` gains `sportsbook_ref`; `ClosingOddsLine` gains `market_ref` + `sportsbook_ref` - Reference rows (`Sport`, `League`, `Sportsbook`, `Market`) gain `numerical_id`; `Team` adds `abbreviation` + `numerical_id` **0.3.1 — TeamRef metadata** - `TeamRef` and the `Team` reference shape gain optional `logo`, `city`, `mascot`, `conference`, `division`. ~93% coverage on `logo`, similar on the rest. All five default to `None`. ## Plus a public-release cleanup pass - Add MIT `LICENSE` and `SECURITY.md` - Untrack `tests/` and add to `.gitignore` (kept locally for dev); CI gate is now lint + typecheck across the 3.10–3.13 matrix - Tighten sdist via `[tool.hatch.build.targets.sdist]` so `.github/` workflows no longer ship to PyPI sdist - Update author email to `hello@sharpapi.io` - Misc internal-comment cleanup ## Compatibility No existing field renamed, retyped, or removed. Code on 0.2.x continues to work. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 63895ef commit 76d98f0

17 files changed

Lines changed: 291 additions & 1433 deletions

.github/workflows/publish.yml

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,11 @@ on:
1010
workflow_dispatch:
1111

1212
jobs:
13-
# Re-run the full test matrix on the tagged commit before publish.
14-
# Mirrors test.yml so a tagged release gets the same coverage as
15-
# a push/PR. Previously publish.yml ran zero tests — a broken SDK
16-
# could reach PyPI, and PyPI versions are immutable (only super-
17-
# ceded). needs: test below gates the publish job on every matrix
18-
# entry passing.
19-
test:
13+
# Mirrors test.yml on the tagged commit: lint + typecheck across the
14+
# support matrix before publish. PyPI versions are immutable, so any
15+
# gate we can apply pre-upload is cheaper than publishing a broken
16+
# release. needs: gate below blocks publish on every matrix entry.
17+
gate:
2018
runs-on: ubuntu-latest
2119
strategy:
2220
matrix:
@@ -31,7 +29,7 @@ jobs:
3129

3230
- name: Install dependencies
3331
run: |
34-
pip install -e ".[test,pandas]"
32+
pip install -e ".[pandas]"
3533
pip install ruff pyright
3634
3735
- name: Ruff check
@@ -40,11 +38,8 @@ jobs:
4038
- name: Pyright
4139
run: pyright
4240

43-
- name: Run tests
44-
run: pytest -v
45-
4641
publish:
47-
needs: test
42+
needs: gate
4843
runs-on: ubuntu-latest
4944
permissions:
5045
id-token: write

.github/workflows/test.yml

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,11 @@ jobs:
2222

2323
- name: Install dependencies
2424
run: |
25-
pip install -e ".[test,pandas]"
25+
pip install -e ".[pandas]"
2626
pip install ruff pyright
2727
2828
- name: Ruff check
29-
# [tool.ruff] config is already in pyproject.toml — rules E, F,
30-
# I, UP. This step enforces them; before today they were only
31-
# suggestions.
3229
run: ruff check
3330

3431
- name: Pyright
35-
# Same story — [tool.pyright] was set to standard mode in
36-
# pyproject.toml but never invoked in CI.
3732
run: pyright
38-
39-
- name: Run tests
40-
run: pytest -v

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,9 @@ build/
1010
.ruff_cache/
1111
*.egg
1212
.mypy_cache/
13+
.coverage
14+
htmlcov/
15+
.claude/
16+
.DS_Store
17+
tests/
18+
.benchmarks/

CHANGELOG.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Changelog
2+
3+
All notable changes to the `sharpapi` Python SDK are documented here.
4+
5+
## 0.3.1 — 2026-05-06
6+
7+
### Added — TeamRef metadata
8+
9+
`TeamRef` now exposes five additional optional fields:
10+
11+
- `logo` — full CDN URL. ~93% of teams are populated.
12+
- `city` — e.g. `"Arizona"` for the Diamondbacks.
13+
- `mascot` — e.g. `"Diamondbacks"`.
14+
- `conference` — e.g. `"NL"`, `"AFC"`, `"Western"`.
15+
- `division` — e.g. `"West Division"`, `"NL East"`, `"Pacific Division"`.
16+
17+
All five default to `None` and are additive — existing 0.3.0 code keeps
18+
working unchanged.
19+
20+
## 0.3.0 — 2026-05-06
21+
22+
### Added — nested refs
23+
24+
Every odds row, opportunity row, and reference-list row may now carry
25+
optional structured reference objects alongside the existing flat fields.
26+
All new fields are **optional and additive** — clients on older API
27+
versions (or talking to older API servers) see `None` and behave
28+
identically.
29+
30+
New models:
31+
32+
- `TeamRef``id`, `numerical_id`, `name`, `abbreviation` (latter only on
33+
team-sport competitors)
34+
- `SportRef``id`, `name`, `numerical_id`
35+
- `EntityRef``id`, `label`, `numerical_id` (used for league / market /
36+
sportsbook refs)
37+
38+
New optional fields:
39+
40+
- `OddsLine`, `EVOpportunity`, `ArbitrageOpportunity`, `MiddleOpportunity`,
41+
`LowHoldOpportunity` — all gain `home`, `away`, `sport_ref`, `league_ref`,
42+
`market_ref`, `sportsbook_ref` (legs / opps without a single book skip
43+
`sportsbook_ref`).
44+
- `ArbitrageLeg` — gains `sportsbook_ref`.
45+
- `ClosingOddsLine` — gains `market_ref`, `sportsbook_ref`.
46+
- `ClosingSnapshot` — gains `home`, `away`, `sport_ref`, `league_ref`.
47+
- `Sport`, `League`, `Sportsbook`, `Market` — gain `numerical_id`.
48+
- `Event` — gains `home`, `away`, `sport_ref`, `league_ref`.
49+
50+
New reference model:
51+
52+
- `Team` — for the `/teams` reference endpoint, includes optional
53+
`abbreviation` and `numerical_id`.
54+
55+
### Backward compatibility
56+
57+
No existing field was renamed, retyped, or removed. Code that does not
58+
reference the new attributes continues to work without changes.

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 SharpAPI LLC
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.

SECURITY.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Security Policy
2+
3+
## Reporting a Vulnerability
4+
5+
If you believe you have found a security vulnerability in this SDK or in
6+
the SharpAPI service, please report it privately to:
7+
8+
**hello@sharpapi.io** (subject line: `[SECURITY] <short summary>`)
9+
10+
Please do not open a public GitHub issue for security reports.
11+
12+
We will acknowledge receipt within 72 hours and aim to provide a status
13+
update within 7 days. If the issue is confirmed, we will work with you on
14+
disclosure timing.
15+
16+
## Scope
17+
18+
In scope:
19+
- This SDK package and its published artifact on PyPI
20+
- The SharpAPI HTTP and WebSocket APIs (`api.sharpapi.io`, `ws.sharpapi.io`)
21+
22+
Out of scope:
23+
- Findings in third-party dependencies (please report those upstream)
24+
- Denial of service via brute-force or volumetric attacks against the API
25+
- Issues that require physical access to a user's device

pyproject.toml

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "sharpapi"
7-
version = "0.2.6"
7+
version = "0.3.1"
88
description = "Official Python SDK for the SharpAPI real-time sports betting odds API"
99
readme = "README.md"
1010
license = "MIT"
1111
requires-python = ">=3.10"
12-
authors = [{ name = "SharpAPI", email = "support@sharpapi.io" }]
12+
authors = [{ name = "SharpAPI", email = "hello@sharpapi.io" }]
1313
keywords = ["sports-betting", "odds", "arbitrage", "ev", "api", "real-time", "pinnacle"]
1414
classifiers = [
1515
"Development Status :: 4 - Beta",
@@ -41,6 +41,25 @@ Changelog = "https://github.com/Sharp-API/sharpapi-python/releases"
4141
[tool.hatch.build.targets.wheel]
4242
packages = ["src/sharpapi"]
4343

44+
# Explicit sdist whitelist. Hatchling's default sdist would otherwise
45+
# include everything not gitignored (CI workflows, dependabot config,
46+
# .gitignore itself), all of which would land on PyPI on every release.
47+
# Keep this in sync with what we actually want users who do
48+
# `pip install --no-binary :all: sharpapi` to receive.
49+
[tool.hatch.build.targets.sdist]
50+
include = [
51+
"src/sharpapi",
52+
"README.md",
53+
"LICENSE",
54+
"CHANGELOG.md",
55+
"SECURITY.md",
56+
"pyproject.toml",
57+
]
58+
exclude = [
59+
".gitignore",
60+
".github",
61+
]
62+
4463
[tool.ruff]
4564
target-version = "py310"
4665
line-length = 100

src/sharpapi/__init__.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
ArbitrageOpportunity,
3939
ClosingOddsLine,
4040
ClosingSnapshot,
41+
EntityRef,
4142
Event,
4243
EVOpportunity,
4344
GameState,
@@ -53,11 +54,14 @@
5354
RateLimitInfo,
5455
ResponseMeta,
5556
Sport,
57+
SportRef,
5658
Sportsbook,
59+
Team,
60+
TeamRef,
5761
)
5862
from .streaming import EventStream
5963

60-
__version__ = "0.2.6"
64+
__version__ = "0.3.1"
6165

6266
__all__ = [
6367
# Clients
@@ -71,6 +75,7 @@
7175
"ArbitrageOpportunity",
7276
"ClosingOddsLine",
7377
"ClosingSnapshot",
78+
"EntityRef",
7479
"EVOpportunity",
7580
"Event",
7681
"GameState",
@@ -86,7 +91,10 @@
8691
"RateLimitInfo",
8792
"ResponseMeta",
8893
"Sport",
94+
"SportRef",
8995
"Sportsbook",
96+
"Team",
97+
"TeamRef",
9098
# Streaming
9199
"EventStream",
92100
# Exceptions

src/sharpapi/exceptions.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
"""SharpAPI exceptions and canonical error-code registry.
22
3-
The error codes here mirror ``pkg/errcodes/errcodes.go`` in sharp-api-go, which
4-
is the single source of truth for every code the API emits. Keep this file in
5-
sync when new codes are added upstream.
3+
The codes here mirror the canonical set the SharpAPI server emits.
4+
Keep this file in sync when new codes are added upstream.
65
"""
76

87
from __future__ import annotations
@@ -65,9 +64,10 @@ class StreamError(SharpAPIError):
6564
# =============================================================================
6665
# Canonical error-code registry
6766
#
68-
# Mirrors sharp-api-go/pkg/errcodes/errcodes.go. When upstream adds a new code,
69-
# add it here too and update the matching description. Each code maps to the
70-
# Python exception class that ``handle_errors`` (in ``_base.py``) raises for it.
67+
# Mirrors the canonical SharpAPI server error-code set. When upstream adds
68+
# a new code, add it here too and update the matching description. Each
69+
# code maps to the Python exception class that ``handle_errors`` (in
70+
# ``_base.py``) raises for it.
7171
# =============================================================================
7272

7373
# HTTP error codes — emitted via REST handlers (httputil.WriteJSONError).
@@ -174,10 +174,10 @@ class StreamError(SharpAPIError):
174174
UPSTREAM_ERROR: SharpAPIError,
175175
}
176176

177-
# Deprecated aliases. ``bad_request`` and ``invalid_request`` were both collapsed
178-
# into ``validation_error`` in sharp-api-go. Kept here so that older API
179-
# responses (or user code still checking these strings) resolve correctly.
180-
# TODO: remove after 2026-10.
177+
# Deprecated aliases. ``bad_request`` and ``invalid_request`` were both
178+
# collapsed into ``validation_error`` server-side. Kept here so that older
179+
# API responses (or user code still checking these strings) resolve
180+
# correctly. Will be removed after 2026-10.
181181
DEPRECATED_CODE_ALIASES: dict[str, str] = {
182182
"bad_request": VALIDATION_ERROR,
183183
"invalid_request": VALIDATION_ERROR,

0 commit comments

Comments
 (0)