43
43
from google .cloud import bigquery
44
44
from google .cloud .bigquery import job
45
45
from google .cloud .bigquery import table
46
- from google .cloud .bigquery .ipython_magics import line_arg_parser as lap
47
46
from google .cloud .bigquery .ipython_magics import magics
48
47
from tests .unit .helpers import make_connection
49
48
from test_utils .imports import maybe_fail_import
@@ -1165,6 +1164,37 @@ def test_bigquery_magic_with_project():
1165
1164
assert magics .context .project == "general-project"
1166
1165
1167
1166
1167
+ @pytest .mark .usefixtures ("ipython_interactive" )
1168
+ def test_bigquery_magic_with_multiple_options ():
1169
+ ip = IPython .get_ipython ()
1170
+ ip .extension_manager .load_extension ("google.cloud.bigquery" )
1171
+ magics .context ._project = None
1172
+
1173
+ credentials_mock = mock .create_autospec (
1174
+ google .auth .credentials .Credentials , instance = True
1175
+ )
1176
+ default_patch = mock .patch (
1177
+ "google.auth.default" , return_value = (credentials_mock , "general-project" )
1178
+ )
1179
+ run_query_patch = mock .patch (
1180
+ "google.cloud.bigquery.ipython_magics.magics._run_query" , autospec = True
1181
+ )
1182
+ with run_query_patch as run_query_mock , default_patch :
1183
+ ip .run_cell_magic (
1184
+ "bigquery" ,
1185
+ "--project=specific-project --use_legacy_sql --maximum_bytes_billed 1024" ,
1186
+ "SELECT 17 as num" ,
1187
+ )
1188
+
1189
+ args , kwargs = run_query_mock .call_args
1190
+ client_used = args [0 ]
1191
+ assert client_used .project == "specific-project"
1192
+
1193
+ job_config_used = kwargs ["job_config" ]
1194
+ assert job_config_used .use_legacy_sql
1195
+ assert job_config_used .maximum_bytes_billed == 1024
1196
+
1197
+
1168
1198
@pytest .mark .usefixtures ("ipython_interactive" )
1169
1199
@pytest .mark .skipif (pandas is None , reason = "Requires `pandas`" )
1170
1200
def test_bigquery_magic_with_string_params ():
@@ -1176,7 +1206,9 @@ def test_bigquery_magic_with_string_params():
1176
1206
1177
1207
sql = "SELECT @num AS num"
1178
1208
result = pandas .DataFrame ([17 ], columns = ["num" ])
1179
- assert "params_string_df" not in ip .user_ns
1209
+
1210
+ if "params_dict_df" in ip .user_ns :
1211
+ del ip .user_ns ["params_dict_df" ]
1180
1212
1181
1213
run_query_patch = mock .patch (
1182
1214
"google.cloud.bigquery.ipython_magics.magics._run_query" , autospec = True
@@ -1207,9 +1239,13 @@ def test_bigquery_magic_with_dict_params():
1207
1239
google .auth .credentials .Credentials , instance = True
1208
1240
)
1209
1241
1210
- sql = "SELECT @num AS num"
1211
- result = pandas .DataFrame ([17 ], columns = ["num" ])
1212
- assert "params_dict_df" not in ip .user_ns
1242
+ sql = "SELECT @num AS num, @tricky_value as tricky_value"
1243
+ result = pandas .DataFrame (
1244
+ [(17 , '--params "value"' )], columns = ["num" , "tricky_value" ]
1245
+ )
1246
+
1247
+ if "params_dict_df" in ip .user_ns :
1248
+ del ip .user_ns ["params_dict_df" ]
1213
1249
1214
1250
run_query_patch = mock .patch (
1215
1251
"google.cloud.bigquery.ipython_magics.magics._run_query" , autospec = True
@@ -1221,7 +1257,7 @@ def test_bigquery_magic_with_dict_params():
1221
1257
with run_query_patch as run_query_mock :
1222
1258
run_query_mock .return_value = query_job_mock
1223
1259
1224
- params = {"num" : 17 }
1260
+ params = {"num" : 17 , "tricky_value" : '--params "value"' }
1225
1261
# Insert dictionary into user namespace so that it can be expanded
1226
1262
ip .user_ns ["params" ] = params
1227
1263
ip .run_cell_magic ("bigquery" , "params_dict_df --params $params" , sql )
@@ -1233,6 +1269,192 @@ def test_bigquery_magic_with_dict_params():
1233
1269
assert len (df ) == len (result ) # verify row count
1234
1270
assert list (df ) == list (result ) # verify column names
1235
1271
1272
+ assert df ["num" ][0 ] == 17
1273
+ assert df ["tricky_value" ][0 ] == '--params "value"'
1274
+
1275
+
1276
+ @pytest .mark .usefixtures ("ipython_interactive" )
1277
+ @pytest .mark .skipif (pandas is None , reason = "Requires `pandas`" )
1278
+ def test_bigquery_magic_with_dict_params_nonexisting ():
1279
+ ip = IPython .get_ipython ()
1280
+ ip .extension_manager .load_extension ("google.cloud.bigquery" )
1281
+ magics .context .credentials = mock .create_autospec (
1282
+ google .auth .credentials .Credentials , instance = True
1283
+ )
1284
+
1285
+ sql = "SELECT @foo AS foo"
1286
+
1287
+ with pytest .raises (NameError , match = r".*undefined variable.*unknown_name.*" ):
1288
+ ip .run_cell_magic ("bigquery" , "params_dict_df --params $unknown_name" , sql )
1289
+
1290
+
1291
+ @pytest .mark .usefixtures ("ipython_interactive" )
1292
+ @pytest .mark .skipif (pandas is None , reason = "Requires `pandas`" )
1293
+ def test_bigquery_magic_with_dict_params_incorrect_syntax ():
1294
+ ip = IPython .get_ipython ()
1295
+ ip .extension_manager .load_extension ("google.cloud.bigquery" )
1296
+ magics .context .credentials = mock .create_autospec (
1297
+ google .auth .credentials .Credentials , instance = True
1298
+ )
1299
+
1300
+ sql = "SELECT @foo AS foo"
1301
+
1302
+ with pytest .raises (SyntaxError , match = r".*--params.*" ):
1303
+ cell_magic_args = "params_dict_df --params {'foo': 1; 'bar': 2}"
1304
+ ip .run_cell_magic ("bigquery" , cell_magic_args , sql )
1305
+
1306
+
1307
+ @pytest .mark .usefixtures ("ipython_interactive" )
1308
+ @pytest .mark .skipif (pandas is None , reason = "Requires `pandas`" )
1309
+ def test_bigquery_magic_with_dict_params_duplicate ():
1310
+ ip = IPython .get_ipython ()
1311
+ ip .extension_manager .load_extension ("google.cloud.bigquery" )
1312
+ magics .context .credentials = mock .create_autospec (
1313
+ google .auth .credentials .Credentials , instance = True
1314
+ )
1315
+
1316
+ sql = "SELECT @foo AS foo"
1317
+
1318
+ with pytest .raises (ValueError , match = r"Duplicate --params option\." ):
1319
+ cell_magic_args = (
1320
+ "params_dict_df --params {'foo': 1} " "--verbose " "--params {'bar': 2} "
1321
+ )
1322
+ ip .run_cell_magic ("bigquery" , cell_magic_args , sql )
1323
+
1324
+
1325
+ @pytest .mark .usefixtures ("ipython_interactive" )
1326
+ @pytest .mark .skipif (pandas is None , reason = "Requires `pandas`" )
1327
+ def test_bigquery_magic_with_option_value_incorrect ():
1328
+ ip = IPython .get_ipython ()
1329
+ ip .extension_manager .load_extension ("google.cloud.bigquery" )
1330
+ magics .context .credentials = mock .create_autospec (
1331
+ google .auth .credentials .Credentials , instance = True
1332
+ )
1333
+
1334
+ sql = "SELECT @foo AS foo"
1335
+
1336
+ exc_pattern = r".*[Uu]nrecognized input.*option values correct\?.*"
1337
+ with pytest .raises (ValueError , match = exc_pattern ):
1338
+ cell_magic_args = "params_dict_df --max_results [PLENTY!]"
1339
+ ip .run_cell_magic ("bigquery" , cell_magic_args , sql )
1340
+
1341
+
1342
+ @pytest .mark .usefixtures ("ipython_interactive" )
1343
+ @pytest .mark .skipif (pandas is None , reason = "Requires `pandas`" )
1344
+ def test_bigquery_magic_with_dict_params_negative_value ():
1345
+ ip = IPython .get_ipython ()
1346
+ ip .extension_manager .load_extension ("google.cloud.bigquery" )
1347
+ magics .context .credentials = mock .create_autospec (
1348
+ google .auth .credentials .Credentials , instance = True
1349
+ )
1350
+
1351
+ sql = "SELECT @num AS num"
1352
+ result = pandas .DataFrame ([- 17 ], columns = ["num" ])
1353
+
1354
+ if "params_dict_df" in ip .user_ns :
1355
+ del ip .user_ns ["params_dict_df" ]
1356
+
1357
+ run_query_patch = mock .patch (
1358
+ "google.cloud.bigquery.ipython_magics.magics._run_query" , autospec = True
1359
+ )
1360
+ query_job_mock = mock .create_autospec (
1361
+ google .cloud .bigquery .job .QueryJob , instance = True
1362
+ )
1363
+ query_job_mock .to_dataframe .return_value = result
1364
+ with run_query_patch as run_query_mock :
1365
+ run_query_mock .return_value = query_job_mock
1366
+
1367
+ params = {"num" : - 17 }
1368
+ # Insert dictionary into user namespace so that it can be expanded
1369
+ ip .user_ns ["params" ] = params
1370
+ ip .run_cell_magic ("bigquery" , "params_dict_df --params $params" , sql )
1371
+
1372
+ run_query_mock .assert_called_once_with (mock .ANY , sql .format (num = - 17 ), mock .ANY )
1373
+
1374
+ assert "params_dict_df" in ip .user_ns # verify that the variable exists
1375
+ df = ip .user_ns ["params_dict_df" ]
1376
+ assert len (df ) == len (result ) # verify row count
1377
+ assert list (df ) == list (result ) # verify column names
1378
+ assert df ["num" ][0 ] == - 17
1379
+
1380
+
1381
+ @pytest .mark .usefixtures ("ipython_interactive" )
1382
+ @pytest .mark .skipif (pandas is None , reason = "Requires `pandas`" )
1383
+ def test_bigquery_magic_with_dict_params_array_value ():
1384
+ ip = IPython .get_ipython ()
1385
+ ip .extension_manager .load_extension ("google.cloud.bigquery" )
1386
+ magics .context .credentials = mock .create_autospec (
1387
+ google .auth .credentials .Credentials , instance = True
1388
+ )
1389
+
1390
+ sql = "SELECT @num AS num"
1391
+ result = pandas .DataFrame (["foo bar" , "baz quux" ], columns = ["array_data" ])
1392
+
1393
+ if "params_dict_df" in ip .user_ns :
1394
+ del ip .user_ns ["params_dict_df" ]
1395
+
1396
+ run_query_patch = mock .patch (
1397
+ "google.cloud.bigquery.ipython_magics.magics._run_query" , autospec = True
1398
+ )
1399
+ query_job_mock = mock .create_autospec (
1400
+ google .cloud .bigquery .job .QueryJob , instance = True
1401
+ )
1402
+ query_job_mock .to_dataframe .return_value = result
1403
+ with run_query_patch as run_query_mock :
1404
+ run_query_mock .return_value = query_job_mock
1405
+
1406
+ params = {"array_data" : ["foo bar" , "baz quux" ]}
1407
+ # Insert dictionary into user namespace so that it can be expanded
1408
+ ip .user_ns ["params" ] = params
1409
+ ip .run_cell_magic ("bigquery" , "params_dict_df --params $params" , sql )
1410
+
1411
+ run_query_mock .assert_called_once_with (mock .ANY , sql .format (num = - 17 ), mock .ANY )
1412
+
1413
+ assert "params_dict_df" in ip .user_ns # verify that the variable exists
1414
+ df = ip .user_ns ["params_dict_df" ]
1415
+ assert len (df ) == len (result ) # verify row count
1416
+ assert list (df ) == list (result ) # verify column names
1417
+ assert list (df ["array_data" ]) == ["foo bar" , "baz quux" ]
1418
+
1419
+
1420
+ @pytest .mark .usefixtures ("ipython_interactive" )
1421
+ @pytest .mark .skipif (pandas is None , reason = "Requires `pandas`" )
1422
+ def test_bigquery_magic_with_dict_params_tuple_value ():
1423
+ ip = IPython .get_ipython ()
1424
+ ip .extension_manager .load_extension ("google.cloud.bigquery" )
1425
+ magics .context .credentials = mock .create_autospec (
1426
+ google .auth .credentials .Credentials , instance = True
1427
+ )
1428
+
1429
+ sql = "SELECT @num AS num"
1430
+ result = pandas .DataFrame (["foo bar" , "baz quux" ], columns = ["array_data" ])
1431
+
1432
+ if "params_dict_df" in ip .user_ns :
1433
+ del ip .user_ns ["params_dict_df" ]
1434
+
1435
+ run_query_patch = mock .patch (
1436
+ "google.cloud.bigquery.ipython_magics.magics._run_query" , autospec = True
1437
+ )
1438
+ query_job_mock = mock .create_autospec (
1439
+ google .cloud .bigquery .job .QueryJob , instance = True
1440
+ )
1441
+ query_job_mock .to_dataframe .return_value = result
1442
+ with run_query_patch as run_query_mock :
1443
+ run_query_mock .return_value = query_job_mock
1444
+
1445
+ params = {"array_data" : ("foo bar" , "baz quux" )}
1446
+ # Insert dictionary into user namespace so that it can be expanded
1447
+ ip .user_ns ["params" ] = params
1448
+ ip .run_cell_magic ("bigquery" , "params_dict_df --params $params" , sql )
1449
+
1450
+ run_query_mock .assert_called_once_with (mock .ANY , sql .format (num = - 17 ), mock .ANY )
1451
+
1452
+ assert "params_dict_df" in ip .user_ns # verify that the variable exists
1453
+ df = ip .user_ns ["params_dict_df" ]
1454
+ assert len (df ) == len (result ) # verify row count
1455
+ assert list (df ) == list (result ) # verify column names
1456
+ assert list (df ["array_data" ]) == ["foo bar" , "baz quux" ]
1457
+
1236
1458
1237
1459
@pytest .mark .usefixtures ("ipython_interactive" )
1238
1460
@pytest .mark .skipif (pandas is None , reason = "Requires `pandas`" )
@@ -1245,7 +1467,7 @@ def test_bigquery_magic_with_improperly_formatted_params():
1245
1467
1246
1468
sql = "SELECT @num AS num"
1247
1469
1248
- with pytest .raises (lap . exceptions . QueryParamsParseError ):
1470
+ with pytest .raises (SyntaxError ):
1249
1471
ip .run_cell_magic ("bigquery" , "--params {17}" , sql )
1250
1472
1251
1473
0 commit comments