sqlalchemy-cloudflare-d1 0.3.9__tar.gz → 0.3.10__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/CHANGELOG.md +11 -0
  2. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/PKG-INFO +1 -1
  3. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/examples/workers/src/entry.py +499 -0
  4. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/examples/workers/uv.lock +1 -1
  5. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/pyproject.toml +1 -1
  6. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/src/sqlalchemy_cloudflare_d1/__init__.py +3 -1
  7. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/src/sqlalchemy_cloudflare_d1/compiler.py +54 -34
  8. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/src/sqlalchemy_cloudflare_d1/connection.py +30 -26
  9. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/src/sqlalchemy_cloudflare_d1/dialect.py +90 -11
  10. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/src/sqlalchemy_cloudflare_d1/dialect_async.py +31 -39
  11. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/tests/integration/test_restapi_integration.py +301 -0
  12. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/tests/integration/test_worker_integration.py +127 -0
  13. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/uv.lock +1 -1
  14. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/.github/DEVELOPMENT.md +0 -0
  15. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/.github/workflows/ci.yml +0 -0
  16. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/.github/workflows/release.yml +0 -0
  17. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/.gitignore +0 -0
  18. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/.pre-commit-config.yaml +0 -0
  19. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/.python-version +0 -0
  20. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/LICENSE +0 -0
  21. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/Makefile +0 -0
  22. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/README.md +0 -0
  23. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/assets/d1-logo.png +0 -0
  24. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/assets/sqlalchemy-logo.png +0 -0
  25. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/examples/restapi/example.py +0 -0
  26. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/examples/workers/README.md +0 -0
  27. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/examples/workers/db_init.sql +0 -0
  28. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/examples/workers/pyproject.toml +0 -0
  29. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/examples/workers/wrangler.jsonc +0 -0
  30. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/src/sqlalchemy_cloudflare_d1/py.typed +0 -0
  31. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/tests/__init__.py +0 -0
  32. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/tests/conftest.py +0 -0
  33. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/tests/integration/test_base_url_integration.py +0 -0
  34. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/tests/test_utils.py +0 -0
  35. {sqlalchemy_cloudflare_d1-0.3.9 → sqlalchemy_cloudflare_d1-0.3.10}/tests/unit/test_dialect.py +0 -0
@@ -13,6 +13,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
13
13
  ### Fixed
14
14
 
15
15
 
16
+ ## [0.3.10]
17
+
18
+ ### Added
19
+
20
+ - `UUID` and `Enum` column type support ([#24](https://github.com/CollierKing/sqlalchemy-cloudflare-d1/issues/24))
21
+ - Added `D1UUID` type processor that converts `uuid.UUID` objects to hyphenated strings on bind and parses them back on result; supports UUID as primary key and nullable columns
22
+ - Added `D1Enum` type processor that converts Python `enum.Enum` objects to their string values on bind and reconstructs them on result; supports both string-value enums and Python enum classes
23
+ - Both types store as `TEXT` in D1 (SQLite-compatible)
24
+ - Works in both REST API and Worker modes
25
+
26
+
16
27
  ## [0.3.9]
17
28
 
18
29
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlalchemy-cloudflare-d1
3
- Version: 0.3.9
3
+ Version: 0.3.10
4
4
  Summary: A SQLAlchemy dialect for Cloudflare's D1 Serverless SQLite Database
5
5
  Project-URL: Homepage, https://github.com/collierking/sqlalchemy-cloudflare-d1
6
6
  Project-URL: Repository, https://github.com/collierking/sqlalchemy-cloudflare-d1
@@ -145,6 +145,24 @@ class Default(WorkerEntrypoint):
145
145
  return await self.test_time_nullable()
146
146
  elif path == "time-orm":
147
147
  return await self.test_time_orm()
148
+ # UUID tests (GitHub issue #24)
149
+ elif path == "uuid-basic":
150
+ return await self.test_uuid_basic()
151
+ elif path == "uuid-nullable":
152
+ return await self.test_uuid_nullable()
153
+ elif path == "uuid-orm":
154
+ return await self.test_uuid_orm()
155
+ # Enum tests (GitHub issue #24)
156
+ elif path == "enum-basic":
157
+ return await self.test_enum_basic()
158
+ elif path == "enum-python-class":
159
+ return await self.test_enum_python_class()
160
+ elif path == "enum-nullable":
161
+ return await self.test_enum_nullable()
162
+ elif path == "enum-orm":
163
+ return await self.test_enum_orm()
164
+ elif path == "uuid-pk":
165
+ return await self.test_uuid_pk()
148
166
  # Parallel query tests (GitHub issue #20)
149
167
  elif path == "parallel-queries-engine":
150
168
  return await self.test_parallel_queries_engine()
@@ -3642,6 +3660,487 @@ class Default(WorkerEntrypoint):
3642
3660
  status=500,
3643
3661
  )
3644
3662
 
3663
+ # MARK: - UUID Tests (GitHub issue #24)
3664
+
3665
+ async def test_uuid_basic(self):
3666
+ """Test UUID column insert and retrieve."""
3667
+ import uuid as uuid_module
3668
+ from sqlalchemy import Column, Integer, MetaData, String, Table, Uuid, select
3669
+
3670
+ table_name = f"test_uuid_{uuid_module.uuid4().hex[:8]}"
3671
+
3672
+ try:
3673
+ engine = self.get_engine()
3674
+ metadata = MetaData()
3675
+
3676
+ test_table = Table(
3677
+ table_name,
3678
+ metadata,
3679
+ Column("id", Integer, primary_key=True),
3680
+ Column("title", String(127)),
3681
+ Column("item_uuid", Uuid(as_uuid=True)),
3682
+ )
3683
+
3684
+ metadata.create_all(engine)
3685
+
3686
+ test_uuid = uuid_module.uuid4()
3687
+
3688
+ with engine.connect() as conn:
3689
+ conn.execute(
3690
+ test_table.insert().values(title="Test", item_uuid=test_uuid)
3691
+ )
3692
+ conn.commit()
3693
+
3694
+ result = conn.execute(
3695
+ select(test_table.c.title, test_table.c.item_uuid)
3696
+ )
3697
+ row = result.fetchone()
3698
+
3699
+ metadata.drop_all(engine)
3700
+
3701
+ success = (
3702
+ row is not None
3703
+ and row[0] == "Test"
3704
+ and isinstance(row[1], uuid_module.UUID)
3705
+ and row[1] == test_uuid
3706
+ )
3707
+
3708
+ return Response.json(
3709
+ {
3710
+ "test": "uuid_basic",
3711
+ "success": success,
3712
+ "uuid_type": type(row[1]).__name__ if row else None,
3713
+ "uuid_match": row[1] == test_uuid if row else False,
3714
+ }
3715
+ )
3716
+ except Exception as e:
3717
+ try:
3718
+ metadata.drop_all(engine)
3719
+ except Exception:
3720
+ pass
3721
+ return Response.json(
3722
+ {"test": "uuid_basic", "success": False, "error": str(e)},
3723
+ status=500,
3724
+ )
3725
+
3726
+ async def test_uuid_nullable(self):
3727
+ """Test nullable UUID columns handle NULL correctly."""
3728
+ import uuid as uuid_module
3729
+ from sqlalchemy import Column, Integer, MetaData, String, Table, Uuid, select
3730
+
3731
+ table_name = f"test_uuid_null_{uuid_module.uuid4().hex[:8]}"
3732
+
3733
+ try:
3734
+ engine = self.get_engine()
3735
+ metadata = MetaData()
3736
+
3737
+ test_table = Table(
3738
+ table_name,
3739
+ metadata,
3740
+ Column("id", Integer, primary_key=True),
3741
+ Column("title", String(127)),
3742
+ Column("item_uuid", Uuid(as_uuid=True), nullable=True),
3743
+ )
3744
+
3745
+ metadata.create_all(engine)
3746
+
3747
+ with engine.connect() as conn:
3748
+ conn.execute(
3749
+ test_table.insert().values(
3750
+ title="With UUID", item_uuid=uuid_module.uuid4()
3751
+ )
3752
+ )
3753
+ conn.execute(
3754
+ test_table.insert().values(title="No UUID", item_uuid=None)
3755
+ )
3756
+ conn.commit()
3757
+
3758
+ result = conn.execute(
3759
+ select(test_table.c.title, test_table.c.item_uuid).order_by(
3760
+ test_table.c.id
3761
+ )
3762
+ )
3763
+ rows = result.fetchall()
3764
+
3765
+ metadata.drop_all(engine)
3766
+
3767
+ success = (
3768
+ len(rows) == 2
3769
+ and isinstance(rows[0][1], uuid_module.UUID)
3770
+ and rows[1][1] is None
3771
+ )
3772
+
3773
+ return Response.json(
3774
+ {
3775
+ "test": "uuid_nullable",
3776
+ "success": success,
3777
+ "with_uuid_is_uuid": isinstance(rows[0][1], uuid_module.UUID)
3778
+ if rows
3779
+ else False,
3780
+ "no_uuid_is_none": rows[1][1] is None if len(rows) > 1 else False,
3781
+ }
3782
+ )
3783
+ except Exception as e:
3784
+ try:
3785
+ metadata.drop_all(engine)
3786
+ except Exception:
3787
+ pass
3788
+ return Response.json(
3789
+ {"test": "uuid_nullable", "success": False, "error": str(e)},
3790
+ status=500,
3791
+ )
3792
+
3793
+ async def test_uuid_orm(self):
3794
+ """Test UUID via ORM session."""
3795
+ import uuid as uuid_module
3796
+ from sqlalchemy import Integer, String, Uuid
3797
+ from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, Session
3798
+
3799
+ engine = create_engine_from_binding(self.env.DB)
3800
+ table_name = f"test_uuid_orm_{uuid_module.uuid4().hex[:8]}"
3801
+
3802
+ try:
3803
+
3804
+ class Base(DeclarativeBase):
3805
+ pass
3806
+
3807
+ class Item(Base):
3808
+ __tablename__ = table_name
3809
+ id: Mapped[int] = mapped_column(Integer, primary_key=True)
3810
+ title: Mapped[str] = mapped_column(String(127))
3811
+ item_uuid: Mapped[uuid_module.UUID] = mapped_column(Uuid(as_uuid=True))
3812
+
3813
+ Base.metadata.create_all(engine)
3814
+
3815
+ test_uuid = uuid_module.uuid4()
3816
+
3817
+ with Session(engine) as session:
3818
+ item = Item(title="UUID ORM Test", item_uuid=test_uuid)
3819
+ session.add(item)
3820
+ session.commit()
3821
+ session.refresh(item)
3822
+ entry_title = item.title
3823
+ uuid_is_uuid = isinstance(item.item_uuid, uuid_module.UUID)
3824
+ uuid_match = item.item_uuid == test_uuid
3825
+
3826
+ Base.metadata.drop_all(engine)
3827
+
3828
+ return Response.json(
3829
+ {
3830
+ "test": "uuid_orm",
3831
+ "success": uuid_is_uuid and uuid_match,
3832
+ "entry_title": entry_title,
3833
+ "uuid_is_uuid": uuid_is_uuid,
3834
+ "uuid_match": uuid_match,
3835
+ }
3836
+ )
3837
+ except Exception as e:
3838
+ try:
3839
+ from sqlalchemy import MetaData, Table
3840
+
3841
+ md = MetaData()
3842
+ Table(table_name, md)
3843
+ md.drop_all(engine)
3844
+ except Exception:
3845
+ pass
3846
+ return Response.json(
3847
+ {"test": "uuid_orm", "success": False, "error": str(e)},
3848
+ status=500,
3849
+ )
3850
+
3851
+ async def test_uuid_pk(self):
3852
+ """Test UUID used as primary key."""
3853
+ import uuid as uuid_module
3854
+ from sqlalchemy import Column, MetaData, String, Table, Uuid, select
3855
+
3856
+ table_name = f"test_uuid_pk_{uuid_module.uuid4().hex[:8]}"
3857
+
3858
+ try:
3859
+ engine = self.get_engine()
3860
+ metadata = MetaData()
3861
+
3862
+ test_table = Table(
3863
+ table_name,
3864
+ metadata,
3865
+ Column("id", Uuid(as_uuid=True), primary_key=True),
3866
+ Column("title", String(127)),
3867
+ )
3868
+
3869
+ metadata.create_all(engine)
3870
+
3871
+ pk_uuid = uuid_module.uuid4()
3872
+
3873
+ with engine.connect() as conn:
3874
+ conn.execute(test_table.insert().values(id=pk_uuid, title="PK Test"))
3875
+ conn.commit()
3876
+
3877
+ result = conn.execute(
3878
+ select(test_table).where(test_table.c.id == pk_uuid)
3879
+ )
3880
+ row = result.fetchone()
3881
+
3882
+ metadata.drop_all(engine)
3883
+
3884
+ success = (
3885
+ row is not None
3886
+ and isinstance(row[0], uuid_module.UUID)
3887
+ and row[0] == pk_uuid
3888
+ and row[1] == "PK Test"
3889
+ )
3890
+
3891
+ return Response.json(
3892
+ {
3893
+ "test": "uuid_pk",
3894
+ "success": success,
3895
+ "pk_is_uuid": isinstance(row[0], uuid_module.UUID)
3896
+ if row
3897
+ else False,
3898
+ "pk_match": row[0] == pk_uuid if row else False,
3899
+ }
3900
+ )
3901
+ except Exception as e:
3902
+ try:
3903
+ metadata.drop_all(engine)
3904
+ except Exception:
3905
+ pass
3906
+ return Response.json(
3907
+ {"test": "uuid_pk", "success": False, "error": str(e)},
3908
+ status=500,
3909
+ )
3910
+
3911
+ # MARK: - Enum Tests (GitHub issue #24)
3912
+
3913
+ async def test_enum_basic(self):
3914
+ """Test Enum column with string values insert and retrieve."""
3915
+ import uuid as uuid_module
3916
+ from sqlalchemy import Column, Enum, Integer, MetaData, String, Table, select
3917
+
3918
+ table_name = f"test_enum_{uuid_module.uuid4().hex[:8]}"
3919
+
3920
+ try:
3921
+ engine = self.get_engine()
3922
+ metadata = MetaData()
3923
+
3924
+ test_table = Table(
3925
+ table_name,
3926
+ metadata,
3927
+ Column("id", Integer, primary_key=True),
3928
+ Column("title", String(127)),
3929
+ Column("status", Enum("active", "inactive", "pending")),
3930
+ )
3931
+
3932
+ metadata.create_all(engine)
3933
+
3934
+ with engine.connect() as conn:
3935
+ conn.execute(test_table.insert().values(title="Test", status="active"))
3936
+ conn.commit()
3937
+
3938
+ result = conn.execute(select(test_table.c.title, test_table.c.status))
3939
+ row = result.fetchone()
3940
+
3941
+ metadata.drop_all(engine)
3942
+
3943
+ success = row is not None and row[0] == "Test" and row[1] == "active"
3944
+
3945
+ return Response.json(
3946
+ {
3947
+ "test": "enum_basic",
3948
+ "success": success,
3949
+ "status_value": row[1] if row else None,
3950
+ "status_type": type(row[1]).__name__ if row else None,
3951
+ }
3952
+ )
3953
+ except Exception as e:
3954
+ try:
3955
+ metadata.drop_all(engine)
3956
+ except Exception:
3957
+ pass
3958
+ return Response.json(
3959
+ {"test": "enum_basic", "success": False, "error": str(e)},
3960
+ status=500,
3961
+ )
3962
+
3963
+ async def test_enum_python_class(self):
3964
+ """Test Enum column with Python enum.Enum class."""
3965
+ import enum as enum_module
3966
+ import uuid as uuid_module
3967
+ from sqlalchemy import Column, Enum, Integer, MetaData, String, Table, select
3968
+
3969
+ class Status(enum_module.Enum):
3970
+ active = "active"
3971
+ inactive = "inactive"
3972
+ pending = "pending"
3973
+
3974
+ table_name = f"test_enum_cls_{uuid_module.uuid4().hex[:8]}"
3975
+
3976
+ try:
3977
+ engine = self.get_engine()
3978
+ metadata = MetaData()
3979
+
3980
+ test_table = Table(
3981
+ table_name,
3982
+ metadata,
3983
+ Column("id", Integer, primary_key=True),
3984
+ Column("title", String(127)),
3985
+ Column("status", Enum(Status)),
3986
+ )
3987
+
3988
+ metadata.create_all(engine)
3989
+
3990
+ with engine.connect() as conn:
3991
+ conn.execute(
3992
+ test_table.insert().values(title="Test", status=Status.active)
3993
+ )
3994
+ conn.commit()
3995
+
3996
+ result = conn.execute(select(test_table.c.title, test_table.c.status))
3997
+ row = result.fetchone()
3998
+
3999
+ metadata.drop_all(engine)
4000
+
4001
+ success = row is not None and row[0] == "Test" and row[1] == Status.active
4002
+
4003
+ return Response.json(
4004
+ {
4005
+ "test": "enum_python_class",
4006
+ "success": success,
4007
+ "status_is_enum": isinstance(row[1], Status) if row else False,
4008
+ "status_value": row[1].value
4009
+ if row and isinstance(row[1], Status)
4010
+ else str(row[1])
4011
+ if row
4012
+ else None,
4013
+ }
4014
+ )
4015
+ except Exception as e:
4016
+ try:
4017
+ metadata.drop_all(engine)
4018
+ except Exception:
4019
+ pass
4020
+ return Response.json(
4021
+ {"test": "enum_python_class", "success": False, "error": str(e)},
4022
+ status=500,
4023
+ )
4024
+
4025
+ async def test_enum_nullable(self):
4026
+ """Test nullable Enum columns handle NULL correctly."""
4027
+ import uuid as uuid_module
4028
+ from sqlalchemy import Column, Enum, Integer, MetaData, String, Table, select
4029
+
4030
+ table_name = f"test_enum_null_{uuid_module.uuid4().hex[:8]}"
4031
+
4032
+ try:
4033
+ engine = self.get_engine()
4034
+ metadata = MetaData()
4035
+
4036
+ test_table = Table(
4037
+ table_name,
4038
+ metadata,
4039
+ Column("id", Integer, primary_key=True),
4040
+ Column("title", String(127)),
4041
+ Column("status", Enum("active", "inactive"), nullable=True),
4042
+ )
4043
+
4044
+ metadata.create_all(engine)
4045
+
4046
+ with engine.connect() as conn:
4047
+ conn.execute(
4048
+ test_table.insert().values(title="With Status", status="active")
4049
+ )
4050
+ conn.execute(test_table.insert().values(title="No Status", status=None))
4051
+ conn.commit()
4052
+
4053
+ result = conn.execute(
4054
+ select(test_table.c.title, test_table.c.status).order_by(
4055
+ test_table.c.id
4056
+ )
4057
+ )
4058
+ rows = result.fetchall()
4059
+
4060
+ metadata.drop_all(engine)
4061
+
4062
+ success = len(rows) == 2 and rows[0][1] == "active" and rows[1][1] is None
4063
+
4064
+ return Response.json(
4065
+ {
4066
+ "test": "enum_nullable",
4067
+ "success": success,
4068
+ "with_status_value": rows[0][1] if rows else None,
4069
+ "no_status_is_none": rows[1][1] is None if len(rows) > 1 else False,
4070
+ }
4071
+ )
4072
+ except Exception as e:
4073
+ try:
4074
+ metadata.drop_all(engine)
4075
+ except Exception:
4076
+ pass
4077
+ return Response.json(
4078
+ {"test": "enum_nullable", "success": False, "error": str(e)},
4079
+ status=500,
4080
+ )
4081
+
4082
+ async def test_enum_orm(self):
4083
+ """Test Enum via ORM session with Python enum class."""
4084
+ import enum as enum_module
4085
+ import uuid as uuid_module
4086
+ from sqlalchemy import Enum, Integer, String
4087
+ from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, Session
4088
+
4089
+ class Priority(enum_module.Enum):
4090
+ low = "low"
4091
+ medium = "medium"
4092
+ high = "high"
4093
+
4094
+ engine = create_engine_from_binding(self.env.DB)
4095
+ table_name = f"test_enum_orm_{uuid_module.uuid4().hex[:8]}"
4096
+
4097
+ try:
4098
+
4099
+ class Base(DeclarativeBase):
4100
+ pass
4101
+
4102
+ class Task(Base):
4103
+ __tablename__ = table_name
4104
+ id: Mapped[int] = mapped_column(Integer, primary_key=True)
4105
+ title: Mapped[str] = mapped_column(String(127))
4106
+ priority: Mapped[Priority] = mapped_column(Enum(Priority))
4107
+
4108
+ Base.metadata.create_all(engine)
4109
+
4110
+ with Session(engine) as session:
4111
+ task = Task(title="Enum ORM Task", priority=Priority.high)
4112
+ session.add(task)
4113
+ session.commit()
4114
+ session.refresh(task)
4115
+ entry_title = task.title
4116
+ priority_is_enum = isinstance(task.priority, Priority)
4117
+ priority_match = task.priority == Priority.high
4118
+
4119
+ Base.metadata.drop_all(engine)
4120
+
4121
+ return Response.json(
4122
+ {
4123
+ "test": "enum_orm",
4124
+ "success": priority_is_enum and priority_match,
4125
+ "entry_title": entry_title,
4126
+ "priority_is_enum": priority_is_enum,
4127
+ "priority_match": priority_match,
4128
+ }
4129
+ )
4130
+ except Exception as e:
4131
+ try:
4132
+ from sqlalchemy import MetaData, Table
4133
+
4134
+ md = MetaData()
4135
+ Table(table_name, md)
4136
+ md.drop_all(engine)
4137
+ except Exception:
4138
+ pass
4139
+ return Response.json(
4140
+ {"test": "enum_orm", "success": False, "error": str(e)},
4141
+ status=500,
4142
+ )
4143
+
3645
4144
  # MARK: - Parallel Query Tests (GitHub issue #20)
3646
4145
 
3647
4146
  async def test_parallel_queries_engine(self):
@@ -614,7 +614,7 @@ wheels = [
614
614
 
615
615
  [[package]]
616
616
  name = "sqlalchemy-cloudflare-d1"
617
- version = "0.3.8"
617
+ version = "0.3.10"
618
618
  source = { editable = "../../" }
619
619
  dependencies = [
620
620
  { name = "httpx" },
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sqlalchemy-cloudflare-d1"
3
- version = "0.3.9"
3
+ version = "0.3.10"
4
4
  description = "A SQLAlchemy dialect for Cloudflare's D1 Serverless SQLite Database"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -15,6 +15,8 @@ Async usage (requires greenlet: pip install sqlalchemy-cloudflare-d1[async]):
15
15
  engine = create_async_engine("cloudflare_d1+async://account_id:api_token@database_id")
16
16
  """
17
17
 
18
+ from typing import Any
19
+
18
20
  from .dialect import CloudflareD1Dialect
19
21
  from .connection import (
20
22
  # Sync classes
@@ -47,7 +49,7 @@ from .connection import (
47
49
  )
48
50
 
49
51
 
50
- def __getattr__(name: str):
52
+ def __getattr__(name: str) -> Any:
51
53
  """Lazy import for async dialect to avoid requiring greenlet at import time.
52
54
 
53
55
  The async SQLAlchemy dialect (CloudflareD1Dialect_async) requires greenlet