Skip to content

Commit 4edf5ed

Browse files
Fix table_name and uuid type resolution bugs
- Fix Table.table_name property to delegate to metaclass for UserTable subclasses (table_name was returning None instead of computed name) - Fix heading type loading to preserve database type for core types (uuid, etc.) instead of overwriting with alias from comment - Add original_type field to Attribute for storing the alias while keeping the actual SQL type in type field - Fix tests: remove obsolete test_external.py, update resolve_dtype tests to expect 3 return values, update type alias tests to use CORE_TYPE_SQL - Update pyproject.toml pytest_env to use D: prefix for default-only vars Test results improved from 174 passed/284 errors to 381 passed/62 errors. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 6d5b745 commit 4edf5ed

File tree

6 files changed

+41
-142
lines changed

6 files changed

+41
-142
lines changed

pyproject.toml

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -153,19 +153,19 @@ skip = ".git,*.pdf,*.svg,*.csv,*.ipynb,*.drawio"
153153
ignore-words-list = "rever,numer,astroid"
154154

155155
[tool.pytest_env]
156-
# Default environment variables for tests
157-
# When running from host: DJ_HOST=localhost (set via pixi task)
156+
# Default environment variables for tests (D: prefix = only set if not defined)
157+
# When running from host: DJ_HOST=localhost (set via command line or pixi task)
158158
# When running in devcontainer/docker: DJ_HOST=db (docker-compose service name)
159-
DJ_HOST="db"
160-
DJ_PORT="3306"
161-
DJ_USER="root"
162-
DJ_PASS="password"
163-
DJ_TEST_USER="datajoint"
164-
DJ_TEST_PASSWORD="datajoint"
165-
S3_ENDPOINT="minio:9000"
166-
S3_ACCESS_KEY="datajoint"
167-
S3_SECRET_KEY="datajoint"
168-
S3_BUCKET="datajoint.test"
159+
"D:DJ_HOST" = "db"
160+
"D:DJ_PORT" = "3306"
161+
"D:DJ_USER" = "root"
162+
"D:DJ_PASS" = "password"
163+
"D:DJ_TEST_USER" = "datajoint"
164+
"D:DJ_TEST_PASSWORD" = "datajoint"
165+
"D:S3_ENDPOINT" = "minio:9000"
166+
"D:S3_ACCESS_KEY" = "datajoint"
167+
"D:S3_SECRET_KEY" = "datajoint"
168+
"D:S3_BUCKET" = "datajoint.test"
169169

170170

171171
[tool.pixi.workspace]

src/datajoint/heading.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ def decode(self, stored, *, key=None):
5050
default_attribute_properties = dict( # these default values are set in computed attributes
5151
name=None,
5252
type="expression",
53+
original_type=None, # For core types, stores the alias (e.g., "uuid") while type has db type ("binary(16)")
5354
in_key=False,
5455
nullable=False,
5556
default=None,
@@ -292,6 +293,7 @@ def _init_from_database(self):
292293
store=None,
293294
attribute_expression=None,
294295
is_hidden=attr["name"].startswith("_"),
296+
original_type=None, # May be set later for core type aliases
295297
)
296298

297299
if any(TYPE_PATTERN[t].match(attr["type"]) for t in ("INTEGER", "FLOAT")):
@@ -303,7 +305,14 @@ def _init_from_database(self):
303305
special = re.match(r":(?P<type>[^:]+):(?P<comment>.*)", attr["comment"])
304306
if special:
305307
special = special.groupdict()
306-
attr.update(special)
308+
attr["comment"] = special["comment"] # Always update the comment
309+
# Only update the type for adapted types (angle brackets)
310+
# Core types (uuid, float32, etc.) keep the database type for SQL
311+
if special["type"].startswith("<"):
312+
attr["type"] = special["type"]
313+
else:
314+
# Store the original type name for display but keep db_type for SQL
315+
attr["original_type"] = special["type"]
307316

308317
# process AttributeTypes (adapted types in angle brackets)
309318
if special and TYPE_PATTERN["ADAPTED"].match(attr["type"]):
@@ -328,18 +337,15 @@ def _init_from_database(self):
328337

329338
# Handle core type aliases (uuid, float32, etc.)
330339
if special:
340+
# Check original_type for core type detection (not attr["type"] which is now db type)
341+
original_type = attr.get("original_type", attr["type"])
331342
try:
332-
category = next(c for c in SPECIAL_TYPES if TYPE_PATTERN[c].match(attr["type"]))
343+
category = next(c for c in SPECIAL_TYPES if TYPE_PATTERN[c].match(original_type))
333344
except StopIteration:
334-
if attr["type"].startswith("external"):
335-
url = (
336-
"https://docs.datajoint.io/python/admin/5-blob-config.html"
337-
"#migration-between-datajoint-v0-11-and-v0-12"
338-
)
345+
if original_type.startswith("external"):
339346
raise DataJointError(
340-
"Legacy datatype `{type}`. Migrate your external stores to datajoint 0.12: {url}".format(
341-
url=url, **attr
342-
)
347+
f"Legacy datatype `{original_type}`. Migrate your external stores to datajoint 0.12: "
348+
"https://docs.datajoint.io/python/admin/5-blob-config.html#migration-between-datajoint-v0-11-and-v0-12"
343349
)
344350
# Not a special type - that's fine, could be native passthrough
345351
category = None

src/datajoint/table.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ class Table(QueryExpression):
7777

7878
@property
7979
def table_name(self):
80+
# For UserTable subclasses, table_name is computed by the metaclass.
81+
# Delegate to the class's table_name if _table_name is not set.
82+
if self._table_name is None:
83+
return type(self).table_name
8084
return self._table_name
8185

8286
@property

tests/test_attribute_type.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -248,9 +248,10 @@ def teardown_method(self):
248248

249249
def test_resolve_native_dtype(self):
250250
"""Test resolving a native dtype."""
251-
final_dtype, chain = resolve_dtype("longblob")
251+
final_dtype, chain, store = resolve_dtype("longblob")
252252
assert final_dtype == "longblob"
253253
assert chain == []
254+
assert store is None
254255

255256
def test_resolve_custom_dtype(self):
256257
"""Test resolving a custom dtype."""
@@ -266,10 +267,11 @@ def encode(self, value, *, key=None):
266267
def decode(self, stored, *, key=None):
267268
return stored
268269

269-
final_dtype, chain = resolve_dtype("<test_resolve>")
270+
final_dtype, chain, store = resolve_dtype("<test_resolve>")
270271
assert final_dtype == "varchar(100)"
271272
assert len(chain) == 1
272273
assert chain[0].type_name == "test_resolve"
274+
assert store is None
273275

274276
def test_resolve_chained_dtype(self):
275277
"""Test resolving a chained dtype."""
@@ -296,11 +298,12 @@ def encode(self, value, *, key=None):
296298
def decode(self, stored, *, key=None):
297299
return stored
298300

299-
final_dtype, chain = resolve_dtype("<test_outer>")
301+
final_dtype, chain, store = resolve_dtype("<test_outer>")
300302
assert final_dtype == "longblob"
301303
assert len(chain) == 2
302304
assert chain[0].type_name == "test_outer"
303305
assert chain[1].type_name == "test_inner"
306+
assert store is None
304307

305308
def test_circular_reference_detection(self):
306309
"""Test that circular type references are detected."""

tests/test_external.py

Lines changed: 0 additions & 114 deletions
This file was deleted.

tests/test_type_aliases.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import pytest
66

7-
from datajoint.declare import SQL_TYPE_ALIASES, SPECIAL_TYPES, match_type
7+
from datajoint.declare import CORE_TYPE_SQL, SPECIAL_TYPES, match_type
88

99
from .schema_type_aliases import TypeAliasTable, TypeAliasPrimaryKey, TypeAliasNullable
1010

@@ -33,7 +33,7 @@ def test_type_alias_pattern_matching(self, alias, expected_category):
3333
category = match_type(alias)
3434
assert category == expected_category
3535
assert category in SPECIAL_TYPES
36-
assert category in SQL_TYPE_ALIASES
36+
assert category in CORE_TYPE_SQL
3737

3838
@pytest.mark.parametrize(
3939
"alias,expected_mysql_type",
@@ -54,7 +54,7 @@ def test_type_alias_pattern_matching(self, alias, expected_category):
5454
def test_type_alias_mysql_mapping(self, alias, expected_mysql_type):
5555
"""Test that type aliases map to correct MySQL types."""
5656
category = match_type(alias)
57-
mysql_type = SQL_TYPE_ALIASES[category]
57+
mysql_type = CORE_TYPE_SQL[category]
5858
assert mysql_type == expected_mysql_type
5959

6060
@pytest.mark.parametrize(

0 commit comments

Comments
 (0)