Skip to content

Commit 5f1a73e

Browse files
committed
BQ: Make snippets.py run as system tests.
Converts snippet function names to pytest conventions (shouldn't affect the docs, as the docs use region tags to grab sub-sections to include. I had to make some minor changes to ensure that the samples work on both Python 2 and Python 3, which is why I didn't try to bulk update the snippets for all the other products.
1 parent 5cc463f commit 5f1a73e

File tree

2 files changed

+82
-89
lines changed

2 files changed

+82
-89
lines changed

bigquery/nox.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,39 @@ def system_tests(session, python_version):
8787
)
8888

8989

90+
@nox.session
91+
@nox.parametrize('python_version', ['2.7', '3.6'])
92+
def snippets_tests(session, python_version):
93+
"""Run the system test suite."""
94+
95+
# Sanity check: Only run system tests if the environment variable is set.
96+
if not os.environ.get('GOOGLE_APPLICATION_CREDENTIALS', ''):
97+
session.skip('Credentials must be set via environment variable.')
98+
99+
# Run the system tests against latest Python 2 and Python 3 only.
100+
session.interpreter = 'python{}'.format(python_version)
101+
102+
# Set the virtualenv dirname.
103+
session.virtualenv_dirname = 'snip-' + python_version
104+
105+
# Install all test dependencies, then install this package into the
106+
# virtualenv's dist-packages.
107+
session.install('mock', 'pytest', *LOCAL_DEPS)
108+
session.install(
109+
os.path.join('..', 'storage'),
110+
os.path.join('..', 'test_utils'),
111+
)
112+
session.install('.')
113+
114+
# Run py.test against the system tests.
115+
session.run(
116+
'py.test',
117+
'--quiet',
118+
os.path.join(os.pardir, 'docs', 'bigquery', 'snippets.py'),
119+
*session.posargs
120+
)
121+
122+
90123
@nox.session
91124
def lint(session):
92125
"""Run linters.
@@ -100,6 +133,8 @@ def lint(session):
100133
session.install('.')
101134
session.run('flake8', os.path.join('google', 'cloud', 'bigquery'))
102135
session.run('flake8', 'tests')
136+
session.run(
137+
'flake8', os.path.join(os.pardir, 'docs', 'bigquery', 'snippets.py'))
103138
session.run(
104139
'gcp-devrel-py-tools', 'run-pylint',
105140
'--config', 'pylint.config.py',

docs/bigquery/snippets.py

Lines changed: 47 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import operator
2727
import time
2828

29+
import pytest
2930
import six
3031

3132
from google.cloud.bigquery import SchemaField
@@ -50,10 +51,17 @@
5051
'WHERE state = "TX"')
5152

5253

53-
def snippet(func):
54-
"""Mark ``func`` as a snippet example function."""
55-
func._snippet = True
56-
return func
54+
@pytest.fixture(scope='module')
55+
def client():
56+
return Client()
57+
58+
59+
@pytest.fixture
60+
def to_delete():
61+
doomed = []
62+
yield doomed
63+
for item in doomed:
64+
item.delete()
5765

5866

5967
def _millis():
@@ -69,8 +77,7 @@ def delete(self):
6977
self._wrapped.close()
7078

7179

72-
@snippet
73-
def client_list_datasets(client, _):
80+
def test_client_list_datasets(client):
7481
"""List datasets for a project."""
7582

7683
def do_something_with(_):
@@ -82,8 +89,7 @@ def do_something_with(_):
8289
# [END client_list_datasets]
8390

8491

85-
@snippet
86-
def dataset_create(client, to_delete):
92+
def test_dataset_create(client, to_delete):
8793
"""Create a dataset."""
8894
DATASET_NAME = 'dataset_create_%d' % (_millis(),)
8995

@@ -95,8 +101,7 @@ def dataset_create(client, to_delete):
95101
to_delete.append(dataset)
96102

97103

98-
@snippet
99-
def dataset_exists(client, to_delete):
104+
def test_dataset_exists(client, to_delete):
100105
"""Test existence of a dataset."""
101106
DATASET_NAME = 'dataset_exists_%d' % (_millis(),)
102107
dataset = client.dataset(DATASET_NAME)
@@ -109,8 +114,7 @@ def dataset_exists(client, to_delete):
109114
# [END dataset_exists]
110115

111116

112-
@snippet
113-
def dataset_reload(client, to_delete):
117+
def test_dataset_reload(client, to_delete):
114118
"""Reload a dataset's metadata."""
115119
DATASET_NAME = 'dataset_reload_%d' % (_millis(),)
116120
dataset = client.dataset(DATASET_NAME)
@@ -127,8 +131,7 @@ def dataset_reload(client, to_delete):
127131
# [END dataset_reload]
128132

129133

130-
@snippet
131-
def dataset_patch(client, to_delete):
134+
def test_dataset_patch(client, to_delete):
132135
"""Patch a dataset's metadata."""
133136
DATASET_NAME = 'dataset_patch_%d' % (_millis(),)
134137
dataset = client.dataset(DATASET_NAME)
@@ -148,8 +151,7 @@ def dataset_patch(client, to_delete):
148151
# [END dataset_patch]
149152

150153

151-
@snippet
152-
def dataset_update(client, to_delete):
154+
def test_dataset_update(client, to_delete):
153155
"""Update a dataset's metadata."""
154156
DATASET_NAME = 'dataset_update_%d' % (_millis(),)
155157
dataset = client.dataset(DATASET_NAME)
@@ -178,8 +180,7 @@ def dataset_update(client, to_delete):
178180
# [END dataset_update]
179181

180182

181-
@snippet
182-
def dataset_delete(client, _):
183+
def test_dataset_delete(client):
183184
"""Delete a dataset."""
184185
DATASET_NAME = 'dataset_delete_%d' % (_millis(),)
185186
dataset = client.dataset(DATASET_NAME)
@@ -192,8 +193,7 @@ def dataset_delete(client, _):
192193
# [END dataset_delete]
193194

194195

195-
@snippet
196-
def dataset_list_tables(client, to_delete):
196+
def test_dataset_list_tables(client, to_delete):
197197
"""List tables within a dataset."""
198198
DATASET_NAME = 'dataset_list_tables_dataset_%d' % (_millis(),)
199199
TABLE_NAME = 'dataset_list_tables_table_%d' % (_millis(),)
@@ -214,8 +214,7 @@ def dataset_list_tables(client, to_delete):
214214
to_delete.insert(0, table)
215215

216216

217-
@snippet
218-
def table_create(client, to_delete):
217+
def test_table_create(client, to_delete):
219218
"""Create a table."""
220219
DATASET_NAME = 'table_create_dataset_%d' % (_millis(),)
221220
TABLE_NAME = 'table_create_table_%d' % (_millis(),)
@@ -231,8 +230,7 @@ def table_create(client, to_delete):
231230
to_delete.insert(0, table)
232231

233232

234-
@snippet
235-
def table_exists(client, to_delete):
233+
def test_table_exists(client, to_delete):
236234
"""Test existence of a table."""
237235
DATASET_NAME = 'table_exists_dataset_%d' % (_millis(),)
238236
TABLE_NAME = 'table_exists_table_%d' % (_millis(),)
@@ -250,8 +248,7 @@ def table_exists(client, to_delete):
250248
to_delete.insert(0, table)
251249

252250

253-
@snippet
254-
def table_reload(client, to_delete):
251+
def test_table_reload(client, to_delete):
255252
"""Reload a table's metadata."""
256253
DATASET_NAME = 'table_reload_dataset_%d' % (_millis(),)
257254
TABLE_NAME = 'table_reload_table_%d' % (_millis(),)
@@ -276,8 +273,7 @@ def table_reload(client, to_delete):
276273
# [END table_reload]
277274

278275

279-
@snippet
280-
def table_patch(client, to_delete):
276+
def test_table_patch(client, to_delete):
281277
"""Patch a table's metadata."""
282278
DATASET_NAME = 'table_patch_dataset_%d' % (_millis(),)
283279
TABLE_NAME = 'table_patch_table_%d' % (_millis(),)
@@ -304,8 +300,7 @@ def table_patch(client, to_delete):
304300
# [END table_patch]
305301

306302

307-
@snippet
308-
def table_update(client, to_delete):
303+
def test_table_update(client, to_delete):
309304
"""Update a table's metadata."""
310305
DATASET_NAME = 'table_update_dataset_%d' % (_millis(),)
311306
TABLE_NAME = 'table_update_table_%d' % (_millis(),)
@@ -350,8 +345,7 @@ def _warm_up_inserted_table_data(table):
350345
time.sleep(5)
351346

352347

353-
@snippet
354-
def table_insert_fetch_data(client, to_delete):
348+
def test_table_insert_fetch_data(client, to_delete):
355349
"""Insert / fetch table data."""
356350
DATASET_NAME = 'table_insert_fetch_data_dataset_%d' % (_millis(),)
357351
TABLE_NAME = 'table_insert_fetch_data_table_%d' % (_millis(),)
@@ -391,11 +385,8 @@ def do_something(row):
391385
assert found == to_insert
392386

393387

394-
@snippet
395-
def table_upload_from_file(client, to_delete):
388+
def test_table_upload_from_file(client, to_delete):
396389
"""Upload table data from a CSV file."""
397-
import csv
398-
import tempfile
399390
DATASET_NAME = 'table_upload_from_file_dataset_%d' % (_millis(),)
400391
TABLE_NAME = 'table_upload_from_file_table_%d' % (_millis(),)
401392
dataset = client.dataset(DATASET_NAME)
@@ -406,19 +397,15 @@ def table_upload_from_file(client, to_delete):
406397
table.create()
407398
to_delete.insert(0, table)
408399

409-
csv_file = tempfile.NamedTemporaryFile(suffix='.csv')
410-
to_delete.append(_CloseOnDelete(csv_file))
411-
412400
# [START table_upload_from_file]
413-
writer = csv.writer(csv_file)
414-
writer.writerow((b'full_name', b'age'))
415-
writer.writerow((b'Phred Phlyntstone', b'32'))
416-
writer.writerow((b'Wylma Phlyntstone', b'29'))
417-
csv_file.flush()
418-
419-
with open(csv_file.name, 'rb') as readable:
420-
table.upload_from_file(
421-
readable, source_format='CSV', skip_leading_rows=1)
401+
csv_file = six.BytesIO(b"""full_name,age
402+
Phred Phlyntstone,32
403+
Wylma Phlyntstone,29
404+
""")
405+
406+
load_job = table.upload_from_file(
407+
csv_file, source_format='CSV', skip_leading_rows=1)
408+
load_job.result() # Wait for table load to complete.
422409
# [END table_upload_from_file]
423410

424411
_warm_up_inserted_table_data(table)
@@ -431,12 +418,11 @@ def table_upload_from_file(client, to_delete):
431418

432419
assert len(rows) == total == 2
433420
assert token is None
434-
assert rows[0] == (u'Phred Phlyntstone', 32)
435-
assert rows[1] == (u'Wylma Phlyntstone', 29)
421+
assert (u'Phred Phlyntstone', 32) in rows
422+
assert (u'Wylma Phlyntstone', 29) in rows
436423

437424

438-
@snippet
439-
def table_delete(client, to_delete):
425+
def test_table_delete(client, to_delete):
440426
"""Delete a table."""
441427
DATASET_NAME = 'table_delete_dataset_%d' % (_millis(),)
442428
TABLE_NAME = 'table_create_table_%d' % (_millis(),)
@@ -454,8 +440,7 @@ def table_delete(client, to_delete):
454440
# [END table_delete]
455441

456442

457-
@snippet
458-
def client_list_jobs(client, _):
443+
def test_client_list_jobs(client):
459444
"""List jobs for a project."""
460445

461446
def do_something_with(_):
@@ -468,12 +453,11 @@ def do_something_with(_):
468453
# [END client_list_jobs]
469454

470455

471-
@snippet
472-
def client_run_sync_query(client, _):
456+
def test_client_run_sync_query(client):
473457
"""Run a synchronous query."""
474458
LIMIT = 100
475459
LIMITED = '%s LIMIT %d' % (QUERY, LIMIT)
476-
TIMEOUT_MS = 1000
460+
TIMEOUT_MS = 10000
477461

478462
# [START client_run_sync_query]
479463
query = client.run_sync_query(LIMITED)
@@ -486,15 +470,14 @@ def client_run_sync_query(client, _):
486470
# [END client_run_sync_query]
487471

488472

489-
@snippet
490-
def client_run_sync_query_w_param(client, _):
473+
def test_client_run_sync_query_w_param(client):
491474
"""Run a synchronous query using a query parameter"""
492475
QUERY_W_PARAM = (
493476
'SELECT name FROM `bigquery-public-data.usa_names.usa_1910_2013` '
494477
'WHERE state = @state')
495478
LIMIT = 100
496479
LIMITED = '%s LIMIT %d' % (QUERY_W_PARAM, LIMIT)
497-
TIMEOUT_MS = 1000
480+
TIMEOUT_MS = 10000
498481

499482
# [START client_run_sync_query_w_param]
500483
from google.cloud.bigquery import ScalarQueryParameter
@@ -510,10 +493,9 @@ def client_run_sync_query_w_param(client, _):
510493
# [END client_run_sync_query_w_param]
511494

512495

513-
@snippet
514-
def client_run_sync_query_paged(client, _):
496+
def test_client_run_sync_query_paged(client):
515497
"""Run a synchronous query with paged results."""
516-
TIMEOUT_MS = 1000
498+
TIMEOUT_MS = 10000
517499
PAGE_SIZE = 100
518500
LIMIT = 1000
519501
LIMITED = '%s LIMIT %d' % (QUERY, LIMIT)
@@ -543,8 +525,7 @@ def do_something_with(row):
543525
assert len(all_rows) == LIMIT
544526

545527

546-
@snippet
547-
def client_run_sync_query_timeout(client, _):
528+
def test_client_run_sync_query_timeout(client):
548529
"""Run a synchronous query w/ timeout"""
549530
TIMEOUT_MS = 10
550531

@@ -580,28 +561,5 @@ def do_something_with(row):
580561
assert len(all_rows) == iterator.total_rows
581562

582563

583-
def _find_examples():
584-
funcs = [obj for obj in globals().values()
585-
if getattr(obj, '_snippet', False)]
586-
for func in sorted(funcs, key=lambda f: f.func_code.co_firstlineno):
587-
yield func
588-
589-
590-
def main():
591-
client = Client()
592-
for example in _find_examples():
593-
to_delete = []
594-
print('%-30s: %s' % (
595-
example.func_name, example.func_doc))
596-
try:
597-
example(client, to_delete)
598-
except AssertionError as e:
599-
print(' FAIL: %s' % (e,))
600-
except Exception as e: # pylint: disable=broad-except
601-
print(' ERROR: %r' % (e,))
602-
for item in to_delete:
603-
item.delete()
604-
605-
606564
if __name__ == '__main__':
607-
main()
565+
pytest.main()

0 commit comments

Comments
 (0)