Changeset - d4f5515f202a
[Not reviewed]
0 6 0
Lance Edgar (lance) - 2 months ago 2024-08-27 22:56:32
lance@edbob.org
fix: cleanup old code for "record changes" session feature

had some tests failing, and one thing led to another.. apparenty we
still had support for some pretty old sqlalchemy so no need to keep
that around.
6 files changed with 75 insertions and 102 deletions:
0 comments (0 inline, 0 general)
pyproject.toml
Show inline comments
 
@@ -69,7 +69,7 @@ db = [
 
memcached = ["pylibmc"]
 
supervisor = ["supervisor"]
 
docs = ["Sphinx", "sphinx-paramlinks", "sphinxcontrib-programoutput", "furo"]
 
tests = ["coverage", "mock", "pytest", "pytest-cov"]
 
tests = ["pytest-cov", "tox"]
 

	
 

	
 
[project.scripts]
rattail/db/changes.py
Show inline comments
 
@@ -30,17 +30,13 @@ from packaging.version import parse as parse_version
 
import sqlalchemy as sa
 
from sqlalchemy.orm import object_mapper, RelationshipProperty
 
from sqlalchemy.orm.session import Session
 

	
 
try:
 
    from sqlalchemy.event import listen
 
except ImportError:
 
    listen = None # old SQLAlchemy; will have to work around that, below
 
from sqlalchemy.event import listen
 

	
 
from rattail.db.model import Setting, Change, DataSyncChange
 

	
 
try:
 
    from rattail.db.continuum import versioning_manager
 
except ImportError:             # assume no continuum
 
except ImportError: # pragma: no cover
 
    versioning_manager = None
 

	
 

	
 
@@ -49,10 +45,14 @@ log = logging.getLogger(__name__)
 

	
 
def record_changes(session, recorder=None, config=None):
 
    """
 
    Record all relevant data changes which occur within a session.
 
    Turn on the "record changes" feature.
 

	
 
    With this enabled, all relevant data changes which occur in the
 
    sesion will be recorded.
 

	
 
    :param session: A :class:`sqlalchemy:sqlalchemy.orm.session.Session` class,
 
       or instance thereof.
 
    :param session: A
 
       :class:`sqlalchemy:sqlalchemy.orm.session.Session` class, or
 
       instance thereof.
 
    """
 
    if isinstance(recorder, ChangeRecorder):
 
        pass
 
@@ -61,23 +61,15 @@ def record_changes(session, recorder=None, config=None):
 
    elif recorder is None:
 
        if config:
 
            app = config.get_app()
 
            spec = config.get('rattail.db', 'changes.recorder', usedb=False)
 
            spec = config.get('rattail.db.changes.recorder', usedb=False)
 
            if spec:
 
                recorder = app.load_object(spec)(config)
 
        if not recorder:
 
            recorder = ChangeRecorder(config)
 
    else:
 
        raise ValueError("Invalid 'recorder' parameter: {}".format(repr(recorder)))
 

	
 
    if listen:
 
        listen(session, 'before_flush', recorder)
 
    else:
 
        extension = ChangeRecorderExtension(recorder)
 
        if isinstance(session, Session):
 
            session.extensions.append(extension)
 
        else:
 
            session.configure(extension=extension)
 
        raise ValueError(f"recorder not valid: {recorder}")
 

	
 
    listen(session, 'before_flush', recorder)
 
    session.rattail_record_changes = True
 
    session.rattail_change_recorder = recorder
 

	
rattail/testing.py
Show inline comments
 
# -*- coding: utf-8; -*-
 

	
 
from wuttjamaican.testing import FileConfigTestCase
 
from wuttjamaican.testing import FileTestCase
 
from rattail.config import RattailConfig
 

	
 

	
 
class DataTestCase(FileConfigTestCase):
 
class DataTestCase(FileTestCase):
 
    """
 
    Base class for test suites requiring a full (typical) database.
 
    """
 
@@ -14,7 +14,9 @@ class DataTestCase(FileConfigTestCase):
 

	
 
    def setup_db(self):
 
        self.setup_files()
 
        self.config = self.make_config()
 
        self.config = self.make_config(defaults={
 
            'rattail.db.default.url': 'sqlite://',
 
        })
 
        self.app = self.config.get_app()
 

	
 
        # init db
 
@@ -28,7 +30,5 @@ class DataTestCase(FileConfigTestCase):
 
    def teardown_db(self):
 
        self.teardown_files()
 

	
 
    def make_config(self):
 
        return RattailConfig(defaults={
 
            'rattail.db.default.url': 'sqlite://',
 
        })
 
    def make_config(self, **kwargs):
 
        return RattailConfig(**kwargs)
tests/db/test_changes.py
Show inline comments
 
# -*- coding: utf-8; -*-
 

	
 
from unittest import TestCase
 
from unittest.mock import patch, DEFAULT, Mock, MagicMock, call
 
from unittest.mock import patch, DEFAULT, Mock, MagicMock
 

	
 
from rattail.config import RattailConfig
 
from rattail.testing import DataTestCase
 

	
 

	
 
try:
 
    from sqlalchemy import orm
 
    from rattail import db
 
    from rattail.db import changes as mod
 
    from rattail.db import changes, model
 
    from .. import DataTestCase
 
except ImportError:
 
    pass
 
else:
 

	
 
    class TestRecordChangesFunc(TestCase):
 

	
 
        def setUp(self):
 
            self.config = RattailConfig()
 
            self.app = self.config.get_app()
 

	
 
        def test_session_class(self):
 
            Session = orm.sessionmaker()
 
            if hasattr(Session, 'kw'):
 
                self.assertRaises(KeyError, Session.kw.__getitem__, 'rattail_record_changes')
 
            self.assertRaises(AttributeError, getattr, Session, 'rattail_record_changes')
 
            changes.record_changes(Session)
 
            self.assertTrue(Session.rattail_record_changes)
 

	
 
        def test_session_instance(self):
 
            session = db.Session()
 
            self.assertFalse(session.rattail_record_changes)
 
            changes.record_changes(session)
 
            self.assertTrue(session.rattail_record_changes)
 
            session.close()
 

	
 
        def test_recorder(self):
 

	
 
            # no recorder
 
            session = db.Session()
 
            self.assertRaises(AttributeError, getattr, session, 'rattail_change_recorder')
 
            session.close()
 

	
 
            # default recorder
 
            session = db.Session()
 
            changes.record_changes(session)
 
            self.assertIs(type(session.rattail_change_recorder), changes.ChangeRecorder)
 
            session.close()
 

	
 
            # specify recorder instance
 
            recorder = changes.ChangeRecorder(self.config)
 
            session = db.Session()
 
            changes.record_changes(session, recorder=recorder)
 
            self.assertIs(session.rattail_change_recorder, recorder)
 
            session.close()
 

	
 
            # specify recorder factory
 
            session = db.Session()
 
            changes.record_changes(session, recorder=changes.ChangeRecorder, config=self.config)
 
            self.assertIs(type(session.rattail_change_recorder), changes.ChangeRecorder)
 
            session.close()
 

	
 
            # specify recorder spec via config
 
            config = RattailConfig()
 
            config.setdefault('rattail.db', 'changes.recorder', 'rattail.db.changes:ChangeRecorder')
 
            session = db.Session()
 
            changes.record_changes(session, config=config)
 
            self.assertIs(type(session.rattail_change_recorder), changes.ChangeRecorder)
 
            session.close()
 

	
 
            # invalid recorder
 
            session = db.Session()
 
            self.assertRaises(ValueError, changes.record_changes, session, recorder='bogus')
 
            session.close()
 
    class MockRecorder:
 
        def __init__(self, config):
 
            pass
 

	
 

	
 
    class TestChangeRecorder(DataTestCase):
 
    class TestRecordChanges(DataTestCase):
 

	
 
        def extra_setup(self):
 
            self.config = RattailConfig()
 
            self.app = self.config.get_app()
 
        def test_recorder_instance(self):
 
            recorder = mod.ChangeRecorder(self.config)
 
            self.assertFalse(self.session.rattail_record_changes)
 
            mod.record_changes(self.session, recorder=recorder)
 
            self.assertTrue(self.session.rattail_record_changes)
 

	
 
        def test_recorder_factory(self):
 
            self.assertFalse(self.session.rattail_record_changes)
 
            mod.record_changes(self.session, recorder=mod.ChangeRecorder, config=self.config)
 
            self.assertTrue(self.session.rattail_record_changes)
 

	
 
        def test_configured_recorder(self):
 
            self.config.setdefault('rattail.db.changes.recorder', 'tests.db.test_changes:MockRecorder')
 
            self.assertFalse(self.session.rattail_record_changes)
 
            mod.record_changes(self.session, config=self.config)
 
            self.assertTrue(self.session.rattail_record_changes)
 

	
 
        def test_default_recorder(self):
 
            self.assertFalse(self.session.rattail_record_changes)
 
            mod.record_changes(self.session, config=self.config)
 
            self.assertTrue(self.session.rattail_record_changes)
 

	
 
        def test_invalid_recorder(self):
 
            self.assertFalse(self.session.rattail_record_changes)
 
            self.assertRaises(ValueError, mod.record_changes, self.session, recorder=42)
 

	
 

	
 
    class TestChangeRecorder(DataTestCase):
 

	
 
        def test_ignore_object(self):
 
            recorder = changes.ChangeRecorder(self.config)
 
@@ -311,8 +283,7 @@ else:
 
    class TestFunctionalChanges(DataTestCase):
 

	
 
        def setUp(self):
 
            super().setUp()
 
            self.config = RattailConfig()
 
            self.setup_db()
 
            changes.record_changes(self.session, config=self.config)
 

	
 
        def test_add(self):
tests/test_app.py
Show inline comments
 
@@ -16,11 +16,20 @@ from rattail.core import Object
 
from rattail.db import Session
 
from rattail.autocomplete import Autocompleter
 
from rattail.batch import BatchHandler
 
from rattail.bouncer import BounceHandler
 
from rattail.importing import ImportHandler
 
from rattail.gpc import GPC
 

	
 

	
 
try:
 
    from rattail.bouncer import BounceHandler
 
except ImportError:
 
    pass
 
else:
 

	
 
    class FooBarBounceHandler(BounceHandler):
 
        pass
 

	
 

	
 
class TestAppHandler(TestCase):
 

	
 
    def setUp(self):
 
@@ -251,6 +260,11 @@ class TestAppHandler(TestCase):
 

	
 
    def test_get_bounce_handler(self):
 

	
 
        try:
 
            from rattail.bouncer import BounceHandler
 
        except ImportError:
 
            pytest.skip("test not relevant without flufl.bounce")
 

	
 
        # unknown type raises error by default
 
        self.assertRaises(ValueError, self.app.get_bounce_handler, 'foobar')
 

	
 
@@ -827,10 +841,6 @@ class FooBarBatchHandler(BatchHandler):
 
    pass
 

	
 

	
 
class FooBarBounceHandler(BounceHandler):
 
    pass
 

	
 

	
 
class FromFooToBar(ImportHandler):
 
    host_key = 'rattail'
 
    local_key = 'rattail'
tox.ini
Show inline comments
 

	
 
[tox]
 
envlist = py38, py39, py310, py311, nodb
 
envlist = py38, py39, py310, py311, nox
 

	
 
[testenv]
 
extras = bouncer,db,tests
 
commands = pytest {posargs}
 

	
 
[testenv:nodb]
 
extras = bouncer,tests
 
[testenv:nox]
 
extras = tests
 

	
 
[testenv:coverage]
 
basepython = python3
0 comments (0 inline, 0 general)