Skip to content

Commit ad7d8ac

Browse files
authored
feat: allow functions returned from bpd.read_gbq_function to execute outside of apply (#706)
* feat: allow functions decorated with `@bpd.remote_function` to execute locally * fix read_gbq_function * feat: allow functions returned from `bpd.read_gbq_function` to execute outside of `apply` * fix for rare case where re-deploy exact same function object * feat: support type annotations to supply input and output types to `@remote_function` decorator * make tests robust to cloud function listing failures too * remove unused bigquery_client argument * add test that function can be called directly
1 parent 0d69ac4 commit ad7d8ac

File tree

2 files changed

+33
-23
lines changed

2 files changed

+33
-23
lines changed

bigframes/functions/remote_function.py

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1146,21 +1146,14 @@ def try_delattr(attr):
11461146

11471147
def read_gbq_function(
11481148
function_name: str,
1149-
session: Optional[Session] = None,
1150-
bigquery_client: Optional[bigquery.Client] = None,
1149+
*,
1150+
session: Session,
11511151
):
11521152
"""
11531153
Read an existing BigQuery function and prepare it for use in future queries.
11541154
"""
1155-
1156-
# A BigQuery client is required to perform BQ operations
1157-
if not bigquery_client and session:
1158-
bigquery_client = session.bqclient
1159-
if not bigquery_client:
1160-
raise ValueError(
1161-
"A bigquery client must be provided, either directly or via session. "
1162-
f"{constants.FEEDBACK_LINK}"
1163-
)
1155+
bigquery_client = session.bqclient
1156+
ibis_client = session.ibis_client
11641157

11651158
try:
11661159
routine_ref = get_routine_reference(function_name, bigquery_client, session)
@@ -1192,8 +1185,10 @@ def read_gbq_function(
11921185
# non-standard names for the arguments here.
11931186
def func(*ignored_args, **ignored_kwargs):
11941187
f"""Remote function {str(routine_ref)}."""
1195-
# TODO(swast): Construct an ibis client from bigquery_client and
1196-
# execute node via a query.
1188+
nonlocal node # type: ignore
1189+
1190+
expr = node(*ignored_args, **ignored_kwargs) # type: ignore
1191+
return ibis_client.execute(expr)
11971192

11981193
# TODO: Move ibis logic to compiler step
11991194
func.__name__ = routine_ref.routine_id

tests/system/small/test_remote_function.py

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -537,19 +537,20 @@ def add_one(x):
537537

538538

539539
@pytest.mark.flaky(retries=2, delay=120)
540-
def test_read_gbq_function_detects_invalid_function(bigquery_client, dataset_id):
540+
def test_read_gbq_function_detects_invalid_function(session, dataset_id):
541541
dataset_ref = bigquery.DatasetReference.from_string(dataset_id)
542542
with pytest.raises(ValueError) as e:
543543
rf.read_gbq_function(
544544
str(dataset_ref.routine("not_a_function")),
545-
bigquery_client=bigquery_client,
545+
session=session,
546546
)
547547

548548
assert "Unknown function" in str(e.value)
549549

550550

551551
@pytest.mark.flaky(retries=2, delay=120)
552552
def test_read_gbq_function_like_original(
553+
session,
553554
bigquery_client,
554555
bigqueryconnection_client,
555556
cloudfunctions_client,
@@ -577,7 +578,7 @@ def square1(x):
577578

578579
square2 = rf.read_gbq_function(
579580
function_name=square1.bigframes_remote_function,
580-
bigquery_client=bigquery_client,
581+
session=session,
581582
)
582583

583584
# The newly-created function (square1) should have a remote function AND a
@@ -607,7 +608,14 @@ def square1(x):
607608

608609

609610
@pytest.mark.flaky(retries=2, delay=120)
610-
def test_read_gbq_function_reads_udfs(bigquery_client, dataset_id):
611+
def test_read_gbq_function_runs_existing_udf(session, bigquery_client, dataset_id):
612+
func = session.read_gbq_function("bqutil.fn.cw_lower_case_ascii_only")
613+
got = func("AURÉLIE")
614+
assert got == "aurÉlie"
615+
616+
617+
@pytest.mark.flaky(retries=2, delay=120)
618+
def test_read_gbq_function_reads_udfs(session, bigquery_client, dataset_id):
611619
dataset_ref = bigquery.DatasetReference.from_string(dataset_id)
612620
arg = bigquery.RoutineArgument(
613621
name="x",
@@ -633,7 +641,8 @@ def test_read_gbq_function_reads_udfs(bigquery_client, dataset_id):
633641
# Create the routine in BigQuery and read it back using read_gbq_function.
634642
bigquery_client.create_routine(routine, exists_ok=True)
635643
square = rf.read_gbq_function(
636-
str(routine.reference), bigquery_client=bigquery_client
644+
str(routine.reference),
645+
session=session,
637646
)
638647

639648
# It should point to the named routine and yield the expected results.
@@ -658,7 +667,9 @@ def test_read_gbq_function_reads_udfs(bigquery_client, dataset_id):
658667

659668

660669
@pytest.mark.flaky(retries=2, delay=120)
661-
def test_read_gbq_function_enforces_explicit_types(bigquery_client, dataset_id):
670+
def test_read_gbq_function_enforces_explicit_types(
671+
session, bigquery_client, dataset_id
672+
):
662673
dataset_ref = bigquery.DatasetReference.from_string(dataset_id)
663674
typed_arg = bigquery.RoutineArgument(
664675
name="x",
@@ -702,18 +713,22 @@ def test_read_gbq_function_enforces_explicit_types(bigquery_client, dataset_id):
702713
bigquery_client.create_routine(neither_type_specified, exists_ok=True)
703714

704715
rf.read_gbq_function(
705-
str(both_types_specified.reference), bigquery_client=bigquery_client
716+
str(both_types_specified.reference),
717+
session=session,
706718
)
707719
rf.read_gbq_function(
708-
str(only_return_type_specified.reference), bigquery_client=bigquery_client
720+
str(only_return_type_specified.reference),
721+
session=session,
709722
)
710723
with pytest.raises(ValueError):
711724
rf.read_gbq_function(
712-
str(only_arg_type_specified.reference), bigquery_client=bigquery_client
725+
str(only_arg_type_specified.reference),
726+
session=session,
713727
)
714728
with pytest.raises(ValueError):
715729
rf.read_gbq_function(
716-
str(neither_type_specified.reference), bigquery_client=bigquery_client
730+
str(neither_type_specified.reference),
731+
session=session,
717732
)
718733

719734

0 commit comments

Comments
 (0)