@@ -95,18 +95,30 @@ class XBlockRuntime(RuntimeShim, Runtime):
95
95
# currently only used to track if we're in the studio_view (see below under service())
96
96
view_name : str | None
97
97
98
- def __init__ (self , system : XBlockRuntimeSystem , user : UserType | None ):
98
+ def __init__ (
99
+ self ,
100
+ user : UserType | None ,
101
+ * ,
102
+ handler_url : Callable [[UsageKey , str , UserType | None ], str ],
103
+ student_data_mode : StudentDataMode ,
104
+ id_reader : Optional [IdReader ] = None ,
105
+ authored_data_store : Optional [FieldData ] = None ,
106
+ ):
99
107
super ().__init__ (
100
- id_reader = system . id_reader ,
108
+ id_reader = id_reader or OpaqueKeyReader () ,
101
109
mixins = (
102
110
LmsBlockMixin , # Adds Non-deprecated LMS/Studio functionality
103
111
XBlockShim , # Adds deprecated LMS/Studio functionality / backwards compatibility
104
112
),
105
113
default_class = None ,
106
114
select = None ,
107
- id_generator = system . id_generator ,
115
+ id_generator = MemoryIdManager (), # We don't really use id_generator until we need to support asides
108
116
)
109
- self .system = system
117
+ assert student_data_mode in (StudentDataMode .Ephemeral , StudentDataMode .Persisted )
118
+ self .authored_data_store = authored_data_store
119
+ self .children_data_store = None
120
+ self .student_data_mode = student_data_mode
121
+ self .handler_url_fn = handler_url
110
122
self .user = user
111
123
# self.user_id must be set as a separate attribute since base class sets it:
112
124
if self .user is None :
@@ -126,7 +138,7 @@ def handler_url(self, block, handler_name: str, suffix='', query='', thirdparty=
126
138
if thirdparty :
127
139
log .warning ("thirdparty handlers are not supported by this runtime for XBlock %s." , type (block ))
128
140
129
- url = self .system . handler_url (block .scope_ids .usage_id , handler_name , self .user )
141
+ url = self .handler_url_fn (block .scope_ids .usage_id , handler_name , self .user )
130
142
if suffix :
131
143
if not url .endswith ('/' ):
132
144
url += '/'
@@ -275,7 +287,7 @@ def service(self, block: XBlock, service_name: str):
275
287
# the preview engine, and 'main' otherwise.
276
288
# For backwards compatibility, we check the student_data_mode (Ephemeral indicates CMS) and the
277
289
# view_name for 'studio_view.' self.view_name is set by render() below.
278
- if self .system . student_data_mode == StudentDataMode .Ephemeral and self .view_name != 'studio_view' :
290
+ if self .student_data_mode == StudentDataMode .Ephemeral and self .view_name != 'studio_view' :
279
291
return MakoService (namespace_prefix = 'lms.' )
280
292
return MakoService ()
281
293
elif service_name == "i18n" :
@@ -301,14 +313,12 @@ def service(self, block: XBlock, service_name: str):
301
313
return EventPublishingService (self .user , context_key , make_track_function ())
302
314
elif service_name == 'enrollments' :
303
315
return EnrollmentsService ()
316
+ elif service_name == 'error_tracker' :
317
+ return make_error_tracker ()
304
318
305
- # Check if the XBlockRuntimeSystem wants to handle this:
306
- service = self .system .get_service (block , service_name )
307
319
# Otherwise, fall back to the base implementation which loads services
308
320
# defined in the constructor:
309
- if service is None :
310
- service = super ().service (block , service_name )
311
- return service
321
+ return super ().service (block , service_name )
312
322
313
323
def _init_field_data_for_block (self , block : XBlock ) -> FieldData :
314
324
"""
@@ -322,7 +332,7 @@ def _init_field_data_for_block(self, block: XBlock) -> FieldData:
322
332
assert isinstance (self .user_id , str ) and self .user_id .startswith ("anon" )
323
333
kvs = EphemeralKeyValueStore ()
324
334
student_data_store = KvsFieldData (kvs )
325
- elif self .system . student_data_mode == StudentDataMode .Ephemeral :
335
+ elif self .student_data_mode == StudentDataMode .Ephemeral :
326
336
# We're in an environment like Studio where we want to let the
327
337
# author test blocks out but not permanently save their state.
328
338
kvs = EphemeralKeyValueStore ()
@@ -341,10 +351,10 @@ def _init_field_data_for_block(self, block: XBlock) -> FieldData:
341
351
student_data_store = KvsFieldData (kvs = DjangoKeyValueStore (field_data_cache ))
342
352
343
353
return SplitFieldData ({
344
- Scope .content : self .system . authored_data_store ,
345
- Scope .settings : self .system . authored_data_store ,
346
- Scope .parent : self .system . authored_data_store ,
347
- Scope .children : self .system . children_data_store ,
354
+ Scope .content : self .authored_data_store ,
355
+ Scope .settings : self .authored_data_store ,
356
+ Scope .parent : self .authored_data_store ,
357
+ Scope .children : self .children_data_store ,
348
358
Scope .user_state_summary : student_data_store ,
349
359
Scope .user_state : student_data_store ,
350
360
Scope .user_info : student_data_store ,
@@ -407,62 +417,3 @@ def _lookup_asset_url(self, block: XBlock, asset_path: str): # pylint: disable=
407
417
"""
408
418
# Subclasses should override this
409
419
return None
410
-
411
-
412
- class XBlockRuntimeSystem :
413
- """
414
- This class is essentially a factory for XBlockRuntimes. This is a
415
- long-lived object which provides the behavior specific to the application
416
- that wants to use XBlocks. Unlike XBlockRuntime, a single instance of this
417
- class can be used with many different XBlocks, whereas each XBlock gets its
418
- own instance of XBlockRuntime.
419
- """
420
- def __init__ (
421
- self ,
422
- handler_url : Callable [[UsageKey , str , UserType | None ], str ],
423
- student_data_mode : StudentDataMode ,
424
- runtime_class : type [XBlockRuntime ],
425
- id_reader : Optional [IdReader ] = None ,
426
- authored_data_store : Optional [FieldData ] = None ,
427
- ):
428
- """
429
- args:
430
- handler_url: A method to get URLs to call XBlock handlers. It must
431
- implement this signature:
432
- handler_url(
433
- usage_key: UsageKey,
434
- handler_name: str,
435
- user: User | AnonymousUser | None
436
- ) -> str
437
- student_data_mode: Specifies whether student data should be kept
438
- in a temporary in-memory store (e.g. Studio) or persisted
439
- forever in the database.
440
- runtime_class: What runtime to use, e.g. LearningCoreXBlockRuntime
441
- """
442
- self .handler_url = handler_url
443
- self .id_reader = id_reader or OpaqueKeyReader ()
444
- self .id_generator = MemoryIdManager () # We don't really use id_generator until we need to support asides
445
- self .runtime_class = runtime_class
446
- self .authored_data_store = authored_data_store
447
- self .children_data_store = None
448
- assert student_data_mode in (StudentDataMode .Ephemeral , StudentDataMode .Persisted )
449
- self .student_data_mode = student_data_mode
450
-
451
- def get_runtime (self , user : UserType | None ) -> XBlockRuntime :
452
- """
453
- Get the XBlock runtime for the specified Django user. The user can be
454
- a regular user, an AnonymousUser, or None.
455
- """
456
- return self .runtime_class (self , user )
457
-
458
- def get_service (self , block , service_name : str ):
459
- """
460
- Get a runtime service
461
-
462
- Runtime services may come from this XBlockRuntimeSystem,
463
- or if this method returns None, they may come from the
464
- XBlockRuntime.
465
- """
466
- if service_name == 'error_tracker' :
467
- return make_error_tracker ()
468
- return None # None means see if XBlockRuntime offers this service
0 commit comments