Skip to content

Commit 6c29792

Browse files
fix: provide helpful error when table heading is not configured (#1324)
* fix: provide helpful error when table heading is not configured When using tables from non-activated schemas, operations that access the heading now raise a clear DataJointError instead of confusing "NoneType has no attribute" errors. Example: schema = dj.Schema() # Not activated @Schema class MyTable(dj.Manual): ... MyTable().heading # Now raises: "Table `MyTable` is not properly # configured. Ensure the schema is activated..." Closes #1039 Co-Authored-By: Claude Opus 4.5 <[email protected]> * fix: Allow heading introspection on base tier classes The heading property now returns None for base tier classes (Lookup, Manual, Imported, Computed, Part) instead of raising an error. This allows Python's help() and inspect modules to work correctly. User-defined table classes still get the helpful error message when trying to access heading on a non-activated schema. Co-Authored-By: Claude Opus 4.5 <[email protected]> --------- Co-authored-by: Claude Opus 4.5 <[email protected]>
1 parent 1aa68c0 commit 6c29792

File tree

2 files changed

+50
-0
lines changed

2 files changed

+50
-0
lines changed

src/datajoint/table.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,30 @@ def table_name(self):
120120
def class_name(self):
121121
return self.__class__.__name__
122122

123+
# Base tier class names that should not raise errors when heading is None
124+
_base_tier_classes = frozenset({"Table", "UserTable", "Lookup", "Manual", "Imported", "Computed", "Part"})
125+
126+
@property
127+
def heading(self):
128+
"""
129+
Return the table's heading, or raise a helpful error if not configured.
130+
131+
Overrides QueryExpression.heading to provide a clear error message
132+
when the table is not properly associated with an activated schema.
133+
For base tier classes (Lookup, Manual, etc.), returns None to support
134+
introspection (e.g., help()).
135+
"""
136+
if self._heading is None:
137+
# Don't raise error for base tier classes - they're used for introspection
138+
if self.__class__.__name__ in self._base_tier_classes:
139+
return None
140+
raise DataJointError(
141+
f"Table `{self.__class__.__name__}` is not properly configured. "
142+
"Ensure the schema is activated before using the table. "
143+
"Example: schema.activate('database_name') or schema = dj.Schema('database_name')"
144+
)
145+
return self._heading
146+
123147
@property
124148
def definition(self):
125149
raise NotImplementedError("Subclasses of Table must implement the `definition` property")

tests/integration/test_schema.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,32 @@ class UndecoratedClass(dj.Manual):
110110
print(a.full_table_name)
111111

112112

113+
def test_non_activated_schema_heading_error():
114+
"""
115+
Tables from non-activated schemas should raise informative errors.
116+
Regression test for issue #1039.
117+
"""
118+
# Create schema without activating (no database name)
119+
schema = dj.Schema()
120+
121+
@schema
122+
class TableA(dj.Manual):
123+
definition = """
124+
id : int
125+
---
126+
value : float
127+
"""
128+
129+
# Accessing heading should raise a helpful error
130+
instance = TableA()
131+
with pytest.raises(dj.DataJointError, match="not properly configured"):
132+
_ = instance.heading
133+
134+
# Operations that use heading should also raise helpful errors
135+
with pytest.raises(dj.DataJointError, match="not properly configured"):
136+
_ = instance.primary_key # Uses heading.primary_key
137+
138+
113139
def test_reject_decorated_part(schema_any):
114140
"""
115141
Decorating a dj.Part table should raise an informative exception.

0 commit comments

Comments
 (0)