<commit_before>.. _changelog:

Versions
^^^^^^^^

This document provides migration instructions for each major version, as well as the complete changelog for
versions dating back to :ref:`v0.9.0<changelog-v0.9.0>` from December 2015.  The migration guides provide detailed
examples and tips for migrating from the previous major version (excluding the 1.0.0 guide, which only covers
migration from 0.9.0 and newer).


====================
 Migrating to 2.0.0
====================

The 2.0.0 release includes a number of api changes and new features.

* The largest functional change is the ability to compose models through subclassing; this is
  referred to as Abstract Inheritance and Mixins throughout the User Guide.
* Python 3.6.0 is the minimum required version.
* ``Meta.init`` now defaults to ``cls.__new__(cls)`` instead of ``cls.__init__()``; when model instances are created
  as part of ``engine.query``, ``engine.stream`` etc. these will not call your model's ``__init__`` method.  The
  default ``BaseModel.__init__`` is not meant for use outside of local instantiation.
* The ``Column`` and ``Index`` kwarg ``name`` was renamed to ``dynamo_name`` to accurately reflect how the value was
  used: ``Column(SomeType, name="foo")`` becomes ``Column(SomeType, dynamo_name="foo")``.<commit_msg>migration guide for 3.0<commit_after>.. _changelog:

Versions
^^^^^^^^

This document provides migration instructions for each major version, as well as the complete changelog for
versions dating back to :ref:`v0.9.0<changelog-v0.9.0>` from December 2015.  The migration guides provide detailed
examples and tips for migrating from the previous major version (excluding the 1.0.0 guide, which only covers
migration from 0.9.0 and newer).

====================
 Migrating to 3.0.0
====================

The 3.0.0 release includes two api changes from 2.4.0 that you will may need to update your code to handle.

* The ``atomic=`` kwarg to ``Engine.save`` and ``Engine.delete`` was deprecated in 2.4.0 and is removed in 3.0.0.
* The return type of ``Type._dump`` must now be a ``bloop.action.Action`` instance, even when the value is ``None``.
  This does not impact custom types that only implement ``dynamo_load`` and ``dynamo_dump``.

--------------------
 ``atomic`` keyword
--------------------

The atomic keyword to Engine.save and Engine.delete has been removed in favor of a user pattern.  This offers a
reasonable performance improvement for users that never used the atomic keyword, and addresses ambiguity related to
per-row atomic vs transactional atomic operations.  For context on the deprecation, see `Issue #138`_.  For the
equivalent user pattern, see :ref:`patterns-snapshot`.  To migrate your existing code, you can use the following:

.. code-block:: python

    # pre-3.0 code to migrate:
    engine.load(some_object)
    some_object.some_attr = "new value"
    engine.save(some_object, atomic=True)

    # post-3.0 code:

    # https://bloop.readthedocs.io/en/latest/user/patterns.html#snapshot-condition
    from your_patterns import snapshot

    engine.load(some_object)
    condition = snapshot(some_object)
    some_object.some_attr = "new value"
    engine.save(some_object, condition=condition)

.. _Issue #138: https://github.com/numberoverzero/bloop/issues/138

------------
 Type._dump
------------

Bloop now allows users to specify how a value should be applied in an UpdateExpression by wrapping a value in a
``bloop.actions.Action`` object.  This is done transparently for raw values, which are interpreted
as either ``bloop.actions.set`` or ``bloop.actions.remove``.  With 2.4 and to support `Issue #136`_ you can also
specify an ``add`` or ``delete`` action:

.. code-block:: python

    my_user.aliases = bloop.actions.add("new_alias")
    my_website.views = bloop.actions.add(1)

To maintain flexibility the bloop ``Type`` class has the final say as to which action a value should use.  This allows
eg. the ``List`` type to take a literal ``[]`` and change the action from ``actions.set`` to ``actions.remove(None)``
to indicate that the value should be cleared.  This also means your custom type could see an ``actions.delete`` and
modify the value to instead be expressible in an ``actions.set``.

If your custom types today only override ``dynamo_dump`` or ``dynamo_load`` then you don't need to do anything for this
migration.  However if you currently override ``_dump`` then you should update your function to (1) handle input that
may be an action or not, and (2) always return an action instance.  In general, you should not modify an input
action and instead should return a new instance (possibly with the same action_type).

Here's the migration of the base ``Type._dump``:

.. code-block:: python

    # pre-3.0 code to migrate:
    def _dump(self, value, **kwargs):
        value = self.dynamo_dump(value, **kwargs)
        if value is None:
            return None
        return {self.backing_type: value}


    # post-3.0 code:
    from bloop import actions

    def _dump(self, value, **kwargs):
        wrapped = actions.wrap(value)  # [1]
        value = self.dynamo_dump(wrapped.value, **kwargs)
        if value is None:
            return actions.wrap(None)  # [2]
        else:
            value = {self.backing_type: value}
            return wrapped.type.new_action(value)  # [3]

    # [1] always wrap the input value to ensure you're working with an Action instance
    # [2] returns actions.remove(None) which will remove the value like None previously did
    # [3] new_action uses the **same action type** as the input.
    #         If you want to always return a SET action instead use: return actions.set(value)


.. _Issue #136: https://github.com/numberoverzero/bloop/issues/136

====================
 Migrating to 2.0.0
====================

The 2.0.0 release includes a number of api changes and new features.

* The largest functional change is the ability to compose models through subclassing; this is
  referred to as Abstract Inheritance and Mixins throughout the User Guide.
* Python 3.6.0 is the minimum required version.
* ``Meta.init`` now defaults to ``cls.__new__(cls)`` instead of ``cls.__init__()``; when model instances are created
  as part of ``engine.query``, ``engine.stream`` etc. these will not call your model's ``__init__`` method.  The
  default ``BaseModel.__init__`` is not meant for use outside of local instantiation.
* The ``Column`` and ``Index`` kwarg ``name`` was renamed to ``dynamo_name`` to accurately reflect how the value was
  used: ``Column(SomeType, name="foo")`` becomes ``Column(SomeType, dynamo_name="foo")``.
{'fraction_non_alphanumeric': 0.1244343891402715, 'fraction_numerical': 0.01568627450980392, 'mean_word_length': 3.8190406976744184, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 3, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '885611', 'n_tokens_mistral': 1920, 'n_tokens_neox': 1833, 'n_words': 803}