Hello community,
here is the log from the commit of package python-SQLAlchemy-Utils for
openSUSE:Factory checked in at 2016-11-24 21:22:40
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-SQLAlchemy-Utils (Old)
and /work/SRC/openSUSE:Factory/.python-SQLAlchemy-Utils.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-SQLAlchemy-Utils"
Changes:
--------
---
/work/SRC/openSUSE:Factory/python-SQLAlchemy-Utils/python-SQLAlchemy-Utils.changes
2016-10-10 16:20:03.000000000 +0200
+++
/work/SRC/openSUSE:Factory/.python-SQLAlchemy-Utils.new/python-SQLAlchemy-Utils.changes
2016-11-24 21:22:41.000000000 +0100
@@ -1,0 +2,7 @@
+Mon Nov 14 13:59:20 UTC 2016 - [email protected]
+
+- udpate to 0.32.9:
+ - Added support for multi-column observers (#231, pull request courtesy of
quantus)
+ - Fixed EmailType to respect constructor args (#230, pull request courtesy
of quantus)
+
+-------------------------------------------------------------------
Old:
----
SQLAlchemy-Utils-0.32.7.tar.gz
New:
----
SQLAlchemy-Utils-0.32.9.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-SQLAlchemy-Utils.spec ++++++
--- /var/tmp/diff_new_pack.RVW1ch/_old 2016-11-24 21:22:42.000000000 +0100
+++ /var/tmp/diff_new_pack.RVW1ch/_new 2016-11-24 21:22:42.000000000 +0100
@@ -17,7 +17,7 @@
Name: python-SQLAlchemy-Utils
-Version: 0.32.7
+Version: 0.32.9
Release: 0
Summary: Various utility functions for SQLAlchemy
License: BSD-3-Clause
++++++ SQLAlchemy-Utils-0.32.7.tar.gz -> SQLAlchemy-Utils-0.32.9.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/SQLAlchemy-Utils-0.32.7/CHANGES.rst
new/SQLAlchemy-Utils-0.32.9/CHANGES.rst
--- old/SQLAlchemy-Utils-0.32.7/CHANGES.rst 2016-05-20 09:01:16.000000000
+0200
+++ new/SQLAlchemy-Utils-0.32.9/CHANGES.rst 2016-07-17 22:00:14.000000000
+0200
@@ -4,6 +4,18 @@
Here you can see the full list of changes between each SQLAlchemy-Utils
release.
+0.32.9 (2016-07-17)
+^^^^^^^^^^^^^^^^^^^
+
+- Added support for multi-column observers (#231, pull request courtesy of
quantus)
+
+
+0.32.8 (2016-05-20)
+^^^^^^^^^^^^^^^^^^^
+
+- Fixed EmailType to respect constructor args (#230, pull request courtesy of
quantus)
+
+
0.32.7 (2016-05-20)
^^^^^^^^^^^^^^^^^^^
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/SQLAlchemy-Utils-0.32.7/PKG-INFO
new/SQLAlchemy-Utils-0.32.9/PKG-INFO
--- old/SQLAlchemy-Utils-0.32.7/PKG-INFO 2016-05-30 11:40:44.000000000
+0200
+++ new/SQLAlchemy-Utils-0.32.9/PKG-INFO 2016-07-17 22:02:35.000000000
+0200
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: SQLAlchemy-Utils
-Version: 0.32.7
+Version: 0.32.9
Summary: Various utility functions for SQLAlchemy.
Home-page: https://github.com/kvesteri/sqlalchemy-utils
Author: Konsta Vesterinen, Ryan Leckey, Janne Vanhala, Vesa Uimonen
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/SQLAlchemy-Utils-0.32.7/README.rst
new/SQLAlchemy-Utils-0.32.9/README.rst
--- old/SQLAlchemy-Utils-0.32.7/README.rst 2015-08-16 10:02:58.000000000
+0200
+++ new/SQLAlchemy-Utils-0.32.9/README.rst 2016-07-10 11:03:30.000000000
+0200
@@ -10,7 +10,7 @@
Resources
---------
-- `Documentation <http://sqlalchemy-utils.readthedocs.org/>`_
+- `Documentation <https://sqlalchemy-utils.readthedocs.io/>`_
- `Issue Tracker <http://github.com/kvesteri/sqlalchemy-utils/issues>`_
- `Code <http://github.com/kvesteri/sqlalchemy-utils/>`_
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/SQLAlchemy-Utils-0.32.7/SQLAlchemy_Utils.egg-info/PKG-INFO
new/SQLAlchemy-Utils-0.32.9/SQLAlchemy_Utils.egg-info/PKG-INFO
--- old/SQLAlchemy-Utils-0.32.7/SQLAlchemy_Utils.egg-info/PKG-INFO
2016-05-30 11:40:43.000000000 +0200
+++ new/SQLAlchemy-Utils-0.32.9/SQLAlchemy_Utils.egg-info/PKG-INFO
2016-07-17 22:02:34.000000000 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: SQLAlchemy-Utils
-Version: 0.32.7
+Version: 0.32.9
Summary: Various utility functions for SQLAlchemy.
Home-page: https://github.com/kvesteri/sqlalchemy-utils
Author: Konsta Vesterinen, Ryan Leckey, Janne Vanhala, Vesa Uimonen
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/SQLAlchemy-Utils-0.32.7/docs/installation.rst
new/SQLAlchemy-Utils-0.32.9/docs/installation.rst
--- old/SQLAlchemy-Utils-0.32.7/docs/installation.rst 2016-01-20
08:23:47.000000000 +0100
+++ new/SQLAlchemy-Utils-0.32.9/docs/installation.rst 2016-07-17
22:00:22.000000000 +0200
@@ -8,7 +8,7 @@
SQLAlchemy-Utils has been tested against the following Python platforms.
-- cPython 2.6
+- cPython 2.6 (unsupported since 0.32)
- cPython 2.7
- cPython 3.3
- cPython 3.4
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/SQLAlchemy-Utils-0.32.7/setup.cfg
new/SQLAlchemy-Utils-0.32.9/setup.cfg
--- old/SQLAlchemy-Utils-0.32.7/setup.cfg 2016-05-30 11:40:44.000000000
+0200
+++ new/SQLAlchemy-Utils-0.32.9/setup.cfg 2016-07-17 22:02:35.000000000
+0200
@@ -1,5 +1,5 @@
[egg_info]
+tag_date = 0
tag_build =
tag_svn_revision = 0
-tag_date = 0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/SQLAlchemy-Utils-0.32.7/sqlalchemy_utils/__init__.py
new/SQLAlchemy-Utils-0.32.9/sqlalchemy_utils/__init__.py
--- old/SQLAlchemy-Utils-0.32.7/sqlalchemy_utils/__init__.py 2016-05-20
09:01:30.000000000 +0200
+++ new/SQLAlchemy-Utils-0.32.9/sqlalchemy_utils/__init__.py 2016-07-17
22:00:12.000000000 +0200
@@ -95,4 +95,4 @@
WeekDaysType
)
-__version__ = '0.32.7'
+__version__ = '0.32.9'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/SQLAlchemy-Utils-0.32.7/sqlalchemy_utils/observer.py
new/SQLAlchemy-Utils-0.32.9/sqlalchemy_utils/observer.py
--- old/SQLAlchemy-Utils-0.32.7/sqlalchemy_utils/observer.py 2016-04-25
16:20:06.000000000 +0200
+++ new/SQLAlchemy-Utils-0.32.9/sqlalchemy_utils/observer.py 2016-07-17
22:00:22.000000000 +0200
@@ -148,6 +148,29 @@
session.commit()
catalog.product_count # 1
+
+Observing multiple columns
+-----------------------
+
+You can also observe multiple columns by spesifying all the observable columns
+in the decorator.
+
+
+::
+
+ class Order(Base):
+ __tablename__ = 'order'
+ id = sa.Column(sa.Integer, primary_key=True)
+ unit_price = sa.Column(sa.Integer)
+ amount = sa.Column(sa.Integer)
+ total_price = sa.Column(sa.Integer)
+
+ @observes('amount', 'unit_price')
+ def total_price_observer(self, amount, unit_price):
+ self.total_price = amount * unit_price
+
+
+
"""
import itertools
from collections import defaultdict, Iterable, namedtuple
@@ -158,7 +181,7 @@
from .path import AttrPath
from .utils import is_sequence
-Callback = namedtuple('Callback', ['func', 'path', 'backref', 'fullpath'])
+Callback = namedtuple('Callback', ['func', 'backref', 'fullpath'])
class PropertyObserver(object):
@@ -208,32 +231,33 @@
)
def gather_paths(self):
- for class_, callbacks in self.generator_registry.items():
- for callback in callbacks:
- path = AttrPath(class_, callback.__observes__)
-
- self.callback_map[class_].append(
- Callback(
- func=callback,
- path=path,
- backref=None,
- fullpath=path
+ for class_, generators in self.generator_registry.items():
+ for callback in generators:
+ full_paths = []
+ for call_path in callback.__observes__:
+ full_paths.append(AttrPath(class_, call_path))
+
+ for path in full_paths:
+ self.callback_map[class_].append(
+ Callback(
+ func=callback,
+ backref=None,
+ fullpath=full_paths
+ )
)
- )
- for index in range(len(path)):
- i = index + 1
- prop = path[index].property
- if isinstance(prop, sa.orm.RelationshipProperty):
- prop_class = path[index].property.mapper.class_
- self.callback_map[prop_class].append(
- Callback(
- func=callback,
- path=path[i:],
- backref=~ (path[:i]),
- fullpath=path
+ for index in range(len(path)):
+ i = index + 1
+ prop = path[index].property
+ if isinstance(prop, sa.orm.RelationshipProperty):
+ prop_class = path[index].property.mapper.class_
+ self.callback_map[prop_class].append(
+ Callback(
+ func=callback,
+ backref=~ (path[:i]),
+ fullpath=full_paths
+ )
)
- )
def gather_callback_args(self, obj, callbacks):
for callback in callbacks:
@@ -252,18 +276,19 @@
def get_callback_args(self, root_obj, callback):
session = sa.orm.object_session(root_obj)
- objects = getdotattr(
+ objects = [getdotattr(
root_obj,
- callback.fullpath,
+ path,
lambda obj: obj not in session.deleted
- )
- path = str(callback.fullpath)
- if '.' in path or has_changes(root_obj, path):
- return (
- root_obj,
- callback.func,
- objects
- )
+ ) for path in callback.fullpath]
+ paths = [str(path) for path in callback.fullpath]
+ for path in paths:
+ if '.' in path or has_changes(root_obj, path):
+ return (
+ root_obj,
+ callback.func,
+ objects
+ )
def iterate_objects_and_callbacks(self, session):
objs = itertools.chain(session.new, session.dirty, session.deleted)
@@ -277,21 +302,25 @@
for obj, callbacks in self.iterate_objects_and_callbacks(session):
args = self.gather_callback_args(obj, callbacks)
for (root_obj, func, objects) in args:
- if is_sequence(objects):
- callback_args[root_obj][func] = (
- callback_args[root_obj][func] | set(objects)
- )
- else:
- callback_args[root_obj][func] = objects
+ if not callback_args[root_obj][func]:
+ callback_args[root_obj][func] = {}
+ for i, object_ in enumerate(objects):
+ if is_sequence(object_):
+ callback_args[root_obj][func][i] = (
+ callback_args[root_obj][func].get(i, set()) |
+ set(object_)
+ )
+ else:
+ callback_args[root_obj][func][i] = object_
for root_obj, callback_objs in callback_args.items():
for callback, objs in callback_objs.items():
- callback(root_obj, objs)
+ callback(root_obj, *[objs[i] for i in range(len(objs))])
observer = PropertyObserver()
-def observes(path, observer=observer):
+def observes(*paths, **observer_kw):
"""
Mark method as property observer for the given property path. Inside
transaction observer gathers all changes made in given property path and
@@ -327,14 +356,17 @@
.. versionadded: 0.28.0
- :param path: Dot-notated property path, eg. 'categories.products.price'
- :param observer: :meth:`PropertyObserver` object
+ :param *paths: One or more dot-notated property paths, eg.
+ 'categories.products.price'
+ :param **observer: A dictionary where value for key 'observer' contains
+ :meth:`PropertyObserver` object
"""
- observer.register_listeners()
+ observer_ = observer_kw.pop('observer', observer)
+ observer_.register_listeners()
def wraps(func):
def wrapper(self, *args, **kwargs):
return func(self, *args, **kwargs)
- wrapper.__observes__ = path
+ wrapper.__observes__ = paths
return wrapper
return wraps
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/SQLAlchemy-Utils-0.32.7/sqlalchemy_utils/types/email.py
new/SQLAlchemy-Utils-0.32.9/sqlalchemy_utils/types/email.py
--- old/SQLAlchemy-Utils-0.32.7/sqlalchemy_utils/types/email.py 2016-04-25
16:20:06.000000000 +0200
+++ new/SQLAlchemy-Utils-0.32.9/sqlalchemy_utils/types/email.py 2016-07-10
11:03:30.000000000 +0200
@@ -4,9 +4,12 @@
class EmailType(sa.types.TypeDecorator):
- impl = sa.Unicode(255)
+ impl = sa.Unicode
comparator_factory = CaseInsensitiveComparator
+ def __init__(self, length=255, *args, **kwargs):
+ super(EmailType, self).__init__(length=length, *args, **kwargs)
+
def process_bind_param(self, value, dialect):
if value is not None:
return value.lower()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/SQLAlchemy-Utils-0.32.7/sqlalchemy_utils/types/encrypted.py
new/SQLAlchemy-Utils-0.32.9/sqlalchemy_utils/types/encrypted.py
--- old/SQLAlchemy-Utils-0.32.7/sqlalchemy_utils/types/encrypted.py
2016-04-25 16:20:06.000000000 +0200
+++ new/SQLAlchemy-Utils-0.32.9/sqlalchemy_utils/types/encrypted.py
2016-07-10 11:03:30.000000000 +0200
@@ -13,7 +13,7 @@
import cryptography
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
- from cryptography.hazmat.primitives.ciphers import(
+ from cryptography.hazmat.primitives.ciphers import (
Cipher, algorithms, modes
)
from cryptography.fernet import Fernet
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/SQLAlchemy-Utils-0.32.7/sqlalchemy_utils/types/password.py
new/SQLAlchemy-Utils-0.32.9/sqlalchemy_utils/types/password.py
--- old/SQLAlchemy-Utils-0.32.7/sqlalchemy_utils/types/password.py
2016-04-25 16:20:06.000000000 +0200
+++ new/SQLAlchemy-Utils-0.32.9/sqlalchemy_utils/types/password.py
2016-07-10 11:03:30.000000000 +0200
@@ -79,13 +79,14 @@
class PasswordType(types.TypeDecorator, ScalarCoercible):
"""
PasswordType hashes passwords as they come into the database and allows
- verifying them using a pythonic interface.
+ verifying them using a Pythonic interface. This Pythonic interface
+ relies on setting up automatic data type coercison using the
+ :func:`~sqlalchemy_utils.listeners.force_auto_coercion` function.
All keyword arguments (aside from max_length) are forwarded to the
construction of a `passlib.context.LazyCryptContext` object, which
also supports deferred configuration via the `onload` callback.
-
The following usage will create a password column that will
automatically hash new passwords as `pbkdf2_sha512` but still compare
passwords against pre-existing `md5_crypt` hashes. As passwords are
@@ -124,6 +125,9 @@
import flask
+ from sqlalchemy_utils import PasswordType, force_auto_coercion
+
+ force_auto_coercion()
class User(db.Model):
__tablename__ = 'user'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/SQLAlchemy-Utils-0.32.7/tests/observes/test_column_property.py
new/SQLAlchemy-Utils-0.32.9/tests/observes/test_column_property.py
--- old/SQLAlchemy-Utils-0.32.7/tests/observes/test_column_property.py
2016-04-25 16:20:06.000000000 +0200
+++ new/SQLAlchemy-Utils-0.32.9/tests/observes/test_column_property.py
2016-07-17 22:00:22.000000000 +0200
@@ -58,3 +58,75 @@
product.price = 500
session.commit()
assert str(e.value) == 'Trying to change price'
+
+
[email protected]('postgresql_dsn')
+class TestObservesForMultipleColumns(object):
+
+ @pytest.fixture
+ def Order(self, Base):
+ class Order(Base):
+ __tablename__ = 'order'
+ id = sa.Column(sa.Integer, primary_key=True)
+ unit_price = sa.Column(sa.Integer)
+ amount = sa.Column(sa.Integer)
+ total_price = sa.Column(sa.Integer)
+
+ @observes('amount', 'unit_price')
+ def total_price_observer(self, amount, unit_price):
+ self.total_price = amount * unit_price
+ return Order
+
+ @pytest.fixture
+ def init_models(self, Order):
+ pass
+
+ def test_only_notifies_observer_on_actual_changes(self, session, Order):
+ order = Order()
+ order.amount = 2
+ order.unit_price = 10
+ session.add(order)
+ session.flush()
+
+ order.amount = 1
+ session.flush()
+ assert order.total_price == 10
+
+ order.unit_price = 100
+ session.flush()
+ assert order.total_price == 100
+
+
[email protected]('postgresql_dsn')
+class TestObservesForMultipleColumnsFiresOnlyOnce(object):
+
+ @pytest.fixture
+ def Order(self, Base):
+ class Order(Base):
+ __tablename__ = 'order'
+ id = sa.Column(sa.Integer, primary_key=True)
+ unit_price = sa.Column(sa.Integer)
+ amount = sa.Column(sa.Integer)
+
+ @observes('amount', 'unit_price')
+ def total_price_observer(self, amount, unit_price):
+ self.call_count = self.call_count + 1
+ return Order
+
+ @pytest.fixture
+ def init_models(self, Order):
+ pass
+
+ def test_only_notifies_observer_on_actual_changes(self, session, Order):
+ order = Order()
+ order.amount = 2
+ order.unit_price = 10
+ order.call_count = 0
+ session.add(order)
+ session.flush()
+ assert order.call_count == 1
+
+ order.amount = 1
+ order.unit_price = 100
+ session.flush()
+ assert order.call_count == 2
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/SQLAlchemy-Utils-0.32.7/tests/types/test_email.py
new/SQLAlchemy-Utils-0.32.9/tests/types/test_email.py
--- old/SQLAlchemy-Utils-0.32.7/tests/types/test_email.py 2016-04-25
16:20:07.000000000 +0200
+++ new/SQLAlchemy-Utils-0.32.9/tests/types/test_email.py 2016-07-10
11:03:30.000000000 +0200
@@ -10,6 +10,7 @@
__tablename__ = 'user'
id = sa.Column(sa.Integer, primary_key=True)
email = sa.Column(EmailType)
+ short_email = sa.Column(EmailType(length=70))
def __repr__(self):
return 'User(%r)' % self.id
@@ -30,3 +31,6 @@
clause = User.email == '[email protected]'
compiled = str(clause.compile(compile_kwargs={'literal_binds': True}))
assert compiled == '"user".email = lower(\'[email protected]\')'
+
+ def test_custom_length(self, session, User):
+ assert User.short_email.type.impl.length == 70
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/SQLAlchemy-Utils-0.32.7/tests/types/test_phonenumber.py
new/SQLAlchemy-Utils-0.32.9/tests/types/test_phonenumber.py
--- old/SQLAlchemy-Utils-0.32.7/tests/types/test_phonenumber.py 2016-05-20
08:58:31.000000000 +0200
+++ new/SQLAlchemy-Utils-0.32.9/tests/types/test_phonenumber.py 2016-05-30
11:49:18.000000000 +0200
@@ -90,8 +90,8 @@
try:
session.execute(
User.__table__.insert().values(
- name='Someone',
- phone_number='abc'
+ name=u'Someone',
+ phone_number=u'abc'
)
)
except PhoneNumberParseException: