google-cloud-bigtable 2.30.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- google/cloud/bigtable/__init__.py +25 -0
- google/cloud/bigtable/app_profile.py +377 -0
- google/cloud/bigtable/backup.py +490 -0
- google/cloud/bigtable/batcher.py +414 -0
- google/cloud/bigtable/client.py +475 -0
- google/cloud/bigtable/cluster.py +541 -0
- google/cloud/bigtable/column_family.py +362 -0
- google/cloud/bigtable/data/__init__.py +103 -0
- google/cloud/bigtable/data/_async/__init__.py +25 -0
- google/cloud/bigtable/data/_async/_mutate_rows.py +226 -0
- google/cloud/bigtable/data/_async/_read_rows.py +363 -0
- google/cloud/bigtable/data/_async/client.py +1522 -0
- google/cloud/bigtable/data/_async/mutations_batcher.py +534 -0
- google/cloud/bigtable/data/_cross_sync/__init__.py +20 -0
- google/cloud/bigtable/data/_cross_sync/_decorators.py +441 -0
- google/cloud/bigtable/data/_cross_sync/_mapping_meta.py +64 -0
- google/cloud/bigtable/data/_cross_sync/cross_sync.py +334 -0
- google/cloud/bigtable/data/_helpers.py +249 -0
- google/cloud/bigtable/data/_sync_autogen/_mutate_rows.py +182 -0
- google/cloud/bigtable/data/_sync_autogen/_read_rows.py +304 -0
- google/cloud/bigtable/data/_sync_autogen/client.py +1293 -0
- google/cloud/bigtable/data/_sync_autogen/mutations_batcher.py +449 -0
- google/cloud/bigtable/data/exceptions.py +340 -0
- google/cloud/bigtable/data/execute_query/__init__.py +43 -0
- google/cloud/bigtable/data/execute_query/_async/__init__.py +13 -0
- google/cloud/bigtable/data/execute_query/_async/execute_query_iterator.py +291 -0
- google/cloud/bigtable/data/execute_query/_byte_cursor.py +123 -0
- google/cloud/bigtable/data/execute_query/_checksum.py +43 -0
- google/cloud/bigtable/data/execute_query/_parameters_formatting.py +155 -0
- google/cloud/bigtable/data/execute_query/_query_result_parsing_utils.py +134 -0
- google/cloud/bigtable/data/execute_query/_reader.py +122 -0
- google/cloud/bigtable/data/execute_query/_sync_autogen/execute_query_iterator.py +239 -0
- google/cloud/bigtable/data/execute_query/metadata.py +399 -0
- google/cloud/bigtable/data/execute_query/values.py +123 -0
- google/cloud/bigtable/data/mutations.py +380 -0
- google/cloud/bigtable/data/read_modify_write_rules.py +112 -0
- google/cloud/bigtable/data/read_rows_query.py +536 -0
- google/cloud/bigtable/data/row.py +535 -0
- google/cloud/bigtable/data/row_filters.py +968 -0
- google/cloud/bigtable/encryption_info.py +64 -0
- google/cloud/bigtable/enums.py +223 -0
- google/cloud/bigtable/error.py +64 -0
- google/cloud/bigtable/gapic_version.py +16 -0
- google/cloud/bigtable/helpers.py +31 -0
- google/cloud/bigtable/instance.py +789 -0
- google/cloud/bigtable/policy.py +255 -0
- google/cloud/bigtable/row.py +1267 -0
- google/cloud/bigtable/row_data.py +380 -0
- google/cloud/bigtable/row_filters.py +838 -0
- google/cloud/bigtable/row_merger.py +250 -0
- google/cloud/bigtable/row_set.py +213 -0
- google/cloud/bigtable/table.py +1409 -0
- google/cloud/bigtable_admin/__init__.py +410 -0
- google/cloud/bigtable_admin/gapic_version.py +16 -0
- google/cloud/bigtable_admin/py.typed +2 -0
- google/cloud/bigtable_admin_v2/__init__.py +250 -0
- google/cloud/bigtable_admin_v2/gapic_metadata.json +962 -0
- google/cloud/bigtable_admin_v2/gapic_version.py +16 -0
- google/cloud/bigtable_admin_v2/py.typed +2 -0
- google/cloud/bigtable_admin_v2/services/__init__.py +15 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/__init__.py +22 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/async_client.py +3465 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/client.py +3944 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/pagers.py +681 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/__init__.py +40 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/base.py +751 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/grpc.py +1249 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/grpc_asyncio.py +1579 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/rest.py +6820 -0
- google/cloud/bigtable_admin_v2/services/bigtable_instance_admin/transports/rest_base.py +1746 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/__init__.py +22 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/async_client.py +3472 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/client.py +3949 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/pagers.py +669 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/__init__.py +40 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/base.py +708 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/grpc.py +1257 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/grpc_asyncio.py +1549 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/rest.py +6609 -0
- google/cloud/bigtable_admin_v2/services/bigtable_table_admin/transports/rest_base.py +1714 -0
- google/cloud/bigtable_admin_v2/types/__init__.py +248 -0
- google/cloud/bigtable_admin_v2/types/bigtable_instance_admin.py +1364 -0
- google/cloud/bigtable_admin_v2/types/bigtable_table_admin.py +1715 -0
- google/cloud/bigtable_admin_v2/types/common.py +81 -0
- google/cloud/bigtable_admin_v2/types/instance.py +819 -0
- google/cloud/bigtable_admin_v2/types/table.py +1028 -0
- google/cloud/bigtable_admin_v2/types/types.py +776 -0
- google/cloud/bigtable_v2/__init__.py +136 -0
- google/cloud/bigtable_v2/gapic_metadata.json +193 -0
- google/cloud/bigtable_v2/gapic_version.py +16 -0
- google/cloud/bigtable_v2/py.typed +2 -0
- google/cloud/bigtable_v2/services/__init__.py +15 -0
- google/cloud/bigtable_v2/services/bigtable/__init__.py +22 -0
- google/cloud/bigtable_v2/services/bigtable/async_client.py +1741 -0
- google/cloud/bigtable_v2/services/bigtable/client.py +2194 -0
- google/cloud/bigtable_v2/services/bigtable/transports/__init__.py +38 -0
- google/cloud/bigtable_v2/services/bigtable/transports/base.py +343 -0
- google/cloud/bigtable_v2/services/bigtable/transports/grpc.py +660 -0
- google/cloud/bigtable_v2/services/bigtable/transports/grpc_asyncio.py +762 -0
- google/cloud/bigtable_v2/services/bigtable/transports/rest.py +2489 -0
- google/cloud/bigtable_v2/services/bigtable/transports/rest_base.py +711 -0
- google/cloud/bigtable_v2/types/__init__.py +138 -0
- google/cloud/bigtable_v2/types/bigtable.py +1531 -0
- google/cloud/bigtable_v2/types/data.py +1612 -0
- google/cloud/bigtable_v2/types/feature_flags.py +119 -0
- google/cloud/bigtable_v2/types/request_stats.py +171 -0
- google/cloud/bigtable_v2/types/response_params.py +64 -0
- google/cloud/bigtable_v2/types/types.py +579 -0
- google_cloud_bigtable-2.30.0.data/scripts/fixup_bigtable_admin_v2_keywords.py +233 -0
- google_cloud_bigtable-2.30.0.data/scripts/fixup_bigtable_v2_keywords.py +186 -0
- google_cloud_bigtable-2.30.0.dist-info/LICENSE +202 -0
- google_cloud_bigtable-2.30.0.dist-info/METADATA +154 -0
- google_cloud_bigtable-2.30.0.dist-info/RECORD +115 -0
- google_cloud_bigtable-2.30.0.dist-info/WHEEL +5 -0
- google_cloud_bigtable-2.30.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1267 @@
|
|
|
1
|
+
# Copyright 2015 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""User-friendly container for Google Cloud Bigtable Row."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
import struct
|
|
19
|
+
|
|
20
|
+
from google.cloud._helpers import _datetime_from_microseconds # type: ignore
|
|
21
|
+
from google.cloud._helpers import _microseconds_from_datetime # type: ignore
|
|
22
|
+
from google.cloud._helpers import _to_bytes # type: ignore
|
|
23
|
+
from google.cloud.bigtable_v2.types import data as data_v2_pb2
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
_PACK_I64 = struct.Struct(">q").pack
|
|
27
|
+
|
|
28
|
+
MAX_MUTATIONS = 100000
|
|
29
|
+
"""The maximum number of mutations that a row can accumulate."""
|
|
30
|
+
|
|
31
|
+
_MISSING_COLUMN_FAMILY = "Column family {} is not among the cells stored in this row."
|
|
32
|
+
_MISSING_COLUMN = (
|
|
33
|
+
"Column {} is not among the cells stored in this row in the column family {}."
|
|
34
|
+
)
|
|
35
|
+
_MISSING_INDEX = (
|
|
36
|
+
"Index {!r} is not valid for the cells stored in this row for column {} "
|
|
37
|
+
"in the column family {}. There are {} such cells."
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Row(object):
|
|
42
|
+
"""Base representation of a Google Cloud Bigtable Row.
|
|
43
|
+
|
|
44
|
+
This class has three subclasses corresponding to the three
|
|
45
|
+
RPC methods for sending row mutations:
|
|
46
|
+
|
|
47
|
+
* :class:`DirectRow` for ``MutateRow``
|
|
48
|
+
* :class:`ConditionalRow` for ``CheckAndMutateRow``
|
|
49
|
+
* :class:`AppendRow` for ``ReadModifyWriteRow``
|
|
50
|
+
|
|
51
|
+
:type row_key: bytes
|
|
52
|
+
:param row_key: The key for the current row.
|
|
53
|
+
|
|
54
|
+
:type table: :class:`Table <google.cloud.bigtable.table.Table>`
|
|
55
|
+
:param table: (Optional) The table that owns the row.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
def __init__(self, row_key, table=None):
|
|
59
|
+
self._row_key = _to_bytes(row_key)
|
|
60
|
+
self._table = table
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def row_key(self):
|
|
64
|
+
"""Row key.
|
|
65
|
+
|
|
66
|
+
For example:
|
|
67
|
+
|
|
68
|
+
.. literalinclude:: snippets_table.py
|
|
69
|
+
:start-after: [START bigtable_api_row_row_key]
|
|
70
|
+
:end-before: [END bigtable_api_row_row_key]
|
|
71
|
+
:dedent: 4
|
|
72
|
+
|
|
73
|
+
:rtype: bytes
|
|
74
|
+
:returns: The key for the current row.
|
|
75
|
+
"""
|
|
76
|
+
return self._row_key
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def table(self):
|
|
80
|
+
"""Row table.
|
|
81
|
+
|
|
82
|
+
For example:
|
|
83
|
+
|
|
84
|
+
.. literalinclude:: snippets_table.py
|
|
85
|
+
:start-after: [START bigtable_api_row_table]
|
|
86
|
+
:end-before: [END bigtable_api_row_table]
|
|
87
|
+
:dedent: 4
|
|
88
|
+
|
|
89
|
+
:rtype: table: :class:`Table <google.cloud.bigtable.table.Table>`
|
|
90
|
+
:returns: table: The table that owns the row.
|
|
91
|
+
"""
|
|
92
|
+
return self._table
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class _SetDeleteRow(Row):
|
|
96
|
+
"""Row helper for setting or deleting cell values.
|
|
97
|
+
|
|
98
|
+
Implements helper methods to add mutations to set or delete cell contents:
|
|
99
|
+
|
|
100
|
+
* :meth:`set_cell`
|
|
101
|
+
* :meth:`delete`
|
|
102
|
+
* :meth:`delete_cell`
|
|
103
|
+
* :meth:`delete_cells`
|
|
104
|
+
|
|
105
|
+
:type row_key: bytes
|
|
106
|
+
:param row_key: The key for the current row.
|
|
107
|
+
|
|
108
|
+
:type table: :class:`Table <google.cloud.bigtable.table.Table>`
|
|
109
|
+
:param table: The table that owns the row.
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
ALL_COLUMNS = object()
|
|
113
|
+
"""Sentinel value used to indicate all columns in a column family."""
|
|
114
|
+
|
|
115
|
+
def _get_mutations(self, state=None):
|
|
116
|
+
"""Gets the list of mutations for a given state.
|
|
117
|
+
|
|
118
|
+
This method intended to be implemented by subclasses.
|
|
119
|
+
|
|
120
|
+
``state`` may not need to be used by all subclasses.
|
|
121
|
+
|
|
122
|
+
:type state: bool
|
|
123
|
+
:param state: The state that the mutation should be
|
|
124
|
+
applied in.
|
|
125
|
+
|
|
126
|
+
:raises: :class:`NotImplementedError <exceptions.NotImplementedError>`
|
|
127
|
+
always.
|
|
128
|
+
"""
|
|
129
|
+
raise NotImplementedError
|
|
130
|
+
|
|
131
|
+
def _set_cell(self, column_family_id, column, value, timestamp=None, state=None):
|
|
132
|
+
"""Helper for :meth:`set_cell`
|
|
133
|
+
|
|
134
|
+
Adds a mutation to set the value in a specific cell.
|
|
135
|
+
|
|
136
|
+
``state`` is unused by :class:`DirectRow` but is used by
|
|
137
|
+
subclasses.
|
|
138
|
+
|
|
139
|
+
:type column_family_id: str
|
|
140
|
+
:param column_family_id: The column family that contains the column.
|
|
141
|
+
Must be of the form
|
|
142
|
+
``[_a-zA-Z0-9][-_.a-zA-Z0-9]*``.
|
|
143
|
+
|
|
144
|
+
:type column: bytes
|
|
145
|
+
:param column: The column within the column family where the cell
|
|
146
|
+
is located.
|
|
147
|
+
|
|
148
|
+
:type value: bytes or :class:`int`
|
|
149
|
+
:param value: The value to set in the cell. If an integer is used,
|
|
150
|
+
will be interpreted as a 64-bit big-endian signed
|
|
151
|
+
integer (8 bytes).
|
|
152
|
+
|
|
153
|
+
:type timestamp: :class:`datetime.datetime`
|
|
154
|
+
:param timestamp: (Optional) The timestamp of the operation.
|
|
155
|
+
|
|
156
|
+
:type state: bool
|
|
157
|
+
:param state: (Optional) The state that is passed along to
|
|
158
|
+
:meth:`_get_mutations`.
|
|
159
|
+
"""
|
|
160
|
+
column = _to_bytes(column)
|
|
161
|
+
if isinstance(value, int):
|
|
162
|
+
value = _PACK_I64(value)
|
|
163
|
+
value = _to_bytes(value)
|
|
164
|
+
if timestamp is None:
|
|
165
|
+
# Use -1 for current Bigtable server time.
|
|
166
|
+
timestamp_micros = -1
|
|
167
|
+
else:
|
|
168
|
+
timestamp_micros = _microseconds_from_datetime(timestamp)
|
|
169
|
+
# Truncate to millisecond granularity.
|
|
170
|
+
timestamp_micros -= timestamp_micros % 1000
|
|
171
|
+
|
|
172
|
+
mutation_val = data_v2_pb2.Mutation.SetCell(
|
|
173
|
+
family_name=column_family_id,
|
|
174
|
+
column_qualifier=column,
|
|
175
|
+
timestamp_micros=timestamp_micros,
|
|
176
|
+
value=value,
|
|
177
|
+
)
|
|
178
|
+
mutation_pb = data_v2_pb2.Mutation(set_cell=mutation_val)
|
|
179
|
+
self._get_mutations(state).append(mutation_pb)
|
|
180
|
+
|
|
181
|
+
def _delete(self, state=None):
|
|
182
|
+
"""Helper for :meth:`delete`
|
|
183
|
+
|
|
184
|
+
Adds a delete mutation (for the entire row) to the accumulated
|
|
185
|
+
mutations.
|
|
186
|
+
|
|
187
|
+
``state`` is unused by :class:`DirectRow` but is used by
|
|
188
|
+
subclasses.
|
|
189
|
+
|
|
190
|
+
:type state: bool
|
|
191
|
+
:param state: (Optional) The state that is passed along to
|
|
192
|
+
:meth:`_get_mutations`.
|
|
193
|
+
"""
|
|
194
|
+
mutation_val = data_v2_pb2.Mutation.DeleteFromRow()
|
|
195
|
+
mutation_pb = data_v2_pb2.Mutation(delete_from_row=mutation_val)
|
|
196
|
+
self._get_mutations(state).append(mutation_pb)
|
|
197
|
+
|
|
198
|
+
def _delete_cells(self, column_family_id, columns, time_range=None, state=None):
|
|
199
|
+
"""Helper for :meth:`delete_cell` and :meth:`delete_cells`.
|
|
200
|
+
|
|
201
|
+
``state`` is unused by :class:`DirectRow` but is used by
|
|
202
|
+
subclasses.
|
|
203
|
+
|
|
204
|
+
:type column_family_id: str
|
|
205
|
+
:param column_family_id: The column family that contains the column
|
|
206
|
+
or columns with cells being deleted. Must be
|
|
207
|
+
of the form ``[_a-zA-Z0-9][-_.a-zA-Z0-9]*``.
|
|
208
|
+
|
|
209
|
+
:type columns: :class:`list` of :class:`str` /
|
|
210
|
+
:func:`unicode <unicode>`, or :class:`object`
|
|
211
|
+
:param columns: The columns within the column family that will have
|
|
212
|
+
cells deleted. If :attr:`ALL_COLUMNS` is used then
|
|
213
|
+
the entire column family will be deleted from the row.
|
|
214
|
+
|
|
215
|
+
:type time_range: :class:`TimestampRange`
|
|
216
|
+
:param time_range: (Optional) The range of time within which cells
|
|
217
|
+
should be deleted.
|
|
218
|
+
|
|
219
|
+
:type state: bool
|
|
220
|
+
:param state: (Optional) The state that is passed along to
|
|
221
|
+
:meth:`_get_mutations`.
|
|
222
|
+
"""
|
|
223
|
+
mutations_list = self._get_mutations(state)
|
|
224
|
+
if columns is self.ALL_COLUMNS:
|
|
225
|
+
mutation_val = data_v2_pb2.Mutation.DeleteFromFamily(
|
|
226
|
+
family_name=column_family_id
|
|
227
|
+
)
|
|
228
|
+
mutation_pb = data_v2_pb2.Mutation(delete_from_family=mutation_val)
|
|
229
|
+
mutations_list.append(mutation_pb)
|
|
230
|
+
else:
|
|
231
|
+
delete_kwargs = {}
|
|
232
|
+
if time_range is not None:
|
|
233
|
+
delete_kwargs["time_range"] = time_range.to_pb()
|
|
234
|
+
|
|
235
|
+
to_append = []
|
|
236
|
+
for column in columns:
|
|
237
|
+
column = _to_bytes(column)
|
|
238
|
+
# time_range will never change if present, but the rest of
|
|
239
|
+
# delete_kwargs will
|
|
240
|
+
delete_kwargs.update(
|
|
241
|
+
family_name=column_family_id, column_qualifier=column
|
|
242
|
+
)
|
|
243
|
+
mutation_val = data_v2_pb2.Mutation.DeleteFromColumn(**delete_kwargs)
|
|
244
|
+
mutation_pb = data_v2_pb2.Mutation(delete_from_column=mutation_val)
|
|
245
|
+
to_append.append(mutation_pb)
|
|
246
|
+
|
|
247
|
+
# We don't add the mutations until all columns have been
|
|
248
|
+
# processed without error.
|
|
249
|
+
mutations_list.extend(to_append)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class DirectRow(_SetDeleteRow):
|
|
253
|
+
"""Google Cloud Bigtable Row for sending "direct" mutations.
|
|
254
|
+
|
|
255
|
+
These mutations directly set or delete cell contents:
|
|
256
|
+
|
|
257
|
+
* :meth:`set_cell`
|
|
258
|
+
* :meth:`delete`
|
|
259
|
+
* :meth:`delete_cell`
|
|
260
|
+
* :meth:`delete_cells`
|
|
261
|
+
|
|
262
|
+
These methods can be used directly::
|
|
263
|
+
|
|
264
|
+
>>> row = table.row(b'row-key1')
|
|
265
|
+
>>> row.set_cell(u'fam', b'col1', b'cell-val')
|
|
266
|
+
>>> row.delete_cell(u'fam', b'col2')
|
|
267
|
+
|
|
268
|
+
.. note::
|
|
269
|
+
|
|
270
|
+
A :class:`DirectRow` accumulates mutations locally via the
|
|
271
|
+
:meth:`set_cell`, :meth:`delete`, :meth:`delete_cell` and
|
|
272
|
+
:meth:`delete_cells` methods. To actually send these mutations to the
|
|
273
|
+
Google Cloud Bigtable API, you must call :meth:`commit`.
|
|
274
|
+
|
|
275
|
+
:type row_key: bytes
|
|
276
|
+
:param row_key: The key for the current row.
|
|
277
|
+
|
|
278
|
+
:type table: :class:`Table <google.cloud.bigtable.table.Table>`
|
|
279
|
+
:param table: (Optional) The table that owns the row. This is
|
|
280
|
+
used for the :meth: `commit` only. Alternatively,
|
|
281
|
+
DirectRows can be persisted via
|
|
282
|
+
:meth:`~google.cloud.bigtable.table.Table.mutate_rows`.
|
|
283
|
+
"""
|
|
284
|
+
|
|
285
|
+
def __init__(self, row_key, table=None):
|
|
286
|
+
super(DirectRow, self).__init__(row_key, table)
|
|
287
|
+
self._pb_mutations = []
|
|
288
|
+
|
|
289
|
+
def _get_mutations(self, state=None): # pylint: disable=unused-argument
|
|
290
|
+
"""Gets the list of mutations for a given state.
|
|
291
|
+
|
|
292
|
+
``state`` is unused by :class:`DirectRow` but is used by
|
|
293
|
+
subclasses.
|
|
294
|
+
|
|
295
|
+
:type state: bool
|
|
296
|
+
:param state: The state that the mutation should be
|
|
297
|
+
applied in.
|
|
298
|
+
|
|
299
|
+
:rtype: list
|
|
300
|
+
:returns: The list to add new mutations to (for the current state).
|
|
301
|
+
"""
|
|
302
|
+
return self._pb_mutations
|
|
303
|
+
|
|
304
|
+
def get_mutations_size(self):
|
|
305
|
+
"""Gets the total mutations size for current row
|
|
306
|
+
|
|
307
|
+
For example:
|
|
308
|
+
|
|
309
|
+
.. literalinclude:: snippets_table.py
|
|
310
|
+
:start-after: [START bigtable_api_row_get_mutations_size]
|
|
311
|
+
:end-before: [END bigtable_api_row_get_mutations_size]
|
|
312
|
+
:dedent: 4
|
|
313
|
+
"""
|
|
314
|
+
|
|
315
|
+
mutation_size = 0
|
|
316
|
+
for mutation in self._get_mutations():
|
|
317
|
+
mutation_size += mutation._pb.ByteSize()
|
|
318
|
+
|
|
319
|
+
return mutation_size
|
|
320
|
+
|
|
321
|
+
def set_cell(self, column_family_id, column, value, timestamp=None):
|
|
322
|
+
"""Sets a value in this row.
|
|
323
|
+
|
|
324
|
+
The cell is determined by the ``row_key`` of this :class:`DirectRow`
|
|
325
|
+
and the ``column``. The ``column`` must be in an existing
|
|
326
|
+
:class:`.ColumnFamily` (as determined by ``column_family_id``).
|
|
327
|
+
|
|
328
|
+
.. note::
|
|
329
|
+
|
|
330
|
+
This method adds a mutation to the accumulated mutations on this
|
|
331
|
+
row, but does not make an API request. To actually
|
|
332
|
+
send an API request (with the mutations) to the Google Cloud
|
|
333
|
+
Bigtable API, call :meth:`commit`.
|
|
334
|
+
|
|
335
|
+
For example:
|
|
336
|
+
|
|
337
|
+
.. literalinclude:: snippets_table.py
|
|
338
|
+
:start-after: [START bigtable_api_row_set_cell]
|
|
339
|
+
:end-before: [END bigtable_api_row_set_cell]
|
|
340
|
+
:dedent: 4
|
|
341
|
+
|
|
342
|
+
:type column_family_id: str
|
|
343
|
+
:param column_family_id: The column family that contains the column.
|
|
344
|
+
Must be of the form
|
|
345
|
+
``[_a-zA-Z0-9][-_.a-zA-Z0-9]*``.
|
|
346
|
+
|
|
347
|
+
:type column: bytes
|
|
348
|
+
:param column: The column within the column family where the cell
|
|
349
|
+
is located.
|
|
350
|
+
|
|
351
|
+
:type value: bytes or :class:`int`
|
|
352
|
+
:param value: The value to set in the cell. If an integer is used,
|
|
353
|
+
will be interpreted as a 64-bit big-endian signed
|
|
354
|
+
integer (8 bytes).
|
|
355
|
+
|
|
356
|
+
:type timestamp: :class:`datetime.datetime`
|
|
357
|
+
:param timestamp: (Optional) The timestamp of the operation.
|
|
358
|
+
"""
|
|
359
|
+
self._set_cell(column_family_id, column, value, timestamp=timestamp, state=None)
|
|
360
|
+
|
|
361
|
+
def delete(self):
|
|
362
|
+
"""Deletes this row from the table.
|
|
363
|
+
|
|
364
|
+
.. note::
|
|
365
|
+
|
|
366
|
+
This method adds a mutation to the accumulated mutations on this
|
|
367
|
+
row, but does not make an API request. To actually
|
|
368
|
+
send an API request (with the mutations) to the Google Cloud
|
|
369
|
+
Bigtable API, call :meth:`commit`.
|
|
370
|
+
|
|
371
|
+
For example:
|
|
372
|
+
|
|
373
|
+
.. literalinclude:: snippets_table.py
|
|
374
|
+
:start-after: [START bigtable_api_row_delete]
|
|
375
|
+
:end-before: [END bigtable_api_row_delete]
|
|
376
|
+
:dedent: 4
|
|
377
|
+
"""
|
|
378
|
+
self._delete(state=None)
|
|
379
|
+
|
|
380
|
+
def delete_cell(self, column_family_id, column, time_range=None):
|
|
381
|
+
"""Deletes cell in this row.
|
|
382
|
+
|
|
383
|
+
.. note::
|
|
384
|
+
|
|
385
|
+
This method adds a mutation to the accumulated mutations on this
|
|
386
|
+
row, but does not make an API request. To actually
|
|
387
|
+
send an API request (with the mutations) to the Google Cloud
|
|
388
|
+
Bigtable API, call :meth:`commit`.
|
|
389
|
+
|
|
390
|
+
For example:
|
|
391
|
+
|
|
392
|
+
.. literalinclude:: snippets_table.py
|
|
393
|
+
:start-after: [START bigtable_api_row_delete_cell]
|
|
394
|
+
:end-before: [END bigtable_api_row_delete_cell]
|
|
395
|
+
:dedent: 4
|
|
396
|
+
|
|
397
|
+
:type column_family_id: str
|
|
398
|
+
:param column_family_id: The column family that contains the column
|
|
399
|
+
or columns with cells being deleted. Must be
|
|
400
|
+
of the form ``[_a-zA-Z0-9][-_.a-zA-Z0-9]*``.
|
|
401
|
+
|
|
402
|
+
:type column: bytes
|
|
403
|
+
:param column: The column within the column family that will have a
|
|
404
|
+
cell deleted.
|
|
405
|
+
|
|
406
|
+
:type time_range: :class:`TimestampRange`
|
|
407
|
+
:param time_range: (Optional) The range of time within which cells
|
|
408
|
+
should be deleted.
|
|
409
|
+
"""
|
|
410
|
+
self._delete_cells(
|
|
411
|
+
column_family_id, [column], time_range=time_range, state=None
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
def delete_cells(self, column_family_id, columns, time_range=None):
|
|
415
|
+
"""Deletes cells in this row.
|
|
416
|
+
|
|
417
|
+
.. note::
|
|
418
|
+
|
|
419
|
+
This method adds a mutation to the accumulated mutations on this
|
|
420
|
+
row, but does not make an API request. To actually
|
|
421
|
+
send an API request (with the mutations) to the Google Cloud
|
|
422
|
+
Bigtable API, call :meth:`commit`.
|
|
423
|
+
|
|
424
|
+
For example:
|
|
425
|
+
|
|
426
|
+
.. literalinclude:: snippets_table.py
|
|
427
|
+
:start-after: [START bigtable_api_row_delete_cells]
|
|
428
|
+
:end-before: [END bigtable_api_row_delete_cells]
|
|
429
|
+
:dedent: 4
|
|
430
|
+
|
|
431
|
+
:type column_family_id: str
|
|
432
|
+
:param column_family_id: The column family that contains the column
|
|
433
|
+
or columns with cells being deleted. Must be
|
|
434
|
+
of the form ``[_a-zA-Z0-9][-_.a-zA-Z0-9]*``.
|
|
435
|
+
|
|
436
|
+
:type columns: :class:`list` of :class:`str` /
|
|
437
|
+
:func:`unicode <unicode>`, or :class:`object`
|
|
438
|
+
:param columns: The columns within the column family that will have
|
|
439
|
+
cells deleted. If :attr:`ALL_COLUMNS` is used then
|
|
440
|
+
the entire column family will be deleted from the row.
|
|
441
|
+
|
|
442
|
+
:type time_range: :class:`TimestampRange`
|
|
443
|
+
:param time_range: (Optional) The range of time within which cells
|
|
444
|
+
should be deleted.
|
|
445
|
+
"""
|
|
446
|
+
self._delete_cells(column_family_id, columns, time_range=time_range, state=None)
|
|
447
|
+
|
|
448
|
+
def commit(self):
|
|
449
|
+
"""Makes a ``MutateRow`` API request.
|
|
450
|
+
|
|
451
|
+
If no mutations have been created in the row, no request is made.
|
|
452
|
+
|
|
453
|
+
Mutations are applied atomically and in order, meaning that earlier
|
|
454
|
+
mutations can be masked / negated by later ones. Cells already present
|
|
455
|
+
in the row are left unchanged unless explicitly changed by a mutation.
|
|
456
|
+
|
|
457
|
+
After committing the accumulated mutations, resets the local
|
|
458
|
+
mutations to an empty list.
|
|
459
|
+
|
|
460
|
+
For example:
|
|
461
|
+
|
|
462
|
+
.. literalinclude:: snippets_table.py
|
|
463
|
+
:start-after: [START bigtable_api_row_commit]
|
|
464
|
+
:end-before: [END bigtable_api_row_commit]
|
|
465
|
+
:dedent: 4
|
|
466
|
+
|
|
467
|
+
:rtype: :class:`~google.rpc.status_pb2.Status`
|
|
468
|
+
:returns: A response status (`google.rpc.status_pb2.Status`)
|
|
469
|
+
representing success or failure of the row committed.
|
|
470
|
+
:raises: :exc:`~.table.TooManyMutationsError` if the number of
|
|
471
|
+
mutations is greater than 100,000.
|
|
472
|
+
"""
|
|
473
|
+
response = self._table.mutate_rows([self])
|
|
474
|
+
|
|
475
|
+
self.clear()
|
|
476
|
+
|
|
477
|
+
return response[0]
|
|
478
|
+
|
|
479
|
+
def clear(self):
|
|
480
|
+
"""Removes all currently accumulated mutations on the current row.
|
|
481
|
+
|
|
482
|
+
For example:
|
|
483
|
+
|
|
484
|
+
.. literalinclude:: snippets_table.py
|
|
485
|
+
:start-after: [START bigtable_api_row_clear]
|
|
486
|
+
:end-before: [END bigtable_api_row_clear]
|
|
487
|
+
:dedent: 4
|
|
488
|
+
"""
|
|
489
|
+
del self._pb_mutations[:]
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
class ConditionalRow(_SetDeleteRow):
|
|
493
|
+
"""Google Cloud Bigtable Row for sending mutations conditionally.
|
|
494
|
+
|
|
495
|
+
Each mutation has an associated state: :data:`True` or :data:`False`.
|
|
496
|
+
When :meth:`commit`-ed, the mutations for the :data:`True`
|
|
497
|
+
state will be applied if the filter matches any cells in
|
|
498
|
+
the row, otherwise the :data:`False` state will be applied.
|
|
499
|
+
|
|
500
|
+
A :class:`ConditionalRow` accumulates mutations in the same way a
|
|
501
|
+
:class:`DirectRow` does:
|
|
502
|
+
|
|
503
|
+
* :meth:`set_cell`
|
|
504
|
+
* :meth:`delete`
|
|
505
|
+
* :meth:`delete_cell`
|
|
506
|
+
* :meth:`delete_cells`
|
|
507
|
+
|
|
508
|
+
with the only change the extra ``state`` parameter::
|
|
509
|
+
|
|
510
|
+
>>> row_cond = table.row(b'row-key2', filter_=row_filter)
|
|
511
|
+
>>> row_cond.set_cell(u'fam', b'col', b'cell-val', state=True)
|
|
512
|
+
>>> row_cond.delete_cell(u'fam', b'col', state=False)
|
|
513
|
+
|
|
514
|
+
.. note::
|
|
515
|
+
|
|
516
|
+
As with :class:`DirectRow`, to actually send these mutations to the
|
|
517
|
+
Google Cloud Bigtable API, you must call :meth:`commit`.
|
|
518
|
+
|
|
519
|
+
:type row_key: bytes
|
|
520
|
+
:param row_key: The key for the current row.
|
|
521
|
+
|
|
522
|
+
:type table: :class:`Table <google.cloud.bigtable.table.Table>`
|
|
523
|
+
:param table: The table that owns the row.
|
|
524
|
+
|
|
525
|
+
:type filter_: :class:`.RowFilter`
|
|
526
|
+
:param filter_: Filter to be used for conditional mutations.
|
|
527
|
+
"""
|
|
528
|
+
|
|
529
|
+
def __init__(self, row_key, table, filter_):
|
|
530
|
+
super(ConditionalRow, self).__init__(row_key, table)
|
|
531
|
+
self._filter = filter_
|
|
532
|
+
self._true_pb_mutations = []
|
|
533
|
+
self._false_pb_mutations = []
|
|
534
|
+
|
|
535
|
+
def _get_mutations(self, state=None):
|
|
536
|
+
"""Gets the list of mutations for a given state.
|
|
537
|
+
|
|
538
|
+
Over-ridden so that the state can be used in:
|
|
539
|
+
|
|
540
|
+
* :meth:`set_cell`
|
|
541
|
+
* :meth:`delete`
|
|
542
|
+
* :meth:`delete_cell`
|
|
543
|
+
* :meth:`delete_cells`
|
|
544
|
+
|
|
545
|
+
:type state: bool
|
|
546
|
+
:param state: The state that the mutation should be
|
|
547
|
+
applied in.
|
|
548
|
+
|
|
549
|
+
:rtype: list
|
|
550
|
+
:returns: The list to add new mutations to (for the current state).
|
|
551
|
+
"""
|
|
552
|
+
if state:
|
|
553
|
+
return self._true_pb_mutations
|
|
554
|
+
else:
|
|
555
|
+
return self._false_pb_mutations
|
|
556
|
+
|
|
557
|
+
def commit(self):
|
|
558
|
+
"""Makes a ``CheckAndMutateRow`` API request.
|
|
559
|
+
|
|
560
|
+
If no mutations have been created in the row, no request is made.
|
|
561
|
+
|
|
562
|
+
The mutations will be applied conditionally, based on whether the
|
|
563
|
+
filter matches any cells in the :class:`ConditionalRow` or not. (Each
|
|
564
|
+
method which adds a mutation has a ``state`` parameter for this
|
|
565
|
+
purpose.)
|
|
566
|
+
|
|
567
|
+
Mutations are applied atomically and in order, meaning that earlier
|
|
568
|
+
mutations can be masked / negated by later ones. Cells already present
|
|
569
|
+
in the row are left unchanged unless explicitly changed by a mutation.
|
|
570
|
+
|
|
571
|
+
After committing the accumulated mutations, resets the local
|
|
572
|
+
mutations.
|
|
573
|
+
|
|
574
|
+
For example:
|
|
575
|
+
|
|
576
|
+
.. literalinclude:: snippets_table.py
|
|
577
|
+
:start-after: [START bigtable_api_row_commit]
|
|
578
|
+
:end-before: [END bigtable_api_row_commit]
|
|
579
|
+
:dedent: 4
|
|
580
|
+
|
|
581
|
+
:rtype: bool
|
|
582
|
+
:returns: Flag indicating if the filter was matched (which also
|
|
583
|
+
indicates which set of mutations were applied by the server).
|
|
584
|
+
:raises: :class:`ValueError <exceptions.ValueError>` if the number of
|
|
585
|
+
mutations exceeds the :data:`MAX_MUTATIONS`.
|
|
586
|
+
"""
|
|
587
|
+
true_mutations = self._get_mutations(state=True)
|
|
588
|
+
false_mutations = self._get_mutations(state=False)
|
|
589
|
+
num_true_mutations = len(true_mutations)
|
|
590
|
+
num_false_mutations = len(false_mutations)
|
|
591
|
+
if num_true_mutations == 0 and num_false_mutations == 0:
|
|
592
|
+
return
|
|
593
|
+
if num_true_mutations > MAX_MUTATIONS or num_false_mutations > MAX_MUTATIONS:
|
|
594
|
+
raise ValueError(
|
|
595
|
+
"Exceed the maximum allowable mutations (%d). Had %s true "
|
|
596
|
+
"mutations and %d false mutations."
|
|
597
|
+
% (MAX_MUTATIONS, num_true_mutations, num_false_mutations)
|
|
598
|
+
)
|
|
599
|
+
|
|
600
|
+
data_client = self._table._instance._client.table_data_client
|
|
601
|
+
resp = data_client.check_and_mutate_row(
|
|
602
|
+
table_name=self._table.name,
|
|
603
|
+
row_key=self._row_key,
|
|
604
|
+
predicate_filter=self._filter.to_pb(),
|
|
605
|
+
app_profile_id=self._table._app_profile_id,
|
|
606
|
+
true_mutations=true_mutations,
|
|
607
|
+
false_mutations=false_mutations,
|
|
608
|
+
)
|
|
609
|
+
self.clear()
|
|
610
|
+
return resp.predicate_matched
|
|
611
|
+
|
|
612
|
+
# pylint: disable=arguments-differ
|
|
613
|
+
def set_cell(self, column_family_id, column, value, timestamp=None, state=True):
|
|
614
|
+
"""Sets a value in this row.
|
|
615
|
+
|
|
616
|
+
The cell is determined by the ``row_key`` of this
|
|
617
|
+
:class:`ConditionalRow` and the ``column``. The ``column`` must be in
|
|
618
|
+
an existing :class:`.ColumnFamily` (as determined by
|
|
619
|
+
``column_family_id``).
|
|
620
|
+
|
|
621
|
+
.. note::
|
|
622
|
+
|
|
623
|
+
This method adds a mutation to the accumulated mutations on this
|
|
624
|
+
row, but does not make an API request. To actually
|
|
625
|
+
send an API request (with the mutations) to the Google Cloud
|
|
626
|
+
Bigtable API, call :meth:`commit`.
|
|
627
|
+
|
|
628
|
+
For example:
|
|
629
|
+
|
|
630
|
+
.. literalinclude:: snippets_table.py
|
|
631
|
+
:start-after: [START bigtable_api_row_set_cell]
|
|
632
|
+
:end-before: [END bigtable_api_row_set_cell]
|
|
633
|
+
:dedent: 4
|
|
634
|
+
|
|
635
|
+
:type column_family_id: str
|
|
636
|
+
:param column_family_id: The column family that contains the column.
|
|
637
|
+
Must be of the form
|
|
638
|
+
``[_a-zA-Z0-9][-_.a-zA-Z0-9]*``.
|
|
639
|
+
|
|
640
|
+
:type column: bytes
|
|
641
|
+
:param column: The column within the column family where the cell
|
|
642
|
+
is located.
|
|
643
|
+
|
|
644
|
+
:type value: bytes or :class:`int`
|
|
645
|
+
:param value: The value to set in the cell. If an integer is used,
|
|
646
|
+
will be interpreted as a 64-bit big-endian signed
|
|
647
|
+
integer (8 bytes).
|
|
648
|
+
|
|
649
|
+
:type timestamp: :class:`datetime.datetime`
|
|
650
|
+
:param timestamp: (Optional) The timestamp of the operation.
|
|
651
|
+
|
|
652
|
+
:type state: bool
|
|
653
|
+
:param state: (Optional) The state that the mutation should be
|
|
654
|
+
applied in. Defaults to :data:`True`.
|
|
655
|
+
"""
|
|
656
|
+
self._set_cell(
|
|
657
|
+
column_family_id, column, value, timestamp=timestamp, state=state
|
|
658
|
+
)
|
|
659
|
+
|
|
660
|
+
def delete(self, state=True):
|
|
661
|
+
"""Deletes this row from the table.
|
|
662
|
+
|
|
663
|
+
.. note::
|
|
664
|
+
|
|
665
|
+
This method adds a mutation to the accumulated mutations on this
|
|
666
|
+
row, but does not make an API request. To actually
|
|
667
|
+
send an API request (with the mutations) to the Google Cloud
|
|
668
|
+
Bigtable API, call :meth:`commit`.
|
|
669
|
+
|
|
670
|
+
For example:
|
|
671
|
+
|
|
672
|
+
.. literalinclude:: snippets_table.py
|
|
673
|
+
:start-after: [START bigtable_api_row_delete]
|
|
674
|
+
:end-before: [END bigtable_api_row_delete]
|
|
675
|
+
:dedent: 4
|
|
676
|
+
|
|
677
|
+
:type state: bool
|
|
678
|
+
:param state: (Optional) The state that the mutation should be
|
|
679
|
+
applied in. Defaults to :data:`True`.
|
|
680
|
+
"""
|
|
681
|
+
self._delete(state=state)
|
|
682
|
+
|
|
683
|
+
def delete_cell(self, column_family_id, column, time_range=None, state=True):
|
|
684
|
+
"""Deletes cell in this row.
|
|
685
|
+
|
|
686
|
+
.. note::
|
|
687
|
+
|
|
688
|
+
This method adds a mutation to the accumulated mutations on this
|
|
689
|
+
row, but does not make an API request. To actually
|
|
690
|
+
send an API request (with the mutations) to the Google Cloud
|
|
691
|
+
Bigtable API, call :meth:`commit`.
|
|
692
|
+
|
|
693
|
+
For example:
|
|
694
|
+
|
|
695
|
+
.. literalinclude:: snippets_table.py
|
|
696
|
+
:start-after: [START bigtable_api_row_delete_cell]
|
|
697
|
+
:end-before: [END bigtable_api_row_delete_cell]
|
|
698
|
+
:dedent: 4
|
|
699
|
+
|
|
700
|
+
:type column_family_id: str
|
|
701
|
+
:param column_family_id: The column family that contains the column
|
|
702
|
+
or columns with cells being deleted. Must be
|
|
703
|
+
of the form ``[_a-zA-Z0-9][-_.a-zA-Z0-9]*``.
|
|
704
|
+
|
|
705
|
+
:type column: bytes
|
|
706
|
+
:param column: The column within the column family that will have a
|
|
707
|
+
cell deleted.
|
|
708
|
+
|
|
709
|
+
:type time_range: :class:`TimestampRange`
|
|
710
|
+
:param time_range: (Optional) The range of time within which cells
|
|
711
|
+
should be deleted.
|
|
712
|
+
|
|
713
|
+
:type state: bool
|
|
714
|
+
:param state: (Optional) The state that the mutation should be
|
|
715
|
+
applied in. Defaults to :data:`True`.
|
|
716
|
+
"""
|
|
717
|
+
self._delete_cells(
|
|
718
|
+
column_family_id, [column], time_range=time_range, state=state
|
|
719
|
+
)
|
|
720
|
+
|
|
721
|
+
def delete_cells(self, column_family_id, columns, time_range=None, state=True):
|
|
722
|
+
"""Deletes cells in this row.
|
|
723
|
+
|
|
724
|
+
.. note::
|
|
725
|
+
|
|
726
|
+
This method adds a mutation to the accumulated mutations on this
|
|
727
|
+
row, but does not make an API request. To actually
|
|
728
|
+
send an API request (with the mutations) to the Google Cloud
|
|
729
|
+
Bigtable API, call :meth:`commit`.
|
|
730
|
+
|
|
731
|
+
For example:
|
|
732
|
+
|
|
733
|
+
.. literalinclude:: snippets_table.py
|
|
734
|
+
:start-after: [START bigtable_api_row_delete_cells]
|
|
735
|
+
:end-before: [END bigtable_api_row_delete_cells]
|
|
736
|
+
:dedent: 4
|
|
737
|
+
|
|
738
|
+
:type column_family_id: str
|
|
739
|
+
:param column_family_id: The column family that contains the column
|
|
740
|
+
or columns with cells being deleted. Must be
|
|
741
|
+
of the form ``[_a-zA-Z0-9][-_.a-zA-Z0-9]*``.
|
|
742
|
+
|
|
743
|
+
:type columns: :class:`list` of :class:`str` /
|
|
744
|
+
:func:`unicode <unicode>`, or :class:`object`
|
|
745
|
+
:param columns: The columns within the column family that will have
|
|
746
|
+
cells deleted. If :attr:`ALL_COLUMNS` is used then the
|
|
747
|
+
entire column family will be deleted from the row.
|
|
748
|
+
|
|
749
|
+
:type time_range: :class:`TimestampRange`
|
|
750
|
+
:param time_range: (Optional) The range of time within which cells
|
|
751
|
+
should be deleted.
|
|
752
|
+
|
|
753
|
+
:type state: bool
|
|
754
|
+
:param state: (Optional) The state that the mutation should be
|
|
755
|
+
applied in. Defaults to :data:`True`.
|
|
756
|
+
"""
|
|
757
|
+
self._delete_cells(
|
|
758
|
+
column_family_id, columns, time_range=time_range, state=state
|
|
759
|
+
)
|
|
760
|
+
|
|
761
|
+
# pylint: enable=arguments-differ
|
|
762
|
+
|
|
763
|
+
def clear(self):
|
|
764
|
+
"""Removes all currently accumulated mutations on the current row.
|
|
765
|
+
|
|
766
|
+
For example:
|
|
767
|
+
|
|
768
|
+
.. literalinclude:: snippets_table.py
|
|
769
|
+
:start-after: [START bigtable_api_row_clear]
|
|
770
|
+
:end-before: [END bigtable_api_row_clear]
|
|
771
|
+
:dedent: 4
|
|
772
|
+
"""
|
|
773
|
+
del self._true_pb_mutations[:]
|
|
774
|
+
del self._false_pb_mutations[:]
|
|
775
|
+
|
|
776
|
+
|
|
777
|
+
class AppendRow(Row):
|
|
778
|
+
"""Google Cloud Bigtable Row for sending append mutations.
|
|
779
|
+
|
|
780
|
+
These mutations are intended to augment the value of an existing cell
|
|
781
|
+
and uses the methods:
|
|
782
|
+
|
|
783
|
+
* :meth:`append_cell_value`
|
|
784
|
+
* :meth:`increment_cell_value`
|
|
785
|
+
|
|
786
|
+
The first works by appending bytes and the second by incrementing an
|
|
787
|
+
integer (stored in the cell as 8 bytes). In either case, if the
|
|
788
|
+
cell is empty, assumes the default empty value (empty string for
|
|
789
|
+
bytes or 0 for integer).
|
|
790
|
+
|
|
791
|
+
:type row_key: bytes
|
|
792
|
+
:param row_key: The key for the current row.
|
|
793
|
+
|
|
794
|
+
:type table: :class:`Table <google.cloud.bigtable.table.Table>`
|
|
795
|
+
:param table: The table that owns the row.
|
|
796
|
+
"""
|
|
797
|
+
|
|
798
|
+
def __init__(self, row_key, table):
|
|
799
|
+
super(AppendRow, self).__init__(row_key, table)
|
|
800
|
+
self._rule_pb_list = []
|
|
801
|
+
|
|
802
|
+
def clear(self):
|
|
803
|
+
"""Removes all currently accumulated modifications on current row.
|
|
804
|
+
|
|
805
|
+
For example:
|
|
806
|
+
|
|
807
|
+
.. literalinclude:: snippets_table.py
|
|
808
|
+
:start-after: [START bigtable_api_row_clear]
|
|
809
|
+
:end-before: [END bigtable_api_row_clear]
|
|
810
|
+
:dedent: 4
|
|
811
|
+
"""
|
|
812
|
+
del self._rule_pb_list[:]
|
|
813
|
+
|
|
814
|
+
def append_cell_value(self, column_family_id, column, value):
|
|
815
|
+
"""Appends a value to an existing cell.
|
|
816
|
+
|
|
817
|
+
.. note::
|
|
818
|
+
|
|
819
|
+
This method adds a read-modify rule protobuf to the accumulated
|
|
820
|
+
read-modify rules on this row, but does not make an API
|
|
821
|
+
request. To actually send an API request (with the rules) to the
|
|
822
|
+
Google Cloud Bigtable API, call :meth:`commit`.
|
|
823
|
+
|
|
824
|
+
For example:
|
|
825
|
+
|
|
826
|
+
.. literalinclude:: snippets_table.py
|
|
827
|
+
:start-after: [START bigtable_api_row_append_cell_value]
|
|
828
|
+
:end-before: [END bigtable_api_row_append_cell_value]
|
|
829
|
+
:dedent: 4
|
|
830
|
+
|
|
831
|
+
:type column_family_id: str
|
|
832
|
+
:param column_family_id: The column family that contains the column.
|
|
833
|
+
Must be of the form
|
|
834
|
+
``[_a-zA-Z0-9][-_.a-zA-Z0-9]*``.
|
|
835
|
+
|
|
836
|
+
:type column: bytes
|
|
837
|
+
:param column: The column within the column family where the cell
|
|
838
|
+
is located.
|
|
839
|
+
|
|
840
|
+
:type value: bytes
|
|
841
|
+
:param value: The value to append to the existing value in the cell. If
|
|
842
|
+
the targeted cell is unset, it will be treated as
|
|
843
|
+
containing the empty string.
|
|
844
|
+
"""
|
|
845
|
+
column = _to_bytes(column)
|
|
846
|
+
value = _to_bytes(value)
|
|
847
|
+
rule_pb = data_v2_pb2.ReadModifyWriteRule(
|
|
848
|
+
family_name=column_family_id, column_qualifier=column, append_value=value
|
|
849
|
+
)
|
|
850
|
+
self._rule_pb_list.append(rule_pb)
|
|
851
|
+
|
|
852
|
+
def increment_cell_value(self, column_family_id, column, int_value):
|
|
853
|
+
"""Increments a value in an existing cell.
|
|
854
|
+
|
|
855
|
+
Assumes the value in the cell is stored as a 64 bit integer
|
|
856
|
+
serialized to bytes.
|
|
857
|
+
|
|
858
|
+
.. note::
|
|
859
|
+
|
|
860
|
+
This method adds a read-modify rule protobuf to the accumulated
|
|
861
|
+
read-modify rules on this row, but does not make an API
|
|
862
|
+
request. To actually send an API request (with the rules) to the
|
|
863
|
+
Google Cloud Bigtable API, call :meth:`commit`.
|
|
864
|
+
|
|
865
|
+
For example:
|
|
866
|
+
|
|
867
|
+
.. literalinclude:: snippets_table.py
|
|
868
|
+
:start-after: [START bigtable_api_row_increment_cell_value]
|
|
869
|
+
:end-before: [END bigtable_api_row_increment_cell_value]
|
|
870
|
+
:dedent: 4
|
|
871
|
+
|
|
872
|
+
:type column_family_id: str
|
|
873
|
+
:param column_family_id: The column family that contains the column.
|
|
874
|
+
Must be of the form
|
|
875
|
+
``[_a-zA-Z0-9][-_.a-zA-Z0-9]*``.
|
|
876
|
+
|
|
877
|
+
:type column: bytes
|
|
878
|
+
:param column: The column within the column family where the cell
|
|
879
|
+
is located.
|
|
880
|
+
|
|
881
|
+
:type int_value: int
|
|
882
|
+
:param int_value: The value to increment the existing value in the cell
|
|
883
|
+
by. If the targeted cell is unset, it will be treated
|
|
884
|
+
as containing a zero. Otherwise, the targeted cell
|
|
885
|
+
must contain an 8-byte value (interpreted as a 64-bit
|
|
886
|
+
big-endian signed integer), or the entire request
|
|
887
|
+
will fail.
|
|
888
|
+
"""
|
|
889
|
+
column = _to_bytes(column)
|
|
890
|
+
rule_pb = data_v2_pb2.ReadModifyWriteRule(
|
|
891
|
+
family_name=column_family_id,
|
|
892
|
+
column_qualifier=column,
|
|
893
|
+
increment_amount=int_value,
|
|
894
|
+
)
|
|
895
|
+
self._rule_pb_list.append(rule_pb)
|
|
896
|
+
|
|
897
|
+
def commit(self):
|
|
898
|
+
"""Makes a ``ReadModifyWriteRow`` API request.
|
|
899
|
+
|
|
900
|
+
This commits modifications made by :meth:`append_cell_value` and
|
|
901
|
+
:meth:`increment_cell_value`. If no modifications were made, makes
|
|
902
|
+
no API request and just returns ``{}``.
|
|
903
|
+
|
|
904
|
+
Modifies a row atomically, reading the latest existing
|
|
905
|
+
timestamp / value from the specified columns and writing a new value by
|
|
906
|
+
appending / incrementing. The new cell created uses either the current
|
|
907
|
+
server time or the highest timestamp of a cell in that column (if it
|
|
908
|
+
exceeds the server time).
|
|
909
|
+
|
|
910
|
+
After committing the accumulated mutations, resets the local mutations.
|
|
911
|
+
|
|
912
|
+
For example:
|
|
913
|
+
|
|
914
|
+
.. literalinclude:: snippets_table.py
|
|
915
|
+
:start-after: [START bigtable_api_row_commit]
|
|
916
|
+
:end-before: [END bigtable_api_row_commit]
|
|
917
|
+
:dedent: 4
|
|
918
|
+
|
|
919
|
+
:rtype: dict
|
|
920
|
+
:returns: The new contents of all modified cells. Returned as a
|
|
921
|
+
dictionary of column families, each of which holds a
|
|
922
|
+
dictionary of columns. Each column contains a list of cells
|
|
923
|
+
modified. Each cell is represented with a two-tuple with the
|
|
924
|
+
value (in bytes) and the timestamp for the cell.
|
|
925
|
+
:raises: :class:`ValueError <exceptions.ValueError>` if the number of
|
|
926
|
+
mutations exceeds the :data:`MAX_MUTATIONS`.
|
|
927
|
+
"""
|
|
928
|
+
num_mutations = len(self._rule_pb_list)
|
|
929
|
+
if num_mutations == 0:
|
|
930
|
+
return {}
|
|
931
|
+
if num_mutations > MAX_MUTATIONS:
|
|
932
|
+
raise ValueError(
|
|
933
|
+
"%d total append mutations exceed the maximum "
|
|
934
|
+
"allowable %d." % (num_mutations, MAX_MUTATIONS)
|
|
935
|
+
)
|
|
936
|
+
|
|
937
|
+
data_client = self._table._instance._client.table_data_client
|
|
938
|
+
row_response = data_client.read_modify_write_row(
|
|
939
|
+
table_name=self._table.name,
|
|
940
|
+
row_key=self._row_key,
|
|
941
|
+
rules=self._rule_pb_list,
|
|
942
|
+
app_profile_id=self._table._app_profile_id,
|
|
943
|
+
)
|
|
944
|
+
|
|
945
|
+
# Reset modifications after commit-ing request.
|
|
946
|
+
self.clear()
|
|
947
|
+
|
|
948
|
+
# NOTE: We expect row_response.key == self._row_key but don't check.
|
|
949
|
+
return _parse_rmw_row_response(row_response)
|
|
950
|
+
|
|
951
|
+
|
|
952
|
+
def _parse_rmw_row_response(row_response):
|
|
953
|
+
"""Parses the response to a ``ReadModifyWriteRow`` request.
|
|
954
|
+
|
|
955
|
+
:type row_response: :class:`.data_v2_pb2.Row`
|
|
956
|
+
:param row_response: The response row (with only modified cells) from a
|
|
957
|
+
``ReadModifyWriteRow`` request.
|
|
958
|
+
|
|
959
|
+
:rtype: dict
|
|
960
|
+
:returns: The new contents of all modified cells. Returned as a
|
|
961
|
+
dictionary of column families, each of which holds a
|
|
962
|
+
dictionary of columns. Each column contains a list of cells
|
|
963
|
+
modified. Each cell is represented with a two-tuple with the
|
|
964
|
+
value (in bytes) and the timestamp for the cell. For example:
|
|
965
|
+
|
|
966
|
+
.. code:: python
|
|
967
|
+
|
|
968
|
+
{
|
|
969
|
+
u'col-fam-id': {
|
|
970
|
+
b'col-name1': [
|
|
971
|
+
(b'cell-val', datetime.datetime(...)),
|
|
972
|
+
(b'cell-val-newer', datetime.datetime(...)),
|
|
973
|
+
],
|
|
974
|
+
b'col-name2': [
|
|
975
|
+
(b'altcol-cell-val', datetime.datetime(...)),
|
|
976
|
+
],
|
|
977
|
+
},
|
|
978
|
+
u'col-fam-id2': {
|
|
979
|
+
b'col-name3-but-other-fam': [
|
|
980
|
+
(b'foo', datetime.datetime(...)),
|
|
981
|
+
],
|
|
982
|
+
},
|
|
983
|
+
}
|
|
984
|
+
"""
|
|
985
|
+
result = {}
|
|
986
|
+
for column_family in row_response.row.families:
|
|
987
|
+
column_family_id, curr_family = _parse_family_pb(column_family)
|
|
988
|
+
result[column_family_id] = curr_family
|
|
989
|
+
return result
|
|
990
|
+
|
|
991
|
+
|
|
992
|
+
def _parse_family_pb(family_pb):
|
|
993
|
+
"""Parses a Family protobuf into a dictionary.
|
|
994
|
+
|
|
995
|
+
:type family_pb: :class:`._generated.data_pb2.Family`
|
|
996
|
+
:param family_pb: A protobuf
|
|
997
|
+
|
|
998
|
+
:rtype: tuple
|
|
999
|
+
:returns: A string and dictionary. The string is the name of the
|
|
1000
|
+
column family and the dictionary has column names (within the
|
|
1001
|
+
family) as keys and cell lists as values. Each cell is
|
|
1002
|
+
represented with a two-tuple with the value (in bytes) and the
|
|
1003
|
+
timestamp for the cell. For example:
|
|
1004
|
+
|
|
1005
|
+
.. code:: python
|
|
1006
|
+
|
|
1007
|
+
{
|
|
1008
|
+
b'col-name1': [
|
|
1009
|
+
(b'cell-val', datetime.datetime(...)),
|
|
1010
|
+
(b'cell-val-newer', datetime.datetime(...)),
|
|
1011
|
+
],
|
|
1012
|
+
b'col-name2': [
|
|
1013
|
+
(b'altcol-cell-val', datetime.datetime(...)),
|
|
1014
|
+
],
|
|
1015
|
+
}
|
|
1016
|
+
"""
|
|
1017
|
+
result = {}
|
|
1018
|
+
for column in family_pb.columns:
|
|
1019
|
+
result[column.qualifier] = cells = []
|
|
1020
|
+
for cell in column.cells:
|
|
1021
|
+
val_pair = (cell.value, _datetime_from_microseconds(cell.timestamp_micros))
|
|
1022
|
+
cells.append(val_pair)
|
|
1023
|
+
|
|
1024
|
+
return family_pb.name, result
|
|
1025
|
+
|
|
1026
|
+
|
|
1027
|
+
class PartialRowData(object):
|
|
1028
|
+
"""Representation of partial row in a Google Cloud Bigtable Table.
|
|
1029
|
+
|
|
1030
|
+
These are expected to be updated directly from a
|
|
1031
|
+
:class:`._generated.bigtable_service_messages_pb2.ReadRowsResponse`
|
|
1032
|
+
|
|
1033
|
+
:type row_key: bytes
|
|
1034
|
+
:param row_key: The key for the row holding the (partial) data.
|
|
1035
|
+
"""
|
|
1036
|
+
|
|
1037
|
+
def __init__(self, row_key):
|
|
1038
|
+
self._row_key = row_key
|
|
1039
|
+
self._cells = {}
|
|
1040
|
+
|
|
1041
|
+
def __eq__(self, other):
|
|
1042
|
+
if not isinstance(other, self.__class__):
|
|
1043
|
+
return NotImplemented
|
|
1044
|
+
return other._row_key == self._row_key and other._cells == self._cells
|
|
1045
|
+
|
|
1046
|
+
def __ne__(self, other):
|
|
1047
|
+
return not self == other
|
|
1048
|
+
|
|
1049
|
+
def to_dict(self):
|
|
1050
|
+
"""Convert the cells to a dictionary.
|
|
1051
|
+
|
|
1052
|
+
This is intended to be used with HappyBase, so the column family and
|
|
1053
|
+
column qualiers are combined (with ``:``).
|
|
1054
|
+
|
|
1055
|
+
:rtype: dict
|
|
1056
|
+
:returns: Dictionary containing all the data in the cells of this row.
|
|
1057
|
+
"""
|
|
1058
|
+
result = {}
|
|
1059
|
+
for column_family_id, columns in self._cells.items():
|
|
1060
|
+
for column_qual, cells in columns.items():
|
|
1061
|
+
key = _to_bytes(column_family_id) + b":" + _to_bytes(column_qual)
|
|
1062
|
+
result[key] = cells
|
|
1063
|
+
return result
|
|
1064
|
+
|
|
1065
|
+
@property
|
|
1066
|
+
def cells(self):
|
|
1067
|
+
"""Property returning all the cells accumulated on this partial row.
|
|
1068
|
+
|
|
1069
|
+
For example:
|
|
1070
|
+
|
|
1071
|
+
.. literalinclude:: snippets_table.py
|
|
1072
|
+
:start-after: [START bigtable_api_row_data_cells]
|
|
1073
|
+
:end-before: [END bigtable_api_row_data_cells]
|
|
1074
|
+
:dedent: 4
|
|
1075
|
+
|
|
1076
|
+
:rtype: dict
|
|
1077
|
+
:returns: Dictionary of the :class:`Cell` objects accumulated. This
|
|
1078
|
+
dictionary has two-levels of keys (first for column families
|
|
1079
|
+
and second for column names/qualifiers within a family). For
|
|
1080
|
+
a given column, a list of :class:`Cell` objects is stored.
|
|
1081
|
+
"""
|
|
1082
|
+
return self._cells
|
|
1083
|
+
|
|
1084
|
+
@property
|
|
1085
|
+
def row_key(self):
|
|
1086
|
+
"""Getter for the current (partial) row's key.
|
|
1087
|
+
|
|
1088
|
+
:rtype: bytes
|
|
1089
|
+
:returns: The current (partial) row's key.
|
|
1090
|
+
"""
|
|
1091
|
+
return self._row_key
|
|
1092
|
+
|
|
1093
|
+
def find_cells(self, column_family_id, column):
|
|
1094
|
+
"""Get a time series of cells stored on this instance.
|
|
1095
|
+
|
|
1096
|
+
For example:
|
|
1097
|
+
|
|
1098
|
+
.. literalinclude:: snippets_table.py
|
|
1099
|
+
:start-after: [START bigtable_api_row_find_cells]
|
|
1100
|
+
:end-before: [END bigtable_api_row_find_cells]
|
|
1101
|
+
:dedent: 4
|
|
1102
|
+
|
|
1103
|
+
Args:
|
|
1104
|
+
column_family_id (str): The ID of the column family. Must be of the
|
|
1105
|
+
form ``[_a-zA-Z0-9][-_.a-zA-Z0-9]*``.
|
|
1106
|
+
column (bytes): The column within the column family where the cells
|
|
1107
|
+
are located.
|
|
1108
|
+
|
|
1109
|
+
Returns:
|
|
1110
|
+
List[~google.cloud.bigtable.row_data.Cell]: The cells stored in the
|
|
1111
|
+
specified column.
|
|
1112
|
+
|
|
1113
|
+
Raises:
|
|
1114
|
+
KeyError: If ``column_family_id`` is not among the cells stored
|
|
1115
|
+
in this row.
|
|
1116
|
+
KeyError: If ``column`` is not among the cells stored in this row
|
|
1117
|
+
for the given ``column_family_id``.
|
|
1118
|
+
"""
|
|
1119
|
+
try:
|
|
1120
|
+
column_family = self._cells[column_family_id]
|
|
1121
|
+
except KeyError:
|
|
1122
|
+
raise KeyError(_MISSING_COLUMN_FAMILY.format(column_family_id))
|
|
1123
|
+
|
|
1124
|
+
try:
|
|
1125
|
+
cells = column_family[column]
|
|
1126
|
+
except KeyError:
|
|
1127
|
+
raise KeyError(_MISSING_COLUMN.format(column, column_family_id))
|
|
1128
|
+
|
|
1129
|
+
return cells
|
|
1130
|
+
|
|
1131
|
+
def cell_value(self, column_family_id, column, index=0):
|
|
1132
|
+
"""Get a single cell value stored on this instance.
|
|
1133
|
+
|
|
1134
|
+
For example:
|
|
1135
|
+
|
|
1136
|
+
.. literalinclude:: snippets_table.py
|
|
1137
|
+
:start-after: [START bigtable_api_row_cell_value]
|
|
1138
|
+
:end-before: [END bigtable_api_row_cell_value]
|
|
1139
|
+
:dedent: 4
|
|
1140
|
+
|
|
1141
|
+
Args:
|
|
1142
|
+
column_family_id (str): The ID of the column family. Must be of the
|
|
1143
|
+
form ``[_a-zA-Z0-9][-_.a-zA-Z0-9]*``.
|
|
1144
|
+
column (bytes): The column within the column family where the cell
|
|
1145
|
+
is located.
|
|
1146
|
+
index (Optional[int]): The offset within the series of values. If
|
|
1147
|
+
not specified, will return the first cell.
|
|
1148
|
+
|
|
1149
|
+
Returns:
|
|
1150
|
+
~google.cloud.bigtable.row_data.Cell value: The cell value stored
|
|
1151
|
+
in the specified column and specified index.
|
|
1152
|
+
|
|
1153
|
+
Raises:
|
|
1154
|
+
KeyError: If ``column_family_id`` is not among the cells stored
|
|
1155
|
+
in this row.
|
|
1156
|
+
KeyError: If ``column`` is not among the cells stored in this row
|
|
1157
|
+
for the given ``column_family_id``.
|
|
1158
|
+
IndexError: If ``index`` cannot be found within the cells stored
|
|
1159
|
+
in this row for the given ``column_family_id``, ``column``
|
|
1160
|
+
pair.
|
|
1161
|
+
"""
|
|
1162
|
+
cells = self.find_cells(column_family_id, column)
|
|
1163
|
+
|
|
1164
|
+
try:
|
|
1165
|
+
cell = cells[index]
|
|
1166
|
+
except (TypeError, IndexError):
|
|
1167
|
+
num_cells = len(cells)
|
|
1168
|
+
msg = _MISSING_INDEX.format(index, column, column_family_id, num_cells)
|
|
1169
|
+
raise IndexError(msg)
|
|
1170
|
+
|
|
1171
|
+
return cell.value
|
|
1172
|
+
|
|
1173
|
+
def cell_values(self, column_family_id, column, max_count=None):
|
|
1174
|
+
"""Get a time series of cells stored on this instance.
|
|
1175
|
+
|
|
1176
|
+
For example:
|
|
1177
|
+
|
|
1178
|
+
.. literalinclude:: snippets_table.py
|
|
1179
|
+
:start-after: [START bigtable_api_row_cell_values]
|
|
1180
|
+
:end-before: [END bigtable_api_row_cell_values]
|
|
1181
|
+
:dedent: 4
|
|
1182
|
+
|
|
1183
|
+
Args:
|
|
1184
|
+
column_family_id (str): The ID of the column family. Must be of the
|
|
1185
|
+
form ``[_a-zA-Z0-9][-_.a-zA-Z0-9]*``.
|
|
1186
|
+
column (bytes): The column within the column family where the cells
|
|
1187
|
+
are located.
|
|
1188
|
+
max_count (int): The maximum number of cells to use.
|
|
1189
|
+
|
|
1190
|
+
Returns:
|
|
1191
|
+
A generator which provides: cell.value, cell.timestamp_micros
|
|
1192
|
+
for each cell in the list of cells
|
|
1193
|
+
|
|
1194
|
+
Raises:
|
|
1195
|
+
KeyError: If ``column_family_id`` is not among the cells stored
|
|
1196
|
+
in this row.
|
|
1197
|
+
KeyError: If ``column`` is not among the cells stored in this row
|
|
1198
|
+
for the given ``column_family_id``.
|
|
1199
|
+
"""
|
|
1200
|
+
cells = self.find_cells(column_family_id, column)
|
|
1201
|
+
if max_count is None:
|
|
1202
|
+
max_count = len(cells)
|
|
1203
|
+
|
|
1204
|
+
for index, cell in enumerate(cells):
|
|
1205
|
+
if index == max_count:
|
|
1206
|
+
break
|
|
1207
|
+
|
|
1208
|
+
yield cell.value, cell.timestamp_micros
|
|
1209
|
+
|
|
1210
|
+
|
|
1211
|
+
class Cell(object):
|
|
1212
|
+
"""Representation of a Google Cloud Bigtable Cell.
|
|
1213
|
+
|
|
1214
|
+
:type value: bytes
|
|
1215
|
+
:param value: The value stored in the cell.
|
|
1216
|
+
|
|
1217
|
+
:type timestamp_micros: int
|
|
1218
|
+
:param timestamp_micros: The timestamp_micros when the cell was stored.
|
|
1219
|
+
|
|
1220
|
+
:type labels: list
|
|
1221
|
+
:param labels: (Optional) List of strings. Labels applied to the cell.
|
|
1222
|
+
"""
|
|
1223
|
+
|
|
1224
|
+
def __init__(self, value, timestamp_micros, labels=None):
|
|
1225
|
+
self.value = value
|
|
1226
|
+
self.timestamp_micros = timestamp_micros
|
|
1227
|
+
self.labels = list(labels) if labels is not None else []
|
|
1228
|
+
|
|
1229
|
+
@classmethod
|
|
1230
|
+
def from_pb(cls, cell_pb):
|
|
1231
|
+
"""Create a new cell from a Cell protobuf.
|
|
1232
|
+
|
|
1233
|
+
:type cell_pb: :class:`._generated.data_pb2.Cell`
|
|
1234
|
+
:param cell_pb: The protobuf to convert.
|
|
1235
|
+
|
|
1236
|
+
:rtype: :class:`Cell`
|
|
1237
|
+
:returns: The cell corresponding to the protobuf.
|
|
1238
|
+
"""
|
|
1239
|
+
if cell_pb.labels:
|
|
1240
|
+
return cls(cell_pb.value, cell_pb.timestamp_micros, labels=cell_pb.labels)
|
|
1241
|
+
else:
|
|
1242
|
+
return cls(cell_pb.value, cell_pb.timestamp_micros)
|
|
1243
|
+
|
|
1244
|
+
@property
|
|
1245
|
+
def timestamp(self):
|
|
1246
|
+
return _datetime_from_microseconds(self.timestamp_micros)
|
|
1247
|
+
|
|
1248
|
+
def __eq__(self, other):
|
|
1249
|
+
if not isinstance(other, self.__class__):
|
|
1250
|
+
return NotImplemented
|
|
1251
|
+
return (
|
|
1252
|
+
other.value == self.value
|
|
1253
|
+
and other.timestamp_micros == self.timestamp_micros
|
|
1254
|
+
and other.labels == self.labels
|
|
1255
|
+
)
|
|
1256
|
+
|
|
1257
|
+
def __ne__(self, other):
|
|
1258
|
+
return not self == other
|
|
1259
|
+
|
|
1260
|
+
def __repr__(self):
|
|
1261
|
+
return "<{name} value={value!r} timestamp={timestamp}>".format(
|
|
1262
|
+
name=self.__class__.__name__, value=self.value, timestamp=self.timestamp
|
|
1263
|
+
)
|
|
1264
|
+
|
|
1265
|
+
|
|
1266
|
+
class InvalidChunk(RuntimeError):
|
|
1267
|
+
"""Exception raised to invalid chunk data from back-end."""
|