This changelog structure is based on Keep a Changelog v0.3.0. Bloop follows Semantic Versioning 2.0.0 and a draft appendix for its Public API.
(no unreleased changes)
DynamicList
andDynamicMap
types can store arbitrary values, although they will only be loaded as their primitive, direct mapping to DynamoDB backing types. For example:class MyModel(BaseModel): id = Column(String, hash_key=True) blob = Column(DynamicMap) i = MyModel(id="i") i.blob = {"foo": "bar", "inner": [True, {1, 2, 3}, b""]}
Meta supports Continuous Backups for Point-In-Time Recovery:
class MyModel(BaseModel): id = Column(String, hash_key=True) class Meta: backups = {"enabled": True}
SearchIterator
exposes anall()
method which eagerly loads all results and returns a single list. Note that the query or scan is reset each time the method is called, discarding any previously buffered state.
String
andBinary
types loadNone
as""
andb""
respectively.- Saving an empty String or Binary (
""
orb""
) will no longer throw a botocore exception, and will instead be treated asNone
. This brings behavior in line with the Set, List, and Map types.
Added support for Server-Side Encryption. This uses an AWS-managed Customer Master Key (CMK) stored in KMS which is managed for free: "You are not charged for the following: AWS-managed CMKs, which are automatically created on your behalf when you first attempt to encrypt a resource in a supported AWS service."
Meta
supports Server Side Encryption:class MyModel(BaseModel): id = Column(String, hash_key=True) class Meta: encryption = {"enabled": True}
Fix a bug where the last records in a closed shard in a Stream were dropped. See Issue #87 and PR #112.
Stream
no longer drops the last records from a closed Shard when moving to the child shard.
2.0.0 introduces 4 significant new features:
Model inheritance and mixins
Table name templates:
table_name_template="prod-{table_name}"
TTL Support:
ttl = {"column": "not_after"}
Column defaults:
verified=Column(Boolean, default=False) not_after = Column( Timestamp, default=lambda: ( datetime.datetime.now() + datetime.timedelta(days=30) ) )
Python 3.6.0 is now the minimum required version, as Bloop takes advantage of __set_name
and
__init_subclass__
to avoid the need for a Metaclass.
A number of internal-only and rarely-used external methods have been removed, as the processes which required them have been simplified:
Column.get, Column.set, Column.delete
in favor of their descriptor protocol counterpartsbloop.Type._register
is no longer necessary before using a custom TypeIndex._bind
is replaced by helpersbind_index
andrefresh_index
. You should not need to call these.- A number of overly-specific exceptions have been removed.
Engine
takes an optional keyword-only arg"table_name_template"
which takes either a string used to format each name, or a function which will be called with the model to get the table name of. This removes the need to connect to thebefore_create_table
signal, which also could not handle multiple table names for the same model. With this changeBaseModel.Meta.table_name
will no longer be authoritative, and the engine must be consulted to find a given model's table name. An internal functionEngine._compute_table_name
is available, and the per-engine table names may be added to the model.Meta in the future. (see Issue #96)A new exception
InvalidTemplate
is raised when an Engine's table_name_template is a string but does not contain the required"{table_name}"
formatting key.You can now specify a TTL (see Issue #87) on a model much like a Stream:
class MyModel(BaseModel): class Meta: ttl = { "column": "expire_after" } id = Column(UUID, hash_key=True) expire_after = Column(Timestamp)
A new type,
Timestamp
was added. This stores adatetime.datetime
as a unix timestamp in whole seconds.Corresponding
Timestamp
types were added to the following extensions, mirroring theDateTime
extension:bloop.ext.arrow.Timestamp
,bloop.ext.delorean.Timestamp
, andbloop.ext.pendulum.Timestamp
.Column
takes an optional kwargdefault
, either a single value or a no-arg function that returns a value. Defaults are applied only duringBaseModel.__init__
and not when loading objects from a Query, Scan, or Stream. If your function returnsbloop.util.missing
, no default will be applied. (see PR #90, PR #105 for extensive discussion)(internal) A new abstract interface,
bloop.models.IMeta
was added to assist with code completion. This fully describes the contents of aBaseModel.Meta
instance, and can safely be subclassed to provide hints to your editor:class MyModel(BaseModel): class Meta(bloop.models.IMeta): table_name = "my-table" ...
(internal)
bloop.session.SessionWrapper.enable_ttl
can be used to enable a TTL on a table. This SHOULD NOT be called unless the table was just created by bloop.(internal) helpers for dynamic model inheritance have been added to the
bloop.models
package:bloop.models.bind_column
bloop.models.bind_index
bloop.models.refresh_index
bloop.models.unbind
Direct use is discouraged without a strong understanding of how binding and inheritance work within bloop.
Python 3.6 is the minimum supported version.
BaseModel
no longer requires a Metaclass, which allows it to be used as a mixin to an existing class which may have a Metaclass.BaseModel.Meta.init
no longer defaults to the model's__init__
method, and will instead usecls.__new__(cls)
to obtain an instance of the model. You can still specify a custom initialization function:class MyModel(BaseModel): class Meta: @classmethod def init(_): instance = MyModel.__new__(MyModel) instance.created_from_init = True id = Column(...)
Column
andIndex
support the shallow copy method__copy__
to simplify inheritance with custom subclasses. You may override this to change how your subclasses are inherited.DateTime
explicitly guards againsttzinfo is None
, sincedatetime.astimezone
started silently allowing this in Python 3.6 -- you should not use a naive datetime for any reason.Column.model_name
is nowColumn.name
, andIndex.model_name
is nowIndex.name
.Column(name=)
is nowColumn(dynamo_name=)
andIndex(name=)
is nowIndex(dynamo_name=)
The exception
InvalidModel
is raised instead ofInvalidIndex
.The exception
InvalidSearch
is raised instead of the following:InvalidSearchMode
,InvalidKeyCondition
,InvalidFilterCondition
, andInvalidProjection
.(internal)
bloop.session.SessionWrapper
methods now require an explicit table name, which is not read from the model name. This exists to support different computed table names per engine. The following methods now require a table name:create_table
,describe_table
(new),validate_table
, andenable_ttl
(new).
- bloop no longer supports Python versions below 3.6.0
- bloop no longer depends on declare
Column.get
,Column.set
, andColumn.delete
helpers have been removed in favor of using the Descriptor protocol methods directly:Column.__get__
,Column.__set__
, andColumn.__delete__
.bloop.Type
no longer exposes a_register
method; there is no need to register types before using them, and you can remove the call entirely.Column.model_name
,Index.model_name
, and the kwargsColumn(name=)
,Index(name=)
(see above)- The exception
InvalidIndex
has been removed. - The exception
InvalidComparisonOperator
was unused and has been removed. - The exception
UnboundModel
is no longer raised duringEngine.bind
and has been removed. - The exceptions
InvalidSearchMode
,InvalidKeyCondition
,InvalidFilterCondition
, andInvalidProjection
have been removed. - (internal)
Index._bind
has been replaced with the more complete solutions inbloop.models.bind_column
andbloop.models.bind_index
.
This release is exclusively to prepare users for the name
/model_name
/dynamo_name
changes coming in 2.0;
your 1.2.0 code will continue to work as usual but will raise DeprecationWarning
when accessing model_name
on
a Column or Index, or when specifying the name=
kwarg in the __init__
method of Column
,
GlobalSecondaryIndex
, or LocalSecondaryIndex
.
Previously it was unclear if Column.model_name
was the name of this column in its model, or the name of the model
it is attached to (eg. a shortcut for Column.model.__name__
). Additionally the name=
kwarg actually mapped to
the object's .dynamo_name
value, which was not obvious.
Now the Column.name
attribute will hold the name of the column in its model, while Column.dynamo_name
will
hold the name used in DynamoDB, and is passed during initialization as dynamo_name=
. Accessing model_name
or
passing name=
during __init__
will raise deprecation warnings, and bloop 2.0.0 will remove the deprecated
properties and ignore the deprecated kwargs.
Column.name
is the new home of theColumn.model_name
attribute. The same is true forIndex
,GlobalSecondaryIndex
, andLocalSecondaryIndex
.- The
__init__
method ofColumn
,Index
,GlobalSecondaryIndex
, andLocalSecondaryIndex
now takesdynamo_name=
in place ofname=
.
- Accessing
Column.model_name
raisesDeprecationWarning
, and the same for Index/GSI/LSI. - Providing
Column(name=)
raisesDeprecationWarning
, and the same for Index/GSI/LSI.
When a Model's Meta does not explicitly set
read_units
andwrite_units
, it will only default to 1/1 if the table does not exist and needs to be created. If the table already exists, any throughput will be considered valid. This will still ensure new tables have 1/1 iops as a default, but won't fail if an existing table has more than one of either.There is no behavior change for explicit integer values of
read_units
andwrite_units
: if the table does not exist it will be created with those values, and if it does exist then validation will fail if the actual values differ from the modeled values.An explicit
None
for eitherread_units
orwrite_units
is equivalent to omitting the value, but allows for a more explicit declaration in the model.Because this is a relaxing of a default only within the context of validation (creation has the same semantics) the only users that should be impacted are those that do not declare
read_units
andwrite_units
and rely on the built-in validation failing to match on values != 1. Users that rely on the validation to succeed on tables with values of 1 will see no change in behavior. This fits within the extended criteria of a minor release since there is a viable and obvious workaround for the current behavior (declare 1/1 and ensure failure on other values).When a Query or Scan has projection type "count", accessing the
count
orscanned
properties will immediately execute and exhaust the iterator to provide the count or scanned count. This simplifies the previous workaround of callingnext(query, None)
before usingquery.count
.
- Fixed a bug where a Query or Scan with projection "count" would always raise KeyError (see Issue #95)
- Fixed a bug where resetting a Query or Scan would cause
__next__
to raisebotocore.exceptions.ParamValidationError
(see Issue #95)
Engine.bind
takes optional kwargskip_table_setup
to skip CreateTable and DescribeTable calls (see Issue #83)- Index validates against a superset of the projection (see Issue #71)
Bug fix.
- Stream orders records on the integer of SequenceNumber, not the lexicographical sorting of its string representation. This is an annoying bug, because as documented we should be using lexicographical sorting on the opaque string. However, without leading 0s that sort fails, and we must assume the string represents an integer to sort on. Particularly annoying, tomorrow the SequenceNumber could start with non-numeric characters and still conform to the spec, but the sorting-as-int assumption breaks. However, we can't properly sort without making that assumption.
Minor bug fix.
- extension types in
ext.arrow
,ext.delorean
, andext.pendulum
now load and dumpNone
correctly.
Bug fixes.
- The
arrow
,delorean
, andpendulum
extensions now have a default timezone of"utc"
instead ofdatetime.timezone.utc
. There are open issues for both projects to verify if that is the expected behavior.
- DynamoDBStreams return a Timestamp for each record's ApproximateCreationDateTime, which botocore is translating into a real datetime.datetime object. Previously, the record parser assumed an int was used. While this fix is a breaking change for an internal API, this bug broke the Stream iterator interface entirely, which means no one could have been using it anyway.
1.0.0 is the culmination of just under a year of redesigns, bug fixes, and new features. Over 550 commits, more than 60 issues closed, over 1200 new unit tests. At an extremely high level:
- The query and scan interfaces have been polished and simplified. Extraneous methods and configuration settings have been cut out, while ambiguous properties and methods have been merged into a single call.
- A new, simple API exposes DynamoDBStreams with just a few methods; no need to manage individual shards, maintain shard hierarchies and open/closed polling. I believe this is a first since the Kinesis Adapter and KCL, although they serve different purposes. When a single worker can keep up with a model's stream, Bloop's interface is immensely easier to use.
- Engine's methods are more consistent with each other and across the code base, and all of the configuration settings
have been made redundant. This removes the need for
EngineView
and its associated temporary config changes. - Blinker-powered signals make it easy to plug in additional logic when certain events occur: before a table is created; after a model is validated; whenever an object is modified.
- Types have been pared down while their flexibility has increased significantly. It's possible to create a type that loads another object as a column's value, using the engine and context passed into the load and dump functions. Be careful with this; transactions on top of DynamoDB are very hard to get right.
See the Migration Guide above for specific examples of breaking changes and how to fix them, or the User Guide for a tour of the new Bloop. Lastly, the Public and Internal API References are finally available and should cover everything you need to extend or replace whole subsystems in Bloop (if not, please open an issue).
bloop.signals
exposes Blinker signals which can be used to monitor object changes, when instances are loaded from a query, before models are bound, etc.before_create_table
object_loaded
object_saved
object_deleted
object_modified
model_bound
model_created
model_validated
Engine.stream
can be used to iterate over all records in a stream, with a total ordering over approximate record creation time. Useengine.stream(model, "trim_horizon")
to get started. See the User GuideNew exceptions
RecordsExpired
andShardIteratorExpired
for errors in stream stateNew exceptions
Invalid*
for bad input subclassBloopException
andValueError
DateTime
types for the three most common date time libraries:bloop.ext.arrow.DateTime
bloop.ext.delorean.DateTime
bloop.ext.pendulum.DateTime
model.Meta
has a new optional attributestream
which can be used to enable a stream on the model's table. See the User Guide for detailsmodel.Meta
exposes the sameprojection
attribute asIndex
so that(index or model.Meta).projection
can be used interchangeablyNew
Stream
class exposes DynamoDBStreams API as a single iterable with powerful seek/jump options, and simple json-friendly tokens for pausing and resuming iteration. See the User Guide for detailsOver 1200 unit tests added
Initial integration tests added
(internal)
bloop.conditions.ReferenceTracker
handles building#n0
,:v1
, and associated values. Useany_ref
to build a reference to a name/path/value, andpop_refs
when backtracking (eg. when a value is actually another column, or when correcting a partially valid condition)(internal)
bloop.conditions.render
is the preferred entry point for rendering, and handles all permutations of conditions, filters, projections. Use overConditionRenderer
unless you need very specific control over rendering sequencing.(internal)
bloop.session.SessionWrapper
exposes DynamoDBStreams operations in addition to previousbloop.Client
wrappers around DynamoDB client(internal) New supporting classes
streams.buffer.RecordBuffer
,streams.shard.Shard
, andstreams.coordinator.Coordinator
to encapsulate the hell^Wjoy that is working with DynamoDBStreams(internal) New class
util.Sentinel
for placeholder values likemissing
andlast_token
that provide clearer docstrings, instead of showingfunc(..., default=object<0x...>)
these will showfunc(..., default=Sentinel<[Missing]>)
bloop.Column
emitsobject_modified
on__set__
and__del__
Conditions now check if they can be used with a column's
typedef
and raiseInvalidCondition
when they can't. For example,contains
can't be used onNumber
, nor>
onSet(String)
bloop.Engine
no longer takes an optionalbloop.Client
but instead optionaldynamodb
anddynamodbstreams
clients (usually created fromboto3.client("dynamodb")
etc.)Engine
no longer takes**config
-- its settings have been dispersed to their local touch pointsatomic
is a parameter ofsave
anddelete
and defaults toFalse
consistent
is a parameter ofload
,query
,scan
and defaults toFalse
prefetch
has no equivalent, and is baked into the new Query/Scan iterator logicstrict
is a parameter of aLocalSecondaryIndex
, defaults toTrue
Engine
no longer has acontext
to create temporary views with different configurationEngine.bind
is no longer by keyword arg only:engine.bind(MyBase)
is acceptable in addition toengine.bind(base=MyBase)
Engine.bind
emits new signalsbefore_create_table
,model_validated
, andmodel_bound
Engine.delete
andEngine.save
take*objs
instead ofobjs
to easily save/delete small multiples of objects (engine.save(user, tweet)
instead ofengine.save([user, tweet])
)Engine
guards against loading, saving, querying, etc against abstract modelsEngine.load
raisesMissingObjects
instead ofNotModified
(exception rename)Engine.scan
andEngine.query
take all query and scan arguments immediately, instead of using the builder pattern. For example,engine.scan(model).filter(Model.x==3)
has becomeengine.scan(model, filter=Model.x==3)
.bloop.exceptions.NotModified
renamed tobloop.exceptions.MissingObjects
Any code that raised
AbstractModelException
now raisesUnboundModel
bloop.types.DateTime
is now backed bydatetime.datetime
instead ofarrow
. Only supports UTC now, no local timezone. Use thebloop.ext.arrow.DateTime
class to continue usingarrow
.The query and scan interfaces have been entirely refactored:
count
,consistent
,ascending
and other properties are part of theEngine.query(...)
parameters.all()
is no longer needed, asEngine.scan
and.query
immediately return an iterable object. There is noprefetch
setting, orlimit
.The
complete
property for Query and Scan have been replaced withexhausted
, to be consistent with the Stream moduleThe query and scan iterator no longer cache results
The
projection
parameter is now required forGlobalSecondaryIndex
andLocalSecondaryIndex
Calling
Index.__set__
orIndex.__del__
will raiseAttributeError
. For example,some_user.by_email = 3
raises ifUser.by_email
is a GSIbloop.Number
replacesbloop.Float
and takes an optionaldecimal.Context
for converting numbers. For a less strict, lossyFloat
type see the Patterns section of the User Guidebloop.String.dynamo_dump
no longer callsstr()
on the value, which was hiding bugs where a non-string object was passed (eg.some_user.name = object()
would save with a name of<object <0x...>
)bloop.DateTime
is now backed bydatetime.datetime
and only knows UTC in a fixed format. Adapters forarrow
,delorean
, andpendulum
are available inbloop.ext
bloop.DateTime
does not support naive datetimes; they must always have atzinfo
docs:
- use RTD theme
- rewritten three times
- now includes public and internal api references
(internal) Path lookups on
Column
(eg.User.profile["name"]["last"]
) use simpler proxies(internal) Proxy behavior split out from
Column
's base classbloop.conditions.ComparisonMixin
for a cleaner namespace(internal)
bloop.conditions.ConditionRenderer
rewritten, uses a newbloop.conditions.ReferenceTracker
with a much clearer api(internal)
ConditionRenderer
can backtrack references and handles columns as values (eg.User.name.in_([User.email, "literal"])
)(internal)
_MultiCondition
logic rolled intobloop.conditions.BaseCondition
,AndCondition
andOrCondition
no longer have intermediate base class(internal)
AttributeExists
logic rolled intobloop.conditions.ComparisonCondition
(internal)
bloop.tracking
rolled intobloop.conditions
and is hooked into theobject_*
signals. Methods are no longer called directly (eg. no need fortracking.sync(some_obj, engine)
)(internal) update condition is built from a set of columns, not a dict of updates to apply
(internal)
bloop.conditions.BaseCondition
is a more comprehensive base class, and handles all manner of out-of-order merges (and(x, y)
vsand(y, x)
where x is anand
condition and y is not)(internal) almost all
*Condition
classes simply implement__repr__
andrender
;BaseCondition
takes care of everything else(internal)
bloop.Client
becamebloop.session.SessionWrapper
(internal)
Engine._dump
takes an optionalcontext
,**kwargs
, matching the signature ofEngine._load
(internal)
BaseModel
no longer implements__hash__
,__eq__
, or__ne__
butModelMetaclass
will always ensure a__hash__
function when the subclass is created(internal)
Filter
andFilterIterator
rewritten entirely in thebloop.search
module across multiple classes
AbstractModelException
has been rolled intoUnboundModel
- The
all()
method has been removed from the query and scan iterator interface. Simply iterate withnext(query)
orfor result in query:
Query.results
andScan.results
have been removed and results are no longer cached. You can begin the search again withquery.reset()
- The
new_base()
function has been removed in favor of subclassingBaseModel
directly bloop.Float
has been replaced bybloop.Number
- (internal)
bloop.engine.LoadManager
logic was rolled intobloop.engine.load(...)
EngineView
has been removed since engines no longer have a baselineconfig
and don't need a context to temporarily modify it- (internal)
Engine._update
has been removed in favor ofutil.unpack_from_dynamodb
- (internal)
Engine._instance
has been removed in favor of directly creating instances frommodel.Meta.init()
inunpack_from_dynamodb
Column.contains(value)
now rendersvalue
with the column typedef's inner type. Previously, the container type was used, soData.some_list.contains("foo"))
would render as(contains(some_list, ["f", "o", "o"]))
instead of(contains(some_list, "foo"))
Set
renders correct wire format -- previously, it incorrectly sent{"SS": [{"S": "h"}, {"S": "i"}]}
instead of the correct{"SS": ["h", "i"]}
- (internal)
Set
andList
expose aninner_typedef
for conditions to force rendering of inner values (currently only used byContainsCondition
)
Set
was rendering an invalid wire format, and now renders the correct "SS", "NS", or "BS" values.Set
andList
were renderingcontains
conditions incorrectly, by trying to dump each value in the value passed to contains. For example,MyModel.strings.contains("foo")
would rendercontains(#n0, :v1)
where:v1
was{"SS": [{"S": "f"}, {"S": "o"}, {"S": "o"}]}
. Now, non-iterable values are rendered singularly, so:v1
would be{"S": "foo"}
. This is a temporary fix, and only works for simple cases. For example,List(List(String))
will still break when performing acontains
check. This is fixed correctly in 1.0.0 and you should migrate as soon as possible.
model.Meta
now exposesgsis
andlsis
, in addition to the existingindexes
. This simplifies code that needs to iterate over each type of index and not all indexes.
engine_for_profile
was no longer necessary, since the client instances could simply be created with a given profile.
bloop.Client
now takesboto_client
, which should be an instance ofboto3.client("dynamodb")
instead of aboto3.session.Session
. This lets you specify endpoints and other configuration only exposed during the client creation process.Engine
no longer uses"session"
from the config, and instead takes aclient
param which should be an instance ofbloop.Client
. bloop.Client will be going away in 1.0.0 and Engine will simply take the boto3 clients directly.
- New exception
AbstractModelException
is raised when attempting to perform an operation which requires a table, on an abstract model. Raised by all Engine functions as well asbloop.Client
operations.
Engine
operations raiseAbstractModelException
when attempting to perform operations on abstract models.- Previously, models were considered non-abstract if
model.Meta.abstract
was False, or there was no value. Now,ModelMetaclass
will explicitly setabstract
to False so thatmodel.Meta.abstract
can be used everywhere, instead ofgetattr(model.Meta, "abstract", False)
.
Column
has a new attributemodel
, the model it is bound to. This is set during the model's creation by theModelMetaclass
.
Engine.bind
will now skip intermediate models that are abstract. This makes it easier to pass abstract models, or models whose subclasses may be abstract (and have non-abstract grandchildren).
(no public changes)
Conditions implement
__eq__
for checking if two conditions will evaluate the same. For example:>>> large = Blob.size > 1024**2 >>> small = Blob.size < 1024**2 >>> large == small False >>> also_large = Blob.size > 1024**2 >>> large == also_large True >>> large is also_large False
0.9.6 is the first significant change to how Bloop binds models, engines, and tables. There are a few breaking changes, although they should be easy to update.
Where you previously created a model from the Engine's model:
from bloop import Engine
engine = Engine()
class MyModel(engine.model):
...
You'll now create a base without any relation to an engine, and then bind it to any engines you want:
from bloop import Engine, new_base
BaseModel = new_base()
class MyModel(BaseModel):
...
engine = Engine()
engine.bind(base=MyModel) # or base=BaseModel
- A new function
engine_for_profile
takes a profile name for the config file and creates an appropriate session. This is a temporary utility, sinceEngine
will eventually take instances of dynamodb and dynamodbstreams clients. This will be going away in 1.0.0. - A new base exception
BloopException
which can be used to catch anything thrown by Bloop. - A new function
new_base()
creates an abstract base for models. This replacesEngine.model
now that multiple engines can bind the same model. This will be going away in 1.0.0 which will provide aBaseModel
class.
- The
session
parameter toEngine
is now part of theconfig
kwargs. The underlyingbloop.Client
is no longer created inEngine.__init__
, which provides an opportunity to swap out the client entirely before the firstEngine.bind
call. The semantics of session and client are unchanged. Engine._load
,Engine._dump
, and all Type signatures now pass an engine explicitly through thecontext
parameter. This was mentioned in 0.9.2 andcontext
is now required.Engine.bind
now binds the given class and all subclasses. This simplifies most workflows, since you can now create a base withMyBase = new_base()
and then bind every model you create withengine.bind(base=MyBase)
.- All exceptions now subclass a new base exception
BloopException
instead ofException
. - Vector types
Set
,List
,Map
, andTypedMap
accept a typedef ofNone
so they can raise a more helpful error message. This will be reverted in 1.0.0 and will once again be a required parameter.
- Engine no longer has
model
,unbound_models
, ormodels
attributes.Engine.model
has been replaced by thenew_base()
function, and models are bound directly to the underlying type engine without tracking on theEngine
instance itself. - EngineView dropped the corresponding attributes above.
EngineView
attributes are now properties, and point to the underlying engine's attributes; this includesclient
,model
,type_engine
, andunbound_models
. This fixed an issue when usingwith engine.context(...) as view:
to perform operations on models bound to the engine but not the engine view. EngineView will be going away in 1.0.0.
- Engine functions now take optional config parameters to override the engine's config. You should update your code to
use these values instead of
engine.config
, since engine.config is going away in 1.0.0.Engine.delete
andEngine.save
expose theatomic
parameter, whileEngine.load
exposesconsistent
. - Added the
TypedMap
class, which provides dict mapping for a single typedef over any number of keys. This differs fromMap
, which must know all keys ahead of time and can use different types.TypedMap
only supports a single type, but can have arbitrary keys. This will be going away in 1.0.0.
- Type functions
_load
,_dump
,dynamo_load
,dynamo_dump
now take an optional keyword-only argcontext
. This dict will become required in 0.9.6, and contains the engine instance that should be used for recursive types. If your type currently usescls.Meta.bloop_engine
, you should start usingcontext["engine"]
in the next release. Thebloop_engine
attribute is being removed, since models will be able to bind to multiple engines.
(no public changes)