Optional / possibly-missing dict keys – codebase scan¶
Scanned for obj[key]-style access where the key might be missing (potential KeyError). Grouped by category. Use .get(key, default) or check key in obj before subscript when the key is optional.
Category 1: Movie dict – keys that may be absent¶
Source: get_movie() / get_movie_metadata() / DynamoDB movies table. Movies can be in different states (just created, uploading, processing, zip not built yet, etc.).
Location |
Key |
Risk |
Notes |
|---|---|---|---|
|
|
FIXED |
Now uses |
|
|
Low |
Set at movie create; could be missing only if DB is inconsistent. |
|
|
FIXED |
|
|
|
FIXED |
|
|
|
FIXED |
|
|
|
FIXED |
|
|
|
FIXED |
|
|
|
Medium |
After |
Recommendations: All high-priority movie URN/version fixes above are implemented.
Category 2: User dict – keys that may be absent¶
Source: get_user() / DynamoDB users table. Some attributes are set only when the user is an admin or has a primary course.
Location |
Key |
Risk |
Notes |
|---|---|---|---|
|
|
High |
New user or user without primary course → KeyError when creating a movie. |
|
|
High |
|
|
|
Medium |
|
|
|
Low–Medium |
Schema and add-course logic usually set these; missing only on legacy or partial records. |
Recommendations:
flask_api.api_new_movie: useuser.get('primary_course_id')(orPRIMARY_COURSE_ID) and return 400 if missing (e.g. “Set a primary course before creating a movie”).odb.list_movies: useuser.get(PRIMARY_COURSE_ID)and handle None (e.g. no movies for that user, or fetch by another rule).apikey.py: useuser_dict.get('primary_course_id')and handle None where needed.
Category 3: Course dict – keys from DynamoDB¶
Source: get_course() / DynamoDB courses table. Schema defines expected attributes.
Location |
Key |
Risk |
Notes |
|---|---|---|---|
|
|
Low |
Part of core course schema; only risky if table is corrupted or schema changed. |
No change suggested unless you add stricter typing (e.g. TypedDict).
Category 4: DynamoDB / boto3 response shapes¶
Source: response from query(), scan(), get_item(), etc.
Location |
Key |
Risk |
Notes |
|---|---|---|---|
|
|
Low |
Only used when |
Various |
|
Low |
Standard DynamoDB contract; use |
Various |
|
Low |
boto3 ClientError contract; documented structure. |
Optional hardening: use response.get('Items', []) and response.get('LastEvaluatedKey') where pagination is used.
Category 5: Item/frame from query or batch¶
Source: Items from response['Items'], or from get_movie_frame(), etc.
Location |
Key |
Risk |
Notes |
|---|---|---|---|
|
|
Low |
From movie_frames table; primary key attributes. |
|
|
Low |
Same table, expected attributes. |
|
|
Low |
From api_keys / users tables; key attributes. |
Generally safe; only change if you add optional attributes to those tables.
Category 6: Newly created / in-memory dicts¶
Source: ret = {'error': False}, create_new_movie return value, etc.
Location |
Key |
Risk |
Notes |
|---|---|---|---|
|
|
High (user) |
|
Summary – high‑value fixes¶
Movie (done):
movie_data_urn_for_movie_id,movie_zipfile_urn_for_movie_id,get_movie_data,set_movie_data,purge_movie_zipfile, and Lambdalambda_tracking_env.get_movie_datanow use.get()for URNs andVERSION.User (pending):
flask_apinew-movie path – useuser.get('primary_course_id')and handle None.User (pending):
odb.list_movies– useuser.get(PRIMARY_COURSE_ID)and handle None.User (pending):
apikey– useuser_dict.get('primary_course_id')where appropriate.
Optional: add a TypedDict (or similar) for movie/user so Pyright can warn on missing optional keys when you use [] instead of .get().
Pydantic types for User and Movie – scope evaluation¶
Current state: schema.py already defines Pydantic User and Movie models. They are used for validation on write (put_user, put_movie use User(**user) / Movie(**moviedict)). get_user and get_movie return plain dicts (DynamoDB items, with fix_movie() applied for movies in some code paths).
What “moving to Pydantic” would mean: Have get_user() and get_movie() return User and Movie instances instead of dicts, and type hints accordingly. That would give attribute access, optional-field safety, and better static checking.
Extent of change¶
Area |
Effort |
Notes |
|---|---|---|
Return type of get_movie / get_user |
Small |
In |
fix_user |
Small |
There is |
Call sites using dict subscript |
None |
Pydantic |
Call sites using .get() |
Medium |
Models don’t have |
Lambda / vendored app |
Medium |
Same |
Tests |
Low–medium |
Tests that build dicts and pass to code expecting User/Movie may need to use |
Schema optionality |
Small |
|
Recommendation¶
Short term: Keep returning dicts from
get_movie/get_user; keep validating on write with existing Pydantic models. The optional-key fixes (e.g..get()forprimary_course_id, URNs, version) are the main win and are done or documented.If you move to Pydantic return types:
Add
fix_user()(and optionallyfix_course()) for DynamoDB payloads.Change
get_movie/get_userto returnMovie/User, withValidationErrorhandling.Replace every
movie.get(KEY, default)/user.get(KEY, default)with a single pattern: e.g.getattr(m, KEY, default)or a helper so Pyright and readers see one convention. Expect on the order of 20–30 call sites across app and lambda.Make
User.primary_course_idoptional (str \| None) if that reflects real data; then handle None in the few places that use it.
Overall: moderate effort (roughly half a day to a day of focused refactor plus tests), with the main cost being the .get() call sites and keeping lambda/vendored code in sync.