Skip to content

Commit bcebfc8

Browse files
aaronweissberg-labAndreasAlbertQCjjurm
authored
feat: Add type error for pathological non-Column members in Schema (#159)
Co-authored-by: Andreas Albert <[email protected]> Co-authored-by: Juraj Mičko <[email protected]>
1 parent f9e67e1 commit bcebfc8

File tree

2 files changed

+54
-0
lines changed

2 files changed

+54
-0
lines changed

dataframely/_base_schema.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,29 @@ def __new__(
137137
f"which are not in the schema: {missing_list}."
138138
)
139139

140+
# 3) Check that all members are non-pathological (i.e., user errors).
141+
for attr, value in namespace.items():
142+
if attr.startswith("__"):
143+
continue
144+
# Check for tuple of column (commonly caused by trailing comma)
145+
if (
146+
isinstance(value, tuple)
147+
and len(value) > 0
148+
and isinstance(value[0], Column)
149+
):
150+
raise TypeError(
151+
f"Column '{attr}' is defined as a tuple of dy.Column. "
152+
f"Did you accidentally add a trailing comma?"
153+
)
154+
155+
# Check for column type instead of instance (e.g., dy.Float64 instead of dy.Float64())
156+
if isinstance(value, type) and issubclass(value, Column):
157+
raise TypeError(
158+
f"Column '{attr}' is a type, not an instance. "
159+
f"Schema members must be of type Column not type[Column]. "
160+
f"Did you forget to add parentheses?"
161+
)
162+
140163
return super().__new__(mcs, name, bases, namespace, *args, **kwargs)
141164

142165
def __getattribute__(cls, name: str) -> Any:

tests/schema/test_base.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,34 @@ def test_dunder_name() -> None:
9595

9696
def test_dunder_name_with_rule() -> None:
9797
assert MySchemaWithRule.__name__ == "MySchemaWithRule"
98+
99+
100+
def test_non_column_member_is_allowed() -> None:
101+
class MySchemaWithNonColumnMembers(dy.Schema):
102+
a = dy.Int32(nullable=False)
103+
version: int = 1
104+
useful_tuple: tuple[int, int] = (1, 2)
105+
106+
columns = MySchemaWithNonColumnMembers.columns()
107+
assert "a" in columns
108+
assert "version" not in columns
109+
assert "useful_tuple" not in columns
110+
assert MySchemaWithNonColumnMembers.version == 1
111+
assert MySchemaWithNonColumnMembers.useful_tuple == (1, 2)
112+
113+
114+
def test_user_error_tuple_column() -> None:
115+
with pytest.raises(TypeError, match="tuple"):
116+
117+
class MySchemaWithTupleOfColumn(dy.Schema):
118+
a = dy.Int32(nullable=False)
119+
b = (dy.Int32(nullable=False),) # User error: Trailing comma = tuple
120+
c = dy.Int32(nullable=False)
121+
122+
123+
def test_user_error_column_type_not_instance() -> None:
124+
with pytest.raises(TypeError, match="type, not an instance"):
125+
126+
class MySchemaWithColumnTypeNotInstance(dy.Schema):
127+
a = dy.Int32(nullable=False, primary_key=True)
128+
b = dy.Float64 # User error: Forgot parentheses!

0 commit comments

Comments
 (0)