From a56d8db1ac2892256d64a434537317a37c78f307 Mon Sep 17 00:00:00 2001 From: Braden MacDonald Date: Fri, 31 May 2024 19:34:54 -0700 Subject: [PATCH] WIP better separation of mixin vs pure v2 --- xblock/core.py | 76 ++++++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/xblock/core.py b/xblock/core.py index 4fea1f10a..0dc41e887 100644 --- a/xblock/core.py +++ b/xblock/core.py @@ -955,28 +955,58 @@ def has_support(self, view, functionality): class XBlock2Mixin: """ Mixin with shared implementation for all v2 XBlocks, whether they are - wrappers around a v1 XBlock, or pure v2-only XBlocks. - + keeping backwards compatibility with v1 or not. + Note: check if an XBlock is "v2" using `issubclass(block, XBlock2Mixin)`, not `issubclass(block, XBlock2)` """ has_children: Final = False + def __init__(self, *args, **kwargs): + """ + Validation during init + """ + super().__init__(*args, **kwargs) + if self.has_children is not False: + raise ValueError('v2 XBlocks cannot declare has_children = True') + + @contextmanager + def _track_field_writes(self, field_updates): + if not isinstance(self, XBlock2Mixin): + raise TypeError("track_field_writes() is only compatible with XBlock2 instances") + if self._dirty_fields: + raise ValueError("Found dirty fields before handler even started - shouldn't happen") + print("Starting handler...") + try: + yield + for field in self._dirty_fields.keys(): + scope_type = "user" if field.scope.user != UserScope.NONE else "content" + field_updates["updated_fields"][scope_type][field.name] = field.to_json(getattr(self, field.name)) + print("success, dirty fields: ", self._dirty_fields) + print("success, dirty fields: ", field_updates["updated_fields"]) + print(f"{self}") + self.force_save_fields([field.name for field in self._dirty_fields.keys()]) + self.runtime.save_block(self) + finally: + self._dirty_fields.clear() + print("Ending handler...") + + +class XBlock2(XBlock2Mixin, XBlock): + """ + Base class for pure "v2" XBlocks, that don't need backwards compatibility with v1 + """ + def __init__( self, runtime, field_data=None, scope_ids=UNSET, for_parent=None, - **kwargs): + **kwargs, + ): """ - Arguments: - - runtime (:class:`.Runtime`): Use it to access the environment. - It is available in XBlock code as ``self.runtime``. - - scope_ids (:class:`.ScopeIds`): Identifiers needed to resolve - scopes. + Initialize this v2 XBlock, checking for deprecated usage first """ if self.has_children is not False: raise ValueError('v2 XBlocks cannot declare has_children = True') @@ -1014,32 +1044,6 @@ def _parent_block_id(self, value): if value is not None: raise ValueError("v2 XBlocks cannot have a parent.") - @contextmanager - def _track_field_writes(self, field_updates): - if not isinstance(self, XBlock2Mixin): - raise TypeError("track_field_writes() is only compatible with XBlock2 instances") - if self._dirty_fields: - raise ValueError("Found dirty fields before handler even started - shouldn't happen") - print("Starting handler...") - try: - yield - for field in self._dirty_fields.keys(): - scope_type = "user" if field.scope.user != UserScope.NONE else "content" - field_updates["updated_fields"][scope_type][field.name] = field.to_json(getattr(self, field.name)) - print("success, dirty fields: ", self._dirty_fields) - print("success, dirty fields: ", field_updates["updated_fields"]) - self.force_save_fields([field.name for field in self._dirty_fields.keys()]) - self.runtime.save_block(self) - finally: - self._dirty_fields.clear() - print("Ending handler...") - - -class XBlock2(XBlock2Mixin, XBlock): - """ - Base class for pure "v2" XBlocks, that don't need backwards compatibility with v1 - """ - class XBlockAside(Plugin, Blocklike): """