Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: row_exists and read_row #778

Merged
merged 373 commits into from
Jun 16, 2023
Merged
Show file tree
Hide file tree
Changes from 250 commits
Commits
Show all changes
373 commits
Select commit Hold shift + click to select a range
1aa694b
got tests working
daniel-sanche Mar 28, 2023
e2d4bd5
fixed type
daniel-sanche Mar 28, 2023
a91362f
reverted rest client
daniel-sanche Mar 28, 2023
d80a8c0
fixed rest tests
daniel-sanche Mar 28, 2023
b888ee8
converted tests to pytest
daniel-sanche Mar 29, 2023
94c1187
added client closure
daniel-sanche Mar 29, 2023
4c02e6c
ran blacken
daniel-sanche Mar 29, 2023
d65b432
use paramaterize in tests
daniel-sanche Mar 29, 2023
4b63d87
improved some tests
daniel-sanche Mar 29, 2023
4ccc421
went back to init without event loop raising warning
daniel-sanche Mar 29, 2023
8001240
removed __del__
daniel-sanche Mar 29, 2023
7c9cea7
changed warning type
daniel-sanche Mar 29, 2023
3bbebea
ran blacken
daniel-sanche Mar 29, 2023
8bff9d0
improved task naming
daniel-sanche Mar 29, 2023
4ae2146
fixed style issues
daniel-sanche Mar 29, 2023
00be65a
fixed broken test
daniel-sanche Mar 29, 2023
8f15e9c
Update docstring
daniel-sanche Mar 30, 2023
b9dc2f7
got 3.7 tests working
daniel-sanche Mar 30, 2023
19036d8
fixed style issue
daniel-sanche Mar 30, 2023
8873e9d
remvoed keys, values, items
daniel-sanche Mar 30, 2023
ff7dcbb
removed nanosecond timestamps
daniel-sanche Mar 30, 2023
39da24d
ran black
daniel-sanche Mar 30, 2023
3a6fff1
removed from_dict
daniel-sanche Mar 30, 2023
d465737
Merge branch 'v3_row_response' into read_rows_state_machine
daniel-sanche Mar 30, 2023
00a3d3e
got acceptance tests passing
daniel-sanche Mar 31, 2023
393749f
removed type conversion
daniel-sanche Mar 31, 2023
2a42216
renamed acceptance test file
daniel-sanche Mar 31, 2023
536e587
ran blacken
daniel-sanche Mar 31, 2023
4e262d1
unwrap proto-plus object
daniel-sanche Mar 31, 2023
fac018e
added test skeleton
daniel-sanche Mar 31, 2023
9800a78
working on tests
daniel-sanche Mar 31, 2023
8a22d15
implement pool as custom grpc channel
daniel-sanche Mar 31, 2023
38e5662
did some restructuring
daniel-sanche Apr 1, 2023
5155800
got some tests working
daniel-sanche Apr 1, 2023
522f7fa
improved tests
daniel-sanche Apr 2, 2023
9429244
renamed RowResponse and CellResponse to Row and Cell
daniel-sanche Apr 2, 2023
1aa7424
fixed tests
daniel-sanche Apr 2, 2023
a603649
simplified row construction
daniel-sanche Apr 2, 2023
68a5a0f
added RowRange object
daniel-sanche Apr 3, 2023
cc2e7c8
added comments
daniel-sanche Apr 3, 2023
ba629c8
added api-core submodule
daniel-sanche Apr 3, 2023
75d2c10
copied in rough retryable logic
daniel-sanche Apr 3, 2023
d5eca2a
Merge branch 'v3_row_response' into read_rows_state_machine
daniel-sanche Apr 3, 2023
2a26797
updated Row and Cell class names
daniel-sanche Apr 3, 2023
bcd394f
fixed tests
daniel-sanche Apr 3, 2023
037af0d
added last scanned row class
daniel-sanche Apr 3, 2023
e17d9bc
ran blacken
daniel-sanche Apr 3, 2023
db80d22
Merge branch 'read_rows_state_machine' into read_rows_retries
daniel-sanche Apr 3, 2023
b3d977d
handle last scanned rows
daniel-sanche Apr 3, 2023
1f85462
Merge branch 'add_new_transport' into read_rows_retries
daniel-sanche Apr 3, 2023
1fba6ea
updated add_keys
daniel-sanche Apr 3, 2023
c4f82b0
removed chaining
daniel-sanche Apr 3, 2023
caca14c
improved to_dicts
daniel-sanche Apr 3, 2023
5f9ce85
improving row_ranges
daniel-sanche Apr 3, 2023
8e5f60a
fixed properties
daniel-sanche Apr 3, 2023
57184c1
added type checking to range
daniel-sanche Apr 3, 2023
3eda7f4
got tests passing
daniel-sanche Apr 3, 2023
65f5a2a
blacken, mypy
daniel-sanche Apr 3, 2023
3e724db
ran blacken
daniel-sanche Apr 3, 2023
45eadce
improved API usage
daniel-sanche Apr 3, 2023
c06213f
use invalid chunk
daniel-sanche Apr 3, 2023
6e75a2f
added per request timeouts
daniel-sanche Apr 3, 2023
a205e93
account for RequestStats
daniel-sanche Apr 3, 2023
ce3eb75
added output generator wrapper
daniel-sanche Apr 3, 2023
74029c9
updated template
daniel-sanche Apr 3, 2023
7f2be30
got tests passing
daniel-sanche Apr 3, 2023
2b044ce
removed metadata
daniel-sanche Apr 4, 2023
1743098
added sleep between swwapping and closing channels
daniel-sanche Apr 4, 2023
e5fa4b6
ran blacken
daniel-sanche Apr 4, 2023
8955ec5
got tests working
daniel-sanche Apr 4, 2023
002bc5f
fixed lint issue
daniel-sanche Apr 4, 2023
65f0d2f
fixed tests
daniel-sanche Apr 4, 2023
664a6d2
Merge branch 'add_new_transport' into read_rows_retries
daniel-sanche Apr 4, 2023
d3db731
Merge branch 'add_new_transport' into read_rows_state_machine
daniel-sanche Apr 4, 2023
5f41c06
changed return type
daniel-sanche Apr 4, 2023
aa26911
Merge branch 'v3_read_rows_query' into read_rows_state_machine
daniel-sanche Apr 4, 2023
7b68207
fixed typing issues
daniel-sanche Apr 4, 2023
a776cb5
Merge branch 'read_rows_state_machine' into read_rows_retries
daniel-sanche Apr 4, 2023
c164a47
adjusted types
daniel-sanche Apr 4, 2023
96d58d1
added per-row-rimeout to merge_row_stream_with_cache
daniel-sanche Apr 4, 2023
216610e
cancel stream on exception
daniel-sanche Apr 4, 2023
c505c39
moved retry logic into RetryableRowMerger
daniel-sanche Apr 4, 2023
179c8b8
fixed issues in merger
daniel-sanche Apr 4, 2023
3cc5380
moved streaming into cache into RetryableRowMerger
daniel-sanche Apr 4, 2023
4af0218
restructuring
daniel-sanche Apr 4, 2023
d6a323f
added idle timeout
daniel-sanche Apr 4, 2023
7b6d1db
keep track of last_raised
daniel-sanche Apr 4, 2023
733a393
fixed mypy issues
daniel-sanche Apr 4, 2023
12807e0
made idle timeout internal value
daniel-sanche Apr 4, 2023
0e3d32c
combined row merger functions
daniel-sanche Apr 4, 2023
5b055b4
made adjustments to RowMerger
daniel-sanche Apr 4, 2023
dbf19c9
holds a gapic client instead of inherits from it
daniel-sanche Apr 5, 2023
ab7931c
Merge branch 'add_new_transport' into read_rows_retries
daniel-sanche Apr 5, 2023
88f14f6
don't emit _LastScannedRows
daniel-sanche Apr 5, 2023
9f15a6a
fixed type issues
daniel-sanche Apr 5, 2023
b3c32b0
got tests passing
daniel-sanche Apr 5, 2023
770d9f5
added comments
daniel-sanche Apr 5, 2023
9f3e0c5
added comment
daniel-sanche Apr 5, 2023
a0620ea
added random noise to refresh intervals
daniel-sanche Apr 5, 2023
4f5ed46
improving comments; clean up
daniel-sanche Apr 5, 2023
c169ba8
fixed param order
daniel-sanche Apr 5, 2023
9ec3697
working on getting end-to-end read_rows working
daniel-sanche Apr 5, 2023
b6873e8
fixed issue in pulling from cache
daniel-sanche Apr 5, 2023
2facc79
added timeout to results generator
daniel-sanche Apr 5, 2023
ee826bb
added acceptance tests for read_rows
daniel-sanche Apr 5, 2023
25af0c0
adding tests
daniel-sanche Apr 5, 2023
2f7778d
got operation deadline error working properly
daniel-sanche Apr 5, 2023
d6b8e6b
made RowMerger back into an iterable
daniel-sanche Apr 5, 2023
3f085a9
added test for per-row timeout
daniel-sanche Apr 6, 2023
6abb9d4
don't attach retry errors if there are none
daniel-sanche Apr 6, 2023
128320c
added tests for per_request_timeout
daniel-sanche Apr 6, 2023
a048536
added idle timeout test
daniel-sanche Apr 6, 2023
371dd64
remove row merger after error
daniel-sanche Apr 6, 2023
ebbaa1e
reorganized retryable_merge_rows
daniel-sanche Apr 6, 2023
2a3e379
improved resource clean up on retries and expiration
daniel-sanche Apr 6, 2023
2e50c51
added tests for request stats
daniel-sanche Apr 6, 2023
0b63b2b
added tests for exceptions
daniel-sanche Apr 6, 2023
de102bb
clean up on_error
daniel-sanche Apr 6, 2023
bbdb8e6
await sleep
daniel-sanche Apr 6, 2023
83472dc
got tests working
daniel-sanche Apr 6, 2023
bef40bd
updated api-core
daniel-sanche Apr 6, 2023
29a98ed
Merge branch 'v3' into read_rows_retries
daniel-sanche Apr 6, 2023
534005a
ran blacken
daniel-sanche Apr 6, 2023
6f1c781
made invalid chunk a server error
daniel-sanche Apr 6, 2023
38f66e5
moved invalid chunk with other exceptions
daniel-sanche Apr 6, 2023
bf24c25
made row merger and classes private
daniel-sanche Apr 6, 2023
4dbacb5
added read_rows
daniel-sanche Apr 6, 2023
6e6978e
ran blacken
daniel-sanche Apr 6, 2023
21f7846
added comments
daniel-sanche Apr 6, 2023
52e9dbf
added test for revise rowset
daniel-sanche Apr 6, 2023
715be51
fixed lint issues
daniel-sanche Apr 6, 2023
2f50cb7
moved ReadRowsIterator into new file
daniel-sanche Apr 6, 2023
1486d5a
Merge branch 'v3' into add_new_transport
daniel-sanche Apr 6, 2023
28d5a7a
fixed lint issues
daniel-sanche Apr 6, 2023
3b11580
Merge branch 'add_new_transport' into read_rows_retries
daniel-sanche Apr 6, 2023
d47c941
changed comment
daniel-sanche Apr 6, 2023
d1bd128
added comments to iterator
daniel-sanche Apr 6, 2023
039d623
added var for idle timeout
daniel-sanche Apr 6, 2023
3d34dcd
sped up acceptance tests
daniel-sanche Apr 6, 2023
70fbff9
reduced size of template by making subclass
daniel-sanche Apr 7, 2023
383d8eb
reverted unintentional gapic generation changes
daniel-sanche Apr 7, 2023
018fe03
updated submodule
daniel-sanche Apr 7, 2023
3764a98
added default timeouts to table surface
daniel-sanche Apr 7, 2023
745ae38
end after row_limit rows
daniel-sanche Apr 13, 2023
3d11d55
changed retryable exceptions
daniel-sanche Apr 13, 2023
f0403e7
changed warning stack level
daniel-sanche Apr 13, 2023
84a775a
changed retryable errors
daniel-sanche Apr 13, 2023
15a9d23
improved comments
daniel-sanche Apr 13, 2023
8636654
improved idle timeouts
daniel-sanche Apr 13, 2023
1aca392
changed retry parameters
daniel-sanche Apr 13, 2023
45fef1e
added limit revision to each retry
daniel-sanche Apr 13, 2023
951a77b
removed unneeded check
daniel-sanche Apr 13, 2023
e3a0b66
fixed idle timeout test
daniel-sanche Apr 13, 2023
6089934
removed tracking of emitted rows
daniel-sanche Apr 13, 2023
fb4b0ca
removed revise_on_retry flag
daniel-sanche Apr 14, 2023
83b908c
changed initial sleep
daniel-sanche Apr 14, 2023
5688561
added extra timeout check
daniel-sanche Apr 14, 2023
ff3724d
removed outdated test
daniel-sanche Apr 17, 2023
78a309c
fixed type annotations
daniel-sanche Apr 17, 2023
c50ae18
added slots
daniel-sanche Apr 17, 2023
d73121b
renamed cache to buffer
daniel-sanche Apr 17, 2023
14d8527
renamed errors
daniel-sanche Apr 17, 2023
4b89c86
replaced type check with None check
daniel-sanche Apr 17, 2023
9f89577
added comment for last_scanned_row heartbeat
daniel-sanche Apr 17, 2023
4b229b9
added early return
daniel-sanche Apr 17, 2023
152bccf
moved validation
daniel-sanche Apr 17, 2023
67c2911
added close call to ReadRowsIterator
daniel-sanche Apr 18, 2023
ff11ad3
removed del
daniel-sanche Apr 18, 2023
78bd5d3
pull out buffer control logic
daniel-sanche Apr 18, 2023
ca4a16d
got buffering working
daniel-sanche Apr 18, 2023
0dba121
check for full table scan revision
daniel-sanche Apr 18, 2023
3537566
renamed and added underscores
daniel-sanche Apr 18, 2023
981f169
added extra check
daniel-sanche Apr 18, 2023
d3d4c76
removed unneeded validation
daniel-sanche Apr 18, 2023
1901094
renamed RowMerger to ReadRowsOperation
daniel-sanche Apr 18, 2023
947fe9b
changed _read_rows test file name
daniel-sanche Apr 18, 2023
773d4e5
added row builder tests
daniel-sanche Apr 18, 2023
cbb0513
added revise_row tests
daniel-sanche Apr 19, 2023
2bec693
ran blacken
daniel-sanche Apr 19, 2023
5cd8e00
added constructor tests
daniel-sanche Apr 19, 2023
d6f3ae1
upgraded submodule
daniel-sanche Apr 19, 2023
f2d7e71
added tests
daniel-sanche Apr 19, 2023
cb23d32
update docstring
daniel-sanche Apr 19, 2023
bc31ab8
update docstring
daniel-sanche Apr 19, 2023
f54dfde
fix typo
daniel-sanche Apr 19, 2023
46cfc49
docstring improvements
daniel-sanche Apr 19, 2023
573bbd1
made creating table outside loop into error
daniel-sanche Apr 19, 2023
4f2657d
make tables own active instances, and remove instances when tables close
daniel-sanche Apr 19, 2023
59955be
added pool_size and channels as public properties
daniel-sanche Apr 19, 2023
377a8c9
fixed typo
daniel-sanche Apr 19, 2023
8a29898
simplified pooled multicallable
daniel-sanche Apr 20, 2023
50aa5ba
ran blacken
daniel-sanche Apr 20, 2023
42a52a3
associate ids with instances, instead of Table objects
daniel-sanche Apr 20, 2023
abc7a2d
fixed tests
daniel-sanche Apr 20, 2023
836af0f
made sure that empty strings are valid family and qualifier inputs
daniel-sanche Apr 20, 2023
e73551d
added tests for state machine
daniel-sanche Apr 20, 2023
792aba1
added state machine tests
daniel-sanche Apr 20, 2023
e57c510
fixed broken mock
daniel-sanche Apr 20, 2023
88748a9
added additional tests
daniel-sanche Apr 20, 2023
0c38981
ran blacken
daniel-sanche Apr 20, 2023
50dc608
reverted pooled multicallable changes
daniel-sanche Apr 20, 2023
b116755
pass scopes to created channels
daniel-sanche Apr 21, 2023
ec5eb07
added basic ping system test
daniel-sanche Apr 21, 2023
55cdcc2
keep both the names and ids in table object
daniel-sanche Apr 21, 2023
0253692
Merge branch 'add_new_transport' into read_rows_retries
daniel-sanche Apr 21, 2023
3855333
added api-core to noxfile tests
daniel-sanche Apr 21, 2023
213519e
added basic read rows stream to system tests
daniel-sanche Apr 21, 2023
9e3b411
pull project details out of env vars
daniel-sanche Apr 21, 2023
d8cf158
added automatic row creation for system tests
daniel-sanche Apr 21, 2023
c9b8217
added read_rows non stream
daniel-sanche Apr 21, 2023
500eff0
added range query system test
daniel-sanche Apr 21, 2023
27130f0
added logic for temporary test tables and instances
daniel-sanche Apr 21, 2023
f4f4fac
made iterator active into a property
daniel-sanche Apr 21, 2023
06dee54
added more read_rows system tests
daniel-sanche Apr 21, 2023
9e11f88
fixed lint issues
daniel-sanche Apr 21, 2023
794c55a
added iterator tests
daniel-sanche Apr 21, 2023
ccd9545
added tests for timeouts
daniel-sanche Apr 21, 2023
ca84b96
ran black
daniel-sanche Apr 21, 2023
eb936cf
fixed lint issues
daniel-sanche Apr 21, 2023
ab43138
restructured test_client
daniel-sanche Apr 21, 2023
cb1884d
changed how random is mocked
daniel-sanche Apr 21, 2023
9a89d74
ran black
daniel-sanche Apr 21, 2023
7f783fc
restructred test_client
daniel-sanche Apr 21, 2023
6a6d219
Merge branch 'add_new_transport' into read_rows_retries
daniel-sanche Apr 21, 2023
72eca75
restructured test_client_read_rows
daniel-sanche Apr 21, 2023
ad42436
moved read rows tests in test_client
daniel-sanche Apr 21, 2023
7606e3a
update submodules in nox
daniel-sanche Apr 22, 2023
829e68f
ran black
daniel-sanche Apr 22, 2023
e8eff39
Merge branch 'v3' into read_rows_retries
daniel-sanche Apr 24, 2023
6a58e86
removed submodule update
daniel-sanche Apr 24, 2023
9be5b07
removed unneeded import
daniel-sanche Apr 24, 2023
83ffe31
added submodule update to trampoline.sh
daniel-sanche Apr 24, 2023
2de0c5e
reverted submodule update
daniel-sanche Apr 24, 2023
3d597dd
added api-core fork to external dependencies
daniel-sanche Apr 28, 2023
a7d6d25
changed pinned api-core version
daniel-sanche Apr 28, 2023
55ca37f
brought in shared deadline logic from mutate_rows
daniel-sanche Apr 28, 2023
fd27aa5
merged in read_rows performance optimizations
daniel-sanche May 12, 2023
12a8879
fixed lint issues
daniel-sanche May 12, 2023
ddb34ed
added implementation for row_exists
daniel-sanche May 18, 2023
e4a21a6
added implementation for read_row
daniel-sanche May 18, 2023
ea18255
added unit tests
daniel-sanche May 18, 2023
a741762
fixed bug in query
daniel-sanche May 18, 2023
c1895c8
added system tests
daniel-sanche May 18, 2023
6ca9027
raise type error if row_key is not str or bytes
daniel-sanche May 18, 2023
dc7a932
added custom exception for RowNotFound
daniel-sanche May 18, 2023
5a136a0
Merge branch 'v3' into read_rows_helpers
daniel-sanche May 24, 2023
7df0311
clean-up
daniel-sanche May 30, 2023
a704ac0
Merge branch 'v3' into read_rows_helpers
daniel-sanche Jun 7, 2023
51c2cd0
ran black
daniel-sanche Jun 7, 2023
1351e53
address PR comments
daniel-sanche Jun 14, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions google/cloud/bigtable/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
)
from google.cloud.client import ClientWithProject
from google.api_core.exceptions import GoogleAPICallError
from google.cloud.bigtable.exceptions import RowNotFound
from google.api_core import retry_async as retries
from google.api_core import exceptions as core_exceptions
from google.cloud.bigtable._read_rows import _ReadRowsOperation
Expand All @@ -55,6 +56,10 @@
from google.cloud.bigtable._helpers import _make_metadata
from google.cloud.bigtable._helpers import _convert_retry_deadline

from google.cloud.bigtable.row_filters import StripValueTransformerFilter
from google.cloud.bigtable.row_filters import CellsRowLimitFilter
from google.cloud.bigtable.row_filters import RowFilterChain

if TYPE_CHECKING:
from google.cloud.bigtable.mutations_batcher import MutationsBatcher
from google.cloud.bigtable import RowKeySamples
Expand Down Expand Up @@ -508,10 +513,22 @@ async def read_row(

See read_rows_stream

Raises:
- google.cloud.bigtable.exceptions.RowNotFound: if the row does not exist
mutianf marked this conversation as resolved.
Show resolved Hide resolved
Returns:
- the individual row requested
"""
raise NotImplementedError
if row_key is None:
raise ValueError("row_key must be string or bytes")
mutianf marked this conversation as resolved.
Show resolved Hide resolved
query = ReadRowsQuery(row_keys=row_key, limit=1)
results = await self.read_rows(
query,
operation_timeout=operation_timeout,
per_request_timeout=per_request_timeout,
)
if len(results) == 0:
raise RowNotFound(f"Row {row_key!r} not found")
return results[0]

async def read_rows_sharded(
self,
Expand Down Expand Up @@ -547,7 +564,18 @@ async def row_exists(
Returns:
- a bool indicating whether the row exists
"""
raise NotImplementedError
if row_key is None:
raise ValueError("row_key must be string or bytes")
strip_filter = StripValueTransformerFilter(flag=True)
limit_filter = CellsRowLimitFilter(1)
chain_filter = RowFilterChain(filters=[limit_filter, strip_filter])
query = ReadRowsQuery(row_keys=row_key, limit=1, row_filter=chain_filter)
results = await self.read_rows(
query,
operation_timeout=operation_timeout,
per_request_timeout=per_request_timeout,
)
return len(results) > 0

async def sample_keys(
self,
Expand Down
4 changes: 4 additions & 0 deletions google/cloud/bigtable/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ class InvalidChunk(core_exceptions.GoogleAPICallError):
"""Exception raised to invalid chunk data from back-end."""


class RowNotFound(core_exceptions.NotFound):
"""Exception raised when a row is not found on a read_row call."""


class _RowSetComplete(Exception):
"""
Internal exception for _ReadRowsOperation
Expand Down
10 changes: 7 additions & 3 deletions google/cloud/bigtable/read_rows_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,12 +106,12 @@ def __init__(
"""
self.row_keys: set[bytes] = set()
self.row_ranges: list[RowRange | dict[str, bytes]] = []
if row_ranges:
if row_ranges is not None:
if isinstance(row_ranges, RowRange):
row_ranges = [row_ranges]
for r in row_ranges:
self.add_range(r)
if row_keys:
if row_keys is not None:
if not isinstance(row_keys, list):
row_keys = [row_keys]
for k in row_keys:
Expand Down Expand Up @@ -221,7 +221,11 @@ def _to_dict(self) -> dict[str, Any]:
row_ranges.append(dict_range)
row_keys = list(self.row_keys)
row_keys.sort()
row_set = {"row_keys": row_keys, "row_ranges": row_ranges}
row_set: dict[str, Any] = {}
if row_keys:
row_set["row_keys"] = row_keys
if row_ranges:
row_set["row_ranges"] = row_ranges
final_dict: dict[str, Any] = {
"rows": row_set,
}
Expand Down
52 changes: 51 additions & 1 deletion tests/system/test_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ def __init__(self, table):
self.table = table

async def add_row(
self, row_key, family=TEST_FAMILY, qualifier=b"q", value=b"test-value"
self, row_key, *, family=TEST_FAMILY, qualifier=b"q", value=b"test-value"
):
if isinstance(value, str):
value = value.encode("utf-8")
Expand Down Expand Up @@ -397,6 +397,56 @@ async def test_read_rows_stream_inactive_timer(table, temp_rows):
assert "idle_timeout=0.1" in str(e)


@pytest.mark.asyncio
async def test_read_row(table, temp_rows):
"""
Test read_row (single row helper)
"""
from google.cloud.bigtable import Row

await temp_rows.add_row(b"row_key_1", value=b"value")
row = await table.read_row(b"row_key_1")
assert isinstance(row, Row)
assert row.row_key == b"row_key_1"
assert row.cells[0].value == b"value"


@pytest.mark.asyncio
async def test_read_row_missing(table):
"""
Test read_row when row does not exist
"""
from google.api_core import exceptions
from google.cloud.bigtable.exceptions import RowNotFound

row_key = "row_key_not_exist"
with pytest.raises(RowNotFound) as e:
await table.read_row(row_key)
assert str(e) == f"Row b'{row_key}' not found"
with pytest.raises(exceptions.InvalidArgument) as e:
await table.read_row("")
assert "Row kest must be non-empty" in str(e)


@pytest.mark.asyncio
async def test_row_exists(table, temp_rows):
from google.api_core import exceptions

"""Test row_exists with rows that exist and don't exist"""
assert await table.row_exists(b"row_key_1") is False
await temp_rows.add_row(b"row_key_1")
assert await table.row_exists(b"row_key_1") is True
assert await table.row_exists("row_key_1") is True
assert await table.row_exists(b"row_key_2") is False
assert await table.row_exists("row_key_2") is False
assert await table.row_exists("3") is False
await temp_rows.add_row(b"3")
assert await table.row_exists(b"3") is True
with pytest.raises(exceptions.InvalidArgument) as e:
await table.row_exists("")
assert "Row kest must be non-empty" in str(e)


@retry.Retry(predicate=retry.if_exception_type(ClientError), initial=1, maximum=5)
@pytest.mark.parametrize(
"cell_value,filter_input,expect_match",
Expand Down
121 changes: 121 additions & 0 deletions tests/unit/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1296,6 +1296,127 @@ async def test_read_rows_default_timeout_override(self):
assert kwargs["operation_timeout"] == operation_timeout
assert kwargs["per_request_timeout"] == per_request_timeout

@pytest.mark.asyncio
async def test_read_row(self):
"""Test reading a single row"""
async with self._make_client() as client:
table = client.get_table("instance", "table")
row_key = b"test_1"
with mock.patch.object(table, "read_rows") as read_rows:
expected_result = object()
read_rows.side_effect = lambda *args, **kwargs: [expected_result]
expected_op_timeout = 8
expected_req_timeout = 4
row = await table.read_row(
row_key,
operation_timeout=expected_op_timeout,
per_request_timeout=expected_req_timeout,
)
assert row == expected_result
assert read_rows.call_count == 1
args, kwargs = read_rows.call_args_list[0]
assert kwargs["operation_timeout"] == expected_op_timeout
assert kwargs["per_request_timeout"] == expected_req_timeout
assert len(args) == 1
assert isinstance(args[0], ReadRowsQuery)
assert args[0]._to_dict() == {
"rows": {"row_keys": [row_key]},
"rows_limit": 1,
}

@pytest.mark.asyncio
async def test_read_row_no_response(self):
"""should raise RowNotFound if row does not exist"""
from google.cloud.bigtable.exceptions import RowNotFound

async with self._make_client() as client:
table = client.get_table("instance", "table")
row_key = b"test_1"
with mock.patch.object(table, "read_rows") as read_rows:
# return no rows
read_rows.side_effect = lambda *args, **kwargs: []
expected_op_timeout = 8
expected_req_timeout = 4
with pytest.raises(RowNotFound):
await table.read_row(
row_key,
operation_timeout=expected_op_timeout,
per_request_timeout=expected_req_timeout,
)
assert read_rows.call_count == 1
args, kwargs = read_rows.call_args_list[0]
assert kwargs["operation_timeout"] == expected_op_timeout
assert kwargs["per_request_timeout"] == expected_req_timeout
assert isinstance(args[0], ReadRowsQuery)
assert args[0]._to_dict() == {
"rows": {"row_keys": [row_key]},
"rows_limit": 1,
}

@pytest.mark.parametrize("input_row", [None, 5, object()])
@pytest.mark.asyncio
async def test_read_row_w_invalid_input(self, input_row):
"""Should raise error when passed None"""
async with self._make_client() as client:
table = client.get_table("instance", "table")
with pytest.raises(ValueError) as e:
await table.read_row(input_row)
assert "must be string or bytes" in e

@pytest.mark.parametrize(
"return_value,expected_result",
[
([], False),
([object()], True),
([object(), object()], True),
],
)
@pytest.mark.asyncio
async def test_row_exists(self, return_value, expected_result):
"""Test checking for row existence"""
async with self._make_client() as client:
table = client.get_table("instance", "table")
row_key = b"test_1"
with mock.patch.object(table, "read_rows") as read_rows:
# return no rows
read_rows.side_effect = lambda *args, **kwargs: return_value
expected_op_timeout = 1
expected_req_timeout = 2
result = await table.row_exists(
row_key,
operation_timeout=expected_op_timeout,
per_request_timeout=expected_req_timeout,
)
assert expected_result == result
assert read_rows.call_count == 1
args, kwargs = read_rows.call_args_list[0]
assert kwargs["operation_timeout"] == expected_op_timeout
assert kwargs["per_request_timeout"] == expected_req_timeout
assert isinstance(args[0], ReadRowsQuery)
expected_filter = {
"chain": {
"filters": [
{"cells_per_row_limit_filter": 1},
{"strip_value_transformer": True},
]
}
}
assert args[0]._to_dict() == {
"rows": {"row_keys": [row_key]},
"rows_limit": 1,
"filter": expected_filter,
}

@pytest.mark.parametrize("input_row", [None, 5, object()])
@pytest.mark.asyncio
async def test_row_exists_w_invalid_input(self, input_row):
"""Should raise error when passed None"""
async with self._make_client() as client:
table = client.get_table("instance", "table")
with pytest.raises(ValueError) as e:
await table.row_exists(input_row)
assert "must be string or bytes" in e

@pytest.mark.parametrize("include_app_profile", [True, False])
@pytest.mark.asyncio
async def test_read_rows_metadata(self, include_app_profile):
Expand Down
7 changes: 6 additions & 1 deletion tests/unit/test_read_rows_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ def test_to_dict_rows_default(self):
output = query._to_dict()
self.assertTrue(isinstance(output, dict))
self.assertEqual(len(output.keys()), 1)
expected = {"rows": {"row_keys": [], "row_ranges": []}}
expected = {"rows": {}}
self.assertEqual(output, expected)

request_proto = ReadRowsRequest(**output)
Expand Down Expand Up @@ -355,5 +355,10 @@ def test_to_dict_rows_populated(self):
filter_proto = request_proto.filter
self.assertEqual(filter_proto, row_filter._to_pb())

def test_empty_row_set(self):
"""Empty strings should be treated as keys inputs"""
query = self._make_one(row_keys="")
self.assertEqual(query.row_keys, {b""})

def test_shard(self):
pass