Skip to content
This repository was archived by the owner on Apr 1, 2026. It is now read-only.

Commit 1eba9fe

Browse files
simplify udf wrapper guarantees
1 parent ac0f9a1 commit 1eba9fe

File tree

5 files changed

+29
-33
lines changed

5 files changed

+29
-33
lines changed

bigframes/dataframe.py

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2843,7 +2843,7 @@ def _apply_callable(self, condition):
28432843
"""Executes the possible callable condition as needed."""
28442844
if callable(condition):
28452845
# When it's a bigframes function.
2846-
if hasattr(condition, "bigframes_bigquery_function"):
2846+
if isinstance(condition, bigframes.functions.Udf):
28472847
return self.apply(condition, axis=1)
28482848

28492849
# When it's a plain Python function.
@@ -4662,13 +4662,7 @@ def _prepare_export(
46624662
return array_value, id_overrides
46634663

46644664
def map(self, func, na_action: Optional[str] = None) -> DataFrame:
4665-
if not isinstance(
4666-
func,
4667-
(
4668-
bigframes.functions.BigqueryCallableRoutine,
4669-
bigframes.functions.UdfRoutine,
4670-
),
4671-
):
4665+
if not isinstance(func, bigframes.functions.Udf):
46724666
raise TypeError("the first argument must be callable")
46734667

46744668
if na_action not in {None, "ignore"}:
@@ -4692,13 +4686,7 @@ def apply(self, func, *, axis=0, args: typing.Tuple = (), **kwargs):
46924686
)
46934687
warnings.warn(msg, category=bfe.FunctionAxisOnePreviewWarning)
46944688

4695-
if not isinstance(
4696-
func,
4697-
(
4698-
bigframes.functions.BigqueryCallableRoutine,
4699-
bigframes.functions.UdfRoutine,
4700-
),
4701-
):
4689+
if not isinstance(func, bigframes.functions.Udf):
47024690
raise ValueError(
47034691
"For axis=1 a BigFrames BigQuery function must be used."
47044692
)
@@ -4832,7 +4820,7 @@ def apply(self, func, *, axis=0, args: typing.Tuple = (), **kwargs):
48324820

48334821
# At this point column-wise or element-wise bigquery function operation will
48344822
# be performed (not supported).
4835-
if hasattr(func, "bigframes_bigquery_function"):
4823+
if isinstance(func, bigframes.functions.Udf):
48364824
raise formatter.create_exception_with_feedback_link(
48374825
NotImplementedError,
48384826
"BigFrames DataFrame '.apply()' does not support BigFrames "

bigframes/functions/__init__.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,8 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14-
from bigframes.functions.function import BigqueryCallableRoutine, UdfRoutine
14+
from bigframes.functions.function import Udf
1515

1616
__all__ = [
17-
"BigqueryCallableRoutine",
18-
"UdfRoutine",
17+
"Udf",
1918
]

bigframes/functions/_function_session.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -851,14 +851,19 @@ def wrapper(func):
851851
signature=udf_sig,
852852
)
853853

854-
if not name:
855-
self._update_temp_artifacts(full_rf_name, "")
856-
857854
if udf_sig.is_row_processor:
858855
msg = bfe.format_message("input_types=Series is in preview.")
859856
warnings.warn(msg, stacklevel=1, category=bfe.PreviewWarning)
860857

861-
return bq_functions.UdfRoutine(func=func, _udf_def=udf_definition)
858+
if not name: # session-managed resource
859+
self._update_temp_artifacts(full_rf_name, "")
860+
return bq_functions.UdfRoutine(func=func, _udf_def=udf_definition)
861+
862+
# user-managed permanent resource
863+
else:
864+
return bq_functions.BigqueryCallableRoutine(
865+
udf_definition, session, local_func=func, is_managed=True
866+
)
862867

863868
return wrapper
864869

bigframes/functions/function.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from __future__ import annotations
1616

1717
import logging
18-
from typing import Callable, Optional, TYPE_CHECKING
18+
from typing import Callable, Optional, Protocol, runtime_checkable, TYPE_CHECKING
1919

2020
if TYPE_CHECKING:
2121
from bigframes.session import Session
@@ -31,6 +31,9 @@
3131
from bigframes.functions import _function_session as bff_session
3232
from bigframes.functions import function_typing, udf_def
3333

34+
if TYPE_CHECKING:
35+
import bigframes.core.col
36+
3437
logger = logging.getLogger(__name__)
3538

3639

@@ -154,6 +157,13 @@ def read_gbq_function(
154157
return _try_import_routine(routine, session)
155158

156159

160+
@runtime_checkable
161+
class Udf(Protocol):
162+
@property
163+
def udf_def(self) -> udf_def.BigqueryUdf:
164+
...
165+
166+
157167
class BigqueryCallableRoutine:
158168
"""
159169
A reference to a routine in the context of a session.

bigframes/series.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,7 +1558,7 @@ def _apply_callable(self, condition):
15581558
""" "Executes the possible callable condition as needed."""
15591559
if callable(condition):
15601560
# When it's a bigframes function.
1561-
if hasattr(condition, "bigframes_bigquery_function"):
1561+
if isinstance(condition, bigframes.functions.Udf):
15621562
return self.apply(condition)
15631563
# When it's a plain Python function.
15641564
else:
@@ -2031,10 +2031,7 @@ def apply(
20312031

20322032
if isinstance(
20332033
func,
2034-
(
2035-
bigframes.functions.BigqueryCallableRoutine,
2036-
bigframes.functions.UdfRoutine,
2037-
),
2034+
(bigframes.functions.Udf,),
20382035
):
20392036
# We are working with bigquery function at this point
20402037
if args:
@@ -2098,10 +2095,7 @@ def combine(
20982095

20992096
if isinstance(
21002097
func,
2101-
(
2102-
bigframes.functions.BigqueryCallableRoutine,
2103-
bigframes.functions.UdfRoutine,
2104-
),
2098+
bigframes.functions.Udf,
21052099
):
21062100
result_series = self._apply_binary_op(
21072101
other, ops.BinaryRemoteFunctionOp(function_def=func.udf_def)

0 commit comments

Comments
 (0)