Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Towards compatibility with SQLAlchemy 2.x #485

Merged
merged 12 commits into from
Dec 30, 2022

Conversation

amotl
Copy link
Member

@amotl amotl commented Dec 20, 2022

About

Based on information from the migration guide referenced below, this patch brings the code base a bit closer to SQLAlchemy 2.0.

-- SQLAlchemy migration to 2.0 Step Two - Turn on RemovedIn20Warnings

Details

  • Declarative becomes a first class API, so it is now located at sqlalchemy.orm.declarative_base.
  • The Session no longer supports "bound metadata" when it resolves the engine to be used for connectivity. This means that an Engine object must be passed to the constructor now.
  • connection.execute will now only accept SQL statements wrapped in text().
  • When needing to run a driver-level SQL statement, the method connection.exec_driver_sql must be used.
  • Library-level "Autocommit" aka. "implicit commit" removed from both Core and ORM. It is advised to use a context manager instead, like with engine.begin() as conn:.
  • select() no longer accepts varied constructor arguments, only the "generative" style of select() will be supported. The list of columns / tables to select from should be passed positionally.
  • The autoload option to sa.MetaData has been deprecated. autoload_with takes place, it is already there and supported.

References

Notes

df3d04c is a bit unrelated to the changes regarding SQLAlchemy. However, those improvements, finally resolving a few leftover socket leaks in the test suite, have been made while working on mitigating the SA warnings, so that's why it's here. I think it will not harm the review process too much.

Compatibility

With #497, this patch also retains compatibility with SQLAlchemy 1.3.

@amotl amotl force-pushed the amo/doctests-refactoring branch 2 times, most recently from 6090d53 to 5dc0e04 Compare December 20, 2022 21:41
@amotl amotl force-pushed the amo/sa-mitigate-deprecation-warnings branch 2 times, most recently from 810c3df to 386e246 Compare December 20, 2022 22:58
Base automatically changed from amo/doctests-refactoring to master December 21, 2022 23:41
@amotl amotl force-pushed the amo/sa-mitigate-deprecation-warnings branch from 386e246 to 7ef37c1 Compare December 21, 2022 23:52
@amotl amotl marked this pull request as ready for review December 22, 2022 01:34
@amotl amotl marked this pull request as draft December 22, 2022 02:07
@amotl amotl force-pushed the amo/sa-mitigate-deprecation-warnings branch from 1429658 to a4454d5 Compare December 22, 2022 12:36
@amotl amotl force-pushed the amo/sa-mitigate-deprecation-warnings branch from fe32a5e to b177b1e Compare December 23, 2022 23:44
@amotl amotl marked this pull request as ready for review December 23, 2022 23:53
@amotl amotl force-pushed the amo/sa-mitigate-deprecation-warnings branch 2 times, most recently from be93172 to 639636f Compare December 27, 2022 11:36

# SQLAlchemy 1.3 does not have the `exec_driver_sql` method.
if SA_VERSION < SA_1_4:
monkeypatch_add_exec_driver_sql()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we already know how much longer we intend to support SQLAlchemy 1.3? According to SQLAlchemy's version policy, 1.3 will become EOL as soon as 2.0 becomes stable. Given that maintaining compatibility with 1.3 here reaches a certain complexity, do we want to keep it much longer?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi. The most recent discussion about EOL of SA13 can be found at sqlalchemy/sqlalchemy#8631 (comment). I think we will support it for a while, and I am happy that the changes have not been too much intrusive, other than needing a few lines in compat/api13.py.

We discussed different alternatives on maintenance of the code within this package with @seut, regarding version deprecations and such, but all other alternatives would need more efforts, especially if we would need to deprecate support for SA13 earlier, because this might mean we would have to keep a maintenance branch or such. See also GH-494.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nevertheless, I think it will not hurt to add a runtime warning for the next release, which will give users a notice that dropping support for SA13 will be around the corner.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 1.3 support is quite nicely encapsulated, indeed. Well done! For now (while 1.3 is still supported upstream) it's a good solution. Once 1.3 reached EOL, I would still vote for removing it shortly after to eliminate the need for monkey patching (which always feels a bit risky to me, especially in tests).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 for removing 1.3 support. Given that crate-python is mostly stable (bugfixes are rare) we can point users of SQLAlchemy 1.3 to an older version of crate-python and make sure to update the version constraints for the next crate-python release. pip should then pick the right version, no?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. See also GH-501.

@amotl amotl requested a review from hammerhead December 27, 2022 15:05
Most notable changes:
- Declarative becomes a first class API, so it is now located at
  `sqlalchemy.orm.declarative_base`.
- The `Session` no longer supports "bound metadata" when it resolves the
  engine to be used for connectivity. This means that an Engine object
  **must** be passed to the constructor now.
- `connection.execute` will now only accept SQL statements wrapped in
  `text()`.
- When needing to run a driver-level SQL statement, the method
  `connection.exec_driver_sql` must be used.
- Library-level "Autocommit" aka. "implicit commit" removed from both
  Core and ORM.
- `select()` no longer accepts varied constructor arguments, only the
  "generative" style of `select()` will be supported. The list of
  columns / tables to select from should be passed positionally.
- The `autoload` option to `sa.MetaData` has been deprecated.
  `autoload_with` takes place, it is already there and supported.

https://docs.sqlalchemy.org/en/20/changelog/migration_20.html
hammerhead and others added 8 commits December 29, 2022 14:09
Don't use `autoload_with` on table `mytable`, because it will never be
persisted to a database, so it can not be inspected.
"Implicit" and "Connectionless" execution, and "bound metadata" have
been removed beginning with SQLAlchemy 2.0 [1].

Earlier and contemporary versions of SQLAlchemy had the ability to
associate an `Engine` with a `MetaData` object. This allowed a number of
so-called "connectionless" execution patterns. That is no longer
possible.

Instead, the association with an `Engine` object has to be concluded
differently. On this very spot, in the context of the `dict_test` test
cases, the most easy fix was to move it to the invocation of the
`compile()` method of the `selectable` instance, which is now returned
by the `sqlalchemy.sql.*` primitives:

  expression = selectable.compile(bind=self.engine)

This is needed, because otherwise, when not associating `Engine` with
`MetaData` properly, `CrateDialect` would be bypassed, and the
"paramstyle" [2] of SQLAlchemy's `DefaultDialect` would be used, which
is actually "named" [3], as originally reflected per b20faba.

This is probably wrong, because the CrateDB Python driver uses the
"qmark" paramstyle [4].

[1] https://docs.sqlalchemy.org/en/20/changelog/migration_20.html#implicit-and-connectionless-execution-bound-metadata-removed
[2] https://docs.sqlalchemy.org/en/20/core/engines.html#sqlalchemy.create_engine.params.paramstyle
[3] https://github.com/sqlalchemy/sqlalchemy/blob/rel_2_0_0b4/lib/sqlalchemy/engine/default.py#L204
[4] https://github.com/crate/crate-python/blob/0.29.0/src/crate/client/__init__.py#L36
By binding the insert clause to an engine, use the "qmark" paramstyle
again, instead of falling back to the "named" paramstyle of the default
dialect.
Resolve import of `declarative_base`. For SA13, it is in
`sqlalchemy.ext.declarative`, while it is in `sqlalchemy.orm` on more
recent versions of SA.
Note that this is on a best-effort basis. SQLAlchemy 2.0 will be
mostly compatible with most SQLAlchemy 1.3 code which is not using any
of the deprecated functionalities, modulo a few aspects.

> It is possible to have code that is compatible with 2.0 and 1.3 at the
> same time, you would just need to ensure you use the subset of
> features and APIs that are common to both.

This patch adds a small compatibility layer, which activates on SA13
only, and monkey-patches two spots where anomalies have been spotted.
@amotl amotl force-pushed the amo/sa-mitigate-deprecation-warnings branch from 639636f to 19dbf7d Compare December 29, 2022 13:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants