Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

--incompatible_default_to_explicit_init_py causes side effect of circular import #22167

Open
Ryang20718 opened this issue Apr 29, 2024 · 0 comments
Labels

Comments

@Ryang20718
Copy link

Description of the bug:

adding the following line to bazelrc

common --incompatible_default_to_explicit_init_py

Will result in circular import errors

ImportError: cannot import name 'S2Pointcloud' from partially initialized module 's2pointcloud' (most likely due to a circular import)

On bazel 6.5.0, we didn't have --incompatible_default_to_explicit_init_py this flag (rules_python 0.31.0)
After setting this flag --incompatible_default_to_explicit_init_py, we have this circular import issue now with any bazel test/run cmd

Which category does this issue belong to?

Python Rules

What's the simplest, easiest way to reproduce this bug? Please provide a minimal example if possible.

rust_python directory

rules/pyo3.bzl
load("@bazel_skylib//rules:copy_file.bzl", "copy_file")
load("@rules_python//python:defs.bzl", "py_library")
load("@rules_rust//rust:defs.bzl", "rust_library")

def pyo3_extension(
        name,
        crate_name = None,
        py_deps = [],
        visibility = None,
        imports = [],
        testonly = False,
        **rust_library_kwargs):

    crate_name = crate_name or name
    tags = rust_library_kwargs.pop("tags", [])
    rustc_flags = _PYO3_RUSTC_FLAGS + rust_library_kwargs.pop("rustc_flags", [])

    rust_shared_library(
        name = name + "_rust_library",
        crate_name = crate_name,
        platform = "//:pyo3_extension",
        rustc_flags = rustc_flags,
        tags = tags + ["pyo3_extension"],
        testonly = testonly,
        visibility = ["//visibility:private"],
        **rust_library_kwargs
    )

    copy_file(
        name = name + "_so",
        src = ":" + name + "_rust_library",
        out = crate_name + ".so",
        allow_symlink = True,
        testonly = testonly,
        visibility = ["//visibility:private"],
    )

    py_library(
        name = name,
        srcs = [],
        data = [
            ":" + name + "_so",
            ":" + name + "_rust_library",
        ],
        deps = py_deps,
        visibility = visibility,
        imports = imports,
        testonly = testonly,
        tags = tags,
    )
BUILD
load("//rules:pyo3.bzl", "pyo3_extension")
load("@rules_python//python:defs.bzl", "py_test", "py_library")

pyo3_extension(
    name = "rust_python",
    srcs = ["rust_python.rs"],
    data = ["__init__.pyi", "__init__.py"],
    deps = [
        "@crate_index//:pyo3",
    ],
)

py_library(
    name = "rust_python_test_py",
    srcs = ["rust_python_test.py"],
    testonly = True,
    deps = [
        ":rust_python",
        "//third_party:bazel_py", # same as https://github.com/bazelbuild/bazel/commits/faf3b6e0281749cd315ebba45a70ea46dc16c26b/tools/python/runfiles/runfiles.py
    ],
)

py_test(
    name = "rust_python_python_test",
    srcs = [":rust_python_test_py"],
    main = "rust_python_test.py",
)

rust_python.rs
use pyo3::{
    prelude::*,
};


#[pymodule]
fn rust_python(py: Python, m: &PyModule) -> PyResult<()> {
    /// Formats the sum of two numbers as string.
    /// Sets the function signature visible in Python tooling (not required).
    #[pyfn(m, text_signature = "a, b, /")]
    fn sum_two_as_string(a: usize, b: usize) -> String {
        (a + b).to_string()
    }
    Ok(())
}
rust_python_test.py
from . import rust_python

def test_sum_as_string():
    """Test exposing rust functions as python modules"""
    assert rust_python.sum_two_as_string(5, 20) == "25"
    
test_sum_as_string()

__init__.py
import os
import sys
from pathlib import Path
from contextlib import contextmanager
from tools.python import runfiles

_runfiles = runfiles.Create()

def resource_location(filename: str):
    path = _runfiles.Rlocation(os.path.join("<workspace name>", filename))
    return path

@contextmanager
def import_path(path: str, *, expected_filename="__init__.py"):
    """Push bazel resource location at the front of the python import path"""
    expected_file_path = resource_location(os.path.join(path, expected_filename))
    desired_import_path = Path(expected_file_path).parents[expected_filename.count(os.path.sep)]
    sys.path.insert(0, str(desired_import_path))
    try:
        yield
    finally:
        sys.path.pop(0)


with import_path("s2pointcloud/", expected_filename="s2pointcloud.so"):
    from s2pointcloud import S2Pointcloud

Which operating system are you running Bazel on?

ubuntu 20.04

What is the output of bazel info release?

release 6.5.0

If bazel info release returns development version or (@non-git), tell us how you built Bazel.

No response

What's the output of git remote get-url origin; git rev-parse HEAD ?

No response

Is this a regression? If yes, please try to identify the Bazel commit where the bug was introduced.

I can repro this error with --incompatible_default_to_explicit_init_py this flag set on bazel test

Have you found anything relevant by searching the web?

n/a

Any other information, logs, or outputs that you want to share?

No response

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants