Skip to content

Upgrading

How to update your code for each release with breaking changes, newest first. Every release's full list of changes is in the changelog.

A name the SDK removed raises an AttributeError that names its replacement, so running your code points at each change. To have a coding agent do the upgrade, give it the migration prompt in the release's section.

0.91

Upgrading

Removed names raise an AttributeError naming the replacement, so running your code points at each change. The module-level functions moved the same way: sweatstack.get_activities() is sweatstack.activities.list().

0.90 0.91
get_activities client.activities.list()
get_activity client.activities.retrieve(activity_id)
get_latest_activity client.activities.latest()
get_activity_data client.activities.data(activity_id)
get_activity_mean_max client.activities.mean_max(activity_id, metric=...)
get_activity_awd client.activities.awd(activity_id)
get_latest_activity_data client.activities.data(client.activities.latest().id)
get_latest_activity_mean_max client.activities.mean_max(client.activities.latest().id, metric=...)
get_longitudinal_data client.activities.longitudinal.data(...)
get_longitudinal_mean_max client.activities.longitudinal.mean_max(...)
get_longitudinal_awd client.activities.longitudinal.awd(...)
upload client.activities.upload(files)
get_backfill_status client.activities.backfill_status()
watch_backfill_status client.activities.watch_backfill_status()
set_activity_app_metadata client.activities.app_metadata.set(activity_id, data=...)
delete_activity_app_metadata client.activities.app_metadata.delete(activity_id)
get_traces client.traces.list()
create_trace client.traces.create(...)
update_trace client.traces.replace(trace_id, ...)
delete_trace client.traces.delete(trace_id)
set_trace_app_metadata client.traces.app_metadata.set(trace_id, data=...)
delete_trace_app_metadata client.traces.app_metadata.delete(trace_id)
get_tests client.tests.list()
get_test client.tests.retrieve(test_id)
create_test client.tests.create(...)
update_test client.tests.replace(test_id, ...)
delete_test client.tests.delete(test_id)
set_test_app_metadata client.tests.app_metadata.set(test_id, data=...)
delete_test_app_metadata client.tests.app_metadata.delete(test_id)
get_dailies client.dailies.list(measure, start=..., end=...)
set_daily client.dailies.set(measure, date=..., value=...)
delete_daily client.dailies.delete(measure, date=...)
get_profile_status client.profile.status()
get_sports client.profile.sports()
get_tags client.profile.tags()
set_user_app_metadata client.profile.app_metadata.set(data=...)
delete_user_app_metadata client.profile.app_metadata.delete()
get_users client.users.list()
get_user client.users.list(name=...): every match, so check the length
create_user client.users.create(first_name=...)
get_teams client.teams.list()
get_team_users client.teams.users(team_id)
get_team_user client.teams.users(team_id, name=...): every match
get_authorized_teams client.teams.authorized()
authorize_team client.teams.authorize(team_id)
create_portal_session client.portal.sessions.create(destination)
get_userinfo client.oauth.userinfo()
get_authorization_url client.oauth.authorization_url(...)
exchange_code_for_token client.oauth.exchange_code(...)
generate_pkce_params client.oauth.generate_pkce_params()
switch_user client.delegated_client(user): a new client; this one is left unchanged
switch_back keep the original client, or client.principal_client()
client.jwt client.api_key

Changes that a rename alone doesn't cover. Most raise a TypeError naming the argument; the ones marked silent don't fail loudly, so check for them.

0.90 0.91
sports=["cycling"] (every filter, and StreamlitAuth.select_activity) sport="cycling" or sport=["cycling", "running"]
get_activity_mean_max(id, "power"), get_activity_awd(id, "power") activities.mean_max(id, metric="power"), activities.awd(id, metric="power")
get_activity_data(id, "power", ["power"]) activities.data(id, segmentation_on="power", metrics=["power"])
create_user("Carla", "Smith") users.create(first_name="Carla", last_name="Smith")
get_sports(True), authorize_team(id, scopes) profile.sports(only_root=True), teams.authorize(id, scopes=scopes)
Positional arguments to get_authorization_url() / exchange_code_for_token() Keywords: oauth.authorization_url(client_id=..., ...)
get_latest_activity(start=, end=, tag=) activities.latest(sport=); for the rest, activities.list(start=..., end=..., tags=..., limit=1)
get_latest_activity() raising StopIteration when there is none Silent: activities.latest() returns None; check before using .id
get_longitudinal_*(date=..., window_days=...) start= and end=
get_longitudinal_*(...) without sport sport= is required (the API already rejected a request without one)
upload(files, sport=...) activities.upload(files): no sport=; a CSV needs a sport column
upload(...) returning a dict Silent: activities.upload(...) returns list[SourceResponse]; read .status / .error
exchange_code_for_token(..., redirect_uri=...) oauth.exchange_code(...) without redirect_uri=
get_user("Carla") returning one UserSummary Silent: users.list(name="Carla") returns a list, every match; search_mode= is gone
switch_user("Carla") (a name) client.delegated_client(users.list(name="Carla")[0]): a name is not accepted
client.jwt client.api_key
sweatlab, sweatshell, pip install "sweatstack[jupyter]" uv add "sweatstack[pandas]" jupyterlab; sweatstack.authenticate() in the first cell

New defaults that change behaviour without changing code: GET, PUT and DELETE requests are retried (Client(max_retries=0) restores single attempts), and requests time out after 60 s (Client(timeout=...)).

Migration prompt. Paste this into your coding agent:

Upgrade this codebase to sweatstack 0.91, which moved every method to a resource namespace.
The full upgrade guide, with both tables, is the "Upgrading" section of the 0.91.0 entry in
https://github.com/SweatStack/sweatstack-python/blob/main/CHANGELOG.md
(also at https://docs.sweatstack.no/learn/python/upgrading/). Read it first.

Find every use of the sweatstack client (a Client instance, the sweatstack module, and
StreamlitAuth or FastAPI user clients) and:

1. Rename calls per the first table, e.g. get_activities() -> activities.list(),
   get_activity_data(id) -> activities.data(id),
   get_longitudinal_mean_max(...) -> activities.longitudinal.mean_max(...),
   update_trace(id, ...) -> traces.replace(id, ...), get_users() -> users.list().
2. Apply every row of the second table. In particular:
   - sports= -> sport= (one value or a list).
   - Pass by keyword: metric= (mean_max, awd), segmentation_on= and metrics= (data),
     first_name= and last_name= (users.create), only_root= (profile.sports),
     scopes= (teams.authorize), and all arguments of oauth.authorization_url() and
     oauth.exchange_code().
   - Longitudinal methods: sport= is required, and date=/window_days= become start=/end=
     (datetime.date objects).
   - activities.latest() takes only sport= and returns None when there is no activity:
     handle None.
   - activities.upload() takes no sport= (a CSV needs a sport column) and returns
     list[SourceResponse], not a dict.
   - oauth.exchange_code() takes no redirect_uri=.
   - get_user(x) -> users.list(name=x), which returns a list of every match.
   - switch_user(user) -> athlete = client.delegated_client(user_id_or_summary); use that
     new client for the athlete's calls. It doesn't accept a name: look the user up first.
     switch_back() -> keep using the original client.
   - client.jwt -> client.api_key.
3. Replace get_latest_activity_data(...) with activities.data(activities.latest().id, ...),
   after checking that latest() returned an activity.
4. If the project used sweatlab, sweatshell or the [jupyter] extra, depend on
   "sweatstack[pandas]" (or [polars]) and jupyterlab instead.

Then run the code and the tests. Fix any AttributeError (its message names the replacement)
and TypeError (a renamed or keyword-only argument). Do not add compatibility shims.