Skip to content

Commit

Permalink
Merge pull request #5 from inspirehep/reset-changes
Browse files Browse the repository at this point in the history
sync fork
  • Loading branch information
MJedr authored Jul 11, 2023
2 parents 54628b0 + 5bcdb0e commit e89a11d
Show file tree
Hide file tree
Showing 29 changed files with 286 additions and 82 deletions.
73 changes: 73 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: "Test"

on:
push:
paths-ignore:
- "docs/**"
pull_request:
paths-ignore:
- "docs/**"
schedule:
- cron: '40 1 * * 3'


jobs:
test:
name: test-python${{ matrix.python-version }}-sa${{ matrix.sqlalchemy-version }}-${{ matrix.db-engine }}
strategy:
matrix:
python-version:
# - "2.7"
# - "3.4"
# - "3.5"
# - "3.6"
# - "3.7"
- "3.8"
# - "3.9"
# - "3.10"
# - "pypy-3.7"
sqlalchemy-version:
- "<1.4"
- ">=1.4,<2.0"
db-engine:
- sqlite
- postgres
- postgres-native
- mysql
runs-on: ubuntu-latest
services:
mysql:
image: mysql
ports:
- 3306:3306
env:
MYSQL_DATABASE: sqlalchemy_continuum_test
MYSQL_ALLOW_EMPTY_PASSWORD: yes
options: >-
--health-cmd "mysqladmin ping"
--health-interval 5s
--health-timeout 2s
--health-retries 3
postgres:
image: postgres
ports:
- 5432:5432
env:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: sqlalchemy_continuum_test
options: >-
--health-cmd pg_isready
--health-interval 5s
--health-timeout 2s
--health-retries 3
steps:
- uses: actions/checkout@v1
- name: Install sqlalchemy
run: pip3 install 'sqlalchemy${{ matrix.sqlalchemy-version }}'
- name: Build
run: pip3 install -e '.[test]'
- name: Run tests
run: pytest
env:
DB: ${{ matrix.db-engine }}

20 changes: 20 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,26 @@ Changelog

Here you can see the full list of changes between each SQLAlchemy-Continuum release.

1.3.14 (2023-01-04)
^^^^^^^^^^^^^^^^^^^

- Undo unneeded change to Flask-Login that breaks alternative IDs (#325, thanks to anthraxx)


1.3.13 (2022-09-07)
^^^^^^^^^^^^^^^^^^^

- Fixes for Flask 2.2 and Flask-Login 0.6.2 (#288, thanks to AbdealiJK)
- Allow changed_entities to work without TransactionChanges plugin (#268, thanks to TomGoBravo)
- Fix Activity plugin for non-composite primary keys not named id (#210, thanks to dryobates)
- Allow sync_trigger to pass arguments through to create_trigger (#273, thanks to nanvel)
- Fix association tables on Oracle (#291, thanks to AbdealiJK)
- Fix some deprecation warnings in SA 1.4 (#269, #277, #279, #300, #302, thanks to TomGoBravo, edhaz, and indiVar0508)

1.3.12 (2022-01-18)
^^^^^^^^^^^^^^^^^^^

- Support SA 1.4

1.3.11 (2020-05-24)
^^^^^^^^^^^^^^^^^^^
Expand Down
5 changes: 3 additions & 2 deletions LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ modification, are permitted provided that the following conditions are met:
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

* The names of the contributors may not be used to endorse or promote products
derived from this software without specific prior written permission.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
Expand Down
4 changes: 2 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ Resources
.. image:: http://i.imgur.com/UFaRx.gif


.. |Build Status| image:: https://travis-ci.org/kvesteri/sqlalchemy-continuum.png?branch=master
:target: https://travis-ci.org/kvesteri/sqlalchemy-continuum
.. |Build Status| image:: https://github.com/kvesteri/sqlalchemy-continuum/workflows/Test/badge.svg
:target: https://github.com/kvesteri/sqlalchemy-continuum/actions?query=workflow%3ATest
.. |Version Status| image:: https://img.shields.io/pypi/v/SQLAlchemy-Continuum.png
:target: https://pypi.python.org/pypi/SQLAlchemy-Continuum/
.. |Downloads| image:: https://img.shields.io/pypi/dm/SQLAlchemy-Continuum.png
Expand Down
6 changes: 3 additions & 3 deletions benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import sqlalchemy as sa
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import sessionmaker, close_all_sessions
from sqlalchemy_continuum import (
make_versioned,
versioning_manager,
Expand Down Expand Up @@ -50,7 +50,7 @@ def test_versioning(

make_versioned(options=options)

dns = 'postgres://postgres@localhost/sqlalchemy_continuum_test'
dns = 'postgresql://postgres:postgres@localhost/sqlalchemy_continuum_test'
versioning_manager.plugins = plugins
versioning_manager.transaction_cls = transaction_cls
versioning_manager.user_cls = user_cls
Expand Down Expand Up @@ -106,7 +106,7 @@ class Tag(Model):
remove_versioning()
versioning_manager.reset()

session.close_all()
close_all_sessions()
session.expunge_all()
Model.metadata.drop_all(connection)
engine.dispose()
Expand Down
6 changes: 6 additions & 0 deletions docs/native_versioning.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,9 @@ When making schema migrations (for example adding new columns to version tables)


sync_trigger(conn, 'article_version')

If you don't use `PropertyModTrackerPlugin`, then you have to disable it:

::

sync_trigger(conn, 'article_version', use_property_mod_tracking=False)
2 changes: 1 addition & 1 deletion docs/version_objects.rst
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ property which holds a dict of changed fields in that version.
# }

session.delete(article)
version = article.versions[1]
version = article.versions[2]
version.changeset
# {
# 'id': [1, None]
Expand Down
10 changes: 5 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,14 @@ def get_version():
'pytest>=2.3.5',
'flexmock>=0.9.7',
'psycopg2>=2.4.6',
'PyMySQL==0.6.1',
'PyMySQL>=0.8.0',
'six>=1.4.0'
],
'anyjson': ['anyjson>=0.3.3'],
'flask': ['Flask>=0.9'],
'flask-login': ['Flask-Login>=0.2.9'],
'flask-sqlalchemy': ['Flask-SQLAlchemy>=1.0'],
'flask-sqlalchemy': ['Flask-SQLAlchemy>=1.0,<3.0.0'],
'flexmock': ['flexmock>=0.9.7'],
'i18n': ['SQLAlchemy-i18n>=0.8.4'],
'i18n': ['SQLAlchemy-i18n>=0.8.4,!=1.1.0'],
}


Expand Down Expand Up @@ -65,7 +64,8 @@ def get_version():
platforms='any',
install_requires=[
'SQLAlchemy>=1.0.8',
'SQLAlchemy-Utils>=0.30.12'
'SQLAlchemy-Utils>=0.30.12',
'six',
],
extras_require=extras_require,
classifiers=[
Expand Down
2 changes: 1 addition & 1 deletion sqlalchemy_continuum/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
)


__version__ = '1.3.11'
__version__ = '1.3.15'


versioning_manager = VersioningManager()
Expand Down
14 changes: 14 additions & 0 deletions sqlalchemy_continuum/builder.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from copy import copy
from inspect import getmro
from functools import wraps

import sqlalchemy as sa
from sqlalchemy_utils.functions import get_declarative_base
Expand All @@ -10,6 +11,18 @@
from .table_builder import TableBuilder


def prevent_reentry(handler):
in_handler = False
@wraps(handler)
def check_reentry(*args, **kwargs):
nonlocal in_handler
if in_handler:
return
in_handler = True
handler(*args, **kwargs)
in_handler = False
return check_reentry

class Builder(object):
def build_triggers(self):
"""
Expand Down Expand Up @@ -141,6 +154,7 @@ def build_transaction_class(self):
self.manager.create_transaction_model()
self.manager.plugins.after_build_tx_class(self.manager)

@prevent_reentry
def configure_versioned_classes(self):
"""
Configures all versioned classes that were collected during
Expand Down
5 changes: 3 additions & 2 deletions sqlalchemy_continuum/dialects/postgresql.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ def create_versioning_trigger_listeners(manager, cls):
)


def sync_trigger(conn, table_name):
def sync_trigger(conn, table_name, **kwargs):
"""
Synchronizes versioning trigger for given table with given connection.
Expand All @@ -468,6 +468,7 @@ def sync_trigger(conn, table_name):
:param conn: SQLAlchemy connection object
:param table_name: Name of the table to synchronize versioning trigger for
:params **kwargs: kwargs to pass to create_trigger
.. versionadded: 1.1.0
"""
Expand All @@ -489,7 +490,7 @@ def sync_trigger(conn, table_name):
set(c.name for c in version_table.c if not c.name.endswith('_mod'))
)
drop_trigger(conn, parent_table.name)
create_trigger(conn, table=parent_table, excluded_columns=excluded_columns)
create_trigger(conn, table=parent_table, excluded_columns=excluded_columns, **kwargs)


def create_trigger(
Expand Down
6 changes: 5 additions & 1 deletion sqlalchemy_continuum/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ def __call__(self, manager):
Create model class but only if it doesn't already exist
in declarative model registry.
"""
registry = manager.declarative_base._decl_class_registry
Base = manager.declarative_base
try:
registry = Base.registry._class_registry
except AttributeError: # SQLAlchemy < 1.4
registry = Base._decl_class_registry
if self.model_name not in registry:
return self.create_class(manager)
return registry[self.model_name]
23 changes: 15 additions & 8 deletions sqlalchemy_continuum/fetcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def _transaction_id_subquery(self, obj, next_or_prev='next', alias=None):
func = sa.func.max

if alias is None:
alias = sa.orm.aliased(obj)
alias = sa.orm.aliased(obj.__class__)
table = alias.__table__
if hasattr(alias, 'c'):
attrs = alias.c
Expand Down Expand Up @@ -90,23 +90,30 @@ def _transaction_id_subquery(self, obj, next_or_prev='next', alias=None):
)
.correlate(table)
)
return query
try:
return query.scalar_subquery()
except AttributeError: # SQLAlchemy < 1.4
return query.as_scalar()

def _next_prev_query(self, obj, next_or_prev='next'):
session = sa.orm.object_session(obj)

subquery = self._transaction_id_subquery(
obj, next_or_prev=next_or_prev
)
try:
subquery = subquery.scalar_subquery()
except AttributeError: # SQLAlchemy < 1.4
subquery = subquery.as_scalar()

return (
session.query(obj.__class__)
.filter(
sa.and_(
getattr(
obj.__class__,
tx_column_name(obj)
)
==
self._transaction_id_subquery(
obj, next_or_prev=next_or_prev
),
) == subquery,
*parent_criteria(obj)
)
)
Expand All @@ -117,7 +124,7 @@ def _index_query(self, obj):
Returns the query needed for fetching the index of this record relative
to version history.
"""
alias = sa.orm.aliased(obj)
alias = sa.orm.aliased(obj.__class__)

subquery = (
sa.select([sa.func.count('1')], from_obj=[alias.__table__])
Expand Down
3 changes: 1 addition & 2 deletions sqlalchemy_continuum/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,11 +402,10 @@ def append_association_operation(self, conn, table_name, params, op):
"""
Append history association operation to pending_statements list.
"""
params['operation_type'] = op
stmt = (
self.metadata.tables[self.options['table_name'] % table_name]
.insert()
.values(params)
.values({**params, 'operation_type': op})
)
try:
uow = self.units_of_work[conn]
Expand Down
7 changes: 5 additions & 2 deletions sqlalchemy_continuum/plugins/activity.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@

import sqlalchemy as sa
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.inspection import inspect
from sqlalchemy_utils import JSONType, generic_relationship

from .base import Plugin
Expand Down Expand Up @@ -254,11 +255,13 @@ def _calculate_tx_id(self, obj):
if object_version:
return object_version.transaction_id

version_cls = version_class(obj.__class__)
model = obj.__class__
version_cls = version_class(model)
primary_key = inspect(model).primary_key[0].name
return session.query(
sa.func.max(version_cls.transaction_id)
).filter(
version_cls.id == obj.id
getattr(version_cls, primary_key) == getattr(obj, primary_key)
).scalar()

def calculate_object_tx_id(self):
Expand Down
Loading

0 comments on commit e89a11d

Please sign in to comment.