Skip to content

Commit 0330dbe

Browse files
fix: Allow table class names with underscores (with warning)
For table class names containing underscores: 1. Emit a warning about underscores 2. Remove underscores 3. Proceed with normal CamelCase validation This restores compatibility with legacy schemas that use names like MouseScoreSheet_BodyCondition. Also adds "fetch" to supported_class_attrs for class-level access. Fixes compatibility for users migrating from DataJoint <= 0.14.1. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent d4faadc commit 0330dbe

File tree

5 files changed

+35
-11
lines changed

5 files changed

+35
-11
lines changed

src/datajoint/table.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -151,11 +151,20 @@ def declare(self, context=None):
151151
"""
152152
if self.connection.in_transaction:
153153
raise DataJointError("Cannot declare new tables inside a transaction, e.g. from inside a populate/make call")
154-
# Enforce strict CamelCase #1150
155-
if not is_camel_case(self.class_name):
154+
# Validate class name #1150
155+
class_name = self.class_name
156+
if "_" in class_name:
157+
warnings.warn(
158+
f"Table class name `{class_name}` contains underscores. "
159+
"CamelCase names without underscores are recommended.",
160+
UserWarning,
161+
stacklevel=2,
162+
)
163+
class_name = class_name.replace("_", "")
164+
if not is_camel_case(class_name):
156165
raise DataJointError(
157-
"Table class name `{name}` is invalid. Please use CamelCase. ".format(name=self.class_name)
158-
+ "Classes defining tables should be formatted in strict CamelCase."
166+
f"Table class name `{self.class_name}` is invalid. "
167+
"Class names must be in CamelCase, starting with a capital letter."
159168
)
160169
sql, _external_stores, primary_key, fk_attribute_map = declare(self.full_table_name, self.definition, context)
161170

src/datajoint/user_tables.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"to_arrow",
3333
"to_arrays",
3434
"keys",
35+
"fetch",
3536
"fetch1",
3637
"head",
3738
"tail",

src/datajoint/utils.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import re
44
import shutil
5+
import warnings
56
from pathlib import Path
67

78
from .errors import DataJointError
@@ -86,6 +87,14 @@ def from_camel_case(s):
8687
def convert(match):
8788
return ("_" if match.groups()[0] else "") + match.group(0).lower()
8889

90+
# Handle underscores: warn and remove them
91+
if "_" in s:
92+
warnings.warn(
93+
f"Table class name `{s}` contains underscores. " "CamelCase names without underscores are recommended.",
94+
UserWarning,
95+
stacklevel=3,
96+
)
97+
s = s.replace("_", "")
8998
if not is_camel_case(s):
9099
raise DataJointError("ClassName must be alphanumeric in CamelCase, begin with a capital letter")
91100
return re.sub(r"(\B[A-Z])|(\b[A-Z])", convert, s)

tests/integration/test_declare.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -348,8 +348,8 @@ class IndexAttribute(dj.Manual):
348348

349349
def test_table_name_with_underscores(schema_any):
350350
"""
351-
Test issue #1150 -- Reject table names containing underscores. Tables should be in strict
352-
CamelCase.
351+
Test issue #1150 -- Table names with underscores should produce a warning but still work.
352+
Strict CamelCase is recommended.
353353
"""
354354

355355
class TableNoUnderscores(dj.Manual):
@@ -363,5 +363,8 @@ class Table_With_Underscores(dj.Manual):
363363
"""
364364

365365
schema_any(TableNoUnderscores)
366-
with pytest.raises(dj.DataJointError, match="must be alphanumeric in CamelCase"):
366+
# Underscores now produce a warning instead of an error (legacy support)
367+
with pytest.warns(UserWarning, match="contains underscores"):
367368
schema_any(Table_With_Underscores)
369+
# Verify the table was created successfully
370+
assert Table_With_Underscores.is_declared

tests/integration/test_utils.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,14 @@ def test_from_camel_case():
2626
assert from_camel_case("AllGroups") == "all_groups"
2727
with pytest.raises(DataJointError):
2828
from_camel_case("repNames")
29-
with pytest.raises(DataJointError):
30-
from_camel_case("10_all")
29+
with pytest.warns(UserWarning, match="contains underscores"):
30+
with pytest.raises(DataJointError):
31+
from_camel_case("10_all")
3132
with pytest.raises(DataJointError):
3233
from_camel_case("hello world")
33-
with pytest.raises(DataJointError):
34-
from_camel_case("#baisc_names")
34+
with pytest.warns(UserWarning, match="contains underscores"):
35+
with pytest.raises(DataJointError):
36+
from_camel_case("#baisc_names")
3537

3638

3739
def test_to_camel_case():

0 commit comments

Comments
 (0)