themeparks 2.0.0a1__tar.gz → 3.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.
- themeparks-3.0.0/CHANGELOG.md +126 -0
- {themeparks-2.0.0a1 → themeparks-3.0.0}/MIGRATION.md +1 -1
- {themeparks-2.0.0a1 → themeparks-3.0.0}/PKG-INFO +4 -4
- {themeparks-2.0.0a1 → themeparks-3.0.0}/pyproject.toml +13 -4
- {themeparks-2.0.0a1 → themeparks-3.0.0}/themeparks/_client.py +7 -1
- {themeparks-2.0.0a1 → themeparks-3.0.0}/themeparks/_generated/models.py +338 -107
- themeparks-2.0.0a1/CHANGELOG.md +0 -41
- {themeparks-2.0.0a1 → themeparks-3.0.0}/.gitignore +0 -0
- {themeparks-2.0.0a1 → themeparks-3.0.0}/LICENSE +0 -0
- {themeparks-2.0.0a1 → themeparks-3.0.0}/README.md +0 -0
- {themeparks-2.0.0a1 → themeparks-3.0.0}/themeparks/__init__.py +0 -0
- {themeparks-2.0.0a1 → themeparks-3.0.0}/themeparks/_cache.py +0 -0
- {themeparks-2.0.0a1 → themeparks-3.0.0}/themeparks/_ergonomic/__init__.py +0 -0
- {themeparks-2.0.0a1 → themeparks-3.0.0}/themeparks/_ergonomic/dates.py +0 -0
- {themeparks-2.0.0a1 → themeparks-3.0.0}/themeparks/_ergonomic/destinations.py +0 -0
- {themeparks-2.0.0a1 → themeparks-3.0.0}/themeparks/_ergonomic/entity.py +0 -0
- {themeparks-2.0.0a1 → themeparks-3.0.0}/themeparks/_ergonomic/live.py +0 -0
- {themeparks-2.0.0a1 → themeparks-3.0.0}/themeparks/_errors.py +0 -0
- {themeparks-2.0.0a1 → themeparks-3.0.0}/themeparks/_generated/__init__.py +0 -0
- {themeparks-2.0.0a1 → themeparks-3.0.0}/themeparks/_raw.py +0 -0
- {themeparks-2.0.0a1 → themeparks-3.0.0}/themeparks/_transport.py +0 -0
- {themeparks-2.0.0a1 → themeparks-3.0.0}/themeparks/py.typed +0 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [3.0.0] - 2026-09-08
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **Schedule entries now carry `purchases`.** The upstream spec described a
|
|
8
|
+
park's schedule two different ways: precisely when nested under a
|
|
9
|
+
destination, loosely when fetched directly. This client uses the direct
|
|
10
|
+
path, so `purchases` was absent from the model entirely. Magic Kingdom
|
|
11
|
+
serves 26 entries carrying purchases.
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
sched = client.entity(park_id).schedule()
|
|
15
|
+
for day in sched.schedule or []:
|
|
16
|
+
for p in day.purchases or []:
|
|
17
|
+
print(p.name, p.price.amount, p.price.currency)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
- **A null price on a schedule purchase no longer rejects the response.**
|
|
21
|
+
2.0.1 made `PriceData.amount` nullable, but the schedule path used a
|
|
22
|
+
second, inline price model that kept `amount` non-nullable. Both now use
|
|
23
|
+
`PriceData`. Tokyo Disneyland serves six Premier Access rows with a null
|
|
24
|
+
amount, and this client raised `ValidationError` on every one of them.
|
|
25
|
+
|
|
26
|
+
- Schedule entries gained the `description` field the API has always sent.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- **BREAKING — `ScheduleEntry.type` is an enum, not a `str`.** It was
|
|
31
|
+
`type: str`; it is now a `Type` enum. This breaks *silently*: the comparison
|
|
32
|
+
does not raise, it just stops being true.
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
# before: True. now: False, with no error.
|
|
36
|
+
if day.type == "OPERATING":
|
|
37
|
+
...
|
|
38
|
+
|
|
39
|
+
# use one of these instead
|
|
40
|
+
if day.type.value == "OPERATING":
|
|
41
|
+
...
|
|
42
|
+
from themeparks._generated.models import Type
|
|
43
|
+
if day.type is Type.OPERATING:
|
|
44
|
+
...
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Audit any comparison of `.type` against a string literal before upgrading.
|
|
48
|
+
This is the one change here that will not announce itself.
|
|
49
|
+
|
|
50
|
+
- **BREAKING — the `PricedScheduleEntry` and `Price` models are gone.**
|
|
51
|
+
`PricedScheduleEntry` is now `ScheduleEntry`, and the inline `Price` model
|
|
52
|
+
is replaced by `PriceData`. Neither was exported from the package root, so
|
|
53
|
+
this only affects code importing from `themeparks._generated.models`
|
|
54
|
+
directly — a private module.
|
|
55
|
+
|
|
56
|
+
- The duplicate `EntityType1` and `EntityType2` enums collapse into
|
|
57
|
+
`EntityType`. Same members, same values.
|
|
58
|
+
|
|
59
|
+
## [2.0.1] - 2026-09-01
|
|
60
|
+
### Fixed
|
|
61
|
+
- `PriceData.amount` is now nullable. The API returns `null` when a paid queue
|
|
62
|
+
exists but the provider does not publish a price, and `0` only when the queue
|
|
63
|
+
is genuinely free. Both previously arrived as `0`, so an unknown price was
|
|
64
|
+
indistinguishable from a free one.
|
|
65
|
+
|
|
66
|
+
Before this, a single null amount on one attraction made pydantic reject the
|
|
67
|
+
**entire** live response for that park, not just the one field.
|
|
68
|
+
|
|
69
|
+
Code testing `if price.amount:` or `if not price.amount:` now conflates
|
|
70
|
+
"free" with "price unknown" — the exact confusion this change exists to
|
|
71
|
+
remove. Switch to an explicit check:
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
if price.amount is None:
|
|
75
|
+
label = "price not published"
|
|
76
|
+
else:
|
|
77
|
+
label = f"{price.currency} {price.amount / 100:.2f}"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`price.formatted` is optional and may be absent when the amount is unknown,
|
|
81
|
+
so do not rely on it alone to detect the case.
|
|
82
|
+
|
|
83
|
+
## [2.0.0] - 2026-04-15
|
|
84
|
+
First stable v2 release. Identical surface to `2.0.0a1` after a brief alpha
|
|
85
|
+
soak; no code changes since `2.0.0a1`. Bumped `Development Status` classifier
|
|
86
|
+
to `Production/Stable`.
|
|
87
|
+
|
|
88
|
+
## [2.0.0a1] - 2026-04-15
|
|
89
|
+
### Added
|
|
90
|
+
- MkDocs Material documentation site with full API reference and a cookbook
|
|
91
|
+
(recipes for sorted wait times, 7-day schedules, geo-locations grouped by
|
|
92
|
+
entity type, every queue variant, and HTTP debugging).
|
|
93
|
+
- Top-level exports for `current_wait_time`, `iter_queues`, `parse_api_datetime`.
|
|
94
|
+
- Class-level docstrings on every public surface for readable API reference rendering.
|
|
95
|
+
- README sections explaining every queue variant (STANDBY, PAID_RETURN_TIME,
|
|
96
|
+
BOARDING_GROUP, etc.) and how to enable the httpx logger for HTTP debugging.
|
|
97
|
+
|
|
98
|
+
### Fixed
|
|
99
|
+
- `walk()` now makes a single API call instead of one per descendant — the
|
|
100
|
+
`/children` endpoint already returns the entire subtree recursively. Walking
|
|
101
|
+
Walt Disney World dropped from ~250 requests to 1.
|
|
102
|
+
- `get_entity_schedule_month` now zero-pads the month (`/schedule/2026/05`,
|
|
103
|
+
not `/schedule/2026/5`) per the API requirement.
|
|
104
|
+
- `RetryConfig` field renamed `max_attempts` → `max_retries` to match its
|
|
105
|
+
actual semantics (N retries beyond the first attempt = N+1 total calls).
|
|
106
|
+
- `APIError` now includes a server-body excerpt in the exception message;
|
|
107
|
+
`RateLimitError.__repr__` includes `retry_after`.
|
|
108
|
+
- `destinations.find()` now performs the loose, case-insensitive substring
|
|
109
|
+
match it had always promised in its docstring (was exact equality).
|
|
110
|
+
- Generated queue variant classes renamed (`STANDBY` → `StandbyQueue` etc.)
|
|
111
|
+
so users access `queue.STANDBY` instead of the awkward `queue.STANDBY_1`.
|
|
112
|
+
- `eval-type-backport` is now a conditional dep on Python 3.9 so pydantic
|
|
113
|
+
can evaluate PEP 604 union syntax in the generated models.
|
|
114
|
+
|
|
115
|
+
## [2.0.0a0] - 2026-04-14
|
|
116
|
+
### Added
|
|
117
|
+
- Full rewrite on pydantic v2 + httpx.
|
|
118
|
+
- Sync `ThemeParks` and `AsyncThemeParks` clients with shared core.
|
|
119
|
+
- Ergonomic `client.entity(id)` navigation, `walk()`, `schedule.range()`.
|
|
120
|
+
- Typed pydantic models, correctly handling nullable queue fields (fixes #1, #2).
|
|
121
|
+
- Default-on caching with per-endpoint TTLs and pluggable adapter.
|
|
122
|
+
- 429 `Retry-After` handling.
|
|
123
|
+
|
|
124
|
+
### Removed
|
|
125
|
+
- Legacy `openapi_client` top-level import and generated surface. See MIGRATION.md.
|
|
126
|
+
- `urllib3`-based transport.
|
|
@@ -170,7 +170,7 @@ The async surface mirrors the sync surface method-for-method.
|
|
|
170
170
|
|
|
171
171
|
## New in v2 (not in v1)
|
|
172
172
|
|
|
173
|
-
- `tp.entity(id).walk()` —
|
|
173
|
+
- `tp.entity(id).walk()` — iterator over every descendant in a single API call (the `/children` endpoint already returns the full subtree).
|
|
174
174
|
- `tp.entity(id).schedule.range(start, end)` — stitches monthly schedule
|
|
175
175
|
responses into a single sorted, filtered list.
|
|
176
176
|
- `tp.destinations.find(query)` — case-insensitive lookup by slug or name.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: themeparks
|
|
3
|
-
Version:
|
|
3
|
+
Version: 3.0.0
|
|
4
4
|
Summary: Official SDK for the ThemeParks.wiki API
|
|
5
5
|
Project-URL: Homepage, https://api.themeparks.wiki
|
|
6
6
|
Project-URL: Source, https://github.com/ThemeParks/ThemeParks_Python
|
|
@@ -9,7 +9,7 @@ Author: ThemeParks.wiki contributors
|
|
|
9
9
|
License: MIT
|
|
10
10
|
License-File: LICENSE
|
|
11
11
|
Keywords: api,disney,themeparks,universal,wait-times
|
|
12
|
-
Classifier: Development Status ::
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
14
14
|
Classifier: License :: OSI Approved :: MIT License
|
|
15
15
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -30,7 +30,7 @@ Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
|
30
30
|
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
31
31
|
Requires-Dist: pytest-recording>=0.13; extra == 'dev'
|
|
32
32
|
Requires-Dist: pytest>=8; extra == 'dev'
|
|
33
|
-
Requires-Dist: ruff
|
|
33
|
+
Requires-Dist: ruff<0.17,>=0.4; extra == 'dev'
|
|
34
34
|
Provides-Extra: docs
|
|
35
35
|
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
|
|
36
36
|
Requires-Dist: mkdocs>=1.5; extra == 'docs'
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "themeparks"
|
|
7
|
-
version = "
|
|
7
|
+
version = "3.0.0"
|
|
8
8
|
description = "Official SDK for the ThemeParks.wiki API"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -12,7 +12,7 @@ license = { text = "MIT" }
|
|
|
12
12
|
authors = [{ name = "ThemeParks.wiki contributors" }]
|
|
13
13
|
keywords = ["themeparks", "disney", "universal", "wait-times", "api"]
|
|
14
14
|
classifiers = [
|
|
15
|
-
"Development Status ::
|
|
15
|
+
"Development Status :: 5 - Production/Stable",
|
|
16
16
|
"Intended Audience :: Developers",
|
|
17
17
|
"License :: OSI Approved :: MIT License",
|
|
18
18
|
"Programming Language :: Python :: 3",
|
|
@@ -40,7 +40,9 @@ dev = [
|
|
|
40
40
|
"pytest>=8",
|
|
41
41
|
"pytest-asyncio>=0.23",
|
|
42
42
|
"pytest-recording>=0.13",
|
|
43
|
-
|
|
43
|
+
# Upper-bounded: an unpinned minor bump is what started reformatting
|
|
44
|
+
# Markdown code blocks and turned CI red on unrelated PRs.
|
|
45
|
+
"ruff>=0.4,<0.17",
|
|
44
46
|
"mypy>=1.10",
|
|
45
47
|
"datamodel-code-generator[http]>=0.25",
|
|
46
48
|
]
|
|
@@ -63,6 +65,11 @@ asyncio_mode = "auto"
|
|
|
63
65
|
[tool.ruff]
|
|
64
66
|
line-length = 100
|
|
65
67
|
target-version = "py39"
|
|
68
|
+
# Markdown is documentation, not source. Ruff 0.16 began formatting Python
|
|
69
|
+
# blocks inside it, which rewrites hand-written examples in README.md,
|
|
70
|
+
# MIGRATION.md and docs/ — CI has been failing `ruff format --check .` on
|
|
71
|
+
# those four files regardless of what any PR changes. Prose stays ours.
|
|
72
|
+
extend-exclude = ["*.md"]
|
|
66
73
|
|
|
67
74
|
[tool.ruff.lint]
|
|
68
75
|
select = ["E", "F", "W", "I", "N", "UP", "B", "A", "SIM", "PL"]
|
|
@@ -72,7 +79,9 @@ select = ["E", "F", "W", "I", "N", "UP", "B", "A", "SIM", "PL"]
|
|
|
72
79
|
"themeparks/_errors.py" = ["A001", "UP045"]
|
|
73
80
|
"themeparks/_transport.py" = ["A004"]
|
|
74
81
|
"themeparks/__init__.py" = ["A004"]
|
|
75
|
-
|
|
82
|
+
# E501: field descriptions come from the upstream spec, so line length is not
|
|
83
|
+
# this repo's to choose. The PriceData.amount description is already over.
|
|
84
|
+
"themeparks/_generated/*.py" = ["N815", "F401", "E501"]
|
|
76
85
|
|
|
77
86
|
[tool.mypy]
|
|
78
87
|
python_version = "3.9"
|
|
@@ -13,7 +13,7 @@ from themeparks._raw import AsyncRawClient, RawClient
|
|
|
13
13
|
from themeparks._transport import AsyncTransport, RetryConfig, SyncTransport
|
|
14
14
|
|
|
15
15
|
DEFAULT_BASE_URL = "https://api.themeparks.wiki/v1"
|
|
16
|
-
PACKAGE_VERSION = "2.0.
|
|
16
|
+
PACKAGE_VERSION = "2.0.0"
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
def _default_user_agent() -> str:
|
|
@@ -118,6 +118,9 @@ class ThemeParks:
|
|
|
118
118
|
def __exit__(self, exc_type: Any, exc: Any, tb: Any) -> None:
|
|
119
119
|
self.close()
|
|
120
120
|
|
|
121
|
+
def __repr__(self) -> str:
|
|
122
|
+
return f"ThemeParks(base_url={str(self._client.base_url).rstrip('/')!r})"
|
|
123
|
+
|
|
121
124
|
|
|
122
125
|
class AsyncThemeParks:
|
|
123
126
|
"""Asynchronous mirror of :class:`ThemeParks`.
|
|
@@ -171,3 +174,6 @@ class AsyncThemeParks:
|
|
|
171
174
|
|
|
172
175
|
async def __aexit__(self, exc_type: Any, exc: Any, tb: Any) -> None:
|
|
173
176
|
await self.close()
|
|
177
|
+
|
|
178
|
+
def __repr__(self) -> str:
|
|
179
|
+
return f"AsyncThemeParks(base_url={str(self._client.base_url).rstrip('/')!r})"
|
|
@@ -9,139 +9,355 @@ from typing import Annotated, Any
|
|
|
9
9
|
from pydantic import AwareDatetime, BaseModel, Field
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
class
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
RESTAURANT = "RESTAURANT"
|
|
17
|
-
HOTEL = "HOTEL"
|
|
18
|
-
SHOW = "SHOW"
|
|
19
|
-
|
|
12
|
+
class BoardingGroupState(Enum):
|
|
13
|
+
"""
|
|
14
|
+
State of boarding group availability
|
|
15
|
+
"""
|
|
20
16
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
DOWN = "DOWN"
|
|
17
|
+
AVAILABLE = "AVAILABLE"
|
|
18
|
+
PAUSED = "PAUSED"
|
|
24
19
|
CLOSED = "CLOSED"
|
|
25
|
-
REFURBISHMENT = "REFURBISHMENT"
|
|
26
20
|
|
|
27
21
|
|
|
28
|
-
class
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
22
|
+
class DiningAvailability(BaseModel):
|
|
23
|
+
partySize: float | None = None
|
|
24
|
+
"""
|
|
25
|
+
Available party size
|
|
26
|
+
"""
|
|
27
|
+
waitTime: float | None = None
|
|
28
|
+
"""
|
|
29
|
+
Current wait time in minutes
|
|
30
|
+
"""
|
|
32
31
|
|
|
33
32
|
|
|
34
|
-
class
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
class EntityLocation(BaseModel):
|
|
34
|
+
latitude: float | None = None
|
|
35
|
+
"""
|
|
36
|
+
Latitude coordinate of the entity location
|
|
37
|
+
"""
|
|
38
|
+
longitude: float | None = None
|
|
39
|
+
"""
|
|
40
|
+
Longitude coordinate of the entity location
|
|
41
|
+
"""
|
|
38
42
|
|
|
39
43
|
|
|
40
|
-
class
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
class EntityType(Enum):
|
|
45
|
+
"""
|
|
46
|
+
Type of entity in the theme park system
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
DESTINATION = "DESTINATION"
|
|
50
|
+
PARK = "PARK"
|
|
51
|
+
ATTRACTION = "ATTRACTION"
|
|
52
|
+
RESTAURANT = "RESTAURANT"
|
|
53
|
+
HOTEL = "HOTEL"
|
|
54
|
+
SHOW = "SHOW"
|
|
44
55
|
|
|
45
56
|
|
|
46
57
|
class StandbyQueue(BaseModel):
|
|
47
58
|
waitTime: float | None = None
|
|
59
|
+
"""
|
|
60
|
+
Current standby wait time in minutes
|
|
61
|
+
"""
|
|
48
62
|
|
|
49
63
|
|
|
50
64
|
class SingleRiderQueue(BaseModel):
|
|
51
65
|
waitTime: float | None = None
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
state: ReturnTimeState | None = None
|
|
56
|
-
returnStart: AwareDatetime | None = None
|
|
57
|
-
returnEnd: AwareDatetime | None = None
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
class PaidReturnTimeQueue(BaseModel):
|
|
61
|
-
state: ReturnTimeState | None = None
|
|
62
|
-
returnStart: AwareDatetime | None = None
|
|
63
|
-
returnEnd: AwareDatetime | None = None
|
|
64
|
-
price: PriceData | None = None
|
|
66
|
+
"""
|
|
67
|
+
Current single rider wait time in minutes
|
|
68
|
+
"""
|
|
65
69
|
|
|
66
70
|
|
|
67
71
|
class BoardingGroupQueue(BaseModel):
|
|
68
72
|
allocationStatus: BoardingGroupState | None = None
|
|
69
73
|
currentGroupStart: float | None = None
|
|
74
|
+
"""
|
|
75
|
+
Current boarding group start number
|
|
76
|
+
"""
|
|
70
77
|
currentGroupEnd: float | None = None
|
|
78
|
+
"""
|
|
79
|
+
Current boarding group end number
|
|
80
|
+
"""
|
|
71
81
|
nextAllocationTime: AwareDatetime | None = None
|
|
82
|
+
"""
|
|
83
|
+
Next boarding group allocation time
|
|
84
|
+
"""
|
|
72
85
|
estimatedWait: float | None = None
|
|
86
|
+
"""
|
|
87
|
+
Estimated wait time in minutes
|
|
88
|
+
"""
|
|
73
89
|
|
|
74
90
|
|
|
75
91
|
class PaidStandbyQueue(BaseModel):
|
|
76
92
|
waitTime: float | None = None
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
STANDBY: StandbyQueue | None = None
|
|
81
|
-
SINGLE_RIDER: SingleRiderQueue | None = None
|
|
82
|
-
RETURN_TIME: ReturnTimeQueue | None = None
|
|
83
|
-
PAID_RETURN_TIME: PaidReturnTimeQueue | None = None
|
|
84
|
-
BOARDING_GROUP: BoardingGroupQueue | None = None
|
|
85
|
-
PAID_STANDBY: PaidStandbyQueue | None = None
|
|
93
|
+
"""
|
|
94
|
+
Current paid standby wait time in minutes
|
|
95
|
+
"""
|
|
86
96
|
|
|
87
97
|
|
|
88
98
|
class LiveShowTime(BaseModel):
|
|
89
99
|
type: str
|
|
100
|
+
"""
|
|
101
|
+
Type of show time entry
|
|
102
|
+
"""
|
|
90
103
|
startTime: AwareDatetime | None = None
|
|
104
|
+
"""
|
|
105
|
+
Start time of the show
|
|
106
|
+
"""
|
|
91
107
|
endTime: AwareDatetime | None = None
|
|
108
|
+
"""
|
|
109
|
+
End time of the show
|
|
110
|
+
"""
|
|
92
111
|
|
|
93
112
|
|
|
94
|
-
class
|
|
95
|
-
|
|
96
|
-
|
|
113
|
+
class LiveStatusType(Enum):
|
|
114
|
+
"""
|
|
115
|
+
Current operating status of an entity
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
OPERATING = "OPERATING"
|
|
119
|
+
DOWN = "DOWN"
|
|
120
|
+
CLOSED = "CLOSED"
|
|
121
|
+
REFURBISHMENT = "REFURBISHMENT"
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class Park(BaseModel):
|
|
125
|
+
id: str
|
|
126
|
+
"""
|
|
127
|
+
Unique identifier of the park
|
|
128
|
+
"""
|
|
129
|
+
name: str
|
|
130
|
+
"""
|
|
131
|
+
Name of the park
|
|
132
|
+
"""
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class PriceData(BaseModel):
|
|
136
|
+
amount: float | None = None
|
|
137
|
+
"""
|
|
138
|
+
Numerical price amount, in the currency's lowest denomination (e.g. cents). null when the item costs money but the provider does not publish an amount; 0 means genuinely free
|
|
139
|
+
"""
|
|
140
|
+
currency: str
|
|
141
|
+
"""
|
|
142
|
+
Currency code
|
|
143
|
+
"""
|
|
144
|
+
formatted: str | None = None
|
|
145
|
+
"""
|
|
146
|
+
Formatted price string
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class ReturnTimeState(Enum):
|
|
151
|
+
"""
|
|
152
|
+
State of return time availability
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
AVAILABLE = "AVAILABLE"
|
|
156
|
+
TEMP_FULL = "TEMP_FULL"
|
|
157
|
+
FINISHED = "FINISHED"
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class Type(Enum):
|
|
161
|
+
"""
|
|
162
|
+
Type of schedule entry
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
OPERATING = "OPERATING"
|
|
166
|
+
TICKETED_EVENT = "TICKETED_EVENT"
|
|
167
|
+
PRIVATE_EVENT = "PRIVATE_EVENT"
|
|
168
|
+
EXTRA_HOURS = "EXTRA_HOURS"
|
|
169
|
+
INFO = "INFO"
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class SchedulePriceType(Enum):
|
|
173
|
+
"""
|
|
174
|
+
Type of schedule price
|
|
175
|
+
"""
|
|
176
|
+
|
|
177
|
+
ADMISSION = "ADMISSION"
|
|
178
|
+
PACKAGE = "PACKAGE"
|
|
179
|
+
ATTRACTION = "ATTRACTION"
|
|
97
180
|
|
|
98
181
|
|
|
99
182
|
class TagData(BaseModel):
|
|
100
183
|
tag: str
|
|
184
|
+
"""
|
|
185
|
+
Tag identifier
|
|
186
|
+
"""
|
|
101
187
|
tagName: str
|
|
188
|
+
"""
|
|
189
|
+
Human readable tag name
|
|
190
|
+
"""
|
|
102
191
|
id: str | None = None
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
192
|
+
"""
|
|
193
|
+
Unique identifier
|
|
194
|
+
"""
|
|
195
|
+
value: Any | None = None
|
|
196
|
+
"""
|
|
197
|
+
Tag value - can be string, number or object
|
|
198
|
+
"""
|
|
109
199
|
|
|
110
200
|
|
|
111
|
-
class
|
|
201
|
+
class DestinationEntry(BaseModel):
|
|
112
202
|
id: str
|
|
203
|
+
"""
|
|
204
|
+
Unique identifier of the destination
|
|
205
|
+
"""
|
|
113
206
|
name: str
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
207
|
+
"""
|
|
208
|
+
Name of the destination
|
|
209
|
+
"""
|
|
210
|
+
slug: str | None = None
|
|
211
|
+
"""
|
|
212
|
+
URL-friendly slug for the destination
|
|
213
|
+
"""
|
|
214
|
+
externalId: str | None = None
|
|
215
|
+
"""
|
|
216
|
+
External entity ID from the source data provider
|
|
217
|
+
"""
|
|
218
|
+
parks: list[Park]
|
|
219
|
+
"""
|
|
220
|
+
Array of parks within this destination
|
|
221
|
+
"""
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class DestinationsResponse(BaseModel):
|
|
225
|
+
destinations: list[DestinationEntry]
|
|
226
|
+
"""
|
|
227
|
+
Array of all destinations
|
|
228
|
+
"""
|
|
120
229
|
|
|
121
230
|
|
|
122
231
|
class EntityChild(BaseModel):
|
|
123
232
|
id: str
|
|
233
|
+
"""
|
|
234
|
+
Unique entity identifier
|
|
235
|
+
"""
|
|
124
236
|
name: str
|
|
237
|
+
"""
|
|
238
|
+
Entity name
|
|
239
|
+
"""
|
|
125
240
|
entityType: EntityType
|
|
126
241
|
externalId: str | None = None
|
|
242
|
+
"""
|
|
243
|
+
External identifier
|
|
244
|
+
"""
|
|
127
245
|
parentId: str | None = None
|
|
128
|
-
|
|
246
|
+
"""
|
|
247
|
+
Parent entity identifier
|
|
248
|
+
"""
|
|
249
|
+
location: EntityLocation | None = None
|
|
129
250
|
|
|
130
251
|
|
|
131
252
|
class EntityChildrenResponse(BaseModel):
|
|
132
253
|
id: str | None = None
|
|
254
|
+
"""
|
|
255
|
+
Parent entity identifier
|
|
256
|
+
"""
|
|
133
257
|
name: str | None = None
|
|
258
|
+
"""
|
|
259
|
+
Parent entity name
|
|
260
|
+
"""
|
|
134
261
|
entityType: EntityType | None = None
|
|
135
262
|
timezone: str | None = None
|
|
263
|
+
"""
|
|
264
|
+
Entity timezone
|
|
265
|
+
"""
|
|
136
266
|
children: list[EntityChild] | None = None
|
|
137
267
|
|
|
138
268
|
|
|
269
|
+
class EntityData(BaseModel):
|
|
270
|
+
id: str
|
|
271
|
+
"""
|
|
272
|
+
Unique entity identifier
|
|
273
|
+
"""
|
|
274
|
+
name: str
|
|
275
|
+
"""
|
|
276
|
+
Entity name
|
|
277
|
+
"""
|
|
278
|
+
entityType: EntityType
|
|
279
|
+
parentId: str | None = None
|
|
280
|
+
"""
|
|
281
|
+
Parent entity identifier
|
|
282
|
+
"""
|
|
283
|
+
destinationId: str | None = None
|
|
284
|
+
"""
|
|
285
|
+
DestinationEntry identifier
|
|
286
|
+
"""
|
|
287
|
+
timezone: str
|
|
288
|
+
"""
|
|
289
|
+
Entity timezone
|
|
290
|
+
"""
|
|
291
|
+
location: EntityLocation | None = None
|
|
292
|
+
tags: list[TagData] | None = None
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
class ReturnTimeQueue(BaseModel):
|
|
296
|
+
state: ReturnTimeState | None = None
|
|
297
|
+
returnStart: AwareDatetime | None = None
|
|
298
|
+
"""
|
|
299
|
+
Start time of return window
|
|
300
|
+
"""
|
|
301
|
+
returnEnd: AwareDatetime | None = None
|
|
302
|
+
"""
|
|
303
|
+
End time of return window
|
|
304
|
+
"""
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
class PaidReturnTimeQueue(BaseModel):
|
|
308
|
+
state: ReturnTimeState | None = None
|
|
309
|
+
returnStart: AwareDatetime | None = None
|
|
310
|
+
returnEnd: AwareDatetime | None = None
|
|
311
|
+
price: PriceData | None = None
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
class LiveQueue(BaseModel):
|
|
315
|
+
STANDBY: StandbyQueue | None = None
|
|
316
|
+
SINGLE_RIDER: SingleRiderQueue | None = None
|
|
317
|
+
RETURN_TIME: ReturnTimeQueue | None = None
|
|
318
|
+
PAID_RETURN_TIME: PaidReturnTimeQueue | None = None
|
|
319
|
+
BOARDING_GROUP: BoardingGroupQueue | None = None
|
|
320
|
+
PAID_STANDBY: PaidStandbyQueue | None = None
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
class SchedulePriceObject(BaseModel):
|
|
324
|
+
type: SchedulePriceType | None = None
|
|
325
|
+
"""
|
|
326
|
+
Type of price object
|
|
327
|
+
"""
|
|
328
|
+
id: str | None = None
|
|
329
|
+
"""
|
|
330
|
+
Unique identifier
|
|
331
|
+
"""
|
|
332
|
+
name: str | None = None
|
|
333
|
+
"""
|
|
334
|
+
Name of the price object
|
|
335
|
+
"""
|
|
336
|
+
price: PriceData | None = None
|
|
337
|
+
"""
|
|
338
|
+
Price of this purchase option
|
|
339
|
+
"""
|
|
340
|
+
available: bool | None = None
|
|
341
|
+
"""
|
|
342
|
+
Whether this price option is available
|
|
343
|
+
"""
|
|
344
|
+
|
|
345
|
+
|
|
139
346
|
class EntityLiveData(BaseModel):
|
|
140
347
|
id: str
|
|
348
|
+
"""
|
|
349
|
+
Entity identifier
|
|
350
|
+
"""
|
|
141
351
|
name: str
|
|
352
|
+
"""
|
|
353
|
+
Entity name
|
|
354
|
+
"""
|
|
142
355
|
entityType: EntityType
|
|
143
356
|
status: LiveStatusType | None = None
|
|
144
357
|
lastUpdated: AwareDatetime
|
|
358
|
+
"""
|
|
359
|
+
Last update timestamp
|
|
360
|
+
"""
|
|
145
361
|
queue: LiveQueue | None = None
|
|
146
362
|
showtimes: list[LiveShowTime] | None = None
|
|
147
363
|
operatingHours: list[LiveShowTime] | None = None
|
|
@@ -150,72 +366,87 @@ class EntityLiveData(BaseModel):
|
|
|
150
366
|
|
|
151
367
|
class EntityLiveDataResponse(BaseModel):
|
|
152
368
|
id: str | None = None
|
|
369
|
+
"""
|
|
370
|
+
Entity identifier
|
|
371
|
+
"""
|
|
153
372
|
name: str | None = None
|
|
373
|
+
"""
|
|
374
|
+
Entity name
|
|
375
|
+
"""
|
|
154
376
|
entityType: EntityType | None = None
|
|
155
377
|
timezone: str | None = None
|
|
378
|
+
"""
|
|
379
|
+
Entity timezone
|
|
380
|
+
"""
|
|
156
381
|
liveData: list[EntityLiveData] | None = None
|
|
157
382
|
|
|
158
383
|
|
|
159
|
-
class Type(Enum):
|
|
160
|
-
ADMISSION = "ADMISSION"
|
|
161
|
-
PACKAGE = "PACKAGE"
|
|
162
|
-
ATTRACTION = "ATTRACTION"
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
class SchedulePriceObject(BaseModel):
|
|
166
|
-
type: Type | None = None
|
|
167
|
-
id: str | None = None
|
|
168
|
-
name: str | None = None
|
|
169
|
-
price: PriceData | None = None
|
|
170
|
-
available: bool | None = None
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
class Type1(Enum):
|
|
174
|
-
OPERATING = "OPERATING"
|
|
175
|
-
TICKETED_EVENT = "TICKETED_EVENT"
|
|
176
|
-
PRIVATE_EVENT = "PRIVATE_EVENT"
|
|
177
|
-
EXTRA_HOURS = "EXTRA_HOURS"
|
|
178
|
-
INFO = "INFO"
|
|
179
|
-
|
|
180
|
-
|
|
181
384
|
class ScheduleEntry(BaseModel):
|
|
182
385
|
date: str
|
|
386
|
+
"""
|
|
387
|
+
Schedule date
|
|
388
|
+
"""
|
|
183
389
|
openingTime: AwareDatetime
|
|
390
|
+
"""
|
|
391
|
+
Opening time
|
|
392
|
+
"""
|
|
184
393
|
closingTime: AwareDatetime
|
|
185
|
-
|
|
394
|
+
"""
|
|
395
|
+
Closing time
|
|
396
|
+
"""
|
|
397
|
+
type: Type
|
|
398
|
+
"""
|
|
399
|
+
Type of schedule entry
|
|
400
|
+
"""
|
|
401
|
+
description: str | None = None
|
|
402
|
+
"""
|
|
403
|
+
Optional description of the schedule entry
|
|
404
|
+
"""
|
|
186
405
|
purchases: list[SchedulePriceObject] | None = None
|
|
406
|
+
"""
|
|
407
|
+
Available purchases for this schedule entry
|
|
408
|
+
"""
|
|
187
409
|
|
|
188
410
|
|
|
189
|
-
class
|
|
411
|
+
class ParkSchedule(BaseModel):
|
|
190
412
|
id: str | None = None
|
|
413
|
+
"""
|
|
414
|
+
Entity identifier
|
|
415
|
+
"""
|
|
191
416
|
name: str | None = None
|
|
417
|
+
"""
|
|
418
|
+
Entity name
|
|
419
|
+
"""
|
|
192
420
|
entityType: EntityType | None = None
|
|
421
|
+
"""
|
|
422
|
+
Type of entity
|
|
423
|
+
"""
|
|
193
424
|
timezone: str | None = None
|
|
194
|
-
schedule: list[ScheduleEntry] | None = None
|
|
195
|
-
parks: list[EntityScheduleResponse] | None = None
|
|
196
425
|
"""
|
|
197
|
-
|
|
426
|
+
Entity timezone
|
|
198
427
|
"""
|
|
428
|
+
schedule: list[ScheduleEntry] | None = None
|
|
199
429
|
|
|
200
430
|
|
|
201
|
-
class
|
|
202
|
-
id: str | None = None
|
|
203
|
-
name: str | None = None
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
class DestinationEntry(BaseModel):
|
|
431
|
+
class EntityScheduleResponse(BaseModel):
|
|
207
432
|
id: str | None = None
|
|
433
|
+
"""
|
|
434
|
+
Entity identifier
|
|
435
|
+
"""
|
|
208
436
|
name: str | None = None
|
|
209
|
-
slug: str | None = None
|
|
210
|
-
externalId: str | None = None
|
|
211
437
|
"""
|
|
212
|
-
|
|
438
|
+
Entity name
|
|
439
|
+
"""
|
|
440
|
+
entityType: EntityType | None = None
|
|
441
|
+
"""
|
|
442
|
+
Type of entity
|
|
443
|
+
"""
|
|
444
|
+
timezone: str | None = None
|
|
445
|
+
"""
|
|
446
|
+
Entity timezone
|
|
447
|
+
"""
|
|
448
|
+
schedule: list[ScheduleEntry] | None = None
|
|
449
|
+
parks: list[ParkSchedule] | None = None
|
|
450
|
+
"""
|
|
451
|
+
Only included for destinations, lists all parks within the destination
|
|
213
452
|
"""
|
|
214
|
-
parks: list[DestinationParkEntry] | None = None
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
class DestinationsResponse(BaseModel):
|
|
218
|
-
destinations: list[DestinationEntry] | None = None
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
EntityScheduleResponse.model_rebuild()
|
themeparks-2.0.0a1/CHANGELOG.md
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
## [2.0.0a1] - 2026-04-15
|
|
4
|
-
### Added
|
|
5
|
-
- MkDocs Material documentation site with full API reference and a cookbook
|
|
6
|
-
(recipes for sorted wait times, 7-day schedules, geo-locations grouped by
|
|
7
|
-
entity type, every queue variant, and HTTP debugging).
|
|
8
|
-
- Top-level exports for `current_wait_time`, `iter_queues`, `parse_api_datetime`.
|
|
9
|
-
- Class-level docstrings on every public surface for readable API reference rendering.
|
|
10
|
-
- README sections explaining every queue variant (STANDBY, PAID_RETURN_TIME,
|
|
11
|
-
BOARDING_GROUP, etc.) and how to enable the httpx logger for HTTP debugging.
|
|
12
|
-
|
|
13
|
-
### Fixed
|
|
14
|
-
- `walk()` now makes a single API call instead of one per descendant — the
|
|
15
|
-
`/children` endpoint already returns the entire subtree recursively. Walking
|
|
16
|
-
Walt Disney World dropped from ~250 requests to 1.
|
|
17
|
-
- `get_entity_schedule_month` now zero-pads the month (`/schedule/2026/05`,
|
|
18
|
-
not `/schedule/2026/5`) per the API requirement.
|
|
19
|
-
- `RetryConfig` field renamed `max_attempts` → `max_retries` to match its
|
|
20
|
-
actual semantics (N retries beyond the first attempt = N+1 total calls).
|
|
21
|
-
- `APIError` now includes a server-body excerpt in the exception message;
|
|
22
|
-
`RateLimitError.__repr__` includes `retry_after`.
|
|
23
|
-
- `destinations.find()` now performs the loose, case-insensitive substring
|
|
24
|
-
match it had always promised in its docstring (was exact equality).
|
|
25
|
-
- Generated queue variant classes renamed (`STANDBY` → `StandbyQueue` etc.)
|
|
26
|
-
so users access `queue.STANDBY` instead of the awkward `queue.STANDBY_1`.
|
|
27
|
-
- `eval-type-backport` is now a conditional dep on Python 3.9 so pydantic
|
|
28
|
-
can evaluate PEP 604 union syntax in the generated models.
|
|
29
|
-
|
|
30
|
-
## [2.0.0a0] - 2026-04-14
|
|
31
|
-
### Added
|
|
32
|
-
- Full rewrite on pydantic v2 + httpx.
|
|
33
|
-
- Sync `ThemeParks` and `AsyncThemeParks` clients with shared core.
|
|
34
|
-
- Ergonomic `client.entity(id)` navigation, `walk()`, `schedule.range()`.
|
|
35
|
-
- Typed pydantic models, correctly handling nullable queue fields (fixes #1, #2).
|
|
36
|
-
- Default-on caching with per-endpoint TTLs and pluggable adapter.
|
|
37
|
-
- 429 `Retry-After` handling.
|
|
38
|
-
|
|
39
|
-
### Removed
|
|
40
|
-
- Legacy `openapi_client` top-level import and generated surface. See MIGRATION.md.
|
|
41
|
-
- `urllib3`-based transport.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|