@@ -242,6 +242,32 @@ class GraphType(dj.AttributeType):
242242 return cls
243243
244244
245+ def parse_type_spec (spec : str ) -> tuple [str , str | None ]:
246+ """
247+ Parse a type specification into type name and optional store parameter.
248+
249+ Handles formats like:
250+ - "<xblob>" -> ("xblob", None)
251+ - "<xblob@cold>" -> ("xblob", "cold")
252+ - "xblob@cold" -> ("xblob", "cold")
253+ - "xblob" -> ("xblob", None)
254+
255+ Args:
256+ spec: Type specification string, with or without angle brackets.
257+
258+ Returns:
259+ Tuple of (type_name, store_name). store_name is None if not specified.
260+ """
261+ # Strip angle brackets
262+ spec = spec .strip ("<>" ).strip ()
263+
264+ if "@" in spec :
265+ type_name , store_name = spec .split ("@" , 1 )
266+ return type_name .strip (), store_name .strip ()
267+
268+ return spec , None
269+
270+
245271def unregister_type (name : str ) -> None :
246272 """
247273 Remove a type from the registry.
@@ -269,27 +295,30 @@ def get_type(name: str) -> AttributeType:
269295
270296 Args:
271297 name: The type name, with or without angle brackets.
298+ Store parameters (e.g., "<xblob@cold>") are stripped.
272299
273300 Returns:
274301 The registered AttributeType instance.
275302
276303 Raises:
277304 DataJointError: If the type is not found.
278305 """
279- name = name .strip ("<>" )
306+ # Strip angle brackets and store parameter
307+ type_name , _ = parse_type_spec (name )
280308
281309 # Check explicit registry first
282- if name in _type_registry :
283- return _type_registry [name ]
310+ if type_name in _type_registry :
311+ return _type_registry [type_name ]
284312
285313 # Lazy-load entry points
286314 _load_entry_points ()
287315
288- if name in _type_registry :
289- return _type_registry [name ]
316+ if type_name in _type_registry :
317+ return _type_registry [type_name ]
290318
291319 raise DataJointError (
292- f"Unknown attribute type: <{ name } >. " f"Ensure the type is registered via @dj.register_type or installed as a package."
320+ f"Unknown attribute type: <{ type_name } >. "
321+ f"Ensure the type is registered via @dj.register_type or installed as a package."
293322 )
294323
295324
@@ -309,16 +338,16 @@ def is_type_registered(name: str) -> bool:
309338 Check if a type name is registered.
310339
311340 Args:
312- name: The type name to check.
341+ name: The type name to check (store parameters are ignored) .
313342
314343 Returns:
315344 True if the type is registered.
316345 """
317- name = name . strip ( "<>" )
318- if name in _type_registry :
346+ type_name , _ = parse_type_spec ( name )
347+ if type_name in _type_registry :
319348 return True
320349 _load_entry_points ()
321- return name in _type_registry
350+ return type_name in _type_registry
322351
323352
324353def _load_entry_points () -> None :
@@ -368,23 +397,37 @@ def _load_entry_points() -> None:
368397 logger .warning (f"Failed to load attribute type '{ ep .name } ' from { ep .value } : { e } " )
369398
370399
371- def resolve_dtype (dtype : str , seen : set [str ] | None = None ) -> tuple [str , list [AttributeType ]]:
400+ def resolve_dtype (
401+ dtype : str , seen : set [str ] | None = None , store_name : str | None = None
402+ ) -> tuple [str , list [AttributeType ], str | None ]:
372403 """
373404 Resolve a dtype string, following type chains.
374405
375406 If dtype references another custom type (e.g., "<other_type>"), recursively
376- resolves to find the ultimate storage type.
407+ resolves to find the ultimate storage type. Store parameters are propagated
408+ through the chain.
377409
378410 Args:
379- dtype: The dtype string to resolve.
411+ dtype: The dtype string to resolve (e.g., "<xblob>", "<xblob@cold>", "longblob") .
380412 seen: Set of already-seen type names (for cycle detection).
413+ store_name: Store name from outer type specification (propagated inward).
381414
382415 Returns:
383- Tuple of (final_storage_type, list_of_types_in_chain).
416+ Tuple of (final_storage_type, list_of_types_in_chain, resolved_store_name ).
384417 The chain is ordered from outermost to innermost type.
385418
386419 Raises:
387420 DataJointError: If a circular type reference is detected.
421+
422+ Examples:
423+ >>> resolve_dtype("<xblob>")
424+ ("json", [XBlobType, ContentType], None)
425+
426+ >>> resolve_dtype("<xblob@cold>")
427+ ("json", [XBlobType, ContentType], "cold")
428+
429+ >>> resolve_dtype("longblob")
430+ ("longblob", [], None)
388431 """
389432 if seen is None :
390433 seen = set ()
@@ -393,7 +436,10 @@ def resolve_dtype(dtype: str, seen: set[str] | None = None) -> tuple[str, list[A
393436
394437 # Check if dtype is a custom type reference
395438 if dtype .startswith ("<" ) and dtype .endswith (">" ):
396- type_name = dtype [1 :- 1 ]
439+ type_name , dtype_store = parse_type_spec (dtype )
440+
441+ # Store from this level overrides inherited store
442+ effective_store = dtype_store if dtype_store is not None else store_name
397443
398444 if type_name in seen :
399445 raise DataJointError (f"Circular type reference detected: <{ type_name } >" )
@@ -402,13 +448,19 @@ def resolve_dtype(dtype: str, seen: set[str] | None = None) -> tuple[str, list[A
402448 attr_type = get_type (type_name )
403449 chain .append (attr_type )
404450
405- # Recursively resolve the inner dtype
406- inner_dtype , inner_chain = resolve_dtype (attr_type .dtype , seen )
451+ # Recursively resolve the inner dtype, propagating store
452+ inner_dtype , inner_chain , resolved_store = resolve_dtype (attr_type .dtype , seen , effective_store )
407453 chain .extend (inner_chain )
408- return inner_dtype , chain
454+ return inner_dtype , chain , resolved_store
455+
456+ # Not a custom type - check if it has a store suffix (e.g., "blob@store")
457+ if "@" in dtype :
458+ base_type , dtype_store = dtype .split ("@" , 1 )
459+ effective_store = dtype_store if dtype_store else store_name
460+ return base_type , chain , effective_store
409461
410- # Not a custom type - return as-is
411- return dtype , chain
462+ # Plain type - return as-is with propagated store
463+ return dtype , chain , store_name
412464
413465
414466# =============================================================================
0 commit comments