Skip to content

Commit 7820702

Browse files
merge: Integrate modern-fetch-api with updated fetch methods
- Remove download_path parameter from fetch methods (use config instead) - Remove deprecated fetch.py module - Simplified docstrings for new API 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
2 parents c16e79f + a237b26 commit 7820702

File tree

6 files changed

+586
-1144
lines changed

6 files changed

+586
-1144
lines changed

src/datajoint/codecs.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,80 @@ def lookup_codec(codec_spec: str) -> tuple[Codec, str | None]:
503503
raise DataJointError(f"Codec <{type_name}> is not registered. " "Define a Codec subclass with name='{type_name}'.")
504504

505505

506+
# =============================================================================
507+
# Decode Helper
508+
# =============================================================================
509+
510+
511+
def decode_attribute(attr, data, squeeze: bool = False):
512+
"""
513+
Decode raw database value using attribute's codec or native type handling.
514+
515+
This is the central decode function used by all fetch methods. It handles:
516+
- Codec chains (e.g., <blob@store> → <hash> → bytes)
517+
- Native type conversions (JSON, UUID)
518+
- External storage downloads (via config["download_path"])
519+
520+
Args:
521+
attr: Attribute from the table's heading.
522+
data: Raw value fetched from the database.
523+
squeeze: If True, remove singleton dimensions from numpy arrays.
524+
525+
Returns:
526+
Decoded Python value.
527+
"""
528+
import json
529+
import uuid as uuid_module
530+
531+
import numpy as np
532+
533+
if data is None:
534+
return None
535+
536+
if attr.codec:
537+
# Get store if present for external storage
538+
store = getattr(attr, "store", None)
539+
if store is not None:
540+
dtype_spec = f"<{attr.codec.name}@{store}>"
541+
else:
542+
dtype_spec = f"<{attr.codec.name}>"
543+
544+
final_dtype, type_chain, _ = resolve_dtype(dtype_spec)
545+
546+
# Process the final storage type (what's in the database)
547+
if final_dtype.lower() == "json":
548+
data = json.loads(data)
549+
elif final_dtype.lower() in ("longblob", "blob", "mediumblob", "tinyblob"):
550+
pass # Blob data is already bytes
551+
elif final_dtype.lower() == "binary(16)":
552+
data = uuid_module.UUID(bytes=data)
553+
554+
# Apply decoders in reverse order: innermost first, then outermost
555+
for codec in reversed(type_chain):
556+
data = codec.decode(data, key=None)
557+
558+
# Squeeze arrays if requested
559+
if squeeze and isinstance(data, np.ndarray):
560+
data = data.squeeze()
561+
562+
return data
563+
564+
# No codec - handle native types
565+
if attr.json:
566+
return json.loads(data)
567+
568+
if attr.uuid:
569+
import uuid as uuid_module
570+
571+
return uuid_module.UUID(bytes=data)
572+
573+
if attr.is_blob:
574+
return data # Raw bytes
575+
576+
# Native types - pass through unchanged
577+
return data
578+
579+
506580
# =============================================================================
507581
# Auto-register built-in codecs
508582
# =============================================================================

0 commit comments

Comments
 (0)