From 37e1072787ed2ffcee4129af237bb1f57cdcc815 Mon Sep 17 00:00:00 2001 From: Konsta Vesterinen Date: Fri, 15 Mar 2013 10:02:28 +0200 Subject: [PATCH] 0.4 release --- CHANGES.rst | 7 +++++++ setup.py | 2 +- sqlalchemy_utils/__init__.py | 39 +++++++++++++----------------------- tests.py | 20 +++++++----------- 4 files changed, 29 insertions(+), 39 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 86d52a6..20e49fb 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,13 @@ Here you can see the full list of changes between each SQLAlchemy-Utils release. +0.4.0 (2013-03-01) +^^^^^^^^^^^^^^^^^^ + +- Renamed SmartList to InstrumentedList +- Added instrumented_list decorator + + 0.3.0 (2013-03-01) ^^^^^^^^^^^^^^^^^^ diff --git a/setup.py b/setup.py index ae9f16e..542e54a 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ class PyTest(Command): setup( name='SQLAlchemy-Utils', - version='0.3', + version='0.4', url='https://github.com/kvesteri/sqlalchemy-utils', license='BSD', author='Konsta Vesterinen', diff --git a/sqlalchemy_utils/__init__.py b/sqlalchemy_utils/__init__.py index 5d55d59..b631e69 100644 --- a/sqlalchemy_utils/__init__.py +++ b/sqlalchemy_utils/__init__.py @@ -1,39 +1,28 @@ +from functools import wraps from sqlalchemy.orm import defer -from sqlalchemy.orm.collections import InstrumentedList +from sqlalchemy.orm.collections import InstrumentedList as _InstrumentedList from sqlalchemy.orm.mapper import Mapper from sqlalchemy.orm.query import _ColumnEntity from sqlalchemy.orm.properties import ColumnProperty from sqlalchemy.sql.expression import desc, asc -class SmartList(InstrumentedList): - def has(self, attr): - """ - Returns True if any member of this collection has given attribute - defined. +class InstrumentedList(_InstrumentedList): + """Enhanced version of SQLAlchemy InstrumentedList. Provides some + additional functionality.""" - Example syntax: + def any(self, attr): + return any(getattr(item, attr) for item in self) - >>> Category.articles.has('name') + def all(self, attr): + return all(getattr(item, attr) for item in self) - :param attr: collection member attribute name - """ - adapter = self._sa_adapter - owner_class = adapter.owner_state.class_ - relation = getattr(owner_class, adapter._key).property - relation_class = relation.mapper.class_ - if not hasattr(relation_class, attr): - raise AttributeError( - 'Class %s does not have attribute named %s' % - (relation_class.__name__, attr) - ) - - for record in self: - if getattr(record, attr): - return True - - return False +def instrumented_list(f): + @wraps(f) + def wrapper(*args, **kwargs): + return InstrumentedList([item for item in f(*args, **kwargs)]) + return wrapper def sort_query(query, sort): diff --git a/tests.py b/tests.py index b21c109..ee775fb 100644 --- a/tests.py +++ b/tests.py @@ -1,11 +1,10 @@ -from pytest import raises import sqlalchemy as sa from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy_utils import escape_like, sort_query, SmartList +from sqlalchemy_utils import escape_like, sort_query, InstrumentedList engine = create_engine( @@ -34,27 +33,22 @@ class Article(Base): primaryjoin=category_id == Category.id, backref=sa.orm.backref( 'articles', - collection_class=SmartList + collection_class=InstrumentedList ) ) -class TestSmartList(object): - def test_has_raises_error_for_unknown_attribute(self): - category = Category() - with raises(AttributeError): - category.articles.has('unknown_column') - - def test_has_returns_true_if_member_has_attr_defined(self): +class TestInstrumentedList(object): + def test_any_returns_true_if_member_has_attr_defined(self): category = Category() category.articles.append(Article()) category.articles.append(Article(name=u'some name')) - assert category.articles.has('name') + assert category.articles.any('name') - def test_has_returns_false_if_no_member_has_attr_defined(self): + def test_any_returns_false_if_no_member_has_attr_defined(self): category = Category() category.articles.append(Article()) - assert not category.articles.has('name') + assert not category.articles.any('name') class TestEscapeLike(object):