Skip to content

Commit

Permalink
feat!: Assume that scope IDs are opaque key objects (WIP)
Browse files Browse the repository at this point in the history
TODO: Bumps XBlock version to 6.0.0

Part of: openedx#708
  • Loading branch information
kdmccormick committed Jan 7, 2025
1 parent 306889d commit e7ed235
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 1 deletion.
1 change: 1 addition & 0 deletions xblock/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,7 @@ def __init__(
"""
if scope_ids is UNSET:
raise TypeError('scope_ids are required')
scope_ids.validate_types()

# A cache of the parent block, retrieved from .parent
self._parent_block = None
Expand Down
26 changes: 25 additions & 1 deletion xblock/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
**scopes** to associate each field with particular sets of blocks and users.
The hosting runtime application decides what actual storage mechanism to use
for each scope.
"""
from __future__ import annotations

from collections import namedtuple
import copy
import datetime
Expand All @@ -15,6 +16,8 @@
import traceback
import warnings

from opaque_keys.edx.keys import UsageKey, DefinitionKey

import dateutil.parser
from lxml import etree
import pytz
Expand Down Expand Up @@ -242,6 +245,27 @@ class ScopeIds(namedtuple('ScopeIds', 'user_id block_type def_id usage_id')):
"""
__slots__ = ()

def validate_types(self):
"""
Raise an AssertionError if any of the ids are an unexpected type.
Originally, these fields were all freely-typed; but in practice,
edx-platform's XBlock runtime would fail if the ids did not match the
types below. In order to make the XBlock library reflect the
edx-platform reality and improve type-safety, we've decided to actually
enforce the types here, per:
https://github.com/openedx/XBlock/issues/708
"""
if self.user_id is not None:
if not isinstance(self.user_id, (int, str)):
raise TypeError(f"got {self.user_id=}; should be an int, str, or None")
if not isinstance(self.block_type, str):
raise TypeError(f"got {self.block_type=}; should be a str")
if not isinstance(self.def_id, DefinitionKey):
raise TypeError(f"got {self.def_id=}; should be a DefinitionKey")
if not isinstance(self.usage_id, UsageKey):
raise TypeError(f"got {self.usage_id=}; should be a UsageKey")


# Define special reference that can be used as a field's default in field
# definition to signal that the field should default to a unique string value
Expand Down

0 comments on commit e7ed235

Please sign in to comment.