diff options
| author | Federico Caselli <cfederico87@gmail.com> | 2023-04-12 21:21:36 +0000 |
|---|---|---|
| committer | Gerrit Code Review <gerrit@bbpush.zzzcomputing.com> | 2023-04-12 21:21:36 +0000 |
| commit | 53be3fc70a44d34fda1ffddcf9d67473d3de50b4 (patch) | |
| tree | fbadb6bb7d078ad6041224270f11973232952b24 /test | |
| parent | 107ec58bdfbcbb09f40d92590f8197ffa683a925 (diff) | |
| parent | 9f43b10e9014e694cb89fe2899dc52f602bf2197 (diff) | |
| download | sqlalchemy-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.py | 50 | ||||
| -rw-r--r-- | test/ext/mypy/plain_files/pep681.py | 32 | ||||
| -rw-r--r-- | test/orm/declarative/test_basic.py | 20 | ||||
| -rw-r--r-- | test/orm/declarative/test_dc_transforms.py | 106 | ||||
| -rw-r--r-- | test/orm/test_deferred.py | 8 | ||||
| -rw-r--r-- | test/orm/test_deprecations.py | 61 |
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" |
