mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-12 12:33:02 +00:00
Merge pull request #4319 from minrk/require-sqla-14
require sqlalchemy 1.4
This commit is contained in:
@@ -8,9 +8,7 @@ from datetime import datetime, timedelta
|
||||
|
||||
import alembic.command
|
||||
import alembic.config
|
||||
import sqlalchemy
|
||||
from alembic.script import ScriptDirectory
|
||||
from packaging.version import parse as parse_version
|
||||
from sqlalchemy import (
|
||||
Boolean,
|
||||
Column,
|
||||
@@ -31,18 +29,12 @@ from sqlalchemy import (
|
||||
from sqlalchemy.orm import (
|
||||
Session,
|
||||
backref,
|
||||
declarative_base,
|
||||
interfaces,
|
||||
object_session,
|
||||
relationship,
|
||||
sessionmaker,
|
||||
)
|
||||
|
||||
try:
|
||||
from sqlalchemy.orm import declarative_base
|
||||
except ImportError:
|
||||
# sqlalchemy < 1.4
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
|
||||
from sqlalchemy.pool import StaticPool
|
||||
from sqlalchemy.types import LargeBinary, Text, TypeDecorator
|
||||
from tornado.log import app_log
|
||||
@@ -912,27 +904,19 @@ def register_ping_connection(engine):
|
||||
|
||||
@event.listens_for(engine, "engine_connect")
|
||||
def ping_connection(connection, branch=None):
|
||||
if branch:
|
||||
# "branch" refers to a sub-connection of a connection,
|
||||
# we don't want to bother pinging on these.
|
||||
return
|
||||
# TODO: remove unused branch arg when we require sqlalchemy 2.0
|
||||
|
||||
# turn off "close with result". This flag is only used with
|
||||
# "connectionless" execution, otherwise will be False in any case
|
||||
save_should_close_with_result = connection.should_close_with_result
|
||||
connection.should_close_with_result = False
|
||||
|
||||
if parse_version(sqlalchemy.__version__) < parse_version("1.4"):
|
||||
one = [1]
|
||||
else:
|
||||
one = 1
|
||||
|
||||
try:
|
||||
# run a SELECT 1. use a core select() so that
|
||||
# the SELECT of a scalar value without a table is
|
||||
# appropriately formatted for the backend
|
||||
with connection.begin() as transaction:
|
||||
connection.scalar(select(one))
|
||||
connection.scalar(select(1))
|
||||
except exc.DBAPIError as err:
|
||||
# catch SQLAlchemy's DBAPIError, which is a wrapper
|
||||
# for the DBAPI's exception. It includes a .connection_invalidated
|
||||
@@ -948,7 +932,7 @@ def register_ping_connection(engine):
|
||||
# here also causes the whole connection pool to be invalidated
|
||||
# so that all stale connections are discarded.
|
||||
with connection.begin() as transaction:
|
||||
connection.scalar(select(one))
|
||||
connection.scalar(select(1))
|
||||
else:
|
||||
raise
|
||||
finally:
|
||||
@@ -972,11 +956,8 @@ def check_db_revision(engine):
|
||||
|
||||
from .dbutil import _temp_alembic_ini
|
||||
|
||||
if hasattr(engine.url, "render_as_string"):
|
||||
# sqlalchemy >= 1.4
|
||||
engine_url = engine.url.render_as_string(hide_password=False)
|
||||
else:
|
||||
engine_url = str(engine.url)
|
||||
# alembic needs the password if it's in the URL
|
||||
engine_url = engine.url.render_as_string(hide_password=False)
|
||||
|
||||
with _temp_alembic_ini(engine_url) as ini:
|
||||
cfg = alembic.config.Config(ini)
|
||||
@@ -1067,6 +1048,8 @@ def new_session_factory(
|
||||
elif url.startswith('mysql'):
|
||||
kwargs.setdefault('pool_recycle', 60)
|
||||
|
||||
kwargs.setdefault("future", True)
|
||||
|
||||
if url.endswith(':memory:'):
|
||||
# If we're using an in-memory database, ensure that only one connection
|
||||
# is ever created.
|
||||
|
@@ -5,6 +5,7 @@ from glob import glob
|
||||
from subprocess import check_call
|
||||
|
||||
import pytest
|
||||
from packaging.version import parse as V
|
||||
from pytest import raises
|
||||
from traitlets.config import Config
|
||||
|
||||
@@ -25,8 +26,14 @@ def generate_old_db(env_dir, hub_version, db_url):
|
||||
env_pip = os.path.join(env_dir, 'bin', 'pip')
|
||||
env_py = os.path.join(env_dir, 'bin', 'python')
|
||||
check_call([sys.executable, '-m', 'virtualenv', env_dir])
|
||||
pkgs = ['jupyterhub==' + hub_version]
|
||||
|
||||
# older jupyterhub needs older sqlachemy version
|
||||
pkgs = ['jupyterhub==' + hub_version, 'sqlalchemy<1.4']
|
||||
if V(hub_version) < V("2"):
|
||||
pkgs.append('sqlalchemy<1.4')
|
||||
elif V(hub_version) < V("3.1.1"):
|
||||
pkgs.append('sqlalchemy<2')
|
||||
|
||||
if 'mysql' in db_url:
|
||||
pkgs.append('mysql-connector-python')
|
||||
elif 'postgres' in db_url:
|
||||
|
@@ -14,20 +14,6 @@ from jupyterhub.objects import Server
|
||||
from jupyterhub.roles import assign_default_roles, update_roles
|
||||
from jupyterhub.utils import url_path_join as ujoin
|
||||
|
||||
try:
|
||||
from sqlalchemy.exc import RemovedIn20Warning
|
||||
except ImportError:
|
||||
|
||||
class RemovedIn20Warning(DeprecationWarning):
|
||||
"""
|
||||
I only exist so I can be used in warnings filters in pytest.ini
|
||||
|
||||
I will never be displayed.
|
||||
|
||||
sqlalchemy 1.4 introduces RemovedIn20Warning,
|
||||
but we still test against older sqlalchemy.
|
||||
"""
|
||||
|
||||
|
||||
class _AsyncRequests:
|
||||
"""Wrapper around requests to return a Future from request methods
|
||||
|
@@ -20,5 +20,5 @@ markers =
|
||||
selenium: web tests that run with selenium
|
||||
|
||||
filterwarnings =
|
||||
error:.*:jupyterhub.tests.utils.RemovedIn20Warning
|
||||
ignore:.*event listener has changed as of version 2.0.*:sqlalchemy.exc.SADeprecationWarning
|
||||
ignore:.*The new signature is "def engine_connect\(conn\)"*:sqlalchemy.exc.SADeprecationWarning
|
||||
ignore:.*The new signature is "def engine_connect\(conn\)"*:sqlalchemy.exc.SAWarning
|
||||
|
@@ -11,6 +11,6 @@ prometheus_client>=0.4.0
|
||||
psutil>=5.6.5; sys_platform == 'win32'
|
||||
python-dateutil
|
||||
requests
|
||||
SQLAlchemy>=1.1
|
||||
SQLAlchemy>=1.4
|
||||
tornado>=5.1
|
||||
traitlets>=4.3.2
|
||||
|
Reference in New Issue
Block a user