Skip to content

Commit 898d0ed

Browse files
Rename AttributeType/adapter terminology to Codec
- Rename attribute_type.py → codecs.py - Rename builtin_types.py → builtin_codecs.py - Rename test_attribute_type.py → test_codecs.py - Rename get_adapter() → lookup_codec() - Rename attr.adapter → attr.codec in Attribute namedtuple - Update all imports and references throughout codebase - Update comments and docstrings to use codec terminology All 471 tests pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent e7054dd commit 898d0ed

File tree

15 files changed

+89
-93
lines changed

15 files changed

+89
-93
lines changed

src/datajoint/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
from . import errors
6767
from . import migrate
6868
from .admin import kill
69-
from .attribute_type import (
69+
from .codecs import (
7070
AttributeType,
7171
Codec,
7272
get_codec,
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class Networks(dj.Manual):
5858

5959
from typing import Any
6060

61-
from .attribute_type import Codec
61+
from .codecs import Codec
6262
from .errors import DataJointError
6363

6464

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -503,24 +503,24 @@ def resolve_dtype(
503503
return dtype, chain, store_name
504504

505505

506-
def get_adapter(context: dict | None, adapter_name: str) -> tuple[Codec, str | None]:
506+
def lookup_codec(codec_spec: str) -> tuple[Codec, str | None]:
507507
"""
508-
Get a codec by name.
508+
Look up a codec from a type specification string.
509509
510-
This is a compatibility function used by heading and declare modules.
510+
Parses a codec specification (e.g., "<blob@store>") and returns
511+
the codec instance along with any store name.
511512
512513
Args:
513-
context: Ignored (legacy parameter, kept for API compatibility).
514-
adapter_name: The codec name, with or without angle brackets.
515-
May include store parameter (e.g., "<blob@cold>").
514+
codec_spec: The codec specification, with or without angle brackets.
515+
May include store parameter (e.g., "<blob@cold>").
516516
517517
Returns:
518518
Tuple of (Codec instance, store_name or None).
519519
520520
Raises:
521521
DataJointError: If the codec is not found.
522522
"""
523-
type_name, store_name = parse_type_spec(adapter_name)
523+
type_name, store_name = parse_type_spec(codec_spec)
524524

525525
if is_codec_registered(type_name):
526526
return get_codec(type_name), store_name
@@ -532,6 +532,6 @@ def get_adapter(context: dict | None, adapter_name: str) -> tuple[Codec, str | N
532532
# Auto-register built-in codecs
533533
# =============================================================================
534534

535-
# Import builtin_types module to register built-in codecs
535+
# Import builtin_codecs module to register built-in codecs
536536
# This import has a side effect: it registers the codecs via __init_subclass__
537-
from . import builtin_types as _builtin_types # noqa: F401, E402
537+
from . import builtin_codecs as _builtin_codecs # noqa: F401, E402

src/datajoint/declare.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
import pyparsing as pp
1111

12-
from .attribute_type import get_adapter
12+
from .codecs import lookup_codec
1313
from .condition import translate_attribute
1414
from .errors import DataJointError
1515
from .settings import config
@@ -464,16 +464,16 @@ def substitute_special_type(match, category, foreign_key_sql, context):
464464
:param match: dict containing with keys "type" and "comment" -- will be modified in place
465465
:param category: attribute type category from TYPE_PATTERN
466466
:param foreign_key_sql: list of foreign key declarations to add to
467-
:param context: context for looking up user-defined attribute_type adapters
467+
:param context: context for looking up user-defined codecs (unused, kept for compatibility)
468468
"""
469469
if category == "ADAPTED":
470-
# AttributeType - resolve to underlying dtype
471-
attr_type, store_name = get_adapter(context, match["type"])
470+
# Codec - resolve to underlying dtype
471+
codec, store_name = lookup_codec(match["type"])
472472
if store_name is not None:
473473
match["store"] = store_name
474474
# Determine if external storage is used (store_name is present, even if empty string for default)
475475
is_external = store_name is not None
476-
inner_dtype = attr_type.get_dtype(is_external=is_external)
476+
inner_dtype = codec.get_dtype(is_external=is_external)
477477

478478
# If inner dtype is a codec without store, propagate the store from outer type
479479
# e.g., <attach@mystore> returns <hash>, we need to resolve as <hash@mystore>

src/datajoint/fetch.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ def _get(connection, attr, data, squeeze, download_path):
3939
- Native types pass through unchanged
4040
- JSON types are parsed
4141
- UUID types are converted from bytes
42-
- Blob types return raw bytes (unless an adapter handles them)
43-
- Adapters (AttributeTypes) handle all custom encoding/decoding via type chains
42+
- Blob types return raw bytes (unless a codec handles them)
43+
- Codecs handle all custom encoding/decoding via type chains
4444
4545
For composed types (e.g., <blob@> using <hash>), decoders are applied
4646
in reverse order: innermost first, then outermost.
@@ -57,16 +57,16 @@ def _get(connection, attr, data, squeeze, download_path):
5757
if data is None:
5858
return None
5959

60-
# Get the final storage type and type chain if adapter present
61-
if attr.adapter:
62-
from .attribute_type import resolve_dtype
60+
# Get the final storage type and type chain if codec present
61+
if attr.codec:
62+
from .codecs import resolve_dtype
6363

6464
# Include store if present to get correct chain for external storage
6565
store = getattr(attr, "store", None)
6666
if store is not None:
67-
dtype_spec = f"<{attr.adapter.type_name}@{store}>"
67+
dtype_spec = f"<{attr.codec.type_name}@{store}>"
6868
else:
69-
dtype_spec = f"<{attr.adapter.type_name}>"
69+
dtype_spec = f"<{attr.codec.type_name}>"
7070
final_dtype, type_chain, _ = resolve_dtype(dtype_spec)
7171

7272
# First, process the final dtype (what's stored in the database)
@@ -93,7 +93,7 @@ def _get(connection, attr, data, squeeze, download_path):
9393

9494
return data
9595

96-
# No adapter - handle native types
96+
# No codec - handle native types
9797
if attr.json:
9898
return json.loads(data)
9999

src/datajoint/gc.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,11 @@ def _uses_content_storage(attr) -> bool:
5252
Returns:
5353
True if the attribute stores content hashes
5454
"""
55-
if not attr.adapter:
55+
if not attr.codec:
5656
return False
5757

5858
# Check if this type uses content storage
59-
type_name = getattr(attr.adapter, "type_name", "")
59+
type_name = getattr(attr.codec, "type_name", "")
6060
store = getattr(attr, "store", None)
6161

6262
# <hash> always uses content storage (external only)
@@ -80,10 +80,10 @@ def _uses_object_storage(attr) -> bool:
8080
Returns:
8181
True if the attribute stores object paths
8282
"""
83-
if not attr.adapter:
83+
if not attr.codec:
8484
return False
8585

86-
type_name = getattr(attr.adapter, "type_name", "")
86+
type_name = getattr(attr.codec, "type_name", "")
8787
return type_name == "object"
8888

8989

src/datajoint/heading.py

Lines changed: 30 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55

66
import numpy as np
77

8-
from .attribute_type import get_adapter
9-
from .attribute_type import AttributeType
8+
from .codecs import lookup_codec
9+
from .codecs import Codec
1010
from .declare import (
1111
CORE_TYPE_NAMES,
1212
SPECIAL_TYPES,
@@ -15,33 +15,31 @@
1515
from .errors import DataJointError
1616

1717

18-
class _MissingType(AttributeType):
19-
"""Placeholder for missing/unregistered attribute types. Raises error on use."""
18+
class _MissingType(Codec, register=False):
19+
"""Placeholder for missing/unregistered codecs. Raises error on use."""
2020

21-
def __init__(self, name: str):
22-
self._name = name
21+
name = None # Don't auto-register
22+
23+
def __init__(self, codec_name: str):
24+
self._codec_name = codec_name
2325

2426
@property
2527
def type_name(self) -> str:
26-
return self._name
28+
return self._codec_name
2729

28-
@property
29-
def dtype(self) -> str:
30+
def get_dtype(self, is_external: bool) -> str:
3031
raise DataJointError(
31-
f"Attribute type <{self._name}> is not registered. "
32-
"Register it with @dj.register_type or include it in the schema context."
32+
f"Codec <{self._codec_name}> is not registered. " f"Define a Codec subclass with name='{self._codec_name}'."
3333
)
3434

35-
def encode(self, value, *, key=None):
35+
def encode(self, value, *, key=None, store_name=None):
3636
raise DataJointError(
37-
f"Attribute type <{self._name}> is not registered. "
38-
"Register it with @dj.register_type or include it in the schema context."
37+
f"Codec <{self._codec_name}> is not registered. " f"Define a Codec subclass with name='{self._codec_name}'."
3938
)
4039

4140
def decode(self, stored, *, key=None):
4241
raise DataJointError(
43-
f"Attribute type <{self._name}> is not registered. "
44-
"Register it with @dj.register_type or include it in the schema context."
42+
f"Codec <{self._codec_name}> is not registered. " f"Define a Codec subclass with name='{self._codec_name}'."
4543
)
4644

4745

@@ -62,7 +60,7 @@ def decode(self, stored, *, key=None):
6260
json=None,
6361
is_blob=False,
6462
is_hidden=False,
65-
adapter=None,
63+
codec=None,
6664
store=None,
6765
unsupported=False,
6866
attribute_expression=None,
@@ -286,7 +284,7 @@ def _init_from_database(self):
286284
is_blob=any(TYPE_PATTERN[t].match(attr["type"]) for t in ("BYTES", "NATIVE_BLOB")),
287285
uuid=False,
288286
json=bool(TYPE_PATTERN["JSON"].match(attr["type"])),
289-
adapter=None,
287+
codec=None,
290288
store=None,
291289
attribute_expression=None,
292290
is_hidden=attr["name"].startswith("_"),
@@ -311,26 +309,24 @@ def _init_from_database(self):
311309
# Store the original type name for display but keep db_type for SQL
312310
attr["original_type"] = special["type"]
313311

314-
# process AttributeTypes (adapted types in angle brackets)
312+
# process Codecs (adapted types in angle brackets)
315313
if special and TYPE_PATTERN["ADAPTED"].match(attr["type"]):
316314
# Context can be None for built-in types that are globally registered
317-
adapter_name = special["type"]
315+
codec_spec = special["type"]
318316
try:
319-
adapter_result = get_adapter(context, adapter_name)
320-
# get_adapter returns (adapter, store_name) tuple
321-
if isinstance(adapter_result, tuple):
322-
attr["adapter"], attr["store"] = adapter_result
323-
else:
324-
attr["adapter"] = adapter_result
317+
codec_instance, codec_store = lookup_codec(codec_spec)
318+
attr["codec"] = codec_instance
319+
if codec_store is not None:
320+
attr["store"] = codec_store
325321
except DataJointError:
326-
# if no adapter, then delay the error until the first invocation
327-
attr["adapter"] = _MissingType(adapter_name)
322+
# if no codec, then delay the error until the first invocation
323+
attr["codec"] = _MissingType(codec_spec)
328324
else:
329325
# Determine if external storage based on store presence
330326
is_external = attr.get("store") is not None
331-
attr["type"] = attr["adapter"].get_dtype(is_external=is_external)
327+
attr["type"] = attr["codec"].get_dtype(is_external=is_external)
332328
if not any(r.match(attr["type"]) for r in TYPE_PATTERN.values()):
333-
raise DataJointError(f"Invalid dtype '{attr['type']}' in attribute type <{adapter_name}>.")
329+
raise DataJointError(f"Invalid dtype '{attr['type']}' in codec <{codec_spec}>.")
334330
# Update is_blob based on resolved dtype (check both BYTES and NATIVE_BLOB patterns)
335331
attr["is_blob"] = any(TYPE_PATTERN[t].match(attr["type"]) for t in ("BYTES", "NATIVE_BLOB"))
336332

@@ -367,7 +363,7 @@ def _init_from_database(self):
367363

368364
# fill out dtype. All floats and non-nullable integers are turned into specific dtypes
369365
attr["dtype"] = object
370-
if attr["numeric"] and not attr["adapter"]:
366+
if attr["numeric"] and not attr["codec"]:
371367
is_integer = TYPE_PATTERN["INTEGER"].match(attr["type"])
372368
is_float = TYPE_PATTERN["FLOAT"].match(attr["type"])
373369
if is_integer and not attr["nullable"] or is_float:
@@ -377,9 +373,9 @@ def _init_from_database(self):
377373
assert (t, is_unsigned) in numeric_types, "dtype not found for type %s" % t
378374
attr["dtype"] = numeric_types[(t, is_unsigned)]
379375

380-
if attr["adapter"]:
381-
# restore adapted type name for display
382-
attr["type"] = adapter_name
376+
if attr["codec"]:
377+
# restore codec type name for display
378+
attr["type"] = codec_spec
383379

384380
self._attributes = dict(((q["name"], Attribute(**q)) for q in attributes))
385381

src/datajoint/migrate.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def analyze_blob_columns(schema: Schema) -> list[dict]:
2929
3030
This function identifies blob columns that:
3131
1. Have a MySQL blob type (tinyblob, blob, mediumblob, longblob)
32-
2. Do NOT already have an adapter/type specified in their comment
32+
2. Do NOT already have a codec/type specified in their comment
3333
3434
All blob size variants are included in the analysis.
3535
@@ -80,16 +80,16 @@ def analyze_blob_columns(schema: Schema) -> list[dict]:
8080
columns = connection.query(columns_query, args=(schema.database, table_name)).fetchall()
8181

8282
for column_name, column_type, comment in columns:
83-
# Check if comment already has an adapter type (starts with :type:)
84-
has_adapter = comment and comment.startswith(":")
83+
# Check if comment already has a codec type (starts with :type:)
84+
has_codec = comment and comment.startswith(":")
8585

8686
results.append(
8787
{
8888
"table_name": f"{schema.database}.{table_name}",
8989
"column_name": column_name,
9090
"column_type": column_type,
9191
"current_comment": comment or "",
92-
"needs_migration": not has_adapter,
92+
"needs_migration": not has_codec,
9393
}
9494
)
9595

src/datajoint/preview.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def _format_object_display(json_data):
2727
def preview(query_expression, limit, width):
2828
heading = query_expression.heading
2929
rel = query_expression.proj(*heading.non_blobs)
30-
# Object fields are AttributeTypes with adapters - not specially handled in simplified model
30+
# Object fields use codecs - not specially handled in simplified model
3131
object_fields = []
3232
if limit is None:
3333
limit = config["display.limit"]
@@ -88,7 +88,7 @@ def get_display_value(tup, f, idx):
8888
def repr_html(query_expression):
8989
heading = query_expression.heading
9090
rel = query_expression.proj(*heading.non_blobs)
91-
# Object fields are AttributeTypes with adapters - not specially handled in simplified model
91+
# Object fields use codecs - not specially handled in simplified model
9292
object_fields = []
9393
info = heading.table_status
9494
tuples = rel.fetch(limit=config["display.limit"] + 1, format="array")

src/datajoint/staged_insert.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def _get_storage_path(self, field: str, ext: str = "") -> str:
9999

100100
attr = self._table.heading[field]
101101
# Check if this is an object AttributeType (has adapter with "object" in type_name)
102-
if not (attr.adapter and hasattr(attr.adapter, "type_name") and "object" in attr.adapter.type_name):
102+
if not (attr.codec and hasattr(attr.codec, "type_name") and "object" in attr.codec.type_name):
103103
raise DataJointError(f"Attribute '{field}' is not an <object> type")
104104

105105
# Extract primary key from rec

0 commit comments

Comments
 (0)