diff --git a/release/bazel_build.py b/release/bazel_build.py new file mode 100644 index 0000000..239bb35 --- /dev/null +++ b/release/bazel_build.py @@ -0,0 +1,207 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Helper for building CEL Python extensions with bazel for PyPI releases.""" + +import glob +import os +import platform +import re +import shutil +import subprocess +import sys +import setuptools +import setuptools.command.build_ext + + +class BazelExtension(setuptools.Extension): + + def __init__(self, name, target): + super().__init__(name, sources=[]) + self.target = target + + +class BazelBuild(setuptools.command.build_ext.build_ext): + """Custom build_ext command to build extensions with bazel.""" + + def run(self): + # Ensure bazel is available + if shutil.which('bazel') is None: + raise RuntimeError('bazel must be installed to build the extensions.') + + for ext in self.extensions: + self.build_extension(ext) + + def build_extension(self, ext): + # cibuildwheel executes setup.py using the target Python version. + # Thus, we can use the current Python version as the target version for + # the bazel build. + python_version = f'{sys.version_info.major}.{sys.version_info.minor}' + + print(f'Building for target Python version: {python_version}') + + module_bazel_path = os.path.join( + os.path.dirname(os.path.abspath(__file__)), 'MODULE.bazel' + ) + if os.path.exists(module_bazel_path): + with open(module_bazel_path, 'r') as f: + content = f.read() + content = re.sub( + r'python_version\s*=\s*".*"', + f'python_version = "{python_version}"', + content, + ) + with open(module_bazel_path, 'w') as f: + f.write(content) + else: + raise RuntimeError(f'MODULE.bazel not found at {module_bazel_path}') + + dest_path = self.get_ext_fullpath(ext.name) + dest_dir = os.path.dirname(dest_path) + os.makedirs(dest_dir, exist_ok=True) + + # Build with bazel + # Use --compilation_mode=opt for release builds + cmd = [ + 'bazel', + 'build', + ext.target, + '--compilation_mode=opt', + '--symlink_prefix=bazel-', + ] + extra_flags = os.environ.get('CEL_BAZEL_FLAGS') + if extra_flags: + cmd.extend(extra_flags.split()) + if sys.platform == 'win32': + self.platform_config_windows(cmd, python_version) + if sys.platform == 'darwin': + self.platform_config_macos(cmd) + print(f"Building {ext.name} with bazel: {' '.join(cmd)}") + subprocess.check_call(cmd) + + # Determine the output path of the bazel build + # We handle targets like //:py_cel and //ext:ext_math + rel_path = ext.target.lstrip('/').replace(':', '/') + if rel_path.startswith('/'): + rel_path = rel_path[1:] + + # Bazel output directory + bazel_bin = 'bazel-bin' + if not os.path.exists(bazel_bin): + try: + bazel_bin = subprocess.check_output( + ['bazel', 'info', 'bazel-bin', '-c', 'opt'], text=True + ).strip() + except Exception: # pylint: disable=broad-exception-caught + pass + + # Attempt to find the generated shared library + # Suffixes can vary by platform + suffixes = ['.so', '.pyd', '.dylib'] + found = None + + candidate_base = os.path.join(bazel_bin, rel_path) + + for suffix in suffixes: + candidate = candidate_base + suffix + if os.path.exists(candidate): + found = candidate + break + + if not found: + # Fallback check for exact name match + # (less likely for extensions but possible) + if os.path.exists(candidate_base): + found = candidate_base + + if not found: + raise RuntimeError( + f'Could not find bazel output for {ext.target} at {candidate_base}.*' + ) + + print(f'Copying {found} to {dest_path}') + shutil.copyfile(found, dest_path) + + def platform_config_windows(self, cmd, python_version): + """Applies Windows-specific Bazel workarounds for Hermetic Python.""" + cmd.insert(1, '--output_user_root=C:/tmp') + + # 1. Get output base + output_base = subprocess.check_output( + ['bazel', '--output_user_root=C:/tmp', 'info', 'output_base'], text=True + ).strip() + + # 2. Find hermetic python directory + py_ver_underscore = python_version.replace('.', '_') + pattern = os.path.join( + output_base, 'external', f'*python_{py_ver_underscore}_host' + ) + matches = glob.glob(pattern) + if not matches: + print( + 'Warning: Hermetic Python directory not found with pattern:' + f' {pattern}' + ) + return + + py_host_dir = matches[0] + print(f'Found Hermetic Python Directory: {py_host_dir}') + + # 3. Copy libs to a space-free directory + target_dir = r'C:\tmp\python_libs' + os.makedirs(target_dir, exist_ok=True) + + lib_pattern = os.path.join(py_host_dir, 'libs', 'python*.lib') + for lib_file in glob.glob(lib_pattern): + print(f'Copying {lib_file} to {target_dir}') + shutil.copy(lib_file, target_dir) + + dll_pattern = os.path.join(py_host_dir, 'python*.dll') + for dll_file in glob.glob(dll_pattern): + print(f'Copying {dll_file} to {target_dir}') + shutil.copy(dll_file, target_dir) + + # 4. Add link flags to bazel command + cmd.extend(['--linkopt=/LIBPATH:C:\\tmp\\python_libs', '--action_env=PATH']) + + # 5. Add to PATH in current process so bazel can find the DLLs + os.environ['PATH'] = f"{target_dir};{os.environ.get('PATH', '')}" + + def platform_config_macos(self, cmd): + """Applies macOS-specific Bazel configurations.""" + deployment_target = os.environ.get('MACOSX_DEPLOYMENT_TARGET', '10.13') + cmd.extend([ + f'--macos_minimum_os={deployment_target}', + '--cxxopt=-faligned-allocation', + ]) + + archflags = os.environ.get('ARCHFLAGS', '') + if 'x86_64' in archflags: + target_arch = 'x86_64' + elif 'arm64' in archflags: + target_arch = 'arm64' + else: + machine = platform.machine() + if machine in ('AMD64', 'x86_64'): + target_arch = 'x86_64' + elif machine in ('arm64', 'aarch64'): + target_arch = 'arm64' + else: + target_arch = machine + + print(f'Target architecture for macOS: {target_arch}', flush=True) + cmd.append(f'--macos_cpus={target_arch}') + cmd.append(f'--cpu=darwin_{target_arch}') + if target_arch == 'x86_64': + cmd.append('--platforms=//cel_expr_python:macos_x86_64') diff --git a/release/build_wheel.sh b/release/build_wheel.sh index e313dfd..eeeef3f 100755 --- a/release/build_wheel.sh +++ b/release/build_wheel.sh @@ -41,13 +41,13 @@ # - Install the resulting wheel: # # ``` -# pip install --force-reinstall wheelhouse/py_cel-*.whl +# pip install --force-reinstall wheelhouse/cel_expr_python-*.whl # ``` # # - Verify that the wheel is working correctly # # ``` -# python release/py_cel_basic_test.py +# python release/cel_basic_test.py # ``` set -e @@ -69,23 +69,29 @@ if [[ "$OSTYPE" == "darwin"* ]]; then else cp -r "${SRC_DIR}/." . fi -cp release/* . rm -rf cel_expr_python/*_test.py +PYTHON_BIN="python" +if command -v python3 &> /dev/null; then + PYTHON_BIN="python3" +fi + +if [ -z "${CIBWHEEL_BIN}" ]; then + CIBWHEEL_BIN="${PYTHON_BIN} -m cibuildwheel" +fi + +echo "--- Building cel-expr-python wheels ---" +cp release/*.py release/*.toml . + # Substitute $VERSION in pyproject.toml with the value of VERSION. if [[ "$OSTYPE" == "darwin"* ]]; then sed -i '' "s/\$VERSION/${VERSION}/g" pyproject.toml else - python -c "import sys; content = open('pyproject.toml').read(); open('pyproject.toml', 'w').write(content.replace('\$VERSION', sys.argv[1]))" "${VERSION}" -fi - -echo "Running cibuildwheel: ${CIBWHEEL_BIN:-python -m cibuildwheel}" -${CIBWHEEL_BIN:-PYTHON_BIN="python" -if command -v python3 &> /dev/null; then - PYTHON_BIN="python3" + "${PYTHON_BIN}" -c "import sys; content = open('pyproject.toml').read(); open('pyproject.toml', 'w').write(content.replace('\$VERSION', sys.argv[1]))" "${VERSION}" fi -"$PYTHON_BIN" -m cibuildwheel} "$@" +echo "Running cibuildwheel: ${CIBWHEEL_BIN}" +${CIBWHEEL_BIN} "$@" echo "Copying generated wheels to ${SRC_DIR}/wheelhouse" mkdir -p "${SRC_DIR}"/wheelhouse diff --git a/release/cel_basic_test.py b/release/cel_basic_test.py index 2cc1c14..99fa652 100644 --- a/release/cel_basic_test.py +++ b/release/cel_basic_test.py @@ -14,18 +14,173 @@ """Basic tests for cel-python.""" +import importlib.metadata import unittest + from cel_expr_python import cel +from cel_expr_python.ext import ext_bindings +from cel_expr_python.ext import ext_encoders +from cel_expr_python.ext import ext_lists from cel_expr_python.ext import ext_math +from cel_expr_python.ext import ext_optional +from cel_expr_python.ext import ext_proto +from cel_expr_python.ext import ext_strings class BasicTest(unittest.TestCase): - def test_basic_test(self): - env = cel.NewEnv(extensions=[ext_math.ExtMath()]) - expr = env.compile("math.sqrt(2)") - res = expr.eval() - self.assertAlmostEqual(res.value(), 1.4142135623730951) + def test_basic_compile_and_eval(self): + env = cel.NewEnv(variables={"x": cel.Type.INT, "name": cel.Type.STRING}) + + expr = env.compile("1 + 2 == 3") + self.assertTrue(expr.eval().value()) + + expr = env.compile("x * 2 + 1") + self.assertEqual(expr.eval(data={"x": 20}).value(), 41) + + expr = env.compile("'Hello, ' + name + '!'") + act = env.Activation({"name": "CEL"}) + self.assertEqual(expr.eval(act).value(), "Hello, CEL!") + + def test_standard_extensions(self): + env = cel.NewEnv( + extensions=[ + ext_bindings.ExtBindings(), + ext_encoders.ExtEncoders(), + ext_lists.ExtLists(), + ext_math.ExtMath(), + ext_optional.ExtOptional(), + ext_proto.ExtProto(), + ext_strings.ExtStrings(), + ] + ) + # Test bindings + expr = env.compile("cel.bind(x, 2, x + 3)") + self.assertEqual(expr.eval().value(), 5) + + # Test encoders + expr = env.compile("base64.encode(b'hello')") + self.assertEqual(expr.eval().value(), "aGVsbG8=") + + # Test lists + expr = env.compile("lists.range(3)") + self.assertEqual(expr.eval().value(), [0, 1, 2]) + + # Test math + expr = env.compile("math.greatest(1, 5, 3)") + self.assertEqual(expr.eval().value(), 5) + + # Test optional + expr = env.compile("optional.of('test').orValue('fallback')") + self.assertEqual(expr.eval().value(), "test") + + # Test strings + expr = env.compile("strings.quote('abc')") + self.assertEqual(expr.eval().value(), '"abc"') + + def test_custom_extension(self): + ext = cel.CelExtension( + "custom_ext", + functions=[ + cel.FunctionDecl( + "greet", + [ + cel.Overload( + "greet_string", + return_type=cel.Type.STRING, + parameters=[cel.Type.STRING], + is_member=False, + impl=lambda name: f"Hello, {name}!", + ) + ], + ), + cel.FunctionDecl( + "greet_late", + [ + cel.Overload( + "greet_late_string", + return_type=cel.Type.STRING, + parameters=[cel.Type.STRING], + is_member=False, + ) + ], + ), + ], + ) + env = cel.NewEnv(extensions=[ext]) + expr = env.compile("greet('world') + ' ' + greet_late('CEL')") + act = env.Activation( + {}, + functions=[ + cel.Function( + "greet_late", + [cel.Type.STRING], + False, + lambda name: f"Hi, {name}!", + return_type=cel.Type.STRING, + ) + ], + ) + res = expr.eval(act) + self.assertEqual(res.value(), "Hello, world! Hi, CEL!") + + def test_wheel_package_contents(self): + dist = importlib.metadata.distribution("cel-expr-python") + top_level = dist.read_text("top_level.txt") + if top_level is not None: + self.assertEqual(top_level.strip().splitlines(), ["cel_expr_python"]) + + self.assertIsNotNone(dist.files) + files = [f.as_posix().replace("\\", "/") for f in dist.files] + + # Verify all expected .pyi stubs are packaged + expected_stubs = [ + "cel_expr_python/cel.pyi", + "cel_expr_python/ext/ext_bindings.pyi", + "cel_expr_python/ext/ext_encoders.pyi", + "cel_expr_python/ext/ext_lists.pyi", + "cel_expr_python/ext/ext_math.pyi", + "cel_expr_python/ext/ext_optional.pyi", + "cel_expr_python/ext/ext_proto.pyi", + "cel_expr_python/ext/ext_strings.pyi", + ] + for stub in expected_stubs: + self.assertIn(stub, files) + + # Verify all compiled C++ extension modules are packaged + expected_modules = [ + "cel_expr_python/cel.", + "cel_expr_python/ext/ext_bindings.", + "cel_expr_python/ext/ext_encoders.", + "cel_expr_python/ext/ext_lists.", + "cel_expr_python/ext/ext_math.", + "cel_expr_python/ext/ext_optional.", + "cel_expr_python/ext/ext_proto.", + "cel_expr_python/ext/ext_strings.", + ] + binary_suffixes = (".so", ".pyd", ".dylib") + for module_prefix in expected_modules: + found = any( + f.startswith(module_prefix) and f.endswith(binary_suffixes) + for f in files + ) + self.assertTrue(found, f"Missing binary module for {module_prefix}") + + unexpected = [ + f + for f in files + if not f.startswith( + ("cel_expr_python/", "cel_expr_python-", "cel_expr_python.libs/") + ) + or f.endswith("_test.py") + or "/testdata/" in f + ] + self.assertEqual( + unexpected, + [], + f"Unexpected files packaged in cel-expr-python wheel:" + f" {unexpected[:20]}", + ) if __name__ == "__main__": diff --git a/release/kokoro/release_linux.sh b/release/kokoro/release_linux.sh index 119ec14..08bfe0c 100755 --- a/release/kokoro/release_linux.sh +++ b/release/kokoro/release_linux.sh @@ -242,7 +242,7 @@ mkdir -p "${TMPDIR}" pushd "${TMP_DIR}" cp -r "${SRC_DIR}"/{*,.*} . 2>/dev/null || true -cp -r "${SRC_DIR}"/release/* . 2>/dev/null || true +cp "${SRC_DIR}"/release/*.py "${SRC_DIR}"/release/*.toml . 2>/dev/null || true rm -rf cel_expr_python/*_test.py echo "Downloading bazelisk on host..." diff --git a/release/kokoro/release_macos.sh b/release/kokoro/release_macos.sh index dd5f501..e36b555 100644 --- a/release/kokoro/release_macos.sh +++ b/release/kokoro/release_macos.sh @@ -57,7 +57,7 @@ trap 'echo "Cleaning up temporary directories: ${REPO_DIR} ${TMP_DIR}"; rm -rf " pushd "${TMP_DIR}" cp -r "${SRC_DIR}"/{*,.*} . 2>/dev/null || true -cp "${SRC_DIR}"/release/* . 2>/dev/null || true +cp "${SRC_DIR}"/release/*.py "${SRC_DIR}"/release/*.toml . 2>/dev/null || true rm -rf cel_expr_python/*_test.py # Check if pyproject.toml exists before running sed @@ -83,4 +83,4 @@ fi popd -echo "cel-expr-python ${VERSION} built and uploaded for release by OSS Exit Gate." \ No newline at end of file +echo "cel-expr-python ${VERSION} built and uploaded for release by OSS Exit Gate." diff --git a/release/kokoro/release_windows.bat b/release/kokoro/release_windows.bat index 8b6f003..2931936 100644 --- a/release/kokoro/release_windows.bat +++ b/release/kokoro/release_windows.bat @@ -113,9 +113,16 @@ if !ERRORLEVEL! NEQ 0 ( goto cleanup ) -xcopy /Y "%SRC_DIR%\release\*.*" . +xcopy /Y "%SRC_DIR%\release\*.py" . if !ERRORLEVEL! NEQ 0 ( - echo Failed to copy release configs! + echo Failed to copy release python files! + set "RELEASE_STATUS=1" + popd + goto cleanup +) +xcopy /Y "%SRC_DIR%\release\*.toml" . +if !ERRORLEVEL! NEQ 0 ( + echo Failed to copy release toml files! set "RELEASE_STATUS=1" popd goto cleanup diff --git a/release/pyproject.toml b/release/pyproject.toml index c90f404..3b54d9e 100644 --- a/release/pyproject.toml +++ b/release/pyproject.toml @@ -35,7 +35,14 @@ Codelab = "https://github.com/cel-expr/cel-python/blob/main/codelab/index.lab.md [tool.setuptools.packages.find] where = ["."] -exclude = ["codelab*", "conformance*", "custom_ext*", "release*", "testing*", "wheelhouse*"] +include = ["cel_expr_python", "cel_expr_python.ext*"] +exclude = ["*testdata*"] + +[tool.setuptools.package-data] +"*" = ["*.pyi"] + +[tool.setuptools.exclude-package-data] +"*" = ["*_test.py"] [tool.cibuildwheel] build = "cp310-* cp311-* cp312-* cp313-* cp314-*" @@ -59,7 +66,7 @@ before-all = "git config --global http.sslVerify false && echo 'Installing bazel [tool.cibuildwheel.macos] archs = ["x86_64", "arm64"] -before-all = "echo 'Installing bazelisk'; brew install bazelisk" +before-all = "command -v bazelisk >/dev/null || (echo 'Installing bazelisk' && brew install bazelisk)" environment = { MACOSX_DEPLOYMENT_TARGET = "10.13" } [tool.cibuildwheel.windows] diff --git a/release/setup.py b/release/setup.py index 16babad..e38ff9f 100644 --- a/release/setup.py +++ b/release/setup.py @@ -12,222 +12,48 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Helper for building py_cel with bazel for PyPI releases.""" +"""Setuptools build configuration for the cel-expr-python PyPI wheel.""" -import glob -import os -import platform -import re -import shutil -import subprocess -import sys +import bazel_build import setuptools -import setuptools.command.build_ext - - -class BazelExtension(setuptools.Extension): - - def __init__(self, name, target): - super().__init__(name, sources=[]) - self.target = target - - -class BazelBuild(setuptools.command.build_ext.build_ext): - """Custom build_ext command to build extensions with bazel.""" - - def run(self): - # Ensure bazel is available - if shutil.which('bazel') is None: - raise RuntimeError('bazel must be installed to build the extensions.') - - for ext in self.extensions: - self.build_extension(ext) - - def build_extension(self, ext): - # cibuildwheel executes setup.py using the target Python version. - # Thus, we can use the current Python version as the target version for - # the bazel build. - python_version = f'{sys.version_info.major}.{sys.version_info.minor}' - - print(f'Building for target Python version: {python_version}') - - module_bazel_path = os.path.join(os.path.dirname(__file__), 'MODULE.bazel') - if os.path.exists(module_bazel_path): - with open(module_bazel_path, 'r') as f: - content = f.read() - content = re.sub( - r'python_version\s*=\s*".*"', - f'python_version = "{python_version}"', - content, - ) - with open(module_bazel_path, 'w') as f: - f.write(content) - else: - raise RuntimeError(f'MODULE.bazel not found at {module_bazel_path}') - - dest_path = self.get_ext_fullpath(ext.name) - dest_dir = os.path.dirname(dest_path) - os.makedirs(dest_dir, exist_ok=True) - - # Build with bazel - # Use --compilation_mode=opt for release builds - cmd = ['bazel', 'build', ext.target, '--compilation_mode=opt'] - extra_flags = os.environ.get('CEL_BAZEL_FLAGS') - if extra_flags: - cmd.extend(extra_flags.split()) - if sys.platform == 'win32': - self.platform_config_windows(cmd, python_version) - if sys.platform == 'darwin': - self.platform_config_macos(cmd) - print(f"Building {ext.name} with bazel: {' '.join(cmd)}") - subprocess.check_call(cmd) - - # Determine the output path of the bazel build - # We handle targets like //:py_cel and //ext:ext_math - rel_path = ext.target.lstrip('/').replace(':', '/') - if rel_path.startswith('/'): - rel_path = rel_path[1:] - - # Bazel output directory - bazel_bin = 'bazel-bin' - - # Attempt to find the generated shared library - # Suffixes can vary by platform - suffixes = ['.so', '.pyd', '.dylib'] - found = None - - candidate_base = os.path.join(bazel_bin, rel_path) - - for suffix in suffixes: - candidate = candidate_base + suffix - if os.path.exists(candidate): - found = candidate - break - - if not found: - # Fallback check for exact name match - # (less likely for extensions but possible) - if os.path.exists(candidate_base): - found = candidate_base - - if not found: - raise RuntimeError( - f'Could not find bazel output for {ext.target} at {candidate_base}.*' - ) - - print(f'Copying {found} to {dest_path}') - shutil.copyfile(found, dest_path) - - def platform_config_windows(self, cmd, python_version): - """Applies Windows-specific Bazel workarounds for Hermetic Python.""" - cmd.insert(1, '--output_user_root=C:/tmp') - - # 1. Get output base - output_base = subprocess.check_output( - ['bazel', '--output_user_root=C:/tmp', 'info', 'output_base'], text=True - ).strip() - - # 2. Find hermetic python directory - py_ver_underscore = python_version.replace('.', '_') - pattern = os.path.join( - output_base, 'external', f'*python_{py_ver_underscore}_host' - ) - matches = glob.glob(pattern) - if not matches: - print( - 'Warning: Hermetic Python directory not found with pattern:' - f' {pattern}' - ) - return - - py_host_dir = matches[0] - print(f'Found Hermetic Python Directory: {py_host_dir}') - - # 3. Copy libs to a space-free directory - target_dir = r'C:\tmp\python_libs' - os.makedirs(target_dir, exist_ok=True) - - lib_pattern = os.path.join(py_host_dir, 'libs', 'python*.lib') - for lib_file in glob.glob(lib_pattern): - print(f'Copying {lib_file} to {target_dir}') - shutil.copy(lib_file, target_dir) - - dll_pattern = os.path.join(py_host_dir, 'python*.dll') - for dll_file in glob.glob(dll_pattern): - print(f'Copying {dll_file} to {target_dir}') - shutil.copy(dll_file, target_dir) - - # 4. Add link flags to bazel command - cmd.extend(['--linkopt=/LIBPATH:C:\\tmp\\python_libs', '--action_env=PATH']) - - # 5. Add to PATH in current process so bazel can find the DLLs - os.environ['PATH'] = f"{target_dir};{os.environ.get('PATH', '')}" - - def platform_config_macos(self, cmd): - """Applies macOS-specific Bazel configurations.""" - deployment_target = os.environ.get('MACOSX_DEPLOYMENT_TARGET', '10.13') - cmd.extend([ - f'--macos_minimum_os={deployment_target}', - '--cxxopt=-faligned-allocation', - ]) - - archflags = os.environ.get('ARCHFLAGS', '') - if 'x86_64' in archflags: - target_arch = 'x86_64' - elif 'arm64' in archflags: - target_arch = 'arm64' - else: - machine = platform.machine() - if machine in ('AMD64', 'x86_64'): - target_arch = 'x86_64' - elif machine in ('arm64', 'aarch64'): - target_arch = 'arm64' - else: - target_arch = machine - - print(f'Target architecture for macOS: {target_arch}', flush=True) - cmd.append(f'--macos_cpus={target_arch}') - cmd.append(f'--cpu=darwin_{target_arch}') - if target_arch == 'x86_64': - cmd.append('--platforms=//cel_expr_python:macos_x86_64') setuptools.setup( name='cel-expr-python', ext_modules=[ - BazelExtension( + bazel_build.BazelExtension( 'cel_expr_python.cel', '//cel_expr_python:cel', ), - BazelExtension( + bazel_build.BazelExtension( 'cel_expr_python.ext.ext_bindings', '//cel_expr_python/ext:ext_bindings', ), - BazelExtension( + bazel_build.BazelExtension( 'cel_expr_python.ext.ext_encoders', '//cel_expr_python/ext:ext_encoders', ), - BazelExtension( + bazel_build.BazelExtension( 'cel_expr_python.ext.ext_lists', '//cel_expr_python/ext:ext_lists', ), - BazelExtension( + bazel_build.BazelExtension( 'cel_expr_python.ext.ext_math', '//cel_expr_python/ext:ext_math', ), - BazelExtension( + bazel_build.BazelExtension( 'cel_expr_python.ext.ext_optional', '//cel_expr_python/ext:ext_optional', ), - BazelExtension( + bazel_build.BazelExtension( 'cel_expr_python.ext.ext_proto', '//cel_expr_python/ext:ext_proto', ), - BazelExtension( + bazel_build.BazelExtension( 'cel_expr_python.ext.ext_strings', '//cel_expr_python/ext:ext_strings', ), ], - cmdclass={'build_ext': BazelBuild}, + cmdclass={'build_ext': bazel_build.BazelBuild}, zip_safe=False, )