You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[R&D] Drop the addition of XModuleMixin for the xblocks-contrib xblocks #223
We can follow these steps per block (or in common branch) to drop the XModuleMixin support from the xblocks-contrib blocks (or even others can make a document How to standardize the xblock)
Study the given main PR
Add the required block name in STANDARD_COMPLIANT_XBLOCKS list
Use the corresponding xblocks branch for the testing or example via tutor mount or as used in the above PR
Run the block on lms/cms and fix the issues or place the required methods to the block for the support. Example PR
Fix the required test cases on the edx-platform
Make a list of all the methods that are needed to add in the xblocks-contrib blocks
Discuss the list with the team and brainstorm the best ways of add them
Make a list of the methods that should be moved to xblocks package upstream
changed the title [-]Drop the addition of `XModuleMixin` for the `xblocks-contrib` xblocks[/-][+][R&D] Drop the addition of `XModuleMixin` for the `xblocks-contrib` xblocks[/+]on Apr 1, 2026
Here is the output of the activity discussed in the yesterdays team meeting
I have done testing on extracted WordCloudBlock, VideoBlock, LTIBlock, HtmlBlock, AnnotatableBlock, ProblemBlock and as per testing following methods and fields are required to make these XBlock compatible with openedx-platform runtime if we don't attach the XmoduleMixin mixin.
has_score=False
crash logs without it
File "/openedx/edx-platform/lms/djangoapps/courseware/model_data.py", line 776, in cache_for_block_descendents
cache.add_block_descendents(block, depth, block_filter)
File "/openedx/edx-platform/lms/djangoapps/courseware/model_data.py", line 760, in add_block_descendents
self.add_blocks_to_cache(blocks)
File "/openedx/edx-platform/lms/djangoapps/courseware/model_data.py", line 715, in add_blocks_to_cache
self.scorable_locations.update(block.location for block in blocks if block.has_score)
File "/openedx/edx-platform/lms/djangoapps/courseware/model_data.py", line 715, in
self.scorable_locations.update(block.location for block in blocks if block.has_score)
^^^^^^^^^^^^^^^
AttributeError: 'WordCloudBlockWithMixins' object has no attribute 'has_score'
@propertydefcourse_id(self):
""" Return the key of the block to which this Course belongs. New code should always used `context_key`, which is the key of the Learning Context to which this block belongs. "Learning Context" is a generalized notion of Courses which is inclusive of Content Libraries, et al. """returnself.context_key@propertydefcategory(self):
""" Return the block type, formerly known as "category". Preferred forms for new code: `self.usage_key.block_type` or `self.scope_ids.blocks_type` """returnself.scope_ids.block_type@propertydeflocation(self):
""" Return the usage key identifying this block instance, formerly called the "location". `self.usage_key` is always preferred in new code. """returnself.usage_key@location.setterdeflocation(self, value):
fromopaque_keys.edx.keysimportUsageKeyassertisinstance(value, UsageKey)
self.scope_ids=self.scope_ids._replace(
def_id=value, # Note: assigning a UsageKey as def_id is OK in old mongo / import system but wrong in splitusage_id=value,
)
@propertydefurl_name(self):
""" Return the URL-friendly name for this block. Preferred forms for new code: `self.usage_key.block_id` """returnself.usage_key.block_id
These 4 properties have been using at lots of code places within openedx-platform
defget_icon_class(self):
""" Return a css class identifying this module in the context of an icon """return'other'# return self.icon_class
crash logs without it
File "/openedx/edx-platform/openedx/core/lib/xblock_utils/init.py", line 522, in get_icon
return block.get_icon_class()
^^^^^^^^^^^^^^^^^^^^^^
File "/openedx/edx-platform/xmodule/vertical_block.py", line 241, in get_icon_class
child_classes = {child.get_icon_class() for child in self.get_children()}
^^^^^^^^^^^^^^^^^^^^
@propertydefdisplay_name_with_default(self):
""" Return a display name for the module: use display_name if defined in metadata, otherwise convert the url name. """return (
self.display_nameifself.display_nameisnotNoneelseself.usage_key.block_id.replace('_', ' ')
)
crash logs without it
File "/mnt/xblock/xblock/runtime.py", line 829, in render
updated_frag = self.wrap_xblock(block, view_name, frag, context)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/openedx/edx-platform/xmodule/x_module.py", line 964, in wrap_xblock
frag = wrapper(block, view, frag, context)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/openedx/edx-platform/openedx/core/lib/xblock_utils/init.py", line 147, in wrap_xblock
'display_name': block.display_name_with_default, # xss-lint: disable=python-deprecated-display-name
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'HtmlBlockWithMixins' object has no attribute 'display_name_with_default'
defbind_for_student(self, user_id, wrappers=None):
""" Set up this XBlock to act as an XModule instead of an XModuleDescriptor. Arguments: user_id: The user_id to set in scope_ids wrappers: These are a list functions that put a wrapper, such as LmsFieldData or OverrideFieldData, around the field_data. Note that the functions will be applied in the order in which they're listed. So [f1, f2] -> f2(f1(field_data)) """# Skip rebinding if we're already bound a user, and it's this user.ifself.scope_ids.user_idisnotNoneanduser_id==self.scope_ids.user_id:
ifgetattr(self.runtime, "position", None):
self.position=self.runtime.position# update the position of the tabreturn# If we are switching users mid-request, save the data from the old user.self.save()
# Update scope_ids to point to the new user.self.scope_ids=self.scope_ids._replace(user_id=user_id)
# Clear out any cached instantiated children.self.clear_child_cache()
# Clear out any cached field data scoped to the old user.forfieldinself.fields.values():
iffield.scopein (Scope.parent, Scope.children):
continueiffield.scope.user==UserScope.ONE:
field._del_cached_value(self) # pylint: disable=protected-access# not the most elegant way of doing this, but if we're removing# a field from the module's field_data_cache, we should also# remove it from its _dirty_fieldsiffieldinself._dirty_fields:
delself._dirty_fields[field]
ifwrappers:
# Put user-specific wrappers around the field-data service for this block.# Note that these are different from modulestore.xblock_field_data_wrappers, which are not user-specific.wrapped_field_data=self.runtime.service(self, "field-data-unbound")
forwrapperinwrappers:
wrapped_field_data=wrapper(wrapped_field_data)
self._bound_field_data=wrapped_field_dataifgetattr(self.runtime, "uses_deprecated_field_data", False):
# This approach is deprecated but OldModuleStoreRuntime still requires it.# For SplitModuleStoreRuntime, don't set ._field_data this way.self._field_data=wrapped_field_data
crash logs without it
File "/mnt/xblock/xblock/core.py", line 866, in get_child
child_block = self.runtime.get_block(usage_id, for_parent=self)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/openedx/edx-platform/xmodule/x_module.py", line 1478, in get_block
return self.get_block_for_descriptor(block)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/openedx/edx-platform/cms/djangoapps/contentstore/views/preview.py", line 288, in _load_preview_block
block.bind_for_student(
^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'WordCloudBlockWithMixins' object has no attribute 'bind_for_student'
defget_required_block_descriptors(self):
""" Return a list of XBlock instances upon which this block depends but are not children of this block. TODO: Move this method directly to the ConditionalBlock. """return []
crash logs without it
File "/openedx/edx-platform/lms/djangoapps/courseware/model_data.py", line 753, in get_child_blocks
blocks.extend(get_child_blocks(child, new_depth, block_filter))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/openedx/edx-platform/lms/djangoapps/courseware/model_data.py", line 753, in get_child_blocks
blocks.extend(get_child_blocks(child, new_depth, block_filter))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/openedx/edx-platform/lms/djangoapps/courseware/model_data.py", line 752, in get_child_blocks
for child in block.get_children() + block.get_required_block_descriptors():
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'WordCloudBlockWithMixins' object has no attribute 'get_required_block_descriptors'
Activity
farhan commentedon Mar 31, 2026
R&D Summary
Main PR after R&D: openedx/openedx-platform#38264
We can follow these steps per block (or in common branch) to drop the
XModuleMixinsupport from thexblocks-contribblocks (or even others can make a documentHow to standardize the xblock)STANDARD_COMPLIANT_XBLOCKSlistxblocksbranch for the testing or example viatutor mountor as used in the above PRedx-platformxblocks-contribblocksxblockspackage upstream[-]Drop the addition of `XModuleMixin` for the `xblocks-contrib` xblocks[/-][+][R&D] Drop the addition of `XModuleMixin` for the `xblocks-contrib` xblocks[/+]farhan commentedon Apr 2, 2026
@feanil @kdmccormick @openedx/axim-aximprovements
Here is the output of the activity discussed in the yesterdays team meeting
I have done testing on extracted
WordCloudBlock,VideoBlock,LTIBlock,HtmlBlock,AnnotatableBlock,ProblemBlockand as per testing following methods and fields are required to make these XBlock compatible withopenedx-platformruntime if we don't attach theXmoduleMixinmixin.crash logs without it
These 4 properties have been using at lots of code places within
openedx-platformcrash logs without it
crash logs without it
crash logs without it
crash logs without it