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

SQLAlchemy 2 support #45

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ jobs:
strategy:
max-parallel: 2
matrix:
python-version: ["3.8", "3.9", "3.10"]
python-version: ["3.9", "3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
- uses: actions/setup-python@v5
with:
python-version: "${{ matrix.python-version }}"
- name: Upgrade pip
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
Release Notes
=============

Version 2.1.0
-------------

(Unreleased yet)

* Added support for SQLAlchemy 2.0 while still allowing 1.4 usage
* Changed tests to use new instead of deprecated functions, excluding declarative_base()
* Make tox run check + tests on both SQLAlchemy versions


Version 2.0.0
-------------

Expand Down
2 changes: 1 addition & 1 deletion nameko_sqlalchemy/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def __init__(
def setup(self):
service_name = self.container.service_name
declarative_base_name = self.declarative_base.__name__
uri_key = '{}:{}'.format(service_name, declarative_base_name)
uri_key = f'{service_name}:{declarative_base_name}'

db_uris = self.container.config[DB_URIS_KEY]
self.db_uri = db_uris[uri_key].format({
Expand Down
2 changes: 1 addition & 1 deletion nameko_sqlalchemy/database_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def __init__(
def setup(self):
service_name = self.container.service_name
decl_base_name = self.declarative_base.__name__
uri_key = '{}:{}'.format(service_name, decl_base_name)
uri_key = f'{service_name}:{decl_base_name}'

db_uris = self.container.config[DB_URIS_KEY]
self.db_uri = db_uris[uri_key].format({
Expand Down
8 changes: 4 additions & 4 deletions nameko_sqlalchemy/pytest_fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def model_base():
raise NotImplementedError("Fixture `model_base` has to be overwritten")


@pytest.yield_fixture(scope='session')
@pytest.fixture(scope='session')
def db_connection(db_url, model_base, db_engine_options):
engine = create_engine(db_url, **db_engine_options)
model_base.metadata.create_all(engine)
Expand All @@ -123,11 +123,11 @@ def db_connection(db_url, model_base, db_engine_options):

yield connection

model_base.metadata.drop_all()
model_base.metadata.drop_all(bind=engine)
engine.dispose()


@pytest.yield_fixture
@pytest.fixture
def db_session(db_connection, model_base):
session = sessionmaker(bind=db_connection, class_=Session)
db_session = session()
Expand All @@ -143,7 +143,7 @@ def db_session(db_connection, model_base):
db_session.close()


@pytest.yield_fixture
@pytest.fixture
def database(db_connection, model_base):

database = DatabaseWrapper(
Expand Down
34 changes: 18 additions & 16 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ build-backend = "setuptools.build_meta"

[project]
name = "nameko-sqlalchemy"
version = "2.0.0"
version = "2.1.0"
description = "SQLAlchemy dependency for nameko services"
license = {file = "LICENSE.txt"}
readme = "README.rst"
requires-python = ">=3.8"
requires-python = ">=3.9"
authors = [{name="onefinestay", email="[email protected]"}]
classifiers = [
"Intended Audience :: Developers",
Expand All @@ -17,31 +17,32 @@ classifiers = [
"Operating System :: POSIX",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Internet",
"Topic :: Software Development :: Libraries :: Python Modules",
]
dependencies = [
"nameko>=2.0.0",
"sqlalchemy>=1.4,<2"
"sqlalchemy>=1.4,<3"
]

[project.urls]
Homepage = "https://github.com/onefinestay/nameko-sqlalchemy"

[project.optional-dependencies]
dev = [
"coverage==7.3.2",
"isort==5.12.0",
"mypy==1.7.1",
"pytest==7.4.3",
"requests==2.31.0",
"ruff==0.1.6",
"PyMySQL==1.1.0",
"types-mock==5.1.0.3",
"types-requests==2.31.0.10",
"coverage==7.6.4",
"isort==5.13.2",
"mypy==1.13.0",
"pytest==8.3.3",
"requests==2.32.3",
"PyMySQL==1.1.1",
"ruff==0.7.1",
"types-mock==5.1.0.20240425",
"types-requests==2.32.0.20241016",
]

[project.entry-points."pytest11"]
Expand All @@ -58,13 +59,14 @@ src_paths = [
"nameko_sqlalchemy/",
"test/",
]
known_first_party = "nameko_chassis"
known_first_party = "nameko_sqlalchemy"

[tool.ruff]
extend-exclude = [
".venv",
"migrations",
]
[tool.ruff.lint]
ignore = [
"E402",
"E501",
Expand All @@ -76,7 +78,7 @@ select = [
]

[tool.mypy]
python_version = "3.10"
python_version = "3.12"
plugins = "sqlalchemy.ext.mypy.plugin"
mypy_path = "nameko_sqlalchemy/"
namespace_packages = true
Expand All @@ -89,4 +91,4 @@ ignore_missing_imports = true

[tool.pytest.ini_options]
norecursedirs = [".git", ".tox", "dist", "build"]
testpaths = ["test"]
testpaths = ["test"]
2 changes: 0 additions & 2 deletions requirements.txt

This file was deleted.

10 changes: 4 additions & 6 deletions test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import pytest
import requests
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import declarative_base

TOXIPROXY_PROXY_NAME = 'nameko_sqlalchemy_test_mysql'

Expand All @@ -25,21 +25,19 @@ class ExampleModel(DeclarativeBase):
data = Column(String(100))


@pytest.yield_fixture
@pytest.fixture
def toxiproxy(toxiproxy_api_url, toxiproxy_db_url):

class Controller(object):
def __init__(self, api_url):
self.api_url = api_url

def enable(self):
resource = 'http://{}/reset'.format(self.api_url)
resource = f'http://{self.api_url}/reset'
requests.post(resource)

def disable(self):
resource = 'http://{}/proxies/{}'.format(
self.api_url, TOXIPROXY_PROXY_NAME
)
resource = f'http://{self.api_url}/proxies/{TOXIPROXY_PROXY_NAME}'
data = {
'enabled': False
}
Expand Down
46 changes: 23 additions & 23 deletions test/test_database.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
from mock import Mock, patch
from nameko.containers import ServiceContainer, WorkerContext
from nameko.testing.services import dummy, entrypoint_hook
from sqlalchemy import Column, String, create_engine
from sqlalchemy import Column, String, create_engine, text
from sqlalchemy.engine import Engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import declarative_base

from nameko_sqlalchemy.database import DB_URIS_KEY, Database, Session

Expand Down Expand Up @@ -300,7 +300,7 @@ class BaseTestEndToEnd:

@pytest.fixture
def db_uri(self, tmpdir):
return 'sqlite:///{}'.format(tmpdir.join("db").strpath)
return f'sqlite:///{tmpdir.join("db").strpath}'

@pytest.fixture
def container(self, container_factory, db_uri):
Expand Down Expand Up @@ -338,7 +338,7 @@ def write(self, key, value):
@dummy
def read(self, key):
session = self.db.get_session()
value = session.query(ExampleModel).get(key).value
value = session.get(ident=key, entity=ExampleModel).value
session.close()
return value

Expand All @@ -349,10 +349,10 @@ def test_successful_write_and_read(slf, container, db_uri):
write(key='spam', value='ham')

# verify changes written to disk
entries = list(
create_engine(db_uri).execute(
'SELECT key, value FROM example LIMIT 1'))
assert entries == [('spam', 'ham',)]
engine = create_engine(db_uri)
with engine.connect() as conn:
entries = list(conn.execute(text('SELECT key, value FROM example LIMIT 1')))
assert entries == [('spam', 'ham',)]

# read through the service
with entrypoint_hook(container, 'read') as read:
Expand All @@ -375,7 +375,7 @@ def write(self, key, value):
@dummy
def read(self, key):
with self.db.get_session() as session:
return session.query(ExampleModel).get(key).value
return session.get(ident=key, entity=ExampleModel).value

def test_successful_write_and_read(slf, container, db_uri):

Expand All @@ -384,10 +384,10 @@ def test_successful_write_and_read(slf, container, db_uri):
write(key='spam', value='ham')

# verify changes written to disk
entries = list(
create_engine(db_uri).execute(
'SELECT key, value FROM example LIMIT 1'))
assert entries == [('spam', 'ham',)]
engine = create_engine(db_uri)
with engine.connect() as conn:
entries = list(conn.execute(text('SELECT key, value FROM example LIMIT 1')))
assert entries == [('spam', 'ham',)]

# read through the service
with entrypoint_hook(container, 'read') as read:
Expand All @@ -409,7 +409,7 @@ def write(self, key, value):

@dummy
def read(self, key):
return self.db.session.query(ExampleModel).get(key).value
return self.db.session.get(ident=key, entity=ExampleModel).value

def test_successful_write_and_read(slf, container, db_uri):

Expand All @@ -418,10 +418,10 @@ def test_successful_write_and_read(slf, container, db_uri):
write(key='spam', value='ham')

# verify changes written to disk
entries = list(
create_engine(db_uri).execute(
'SELECT key, value FROM example LIMIT 1'))
assert entries == [('spam', 'ham',)]
engine = create_engine(db_uri)
with engine.connect() as conn:
entries = list(conn.execute(text('SELECT key, value FROM example LIMIT 1')))
assert entries == [('spam', 'ham',)]

# read through the service
with entrypoint_hook(container, 'read') as read:
Expand All @@ -444,7 +444,7 @@ def write(self, key, value):
@dummy
def read(self, key):
with self.db.session as session:
return session.query(ExampleModel).get(key).value
return session.get(ident=key, entity=ExampleModel).value

def test_successful_write_and_read(slf, container, db_uri):

Expand All @@ -453,10 +453,10 @@ def test_successful_write_and_read(slf, container, db_uri):
write(key='spam', value='ham')

# verify changes written to disk
entries = list(
create_engine(db_uri).execute(
'SELECT key, value FROM example LIMIT 1'))
assert entries == [('spam', 'ham',)]
engine = create_engine(db_uri)
with engine.connect() as conn:
entries = list(conn.execute(text('SELECT key, value FROM example LIMIT 1')))
assert entries == [('spam', 'ham',)]

# read through the service
with entrypoint_hook(container, 'read') as read:
Expand Down
14 changes: 8 additions & 6 deletions test/test_database_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
from mock import Mock
from nameko.containers import ServiceContainer, WorkerContext
from nameko.testing.services import dummy, entrypoint_hook
from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy import Column, Integer, String, create_engine, text
from sqlalchemy.engine import Engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import declarative_base
from sqlalchemy.orm.session import Session

from nameko_sqlalchemy.database import DB_URIS_KEY
Expand Down Expand Up @@ -35,7 +35,7 @@ def write(self, value):

@dummy
def read(self, id):
return self.session.query(ExampleModel).get(id).data
return self.session.get(ident=id, entity=ExampleModel).data


@pytest.fixture
Expand Down Expand Up @@ -174,7 +174,7 @@ def test_worker_teardown(db_session):
def test_end_to_end(container_factory, tmpdir):

# create a temporary database
db_uri = 'sqlite:///{}'.format(tmpdir.join("db").strpath)
db_uri = f'sqlite:///{tmpdir.join("db").strpath}'
engine = create_engine(db_uri)
ExampleModel.metadata.create_all(engine)

Expand All @@ -192,8 +192,10 @@ def test_end_to_end(container_factory, tmpdir):
pk = write("foobar")

# verify changes written to disk
entries = list(engine.execute('SELECT data FROM example LIMIT 1'))
assert entries == [('foobar',)]
engine = create_engine(db_uri)
with engine.connect() as conn:
entries = list(conn.execute(text('SELECT data FROM example LIMIT 1')))
assert entries == [('foobar',)]

# read through the service
with entrypoint_hook(container, "read") as read:
Expand Down
Loading