diff --git a/bigframes/functions/_function_session.py b/bigframes/functions/_function_session.py index 0ae674b97d..a66f619cf9 100644 --- a/bigframes/functions/_function_session.py +++ b/bigframes/functions/_function_session.py @@ -47,7 +47,6 @@ ) from bigframes import clients -from bigframes import version as bigframes_version import bigframes.core.compile.ibis_types import bigframes.exceptions as bfe import bigframes.series as bf_series @@ -458,16 +457,13 @@ def remote_function( msg = bfe.format_message( "You have not explicitly set a user-managed `cloud_function_service_account`. " "Using the default Compute Engine service account. " - "To use Bigframes 2.0, please explicitly set `cloud_function_service_account` " + "In BigFrames 2.0 onwards, you would have to explicitly set `cloud_function_service_account` " 'either to a user-managed service account (preferred) or to `"default"` ' - "to use the Compute Engine service account (discouraged). " + "to use the default Compute Engine service account (discouraged). " "See, https://cloud.google.com/functions/docs/securing/function-identity." ) - if ( - bigframes_version.__version__.startswith("1.") - and cloud_function_service_account is None - ): + if cloud_function_service_account is None: warnings.warn(msg, stacklevel=2, category=FutureWarning) if cloud_function_service_account == "default": diff --git a/noxfile.py b/noxfile.py index b95e58f4ef..a08ef27781 100644 --- a/noxfile.py +++ b/noxfile.py @@ -59,6 +59,12 @@ DEFAULT_PYTHON_VERSION = "3.10" +# Cloud Run Functions supports Python versions up to 3.12 +# https://cloud.google.com/run/docs/runtimes/python +# Managed Python UDF is supported only in Python 3.11 +# Let's set the E2E tests version to 3.11 to cover most code paths. +E2E_TEST_PYTHON_VERSION = "3.11" + UNIT_TEST_PYTHON_VERSIONS = ["3.9", "3.10", "3.11", "3.12", "3.13"] UNIT_TEST_STANDARD_DEPENDENCIES = [ "mock", @@ -418,7 +424,7 @@ def doctest(session: nox.sessions.Session): ) -@nox.session(python=SYSTEM_TEST_PYTHON_VERSIONS[-1]) +@nox.session(python=E2E_TEST_PYTHON_VERSION) def e2e(session: nox.sessions.Session): """Run the large tests in system test suite.""" run_system( diff --git a/tests/system/large/functions/test_managed_function.py b/tests/system/large/functions/test_managed_function.py index 503720edcc..efab338861 100644 --- a/tests/system/large/functions/test_managed_function.py +++ b/tests/system/large/functions/test_managed_function.py @@ -25,10 +25,6 @@ bpd.options.experiments.udf = True -@pytest.mark.skipif( - get_python_version() not in bff_session._MANAGED_FUNC_PYTHON_VERSIONS, - reason=f"Supported version: {bff_session._MANAGED_FUNC_PYTHON_VERSIONS}", -) def test_managed_function_multiply_with_ibis( session, scalars_table_id, @@ -80,10 +76,6 @@ def multiply(x, y): cleanup_function_assets(multiply, bigquery_client) -@pytest.mark.skipif( - get_python_version() not in bff_session._MANAGED_FUNC_PYTHON_VERSIONS, - reason=f"Supported version: {bff_session._MANAGED_FUNC_PYTHON_VERSIONS}", -) def test_managed_function_stringify_with_ibis( session, scalars_table_id, @@ -132,10 +124,6 @@ def stringify(x): ) -@pytest.mark.skipif( - get_python_version() not in bff_session._MANAGED_FUNC_PYTHON_VERSIONS, - reason=f"Supported version: {bff_session._MANAGED_FUNC_PYTHON_VERSIONS}", -) def test_managed_function_binop(session, scalars_dfs, dataset_id): try: diff --git a/tests/system/large/functions/test_remote_function.py b/tests/system/large/functions/test_remote_function.py index 65bf20b966..0d7f888306 100644 --- a/tests/system/large/functions/test_remote_function.py +++ b/tests/system/large/functions/test_remote_function.py @@ -17,10 +17,11 @@ import inspect import math # must keep this at top level to test udf referring global import import os.path +import re import shutil -import sys import tempfile import textwrap +import typing import warnings import google.api_core.exceptions @@ -50,12 +51,6 @@ _team_euler = "Team Euler" -pytestmark = pytest.mark.skipif( - sys.version_info >= (3, 13), - reason="Runtime 'python313' is not supported yet. Skip for now.", -) - - def make_uniq_udf(udf): """Transform a udf to another with same behavior but a unique name. Use this to test remote functions with reuse=True, in which case parallel @@ -1323,14 +1318,38 @@ def square_num(x): ) -def test_remote_function_warns_default_cloud_function_service_account(): - project = "bigframes-dev-perf" - rf_session = bigframes.Session(context=bigframes.BigQueryOptions(project=project)) - - with pytest.warns(FutureWarning, match="You have not explicitly set a"): - rf_session.remote_function( - cloud_function_service_account=None, # Explicitly omit service account. - ) +@pytest.mark.parametrize( + ("remote_function_args"), + [ + pytest.param( + {}, + id="no-set", + ), + pytest.param( + {"cloud_function_service_account": None}, + id="set-none", + ), + ], +) +def test_remote_function_warns_default_cloud_function_service_account( + session, remote_function_args +): + with pytest.warns(FutureWarning) as record: + session.remote_function(**remote_function_args) + + len( + [ + warn + for warn in record + if re.search( + ( + "You have not explicitly set a user-managed.*Using the default Compute Engine.*service account" + ), + typing.cast(FutureWarning, warn.message).args[0], + re.DOTALL, + ) + ] + ) == 1 @pytest.mark.flaky(retries=2, delay=120) @@ -2319,36 +2338,40 @@ def generate_stats(row: pandas.Series) -> list[int]: @pytest.mark.parametrize( - ("ingress_settings_args", "effective_ingress_settings", "expected_warning"), + ( + "ingress_settings_args", + "effective_ingress_settings", + "expect_default_ingress_setting_warning", + ), [ pytest.param( {}, functions_v2.ServiceConfig.IngressSettings.ALLOW_ALL, - FutureWarning, + True, id="no-set", ), pytest.param( {"cloud_function_ingress_settings": None}, functions_v2.ServiceConfig.IngressSettings.ALLOW_ALL, - FutureWarning, + True, id="set-none", ), pytest.param( {"cloud_function_ingress_settings": "all"}, functions_v2.ServiceConfig.IngressSettings.ALLOW_ALL, - None, + False, id="set-all", ), pytest.param( {"cloud_function_ingress_settings": "internal-only"}, functions_v2.ServiceConfig.IngressSettings.ALLOW_INTERNAL_ONLY, - None, + False, id="set-internal-only", ), pytest.param( {"cloud_function_ingress_settings": "internal-and-gclb"}, functions_v2.ServiceConfig.IngressSettings.ALLOW_INTERNAL_AND_GCLB, - None, + False, id="set-internal-and-gclb", ), ], @@ -2359,11 +2382,11 @@ def test_remote_function_ingress_settings( scalars_dfs, ingress_settings_args, effective_ingress_settings, - expected_warning, + expect_default_ingress_setting_warning, ): try: # Verify the function raises the expected security warning message. - with warnings.catch_warnings(record=True) as w: + with warnings.catch_warnings(record=True) as record: def square(x: int) -> int: return x * x @@ -2372,11 +2395,18 @@ def square(x: int) -> int: reuse=False, **ingress_settings_args )(square) - if expected_warning is not None: - assert issubclass(w[0].category, FutureWarning) - assert "Consider using 'internal-only' for enhanced security." in str( - w[0].message - ) + default_ingress_setting_warnings = [ + warn + for warn in record + if isinstance(warn.message, FutureWarning) + and "`cloud_function_ingress_settings` are set to 'all' by default" + in warn.message.args[0] + and "will change to 'internal-only' for enhanced security in future" + in warn.message.args[0] + ] + assert len(default_ingress_setting_warnings) == ( + 1 if expect_default_ingress_setting_warning else 0 + ) # Assert that the GCF is created with the intended maximum timeout gcf = session.cloudfunctionsclient.get_function(