Skip to content

Commit

Permalink
Merge pull request #1152 from datajoint/dev-1150-underscores
Browse files Browse the repository at this point in the history
Fix #1150
  • Loading branch information
dimitri-yatsenko authored Apr 11, 2024
2 parents f357955 + f54cd5c commit b42b239
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 3 deletions.
14 changes: 13 additions & 1 deletion datajoint/table.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from .condition import make_condition
from .expression import QueryExpression
from . import blob
from .utils import user_choice, get_master
from .utils import user_choice, get_master, is_camel_case
from .heading import Heading
from .errors import (
DuplicateError,
Expand Down Expand Up @@ -75,6 +75,10 @@ class Table(QueryExpression):
def table_name(self):
return self._table_name

@property
def class_name(self):
return self.__class__.__name__

@property
def definition(self):
raise NotImplementedError(
Expand All @@ -93,6 +97,14 @@ def declare(self, context=None):
"Cannot declare new tables inside a transaction, "
"e.g. from inside a populate/make call"
)
# Enforce strict CamelCase #1150
if not is_camel_case(self.class_name):
raise DataJointError(
"Table class name `{name}` is invalid. Please use CamelCase. ".format(
name=self.class_name
)
+ "Classes defining tables should be formatted in strict CamelCase."
)
sql, external_stores = declare(self.full_table_name, self.definition, context)
sql = sql.format(database=self.database)
try:
Expand Down
15 changes: 14 additions & 1 deletion datajoint/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,19 @@ def get_master(full_table_name: str) -> str:
return match["master"] + "`" if match else ""


def is_camel_case(s):
"""
Check if a string is in CamelCase notation.
:param s: string to check
:returns: True if the string is in CamelCase notation, False otherwise
Example:
>>> is_camel_case("TableName") # returns True
>>> is_camel_case("table_name") # returns False
"""
return bool(re.match(r"^[A-Z][A-Za-z0-9]*$", s))


def to_camel_case(s):
"""
Convert names with under score (_) separation into camel case names.
Expand Down Expand Up @@ -82,7 +95,7 @@ def from_camel_case(s):
def convert(match):
return ("_" if match.groups()[0] else "") + match.group(0).lower()

if not re.match(r"[A-Z][a-zA-Z0-9]*", s):
if not is_camel_case(s):
raise DataJointError(
"ClassName must be alphanumeric in CamelCase, begin with a capital letter"
)
Expand Down
23 changes: 23 additions & 0 deletions tests/test_declare.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,3 +337,26 @@ class WithSuchALongPartNameThatItCrashesMySQL(dj.Part):

with pytest.raises(dj.DataJointError):
schema_any(WhyWouldAnyoneCreateATableNameThisLong)


def test_table_name_with_underscores(schema_any):
"""
Test issue #1150 -- Reject table names containing underscores. Tables should be in strict
CamelCase.
"""

class TableNoUnderscores(dj.Manual):
definition = """
id : int
"""

class Table_With_Underscores(dj.Manual):
definition = """
id : int
"""

schema_any(TableNoUnderscores)
with pytest.raises(
dj.DataJointError, match="must be alphanumeric in CamelCase"
) as e:
schema_any(Table_With_Underscores)
20 changes: 19 additions & 1 deletion tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,28 @@
"""

from datajoint import DataJointError
from datajoint.utils import from_camel_case, to_camel_case
from datajoint.utils import (
from_camel_case,
to_camel_case,
is_camel_case,
)
import pytest


def test_is_camel_case():
assert is_camel_case("AllGroups")
assert not is_camel_case("All_Groups")
assert not is_camel_case("All_Groups_")
assert not is_camel_case("_AllGroups")
assert not is_camel_case("allGroups")
assert not is_camel_case("repNames")
assert not is_camel_case("10_all")
assert not is_camel_case("hello world")
assert not is_camel_case("#baisc_names")
assert not is_camel_case("alphaBeta")
assert not is_camel_case("TestΣ")


def test_from_camel_case():
assert from_camel_case("AllGroups") == "all_groups"
with pytest.raises(DataJointError):
Expand Down

0 comments on commit b42b239

Please sign in to comment.