Skip to content

Commit c5acff1

Browse files
Merge branch 'release-v5.6.1'
2 parents 39184b2 + 0a0bb05 commit c5acff1

File tree

7 files changed

+122
-24
lines changed

7 files changed

+122
-24
lines changed

btrdb/endpoint.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ def obliterate(self, uu):
6666
result = self.stub.Obliterate(params)
6767
BTrDBError.checkProtoStat(result.stat)
6868

69-
def setStreamAnnotations(self, uu, expected, changes):
69+
def setStreamAnnotations(self, uu, expected, changes, removals):
7070
annkvlist = []
7171
for k, v in changes.items():
7272
if v is None:
@@ -77,7 +77,7 @@ def setStreamAnnotations(self, uu, expected, changes):
7777
ov = btrdb_pb2.OptValue(value = v)
7878
kv = btrdb_pb2.KeyOptValue(key = k, val = ov)
7979
annkvlist.append(kv)
80-
params = btrdb_pb2.SetStreamAnnotationsParams(uuid=uu.bytes, expectedPropertyVersion=expected, changes=annkvlist)
80+
params = btrdb_pb2.SetStreamAnnotationsParams(uuid=uu.bytes, expectedPropertyVersion=expected, changes=annkvlist, removals=removals)
8181
result = self.stub.SetStreamAnnotations(params)
8282
BTrDBError.checkProtoStat(result.stat)
8383

btrdb/point.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ class RawPoint(object):
3636
The value of a time series at a single point in time.
3737
3838
"""
39+
40+
__slots__ = ["_time", "_value"]
41+
3942
def __init__(self, time, value):
4043
self._time = time
4144
self._value = value
@@ -132,6 +135,9 @@ class StatPoint(object):
132135
133136
134137
"""
138+
139+
__slots__ = ["_time", "_min", "_mean", "_max", "_count", "_stddev"]
140+
135141
def __init__(self, time, minv, meanv, maxv, count, stddev):
136142
self._time = time
137143
self._min = minv

btrdb/stream.py

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ def exists(self):
108108
Returns
109109
-------
110110
bool
111-
Indicates whether stream exists.
111+
Indicates whether stream is extant in the BTrDB server.
112112
"""
113113

114114
if self._known_to_exist:
@@ -425,7 +425,7 @@ def _update_tags_collection(self, tags, collection):
425425
collection=collection
426426
)
427427

428-
def _update_annotations(self, annotations, encoder):
428+
def _update_annotations(self, annotations, encoder, replace):
429429
# make a copy of the annotations to prevent accidental mutable object mutation
430430
serialized = deepcopy(annotations)
431431
if encoder is not None:
@@ -434,32 +434,64 @@ def _update_annotations(self, annotations, encoder):
434434
for k, v in serialized.items()
435435
}
436436

437+
removals = []
438+
if replace:
439+
removals = [i for i in self._annotations.keys() if i not in annotations.keys()]
440+
437441
self._btrdb.ep.setStreamAnnotations(
438442
uu=self.uuid,
439443
expected=self._property_version,
440-
changes=serialized
444+
changes=serialized,
445+
removals=removals
441446
)
442447

443-
def update(self, tags=None, annotations=None, collection=None, encoder=AnnotationEncoder):
448+
def update(self, tags=None, annotations=None, collection=None, encoder=AnnotationEncoder, replace=False):
444449
"""
445-
Updates metadata including tags, annotations, and collection.
450+
Updates metadata including tags, annotations, and collection as an
451+
UPSERT operation.
452+
453+
By default, the update will only affect the keys and values in the
454+
specified tags and annotations dictionaries, inserting them if they
455+
don't exist, or updating the value for the key if it does exist. If
456+
any of the update arguments (i.e. tags, annotations, collection) are
457+
None, they will remain unchanged in the database.
458+
459+
To delete either tags or annotations, you must specify exactly which
460+
keys and values you want set for the field and set `replace=True`. For
461+
example:
462+
463+
>>> annotations, _ = stream.anotations()
464+
>>> del annotations["key_to_delete"]
465+
>>> stream.update(annotations=annotations, replace=True)
466+
467+
This ensures that all of the keys and values for the annotations are
468+
preserved except for the key to be deleted.
446469
447470
Parameters
448-
----------
449-
tags: dict
450-
dict of tag information for the stream.
451-
annotations: dict
452-
dict of annotation information for the stream.
453-
collection: str
454-
The collection prefix for a stream
455-
encoder: json.JSONEncoder or None
456-
JSON encoder to class to use for annotation serializations, set to
457-
None to prevent JSON encoding of the annotations.
471+
-----------
472+
tags : dict, optional
473+
Specify the tag key/value pairs as a dictionary to upsert or
474+
replace. If None, the tags will remain unchanged in the database.
475+
annotations : dict, optional
476+
Specify the annotations key/value pairs as a dictionary to upsert
477+
or replace. If None, the annotations will remain unchanged in the
478+
database.
479+
collection : str, optional
480+
Specify a new collection for the stream. If None, the collection
481+
will remain unchanged.
482+
encoder : json.JSONEncoder or None
483+
JSON encoder class to use for annotation serialization. Set to None
484+
to prevent JSON encoding of the annotations.
485+
replace : bool, default: False
486+
Replace all annotations or tags with the specified dictionaries
487+
instead of performing the normal upsert operation. Specifying True
488+
is the only way to remove annotation keys.
458489
459490
Returns
460491
-------
461492
int
462493
The version of the metadata (separate from the version of the data)
494+
also known as the "property version".
463495
464496
"""
465497
if tags is None and annotations is None and collection is None:
@@ -479,7 +511,7 @@ def update(self, tags=None, annotations=None, collection=None, encoder=Annotatio
479511
self.refresh_metadata()
480512

481513
if annotations is not None:
482-
self._update_annotations(annotations, encoder)
514+
self._update_annotations(annotations, encoder, replace)
483515
self.refresh_metadata()
484516

485517
return self._property_version

btrdb/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
__version_info__ = {
1919
'major': 5,
2020
'minor': 6,
21-
'micro': 0,
21+
'micro': 1,
2222
'releaselevel': 'final',
2323
'serial': 12,
2424
}

docs/source/working/stream-manage-metadata.rst

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ is also returned when asking for annotations. This version number is incremente
5050
whenever metadata (tags, annotations, collection, etc.) are updated but not when
5151
making changes to the underlying time series data.
5252

53+
By default the method will attempt to provide a cached copy of the annotations
54+
however you can request the latest version from the server using the `refresh`
55+
argument.
56+
5357
.. code-block:: python
5458
5559
stream.annotations(refresh=True)
@@ -76,8 +80,14 @@ just a convenience as this value can also be found within the tags.
7680
Updating Metadata
7781
----------------------------
7882
An :code:`update` method is available if you would like to make changes to
79-
the tags, annotations, or collection. Note that a single operation could make
80-
multiple updates to the property version.
83+
the tags, annotations, or collection. By default, all updates are implemented
84+
as an UPSERT operation and a single change could result in multiple increments
85+
to the property version (the version of the metadata).
86+
87+
Upon calling this method, the library will first verify that the local property version of your
88+
stream object matches the version found on the server. If the two versions
89+
do not match then you will not be allowed to perform an update as this implies
90+
that the data has already been changed by another user or process.
8191

8292
.. code-block:: python
8393
@@ -87,4 +97,17 @@ multiple updates to the property version.
8797
'state': 'VT',
8898
'created': '2018-01-01 12:42:03 -0500'
8999
}
90-
prop_version = stream.update(collection=collection, annotations=annotations)
100+
property_version = stream.update(
101+
collection=collection,
102+
annotations=annotations
103+
)
104+
105+
If you would like to remove any keys from your annotations you must use the `replace=True` keyword argument. This will ensure that the annotations dictionary you provide completely replaces the existing values rather than perform an UPSERT operation. The example below shows how you could remove an existing key from the annotations dictionary.
106+
107+
.. code-block:: python
108+
109+
annotations, _ = stream.anotations()
110+
del annotations["key_to_delete"]
111+
stream.update(annotations=annotations, replace=True)
112+
113+

tests/btrdb/test_base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
## Test Constants
2727
##########################################################################
2828

29-
EXPECTED_VERSION = "5.6"
29+
EXPECTED_VERSION = "5.6.1"
3030

3131

3232
##########################################################################

tests/btrdb/test_stream.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,8 @@ def test_update_annotations(self):
276276
'frequency': '30',
277277
'control': '2019-11-07 13:21:23.000000-0500',
278278
"calibrate": '{"racf": 1.8, "pacf": 0.005}',
279-
}
279+
},
280+
removals=[],
280281
)
281282
stream._btrdb.ep.setStreamTags.assert_not_called()
282283

@@ -327,6 +328,9 @@ def test_update_annotations_nested_conversions(self):
327328
)
328329

329330
def test_update_annotations_no_encoder(self):
331+
"""
332+
Assert update annotations works with None as encoder argument
333+
"""
330334
uu = uuid.UUID('0d22a53b-e2ef-4e0a-ab89-b2d48fb2592a')
331335
endpoint = Mock(Endpoint)
332336
endpoint.streamInfo = Mock(return_value=("koala", 42, {}, {}, None))
@@ -340,11 +344,44 @@ def test_update_annotations_no_encoder(self):
340344
uu=uu,
341345
expected=42,
342346
changes=annotations,
347+
removals=[],
343348
)
344349

345350
# TODO: mock json.dumps
346351
# assert mock_dumps.assert_not_called()
347352

353+
def test_update_annotations_replace(self):
354+
"""
355+
Assert that replace argument will add proper keys to removals array in
356+
endpoint call.
357+
"""
358+
uu = uuid.UUID('0d22a53b-e2ef-4e0a-ab89-b2d48fb2592a')
359+
endpoint = Mock(Endpoint)
360+
endpoint.streamInfo = Mock(return_value=("koala", 42, {}, {"phase": "A", "source": "PJM"}, None))
361+
stream = Stream(btrdb=BTrDB(endpoint), uuid=uu)
362+
363+
annotations = {"foo": "this is a string", "phase": "A", }
364+
365+
stream.refresh_metadata()
366+
367+
# remove one of the keys and add new ones
368+
stream.update(annotations=annotations, replace=True)
369+
stream._btrdb.ep.setStreamAnnotations.assert_called_once_with(
370+
uu=uu,
371+
expected=42,
372+
changes=annotations,
373+
removals=["source"],
374+
)
375+
376+
# clear annotations
377+
stream.update(annotations={}, replace=True)
378+
stream._btrdb.ep.setStreamAnnotations.assert_called_with(
379+
uu=uu,
380+
expected=42,
381+
changes={},
382+
removals=["phase", "source"],
383+
)
384+
348385
##########################################################################
349386
## exists tests
350387
##########################################################################

0 commit comments

Comments
 (0)