Hello community, here is the log from the commit of package python-pynamodb for openSUSE:Factory checked in at 2020-07-20 21:05:15 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pynamodb (Old) and /work/SRC/openSUSE:Factory/.python-pynamodb.new.3592 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pynamodb" Mon Jul 20 21:05:15 2020 rev:3 rq:821878 version:4.3.2 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pynamodb/python-pynamodb.changes 2020-03-09 18:34:12.490020909 +0100 +++ /work/SRC/openSUSE:Factory/.python-pynamodb.new.3592/python-pynamodb.changes 2020-07-20 21:06:52.253427475 +0200 @@ -1,0 +2,10 @@ +Fri Jul 3 15:17:46 UTC 2020 - Marketa Calabkova <mcalabk...@suse.com> + +- Update to 4.3.2 + * Fix discrepancy between runtime and type-checker's perspective of Index and derived types + * Add ListAttribute.remove_indexes action for removing specific indexes from a ListAttribute + * Type stub fixes + * Prevent integration tests from being packaged + * Various documentation fixes + +------------------------------------------------------------------- Old: ---- pynamodb-4.3.1.tar.gz New: ---- pynamodb-4.3.2.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pynamodb.spec ++++++ --- /var/tmp/diff_new_pack.L95Shg/_old 2020-07-20 21:06:54.313429564 +0200 +++ /var/tmp/diff_new_pack.L95Shg/_new 2020-07-20 21:06:54.317429568 +0200 @@ -18,7 +18,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-pynamodb -Version: 4.3.1 +Version: 4.3.2 Release: 0 Summary: Python Interface to DynamoDB License: MIT @@ -28,14 +28,14 @@ BuildRequires: %{python_module setuptools} BuildRequires: fdupes BuildRequires: python-rpm-macros -Requires: python-botocore >= 1.2.54 +Requires: python-botocore >= 1.12.54 Requires: python-python-dateutil >= 2.1 Requires: python-six Recommends: python-blinker >= 1.3 BuildArch: noarch # SECTION test requirements BuildRequires: %{python_module blinker >= 1.3} -BuildRequires: %{python_module botocore >= 1.2.54} +BuildRequires: %{python_module botocore >= 1.12.54} BuildRequires: %{python_module pytest-mock} BuildRequires: %{python_module pytest} BuildRequires: %{python_module python-dateutil >= 2.1} ++++++ pynamodb-4.3.1.tar.gz -> pynamodb-4.3.2.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pynamodb-4.3.1/PKG-INFO new/pynamodb-4.3.2/PKG-INFO --- old/pynamodb-4.3.1/PKG-INFO 2020-01-25 04:33:50.000000000 +0100 +++ new/pynamodb-4.3.2/PKG-INFO 2020-04-28 16:17:22.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: pynamodb -Version: 4.3.1 +Version: 4.3.2 Summary: A Pythonic Interface to DynamoDB Home-page: http://jlafon.io/pynamodb.html Author: Jharrod LaFon @@ -28,7 +28,7 @@ Useful links: * See the full documentation at https://pynamodb.readthedocs.io/ - * Ask questions at `Google group <https://groups.google.com/forum/#!forum/pynamodb>`_ + * Ask questions in the `GitHub issues <https://github.com/pynamodb/PynamoDB/issues>`_ * See release notes at https://pynamodb.readthedocs.io/en/latest/release_notes.html Installation diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pynamodb-4.3.1/README.rst new/pynamodb-4.3.2/README.rst --- old/pynamodb-4.3.1/README.rst 2020-01-25 04:33:12.000000000 +0100 +++ new/pynamodb-4.3.2/README.rst 2020-04-28 16:16:17.000000000 +0200 @@ -20,7 +20,7 @@ Useful links: * See the full documentation at https://pynamodb.readthedocs.io/ -* Ask questions at `Google group <https://groups.google.com/forum/#!forum/pynamodb>`_ +* Ask questions in the `GitHub issues <https://github.com/pynamodb/PynamoDB/issues>`_ * See release notes at https://pynamodb.readthedocs.io/en/latest/release_notes.html Installation diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pynamodb-4.3.1/pynamodb/__init__.py new/pynamodb-4.3.2/pynamodb/__init__.py --- old/pynamodb-4.3.1/pynamodb/__init__.py 2020-01-25 04:33:12.000000000 +0100 +++ new/pynamodb-4.3.2/pynamodb/__init__.py 2020-04-28 16:16:17.000000000 +0200 @@ -7,4 +7,4 @@ """ __author__ = 'Jharrod LaFon' __license__ = 'MIT' -__version__ = '4.3.1' +__version__ = '4.3.2' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pynamodb-4.3.1/pynamodb/_compat.py new/pynamodb-4.3.2/pynamodb/_compat.py --- old/pynamodb-4.3.1/pynamodb/_compat.py 2020-01-25 04:33:12.000000000 +0100 +++ new/pynamodb-4.3.2/pynamodb/_compat.py 2020-04-28 16:16:17.000000000 +0200 @@ -6,13 +6,28 @@ else: from inspect import getfullargspec + +class FakeGenericMeta(type): + """Poor man's Generic[T] that doesn't depend on typing. The real generics are in the type stubs.""" + def __getitem__(self, item): + return self + + def load_module(name, path): """Load module using the Python version compatible function.""" if sys.version_info >= (3, 3): from importlib.machinery import SourceFileLoader - return SourceFileLoader(name, path).load_module() - else: + + # Typeshed is incorrect in requiring a string arg to `load_module`, + # as this works with no args or a None arg. + # Even `load_module` is now deprecated, so we should update to just + # using the following approach in >= python 3.5: + # https://docs.python.org/3/library/importlib.html#importing-a-source-file-directly + loader = SourceFileLoader(name, path) + return loader.load_module() # type: ignore + else: from imp import load_source return load_source(name, path) -__all__ = ('getfullargspec', 'load_module') + +__all__ = ('getfullargspec', 'FakeGenericMeta', 'load_module') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pynamodb-4.3.1/pynamodb/attributes.py new/pynamodb-4.3.2/pynamodb/attributes.py --- old/pynamodb-4.3.1/pynamodb/attributes.py 2020-01-25 04:33:12.000000000 +0100 +++ new/pynamodb-4.3.2/pynamodb/attributes.py 2020-04-28 16:16:17.000000000 +0200 @@ -961,6 +961,11 @@ raise ValueError("'of' must be subclass of MapAttribute") self.element_type = of + def remove_indexes(self, *indexes): + if not all([isinstance(i, int) for i in indexes]): + raise ValueError("Method 'remove_indexes' arguments must be 'int'") + return Path(self).remove_list_elements(*indexes) + def serialize(self, values): """ Encode the given list of objects into a list of AttributeValue types. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pynamodb-4.3.1/pynamodb/attributes.pyi new/pynamodb-4.3.2/pynamodb/attributes.pyi --- old/pynamodb-4.3.1/pynamodb/attributes.pyi 2020-01-25 04:33:12.000000000 +0100 +++ new/pynamodb-4.3.2/pynamodb/attributes.pyi 2020-04-28 16:16:17.000000000 +0200 @@ -9,7 +9,7 @@ _Decrement, _IfNotExists, _Increment, _ListAppend ) from pynamodb.expressions.update import ( - AddAction, DeleteAction, RemoveAction, SetAction + AddAction, DeleteAction, RemoveAction, SetAction, ListRemoveAction ) @@ -168,6 +168,8 @@ def __get__(self: _A, instance: None, owner: Any) -> _A: ... @overload def __get__(self, instance: Any, owner: Any) -> List[_T]: ... + def remove_indexes(self, *indexes: int) -> Union[ListRemoveAction]: ... + DESERIALIZE_CLASS_MAP: Dict[Text, Attribute] SERIALIZE_CLASS_MAP: Dict[Type, Attribute] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pynamodb-4.3.1/pynamodb/expressions/operand.py new/pynamodb-4.3.2/pynamodb/expressions/operand.py --- old/pynamodb-4.3.1/pynamodb/expressions/operand.py 2020-01-25 04:33:12.000000000 +0100 +++ new/pynamodb-4.3.2/pynamodb/expressions/operand.py 2020-04-28 16:16:17.000000000 +0200 @@ -6,7 +6,7 @@ BeginsWith, Between, Comparison, Contains, Exists, In, IsType, NotExists ) from pynamodb.expressions.update import ( - AddAction, DeleteAction, RemoveAction, SetAction + AddAction, DeleteAction, RemoveAction, SetAction, ListRemoveAction ) from pynamodb.expressions.util import get_path_segments, get_value_placeholder, substitute_names from six import string_types @@ -277,6 +277,9 @@ # Returns an update action that removes this attribute from the item return RemoveAction(self) + def remove_list_elements(self, *indexes): + return ListRemoveAction(self, *indexes) + def add(self, *values): # Returns an update action that appends the given values to a set or mathematically adds a value to a number value = values[0] if len(values) == 1 else values diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pynamodb-4.3.1/pynamodb/expressions/operand.pyi new/pynamodb-4.3.2/pynamodb/expressions/operand.pyi --- old/pynamodb-4.3.1/pynamodb/expressions/operand.pyi 2020-01-25 04:33:12.000000000 +0100 +++ new/pynamodb-4.3.2/pynamodb/expressions/operand.pyi 2020-04-28 16:16:17.000000000 +0200 @@ -5,7 +5,7 @@ BeginsWith, Between, Comparison, Contains, Exists, In, IsType, NotExists ) from pynamodb.expressions.update import ( - AddAction, DeleteAction, RemoveAction, SetAction + AddAction, DeleteAction, RemoveAction, SetAction, ListRemoveAction ) @@ -78,3 +78,4 @@ def contains(self, item: Any) -> Contains: ... def set(self, value: Any) -> SetAction: ... def remove(self) -> RemoveAction: ... + def remove_list_elements(self, *indexes: int) -> ListRemoveAction: ... diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pynamodb-4.3.1/pynamodb/expressions/update.py new/pynamodb-4.3.2/pynamodb/expressions/update.py --- old/pynamodb-4.3.1/pynamodb/expressions/update.py 2020-01-25 04:33:12.000000000 +0100 +++ new/pynamodb-4.3.2/pynamodb/expressions/update.py 2020-04-28 16:16:17.000000000 +0200 @@ -36,6 +36,17 @@ super(RemoveAction, self).__init__(path) +class ListRemoveAction(Action): + """ + The List REMOVE action deletes an element from a list item based on the index. + """ + format_string = None + + def __init__(self, path, *indexes): + self.format_string = ", ".join("{{0}}[{}]".format(index) for index in indexes) + super(ListRemoveAction, self).__init__(path) + + class AddAction(Action): """ The ADD action appends elements to a set or mathematically adds to a number attribute. @@ -65,6 +76,7 @@ self.remove_actions = [] self.add_actions = [] self.delete_actions = [] + self.list_remove_actions = [] for action in actions: self.add_action(action) @@ -73,6 +85,8 @@ self.set_actions.append(action) elif isinstance(action, RemoveAction): self.remove_actions.append(action) + elif isinstance(action, ListRemoveAction): + self.list_remove_actions.append(action) elif isinstance(action, AddAction): self.add_actions.append(action) elif isinstance(action, DeleteAction): @@ -84,6 +98,7 @@ expression = None expression = self._add_clause(expression, 'SET', self.set_actions, placeholder_names, expression_attribute_values) expression = self._add_clause(expression, 'REMOVE', self.remove_actions, placeholder_names, expression_attribute_values) + expression = self._add_clause(expression, 'REMOVE', self.list_remove_actions, placeholder_names, expression_attribute_values) expression = self._add_clause(expression, 'ADD', self.add_actions, placeholder_names, expression_attribute_values) expression = self._add_clause(expression, 'DELETE', self.delete_actions, placeholder_names, expression_attribute_values) return expression diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pynamodb-4.3.1/pynamodb/expressions/update.pyi new/pynamodb-4.3.2/pynamodb/expressions/update.pyi --- old/pynamodb-4.3.1/pynamodb/expressions/update.pyi 2020-01-25 04:33:12.000000000 +0100 +++ new/pynamodb-4.3.2/pynamodb/expressions/update.pyi 2020-04-28 16:16:17.000000000 +0200 @@ -15,6 +15,10 @@ def __init__(self, path: Path) -> None: ... +class ListRemoveAction(Action): + def __init__(self, path: Path, *indexes: int) -> None: ... + + class AddAction(Action): def __init__(self, path: Path, subset: Path) -> None: ... @@ -26,6 +30,7 @@ class Update(object): set_actions: List[SetAction] remove_actions: List[RemoveAction] + list_remove_actions: List[ListRemoveAction] add_actions: List[AddAction] delete_actions: List[DeleteAction] def __init__(self, *actions: Action) -> None: ... diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pynamodb-4.3.1/pynamodb/indexes.py new/pynamodb-4.3.2/pynamodb/indexes.py --- old/pynamodb-4.3.1/pynamodb/indexes.py 2020-01-25 04:33:12.000000000 +0100 +++ new/pynamodb-4.3.2/pynamodb/indexes.py 2020-04-28 16:16:17.000000000 +0200 @@ -3,6 +3,7 @@ """ from inspect import getmembers +from pynamodb._compat import FakeGenericMeta from pynamodb.constants import ( INCLUDE, ALL, KEYS_ONLY, ATTR_NAME, ATTR_TYPE, KEY_TYPE, ATTR_TYPE_MAP, KEY_SCHEMA, ATTR_DEFINITIONS, META_CLASS_NAME @@ -13,7 +14,7 @@ from six import with_metaclass -class IndexMeta(type): +class IndexMeta(FakeGenericMeta): """ Index meta class diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pynamodb-4.3.1/pynamodb/models.py new/pynamodb-4.3.2/pynamodb/models.py --- old/pynamodb-4.3.1/pynamodb/models.py 2020-01-25 04:33:12.000000000 +0100 +++ new/pynamodb-4.3.2/pynamodb/models.py 2020-04-28 16:16:17.000000000 +0200 @@ -356,6 +356,8 @@ def delete(self, condition=None): """ Deletes this object from dynamodb + + :raises pynamodb.exceptions.DeleteError: If the record can not be deleted """ args, kwargs = self._get_save_args(attributes=False, null_check=False) version_condition = self._handle_version_attribute(kwargs) @@ -371,6 +373,8 @@ :param actions: a list of Action updates to apply :param condition: an optional Condition on which to update + :raises ModelInstance.DoesNotExist: if the object to be updated does not exist + :raises pynamodb.exceptions.UpdateError: if the `condition` is not met """ if not isinstance(actions, list) or len(actions) == 0: raise TypeError("the value of `actions` is expected to be a non-empty list") @@ -411,6 +415,7 @@ Retrieves this object's data from dynamodb and syncs this local object :param consistent_read: If True, then a consistent read is performed. + :raises ModelInstance.DoesNotExist: if the object to be updated does not exist """ args, kwargs = self._get_save_args(attributes=False) kwargs.setdefault('consistent_read', consistent_read) @@ -471,8 +476,9 @@ :param hash_key: The hash key of the desired item :param range_key: The range key of the desired item, only used when appropriate. - :param consistent_read - :param attributes_to_get + :param consistent_read: + :param attributes_to_get: + :raises ModelInstance.DoesNotExist: if the object to be updated does not exist """ hash_key, range_key = cls._serialize_keys(hash_key, range_key) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pynamodb-4.3.1/pynamodb/models.pyi new/pynamodb-4.3.2/pynamodb/models.pyi --- old/pynamodb-4.3.1/pynamodb/models.pyi 2020-01-25 04:33:12.000000000 +0100 +++ new/pynamodb-4.3.2/pynamodb/models.pyi 2020-04-28 16:16:17.000000000 +0200 @@ -21,7 +21,7 @@ def __init__(self, name: Text, bases: Tuple[type, ...], attrs: Dict[Any, Any]) -> None: ... _T = TypeVar('_T', bound='Model') -KeyType = Union[Text, bytes, float, int, Tuple] +KeyType = Any class Model(metaclass=MetaModel): DoesNotExist = DoesNotExist @@ -82,6 +82,8 @@ limit: Optional[int] = ..., last_evaluated_key: Optional[Dict[str, Dict[str, Any]]] = ..., page_size: Optional[int] = ..., + consistent_read: bool = ..., + index_name: Optional[Text] = ..., rate_limit: Optional[float] = ..., attributes_to_get: Optional[List[str]] = ..., ) -> ResultIterator[_T]: ... diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pynamodb-4.3.1/pynamodb.egg-info/PKG-INFO new/pynamodb-4.3.2/pynamodb.egg-info/PKG-INFO --- old/pynamodb-4.3.1/pynamodb.egg-info/PKG-INFO 2020-01-25 04:33:50.000000000 +0100 +++ new/pynamodb-4.3.2/pynamodb.egg-info/PKG-INFO 2020-04-28 16:17:22.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: pynamodb -Version: 4.3.1 +Version: 4.3.2 Summary: A Pythonic Interface to DynamoDB Home-page: http://jlafon.io/pynamodb.html Author: Jharrod LaFon @@ -28,7 +28,7 @@ Useful links: * See the full documentation at https://pynamodb.readthedocs.io/ - * Ask questions at `Google group <https://groups.google.com/forum/#!forum/pynamodb>`_ + * Ask questions in the `GitHub issues <https://github.com/pynamodb/PynamoDB/issues>`_ * See release notes at https://pynamodb.readthedocs.io/en/latest/release_notes.html Installation diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pynamodb-4.3.1/pynamodb.egg-info/SOURCES.txt new/pynamodb-4.3.2/pynamodb.egg-info/SOURCES.txt --- old/pynamodb-4.3.1/pynamodb.egg-info/SOURCES.txt 2020-01-25 04:33:50.000000000 +0100 +++ new/pynamodb-4.3.2/pynamodb.egg-info/SOURCES.txt 2020-04-28 16:17:22.000000000 +0200 @@ -47,11 +47,4 @@ pynamodb/expressions/update.py pynamodb/expressions/update.pyi pynamodb/expressions/util.py -pynamodb/expressions/util.pyi -tests/integration/__init__.py -tests/integration/base_integration_test.py -tests/integration/binary_update_test.py -tests/integration/conftest.py -tests/integration/model_integration_test.py -tests/integration/table_integration_test.py -tests/integration/test_transaction_integration.py \ No newline at end of file +pynamodb/expressions/util.pyi \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pynamodb-4.3.1/pynamodb.egg-info/top_level.txt new/pynamodb-4.3.2/pynamodb.egg-info/top_level.txt --- old/pynamodb-4.3.1/pynamodb.egg-info/top_level.txt 2020-01-25 04:33:50.000000000 +0100 +++ new/pynamodb-4.3.2/pynamodb.egg-info/top_level.txt 2020-04-28 16:17:22.000000000 +0200 @@ -1,2 +1 @@ pynamodb -tests diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pynamodb-4.3.1/requirements-dev.txt new/pynamodb-4.3.2/requirements-dev.txt --- old/pynamodb-4.3.1/requirements-dev.txt 2020-01-25 04:33:12.000000000 +0100 +++ new/pynamodb-4.3.2/requirements-dev.txt 2020-04-28 16:16:17.000000000 +0200 @@ -9,3 +9,5 @@ coveralls mypy==0.761;python_version>="3.7" pytest-cov +sphinx +sphinx-rtd-theme diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pynamodb-4.3.1/setup.py new/pynamodb-4.3.2/setup.py --- old/pynamodb-4.3.1/setup.py 2020-01-25 04:33:12.000000000 +0100 +++ new/pynamodb-4.3.2/setup.py 2020-04-28 16:16:17.000000000 +0200 @@ -31,7 +31,7 @@ setup( name='pynamodb', version=__import__('pynamodb').__version__, - packages=find_packages(exclude=('tests',)), + packages=find_packages(exclude=('tests', 'tests.integration',)), url='http://jlafon.io/pynamodb.html', author='Jharrod LaFon', author_email='jla...@eyesopen.com', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pynamodb-4.3.1/tests/integration/base_integration_test.py new/pynamodb-4.3.2/tests/integration/base_integration_test.py --- old/pynamodb-4.3.1/tests/integration/base_integration_test.py 2020-01-25 04:33:12.000000000 +0100 +++ new/pynamodb-4.3.2/tests/integration/base_integration_test.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,169 +0,0 @@ -""" -Runs tests against dynamodb -""" -import time - -from pynamodb.connection import Connection -from pynamodb.constants import PROVISIONED_THROUGHPUT, READ_CAPACITY_UNITS -from pynamodb.expressions.condition import BeginsWith, NotExists -from pynamodb.expressions.operand import Path, Value -from pynamodb.exceptions import TableDoesNotExist -from pynamodb.types import STRING, HASH, RANGE, NUMBER - -import pytest - - -@pytest.mark.ddblocal -def test_connection_integration(ddb_url): - table_name = 'pynamodb-ci-connection' - - # For use with a fake dynamodb connection - # See: http://aws.amazon.com/dynamodb/developer-resources/ - conn = Connection(host=ddb_url) - - print(conn) - print("conn.describe_table...") - table = None - try: - table = conn.describe_table(table_name) - except TableDoesNotExist: - params = { - 'read_capacity_units': 1, - 'write_capacity_units': 1, - 'attribute_definitions': [ - { - 'attribute_type': STRING, - 'attribute_name': 'Forum' - }, - { - 'attribute_type': STRING, - 'attribute_name': 'Thread' - }, - { - 'attribute_type': STRING, - 'attribute_name': 'AltKey' - }, - { - 'attribute_type': NUMBER, - 'attribute_name': 'number' - } - ], - 'key_schema': [ - { - 'key_type': HASH, - 'attribute_name': 'Forum' - }, - { - 'key_type': RANGE, - 'attribute_name': 'Thread' - } - ], - 'global_secondary_indexes': [ - { - 'index_name': 'alt-index', - 'key_schema': [ - { - 'KeyType': 'HASH', - 'AttributeName': 'AltKey' - } - ], - 'projection': { - 'ProjectionType': 'KEYS_ONLY' - }, - 'provisioned_throughput': { - 'ReadCapacityUnits': 1, - 'WriteCapacityUnits': 1, - } - } - ], - 'local_secondary_indexes': [ - { - 'index_name': 'view-index', - 'key_schema': [ - { - 'KeyType': 'HASH', - 'AttributeName': 'Forum' - }, - { - 'KeyType': 'RANGE', - 'AttributeName': 'AltKey' - } - ], - 'projection': { - 'ProjectionType': 'KEYS_ONLY' - } - } - ] - } - print("conn.create_table...") - conn.create_table(table_name, **params) - - while table is None: - time.sleep(1) - table = conn.describe_table(table_name) - - while table['TableStatus'] == 'CREATING': - time.sleep(2) - table = conn.describe_table(table_name) - print("conn.list_tables") - conn.list_tables() - print("conn.update_table...") - - conn.update_table( - table_name, - read_capacity_units=table.get(PROVISIONED_THROUGHPUT).get(READ_CAPACITY_UNITS) + 1, - write_capacity_units=2 - ) - - table = conn.describe_table(table_name) - - while table['TableStatus'] != 'ACTIVE': - time.sleep(2) - table = conn.describe_table(table_name) - - print("conn.put_item") - conn.put_item( - table_name, - 'item1-hash', - range_key='item1-range', - attributes={'foo': {'S': 'bar'}}, - condition=NotExists(Path('Forum')), - ) - conn.get_item( - table_name, - 'item1-hash', - range_key='item1-range' - ) - conn.delete_item( - table_name, - 'item1-hash', - range_key='item1-range' - ) - - items = [] - for i in range(10): - items.append( - {"Forum": "FooForum", "Thread": "thread-{}".format(i)} - ) - print("conn.batch_write_items...") - conn.batch_write_item( - table_name, - put_items=items - ) - print("conn.batch_get_items...") - data = conn.batch_get_item( - table_name, - items - ) - print("conn.query...") - conn.query( - table_name, - "FooForum", - range_key_condition=(BeginsWith(Path('Thread'), Value('thread'))), - ) - print("conn.scan...") - conn.scan( - table_name, - ) - print("conn.delete_table...") - conn.delete_table(table_name) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pynamodb-4.3.1/tests/integration/binary_update_test.py new/pynamodb-4.3.2/tests/integration/binary_update_test.py --- old/pynamodb-4.3.1/tests/integration/binary_update_test.py 2020-01-25 04:33:12.000000000 +0100 +++ new/pynamodb-4.3.2/tests/integration/binary_update_test.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,45 +0,0 @@ -import pytest - -from pynamodb.attributes import UnicodeAttribute, BinaryAttribute, BinarySetAttribute -from pynamodb.models import Model - - -@pytest.mark.ddblocal -def test_binary_attribute_update(ddb_url): - class DataModel(Model): - class Meta: - table_name = 'binary_attr_update' - host = ddb_url - pkey = UnicodeAttribute(hash_key=True) - data = BinaryAttribute() - - DataModel.create_table(read_capacity_units=1, write_capacity_units=1, wait=True) - data = b'\x00hey\xfb' - pkey = 'pkey' - DataModel(pkey, data=data).save() - m = DataModel.get(pkey) - assert m.data == data - - new_data = b'\xff' - m.update(actions=[DataModel.data.set(new_data)]) - assert new_data == m.data - -@pytest.mark.ddblocal -def test_binary_set_attribute_update(ddb_url): - class DataModel(Model): - class Meta: - table_name = 'binary_set_attr_update' - host = ddb_url - pkey = UnicodeAttribute(hash_key=True) - data = BinarySetAttribute() - - DataModel.create_table(read_capacity_units=1, write_capacity_units=1, wait=True) - data = {b'\x00hey\xfb', b'\x00beautiful\xfb'} - pkey = 'pkey' - DataModel(pkey, data=data).save() - m = DataModel.get(pkey) - assert m.data == data - - new_data = {b'\xff'} - m.update(actions=[DataModel.data.set(new_data)]) - assert new_data == m.data diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pynamodb-4.3.1/tests/integration/conftest.py new/pynamodb-4.3.2/tests/integration/conftest.py --- old/pynamodb-4.3.1/tests/integration/conftest.py 2020-01-25 04:33:12.000000000 +0100 +++ new/pynamodb-4.3.2/tests/integration/conftest.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,19 +0,0 @@ -import os - -import pytest - - -@pytest.fixture(scope='module') -def ddb_url(): - """Obtain the URL of a local DynamoDB instance. - - This is meant to be used with something like DynamoDB Local: - - http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.html - - It must be set up "out of band"; we merely assume it exists on - http://localhost:8000 or a URL specified though the - PYNAMODB_INTEGRATION_TEST_DDB_URL environment variable. - """ - ddb_url = os.getenv("PYNAMODB_INTEGRATION_TEST_DDB_URL") - return "http://localhost:8000" if ddb_url is None else ddb_url diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pynamodb-4.3.1/tests/integration/model_integration_test.py new/pynamodb-4.3.2/tests/integration/model_integration_test.py --- old/pynamodb-4.3.1/tests/integration/model_integration_test.py 2020-01-25 04:33:12.000000000 +0100 +++ new/pynamodb-4.3.2/tests/integration/model_integration_test.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,104 +0,0 @@ -""" -Integration tests for the model API -""" -from datetime import datetime -from pynamodb.models import Model -from pynamodb.indexes import GlobalSecondaryIndex, AllProjection, LocalSecondaryIndex -from pynamodb.attributes import ( - UnicodeAttribute, BinaryAttribute, UTCDateTimeAttribute, NumberSetAttribute, NumberAttribute, - VersionAttribute) - -import pytest - - -class LSIndex(LocalSecondaryIndex): - """ - A model for the local secondary index - """ - class Meta: - projection = AllProjection() - forum = UnicodeAttribute(hash_key=True) - view = NumberAttribute(range_key=True) - - -class GSIndex(GlobalSecondaryIndex): - """ - A model for the secondary index - """ - class Meta: - projection = AllProjection() - read_capacity_units = 2 - write_capacity_units = 1 - epoch = UTCDateTimeAttribute(hash_key=True) - - -@pytest.mark.ddblocal -def test_model_integration(ddb_url): - - class TestModel(Model): - """ - A model for testing - """ - class Meta: - region = 'us-east-1' - table_name = 'pynamodb-ci' - host = ddb_url - forum = UnicodeAttribute(hash_key=True) - thread = UnicodeAttribute(range_key=True) - view = NumberAttribute(default=0) - view_index = LSIndex() - epoch_index = GSIndex() - epoch = UTCDateTimeAttribute(default=datetime.now) - content = BinaryAttribute(null=True) - scores = NumberSetAttribute() - version = VersionAttribute() - - if not TestModel.exists(): - print("Creating table") - TestModel.create_table(read_capacity_units=1, write_capacity_units=1, wait=True) - - obj = TestModel('1', '2') - obj.save() - obj.refresh() - obj = TestModel('foo', 'bar') - obj.save() - TestModel('foo2', 'bar2') - obj3 = TestModel('setitem', 'setrange', scores={1, 2.1}) - obj3.save() - obj3.refresh() - - with TestModel.batch_write() as batch: - items = [TestModel('hash-{}'.format(x), '{}'.format(x)) for x in range(10)] - for item in items: - batch.save(item) - - item_keys = [('hash-{}'.format(x), 'thread-{}'.format(x)) for x in range(10)] - - for item in TestModel.batch_get(item_keys): - print(item) - - for item in TestModel.query('setitem', TestModel.thread.startswith('set')): - print("Query Item {}".format(item)) - - with TestModel.batch_write() as batch: - items = [TestModel('hash-{}'.format(x), '{}'.format(x)) for x in range(10)] - for item in items: - print("Batch delete") - batch.delete(item) - - for item in TestModel.scan(): - print("Scanned item: {}".format(item)) - - tstamp = datetime.now() - query_obj = TestModel('query_forum', 'query_thread') - query_obj.forum = 'foo' - query_obj.save() - query_obj.update([TestModel.view.add(1)]) - for item in TestModel.epoch_index.query(tstamp): - print("Item queried from index: {}".format(item)) - - for item in TestModel.view_index.query('foo', TestModel.view > 0): - print("Item queried from index: {}".format(item.view)) - - print(query_obj.update([TestModel.view.add(1)], condition=TestModel.forum.exists())) - TestModel.delete_table() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pynamodb-4.3.1/tests/integration/table_integration_test.py new/pynamodb-4.3.2/tests/integration/table_integration_test.py --- old/pynamodb-4.3.1/tests/integration/table_integration_test.py 2020-01-25 04:33:12.000000000 +0100 +++ new/pynamodb-4.3.2/tests/integration/table_integration_test.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,156 +0,0 @@ -""" -Run tests against dynamodb using the table abstraction -""" -import time -from pynamodb.constants import PROVISIONED_THROUGHPUT, READ_CAPACITY_UNITS -from pynamodb.connection import TableConnection -from pynamodb.expressions.condition import BeginsWith, NotExists -from pynamodb.expressions.operand import Path, Value -from pynamodb.exceptions import TableDoesNotExist -from pynamodb.types import STRING, HASH, RANGE, NUMBER - -import pytest - - -@pytest.mark.ddblocal -def test_table_integration(ddb_url): - table_name = 'pynamodb-ci-table' - - # For use with a fake dynamodb connection - # See: http://aws.amazon.com/dynamodb/developer-resources/ - conn = TableConnection(table_name, host=ddb_url) - print(conn) - - print("conn.describe_table...") - table = None - try: - table = conn.describe_table() - except TableDoesNotExist: - params = { - 'read_capacity_units': 1, - 'write_capacity_units': 1, - 'attribute_definitions': [ - { - 'attribute_type': STRING, - 'attribute_name': 'Forum' - }, - { - 'attribute_type': STRING, - 'attribute_name': 'Thread' - }, - { - 'attribute_type': STRING, - 'attribute_name': 'AltKey' - }, - { - 'attribute_type': NUMBER, - 'attribute_name': 'number' - } - ], - 'key_schema': [ - { - 'key_type': HASH, - 'attribute_name': 'Forum' - }, - { - 'key_type': RANGE, - 'attribute_name': 'Thread' - } - ], - 'global_secondary_indexes': [ - { - 'index_name': 'alt-index', - 'key_schema': [ - { - 'KeyType': 'HASH', - 'AttributeName': 'AltKey' - } - ], - 'projection': { - 'ProjectionType': 'KEYS_ONLY' - }, - 'provisioned_throughput': { - 'ReadCapacityUnits': 1, - 'WriteCapacityUnits': 1, - } - } - ], - 'local_secondary_indexes': [ - { - 'index_name': 'view-index', - 'key_schema': [ - { - 'KeyType': 'HASH', - 'AttributeName': 'Forum' - }, - { - 'KeyType': 'RANGE', - 'AttributeName': 'AltKey' - } - ], - 'projection': { - 'ProjectionType': 'KEYS_ONLY' - } - } - ] - } - print("conn.create_table...") - conn.create_table(**params) - - while table is None: - time.sleep(2) - table = conn.describe_table() - while table['TableStatus'] == 'CREATING': - time.sleep(5) - print(table['TableStatus']) - table = conn.describe_table() - print("conn.update_table...") - - conn.update_table( - read_capacity_units=table.get(PROVISIONED_THROUGHPUT).get(READ_CAPACITY_UNITS) + 1, - write_capacity_units=2 - ) - - table = conn.describe_table() - while table['TableStatus'] != 'ACTIVE': - time.sleep(2) - table = conn.describe_table() - - print("conn.put_item") - conn.put_item( - 'item1-hash', - range_key='item1-range', - attributes={'foo': {'S': 'bar'}}, - condition=NotExists(Path('Forum')), - ) - conn.get_item( - 'item1-hash', - range_key='item1-range' - ) - conn.delete_item( - 'item1-hash', - range_key='item1-range' - ) - - items = [] - for i in range(10): - items.append( - {"Forum": "FooForum", "Thread": "thread-{}".format(i)} - ) - print("conn.batch_write_items...") - conn.batch_write_item( - put_items=items - ) - print("conn.batch_get_items...") - data = conn.batch_get_item( - items - ) - print("conn.query...") - conn.query( - "FooForum", - range_key_condition=(BeginsWith(Path('Thread'), Value('thread'))), - ) - print("conn.scan...") - conn.scan() - print("conn.delete_table...") - conn.delete_table() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pynamodb-4.3.1/tests/integration/test_transaction_integration.py new/pynamodb-4.3.2/tests/integration/test_transaction_integration.py --- old/pynamodb-4.3.1/tests/integration/test_transaction_integration.py 2020-01-25 04:33:12.000000000 +0100 +++ new/pynamodb-4.3.2/tests/integration/test_transaction_integration.py 1970-01-01 01:00:00.000000000 +0100 @@ -1,364 +0,0 @@ -import uuid -from datetime import datetime - -import pytest - -from pynamodb.connection import Connection -from pynamodb.exceptions import DoesNotExist, TransactWriteError, TransactGetError, InvalidStateError - - -from pynamodb.attributes import ( - NumberAttribute, UnicodeAttribute, UTCDateTimeAttribute, BooleanAttribute, VersionAttribute -) -from pynamodb.transactions import TransactGet, TransactWrite - -from pynamodb.models import Model - -IDEMPOTENT_PARAMETER_MISMATCH = 'IdempotentParameterMismatchException' -PROVISIONED_THROUGHPUT_EXCEEDED = 'ProvisionedThroughputExceededException' -RESOURCE_NOT_FOUND = 'ResourceNotFoundException' -TRANSACTION_CANCELLED = 'TransactionCanceledException' -TRANSACTION_IN_PROGRESS = 'TransactionInProgressException' -VALIDATION_EXCEPTION = 'ValidationException' - - -class User(Model): - class Meta: - region = 'us-east-1' - table_name = 'user' - - user_id = NumberAttribute(hash_key=True) - - -class BankStatement(Model): - - class Meta: - region = 'us-east-1' - table_name = 'statement' - - user_id = NumberAttribute(hash_key=True) - balance = NumberAttribute(default=0) - active = BooleanAttribute(default=True) - - -class LineItem(Model): - - class Meta: - region = 'us-east-1' - table_name = 'line-item' - - user_id = NumberAttribute(hash_key=True) - created_at = UTCDateTimeAttribute(range_key=True, default=datetime.now()) - amount = NumberAttribute() - currency = UnicodeAttribute() - - -class DifferentRegion(Model): - - class Meta: - region = 'us-east-2' - table_name = 'different-region' - - entry_index = NumberAttribute(hash_key=True) - - -class Foo(Model): - class Meta: - region = 'us-east-1' - table_name = 'foo' - - bar = NumberAttribute(hash_key=True) - star = UnicodeAttribute(null=True) - version = VersionAttribute() - - -TEST_MODELS = [ - BankStatement, - DifferentRegion, - LineItem, - User, - Foo -] - - -@pytest.fixture(scope='module') -def connection(ddb_url): - yield Connection(host=ddb_url) - - -@pytest.fixture(scope='module', autouse=True) -def create_tables(ddb_url): - for m in TEST_MODELS: - m.Meta.host = ddb_url - m.create_table( - read_capacity_units=10, - write_capacity_units=10, - wait=True - ) - - yield - - for m in TEST_MODELS: - if m.exists(): - m.delete_table() - - -def get_error_code(error): - return error.cause.response['Error'].get('Code') - - -def get_error_message(error): - return error.cause.response['Error'].get('Message') - - -@pytest.mark.ddblocal -def test_transact_write__error__idempotent_parameter_mismatch(connection): - client_token = str(uuid.uuid4()) - - with TransactWrite(connection=connection, client_request_token=client_token) as transaction: - transaction.save(User(1)) - transaction.save(User(2)) - - with pytest.raises(TransactWriteError) as exc_info: - # committing the first time, then adding more info and committing again - with TransactWrite(connection=connection, client_request_token=client_token) as transaction: - transaction.save(User(3)) - assert get_error_code(exc_info.value) == IDEMPOTENT_PARAMETER_MISMATCH - - # ensure that the first request succeeded in creating new users - assert User.get(1) - assert User.get(2) - - with pytest.raises(DoesNotExist): - # ensure it did not create the user from second request - User.get(3) - - -@pytest.mark.ddblocal -def test_transact_write__error__different_regions(connection): - with pytest.raises(TransactWriteError) as exc_info: - with TransactWrite(connection=connection) as transact_write: - # creating a model in a table outside the region everyone else operates in - transact_write.save(DifferentRegion(entry_index=0)) - transact_write.save(BankStatement(1)) - transact_write.save(User(1)) - assert get_error_code(exc_info.value) == RESOURCE_NOT_FOUND - - -@pytest.mark.ddblocal -def test_transact_write__error__transaction_cancelled__condition_check_failure(connection): - # create a users and a bank statements for them - User(1).save() - BankStatement(1).save() - - # attempt to do this as a transaction with the condition that they don't already exist - with pytest.raises(TransactWriteError) as exc_info: - with TransactWrite(connection=connection) as transaction: - transaction.save(User(1), condition=(User.user_id.does_not_exist())) - transaction.save(BankStatement(1), condition=(BankStatement.user_id.does_not_exist())) - assert get_error_code(exc_info.value) == TRANSACTION_CANCELLED - assert 'ConditionalCheckFailed' in get_error_message(exc_info.value) - - -@pytest.mark.ddblocal -def test_transact_write__error__multiple_operations_on_same_record(connection): - BankStatement(1).save() - - # attempt to do a transaction with multiple operations on the same record - with pytest.raises(TransactWriteError) as exc_info: - with TransactWrite(connection=connection) as transaction: - transaction.condition_check(BankStatement, 1, condition=(BankStatement.user_id.exists())) - transaction.update(BankStatement(1), actions=[(BankStatement.balance.add(10))]) - assert get_error_code(exc_info.value) == VALIDATION_EXCEPTION - - -@pytest.mark.ddblocal -def test_transact_get(connection): - # making sure these entries exist, and with the expected info - User(1).save() - BankStatement(1).save() - User(2).save() - BankStatement(2, balance=100).save() - - # get users and statements we just created and assign them to variables - with TransactGet(connection=connection) as transaction: - _user1_future = transaction.get(User, 1) - _statement1_future = transaction.get(BankStatement, 1) - _user2_future = transaction.get(User, 2) - _statement2_future = transaction.get(BankStatement, 2) - - user1 = _user1_future.get() - statement1 = _statement1_future.get() - user2 = _user2_future.get() - statement2 = _statement2_future.get() - - assert user1.user_id == statement1.user_id == 1 - assert statement1.balance == 0 - assert user2.user_id == statement2.user_id == 2 - assert statement2.balance == 100 - - -@pytest.mark.ddblocal -def test_transact_get__does_not_exist(connection): - with TransactGet(connection=connection) as transaction: - _user_future = transaction.get(User, 100) - with pytest.raises(User.DoesNotExist): - _user_future.get() - - -@pytest.mark.ddblocal -def test_transact_get__invalid_state(connection): - with TransactGet(connection=connection) as transaction: - _user_future = transaction.get(User, 100) - with pytest.raises(InvalidStateError): - _user_future.get() - - -@pytest.mark.ddblocal -def test_transact_write(connection): - # making sure these entries exist, and with the expected info - BankStatement(1, balance=0).save() - BankStatement(2, balance=100).save() - - # assert values are what we think they should be - statement1 = BankStatement.get(1) - statement2 = BankStatement.get(2) - assert statement1.balance == 0 - assert statement2.balance == 100 - - with TransactWrite(connection=connection) as transaction: - # let the users send money to one another - # create a credit line item to user 1's account - transaction.save( - LineItem(user_id=1, amount=50, currency='USD'), - condition=(LineItem.user_id.does_not_exist()), - ) - # create a debit to user 2's account - transaction.save( - LineItem(user_id=2, amount=-50, currency='USD'), - condition=(LineItem.user_id.does_not_exist()), - ) - - # add credit to user 1's account - transaction.update(statement1, actions=[BankStatement.balance.add(50)]) - # debit from user 2's account if they have enough in the bank - transaction.update( - statement2, - actions=[BankStatement.balance.add(-50)], - condition=(BankStatement.balance >= 50) - ) - - statement1.refresh() - statement2.refresh() - assert statement1.balance == statement2.balance == 50 - - -@pytest.mark.ddblocal -def test_transact_write__one_of_each(connection): - User(1).save() - User(2).save() - statement = BankStatement(1, balance=100, active=True) - statement.save() - - with TransactWrite(connection=connection) as transaction: - transaction.condition_check(User, 1, condition=(User.user_id.exists())) - transaction.delete(User(2)) - transaction.save(LineItem(4, amount=100, currency='USD'), condition=(LineItem.user_id.does_not_exist())) - transaction.update( - statement, - actions=[ - BankStatement.active.set(False), - BankStatement.balance.set(0), - ] - ) - - # confirming transaction correct and successful - assert User.get(1) - with pytest.raises(DoesNotExist): - User.get(2) - - new_line_item = next(LineItem.query(4, scan_index_forward=False, limit=1), None) - assert new_line_item - assert new_line_item.amount == 100 - assert new_line_item.currency == 'USD' - - statement.refresh() - assert not statement.active - assert statement.balance == 0 - - -@pytest.mark.ddblocal -def test_transaction_write_with_version_attribute(connection): - foo1 = Foo(1) - foo1.save() - foo2 = Foo(2, star='bar') - foo2.save() - foo3 = Foo(3) - foo3.save() - - with TransactWrite(connection=connection) as transaction: - transaction.condition_check(Foo, 1, condition=(Foo.bar.exists())) - transaction.delete(foo2) - transaction.save(Foo(4)) - transaction.update( - foo3, - actions=[ - Foo.star.set('birdistheword'), - ] - ) - - assert Foo.get(1).version == 1 - with pytest.raises(DoesNotExist): - Foo.get(2) - # Local object's version attribute is updated automatically. - assert foo3.version == 2 - assert Foo.get(4).version == 1 - - -@pytest.mark.ddblocal -def test_transaction_get_with_version_attribute(connection): - Foo(11).save() - Foo(12, star='bar').save() - - with TransactGet(connection=connection) as transaction: - foo1_future = transaction.get(Foo, 11) - foo2_future = transaction.get(Foo, 12) - - foo1 = foo1_future.get() - assert foo1.version == 1 - foo2 = foo2_future.get() - assert foo2.version == 1 - assert foo2.star == 'bar' - - -@pytest.mark.ddblocal -def test_transaction_write_with_version_attribute_condition_failure(connection): - foo = Foo(21) - foo.save() - - foo2 = Foo(21) - - with pytest.raises(TransactWriteError) as exc_info: - with TransactWrite(connection=connection) as transaction: - transaction.save(Foo(21)) - assert get_error_code(exc_info.value) == TRANSACTION_CANCELLED - assert 'ConditionalCheckFailed' in get_error_message(exc_info.value) - - with pytest.raises(TransactWriteError) as exc_info: - with TransactWrite(connection=connection) as transaction: - transaction.update( - foo2, - actions=[ - Foo.star.set('birdistheword'), - ] - ) - assert get_error_code(exc_info.value) == TRANSACTION_CANCELLED - assert 'ConditionalCheckFailed' in get_error_message(exc_info.value) - # Version attribute is not updated on failure. - assert foo2.version is None - - with pytest.raises(TransactWriteError) as exc_info: - with TransactWrite(connection=connection) as transaction: - transaction.delete(foo2) - assert get_error_code(exc_info.value) == TRANSACTION_CANCELLED - assert 'ConditionalCheckFailed' in get_error_message(exc_info.value)