Repository navigation
Gradebook fails with UnboundLocalError in BulkRoleCache.prefetch (authz compat roles loop) #514
Description
Description
Opening the Gradebook from the Instructor Dashboard fails with a 500 error. GET on the gradebook API (lms/djangoapps/grades/rest_api/v1/gradebook_views.py) raises UnboundLocalError: cannot access local variable 'compat_role' where it is not associated with a value inside BulkRoleCache.prefetch.
Steps to reproduce
- Use an environment where openedx-authz compat roles are returned for at least one user listed in the gradebook (i.e.
get_authz_compat_course_access_roles_for_user(user)returns a non-empty list). The user has no legacyCourseAccessRoleentries, or the legacy loop didn't run. - Open a course in the Instructor Dashboard and click through to the Gradebook.
- The gradebook API request fails with a 500.
Expected behavior
The Gradebook loads, and BulkRoleCache.prefetch caches the authz compat roles for each user.
Actual behavior
File "/openedx/edx-platform/lms/djangoapps/grades/rest_api/v1/gradebook_views.py", line 667, in get
with bulk_gradebook_view_context(course_key, users):
File "/openedx/edx-platform/lms/djangoapps/grades/rest_api/v1/gradebook_views.py", line 91, in bulk_gradebook_view_context
BulkRoleCache.prefetch(users)
File "/openedx/edx-platform/common/djangoapps/student/roles.py", line 332, in prefetch
user_roles_set_for_course.add(compat_role)
UnboundLocalError: cannot access local variable 'compat_role' where it is not associated with a value
Root cause
In common/djangoapps/student/roles.py, BulkRoleCache.prefetch has a second loop for openedx-authz roles that iterates with role but adds compat_role:
# openedx-authz roles
for user in users:
compat_roles = get_authz_compat_course_access_roles_for_user(user)
for role in compat_roles:
course_id = get_role_cache_key_for_course(role.course_id)
user_roles_set_for_course = roles_by_user[user.id][course_id]
user_roles_set_for_course.add(compat_role) # <-- should be `role`compat_role is only assigned in the preceding legacy loop (CourseAccessRole). So:
- If no legacy roles exist,
compat_roleis unbound and the code raisesUnboundLocalError. - If legacy roles exist, it silently adds the last legacy role instead of the authz role. This is a latent wrong-data bug.
The bug was introduced in #38013 (12a46e6463, AuthZ compatibility layer for course authoring). It is still present on master and on release/verawood. The equivalent code in RoleCache.__init__ is correct.
Proposed fix
Add role instead of compat_role in the authz loop:
user_roles_set_for_course.add(role)Also add a unit test for BulkRoleCache.prefetch with a user that has only authz compat roles, and one with both legacy and authz roles, asserting the cached sets contain the expected roles.
Impact
- The Gradebook is unusable for courses where the listed users have authz compat roles.
- Any other code path that calls
BulkRoleCache.prefetchis affected as well.
Tasks
- Fix
BulkRoleCache.prefetchinmaster. - Add regression tests.
- Backport the fix to
release/verawood.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsReady for Development
Activity
[-]# Gradebook fails with `UnboundLocalError` in `BulkRoleCache.prefetch` (authz compat roles loop)[/-][+]Gradebook fails with `UnboundLocalError` in `BulkRoleCache.prefetch` (authz compat roles loop)[/+]