sweatstack 0.93.0__tar.gz → 0.94.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.
- {sweatstack-0.93.0 → sweatstack-0.94.0}/AGENTS.md +6 -2
- {sweatstack-0.93.0 → sweatstack-0.94.0}/CHANGELOG.md +157 -5
- {sweatstack-0.93.0 → sweatstack-0.94.0}/DEVELOPMENT.md +3 -1
- {sweatstack-0.93.0 → sweatstack-0.94.0}/PKG-INFO +5 -2
- {sweatstack-0.93.0 → sweatstack-0.94.0}/README.md +3 -1
- sweatstack-0.94.0/UPGRADING.md +62 -0
- sweatstack-0.94.0/plans/010_credentials_one_source.md +497 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/pyproject.toml +2 -1
- {sweatstack-0.93.0 → sweatstack-0.94.0}/skills/sweatstack-python/SKILL.md +7 -2
- {sweatstack-0.93.0 → sweatstack-0.94.0}/skills/sweatstack-python/api.md +9 -3
- {sweatstack-0.93.0 → sweatstack-0.94.0}/skills/sweatstack-python/streamlit.md +7 -5
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/_renames.py +3 -1
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/client.py +120 -66
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/fastapi/dependencies.py +2 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/resources/oauth.py +7 -2
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/streamlit.py +128 -31
- sweatstack-0.94.0/tests/conftest.py +26 -0
- sweatstack-0.94.0/tests/test_changelog.py +73 -0
- sweatstack-0.94.0/tests/test_credentials.py +261 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/tests/test_identity.py +1 -5
- sweatstack-0.94.0/tests/test_streamlit_auth.py +199 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/tests/test_transport.py +3 -5
- {sweatstack-0.93.0 → sweatstack-0.94.0}/uv.lock +1 -1
- {sweatstack-0.93.0 → sweatstack-0.94.0}/.git-blame-ignore-revs +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/.github/workflows/ci.yml +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/.gitignore +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/.gitleaks.toml +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/.python-version +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/CONTRIBUTING.md +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/LICENSE +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/Makefile +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/evals/__init__.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/evals/agent/__init__.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/evals/agent/agent.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/evals/agent/cases.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/evals/agent/evaluators.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/evals/agent/harness.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/evals/agent/lifecycle.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/evals/agent/run.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/examples/fastapi_webhooks_example.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/examples/send_webhook.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/plans/001a_tests.md +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/plans/001b_metadata.md +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/plans/001c_dailies.md +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/plans/002_TYPED_EXCEPTIONS.md +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/plans/003_trace_test_linking.md +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/plans/004_codebase_hygiene.md +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/plans/005_ost_sport_bridge.md +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/plans/006_output_backends.md +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/plans/007_account_status_userinfo_portal.md +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/plans/008_sdk_documentation.md +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/plans/009_road_to_1_0.md +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/skills/sweatstack-python/data-models.md +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/skills/sweatstack-python/fastapi.md +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/__init__.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/_docs.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/_frames.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/_transport.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/cli.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/constants.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/exceptions.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/fastapi/__init__.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/fastapi/access_token_cache.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/fastapi/config.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/fastapi/models.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/fastapi/routes.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/fastapi/session.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/fastapi/token_stores.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/fastapi/webhooks.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/openapi_schemas.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/py.typed +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/resources/__init__.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/resources/_app_metadata.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/resources/_base.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/resources/activities.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/resources/dailies.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/resources/portal.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/resources/profile.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/resources/teams.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/resources/tests.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/resources/traces.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/resources/users.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/schemas.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/src/sweatstack/utils.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/tests/__init__.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/tests/test_access_token_cache.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/tests/test_account_status.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/tests/test_dailies.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/tests/test_docs_check.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/tests/test_docstrings.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/tests/test_dtype_conversion.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/tests/test_exceptions.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/tests/test_frames.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/tests/test_longitudinal_mean_max_after.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/tests/test_metadata.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/tests/test_output.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/tests/test_portal.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/tests/test_public_hygiene.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/tests/test_public_surface.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/tests/test_renames.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/tests/test_resources.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/tests/test_segmentation.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/tests/test_skill.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/tests/test_sport_ost.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/tests/test_teams.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/tests/test_tests.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/tests/test_trace_test_linking.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/tests/test_webhooks.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/tests/test_wire_names.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/tests/test_write_timezone_validation.py +0 -0
- {sweatstack-0.93.0 → sweatstack-0.94.0}/tests/typing/output_types.py +0 -0
|
@@ -258,9 +258,13 @@ fields unchanged (users) is an `update` and sends only the fields given.
|
|
|
258
258
|
|
|
259
259
|
Until 1.0, breaking changes are allowed when they ship with an upgrade path. Every one needs:
|
|
260
260
|
|
|
261
|
-
1. A `### Upgrading` section in the CHANGELOG entry, with an old-to-new table.
|
|
261
|
+
1. A `### Upgrading` section in the CHANGELOG entry, with an old-to-new table. Mark a row
|
|
262
|
+
whose change raises nothing as **Silent:**. Wrap the section in
|
|
263
|
+
`<!-- --8<-- [start:upgrading-X-Y] -->` / `[end:...]` markers (the docs include it), and add
|
|
264
|
+
a `## X.Y` section with that include to the docs' Upgrading page. `tests/test_changelog.py`
|
|
265
|
+
checks that every entry saying "breaking" has the section and its markers.
|
|
262
266
|
2. A copy-paste migration prompt for coding agents in the same section, for any change bigger
|
|
263
|
-
than a rename or two.
|
|
267
|
+
than a rename or two. The generic prompt in `UPGRADING.md` covers the rest.
|
|
264
268
|
3. For a removed method or attribute, an entry in `_renames.REMOVED_IN`, so calling it raises an
|
|
265
269
|
`AttributeError` that names the replacement. Entries are permanent.
|
|
266
270
|
`tests/test_renames.py` checks each one against the client, the module and the CHANGELOG.
|
|
@@ -5,6 +5,54 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
Until 1.0 a minor release may contain breaking changes. Every one has an **Upgrading** section
|
|
9
|
+
with a table from old to new. The procedure for moving across several releases, and a prompt
|
|
10
|
+
for a coding agent, are in `UPGRADING.md` next to this file and at
|
|
11
|
+
https://docs.sweatstack.no/learn/python/upgrading/.
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## [0.94.0] - 2026-10-12
|
|
15
|
+
|
|
16
|
+
A client's tokens come from one source. Upgrade every app that other people sign in to.
|
|
17
|
+
|
|
18
|
+
### Security
|
|
19
|
+
|
|
20
|
+
- A `Client` in a process shared by several users (Streamlit, FastAPI, any server) could pick
|
|
21
|
+
up a refresh token, or a whole token pair, that another user's client had saved on that
|
|
22
|
+
machine, and act as that user. Credentials now come from a single source: the arguments you
|
|
23
|
+
pass, else the environment, else the tokens saved by `authenticate()`. A client never
|
|
24
|
+
combines halves from different sources, and writes the saved tokens only when they came from
|
|
25
|
+
there. The Streamlit and FastAPI integrations never read or write saved tokens.
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- Streamlit apps behind a proxy no longer fail after the access token expires. With a proxy
|
|
30
|
+
that forwards `X-SweatStack-Refresh-Token` (the SweatStack Streamlit template does), the
|
|
31
|
+
session renews its own token and keeps the renewed pair across reruns. Streamlit apps in
|
|
32
|
+
OAuth mode refresh once per token lifetime instead of once per rerun.
|
|
33
|
+
- A refresh response that carries a new refresh token replaces the old one.
|
|
34
|
+
- `StreamlitAuth.select_user()` with a user that gets no refresh token (admin access) no longer
|
|
35
|
+
keeps the previous user's refresh token.
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
|
|
39
|
+
- `Client(..., ambient_credentials=False)` builds a client that never reads the environment or
|
|
40
|
+
the saved tokens and never writes them. Use it for per-user clients in your own server.
|
|
41
|
+
- `oauth.exchange_code()` no longer saves the tokens on the machine by default; pass
|
|
42
|
+
`persist=True` for that. `authenticate()` still saves them.
|
|
43
|
+
- `StreamlitAuth.behind_proxy()` checks token expiry again. Without a forwarded refresh token,
|
|
44
|
+
calls after expiry raise `SweatStackTokenRefreshError` with a reload hint, where the API
|
|
45
|
+
returned a 401.
|
|
46
|
+
|
|
47
|
+
<!-- --8<-- [start:upgrading-0-94] -->
|
|
48
|
+
### Upgrading
|
|
49
|
+
|
|
50
|
+
| 0.93 | 0.94 |
|
|
51
|
+
|---|---|
|
|
52
|
+
| `Client(api_key=...)` without a refresh token renewed with `SWEATSTACK_REFRESH_TOKEN` or the saved refresh token | **Silent:** it never borrows one, and raises `SweatStackTokenRefreshError` on expiry. Pass `refresh_token=` too, or pass neither to use the environment or the saved tokens |
|
|
53
|
+
| `oauth.exchange_code(...)` saved the tokens on this machine | **Silent:** pass `persist=True` to keep that |
|
|
54
|
+
| `StreamlitAuth.behind_proxy()` sent the handshake token as it was after expiry (a 401 from the API) | Forward `X-SweatStack-Refresh-Token` from the proxy and the session renews itself. Without it, calls after expiry raise `SweatStackTokenRefreshError` with a reload hint |
|
|
55
|
+
<!-- --8<-- [end:upgrading-0-94] -->
|
|
8
56
|
|
|
9
57
|
## [0.93.0] - 2026-10-09
|
|
10
58
|
|
|
@@ -24,6 +72,17 @@ Issues are only about the data your app uses. Requires the SweatStack API releas
|
|
|
24
72
|
app declares it uses (`list[Capability]`; omit it for the default, `activities` and
|
|
25
73
|
`activity_history`). SweatStack reports issues only for these.
|
|
26
74
|
|
|
75
|
+
<!-- --8<-- [start:upgrading-0-93] -->
|
|
76
|
+
### Upgrading
|
|
77
|
+
|
|
78
|
+
| 0.92 | 0.93 |
|
|
79
|
+
|---|---|
|
|
80
|
+
| `issue.status` is a `CapabilityStatus` | `IssueStatus`: `action_required` or `syncing`. The `.value` strings are unchanged |
|
|
81
|
+
| Branching on `issue.code` being `activity_history_unavailable`, `dailies_unavailable` or `workouts_unavailable` | **Silent:** these codes are never sent. Read `profile.status().capabilities` instead, where `unavailable` means no connected source provides it |
|
|
82
|
+
| Only the known issue codes | Also `activities_blocked_upstream`, `dailies_no_source` and `workouts_no_source`: each `action_required` with a `destination`; handle them like any other issue |
|
|
83
|
+
| An app that uses dailies or workouts | Declare them in the app's `capabilities` (`ApplicationCreate.capabilities`); issues are reported only for declared capabilities |
|
|
84
|
+
<!-- --8<-- [end:upgrading-0-93] -->
|
|
85
|
+
|
|
27
86
|
## [0.92.0] - 2026-10-08
|
|
28
87
|
|
|
29
88
|
Portal links are created with the user's token, when the user acts. Requires the SweatStack API
|
|
@@ -43,12 +102,15 @@ release that replaces `action_url` with `destination` (see the platform changelo
|
|
|
43
102
|
close the page. `return_url` must equal or sit under one of your app's registered redirect
|
|
44
103
|
URIs.
|
|
45
104
|
|
|
105
|
+
<!-- --8<-- [start:upgrading-0-92] -->
|
|
46
106
|
### Upgrading
|
|
47
107
|
|
|
48
|
-
|
|
|
108
|
+
| 0.91 | 0.92 |
|
|
49
109
|
|---|---|
|
|
50
110
|
| `user.issue.action_url` | `client.portal.sessions.create(user.issue.destination, return_url=...).url` |
|
|
51
111
|
| `Client(client_id=..., client_secret=...).portal.sessions.create(...)` | the same call on the client holding the user's token |
|
|
112
|
+
| `portal.sessions.create(destination)` without `return_url` linking back to your app | **Silent:** the Portal tells the user to close the page; pass `return_url=` (under a registered redirect URI) to link back |
|
|
113
|
+
<!-- --8<-- [end:upgrading-0-92] -->
|
|
52
114
|
|
|
53
115
|
## [0.91.0] - 2026-10-08
|
|
54
116
|
|
|
@@ -143,7 +205,8 @@ removed name raises an `AttributeError` that names its replacement.
|
|
|
143
205
|
|
|
144
206
|
Removed names raise an `AttributeError` naming the replacement, so running your code points at
|
|
145
207
|
each change. The module-level functions moved the same way: `sweatstack.get_activities()` is
|
|
146
|
-
`sweatstack.activities.list()`.
|
|
208
|
+
`sweatstack.activities.list()`. Only `from sweatstack import get_activities` fails with Python's
|
|
209
|
+
plain `ImportError`; look the name up in the table.
|
|
147
210
|
|
|
148
211
|
| 0.90 | 0.91 |
|
|
149
212
|
|---|---|
|
|
@@ -294,6 +357,17 @@ and TypeError (a renamed or keyword-only argument). Do not add compatibility shi
|
|
|
294
357
|
- **BREAKING:** `by` on `get_longitudinal_mean_max`, and its `DeprecationWarning`. Every curve is
|
|
295
358
|
duration-oriented.
|
|
296
359
|
|
|
360
|
+
<!-- --8<-- [start:upgrading-0-90] -->
|
|
361
|
+
### Upgrading
|
|
362
|
+
|
|
363
|
+
| 0.89 | 0.90 |
|
|
364
|
+
|---|---|
|
|
365
|
+
| A mean-max frame with one row per intensity: the metric value, and the `duration` it was held | **Silent:** one row per duration, 19 by default: `duration`, the metric (`power` or `speed`) and `start`; the longitudinal curve adds `activity_id`, `sport` and `after`. The column names still exist, so code keeps running with the axes swapped: `df[df.power >= 300]` becomes `df[df.duration == 300]` |
|
|
366
|
+
| The full curve | `durations="all"`, or a list of seconds |
|
|
367
|
+
| `get_activity_mean_max(id, "power", True)` or `segmentation=True` | Drop the argument |
|
|
368
|
+
| `get_longitudinal_mean_max(..., by="duration")` or `by="intensity"` | Drop `by=`; every curve is by duration |
|
|
369
|
+
<!-- --8<-- [end:upgrading-0-90] -->
|
|
370
|
+
|
|
297
371
|
## [0.89.0] - 2026-09-28
|
|
298
372
|
|
|
299
373
|
Frames on your terms. Every method that returns a collection takes `output=`:
|
|
@@ -359,6 +433,7 @@ Four breaking changes come with it; the upgrade is mechanical, see **Upgrading**
|
|
|
359
433
|
previously re-indexed by the client.
|
|
360
434
|
- pandas frames keep the float64 / nanosecond dtype policy. Polars and Arrow do not upcast.
|
|
361
435
|
|
|
436
|
+
<!-- --8<-- [start:upgrading-0-89] -->
|
|
362
437
|
### Upgrading
|
|
363
438
|
|
|
364
439
|
1. Change the install line: `uv add "sweatstack[pandas]"` (or `[polars]`, or `[arrow]` for
|
|
@@ -370,6 +445,10 @@ Four breaking changes come with it; the upgrade is mechanical, see **Upgrading**
|
|
|
370
445
|
4. Search for `.index`, `.loc[<timestamp>]`, `.resample(`, `.plot()` on frames from the
|
|
371
446
|
time-series, mean-max, AWD and dailies methods. Where the index mattered, add
|
|
372
447
|
`.set_index("timestamp")` (or the metric name, or `"date"`) right after the call.
|
|
448
|
+
5. **Silent:** `as_dataframe=True` on `get_activities` and `get_traces` kept nested fields as
|
|
449
|
+
dict columns (`df["summary"][0]["power"]`). `output="pandas"` flattens them to dotted
|
|
450
|
+
columns (`df["summary.power.mean"]`); Polars and Arrow give structs (`df.unnest("summary")`).
|
|
451
|
+
<!-- --8<-- [end:upgrading-0-89] -->
|
|
373
452
|
|
|
374
453
|
## [0.88.0] - 2026-08-06
|
|
375
454
|
|
|
@@ -383,12 +462,30 @@ Four breaking changes come with it; the upgrade is mechanical, see **Upgrading**
|
|
|
383
462
|
|
|
384
463
|
- Token refresh no longer sends a `tz` field. The server derives all timezone information from the offsets stored with each record, so the client has nothing to pass.
|
|
385
464
|
|
|
465
|
+
<!-- --8<-- [start:upgrading-0-88] -->
|
|
466
|
+
### Upgrading
|
|
467
|
+
|
|
468
|
+
| 0.87 | 0.88 |
|
|
469
|
+
|---|---|
|
|
470
|
+
| `create_trace(timestamp=datetime(2026, 1, 1, 10, 0))`, and `start`/`end` on `create_test`, `update_test`, `update_trace` | Aware: `datetime(2026, 1, 1, 10, 0, tzinfo=ZoneInfo("Europe/Amsterdam"))` or `datetime.now(timezone.utc)`. Naive raises `ValueError` |
|
|
471
|
+
| `activity.start`, `trace.timestamp` shown as the athlete's wall-clock time | **Silent:** these are UTC now. Show `start_local`, `end_local` and `timestamp_local`, which are always present |
|
|
472
|
+
<!-- --8<-- [end:upgrading-0-88] -->
|
|
473
|
+
|
|
386
474
|
## [0.87.0] - 2026-06-30
|
|
387
475
|
|
|
388
476
|
### Changed
|
|
389
477
|
|
|
390
478
|
- **Breaking:** the codec previously named NLEC is now AISC (Adaptive Intensity Segmentation Codec), and its parameters are renamed: `nlec_on` is now `segmentation_on` (on `get_activity_data`, `get_latest_activity_data`, `get_longitudinal_data`) and `nlec` is now `segmentation` (on `get_activity_mean_max`, `get_latest_activity_mean_max`). No backwards-compatible aliases.
|
|
391
479
|
|
|
480
|
+
<!-- --8<-- [start:upgrading-0-87] -->
|
|
481
|
+
### Upgrading
|
|
482
|
+
|
|
483
|
+
| 0.86 | 0.87 |
|
|
484
|
+
|---|---|
|
|
485
|
+
| `nlec_on=` on `get_activity_data`, `get_latest_activity_data`, `get_longitudinal_data` | `segmentation_on=` |
|
|
486
|
+
| `nlec=` on `get_activity_mean_max`, `get_latest_activity_mean_max` | `segmentation=` (removed in 0.90) |
|
|
487
|
+
<!-- --8<-- [end:upgrading-0-87] -->
|
|
488
|
+
|
|
392
489
|
|
|
393
490
|
## [0.86.0] - 2026-06-18
|
|
394
491
|
|
|
@@ -396,6 +493,15 @@ Four breaking changes come with it; the upgrade is mechanical, see **Upgrading**
|
|
|
396
493
|
|
|
397
494
|
- **Breaking:** renamed the adaptive-sampling parameters to NLEC (near-lossless effort codec). `adaptive_sampling_on` is now `nlec_on` (on `get_activity_data`, `get_latest_activity_data`, `get_longitudinal_data`) and `adaptive_sampling` is now `nlec` (on `get_activity_mean_max`, `get_latest_activity_mean_max`). No backwards-compatible aliases; requires the SweatStack server 0.107.0 or later.
|
|
398
495
|
|
|
496
|
+
<!-- --8<-- [start:upgrading-0-86] -->
|
|
497
|
+
### Upgrading
|
|
498
|
+
|
|
499
|
+
| 0.85 | 0.86 |
|
|
500
|
+
|---|---|
|
|
501
|
+
| `adaptive_sampling_on=` on `get_activity_data`, `get_latest_activity_data`, `get_longitudinal_data` | `nlec_on=` (`segmentation_on=` from 0.87) |
|
|
502
|
+
| `adaptive_sampling=` on `get_activity_mean_max`, `get_latest_activity_mean_max` | `nlec=` (`segmentation=` from 0.87; removed in 0.90) |
|
|
503
|
+
<!-- --8<-- [end:upgrading-0-86] -->
|
|
504
|
+
|
|
399
505
|
|
|
400
506
|
## [0.85.0] - 2026-06-17
|
|
401
507
|
|
|
@@ -457,8 +563,21 @@ This is a **breaking change** for code that uses `Sport`.
|
|
|
457
563
|
- `open-sport-taxonomy[pydantic]` is now a runtime dependency. Response models consume `sport` via OST's
|
|
458
564
|
permissive `SportField`, so sports newer than the bundled taxonomy are preserved rather than rejected.
|
|
459
565
|
|
|
460
|
-
|
|
461
|
-
|
|
566
|
+
<!-- --8<-- [start:upgrading-0-81] -->
|
|
567
|
+
### Upgrading
|
|
568
|
+
|
|
569
|
+
| 0.80 | 0.81 |
|
|
570
|
+
|---|---|
|
|
571
|
+
| `Sport.cycling_road` and other enum members | `Sport("cycling.road")`; `Sport.parse(value)` for external input |
|
|
572
|
+
| `sport.value` | `str(sport)` (with modifiers) or `sport.code` |
|
|
573
|
+
| `sport.display_name()` | `sport.label` |
|
|
574
|
+
| `sport.parent_sport()` | `sport.parent` |
|
|
575
|
+
| `sport.is_sub_sport_of(x)` | `sport.is_subsport_of(x)` (one `Sport`; `any(...)` for a list) |
|
|
576
|
+
| `sport.root_sport()`, `sport.is_root_sport()` | `Sport(sport.code.split(".")[0])`, `"." not in sport.code and not sport.modifiers` |
|
|
577
|
+
| `for s in Sport` | `for s in Sport.all()` |
|
|
578
|
+
| Hardcoded `cycling.trainer`, `running.treadmill`, `rowing.ergometer`, `cycling.tt`, `cycling.mountainbike`, `cross_country_skiing[.classic\|.skate]`, `unknown` | **Silent** in string comparisons: `cycling+stationary`, `running+stationary`, `rowing+stationary`, `cycling.time_trial`, `cycling.mountain`, `xc_skiing[.classic\|.skate]`, `generic` |
|
|
579
|
+
|
|
580
|
+
**Migration prompt.** Paste this into your coding agent:
|
|
462
581
|
|
|
463
582
|
```text
|
|
464
583
|
Migrate this codebase to sweatstack 0.81.0, which replaces its custom `Sport` enum with the
|
|
@@ -492,6 +611,7 @@ OpenSportTaxonomy type (`open_sport_taxonomy.Sport`). `from sweatstack import Sp
|
|
|
492
611
|
After editing, run the test suite and fix any remaining references. Do not add a compatibility shim;
|
|
493
612
|
migrate call sites to the OST API directly.
|
|
494
613
|
```
|
|
614
|
+
<!-- --8<-- [end:upgrading-0-81] -->
|
|
495
615
|
|
|
496
616
|
|
|
497
617
|
## [0.80.0] - 2026-06-12
|
|
@@ -521,6 +641,17 @@ migrate call sites to the OST API directly.
|
|
|
521
641
|
### Changed
|
|
522
642
|
- Minimum Python version is now declared as `>=3.10` (the code already required 3.10+ syntax; the previous `>=3.9` declaration was incorrect).
|
|
523
643
|
- Exception types in docstrings now reference the typed hierarchy introduced in 0.76.0 (`SweatStackAPIError`, `SweatStackNotFoundError`, `SweatStackAuthError`, `SweatStackBadRequestError`) instead of the now-incorrect `HTTPStatusError`.
|
|
644
|
+
- **Breaking:** with `__all__` in place, the helpers that leaked to the top level by accident are no longer there: `sweatstack.make_dataframe_streamlit_compatible`, `convert_to_standard_dtypes` and `decode_jwt_body` live in `sweatstack.utils`, `DEFAULT_URL` in `sweatstack.constants`.
|
|
645
|
+
|
|
646
|
+
<!-- --8<-- [start:upgrading-0-78] -->
|
|
647
|
+
### Upgrading
|
|
648
|
+
|
|
649
|
+
| 0.77 | 0.78 |
|
|
650
|
+
|---|---|
|
|
651
|
+
| `from sweatstack import make_dataframe_streamlit_compatible` (also `convert_to_standard_dtypes`, `decode_jwt_body`) | `from sweatstack.utils import make_dataframe_streamlit_compatible` |
|
|
652
|
+
| `sweatstack.DEFAULT_URL` | `sweatstack.constants.DEFAULT_URL` |
|
|
653
|
+
| Python 3.9 | Python 3.10 or later |
|
|
654
|
+
<!-- --8<-- [end:upgrading-0-78] -->
|
|
524
655
|
|
|
525
656
|
|
|
526
657
|
## [0.77.1] - 2026-05-19
|
|
@@ -566,6 +697,18 @@ migrate call sites to the OST API directly.
|
|
|
566
697
|
### Removed
|
|
567
698
|
- `httpx` is no longer part of the public error surface. Consumers do not need to import `httpx` to handle errors.
|
|
568
699
|
|
|
700
|
+
<!-- --8<-- [start:upgrading-0-76] -->
|
|
701
|
+
### Upgrading
|
|
702
|
+
|
|
703
|
+
| 0.75 | 0.76 |
|
|
704
|
+
|---|---|
|
|
705
|
+
| `except httpx.HTTPStatusError as e` | `except SweatStackAPIError as e`, or a subclass: `SweatStackNotFoundError` (404), `SweatStackAuthError` (401, 403), `SweatStackRateLimitError` (429), `SweatStackBadRequestError` (other 4xx), `SweatStackServerError` (5xx) |
|
|
706
|
+
| `e.response.status_code`, `e.request.url` | `e.status_code`, `e.url`; also `e.method`, `e.request_id`, `e.body` |
|
|
707
|
+
| `except ValueError` around a call, for a 422 | `except SweatStackBadRequestError` |
|
|
708
|
+
| `except httpx.ConnectError`, `httpx.TimeoutException` | `except SweatStackConnectionError` |
|
|
709
|
+
| `from sweatstack import TokenRefreshError` | `from sweatstack import SweatStackTokenRefreshError` |
|
|
710
|
+
<!-- --8<-- [end:upgrading-0-76] -->
|
|
711
|
+
|
|
569
712
|
|
|
570
713
|
## [0.75.0] - 2026-04-23
|
|
571
714
|
|
|
@@ -600,7 +743,16 @@ migrate call sites to the OST API directly.
|
|
|
600
743
|
- `AttributeError` on Python <3.11 when the API returns an error response (`add_note` is a Python 3.11+ feature).
|
|
601
744
|
|
|
602
745
|
### Removed
|
|
603
|
-
- `SWEATSTACK_LOCAL_CACHE` and `SWEATSTACK_CACHE_DIR` environment variables. Use `sweatstack.enable_cache()` instead.
|
|
746
|
+
- **Breaking:** `SWEATSTACK_LOCAL_CACHE` and `SWEATSTACK_CACHE_DIR` environment variables. Use `sweatstack.enable_cache()` instead.
|
|
747
|
+
|
|
748
|
+
<!-- --8<-- [start:upgrading-0-72] -->
|
|
749
|
+
### Upgrading
|
|
750
|
+
|
|
751
|
+
| 0.71 | 0.72 |
|
|
752
|
+
|---|---|
|
|
753
|
+
| `SWEATSTACK_LOCAL_CACHE=true` in the environment | `sweatstack.enable_cache()` in code. **Silent:** the variable is ignored, so nothing is cached until you call it |
|
|
754
|
+
| `SWEATSTACK_CACHE_DIR=/some/dir` | `sweatstack.enable_cache("/some/dir")` |
|
|
755
|
+
<!-- --8<-- [end:upgrading-0-72] -->
|
|
604
756
|
|
|
605
757
|
|
|
606
758
|
## [0.71.0] - 2026-03-13
|
|
@@ -156,7 +156,9 @@ minimal feature delta. Commit, then handle the remainder separately.
|
|
|
156
156
|
## Releasing
|
|
157
157
|
|
|
158
158
|
1. Bump `version` in `pyproject.toml` (SemVer).
|
|
159
|
-
2. Add a CHANGELOG entry — see [AGENTS.md → CHANGELOG](AGENTS.md#changelog).
|
|
159
|
+
2. Add a CHANGELOG entry — see [AGENTS.md → CHANGELOG](AGENTS.md#changelog). A breaking
|
|
160
|
+
change also needs its `### Upgrading` section
|
|
161
|
+
([AGENTS.md → Breaking changes](AGENTS.md#breaking-changes)).
|
|
160
162
|
3. `gitleaks git .` reports no leaks (`brew install gitleaks`). Known false
|
|
161
163
|
positives go in `.gitleaks.toml`, each with the reason it's safe.
|
|
162
164
|
4. `make publish`: runs `make check`, builds, and uploads with twine.
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sweatstack
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.94.0
|
|
4
4
|
Summary: The official Python client for SweatStack
|
|
5
5
|
Project-URL: Homepage, https://sweatstack.no
|
|
6
6
|
Project-URL: Documentation, https://docs.sweatstack.no/learn/python/
|
|
7
7
|
Project-URL: Repository, https://github.com/SweatStack/sweatstack-python
|
|
8
8
|
Project-URL: Issues, https://github.com/SweatStack/sweatstack-python/issues
|
|
9
9
|
Project-URL: Changelog, https://github.com/SweatStack/sweatstack-python/blob/main/CHANGELOG.md
|
|
10
|
+
Project-URL: Upgrading, https://github.com/SweatStack/sweatstack-python/blob/main/UPGRADING.md
|
|
10
11
|
Author-email: Aart Goossens <aart@sweatstack.no>
|
|
11
12
|
License-Expression: MIT
|
|
12
13
|
License-File: LICENSE
|
|
@@ -97,7 +98,9 @@ npx skills add SweatStack/sweatstack-python
|
|
|
97
98
|
- [Python SDK guide](https://docs.sweatstack.no/learn/python/): authentication,
|
|
98
99
|
clients, data output, errors, Streamlit and FastAPI
|
|
99
100
|
- [API reference](https://docs.sweatstack.no/learn/api-reference/): every endpoint and model
|
|
100
|
-
- [Changelog](https://docs.sweatstack.no/learn/python/changelog/)
|
|
101
|
+
- [Changelog](https://docs.sweatstack.no/learn/python/changelog/)
|
|
102
|
+
- [Upgrading](UPGRADING.md): moving from an older release, from any version, with a prompt for
|
|
103
|
+
your coding agent. Also at [docs.sweatstack.no](https://docs.sweatstack.no/learn/python/upgrading/).
|
|
101
104
|
|
|
102
105
|
Sports follow [OpenSportTaxonomy](https://open-sport-taxonomy.sweatstack.no): `sweatstack.Sport`
|
|
103
106
|
is its `Sport` type.
|
|
@@ -53,7 +53,9 @@ npx skills add SweatStack/sweatstack-python
|
|
|
53
53
|
- [Python SDK guide](https://docs.sweatstack.no/learn/python/): authentication,
|
|
54
54
|
clients, data output, errors, Streamlit and FastAPI
|
|
55
55
|
- [API reference](https://docs.sweatstack.no/learn/api-reference/): every endpoint and model
|
|
56
|
-
- [Changelog](https://docs.sweatstack.no/learn/python/changelog/)
|
|
56
|
+
- [Changelog](https://docs.sweatstack.no/learn/python/changelog/)
|
|
57
|
+
- [Upgrading](UPGRADING.md): moving from an older release, from any version, with a prompt for
|
|
58
|
+
your coding agent. Also at [docs.sweatstack.no](https://docs.sweatstack.no/learn/python/upgrading/).
|
|
57
59
|
|
|
58
60
|
Sports follow [OpenSportTaxonomy](https://open-sport-taxonomy.sweatstack.no): `sweatstack.Sport`
|
|
59
61
|
is its `Sport` type.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Upgrading
|
|
2
|
+
|
|
3
|
+
How to move a project to a newer `sweatstack` release, from any older one. Until 1.0 a minor
|
|
4
|
+
release may contain breaking changes, and every one ships with its upgrade path in the
|
|
5
|
+
[CHANGELOG](CHANGELOG.md): an **Upgrading** section with a table from old to new, and for the
|
|
6
|
+
larger releases a prompt for a coding agent. This page is also at
|
|
7
|
+
https://docs.sweatstack.no/learn/python/upgrading/, with every release's section inline.
|
|
8
|
+
|
|
9
|
+
<!-- --8<-- [start:procedure] -->
|
|
10
|
+
## Procedure
|
|
11
|
+
|
|
12
|
+
1. **Find your version.** `uv pip show sweatstack`, or your lockfile.
|
|
13
|
+
2. **Inventory your SDK usage** before changing anything: every call on a `Client` or on the
|
|
14
|
+
`sweatstack` module (`import sweatstack as ss` is common), `StreamlitAuth` and
|
|
15
|
+
`sweatstack.fastapi`, the model fields you read, the frame columns and indexes you rely on,
|
|
16
|
+
the exceptions you catch, and the install line with its extras.
|
|
17
|
+
3. **Read the Upgrading section of every release** between your version and the target; a
|
|
18
|
+
release without one has nothing to do. Work oldest first: each table uses the names of the
|
|
19
|
+
release before it.
|
|
20
|
+
`adaptive_sampling_on` became `nlec_on` in 0.86 and `segmentation_on` in 0.87, so a name may
|
|
21
|
+
change more than once; follow the chain.
|
|
22
|
+
4. **Change the pin and run your code and tests.** A removed name raises an `AttributeError`
|
|
23
|
+
naming its replacement, a renamed or keyword-only argument raises `TypeError`, and
|
|
24
|
+
`from sweatstack import <removed name>` raises Python's plain `ImportError`. The rows marked
|
|
25
|
+
**Silent** don't fail at all: check each one by hand.
|
|
26
|
+
5. **Don't add compatibility shims.** Move every call site to the new name.
|
|
27
|
+
|
|
28
|
+
The install line changes too: Python 3.10 or later (0.78), the frame library as an extra
|
|
29
|
+
(`sweatstack[pandas]`, `[polars]` or `[arrow]`; `[streamlit]` includes pandas) and pyarrow 20 or
|
|
30
|
+
later (0.84, 0.89), and no `[jupyter]` extra (0.91).
|
|
31
|
+
<!-- --8<-- [end:procedure] -->
|
|
32
|
+
|
|
33
|
+
<!-- --8<-- [start:prompt] -->
|
|
34
|
+
## Prompt for a coding agent
|
|
35
|
+
|
|
36
|
+
Fill in the two versions and paste it into your agent. It works for any distance between them.
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
Upgrade this project from sweatstack <OLD> to sweatstack <NEW>.
|
|
40
|
+
|
|
41
|
+
1. Inventory. Before changing anything, list every use of the SDK: calls on a Client or on the
|
|
42
|
+
sweatstack module (also as "ss"), StreamlitAuth and sweatstack.fastapi, model fields read
|
|
43
|
+
from responses, frame columns and indexes, exceptions caught, and the dependency line with
|
|
44
|
+
its extras.
|
|
45
|
+
2. Plan. Read https://docs.sweatstack.no/learn/python/upgrading/ (the same content is
|
|
46
|
+
UPGRADING.md and the "Upgrading" sections of CHANGELOG.md in
|
|
47
|
+
https://github.com/SweatStack/sweatstack-python). Take every release between <OLD> and <NEW>
|
|
48
|
+
that has an Upgrading section, oldest first, and map each row of its table onto the
|
|
49
|
+
inventory. A name may be renamed more than once across releases; follow the chain to the
|
|
50
|
+
final name.
|
|
51
|
+
3. Apply the changes, oldest release first. Do not add compatibility shims.
|
|
52
|
+
4. Install: uv add "sweatstack[<extras>]==<NEW>". The frame library is an extra ([pandas],
|
|
53
|
+
[polars] or [arrow]); [streamlit] includes pandas.
|
|
54
|
+
5. Run the code and the tests. Fix every AttributeError (its message names the replacement),
|
|
55
|
+
ImportError and TypeError. Then check every row marked "Silent" against the inventory:
|
|
56
|
+
those change behaviour without raising.
|
|
57
|
+
6. For the current API, install the SDK's agent skill (npx skills add
|
|
58
|
+
SweatStack/sweatstack-python) or read https://docs.sweatstack.no/llms.txt.
|
|
59
|
+
|
|
60
|
+
Report what you changed, and every item of the inventory you could not map to a table row.
|
|
61
|
+
```
|
|
62
|
+
<!-- --8<-- [end:prompt] -->
|