Skip to content

Commit 346bddb

Browse files
committed
Also accept any iterables for row_ids
1 parent bf0d408 commit 346bddb

File tree

2 files changed

+48
-12
lines changed

2 files changed

+48
-12
lines changed

google/cloud/bigquery/client.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3350,7 +3350,7 @@ def insert_rows_json(
33503350
self,
33513351
table: Union[Table, TableReference, str],
33523352
json_rows: Sequence[Dict],
3353-
row_ids: Union[Sequence[str], AutoRowIDs, None] = AutoRowIDs.GENERATE_UUID,
3353+
row_ids: Union[Iterable[str], AutoRowIDs, None] = AutoRowIDs.GENERATE_UUID,
33543354
skip_invalid_rows: bool = None,
33553355
ignore_unknown_values: bool = None,
33563356
template_suffix: str = None,
@@ -3372,15 +3372,19 @@ def insert_rows_json(
33723372
json_rows (Sequence[Dict]):
33733373
Row data to be inserted. Keys must match the table schema fields
33743374
and values must be JSON-compatible representations.
3375-
row_ids (Union[Sequence[str], AutoRowIDs, None]):
3375+
row_ids (Union[Iterable[str], AutoRowIDs, None]):
33763376
Unique IDs, one per row being inserted. An ID can also be
33773377
``None``, indicating that an explicit insert ID should **not**
33783378
be used for that row. If the argument is omitted altogether,
33793379
unique IDs are created automatically.
33803380
3381+
.. versionchanged:: 2.21.0
3382+
Can also be an iterable, not just a sequence, or an
3383+
:class:`AutoRowIDs` enum member.
3384+
33813385
.. deprecated:: 2.21.0
33823386
Passing ``None`` to explicitly request autogenerating insert IDs is
3383-
deprecated, use :attr:`.AutoRowIDs.GENERATE_UUID` instead.
3387+
deprecated, use :attr:`AutoRowIDs.GENERATE_UUID` instead.
33843388
33853389
skip_invalid_rows (Optional[bool]):
33863390
Insert all valid rows of a request, even if invalid rows exist.
@@ -3429,15 +3433,28 @@ def insert_rows_json(
34293433
)
34303434
row_ids = AutoRowIDs.GENERATE_UUID
34313435

3432-
for index, row in enumerate(json_rows):
3436+
if not isinstance(row_ids, AutoRowIDs):
3437+
try:
3438+
row_ids_iter = iter(row_ids)
3439+
except TypeError:
3440+
msg = "rows_ids is neither an iterable nor an AutoRowIDs enum member"
3441+
raise TypeError(msg)
3442+
3443+
for i, row in enumerate(json_rows):
34333444
info = {"json": row}
34343445

34353446
if row_ids is AutoRowIDs.GENERATE_UUID:
34363447
info["insertId"] = str(uuid.uuid4())
34373448
elif row_ids is AutoRowIDs.DISABLED:
34383449
info["insertId"] = None
34393450
else:
3440-
info["insertId"] = row_ids[index]
3451+
try:
3452+
insert_id = next(row_ids_iter)
3453+
except StopIteration:
3454+
msg = f"row_ids did not generate enough IDs, error at index {i}"
3455+
raise ValueError(msg)
3456+
else:
3457+
info["insertId"] = insert_id
34413458

34423459
rows_info.append(info)
34433460

tests/unit/test_client.py

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5544,29 +5544,48 @@ def test_insert_rows_json_w_explicitly_disabled_insert_ids(self):
55445544
timeout=None,
55455545
)
55465546

5547-
def test_insert_rows_json_with_string_id(self):
5548-
rows = [{"col1": "val1"}]
5547+
def test_insert_rows_json_with_iterator_row_ids(self):
5548+
rows = [{"col1": "val1"}, {"col2": "val2"}, {"col3": "val3"}]
55495549
creds = _make_credentials()
55505550
http = object()
55515551
client = self._make_one(
55525552
project="default-project", credentials=creds, _http=http
55535553
)
55545554
conn = client._connection = make_connection({})
55555555

5556-
with mock.patch("uuid.uuid4", side_effect=map(str, range(len(rows)))):
5557-
errors = client.insert_rows_json("proj.dset.tbl", rows)
5556+
row_ids_iter = map(str, itertools.count(42))
5557+
errors = client.insert_rows_json("proj.dset.tbl", rows, row_ids=row_ids_iter)
55585558

55595559
self.assertEqual(len(errors), 0)
5560-
expected = {
5561-
"rows": [{"json": row, "insertId": str(i)} for i, row in enumerate(rows)]
5560+
expected_row_data = {
5561+
"rows": [
5562+
{"json": {"col1": "val1"}, "insertId": "42"},
5563+
{"json": {"col2": "val2"}, "insertId": "43"},
5564+
{"json": {"col3": "val3"}, "insertId": "44"},
5565+
]
55625566
}
55635567
conn.api_request.assert_called_once_with(
55645568
method="POST",
55655569
path="/projects/proj/datasets/dset/tables/tbl/insertAll",
5566-
data=expected,
5570+
data=expected_row_data,
55675571
timeout=None,
55685572
)
55695573

5574+
def test_insert_rows_json_with_too_few_row_ids(self):
5575+
rows = [{"col1": "val1"}, {"col2": "val2"}, {"col3": "val3"}]
5576+
creds = _make_credentials()
5577+
http = object()
5578+
client = self._make_one(
5579+
project="default-project", credentials=creds, _http=http
5580+
)
5581+
client._connection = make_connection({})
5582+
5583+
insert_ids = ["10", "20"]
5584+
5585+
error_msg_pattern = "row_ids did not generate enough IDs.*index 2"
5586+
with self.assertRaisesRegex(ValueError, error_msg_pattern):
5587+
client.insert_rows_json("proj.dset.tbl", rows, row_ids=insert_ids)
5588+
55705589
def test_insert_rows_json_w_explicit_none_insert_ids(self):
55715590
rows = [{"col1": "val1"}, {"col2": "val2"}]
55725591
creds = _make_credentials()

0 commit comments

Comments
 (0)