Skip to content

Commit 488c11e

Browse files
committed
Add tests for UUID/Annotated type fix in get_column_python_type
1 parent 02789f0 commit 488c11e

2 files changed

Lines changed: 59 additions & 6 deletions

File tree

sqladmin/helpers.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
Callable,
1515
Generator,
1616
TypeVar,
17+
get_args,
18+
get_origin,
1719
)
1820

1921
from sqlalchemy import Column
@@ -255,14 +257,20 @@ def get_direction(prop: MODEL_PROPERTY) -> str:
255257

256258
def get_column_python_type(column: Column) -> type:
257259
try:
258-
return column.type.python_type
260+
python_type = column.type.python_type
259261
except NotImplementedError:
260262
if hasattr(column.type, "impl"):
261263
try:
262-
return column.type.impl.python_type
264+
python_type = column.type.impl.python_type
263265
except NotImplementedError:
264-
...
265-
return str
266+
return str
267+
else:
268+
return str
269+
270+
if get_origin(python_type) is not None:
271+
python_type = get_args(python_type)[0]
272+
273+
return python_type
266274

267275

268276
def is_relationship(prop: MODEL_PROPERTY) -> bool:

tests/test_helpers.py

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1+
import uuid
12
from datetime import date, datetime, time, timedelta, timezone
2-
from typing import Any
3+
from typing import Annotated, Any
4+
from unittest.mock import MagicMock, PropertyMock
35

46
import pytest
5-
from sqlalchemy import Column, Date, DateTime, ForeignKey, Integer, String, Time
7+
from sqlalchemy import Column, Date, DateTime, ForeignKey, Integer, String, Time, Uuid
68
from sqlalchemy.orm import declarative_base
79

810
from sqladmin.helpers import (
11+
get_column_python_type,
912
get_object_identifier,
1013
is_falsy_value,
1114
object_identifier_values,
@@ -147,3 +150,45 @@ def test_case(ident):
147150

148151
test_case("Missing;1")
149152
test_case("Johnson;7;A;Extra")
153+
154+
155+
class UUIDPKModel(Base):
156+
__tablename__ = "uuid_pk_model"
157+
id = Column(Uuid, primary_key=True)
158+
159+
160+
def test_get_column_python_type_with_uuid_pk():
161+
"""Regression #981: must not raise TypeError when python_type
162+
returns a type annotation instead of a plain class."""
163+
pk = UUIDPKModel.__table__.c["id"]
164+
result = get_column_python_type(pk)
165+
assert callable(result)
166+
167+
168+
def test_get_column_python_type_annotated_type_no_typeerror():
169+
"""When python_type returns Annotated[uuid.UUID, ...], issubclass
170+
must not be called on it — no TypeError should be raised."""
171+
mock_col = MagicMock()
172+
mock_col.type.python_type = Annotated[uuid.UUID, "meta"]
173+
# Before the fix: TypeError: issubclass() arg 1 must be a class
174+
result = get_column_python_type(mock_col)
175+
assert callable(result)
176+
177+
178+
def test_get_column_python_type_annotated_returns_origin():
179+
"""When python_type is Annotated[uuid.UUID, ...], the returned
180+
type should resolve to the origin class (uuid.UUID)."""
181+
mock_col = MagicMock()
182+
mock_col.type.python_type = Annotated[uuid.UUID, "meta"]
183+
result = get_column_python_type(mock_col)
184+
assert result is uuid.UUID
185+
186+
187+
def test_get_column_python_type_not_implemented_no_impl():
188+
"""Falls back to str when python_type raises NotImplementedError
189+
and there is no impl."""
190+
mock_col = MagicMock(spec=["type"])
191+
t = MagicMock(spec=["python_type"])
192+
type(t).python_type = PropertyMock(side_effect=NotImplementedError)
193+
mock_col.type = t
194+
assert get_column_python_type(mock_col) is str

0 commit comments

Comments
 (0)