Skip to content

chore(lambda): enable strict mypy and annotate the codebase - #571

Merged
layertwo merged 1 commit into
mainlinefrom
deps-mypy
Sep 18, 2026
Merged

layertwo merged 1 commit into
mainlinefrom
deps-mypy

Conversation

@layertwo

@layertwo layertwo commented Sep 17, 2026

Copy link
Copy Markdown
Owner

Turns on disallow_untyped_defs / check_untyped_defs and makes them pass: mypy goes from 1376 errors to 0 across 150 files.

What's here

  • ~1300 annotations across src/ and tests/.
  • Route handlers take APIGatewayProxyEvent instead of Any. Tests now construct real event objects, which is what powertools passes at runtime — so this makes the tests more faithful, not just more typed. A typo like event.bodyy is now a mypy error; under Any it was silent.
  • Real types-boto3 service stubs (dynamodb, kms, apigatewaymanagementapi). Worth knowing for review: without them table: "Table" resolved to Any and checked nothing (reveal_type confirmed it). Installing them surfaced 85 previously-invisible errors, fixed with casts at the DynamoDB extraction boundary — item values genuinely are a 13-member union, so the cast keeps values dynamic while the Table API stays checked.
  • ignore_missing_imports scoped to mohawk rather than global. That global setting is exactly what made the annotation above vacuous. Measured cost of dropping it: 7 errors, all mohawk, which ships no stubs.
  • warn_unused_ignores added — it caught 3 already-rotted suppressions on its first run.

Suppressions: 65 → 4. Remaining Any: 9, all genuine (*args/**kwargs variadics, a WebSocket event dict, two Response[Any] helper signatures).

Cleanup this exposed

  • json_body() / header() test helpers replace 145 hand-rolled response-body decodes and delete 56 # type: ignore[arg-type].
  • BaseRoute.hawk_uid() / unauthorized() replace a byte-identical 8-line block in 14 routes.
  • SyncStorageException.default_message deletes 27 identical __init__ overrides. This also closes a real hole: **kwargs: Any on those subclasses erased the base's precise signature, so mypy accepted ValidationException("x", bogus=1) — which raises TypeError at runtime.
  • Deleted 4 dead fixtures and 6 tests that asserted CPython's abc semantics rather than this codebase. base_route.py still reports 100% coverage without them, which is the evidence they weren't testing it.

Behavior changes — 2, flagged for review

Both make sibling routes agree:

  1. Empty-body PUT to a collection returns 400 instead of 500. json.loads(None) raised TypeError, which that handler's except (json.JSONDecodeError, KeyError) didn't catch, so it fell through to except Exception. The BSO route already returned 400 for the same input.
  2. bso/read.py matches request headers case-insensitively, like the other 10 routes — it was the only one reading event.get("headers") instead of the powertools property.

Verification

mypy clean (150 files) · black / isort / flake8 clean · 958 tests pass at 100% coverage.

Test count is 958 rather than 964 because of the 6 deleted abc-semantics tests; nothing else was removed or skipped.

@github-actions

github-actions Bot commented Sep 17, 2026

Copy link
Copy Markdown

Diff for stage: DefaultStage

Warning

3 Destructive Changes

No Changes for stack: GitHubOidcStack ✅

Diff for stack: Service-prod - 3 to add, 3 to update, 3 to destroy

Details

[!WARNING]
Destructive Changes ‼️
Stack: Service-prod - Resource: AuthApiDeploymentB62B2E46bda7a3ee526845c6f831f98a8023195f - Impact: WILL_DESTROY

Stack: Service-prod - Resource: TokenApiDeploymentB896C219ee4433fd7a430b8eb183e22b78e32990 - Impact: WILL_DESTROY

Stack: Service-prod - Resource: ProfileApiDeployment84A54415750bd55753532dd2ebecd42716cf4155 - Impact: WILL_DESTROY

Resources
[-] AWS::ApiGateway::Deployment AuthApiDeploymentB62B2E46bda7a3ee526845c6f831f98a8023195f destroy
[-] AWS::ApiGateway::Deployment TokenApiDeploymentB896C219ee4433fd7a430b8eb183e22b78e32990 destroy
[-] AWS::ApiGateway::Deployment ProfileApiDeployment84A54415750bd55753532dd2ebecd42716cf4155 destroy
[+] AWS::ApiGateway::Deployment AuthApiDeploymentB62B2E46ac99c3a69dd45f19958cb37bb317091a
[+] AWS::ApiGateway::Deployment TokenApiDeploymentB896C21940d30031daf269dad58a2f6f3cd50e46
[+] AWS::ApiGateway::Deployment ProfileApiDeployment84A54415225de1b126ba8d19e1f58d99b83b22e1
[~] AWS::Lambda::Function AuthApiHandlerED50ACFA
 ├─ [~] Code
 │   └─ [~] .S3Key:
 │       ├─ [-] 51c4ffab6683e2fc0476f48392bf2435c9e2addfbc5604e0c492f637828589fa.zip
 │       └─ [+] 0591ee89c558a8ed5fa8f88e4083c55df9860b74fe3dd7ce90ee792698d3a55f.zip
 └─ [~] Metadata
     └─ [~] .aws:asset:path:
         ├─ [-] asset.51c4ffab6683e2fc0476f48392bf2435c9e2addfbc5604e0c492f637828589fa
         └─ [+] asset.0591ee89c558a8ed5fa8f88e4083c55df9860b74fe3dd7ce90ee792698d3a55f
[~] AWS::Lambda::Function TokenApiHandler2E66DB25
 ├─ [~] Code
 │   └─ [~] .S3Key:
 │       ├─ [-] 51c4ffab6683e2fc0476f48392bf2435c9e2addfbc5604e0c492f637828589fa.zip
 │       └─ [+] 0591ee89c558a8ed5fa8f88e4083c55df9860b74fe3dd7ce90ee792698d3a55f.zip
 └─ [~] Metadata
     └─ [~] .aws:asset:path:
         ├─ [-] asset.51c4ffab6683e2fc0476f48392bf2435c9e2addfbc5604e0c492f637828589fa
         └─ [+] asset.0591ee89c558a8ed5fa8f88e4083c55df9860b74fe3dd7ce90ee792698d3a55f
[~] AWS::Lambda::Function ProfileApiHandler9B65A298
 ├─ [~] Code
 │   └─ [~] .S3Key:
 │       ├─ [-] 51c4ffab6683e2fc0476f48392bf2435c9e2addfbc5604e0c492f637828589fa.zip
 │       └─ [+] 0591ee89c558a8ed5fa8f88e4083c55df9860b74fe3dd7ce90ee792698d3a55f.zip
 └─ [~] Metadata
     └─ [~] .aws:asset:path:
         ├─ [-] asset.51c4ffab6683e2fc0476f48392bf2435c9e2addfbc5604e0c492f637828589fa
         └─ [+] asset.0591ee89c558a8ed5fa8f88e4083c55df9860b74fe3dd7ce90ee792698d3a55f
[~] AWS::Lambda::Function ApiHandler5E7490E8
 ├─ [~] Code
 │   └─ [~] .S3Key:
 │       ├─ [-] 51c4ffab6683e2fc0476f48392bf2435c9e2addfbc5604e0c492f637828589fa.zip
 │       └─ [+] 0591ee89c558a8ed5fa8f88e4083c55df9860b74fe3dd7ce90ee792698d3a55f.zip
 └─ [~] Metadata
     └─ [~] .aws:asset:path:
         ├─ [-] asset.51c4ffab6683e2fc0476f48392bf2435c9e2addfbc5604e0c492f637828589fa
         └─ [+] asset.0591ee89c558a8ed5fa8f88e4083c55df9860b74fe3dd7ce90ee792698d3a55f
[~] AWS::ApiGateway::Stage AuthApiDeploymentStageprodB0E4172A
 └─ [~] DeploymentId
     └─ [~] .Ref:
         ├─ [-] AuthApiDeploymentB62B2E46bda7a3ee526845c6f831f98a8023195f
         └─ [+] AuthApiDeploymentB62B2E46ac99c3a69dd45f19958cb37bb317091a
[~] AWS::ApiGateway::Stage TokenApiDeploymentStageprod11035AE4
 └─ [~] DeploymentId
     └─ [~] .Ref:
         ├─ [-] TokenApiDeploymentB896C219ee4433fd7a430b8eb183e22b78e32990
         └─ [+] TokenApiDeploymentB896C21940d30031daf269dad58a2f6f3cd50e46
[~] AWS::ApiGateway::Stage ProfileApiDeploymentStageprodF609D968
 └─ [~] DeploymentId
     └─ [~] .Ref:
         ├─ [-] ProfileApiDeployment84A54415750bd55753532dd2ebecd42716cf4155
         └─ [+] ProfileApiDeployment84A54415225de1b126ba8d19e1f58d99b83b22e1
[~] AWS::Lambda::Function ChannelApiHandler02759D57
 ├─ [~] Code
 │   └─ [~] .S3Key:
 │       ├─ [-] 51c4ffab6683e2fc0476f48392bf2435c9e2addfbc5604e0c492f637828589fa.zip
 │       └─ [+] 0591ee89c558a8ed5fa8f88e4083c55df9860b74fe3dd7ce90ee792698d3a55f.zip
 └─ [~] Metadata
     └─ [~] .aws:asset:path:
         ├─ [-] asset.51c4ffab6683e2fc0476f48392bf2435c9e2addfbc5604e0c492f637828589fa
         └─ [+] asset.0591ee89c558a8ed5fa8f88e4083c55df9860b74fe3dd7ce90ee792698d3a55f

No Changes for stack: Frontend-prod ✅
No Changes for stack: Monitoring-prod ✅

Generated for commit ba5e26e at 2026-09-18T05:53:17.413Z

Turns on disallow_untyped_defs/check_untyped_defs and makes them pass.

- annotate ~1300 functions across src/ and tests/
- type route handlers as APIGatewayProxyEvent rather than Any; tests now pass
  real event objects, which is what powertools passes at runtime
- add types-boto3 service stubs (dynamodb, kms, apigatewaymanagementapi) and
  cast at DynamoDB extraction boundaries, where item values are genuinely dynamic
- scope ignore_missing_imports to mohawk instead of applying it globally, so a
  missing stub package surfaces as an error rather than a silent Any
- add warn_unused_ignores; suppressions are down to 4 from 65

Cleanup this exposed:

- shared json_body/header test helpers replace 145 hand-rolled body decodes
- BaseRoute.hawk_uid/unauthorized replace a byte-identical block in 14 routes
- SyncStorageException gains a default_message class attribute, deleting 27
  identical __init__ overrides; this also closes a hole where **kwargs: Any let
  mypy accept bogus keyword arguments that then failed at runtime
- delete dead fixtures, and 6 tests that asserted CPython abc semantics

Two behavior changes, both making sibling routes agree:

- empty-body PUT to a collection returns 400 instead of 500
- bso/read.py matches request headers case-insensitively, like every other route
@layertwo layertwo changed the title chore: update deps and set mypy options chore(lambda): enable strict mypy and annotate the codebase Sep 18, 2026
@github-actions

Copy link
Copy Markdown

Coverage report

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  lambda/src/environment
  service_provider.py
  lambda/src/routes/auth
  account_create.py
  oauth_token.py
  oidc_exchange.py
  session_destroy.py
  lambda/src/routes/bso
  delete.py
  read.py
  update.py
  lambda/src/routes/collections
  create.py
  delete.py
  list.py
  read.py
  update.py
  lambda/src/routes/info
  read_collections.py
  read_counts.py
  read_quota.py
  read_usage.py
  lambda/src/routes/storage
  delete_all.py
  delete_root.py
  lambda/src/routes/token
  request.py
  lambda/src/services
  auth_account_manager.py
  channel_service.py
  fxa_token_manager.py
  hawk_service.py
  lambda/src/shared
  exceptions.py
Project Total  

The report is truncated to 25 files out of 50. To see the full report, please visit the workflow summary page.

This report was generated by python-coverage-comment-action

@layertwo
layertwo merged commit 22a7d3b into mainline Sep 18, 2026
10 checks passed
@layertwo
layertwo deleted the deps-mypy branch September 18, 2026 05:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant