Skip to content

Commit

Permalink
Merge pull request #11 from trialspark/josh__roll-back-continuum-upda…
Browse files Browse the repository at this point in the history
…te-a-bit

Roll back full upstream update, only add SA 1.3 compat commit
  • Loading branch information
minznerjosh authored May 28, 2019
2 parents 58f920d + b8bece9 commit d4a87ba
Show file tree
Hide file tree
Showing 21 changed files with 25 additions and 471 deletions.
6 changes: 0 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,3 @@ nosetests.xml
.mr.developer.cfg
.project
.pydevproject

# mypy
.mypy_cache/

# Unit test / coverage reports
.cache
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ before_script:
language: python
python:
- 2.7
- 3.3
- 3.4
- 3.5
- 3.6
install:
- pip install -e ".[test]"
script:
Expand Down
45 changes: 1 addition & 44 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,53 +4,10 @@ Changelog
Here you can see the full list of changes between each SQLAlchemy-Continuum release.


1.3.9 (2019-03-19)
1.3.12 (2019-03-18)
^^^^^^^^^^^^^^^^^^

- Added SA 1.3 support
- Reverted trigger creation from 1.3.7


1.3.8 (2019-02-27)
^^^^^^^^^^^^^^^^^^

- Fixed revert to ignore non-columns (#197, courtesy of mauler)


1.3.7 (2019-01-13)
^^^^^^^^^^^^^^^^^^

- Fix trigger creation during alembic migrations (#209, courtesy of lyndsysimon)


1.3.6 (2018-07-30)
^^^^^^^^^^^^^^^^^^

- Fixed ResourceClosedErrors from connections leaking when using an external transaction (#196, courtesy of vault)


1.3.5 (2018-06-03)
^^^^^^^^^^^^^^^^^^

- Track cloned connections (#167, courtesy of netcriptus)


1.3.4 (2018-03-07)
^^^^^^^^^^^^^^^^^^

- Exclude many-to-many properties from versioning if they are added in exclude parameter (#169, courtesy of fuhrysteve)


1.3.3 (2017-11-05)
^^^^^^^^^^^^^^^^^^

- Fixed changeset when updating object in same transaction as inserting it (#141, courtesy of oinopion)


1.3.2 (2017-10-12)
^^^^^^^^^^^^^^^^^^

- Fixed multiple schema handling (#132, courtesy of vault)


1.3.1 (2017-06-28)
Expand Down
4 changes: 2 additions & 2 deletions docs/intro.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Introduction
Why?
^^^^

SQLAlchemy already has a versioning extension. This extension however is very limited. It does not support versioning entire transactions.
SQLAlchemy already has versioning extension. This extension however is very limited. It does not support versioning entire transactions.

Hibernate for Java has Envers, which had nice features but lacks a nice API. Ruby on Rails has papertrail_, which has very nice API but lacks the efficiency and feature set of Envers.

Expand Down Expand Up @@ -54,7 +54,7 @@ In order to make your models versioned you need two things:
from sqlalchemy_continuum import make_versioned


make_versioned(user_cls=None)
make_versioned()


class Article(Base):
Expand Down
2 changes: 1 addition & 1 deletion docs/plugins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Using plugins

::

from sqlalchemy_continuum.plugins import PropertyModTrackerPlugin
from sqlalchemy.continuum.plugins import PropertyModTrackerPlugin


versioning_manager.plugins.append(PropertyModTrackerPlugin())
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,9 @@ def get_version():
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
'Topic :: Software Development :: Libraries :: Python Modules'
]
Expand Down
26 changes: 1 addition & 25 deletions sqlalchemy_continuum/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
)


__version__ = '1.3.11'
__version__ = '1.3.12'


versioning_manager = VersioningManager()
Expand Down Expand Up @@ -72,18 +72,6 @@ def make_versioned(
manager.track_association_operations
)

sa.event.listen(
sa.engine.Engine,
'rollback',
manager.clear_connection
)

sa.event.listen(
sa.engine.Engine,
'set_connection_execution_options',
manager.track_cloned_connections
)


def remove_versioning(
mapper=sa.orm.mapper,
Expand All @@ -110,15 +98,3 @@ def remove_versioning(
'before_cursor_execute',
manager.track_association_operations
)

sa.event.remove(
sa.engine.Engine,
'rollback',
manager.clear_connection
)

sa.event.remove(
sa.engine.Engine,
'set_connection_execution_options',
manager.track_cloned_connections
)
54 changes: 2 additions & 52 deletions sqlalchemy_continuum/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,7 @@ def wrapper(self, mapper, connection, target):
try:
uow = self.units_of_work[conn]
except KeyError:
try:
uow = self.units_of_work[conn.engine]
except KeyError:
for connection in self.units_of_work.keys():
if not connection.closed and connection.connection is conn.connection:
uow = self.unit_of_work(session)
break # The ConnectionFairy is the same, this connection is a clone
else:
raise
uow = self.units_of_work[conn.engine]
return func(self, uow, target)
return wrapper

Expand Down Expand Up @@ -366,39 +358,11 @@ def clear(self, session):
if session.transaction.nested:
return
conn = self.session_connection_map.pop(session, None)
if conn is None:
return

if conn in self.units_of_work:
uow = self.units_of_work[conn]
uow.reset(session)
del self.units_of_work[conn]

for connection in dict(self.units_of_work).keys():
if connection.closed or conn.connection is connection.connection:
uow = self.units_of_work[connection]
uow.reset(session)
del self.units_of_work[connection]

def clear_connection(self, conn):
if conn in self.units_of_work:
uow = self.units_of_work[conn]
uow.reset()
del self.units_of_work[conn]


for session, connection in dict(self.session_connection_map).items():
if connection is conn:
del self.session_connection_map[session]


for connection in dict(self.units_of_work).keys():
if connection.closed or conn.connection is connection.connection:
uow = self.units_of_work[connection]
uow.reset()
del self.units_of_work[connection]


def append_association_operation(self, conn, table_name, params, op):
"""
Append history association operation to pending_statements list.
Expand All @@ -425,23 +389,9 @@ def append_association_operation(self, conn, table_name, params, op):
try:
uow = self.units_of_work[conn.engine]
except KeyError:
for connection in self.units_of_work.keys():
if not connection.closed and connection.connection is conn.connection:
uow = self.unit_of_work(conn.session)
break # The ConnectionFairy is the same, this connection is a clone
else:
return
return
uow.pending_statements.append(stmt)

def track_cloned_connections(self, c, opt):
"""
Track cloned connections from association tables.
"""
if c not in self.units_of_work.keys():
for connection, uow in dict(self.units_of_work).items():
if not connection.closed and connection.connection is c.connection: # ConnectionFairy is the same - this is a clone
self.units_of_work[c] = uow

def track_association_operations(
self, conn, cursor, statement, parameters, context, executemany
):
Expand Down
5 changes: 1 addition & 4 deletions sqlalchemy_continuum/relationship_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,10 +344,7 @@ def __call__(self):
except ClassNotVersioned:
self.remote_cls = self.property.mapper.class_

if (self.property.secondary is not None and
not self.property.viewonly and
not self.manager.is_excluded_property(
self.model, self.property.key)):
if self.property.secondary is not None and not self.property.viewonly:
self.build_association_version_tables()

# store remote cls to association table column pairs
Expand Down
2 changes: 1 addition & 1 deletion sqlalchemy_continuum/table_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,6 @@ def __call__(self, extends=None):
extends.name if extends is not None else self.table_name,
self.parent_table.metadata,
*columns,
schema=apply_table_schema(self.option('table_schema'), self.parent_table.schema),
extend_existing=extends is not None,
schema=apply_table_schema(self.option('table_schema'), self.parent_table.schema)
)
1 change: 0 additions & 1 deletion sqlalchemy_continuum/transaction.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from datetime import datetime
from functools import partial

try:
from collections import OrderedDict
Expand Down
36 changes: 7 additions & 29 deletions sqlalchemy_continuum/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from types import LambdaType

import six

import sqlalchemy as sa
from sqlalchemy.orm.attributes import get_history
from sqlalchemy.orm.util import AliasedClass
Expand Down Expand Up @@ -231,11 +230,7 @@ def versioned_column_properties(obj_or_class):
cls = obj_or_class if isclass(obj_or_class) else obj_or_class.__class__

mapper = sa.inspect(cls)
for key, column in mapper.columns.items():
# Ignores non table columns
if not is_table_column(column):
continue

for key in mapper.columns.keys():
if not manager.is_excluded_property(obj_or_class, key):
yield getattr(mapper.attrs, key)

Expand All @@ -252,7 +247,7 @@ def versioned_relationships(obj, versioned_column_keys):
yield prop


def vacuum(session, model, yield_per=1000):
def vacuum(session, model):
"""
When making structural changes to version tables (for example dropping
columns) there are sometimes situations where some old version records
Expand All @@ -273,36 +268,22 @@ def vacuum(session, model, yield_per=1000):
:param session: SQLAlchemy session object
:param model: SQLAlchemy declarative model class
:param yield_per: how many rows to process at a time
"""
version_cls = version_class(model)
versions = defaultdict(list)

query = (
session.query(version_cls)
.order_by(option(version_cls, 'transaction_column_name'))
).yield_per(yield_per)

primary_key_col = sa.inspection.inspect(model).primary_key[0].name
)

for version in query:
version_id = getattr(version, primary_key_col)
if versions[version_id]:
prev_version = versions[version_id][-1]
if versions[version.id]:
prev_version = versions[version.id][-1]
if naturally_equivalent(prev_version, version):
session.delete(version)
else:
versions[version_id].append(version)


def is_table_column(column):
"""
Return wheter of not give field is a column over the database table.
:param column: SQLAclhemy model field.
:rtype: bool
"""
return isinstance(column, sa.Column)
versions[version.id].append(version)


def is_internal_column(model, column_name):
Expand Down Expand Up @@ -448,10 +429,7 @@ def changeset(obj):
data = {}
session = sa.orm.object_session(obj)
if session and obj in session.deleted:
columns = [c for c in sa.inspect(obj.__class__).columns.values()
if is_table_column(c)]

for column in columns:
for column in sa.inspect(obj.__class__).columns.values():
if not column.primary_key:
value = getattr(obj, column.key)
if value is not None:
Expand Down
4 changes: 3 additions & 1 deletion sqlalchemy_continuum/version.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import sqlalchemy as sa

from .reverter import Reverter
from .utils import get_versioning_manager, is_internal_column, parent_class

Expand Down Expand Up @@ -50,6 +49,9 @@ def changeset(self):
and second list value as the new value.
"""
previous_version = self.previous
if not previous_version and self.operation_type != 0:
return {}

data = {}

for key in sa.inspect(self.__class__).columns.keys():
Expand Down
6 changes: 1 addition & 5 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

from copy import copy
import inspect
import itertools as it
Expand All @@ -7,7 +6,7 @@
import sqlalchemy as sa
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, column_property
from sqlalchemy.orm import sessionmaker
from sqlalchemy_continuum import (
ClassNotVersioned,
version_class,
Expand Down Expand Up @@ -163,9 +162,6 @@ class Article(self.Model):
content = sa.Column(sa.UnicodeText)
description = sa.Column(sa.UnicodeText)

# Dynamic column cotaining all text content data
fulltext_content = column_property(name + content + description)

class Tag(self.Model):
__tablename__ = 'tag'
__versioned__ = copy(self.options)
Expand Down
Loading

0 comments on commit d4a87ba

Please sign in to comment.