summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorFederico Caselli <cfederico87@gmail.com>2023-04-12 21:21:36 +0000
committerGerrit Code Review <gerrit@bbpush.zzzcomputing.com>2023-04-12 21:21:36 +0000
commit53be3fc70a44d34fda1ffddcf9d67473d3de50b4 (patch)
treefbadb6bb7d078ad6041224270f11973232952b24 /test
parent107ec58bdfbcbb09f40d92590f8197ffa683a925 (diff)
parent9f43b10e9014e694cb89fe2899dc52f602bf2197 (diff)
downloadsqlalchemy-53be3fc70a44d34fda1ffddcf9d67473d3de50b4.tar.gz
Merge "establish column_property and query_expression as readonly from a dc perspective" into main
Diffstat (limited to 'test')
-rw-r--r--test/ext/mypy/plain_files/dataclass_transforms_one.py50
-rw-r--r--test/ext/mypy/plain_files/pep681.py32
-rw-r--r--test/orm/declarative/test_basic.py20
-rw-r--r--test/orm/declarative/test_dc_transforms.py106
-rw-r--r--test/orm/test_deferred.py8
-rw-r--r--test/orm/test_deprecations.py61
6 files changed, 237 insertions, 40 deletions
diff --git a/test/ext/mypy/plain_files/dataclass_transforms_one.py b/test/ext/mypy/plain_files/dataclass_transforms_one.py
new file mode 100644
index 000000000..b7b88590f
--- /dev/null
+++ b/test/ext/mypy/plain_files/dataclass_transforms_one.py
@@ -0,0 +1,50 @@
+from __future__ import annotations
+
+from typing import Optional
+
+from sqlalchemy.orm import column_property
+from sqlalchemy.orm import DeclarativeBase
+from sqlalchemy.orm import Mapped
+from sqlalchemy.orm import mapped_column
+from sqlalchemy.orm import MappedAsDataclass
+from sqlalchemy.orm import query_expression
+
+
+class Base(DeclarativeBase):
+ pass
+
+
+class TestInitialSupport(Base):
+ __tablename__ = "a"
+
+ id: Mapped[int] = mapped_column(primary_key=True, init=False)
+ data: Mapped[str]
+ x: Mapped[Optional[int]] = mapped_column(default=None)
+ y: Mapped[Optional[int]] = mapped_column(kw_only=True)
+
+
+tis = TestInitialSupport(data="some data", y=5)
+
+# EXPECTED_TYPE: str
+reveal_type(tis.data)
+
+# EXPECTED_RE_TYPE: .*Union\[builtins.int, None\]
+reveal_type(tis.y)
+
+tis.data = "some other data"
+
+
+class TestTicket9628(MappedAsDataclass, Base):
+ __tablename__ = "ticket_9628"
+
+ id: Mapped[int] = mapped_column(primary_key=True, init=False)
+ data: Mapped[str] = mapped_column()
+
+ d2: Mapped[str] = column_property(data + "Asdf")
+ d3: Mapped[str] = query_expression(data + "Asdf")
+
+
+# d2 and d3 are not required, as these have init=False. We omit
+# them from dataclass transforms entirely as these are never intended
+# to be writeable fields in a 2.0 declarative mapping
+t9628 = TestTicket9628(data="asf")
diff --git a/test/ext/mypy/plain_files/pep681.py b/test/ext/mypy/plain_files/pep681.py
deleted file mode 100644
index caa219d78..000000000
--- a/test/ext/mypy/plain_files/pep681.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from __future__ import annotations
-
-from typing import Optional
-
-from sqlalchemy.orm import DeclarativeBase
-from sqlalchemy.orm import Mapped
-from sqlalchemy.orm import mapped_column
-from sqlalchemy.orm import MappedAsDataclass
-
-
-class Base(MappedAsDataclass, DeclarativeBase):
- pass
-
-
-class A(Base):
- __tablename__ = "a"
-
- id: Mapped[int] = mapped_column(primary_key=True, init=False)
- data: Mapped[str]
- x: Mapped[Optional[int]] = mapped_column(default=None)
- y: Mapped[Optional[int]] = mapped_column(kw_only=True)
-
-
-a1 = A(data="some data", y=5)
-
-# EXPECTED_TYPE: str
-reveal_type(a1.data)
-
-# EXPECTED_RE_TYPE: .*Union\[builtins.int, None\]
-reveal_type(a1.y)
-
-a1.data = "some other data"
diff --git a/test/orm/declarative/test_basic.py b/test/orm/declarative/test_basic.py
index 2d712c823..698b66db1 100644
--- a/test/orm/declarative/test_basic.py
+++ b/test/orm/declarative/test_basic.py
@@ -2593,6 +2593,26 @@ class DeclarativeMultiBaseTest(
sess.expunge_all()
eq_(sess.query(User).all(), [User(name="u1", a="a", b="b")])
+ def test_active_history_columns(self):
+ class Foo(Base):
+ __tablename__ = "foo"
+
+ id = Column(
+ Integer, primary_key=True, test_needs_autoincrement=True
+ )
+ a = column_property(Column(String), active_history=True)
+ b = mapped_column(String, active_history=True)
+ c = column_property(Column(String))
+ d = mapped_column(String)
+
+ self.assert_compile(
+ select(Foo), "SELECT foo.id, foo.a, foo.b, foo.c, foo.d FROM foo"
+ )
+ eq_(Foo.a.impl.active_history, True)
+ eq_(Foo.b.impl.active_history, True)
+ eq_(Foo.c.impl.active_history, False)
+ eq_(Foo.d.impl.active_history, False)
+
def test_column_properties(self):
class Address(Base, fixtures.ComparableEntity):
diff --git a/test/orm/declarative/test_dc_transforms.py b/test/orm/declarative/test_dc_transforms.py
index 031aad5d5..576ee7fbf 100644
--- a/test/orm/declarative/test_dc_transforms.py
+++ b/test/orm/declarative/test_dc_transforms.py
@@ -39,11 +39,13 @@ from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import MappedAsDataclass
from sqlalchemy.orm import MappedColumn
+from sqlalchemy.orm import query_expression
from sqlalchemy.orm import registry
from sqlalchemy.orm import registry as _RegistryType
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Session
from sqlalchemy.orm import synonym
+from sqlalchemy.sql.base import _NoArg
from sqlalchemy.testing import AssertsCompiledSQL
from sqlalchemy.testing import eq_
from sqlalchemy.testing import eq_regex
@@ -1355,9 +1357,7 @@ class DataclassArgsTest(fixtures.TestBase):
else:
return args, args
- @testing.fixture(
- params=["mapped_column", "synonym", "deferred", "column_property"]
- )
+ @testing.fixture(params=["mapped_column", "synonym", "deferred"])
def mapped_expr_constructor(self, request):
name = request.param
@@ -1367,8 +1367,6 @@ class DataclassArgsTest(fixtures.TestBase):
yield synonym("some_int", default=7, init=True)
elif name == "deferred":
yield deferred(Column(Integer), default=7, init=True)
- elif name == "column_property":
- yield column_property(Column(Integer), default=7, init=True)
def test_attrs_rejected_if_not_a_dc(
self, mapped_expr_constructor, decl_base: Type[DeclarativeBase]
@@ -1725,7 +1723,6 @@ class DataclassArgsTest(fixtures.TestBase):
@testing.combinations(
mapped_column,
lambda **kw: synonym("some_int", **kw),
- lambda **kw: column_property(Column(Integer), **kw),
lambda **kw: deferred(Column(Integer), **kw),
lambda **kw: composite("foo", **kw),
lambda **kw: relationship("Foo", **kw),
@@ -1752,6 +1749,28 @@ class DataclassArgsTest(fixtures.TestBase):
prop = construct(**kw)
eq_(prop._attribute_options, exp)
+ @testing.variation("use_arguments", [True, False])
+ @testing.combinations(
+ lambda **kw: column_property(Column(Integer), **kw),
+ lambda **kw: query_expression(**kw),
+ argnames="construct",
+ )
+ def test_ro_attribute_options(self, use_arguments, construct):
+ if use_arguments:
+ kw = {
+ "repr": False,
+ "compare": True,
+ }
+ exp = interfaces._AttributeOptions(
+ False, False, _NoArg.NO_ARG, _NoArg.NO_ARG, True, _NoArg.NO_ARG
+ )
+ else:
+ kw = {}
+ exp = interfaces._DEFAULT_READONLY_ATTRIBUTE_OPTIONS
+
+ prop = construct(**kw)
+ eq_(prop._attribute_options, exp)
+
class MixinColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL):
"""tests for #8718"""
@@ -1978,3 +1997,78 @@ class CompositeTest(fixtures.TestBase, testing.AssertsCompiledSQL):
"state='NY', zip_='12345'))",
)
eq_(repr(u2), "mymodule.User(name='u2', address=None)")
+
+
+class ReadOnlyAttrTest(fixtures.TestBase, testing.AssertsCompiledSQL):
+ """tests related to #9628"""
+
+ __dialect__ = "default"
+
+ @testing.combinations(
+ (query_expression,), (column_property,), argnames="construct"
+ )
+ def test_default_behavior(
+ self, dc_decl_base: Type[MappedAsDataclass], construct
+ ):
+ class MyClass(dc_decl_base):
+ __tablename__ = "a"
+
+ id: Mapped[int] = mapped_column(primary_key=True, init=False)
+ data: Mapped[str] = mapped_column()
+
+ const: Mapped[str] = construct(data + "asdf")
+
+ m1 = MyClass(data="foo")
+ eq_(m1, MyClass(data="foo"))
+ ne_(m1, MyClass(data="bar"))
+
+ eq_regex(
+ repr(m1),
+ r".*MyClass\(id=None, data='foo', const=None\)",
+ )
+
+ @testing.combinations(
+ (query_expression,), (column_property,), argnames="construct"
+ )
+ def test_no_repr_behavior(
+ self, dc_decl_base: Type[MappedAsDataclass], construct
+ ):
+ class MyClass(dc_decl_base):
+ __tablename__ = "a"
+
+ id: Mapped[int] = mapped_column(primary_key=True, init=False)
+ data: Mapped[str] = mapped_column()
+
+ const: Mapped[str] = construct(data + "asdf", repr=False)
+
+ m1 = MyClass(data="foo")
+
+ eq_regex(
+ repr(m1),
+ r".*MyClass\(id=None, data='foo'\)",
+ )
+
+ @testing.combinations(
+ (query_expression,), (column_property,), argnames="construct"
+ )
+ def test_enable_compare(
+ self, dc_decl_base: Type[MappedAsDataclass], construct
+ ):
+ class MyClass(dc_decl_base):
+ __tablename__ = "a"
+
+ id: Mapped[int] = mapped_column(primary_key=True, init=False)
+ data: Mapped[str] = mapped_column()
+
+ const: Mapped[str] = construct(data + "asdf", compare=True)
+
+ m1 = MyClass(data="foo")
+ eq_(m1, MyClass(data="foo"))
+ ne_(m1, MyClass(data="bar"))
+
+ m2 = MyClass(data="foo")
+ m2.const = "some const"
+ ne_(m2, MyClass(data="foo"))
+ m3 = MyClass(data="foo")
+ m3.const = "some const"
+ eq_(m2, m3)
diff --git a/test/orm/test_deferred.py b/test/orm/test_deferred.py
index 5ce7475f2..e1eb6e9e1 100644
--- a/test/orm/test_deferred.py
+++ b/test/orm/test_deferred.py
@@ -1,3 +1,7 @@
+from __future__ import annotations
+
+from typing import Union
+
import sqlalchemy as sa
from sqlalchemy import ForeignKey
from sqlalchemy import func
@@ -132,8 +136,8 @@ class DeferredTest(AssertsCompiledSQL, _fixtures.FixtureTest):
],
)
- @testing.combinations(True, False, None, "deferred_parameter")
- def test_group_defer_newstyle(self, deferred_parameter):
+ @testing.combinations(True, False, None, argnames="deferred_parameter")
+ def test_group_defer_newstyle(self, deferred_parameter: Union[bool, None]):
class Base(DeclarativeBase):
pass
diff --git a/test/orm/test_deprecations.py b/test/orm/test_deprecations.py
index 722563964..a3e2f4ef7 100644
--- a/test/orm/test_deprecations.py
+++ b/test/orm/test_deprecations.py
@@ -3,6 +3,7 @@ from unittest.mock import Mock
import sqlalchemy as sa
from sqlalchemy import cast
+from sqlalchemy import column
from sqlalchemy import desc
from sqlalchemy import event
from sqlalchemy import exc as sa_exc
@@ -31,6 +32,8 @@ from sqlalchemy.orm import deferred
from sqlalchemy.orm import foreign
from sqlalchemy.orm import instrumentation
from sqlalchemy.orm import joinedload
+from sqlalchemy.orm import Mapped
+from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship
from sqlalchemy.orm import scoped_session
from sqlalchemy.orm import Session
@@ -48,6 +51,7 @@ from sqlalchemy.testing import AssertsCompiledSQL
from sqlalchemy.testing import eq_
from sqlalchemy.testing import eq_ignore_whitespace
from sqlalchemy.testing import expect_deprecated
+from sqlalchemy.testing import expect_raises_message
from sqlalchemy.testing import fixtures
from sqlalchemy.testing import is_
from sqlalchemy.testing import is_true
@@ -399,6 +403,63 @@ class MiscDeprecationsTest(fixtures.TestBase):
is_(EvaluatorCompiler, _EvaluatorCompiler)
+ @testing.combinations(
+ ("init", True),
+ ("kw_only", True, testing.requires.python310),
+ ("default", 5),
+ ("default_factory", lambda: 10),
+ argnames="paramname, value",
+ )
+ def test_column_property_dc_attributes(self, paramname, value):
+ with expect_deprecated(
+ rf"The column_property.{paramname} parameter is deprecated "
+ r"for column_property\(\)",
+ raise_on_any_unexpected=True,
+ ):
+ column_property(column("q"), **{paramname: value})
+
+ @testing.requires.python310
+ def test_column_property_dc_attributes_still_function(self, dc_decl_base):
+ with expect_deprecated(
+ r"The column_property.init parameter is deprecated "
+ r"for column_property\(\)",
+ r"The column_property.default parameter is deprecated "
+ r"for column_property\(\)",
+ r"The column_property.default_factory parameter is deprecated "
+ r"for column_property\(\)",
+ r"The column_property.kw_only parameter is deprecated "
+ r"for column_property\(\)",
+ raise_on_any_unexpected=True,
+ ):
+
+ class MyClass(dc_decl_base):
+ __tablename__ = "a"
+
+ id: Mapped[int] = mapped_column(primary_key=True, init=False)
+ data: Mapped[str] = mapped_column()
+
+ const1: Mapped[str] = column_property(
+ data + "asdf", init=True, default="foobar"
+ )
+ const2: Mapped[str] = column_property(
+ data + "asdf",
+ init=True,
+ default_factory=lambda: "factory_foo",
+ )
+ const3: Mapped[str] = column_property(
+ data + "asdf", init=True, kw_only=True
+ )
+
+ m1 = MyClass(data="d1", const3="c3")
+ eq_(m1.const1, "foobar")
+ eq_(m1.const2, "factory_foo")
+ eq_(m1.const3, "c3")
+
+ with expect_raises_message(
+ TypeError, "missing 1 required keyword-only argument: 'const3'"
+ ):
+ MyClass(data="d1")
+
class DeprecatedQueryTest(_fixtures.FixtureTest, AssertsCompiledSQL):
__dialect__ = "default"