Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions src/datajoint/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,30 @@ def table_name(self):
def class_name(self):
return self.__class__.__name__

# Base tier class names that should not raise errors when heading is None
_base_tier_classes = frozenset({"Table", "UserTable", "Lookup", "Manual", "Imported", "Computed", "Part"})

@property
def heading(self):
"""
Return the table's heading, or raise a helpful error if not configured.

Overrides QueryExpression.heading to provide a clear error message
when the table is not properly associated with an activated schema.
For base tier classes (Lookup, Manual, etc.), returns None to support
introspection (e.g., help()).
"""
if self._heading is None:
# Don't raise error for base tier classes - they're used for introspection
if self.__class__.__name__ in self._base_tier_classes:
return None
raise DataJointError(
f"Table `{self.__class__.__name__}` is not properly configured. "
"Ensure the schema is activated before using the table. "
"Example: schema.activate('database_name') or schema = dj.Schema('database_name')"
)
return self._heading

@property
def definition(self):
raise NotImplementedError("Subclasses of Table must implement the `definition` property")
Expand Down
26 changes: 26 additions & 0 deletions tests/integration/test_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,32 @@ class UndecoratedClass(dj.Manual):
print(a.full_table_name)


def test_non_activated_schema_heading_error():
"""
Tables from non-activated schemas should raise informative errors.
Regression test for issue #1039.
"""
# Create schema without activating (no database name)
schema = dj.Schema()

@schema
class TableA(dj.Manual):
definition = """
id : int
---
value : float
"""

# Accessing heading should raise a helpful error
instance = TableA()
with pytest.raises(dj.DataJointError, match="not properly configured"):
_ = instance.heading

# Operations that use heading should also raise helpful errors
with pytest.raises(dj.DataJointError, match="not properly configured"):
_ = instance.primary_key # Uses heading.primary_key


def test_reject_decorated_part(schema_any):
"""
Decorating a dj.Part table should raise an informative exception.
Expand Down
Loading