Skip to content

description: chiptime Python API entry points: parse, iter_messages, iter_frames, repair, validate — one call, then navigate plain data.

Python API — core

Five entry points, one mental model: make one call, then navigate the result (the object tree). There are no sessions to open, no configuration objects, no state to manage — every function is input → complete result.

Which entry point?

You want Call
The workout, fully interpreted parse — the main call, 99% of uses
To stream a huge file in constant memory iter_messages
Wire-level bytes forensics iter_frames
To know why a platform refuses a file doctor
A broken file made uploadable repair
Metadata changed (sport, device, clock) edit
An activity cropped, totals rebuilt trim
To know what a file discloses reveal
Personal data removed before sharing scrub
"Will this platform accept it?" validate

parse reads everything into a ParseResult; the iterators yield as they go and skip the semantic layer entirely. repair and validate are built on parse — they see exactly what it sees.

Parse

chiptime.parse

parse(
    src: Source,
    *,
    mode: Mode = "lenient",
    strip_pii: bool = False,
    include_unknown: bool = True,
    include_raw: bool = False,
) -> ParseResult

Parse a FIT source. lenient (default) recovers and annotates; strict raises the first FitError; forensic maximizes salvage and never drops.

Stream without the semantic layer

chiptime.iter_messages

iter_messages(
    src: Source, *, mode: Mode = "lenient"
) -> Iterator[Message]

Profile-applied message stream without building the semantic model.

chiptime.iter_frames

iter_frames(
    src: Source, *, mode: Mode = "lenient"
) -> Iterator[FrameEvent]

Lossless wire-level frame events (forensics layer).

Doctor

chiptime.doctor

Why won't this file upload, and what should I run? (F29)

The most persistent unanswered question in the FIT world is not "is my file broken" — it is "I fixed it and the platform still refuses it, and nothing tells me why." chiptime already knows the answer: it validates against platform profiles and it has verbs that repair and edit. What was missing is the join — a single command that reads a stubborn file and prints what is wrong, who cares, and the exact command that fixes it.

That is the "errors are written for agents" contract (code + sentence + suggested flag) applied to a whole file, for humans.

Remedy dataclass

A concrete next step, not a hint.

Attributes:

Name Type Description
command str

The command to run, ready to paste.

reason str

Why this fixes the findings it covers.

codes tuple[str, ...]

The finding codes this remedy resolves.

priority int

Lower runs first (structural repair before cosmetics).

Diagnosis dataclass

What a platform will make of this file, and what to do about it.

Attributes:

Name Type Description
platform str

The profile the verdict is against.

will_upload bool

True when nothing blocking was found.

blocking list[Finding]

Findings that will cause rejection.

advisory list[Finding]

Findings worth knowing that should not block.

remedies list[Remedy]

Ordered, deduplicated next steps.

unresolved list[Finding]

Blocking findings with no known automatic fix.

summary str

One-line description of the parse itself.

doctor

doctor(
    src: Source,
    *,
    platform: Platform = "garmin-connect",
    mode: Mode = "lenient",
) -> Diagnosis

Diagnose why a platform will refuse a file, and prescribe the fix.

Parameters:

Name Type Description Default
src Source

Path, bytes, or binary file object.

required
platform Platform

Which platform's observed rules to judge against.

'garmin-connect'
mode Mode

Parse policy for reading the input.

'lenient'

Returns:

Type Description
Diagnosis

Diagnosis with blocking findings, advisory findings, ordered

Diagnosis

remedies, and any blocking finding for which chiptime has no

Diagnosis

automatic fix (named honestly rather than papered over).

chiptime.doctor.Diagnosis dataclass

What a platform will make of this file, and what to do about it.

Attributes:

Name Type Description
platform str

The profile the verdict is against.

will_upload bool

True when nothing blocking was found.

blocking list[Finding]

Findings that will cause rejection.

advisory list[Finding]

Findings worth knowing that should not block.

remedies list[Remedy]

Ordered, deduplicated next steps.

unresolved list[Finding]

Blocking findings with no known automatic fix.

summary str

One-line description of the parse itself.

chiptime.doctor.Remedy dataclass

A concrete next step, not a hint.

Attributes:

Name Type Description
command str

The command to run, ready to paste.

reason str

Why this fixes the findings it covers.

codes tuple[str, ...]

The finding codes this remedy resolves.

priority int

Lower runs first (structural repair before cosmetics).

Repair

chiptime.repair

Repair: salvage → synthesize missing structure → valid canonical .fit.

Every synthesis lands in provenance (REPAIR_*). Genuinely absent data is refused, never fabricated (taxonomy #16, contract #8).

RepairResult dataclass

A repaired file plus the proof of what repair did.

Attributes:

Name Type Description
data bytes

The complete, valid .fit bytes — write them to disk as-is.

provenance list[ProvenanceEntry]

Every salvaged, synthesized, and dropped element.

output_strict_ok bool

Self-check — the output re-parsed in strict mode.

parse_result ParseResult | None

The salvage parse of the input, for inspection.

Edit

chiptime.edit

User-directed metadata edits with a validated round-trip (F26).

The distinction that governs this module (PRD §5): chiptime never infers intent and never mutates a file on its own — but when the user names an edit explicitly, it is performed, recorded in provenance[], and the result is re-parsed in strict mode to prove the file is still sound.

Metadata only: this module never touches a measurement.

EditError

Bases: FitError

A requested edit cannot be performed; no bytes are written.

EditResult dataclass

The edited file plus proof of what changed.

Attributes:

Name Type Description
data bytes

The edited .fit bytes — write them to disk as-is.

provenance list[ProvenanceEntry]

One entry per edit performed, with before/after values.

warnings list[Diagnostic]

Non-fatal observations (e.g. a sport/sub-sport pair worth a second look). chiptime flags; it does not silently fix.

output_strict_ok bool

Self-check — the output re-parsed in strict mode.

parse_result ParseResult | None

The parse of the input, for inspection.

edit

edit(
    src: Source,
    *,
    sport: str | int | None = None,
    sub_sport: str | int | None = None,
    manufacturer: str | int | None = None,
    product: int | None = None,
    time_shift_s: int | None = None,
    total_distance_m: float | None = None,
    mode: Mode = "lenient",
) -> EditResult

Change what a file says about itself, then prove it still parses.

Only the named edits are applied; every other message, field, developer field, and unknown value round-trips untouched. Each edit is recorded in provenance[], and the output is re-parsed in strict mode (output_strict_ok).

Parameters:

Name Type Description Default
src Source

Path, bytes, or binary file object.

required
sport str | int | None

New sport, by profile name ("running") or raw number.

None
sub_sport str | int | None

New sub-sport; never inferred from sport.

None
manufacturer str | int | None

New recording-device manufacturer, name or number.

None
product int | None

New product id (numeric — products are vendor-specific).

None
time_shift_s int | None

Signed seconds added to every profile-typed timestamp.

None
total_distance_m float | None

Set the activity's true distance (treadmill calibration). Records and speed are scaled by the same factor so the stream and the summaries still agree.

None
mode Mode

Parse policy for reading the input. strict refuses to edit a file that does not parse strictly — editing implies you believe the file is sound; use repair first if it is not.

'lenient'

Returns:

Type Description
EditResult

EditResult with the new bytes, provenance, warnings, and the

EditResult

strict-mode self-check verdict.

Raises:

Type Description
EditError

no edit was requested, an enum name is unknown, or a time shift would leave the representable range. No bytes are written in any of these cases.

chiptime.edit.EditResult dataclass

The edited file plus proof of what changed.

Attributes:

Name Type Description
data bytes

The edited .fit bytes — write them to disk as-is.

provenance list[ProvenanceEntry]

One entry per edit performed, with before/after values.

warnings list[Diagnostic]

Non-fatal observations (e.g. a sport/sub-sport pair worth a second look). chiptime flags; it does not silently fix.

output_strict_ok bool

Self-check — the output re-parsed in strict mode.

parse_result ParseResult | None

The parse of the input, for inspection.

Trim

chiptime.trim

Crop an activity without letting the file lie about itself (F27).

Removing records is easy; the hard part is that every number computed from those records — session totals, activity totals, averages — is wrong the moment they disappear. A trimmed file with stale totals is worse than an untrimmed one, because the error is invisible and travels downstream forever.

So trimming here is two acts: filter the records, then rebuild everything that depended on them, using the same semantic layer that computes totals during a normal parse. There is no second implementation of totals arithmetic to drift.

TrimError

Bases: FitError

A trim cannot be performed; no bytes are written.

TrimResult dataclass

The cropped file plus an account of what was removed and rebuilt.

Attributes:

Name Type Description
data bytes

The trimmed .fit bytes.

provenance list[ProvenanceEntry]

What was dropped and what was rebuilt, with counts.

records_kept int

Records inside the keep-window.

records_dropped int

Records removed by the trim.

warnings list[Diagnostic]

Non-fatal observations carried from the rebuild.

output_strict_ok bool

Self-check — the output re-parsed in strict mode.

parse_result ParseResult | None

The parse of the input, for inspection.

trim

trim(
    src: Source,
    *,
    after: datetime | str | int | None = None,
    before: datetime | str | int | None = None,
    mode: Mode = "lenient",
) -> TrimResult

Crop an activity to a time window and rebuild every derived number.

Parameters:

Name Type Description Default
src Source

Path, bytes, or binary file object.

required
after datetime | str | int | None

Keep records at or after this bound. Absolute datetime / ISO string, or relative: "+5m" = five minutes after the start (i.e. cut the first five minutes).

None
before datetime | str | int | None

Keep records at or before this bound. "-10m" = ten minutes before the end (i.e. cut the last ten minutes).

None
mode Mode

Parse policy for reading the input.

'lenient'

Returns:

Type Description
TrimResult

TrimResult with the cropped bytes, provenance, kept/dropped counts,

TrimResult

and the strict-mode self-check verdict.

Raises:

Type Description
TrimError

no bound given, a bound cannot be interpreted, the file has no records, or the window keeps nothing. No bytes are written.

chiptime.trim.TrimResult dataclass

The cropped file plus an account of what was removed and rebuilt.

Attributes:

Name Type Description
data bytes

The trimmed .fit bytes.

provenance list[ProvenanceEntry]

What was dropped and what was rebuilt, with counts.

records_kept int

Records inside the keep-window.

records_dropped int

Records removed by the trim.

warnings list[Diagnostic]

Non-fatal observations carried from the rebuild.

output_strict_ok bool

Self-check — the output re-parsed in strict mode.

parse_result ParseResult | None

The parse of the input, for inspection.

Privacy

chiptime.reveal

reveal(
    src: Source, *, mode: Mode = "lenient"
) -> PrivacyReport

Report what a file discloses about you. Reads only; writes nothing.

Parameters:

Name Type Description Default
src Source

Path, bytes, or binary file object.

required
mode Mode

Parse policy for reading the input.

'lenient'

Returns:

Type Description
PrivacyReport

PrivacyReport. Coordinates are rounded to ~1.1 km so the report

PrivacyReport

itself is safe to share — which is the whole point of having one.

chiptime.scrub

scrub(
    src: Source,
    *,
    identity: bool = True,
    serials: bool = True,
    body_metrics: bool = True,
    gps_radius_m: float | None = None,
    drop_all_gps: bool = False,
    mode: Mode = "lenient",
) -> ScrubResult

Remove personal data and write a file that still parses and uploads.

Metadata categories are on by default because removing them costs no measurements. Location scrubbing is opt-in and explicit, because it does.

Parameters:

Name Type Description Default
src Source

Path, bytes, or binary file object.

required
identity bool

Drop user_profile and identity fields.

True
serials bool

Null device serial numbers and ANT device ids. Note that platforms are reported to use file_id.serial_number when deciding whether an activity counts toward challenges and badges — keep them if you intend to re-upload.

True
body_metrics bool

Drop zones_target and physiology fields (FTP, max HR, VO2max…).

True
gps_radius_m float | None

Conceal every GPS point within this many metres of the route's first or last fix — wherever it occurs in the ride, so a loop that passes home mid-route is covered too.

None
drop_all_gps bool

Remove every coordinate outright.

False
mode Mode

Parse policy for reading the input.

'lenient'

Returns:

Type Description
ScrubResult

ScrubResult with the scrubbed bytes, provenance, per-category

ScrubResult

counts, and the strict-mode self-check verdict.

Raises:

Type Description
ScrubError

nothing was selected to remove.

chiptime.privacy.PrivacyReport dataclass

What a file discloses, by category.

Coordinates are deliberately coarse (see COARSE_DECIMALS).

Attributes:

Name Type Description
findings list[PrivacyFinding]

One entry per disclosing message/field, with counts.

positions_present int

Records carrying GPS coordinates.

start_coarse tuple[float, float] | None

Approximate start coordinate, rounded, or None.

end_coarse tuple[float, float] | None

Approximate end coordinate, rounded, or None.

clean_categories list[str]

Categories this file does not disclose at all.

chiptime.privacy.ScrubResult dataclass

The scrubbed file plus an account of what was removed.

Attributes:

Name Type Description
data bytes

The scrubbed .fit bytes.

provenance list[ProvenanceEntry]

One entry per category removed, with counts.

warnings list[Diagnostic]

Non-fatal observations (e.g. every position was concealed).

removed dict[str, int]

Count of removals per category key.

output_strict_ok bool

Self-check — the output re-parsed in strict mode.

parse_result ParseResult | None

The parse of the input, for inspection.

Validate

chiptime.validate.validate

validate(
    src: Source, platform: Platform = "strict-spec"
) -> list[Finding]