fix: move BlockKey and derived_key to avoid cyclical import

After we merged this PR: https://github.com/openedx/edx-platform/pull/33920
this error began popping up in logs:

    Unable to load XBlock 'staffgradedxblock'
    ....
    ImportError: cannot import name 'get_course_blocks' from
    partially initialized module 'lms.djangoapps.course_blocks.api'
    (most likely due to a circular import) ...

The root cause was the new imports of `derived_key` and `BlockKey` into
xmodule/library_content_block.py. Those new imports come from
xmodule/modulestore/store_utilities.py, which runs
`XBlock.load_classes()` at the module level, which fails because we are
still in the process of loading xmodule/library_content_block.

As a solution, we move both `derived_key` and `BlockKey` to
xmodule/util/keys.py. We could potentially move that file to opaque-keys
eventually, depending on how well we think that those concepts generalize.

Also:

* We rename the function from derived_key to derive_key, as
  functions should be verbs.
* We combine the first to parameters of derive_key (a source ContextKey
  and a source BlockKey) into a single parameter (a source UsageKey). In
  my opinion, this makes the function call easier to understand.
This commit is contained in:
Kyle D. McCormick
2024-01-12 16:38:56 -05:00
committed by Kyle McCormick
parent d772ed1519
commit e8b60aef60
8 changed files with 123 additions and 106 deletions

View File

@@ -1,22 +1,10 @@
"""
General utilities
"""
from collections import namedtuple
from opaque_keys.edx.locator import BlockUsageLocator
class BlockKey(namedtuple('BlockKey', 'type id')): # lint-amnesty, pylint: disable=missing-class-docstring
__slots__ = ()
def __new__(cls, type, id): # lint-amnesty, pylint: disable=redefined-builtin
return super().__new__(cls, type, id)
@classmethod
def from_usage_key(cls, usage_key):
return cls(usage_key.block_type, usage_key.block_id)
# We import BlockKey here for backwards compatibility with modulestore code.
# Feel free to remove this and fix the imports if you have time.
from xmodule.util.keys import BlockKey
CourseEnvelope = namedtuple('CourseEnvelope', 'course_key structure')

View File

@@ -99,11 +99,12 @@ from xmodule.modulestore.exceptions import (
MultipleLibraryBlocksFound,
VersionConflictError
)
from xmodule.modulestore.split_mongo import BlockKey, CourseEnvelope
from xmodule.modulestore.split_mongo import CourseEnvelope
from xmodule.modulestore.split_mongo.mongo_connection import DuplicateKeyError, DjangoFlexPersistenceBackend
from xmodule.modulestore.store_utilities import DETACHED_XBLOCK_TYPES, derived_key
from xmodule.modulestore.store_utilities import DETACHED_XBLOCK_TYPES
from xmodule.partitions.partitions_service import PartitionService
from xmodule.util.misc import get_library_or_course_attribute
from xmodule.util.keys import BlockKey, derive_key
from ..exceptions import ItemNotFoundError
from .caching_descriptor_system import CachingDescriptorSystem
@@ -2452,7 +2453,7 @@ class SplitMongoModuleStore(SplitBulkWriteMixin, ModuleStoreWriteBase):
raise ItemNotFoundError(usage_key)
source_block_info = source_structure['blocks'][block_key]
new_block_key = derived_key(src_course_key, block_key, new_parent_block_key)
new_block_key = derive_key(usage_key, new_parent_block_key)
# Now clone block_key to new_block_key:
new_block_info = copy.deepcopy(source_block_info)