Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -59,6 +59,10 @@ Unreleased changes template.
* (pypi) The `ppc64le` is now pointing to the right target in the `platforms` package.
* (gazelle) No longer incorrectly merge `py_binary` targets during partial updates in
`file` generation mode. Fixed in [#2619](https://github.com/bazelbuild/rules_python/pull/2619).
* (bzlmod) Running as root is no longer an error. `ignore_root_user_error=True`
is now the default. Note that running as root may still cause spurious
Bazel cache invalidation
([#1169](https://github.com/bazelbuild/rules_python/issues/1169)).

{#v0-0-0-added}
### Added
Expand Down
39 changes: 16 additions & 23 deletions python/private/python.bzl
Original file line numberDiff line numberDiff line change
Expand Up@@ -72,9 +72,9 @@ def parse_modules(*, module_ctx, _fail = fail):
logger = repo_utils.logger(module_ctx, "python")

# if the root module does not register any toolchain then the
# ignore_root_user_error takes its default value: False
# ignore_root_user_error takes its default value: True
if not module_ctx.modules[0].tags.toolchain:
ignore_root_user_error = False
ignore_root_user_error = True

config = _get_toolchain_config(modules = module_ctx.modules, _fail = _fail)

Expand DownExpand Up@@ -559,7 +559,7 @@ def _create_toolchain_attrs_struct(*, tag = None, python_version = None, toolcha
is_default = is_default,
python_version = python_version if python_version else tag.python_version,
configure_coverage_tool = getattr(tag, "configure_coverage_tool", False),
ignore_root_user_error = getattr(tag, "ignore_root_user_error", False),
ignore_root_user_error = getattr(tag, "ignore_root_user_error", True),
)

def _get_bazel_version_specific_kwargs():
Expand DownExpand Up@@ -636,16 +636,18 @@ Then the python interpreter will be available as `my_python_name`.
doc = "Whether or not to configure the default coverage tool provided by `rules_python` for the compatible toolchains.",
),
"ignore_root_user_error": attr.bool(
default = False,
default = True,
doc = """\
If `False`, the Python runtime installation will be made read only. This improves
the ability for Bazel to cache it, but prevents the interpreter from creating
`.pyc` files for the standard library dynamically at runtime as they are loaded.

If `True`, the Python runtime installation is read-write. This allows the
interpreter to create `.pyc` files for the standard library, but, because they are
created as needed, it adversely affects Bazel's ability to cache the runtime and
can result in spurious build failures.
The Python runtime installation is made read only. This improves the ability for
Bazel to cache it by preventing the interpreter from creating `.pyc` files for
the standard library dynamically at runtime as they are loaded (this often leads
to spurious cache misses or build failures).

However, if the user is running Bazel as root, this read-onlyness is not
respected. Bazel will print a warning message when it detects that the runtime
installation is writable despite being made read only (i.e. it's running with
root access). If this attribute is set to `False`, Bazel will make it a hard
error to run with root access instead.
""",
mandatory = False,
),
Expand DownExpand Up@@ -690,17 +692,8 @@ dependencies are introduced.
default = DEFAULT_RELEASE_BASE_URL,
),
"ignore_root_user_error": attr.bool(
default = False,
doc = """\
If `False`, the Python runtime installation will be made read only. This improves
the ability for Bazel to cache it, but prevents the interpreter from creating
`.pyc` files for the standard library dynamically at runtime as they are loaded.

If `True`, the Python runtime installation is read-write. This allows the
interpreter to create `.pyc` files for the standard library, but, because they are
created as needed, it adversely affects Bazel's ability to cache the runtime and
can result in spurious build failures.
""",
default = True,
doc = """Deprecated; do not use. This attribute has no effect.""",
mandatory = False,
),
"minor_mapping": attr.string_dict(
Expand Down
55 changes: 27 additions & 28 deletions python/private/python_repository.bzl
Original file line numberDiff line numberDiff line change
Expand Up@@ -127,37 +127,36 @@ def _python_repository_impl(rctx):
# pycs being generated at runtime:
# * The pycs are not deterministic (they contain timestamps)
# * Multiple processes trying to write the same pycs can result in errors.
if not rctx.attr.ignore_root_user_error:
if "windows" not in platform:
lib_dir = "lib" if "windows" not in platform else "Lib"
if "windows" not in platform:
repo_utils.execute_checked(
rctx,
op = "python_repository.MakeReadOnly",
arguments = [repo_utils.which_checked(rctx, "chmod"), "-R", "ugo-w", "lib"],
logger = logger,
)

repo_utils.execute_checked(
rctx,
op = "python_repository.MakeReadOnly",
arguments = [repo_utils.which_checked(rctx, "chmod"), "-R", "ugo-w", lib_dir],
logger = logger,
)
exec_result = repo_utils.execute_unchecked(
fail_or_warn = logger.warn if rctx.attr.ignore_root_user_error else logger.fail
exec_result = repo_utils.execute_unchecked(
rctx,
op = "python_repository.TestReadOnly",
arguments = [repo_utils.which_checked(rctx, "touch"), "lib/.test"],
logger = logger,
)

# The issue with running as root is the installation is no longer
# read-only, so the problems due to pyc can resurface.
if exec_result.return_code == 0:
stdout = repo_utils.execute_checked_stdout(
rctx,
op = "python_repository.TestReadOnly",
arguments = [repo_utils.which_checked(rctx, "touch"), "{}/.test".format(lib_dir)],
op = "python_repository.GetUserId",
arguments = [repo_utils.which_checked(rctx, "id"), "-u"],
logger = logger,
)

# The issue with running as root is the installation is no longer
# read-only, so the problems due to pyc can resurface.
if exec_result.return_code == 0:
stdout = repo_utils.execute_checked_stdout(
rctx,
op = "python_repository.GetUserId",
arguments = [repo_utils.which_checked(rctx, "id"), "-u"],
logger = logger,
)
uid = int(stdout.strip())
if uid == 0:
fail("The current user is root, please run as non-root when using the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")
else:
fail("The current user has CAP_DAC_OVERRIDE set, please drop this capability when using the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")
uid = int(stdout.strip())
if uid == 0:
fail_or_warn("The current user is root, which can cause spurious cache misses or build failures with the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")
else:
fail_or_warn("The current user has CAP_DAC_OVERRIDE set, which can cause spurious cache misses or build failures with the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")

python_bin = "python.exe" if ("windows" in platform) else "bin/python3"

Expand DownExpand Up@@ -294,7 +293,7 @@ For more information see {attr}`py_runtime.coverage_tool`.
mandatory = False,
),
"ignore_root_user_error": attr.bool(
default = False,
default = True,
doc = "Whether the check for root should be ignored or not. This causes cache misses with .pyc files.",
mandatory = False,
),
Expand Down
50 changes: 9 additions & 41 deletions tests/python/python_tests.bzl
Original file line numberDiff line numberDiff line change
Expand Up@@ -62,7 +62,7 @@ def _override(
auth_patterns = {},
available_python_versions = [],
base_url = "",
ignore_root_user_error = False,
ignore_root_user_error = True,
minor_mapping = {},
netrc = "",
register_all_versions = False):
Expand DownExpand Up@@ -139,7 +139,7 @@ def _test_default(env):
"ignore_root_user_error",
"tool_versions",
])
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(False)
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)
env.expect.that_str(py.default_python_version).equals("3.11")

want_toolchain = struct(
Expand DownExpand Up@@ -212,13 +212,13 @@ def _test_default_non_rules_python_ignore_root_user_error(env):
module_ctx = _mock_mctx(
_mod(
name = "my_module",
toolchain = [_toolchain("3.12", ignore_root_user_error = True)],
toolchain = [_toolchain("3.12", ignore_root_user_error = False)],
),
_mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
),
)

env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(False)
env.expect.that_str(py.default_python_version).equals("3.12")

my_module_toolchain = struct(
Expand All@@ -238,49 +238,17 @@ def _test_default_non_rules_python_ignore_root_user_error(env):

_tests.append(_test_default_non_rules_python_ignore_root_user_error)

def _test_default_non_rules_python_ignore_root_user_error_override(env):
py = parse_modules(
module_ctx = _mock_mctx(
_mod(
name = "my_module",
toolchain = [_toolchain("3.12")],
override = [_override(ignore_root_user_error = True)],
),
_mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
),
)

env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)
env.expect.that_str(py.default_python_version).equals("3.12")

my_module_toolchain = struct(
name = "python_3_12",
python_version = "3.12",
register_coverage_tool = False,
)
rules_python_toolchain = struct(
name = "python_3_11",
python_version = "3.11",
register_coverage_tool = False,
)
env.expect.that_collection(py.toolchains).contains_exactly([
rules_python_toolchain,
my_module_toolchain,
]).in_order()

_tests.append(_test_default_non_rules_python_ignore_root_user_error_override)

def _test_default_non_rules_python_ignore_root_user_error_non_root_module(env):
py = parse_modules(
module_ctx = _mock_mctx(
_mod(name = "my_module", toolchain = [_toolchain("3.13")]),
_mod(name = "some_module", toolchain = [_toolchain("3.12", ignore_root_user_error = True)]),
_mod(name = "some_module", toolchain = [_toolchain("3.12", ignore_root_user_error = False)]),
_mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
),
)

env.expect.that_str(py.default_python_version).equals("3.13")
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(False)
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)

my_module_toolchain = struct(
name = "python_3_13",
Expand DownExpand Up@@ -338,8 +306,8 @@ def _test_first_occurance_of_the_toolchain_wins(env):

env.expect.that_dict(py.debug_info).contains_exactly({
"toolchains_registered": [
{"ignore_root_user_error": False, "module": {"is_root": True, "name": "my_module"}, "name": "python_3_12"},
{"ignore_root_user_error": False, "module": {"is_root": False, "name": "rules_python"}, "name": "python_3_11"},
{"ignore_root_user_error": True, "module": {"is_root": True, "name": "my_module"}, "name": "python_3_12"},
{"ignore_root_user_error": True, "module": {"is_root": False, "name": "rules_python"}, "name": "python_3_11"},
],
})

Expand All@@ -364,7 +332,7 @@ def _test_auth_overrides(env):

env.expect.that_dict(py.config.default).contains_at_least({
"auth_patterns": {"foo": "bar"},
"ignore_root_user_error": False,
"ignore_root_user_error": True,
"netrc": "/my/netrc",
})
env.expect.that_str(py.default_python_version).equals("3.12")
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -59,6 +59,10 @@ Unreleased changes template.
* (pypi) The `ppc64le` is now pointing to the right target in the `platforms` package.
* (gazelle) No longer incorrectly merge `py_binary` targets during partial updates in
`file` generation mode. Fixed in [#2619](https://github.com/bazelbuild/rules_python/pull/2619).
* (bzlmod) Running as root is no longer an error. `ignore_root_user_error=True`
is now the default. Note that running as root may still cause spurious
Bazel cache invalidation
([#1169](https://github.com/bazelbuild/rules_python/issues/1169)).

{#v0-0-0-added}
### Added
Expand Down
39 changes: 16 additions & 23 deletions python/private/python.bzl
Original file line numberDiff line numberDiff line change
Expand Up@@ -72,9 +72,9 @@ def parse_modules(*, module_ctx, _fail = fail):
logger = repo_utils.logger(module_ctx, "python")

# if the root module does not register any toolchain then the
# ignore_root_user_error takes its default value: False
# ignore_root_user_error takes its default value: True
if not module_ctx.modules[0].tags.toolchain:
ignore_root_user_error = False
ignore_root_user_error = True

config = _get_toolchain_config(modules = module_ctx.modules, _fail = _fail)

Expand DownExpand Up@@ -559,7 +559,7 @@ def _create_toolchain_attrs_struct(*, tag = None, python_version = None, toolcha
is_default = is_default,
python_version = python_version if python_version else tag.python_version,
configure_coverage_tool = getattr(tag, "configure_coverage_tool", False),
ignore_root_user_error = getattr(tag, "ignore_root_user_error", False),
ignore_root_user_error = getattr(tag, "ignore_root_user_error", True),
)

def _get_bazel_version_specific_kwargs():
Expand DownExpand Up@@ -636,16 +636,18 @@ Then the python interpreter will be available as `my_python_name`.
doc = "Whether or not to configure the default coverage tool provided by `rules_python` for the compatible toolchains.",
),
"ignore_root_user_error": attr.bool(
default = False,
default = True,
doc = """\
If `False`, the Python runtime installation will be made read only. This improves
the ability for Bazel to cache it, but prevents the interpreter from creating
`.pyc` files for the standard library dynamically at runtime as they are loaded.

If `True`, the Python runtime installation is read-write. This allows the
interpreter to create `.pyc` files for the standard library, but, because they are
created as needed, it adversely affects Bazel's ability to cache the runtime and
can result in spurious build failures.
The Python runtime installation is made read only. This improves the ability for
Bazel to cache it by preventing the interpreter from creating `.pyc` files for
the standard library dynamically at runtime as they are loaded (this often leads
to spurious cache misses or build failures).

However, if the user is running Bazel as root, this read-onlyness is not
respected. Bazel will print a warning message when it detects that the runtime
installation is writable despite being made read only (i.e. it's running with
root access). If this attribute is set to `False`, Bazel will make it a hard
error to run with root access instead.
""",
mandatory = False,
),
Expand DownExpand Up@@ -690,17 +692,8 @@ dependencies are introduced.
default = DEFAULT_RELEASE_BASE_URL,
),
"ignore_root_user_error": attr.bool(
default = False,
doc = """\
If `False`, the Python runtime installation will be made read only. This improves
the ability for Bazel to cache it, but prevents the interpreter from creating
`.pyc` files for the standard library dynamically at runtime as they are loaded.

If `True`, the Python runtime installation is read-write. This allows the
interpreter to create `.pyc` files for the standard library, but, because they are
created as needed, it adversely affects Bazel's ability to cache the runtime and
can result in spurious build failures.
""",
default = True,
doc = """Deprecated; do not use. This attribute has no effect.""",
mandatory = False,
),
"minor_mapping": attr.string_dict(
Expand Down
55 changes: 27 additions & 28 deletions python/private/python_repository.bzl
Original file line numberDiff line numberDiff line change
Expand Up@@ -127,37 +127,36 @@ def _python_repository_impl(rctx):
# pycs being generated at runtime:
# * The pycs are not deterministic (they contain timestamps)
# * Multiple processes trying to write the same pycs can result in errors.
if not rctx.attr.ignore_root_user_error:
if "windows" not in platform:
lib_dir = "lib" if "windows" not in platform else "Lib"
if "windows" not in platform:
repo_utils.execute_checked(
rctx,
op = "python_repository.MakeReadOnly",
arguments = [repo_utils.which_checked(rctx, "chmod"), "-R", "ugo-w", "lib"],
logger = logger,
)

repo_utils.execute_checked(
rctx,
op = "python_repository.MakeReadOnly",
arguments = [repo_utils.which_checked(rctx, "chmod"), "-R", "ugo-w", lib_dir],
logger = logger,
)
exec_result = repo_utils.execute_unchecked(
fail_or_warn = logger.warn if rctx.attr.ignore_root_user_error else logger.fail
exec_result = repo_utils.execute_unchecked(
rctx,
op = "python_repository.TestReadOnly",
arguments = [repo_utils.which_checked(rctx, "touch"), "lib/.test"],
logger = logger,
)

# The issue with running as root is the installation is no longer
# read-only, so the problems due to pyc can resurface.
if exec_result.return_code == 0:
stdout = repo_utils.execute_checked_stdout(
rctx,
op = "python_repository.TestReadOnly",
arguments = [repo_utils.which_checked(rctx, "touch"), "{}/.test".format(lib_dir)],
op = "python_repository.GetUserId",
arguments = [repo_utils.which_checked(rctx, "id"), "-u"],
logger = logger,
)

# The issue with running as root is the installation is no longer
# read-only, so the problems due to pyc can resurface.
if exec_result.return_code == 0:
stdout = repo_utils.execute_checked_stdout(
rctx,
op = "python_repository.GetUserId",
arguments = [repo_utils.which_checked(rctx, "id"), "-u"],
logger = logger,
)
uid = int(stdout.strip())
if uid == 0:
fail("The current user is root, please run as non-root when using the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")
else:
fail("The current user has CAP_DAC_OVERRIDE set, please drop this capability when using the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")
uid = int(stdout.strip())
if uid == 0:
fail_or_warn("The current user is root, which can cause spurious cache misses or build failures with the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")
else:
fail_or_warn("The current user has CAP_DAC_OVERRIDE set, which can cause spurious cache misses or build failures with the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")

python_bin = "python.exe" if ("windows" in platform) else "bin/python3"

Expand DownExpand Up@@ -294,7 +293,7 @@ For more information see {attr}`py_runtime.coverage_tool`.
mandatory = False,
),
"ignore_root_user_error": attr.bool(
default = False,
default = True,
doc = "Whether the check for root should be ignored or not. This causes cache misses with .pyc files.",
mandatory = False,
),
Expand Down
50 changes: 9 additions & 41 deletions tests/python/python_tests.bzl
Original file line numberDiff line numberDiff line change
Expand Up@@ -62,7 +62,7 @@ def _override(
auth_patterns = {},
available_python_versions = [],
base_url = "",
ignore_root_user_error = False,
ignore_root_user_error = True,
minor_mapping = {},
netrc = "",
register_all_versions = False):
Expand DownExpand Up@@ -139,7 +139,7 @@ def _test_default(env):
"ignore_root_user_error",
"tool_versions",
])
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(False)
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)
env.expect.that_str(py.default_python_version).equals("3.11")

want_toolchain = struct(
Expand DownExpand Up@@ -212,13 +212,13 @@ def _test_default_non_rules_python_ignore_root_user_error(env):
module_ctx = _mock_mctx(
_mod(
name = "my_module",
toolchain = [_toolchain("3.12", ignore_root_user_error = True)],
toolchain = [_toolchain("3.12", ignore_root_user_error = False)],
),
_mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
),
)

env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(False)
env.expect.that_str(py.default_python_version).equals("3.12")

my_module_toolchain = struct(
Expand All@@ -238,49 +238,17 @@ def _test_default_non_rules_python_ignore_root_user_error(env):

_tests.append(_test_default_non_rules_python_ignore_root_user_error)

def _test_default_non_rules_python_ignore_root_user_error_override(env):
py = parse_modules(
module_ctx = _mock_mctx(
_mod(
name = "my_module",
toolchain = [_toolchain("3.12")],
override = [_override(ignore_root_user_error = True)],
),
_mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
),
)

env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)
env.expect.that_str(py.default_python_version).equals("3.12")

my_module_toolchain = struct(
name = "python_3_12",
python_version = "3.12",
register_coverage_tool = False,
)
rules_python_toolchain = struct(
name = "python_3_11",
python_version = "3.11",
register_coverage_tool = False,
)
env.expect.that_collection(py.toolchains).contains_exactly([
rules_python_toolchain,
my_module_toolchain,
]).in_order()

_tests.append(_test_default_non_rules_python_ignore_root_user_error_override)

def _test_default_non_rules_python_ignore_root_user_error_non_root_module(env):
py = parse_modules(
module_ctx = _mock_mctx(
_mod(name = "my_module", toolchain = [_toolchain("3.13")]),
_mod(name = "some_module", toolchain = [_toolchain("3.12", ignore_root_user_error = True)]),
_mod(name = "some_module", toolchain = [_toolchain("3.12", ignore_root_user_error = False)]),
_mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
),
)

env.expect.that_str(py.default_python_version).equals("3.13")
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(False)
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)

my_module_toolchain = struct(
name = "python_3_13",
Expand DownExpand Up@@ -338,8 +306,8 @@ def _test_first_occurance_of_the_toolchain_wins(env):

env.expect.that_dict(py.debug_info).contains_exactly({
"toolchains_registered": [
{"ignore_root_user_error": False, "module": {"is_root": True, "name": "my_module"}, "name": "python_3_12"},
{"ignore_root_user_error": False, "module": {"is_root": False, "name": "rules_python"}, "name": "python_3_11"},
{"ignore_root_user_error": True, "module": {"is_root": True, "name": "my_module"}, "name": "python_3_12"},
{"ignore_root_user_error": True, "module": {"is_root": False, "name": "rules_python"}, "name": "python_3_11"},
],
})

Expand All@@ -364,7 +332,7 @@ def _test_auth_overrides(env):

env.expect.that_dict(py.config.default).contains_at_least({
"auth_patterns": {"foo": "bar"},
"ignore_root_user_error": False,
"ignore_root_user_error": True,
"netrc": "/my/netrc",
})
env.expect.that_str(py.default_python_version).equals("3.12")
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -59,6 +59,10 @@ Unreleased changes template.
* (pypi) The `ppc64le` is now pointing to the right target in the `platforms` package.
* (gazelle) No longer incorrectly merge `py_binary` targets during partial updates in
`file` generation mode. Fixed in [#2619](https://github.com/bazelbuild/rules_python/pull/2619).
* (bzlmod) Running as root is no longer an error. `ignore_root_user_error=True`
is now the default. Note that running as root may still cause spurious
Bazel cache invalidation
([#1169](https://github.com/bazelbuild/rules_python/issues/1169)).

{#v0-0-0-added}
### Added
Expand Down
39 changes: 16 additions & 23 deletions python/private/python.bzl
Original file line numberDiff line numberDiff line change
Expand Up@@ -72,9 +72,9 @@ def parse_modules(*, module_ctx, _fail = fail):
logger = repo_utils.logger(module_ctx, "python")

# if the root module does not register any toolchain then the
# ignore_root_user_error takes its default value: False
# ignore_root_user_error takes its default value: True
if not module_ctx.modules[0].tags.toolchain:
ignore_root_user_error = False
ignore_root_user_error = True

config = _get_toolchain_config(modules = module_ctx.modules, _fail = _fail)

Expand DownExpand Up@@ -559,7 +559,7 @@ def _create_toolchain_attrs_struct(*, tag = None, python_version = None, toolcha
is_default = is_default,
python_version = python_version if python_version else tag.python_version,
configure_coverage_tool = getattr(tag, "configure_coverage_tool", False),
ignore_root_user_error = getattr(tag, "ignore_root_user_error", False),
ignore_root_user_error = getattr(tag, "ignore_root_user_error", True),
)

def _get_bazel_version_specific_kwargs():
Expand DownExpand Up@@ -636,16 +636,18 @@ Then the python interpreter will be available as `my_python_name`.
doc = "Whether or not to configure the default coverage tool provided by `rules_python` for the compatible toolchains.",
),
"ignore_root_user_error": attr.bool(
default = False,
default = True,
doc = """\
If `False`, the Python runtime installation will be made read only. This improves
the ability for Bazel to cache it, but prevents the interpreter from creating
`.pyc` files for the standard library dynamically at runtime as they are loaded.

If `True`, the Python runtime installation is read-write. This allows the
interpreter to create `.pyc` files for the standard library, but, because they are
created as needed, it adversely affects Bazel's ability to cache the runtime and
can result in spurious build failures.
The Python runtime installation is made read only. This improves the ability for
Bazel to cache it by preventing the interpreter from creating `.pyc` files for
the standard library dynamically at runtime as they are loaded (this often leads
to spurious cache misses or build failures).

However, if the user is running Bazel as root, this read-onlyness is not
respected. Bazel will print a warning message when it detects that the runtime
installation is writable despite being made read only (i.e. it's running with
root access). If this attribute is set to `False`, Bazel will make it a hard
error to run with root access instead.
""",
mandatory = False,
),
Expand DownExpand Up@@ -690,17 +692,8 @@ dependencies are introduced.
default = DEFAULT_RELEASE_BASE_URL,
),
"ignore_root_user_error": attr.bool(
default = False,
doc = """\
If `False`, the Python runtime installation will be made read only. This improves
the ability for Bazel to cache it, but prevents the interpreter from creating
`.pyc` files for the standard library dynamically at runtime as they are loaded.

If `True`, the Python runtime installation is read-write. This allows the
interpreter to create `.pyc` files for the standard library, but, because they are
created as needed, it adversely affects Bazel's ability to cache the runtime and
can result in spurious build failures.
""",
default = True,
doc = """Deprecated; do not use. This attribute has no effect.""",
mandatory = False,
),
"minor_mapping": attr.string_dict(
Expand Down
55 changes: 27 additions & 28 deletions python/private/python_repository.bzl
Original file line numberDiff line numberDiff line change
Expand Up@@ -127,37 +127,36 @@ def _python_repository_impl(rctx):
# pycs being generated at runtime:
# * The pycs are not deterministic (they contain timestamps)
# * Multiple processes trying to write the same pycs can result in errors.
if not rctx.attr.ignore_root_user_error:
if "windows" not in platform:
lib_dir = "lib" if "windows" not in platform else "Lib"
if "windows" not in platform:
repo_utils.execute_checked(
rctx,
op = "python_repository.MakeReadOnly",
arguments = [repo_utils.which_checked(rctx, "chmod"), "-R", "ugo-w", "lib"],
logger = logger,
)

repo_utils.execute_checked(
rctx,
op = "python_repository.MakeReadOnly",
arguments = [repo_utils.which_checked(rctx, "chmod"), "-R", "ugo-w", lib_dir],
logger = logger,
)
exec_result = repo_utils.execute_unchecked(
fail_or_warn = logger.warn if rctx.attr.ignore_root_user_error else logger.fail
exec_result = repo_utils.execute_unchecked(
rctx,
op = "python_repository.TestReadOnly",
arguments = [repo_utils.which_checked(rctx, "touch"), "lib/.test"],
logger = logger,
)

# The issue with running as root is the installation is no longer
# read-only, so the problems due to pyc can resurface.
if exec_result.return_code == 0:
stdout = repo_utils.execute_checked_stdout(
rctx,
op = "python_repository.TestReadOnly",
arguments = [repo_utils.which_checked(rctx, "touch"), "{}/.test".format(lib_dir)],
op = "python_repository.GetUserId",
arguments = [repo_utils.which_checked(rctx, "id"), "-u"],
logger = logger,
)

# The issue with running as root is the installation is no longer
# read-only, so the problems due to pyc can resurface.
if exec_result.return_code == 0:
stdout = repo_utils.execute_checked_stdout(
rctx,
op = "python_repository.GetUserId",
arguments = [repo_utils.which_checked(rctx, "id"), "-u"],
logger = logger,
)
uid = int(stdout.strip())
if uid == 0:
fail("The current user is root, please run as non-root when using the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")
else:
fail("The current user has CAP_DAC_OVERRIDE set, please drop this capability when using the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")
uid = int(stdout.strip())
if uid == 0:
fail_or_warn("The current user is root, which can cause spurious cache misses or build failures with the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")
else:
fail_or_warn("The current user has CAP_DAC_OVERRIDE set, which can cause spurious cache misses or build failures with the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")

python_bin = "python.exe" if ("windows" in platform) else "bin/python3"

Expand DownExpand Up@@ -294,7 +293,7 @@ For more information see {attr}`py_runtime.coverage_tool`.
mandatory = False,
),
"ignore_root_user_error": attr.bool(
default = False,
default = True,
doc = "Whether the check for root should be ignored or not. This causes cache misses with .pyc files.",
mandatory = False,
),
Expand Down
50 changes: 9 additions & 41 deletions tests/python/python_tests.bzl
Original file line numberDiff line numberDiff line change
Expand Up@@ -62,7 +62,7 @@ def _override(
auth_patterns = {},
available_python_versions = [],
base_url = "",
ignore_root_user_error = False,
ignore_root_user_error = True,
minor_mapping = {},
netrc = "",
register_all_versions = False):
Expand DownExpand Up@@ -139,7 +139,7 @@ def _test_default(env):
"ignore_root_user_error",
"tool_versions",
])
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(False)
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)
env.expect.that_str(py.default_python_version).equals("3.11")

want_toolchain = struct(
Expand DownExpand Up@@ -212,13 +212,13 @@ def _test_default_non_rules_python_ignore_root_user_error(env):
module_ctx = _mock_mctx(
_mod(
name = "my_module",
toolchain = [_toolchain("3.12", ignore_root_user_error = True)],
toolchain = [_toolchain("3.12", ignore_root_user_error = False)],
),
_mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
),
)

env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(False)
env.expect.that_str(py.default_python_version).equals("3.12")

my_module_toolchain = struct(
Expand All@@ -238,49 +238,17 @@ def _test_default_non_rules_python_ignore_root_user_error(env):

_tests.append(_test_default_non_rules_python_ignore_root_user_error)

def _test_default_non_rules_python_ignore_root_user_error_override(env):
py = parse_modules(
module_ctx = _mock_mctx(
_mod(
name = "my_module",
toolchain = [_toolchain("3.12")],
override = [_override(ignore_root_user_error = True)],
),
_mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
),
)

env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)
env.expect.that_str(py.default_python_version).equals("3.12")

my_module_toolchain = struct(
name = "python_3_12",
python_version = "3.12",
register_coverage_tool = False,
)
rules_python_toolchain = struct(
name = "python_3_11",
python_version = "3.11",
register_coverage_tool = False,
)
env.expect.that_collection(py.toolchains).contains_exactly([
rules_python_toolchain,
my_module_toolchain,
]).in_order()

_tests.append(_test_default_non_rules_python_ignore_root_user_error_override)

def _test_default_non_rules_python_ignore_root_user_error_non_root_module(env):
py = parse_modules(
module_ctx = _mock_mctx(
_mod(name = "my_module", toolchain = [_toolchain("3.13")]),
_mod(name = "some_module", toolchain = [_toolchain("3.12", ignore_root_user_error = True)]),
_mod(name = "some_module", toolchain = [_toolchain("3.12", ignore_root_user_error = False)]),
_mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
),
)

env.expect.that_str(py.default_python_version).equals("3.13")
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(False)
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)

my_module_toolchain = struct(
name = "python_3_13",
Expand DownExpand Up@@ -338,8 +306,8 @@ def _test_first_occurance_of_the_toolchain_wins(env):

env.expect.that_dict(py.debug_info).contains_exactly({
"toolchains_registered": [
{"ignore_root_user_error": False, "module": {"is_root": True, "name": "my_module"}, "name": "python_3_12"},
{"ignore_root_user_error": False, "module": {"is_root": False, "name": "rules_python"}, "name": "python_3_11"},
{"ignore_root_user_error": True, "module": {"is_root": True, "name": "my_module"}, "name": "python_3_12"},
{"ignore_root_user_error": True, "module": {"is_root": False, "name": "rules_python"}, "name": "python_3_11"},
],
})

Expand All@@ -364,7 +332,7 @@ def _test_auth_overrides(env):

env.expect.that_dict(py.config.default).contains_at_least({
"auth_patterns": {"foo": "bar"},
"ignore_root_user_error": False,
"ignore_root_user_error": True,
"netrc": "/my/netrc",
})
env.expect.that_str(py.default_python_version).equals("3.12")
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -59,6 +59,10 @@ Unreleased changes template.
* (pypi) The `ppc64le` is now pointing to the right target in the `platforms` package.
* (gazelle) No longer incorrectly merge `py_binary` targets during partial updates in
`file` generation mode. Fixed in [#2619](https://github.com/bazelbuild/rules_python/pull/2619).
* (bzlmod) Running as root is no longer an error. `ignore_root_user_error=True`
is now the default. Note that running as root may still cause spurious
Bazel cache invalidation
([#1169](https://github.com/bazelbuild/rules_python/issues/1169)).

{#v0-0-0-added}
### Added
Expand Down
39 changes: 16 additions & 23 deletions python/private/python.bzl
Original file line numberDiff line numberDiff line change
Expand Up@@ -72,9 +72,9 @@ def parse_modules(*, module_ctx, _fail = fail):
logger = repo_utils.logger(module_ctx, "python")

# if the root module does not register any toolchain then the
# ignore_root_user_error takes its default value: False
# ignore_root_user_error takes its default value: True
if not module_ctx.modules[0].tags.toolchain:
ignore_root_user_error = False
ignore_root_user_error = True

config = _get_toolchain_config(modules = module_ctx.modules, _fail = _fail)

Expand DownExpand Up@@ -559,7 +559,7 @@ def _create_toolchain_attrs_struct(*, tag = None, python_version = None, toolcha
is_default = is_default,
python_version = python_version if python_version else tag.python_version,
configure_coverage_tool = getattr(tag, "configure_coverage_tool", False),
ignore_root_user_error = getattr(tag, "ignore_root_user_error", False),
ignore_root_user_error = getattr(tag, "ignore_root_user_error", True),
)

def _get_bazel_version_specific_kwargs():
Expand DownExpand Up@@ -636,16 +636,18 @@ Then the python interpreter will be available as `my_python_name`.
doc = "Whether or not to configure the default coverage tool provided by `rules_python` for the compatible toolchains.",
),
"ignore_root_user_error": attr.bool(
default = False,
default = True,
doc = """\
If `False`, the Python runtime installation will be made read only. This improves
the ability for Bazel to cache it, but prevents the interpreter from creating
`.pyc` files for the standard library dynamically at runtime as they are loaded.

If `True`, the Python runtime installation is read-write. This allows the
interpreter to create `.pyc` files for the standard library, but, because they are
created as needed, it adversely affects Bazel's ability to cache the runtime and
can result in spurious build failures.
The Python runtime installation is made read only. This improves the ability for
Bazel to cache it by preventing the interpreter from creating `.pyc` files for
the standard library dynamically at runtime as they are loaded (this often leads
to spurious cache misses or build failures).

However, if the user is running Bazel as root, this read-onlyness is not
respected. Bazel will print a warning message when it detects that the runtime
installation is writable despite being made read only (i.e. it's running with
root access). If this attribute is set to `False`, Bazel will make it a hard
error to run with root access instead.
""",
mandatory = False,
),
Expand DownExpand Up@@ -690,17 +692,8 @@ dependencies are introduced.
default = DEFAULT_RELEASE_BASE_URL,
),
"ignore_root_user_error": attr.bool(
default = False,
doc = """\
If `False`, the Python runtime installation will be made read only. This improves
the ability for Bazel to cache it, but prevents the interpreter from creating
`.pyc` files for the standard library dynamically at runtime as they are loaded.

If `True`, the Python runtime installation is read-write. This allows the
interpreter to create `.pyc` files for the standard library, but, because they are
created as needed, it adversely affects Bazel's ability to cache the runtime and
can result in spurious build failures.
""",
default = True,
doc = """Deprecated; do not use. This attribute has no effect.""",
mandatory = False,
),
"minor_mapping": attr.string_dict(
Expand Down
55 changes: 27 additions & 28 deletions python/private/python_repository.bzl
Original file line numberDiff line numberDiff line change
Expand Up@@ -127,37 +127,36 @@ def _python_repository_impl(rctx):
# pycs being generated at runtime:
# * The pycs are not deterministic (they contain timestamps)
# * Multiple processes trying to write the same pycs can result in errors.
if not rctx.attr.ignore_root_user_error:
if "windows" not in platform:
lib_dir = "lib" if "windows" not in platform else "Lib"
if "windows" not in platform:
repo_utils.execute_checked(
rctx,
op = "python_repository.MakeReadOnly",
arguments = [repo_utils.which_checked(rctx, "chmod"), "-R", "ugo-w", "lib"],
logger = logger,
)

repo_utils.execute_checked(
rctx,
op = "python_repository.MakeReadOnly",
arguments = [repo_utils.which_checked(rctx, "chmod"), "-R", "ugo-w", lib_dir],
logger = logger,
)
exec_result = repo_utils.execute_unchecked(
fail_or_warn = logger.warn if rctx.attr.ignore_root_user_error else logger.fail
exec_result = repo_utils.execute_unchecked(
rctx,
op = "python_repository.TestReadOnly",
arguments = [repo_utils.which_checked(rctx, "touch"), "lib/.test"],
logger = logger,
)

# The issue with running as root is the installation is no longer
# read-only, so the problems due to pyc can resurface.
if exec_result.return_code == 0:
stdout = repo_utils.execute_checked_stdout(
rctx,
op = "python_repository.TestReadOnly",
arguments = [repo_utils.which_checked(rctx, "touch"), "{}/.test".format(lib_dir)],
op = "python_repository.GetUserId",
arguments = [repo_utils.which_checked(rctx, "id"), "-u"],
logger = logger,
)

# The issue with running as root is the installation is no longer
# read-only, so the problems due to pyc can resurface.
if exec_result.return_code == 0:
stdout = repo_utils.execute_checked_stdout(
rctx,
op = "python_repository.GetUserId",
arguments = [repo_utils.which_checked(rctx, "id"), "-u"],
logger = logger,
)
uid = int(stdout.strip())
if uid == 0:
fail("The current user is root, please run as non-root when using the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")
else:
fail("The current user has CAP_DAC_OVERRIDE set, please drop this capability when using the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")
uid = int(stdout.strip())
if uid == 0:
fail_or_warn("The current user is root, which can cause spurious cache misses or build failures with the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")
else:
fail_or_warn("The current user has CAP_DAC_OVERRIDE set, which can cause spurious cache misses or build failures with the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")

python_bin = "python.exe" if ("windows" in platform) else "bin/python3"

Expand DownExpand Up@@ -294,7 +293,7 @@ For more information see {attr}`py_runtime.coverage_tool`.
mandatory = False,
),
"ignore_root_user_error": attr.bool(
default = False,
default = True,
doc = "Whether the check for root should be ignored or not. This causes cache misses with .pyc files.",
mandatory = False,
),
Expand Down
50 changes: 9 additions & 41 deletions tests/python/python_tests.bzl
Original file line numberDiff line numberDiff line change
Expand Up@@ -62,7 +62,7 @@ def _override(
auth_patterns = {},
available_python_versions = [],
base_url = "",
ignore_root_user_error = False,
ignore_root_user_error = True,
minor_mapping = {},
netrc = "",
register_all_versions = False):
Expand DownExpand Up@@ -139,7 +139,7 @@ def _test_default(env):
"ignore_root_user_error",
"tool_versions",
])
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(False)
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)
env.expect.that_str(py.default_python_version).equals("3.11")

want_toolchain = struct(
Expand DownExpand Up@@ -212,13 +212,13 @@ def _test_default_non_rules_python_ignore_root_user_error(env):
module_ctx = _mock_mctx(
_mod(
name = "my_module",
toolchain = [_toolchain("3.12", ignore_root_user_error = True)],
toolchain = [_toolchain("3.12", ignore_root_user_error = False)],
),
_mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
),
)

env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(False)
env.expect.that_str(py.default_python_version).equals("3.12")

my_module_toolchain = struct(
Expand All@@ -238,49 +238,17 @@ def _test_default_non_rules_python_ignore_root_user_error(env):

_tests.append(_test_default_non_rules_python_ignore_root_user_error)

def _test_default_non_rules_python_ignore_root_user_error_override(env):
py = parse_modules(
module_ctx = _mock_mctx(
_mod(
name = "my_module",
toolchain = [_toolchain("3.12")],
override = [_override(ignore_root_user_error = True)],
),
_mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
),
)

env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)
env.expect.that_str(py.default_python_version).equals("3.12")

my_module_toolchain = struct(
name = "python_3_12",
python_version = "3.12",
register_coverage_tool = False,
)
rules_python_toolchain = struct(
name = "python_3_11",
python_version = "3.11",
register_coverage_tool = False,
)
env.expect.that_collection(py.toolchains).contains_exactly([
rules_python_toolchain,
my_module_toolchain,
]).in_order()

_tests.append(_test_default_non_rules_python_ignore_root_user_error_override)

def _test_default_non_rules_python_ignore_root_user_error_non_root_module(env):
py = parse_modules(
module_ctx = _mock_mctx(
_mod(name = "my_module", toolchain = [_toolchain("3.13")]),
_mod(name = "some_module", toolchain = [_toolchain("3.12", ignore_root_user_error = True)]),
_mod(name = "some_module", toolchain = [_toolchain("3.12", ignore_root_user_error = False)]),
_mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
),
)

env.expect.that_str(py.default_python_version).equals("3.13")
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(False)
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)

my_module_toolchain = struct(
name = "python_3_13",
Expand DownExpand Up@@ -338,8 +306,8 @@ def _test_first_occurance_of_the_toolchain_wins(env):

env.expect.that_dict(py.debug_info).contains_exactly({
"toolchains_registered": [
{"ignore_root_user_error": False, "module": {"is_root": True, "name": "my_module"}, "name": "python_3_12"},
{"ignore_root_user_error": False, "module": {"is_root": False, "name": "rules_python"}, "name": "python_3_11"},
{"ignore_root_user_error": True, "module": {"is_root": True, "name": "my_module"}, "name": "python_3_12"},
{"ignore_root_user_error": True, "module": {"is_root": False, "name": "rules_python"}, "name": "python_3_11"},
],
})

Expand All@@ -364,7 +332,7 @@ def _test_auth_overrides(env):

env.expect.that_dict(py.config.default).contains_at_least({
"auth_patterns": {"foo": "bar"},
"ignore_root_user_error": False,
"ignore_root_user_error": True,
"netrc": "/my/netrc",
})
env.expect.that_str(py.default_python_version).equals("3.12")
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -59,6 +59,10 @@ Unreleased changes template.
* (pypi) The `ppc64le` is now pointing to the right target in the `platforms` package.
* (gazelle) No longer incorrectly merge `py_binary` targets during partial updates in
`file` generation mode. Fixed in [#2619](https://github.com/bazelbuild/rules_python/pull/2619).
* (bzlmod) Running as root is no longer an error. `ignore_root_user_error=True`
is now the default. Note that running as root may still cause spurious
Bazel cache invalidation
([#1169](https://github.com/bazelbuild/rules_python/issues/1169)).

{#v0-0-0-added}
### Added
Expand Down
39 changes: 16 additions & 23 deletions python/private/python.bzl
Original file line numberDiff line numberDiff line change
Expand Up@@ -72,9 +72,9 @@ def parse_modules(*, module_ctx, _fail = fail):
logger = repo_utils.logger(module_ctx, "python")

# if the root module does not register any toolchain then the
# ignore_root_user_error takes its default value: False
# ignore_root_user_error takes its default value: True
if not module_ctx.modules[0].tags.toolchain:
ignore_root_user_error = False
ignore_root_user_error = True

config = _get_toolchain_config(modules = module_ctx.modules, _fail = _fail)

Expand DownExpand Up@@ -559,7 +559,7 @@ def _create_toolchain_attrs_struct(*, tag = None, python_version = None, toolcha
is_default = is_default,
python_version = python_version if python_version else tag.python_version,
configure_coverage_tool = getattr(tag, "configure_coverage_tool", False),
ignore_root_user_error = getattr(tag, "ignore_root_user_error", False),
ignore_root_user_error = getattr(tag, "ignore_root_user_error", True),
)

def _get_bazel_version_specific_kwargs():
Expand DownExpand Up@@ -636,16 +636,18 @@ Then the python interpreter will be available as `my_python_name`.
doc = "Whether or not to configure the default coverage tool provided by `rules_python` for the compatible toolchains.",
),
"ignore_root_user_error": attr.bool(
default = False,
default = True,
doc = """\
If `False`, the Python runtime installation will be made read only. This improves
the ability for Bazel to cache it, but prevents the interpreter from creating
`.pyc` files for the standard library dynamically at runtime as they are loaded.

If `True`, the Python runtime installation is read-write. This allows the
interpreter to create `.pyc` files for the standard library, but, because they are
created as needed, it adversely affects Bazel's ability to cache the runtime and
can result in spurious build failures.
The Python runtime installation is made read only. This improves the ability for
Bazel to cache it by preventing the interpreter from creating `.pyc` files for
the standard library dynamically at runtime as they are loaded (this often leads
to spurious cache misses or build failures).

However, if the user is running Bazel as root, this read-onlyness is not
respected. Bazel will print a warning message when it detects that the runtime
installation is writable despite being made read only (i.e. it's running with
root access). If this attribute is set to `False`, Bazel will make it a hard
error to run with root access instead.
""",
mandatory = False,
),
Expand DownExpand Up@@ -690,17 +692,8 @@ dependencies are introduced.
default = DEFAULT_RELEASE_BASE_URL,
),
"ignore_root_user_error": attr.bool(
default = False,
doc = """\
If `False`, the Python runtime installation will be made read only. This improves
the ability for Bazel to cache it, but prevents the interpreter from creating
`.pyc` files for the standard library dynamically at runtime as they are loaded.

If `True`, the Python runtime installation is read-write. This allows the
interpreter to create `.pyc` files for the standard library, but, because they are
created as needed, it adversely affects Bazel's ability to cache the runtime and
can result in spurious build failures.
""",
default = True,
doc = """Deprecated; do not use. This attribute has no effect.""",
mandatory = False,
),
"minor_mapping": attr.string_dict(
Expand Down
55 changes: 27 additions & 28 deletions python/private/python_repository.bzl
Original file line numberDiff line numberDiff line change
Expand Up@@ -127,37 +127,36 @@ def _python_repository_impl(rctx):
# pycs being generated at runtime:
# * The pycs are not deterministic (they contain timestamps)
# * Multiple processes trying to write the same pycs can result in errors.
if not rctx.attr.ignore_root_user_error:
if "windows" not in platform:
lib_dir = "lib" if "windows" not in platform else "Lib"
if "windows" not in platform:
repo_utils.execute_checked(
rctx,
op = "python_repository.MakeReadOnly",
arguments = [repo_utils.which_checked(rctx, "chmod"), "-R", "ugo-w", "lib"],
logger = logger,
)

repo_utils.execute_checked(
rctx,
op = "python_repository.MakeReadOnly",
arguments = [repo_utils.which_checked(rctx, "chmod"), "-R", "ugo-w", lib_dir],
logger = logger,
)
exec_result = repo_utils.execute_unchecked(
fail_or_warn = logger.warn if rctx.attr.ignore_root_user_error else logger.fail
exec_result = repo_utils.execute_unchecked(
rctx,
op = "python_repository.TestReadOnly",
arguments = [repo_utils.which_checked(rctx, "touch"), "lib/.test"],
logger = logger,
)

# The issue with running as root is the installation is no longer
# read-only, so the problems due to pyc can resurface.
if exec_result.return_code == 0:
stdout = repo_utils.execute_checked_stdout(
rctx,
op = "python_repository.TestReadOnly",
arguments = [repo_utils.which_checked(rctx, "touch"), "{}/.test".format(lib_dir)],
op = "python_repository.GetUserId",
arguments = [repo_utils.which_checked(rctx, "id"), "-u"],
logger = logger,
)

# The issue with running as root is the installation is no longer
# read-only, so the problems due to pyc can resurface.
if exec_result.return_code == 0:
stdout = repo_utils.execute_checked_stdout(
rctx,
op = "python_repository.GetUserId",
arguments = [repo_utils.which_checked(rctx, "id"), "-u"],
logger = logger,
)
uid = int(stdout.strip())
if uid == 0:
fail("The current user is root, please run as non-root when using the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")
else:
fail("The current user has CAP_DAC_OVERRIDE set, please drop this capability when using the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")
uid = int(stdout.strip())
if uid == 0:
fail_or_warn("The current user is root, which can cause spurious cache misses or build failures with the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")
else:
fail_or_warn("The current user has CAP_DAC_OVERRIDE set, which can cause spurious cache misses or build failures with the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")

python_bin = "python.exe" if ("windows" in platform) else "bin/python3"

Expand DownExpand Up@@ -294,7 +293,7 @@ For more information see {attr}`py_runtime.coverage_tool`.
mandatory = False,
),
"ignore_root_user_error": attr.bool(
default = False,
default = True,
doc = "Whether the check for root should be ignored or not. This causes cache misses with .pyc files.",
mandatory = False,
),
Expand Down
50 changes: 9 additions & 41 deletions tests/python/python_tests.bzl
Original file line numberDiff line numberDiff line change
Expand Up@@ -62,7 +62,7 @@ def _override(
auth_patterns = {},
available_python_versions = [],
base_url = "",
ignore_root_user_error = False,
ignore_root_user_error = True,
minor_mapping = {},
netrc = "",
register_all_versions = False):
Expand DownExpand Up@@ -139,7 +139,7 @@ def _test_default(env):
"ignore_root_user_error",
"tool_versions",
])
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(False)
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)
env.expect.that_str(py.default_python_version).equals("3.11")

want_toolchain = struct(
Expand DownExpand Up@@ -212,13 +212,13 @@ def _test_default_non_rules_python_ignore_root_user_error(env):
module_ctx = _mock_mctx(
_mod(
name = "my_module",
toolchain = [_toolchain("3.12", ignore_root_user_error = True)],
toolchain = [_toolchain("3.12", ignore_root_user_error = False)],
),
_mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
),
)

env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(False)
env.expect.that_str(py.default_python_version).equals("3.12")

my_module_toolchain = struct(
Expand All@@ -238,49 +238,17 @@ def _test_default_non_rules_python_ignore_root_user_error(env):

_tests.append(_test_default_non_rules_python_ignore_root_user_error)

def _test_default_non_rules_python_ignore_root_user_error_override(env):
py = parse_modules(
module_ctx = _mock_mctx(
_mod(
name = "my_module",
toolchain = [_toolchain("3.12")],
override = [_override(ignore_root_user_error = True)],
),
_mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
),
)

env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)
env.expect.that_str(py.default_python_version).equals("3.12")

my_module_toolchain = struct(
name = "python_3_12",
python_version = "3.12",
register_coverage_tool = False,
)
rules_python_toolchain = struct(
name = "python_3_11",
python_version = "3.11",
register_coverage_tool = False,
)
env.expect.that_collection(py.toolchains).contains_exactly([
rules_python_toolchain,
my_module_toolchain,
]).in_order()

_tests.append(_test_default_non_rules_python_ignore_root_user_error_override)

def _test_default_non_rules_python_ignore_root_user_error_non_root_module(env):
py = parse_modules(
module_ctx = _mock_mctx(
_mod(name = "my_module", toolchain = [_toolchain("3.13")]),
_mod(name = "some_module", toolchain = [_toolchain("3.12", ignore_root_user_error = True)]),
_mod(name = "some_module", toolchain = [_toolchain("3.12", ignore_root_user_error = False)]),
_mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
),
)

env.expect.that_str(py.default_python_version).equals("3.13")
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(False)
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)

my_module_toolchain = struct(
name = "python_3_13",
Expand DownExpand Up@@ -338,8 +306,8 @@ def _test_first_occurance_of_the_toolchain_wins(env):

env.expect.that_dict(py.debug_info).contains_exactly({
"toolchains_registered": [
{"ignore_root_user_error": False, "module": {"is_root": True, "name": "my_module"}, "name": "python_3_12"},
{"ignore_root_user_error": False, "module": {"is_root": False, "name": "rules_python"}, "name": "python_3_11"},
{"ignore_root_user_error": True, "module": {"is_root": True, "name": "my_module"}, "name": "python_3_12"},
{"ignore_root_user_error": True, "module": {"is_root": False, "name": "rules_python"}, "name": "python_3_11"},
],
})

Expand All@@ -364,7 +332,7 @@ def _test_auth_overrides(env):

env.expect.that_dict(py.config.default).contains_at_least({
"auth_patterns": {"foo": "bar"},
"ignore_root_user_error": False,
"ignore_root_user_error": True,
"netrc": "/my/netrc",
})
env.expect.that_str(py.default_python_version).equals("3.12")
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -59,6 +59,10 @@ Unreleased changes template.
* (pypi) The `ppc64le` is now pointing to the right target in the `platforms` package.
* (gazelle) No longer incorrectly merge `py_binary` targets during partial updates in
`file` generation mode. Fixed in [#2619](https://github.com/bazelbuild/rules_python/pull/2619).
* (bzlmod) Running as root is no longer an error. `ignore_root_user_error=True`
is now the default. Note that running as root may still cause spurious
Bazel cache invalidation
([#1169](https://github.com/bazelbuild/rules_python/issues/1169)).

{#v0-0-0-added}
### Added
Expand Down
39 changes: 16 additions & 23 deletions python/private/python.bzl
Original file line numberDiff line numberDiff line change
Expand Up@@ -72,9 +72,9 @@ def parse_modules(*, module_ctx, _fail = fail):
logger = repo_utils.logger(module_ctx, "python")

# if the root module does not register any toolchain then the
# ignore_root_user_error takes its default value: False
# ignore_root_user_error takes its default value: True
if not module_ctx.modules[0].tags.toolchain:
ignore_root_user_error = False
ignore_root_user_error = True

config = _get_toolchain_config(modules = module_ctx.modules, _fail = _fail)

Expand DownExpand Up@@ -559,7 +559,7 @@ def _create_toolchain_attrs_struct(*, tag = None, python_version = None, toolcha
is_default = is_default,
python_version = python_version if python_version else tag.python_version,
configure_coverage_tool = getattr(tag, "configure_coverage_tool", False),
ignore_root_user_error = getattr(tag, "ignore_root_user_error", False),
ignore_root_user_error = getattr(tag, "ignore_root_user_error", True),
)

def _get_bazel_version_specific_kwargs():
Expand DownExpand Up@@ -636,16 +636,18 @@ Then the python interpreter will be available as `my_python_name`.
doc = "Whether or not to configure the default coverage tool provided by `rules_python` for the compatible toolchains.",
),
"ignore_root_user_error": attr.bool(
default = False,
default = True,
doc = """\
If `False`, the Python runtime installation will be made read only. This improves
the ability for Bazel to cache it, but prevents the interpreter from creating
`.pyc` files for the standard library dynamically at runtime as they are loaded.

If `True`, the Python runtime installation is read-write. This allows the
interpreter to create `.pyc` files for the standard library, but, because they are
created as needed, it adversely affects Bazel's ability to cache the runtime and
can result in spurious build failures.
The Python runtime installation is made read only. This improves the ability for
Bazel to cache it by preventing the interpreter from creating `.pyc` files for
the standard library dynamically at runtime as they are loaded (this often leads
to spurious cache misses or build failures).

However, if the user is running Bazel as root, this read-onlyness is not
respected. Bazel will print a warning message when it detects that the runtime
installation is writable despite being made read only (i.e. it's running with
root access). If this attribute is set to `False`, Bazel will make it a hard
error to run with root access instead.
""",
mandatory = False,
),
Expand DownExpand Up@@ -690,17 +692,8 @@ dependencies are introduced.
default = DEFAULT_RELEASE_BASE_URL,
),
"ignore_root_user_error": attr.bool(
default = False,
doc = """\
If `False`, the Python runtime installation will be made read only. This improves
the ability for Bazel to cache it, but prevents the interpreter from creating
`.pyc` files for the standard library dynamically at runtime as they are loaded.

If `True`, the Python runtime installation is read-write. This allows the
interpreter to create `.pyc` files for the standard library, but, because they are
created as needed, it adversely affects Bazel's ability to cache the runtime and
can result in spurious build failures.
""",
default = True,
doc = """Deprecated; do not use. This attribute has no effect.""",
mandatory = False,
),
"minor_mapping": attr.string_dict(
Expand Down
55 changes: 27 additions & 28 deletions python/private/python_repository.bzl
Original file line numberDiff line numberDiff line change
Expand Up@@ -127,37 +127,36 @@ def _python_repository_impl(rctx):
# pycs being generated at runtime:
# * The pycs are not deterministic (they contain timestamps)
# * Multiple processes trying to write the same pycs can result in errors.
if not rctx.attr.ignore_root_user_error:
if "windows" not in platform:
lib_dir = "lib" if "windows" not in platform else "Lib"
if "windows" not in platform:
repo_utils.execute_checked(
rctx,
op = "python_repository.MakeReadOnly",
arguments = [repo_utils.which_checked(rctx, "chmod"), "-R", "ugo-w", "lib"],
logger = logger,
)

repo_utils.execute_checked(
rctx,
op = "python_repository.MakeReadOnly",
arguments = [repo_utils.which_checked(rctx, "chmod"), "-R", "ugo-w", lib_dir],
logger = logger,
)
exec_result = repo_utils.execute_unchecked(
fail_or_warn = logger.warn if rctx.attr.ignore_root_user_error else logger.fail
exec_result = repo_utils.execute_unchecked(
rctx,
op = "python_repository.TestReadOnly",
arguments = [repo_utils.which_checked(rctx, "touch"), "lib/.test"],
logger = logger,
)

# The issue with running as root is the installation is no longer
# read-only, so the problems due to pyc can resurface.
if exec_result.return_code == 0:
stdout = repo_utils.execute_checked_stdout(
rctx,
op = "python_repository.TestReadOnly",
arguments = [repo_utils.which_checked(rctx, "touch"), "{}/.test".format(lib_dir)],
op = "python_repository.GetUserId",
arguments = [repo_utils.which_checked(rctx, "id"), "-u"],
logger = logger,
)

# The issue with running as root is the installation is no longer
# read-only, so the problems due to pyc can resurface.
if exec_result.return_code == 0:
stdout = repo_utils.execute_checked_stdout(
rctx,
op = "python_repository.GetUserId",
arguments = [repo_utils.which_checked(rctx, "id"), "-u"],
logger = logger,
)
uid = int(stdout.strip())
if uid == 0:
fail("The current user is root, please run as non-root when using the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")
else:
fail("The current user has CAP_DAC_OVERRIDE set, please drop this capability when using the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")
uid = int(stdout.strip())
if uid == 0:
fail_or_warn("The current user is root, which can cause spurious cache misses or build failures with the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")
else:
fail_or_warn("The current user has CAP_DAC_OVERRIDE set, which can cause spurious cache misses or build failures with the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")

python_bin = "python.exe" if ("windows" in platform) else "bin/python3"

Expand DownExpand Up@@ -294,7 +293,7 @@ For more information see {attr}`py_runtime.coverage_tool`.
mandatory = False,
),
"ignore_root_user_error": attr.bool(
default = False,
default = True,
doc = "Whether the check for root should be ignored or not. This causes cache misses with .pyc files.",
mandatory = False,
),
Expand Down
50 changes: 9 additions & 41 deletions tests/python/python_tests.bzl
Original file line numberDiff line numberDiff line change
Expand Up@@ -62,7 +62,7 @@ def _override(
auth_patterns = {},
available_python_versions = [],
base_url = "",
ignore_root_user_error = False,
ignore_root_user_error = True,
minor_mapping = {},
netrc = "",
register_all_versions = False):
Expand DownExpand Up@@ -139,7 +139,7 @@ def _test_default(env):
"ignore_root_user_error",
"tool_versions",
])
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(False)
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)
env.expect.that_str(py.default_python_version).equals("3.11")

want_toolchain = struct(
Expand DownExpand Up@@ -212,13 +212,13 @@ def _test_default_non_rules_python_ignore_root_user_error(env):
module_ctx = _mock_mctx(
_mod(
name = "my_module",
toolchain = [_toolchain("3.12", ignore_root_user_error = True)],
toolchain = [_toolchain("3.12", ignore_root_user_error = False)],
),
_mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
),
)

env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(False)
env.expect.that_str(py.default_python_version).equals("3.12")

my_module_toolchain = struct(
Expand All@@ -238,49 +238,17 @@ def _test_default_non_rules_python_ignore_root_user_error(env):

_tests.append(_test_default_non_rules_python_ignore_root_user_error)

def _test_default_non_rules_python_ignore_root_user_error_override(env):
py = parse_modules(
module_ctx = _mock_mctx(
_mod(
name = "my_module",
toolchain = [_toolchain("3.12")],
override = [_override(ignore_root_user_error = True)],
),
_mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
),
)

env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)
env.expect.that_str(py.default_python_version).equals("3.12")

my_module_toolchain = struct(
name = "python_3_12",
python_version = "3.12",
register_coverage_tool = False,
)
rules_python_toolchain = struct(
name = "python_3_11",
python_version = "3.11",
register_coverage_tool = False,
)
env.expect.that_collection(py.toolchains).contains_exactly([
rules_python_toolchain,
my_module_toolchain,
]).in_order()

_tests.append(_test_default_non_rules_python_ignore_root_user_error_override)

def _test_default_non_rules_python_ignore_root_user_error_non_root_module(env):
py = parse_modules(
module_ctx = _mock_mctx(
_mod(name = "my_module", toolchain = [_toolchain("3.13")]),
_mod(name = "some_module", toolchain = [_toolchain("3.12", ignore_root_user_error = True)]),
_mod(name = "some_module", toolchain = [_toolchain("3.12", ignore_root_user_error = False)]),
_mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
),
)

env.expect.that_str(py.default_python_version).equals("3.13")
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(False)
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)

my_module_toolchain = struct(
name = "python_3_13",
Expand DownExpand Up@@ -338,8 +306,8 @@ def _test_first_occurance_of_the_toolchain_wins(env):

env.expect.that_dict(py.debug_info).contains_exactly({
"toolchains_registered": [
{"ignore_root_user_error": False, "module": {"is_root": True, "name": "my_module"}, "name": "python_3_12"},
{"ignore_root_user_error": False, "module": {"is_root": False, "name": "rules_python"}, "name": "python_3_11"},
{"ignore_root_user_error": True, "module": {"is_root": True, "name": "my_module"}, "name": "python_3_12"},
{"ignore_root_user_error": True, "module": {"is_root": False, "name": "rules_python"}, "name": "python_3_11"},
],
})

Expand All@@ -364,7 +332,7 @@ def _test_auth_overrides(env):

env.expect.that_dict(py.config.default).contains_at_least({
"auth_patterns": {"foo": "bar"},
"ignore_root_user_error": False,
"ignore_root_user_error": True,
"netrc": "/my/netrc",
})
env.expect.that_str(py.default_python_version).equals("3.12")
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -59,6 +59,10 @@ Unreleased changes template.
* (pypi) The `ppc64le` is now pointing to the right target in the `platforms` package.
* (gazelle) No longer incorrectly merge `py_binary` targets during partial updates in
`file` generation mode. Fixed in [#2619](https://github.com/bazelbuild/rules_python/pull/2619).
* (bzlmod) Running as root is no longer an error. `ignore_root_user_error=True`
is now the default. Note that running as root may still cause spurious
Bazel cache invalidation
([#1169](https://github.com/bazelbuild/rules_python/issues/1169)).

{#v0-0-0-added}
### Added
Expand Down
39 changes: 16 additions & 23 deletions python/private/python.bzl
Original file line numberDiff line numberDiff line change
Expand Up@@ -72,9 +72,9 @@ def parse_modules(*, module_ctx, _fail = fail):
logger = repo_utils.logger(module_ctx, "python")

# if the root module does not register any toolchain then the
# ignore_root_user_error takes its default value: False
# ignore_root_user_error takes its default value: True
if not module_ctx.modules[0].tags.toolchain:
ignore_root_user_error = False
ignore_root_user_error = True

config = _get_toolchain_config(modules = module_ctx.modules, _fail = _fail)

Expand DownExpand Up@@ -559,7 +559,7 @@ def _create_toolchain_attrs_struct(*, tag = None, python_version = None, toolcha
is_default = is_default,
python_version = python_version if python_version else tag.python_version,
configure_coverage_tool = getattr(tag, "configure_coverage_tool", False),
ignore_root_user_error = getattr(tag, "ignore_root_user_error", False),
ignore_root_user_error = getattr(tag, "ignore_root_user_error", True),
)

def _get_bazel_version_specific_kwargs():
Expand DownExpand Up@@ -636,16 +636,18 @@ Then the python interpreter will be available as `my_python_name`.
doc = "Whether or not to configure the default coverage tool provided by `rules_python` for the compatible toolchains.",
),
"ignore_root_user_error": attr.bool(
default = False,
default = True,
doc = """\
If `False`, the Python runtime installation will be made read only. This improves
the ability for Bazel to cache it, but prevents the interpreter from creating
`.pyc` files for the standard library dynamically at runtime as they are loaded.

If `True`, the Python runtime installation is read-write. This allows the
interpreter to create `.pyc` files for the standard library, but, because they are
created as needed, it adversely affects Bazel's ability to cache the runtime and
can result in spurious build failures.
The Python runtime installation is made read only. This improves the ability for
Bazel to cache it by preventing the interpreter from creating `.pyc` files for
the standard library dynamically at runtime as they are loaded (this often leads
to spurious cache misses or build failures).

However, if the user is running Bazel as root, this read-onlyness is not
respected. Bazel will print a warning message when it detects that the runtime
installation is writable despite being made read only (i.e. it's running with
root access). If this attribute is set to `False`, Bazel will make it a hard
error to run with root access instead.
""",
mandatory = False,
),
Expand DownExpand Up@@ -690,17 +692,8 @@ dependencies are introduced.
default = DEFAULT_RELEASE_BASE_URL,
),
"ignore_root_user_error": attr.bool(
default = False,
doc = """\
If `False`, the Python runtime installation will be made read only. This improves
the ability for Bazel to cache it, but prevents the interpreter from creating
`.pyc` files for the standard library dynamically at runtime as they are loaded.

If `True`, the Python runtime installation is read-write. This allows the
interpreter to create `.pyc` files for the standard library, but, because they are
created as needed, it adversely affects Bazel's ability to cache the runtime and
can result in spurious build failures.
""",
default = True,
doc = """Deprecated; do not use. This attribute has no effect.""",
mandatory = False,
),
"minor_mapping": attr.string_dict(
Expand Down
55 changes: 27 additions & 28 deletions python/private/python_repository.bzl
Original file line numberDiff line numberDiff line change
Expand Up@@ -127,37 +127,36 @@ def _python_repository_impl(rctx):
# pycs being generated at runtime:
# * The pycs are not deterministic (they contain timestamps)
# * Multiple processes trying to write the same pycs can result in errors.
if not rctx.attr.ignore_root_user_error:
if "windows" not in platform:
lib_dir = "lib" if "windows" not in platform else "Lib"
if "windows" not in platform:
repo_utils.execute_checked(
rctx,
op = "python_repository.MakeReadOnly",
arguments = [repo_utils.which_checked(rctx, "chmod"), "-R", "ugo-w", "lib"],
logger = logger,
)

repo_utils.execute_checked(
rctx,
op = "python_repository.MakeReadOnly",
arguments = [repo_utils.which_checked(rctx, "chmod"), "-R", "ugo-w", lib_dir],
logger = logger,
)
exec_result = repo_utils.execute_unchecked(
fail_or_warn = logger.warn if rctx.attr.ignore_root_user_error else logger.fail
exec_result = repo_utils.execute_unchecked(
rctx,
op = "python_repository.TestReadOnly",
arguments = [repo_utils.which_checked(rctx, "touch"), "lib/.test"],
logger = logger,
)

# The issue with running as root is the installation is no longer
# read-only, so the problems due to pyc can resurface.
if exec_result.return_code == 0:
stdout = repo_utils.execute_checked_stdout(
rctx,
op = "python_repository.TestReadOnly",
arguments = [repo_utils.which_checked(rctx, "touch"), "{}/.test".format(lib_dir)],
op = "python_repository.GetUserId",
arguments = [repo_utils.which_checked(rctx, "id"), "-u"],
logger = logger,
)

# The issue with running as root is the installation is no longer
# read-only, so the problems due to pyc can resurface.
if exec_result.return_code == 0:
stdout = repo_utils.execute_checked_stdout(
rctx,
op = "python_repository.GetUserId",
arguments = [repo_utils.which_checked(rctx, "id"), "-u"],
logger = logger,
)
uid = int(stdout.strip())
if uid == 0:
fail("The current user is root, please run as non-root when using the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")
else:
fail("The current user has CAP_DAC_OVERRIDE set, please drop this capability when using the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")
uid = int(stdout.strip())
if uid == 0:
fail_or_warn("The current user is root, which can cause spurious cache misses or build failures with the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")
else:
fail_or_warn("The current user has CAP_DAC_OVERRIDE set, which can cause spurious cache misses or build failures with the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")

python_bin = "python.exe" if ("windows" in platform) else "bin/python3"

Expand DownExpand Up@@ -294,7 +293,7 @@ For more information see {attr}`py_runtime.coverage_tool`.
mandatory = False,
),
"ignore_root_user_error": attr.bool(
default = False,
default = True,
doc = "Whether the check for root should be ignored or not. This causes cache misses with .pyc files.",
mandatory = False,
),
Expand Down
50 changes: 9 additions & 41 deletions tests/python/python_tests.bzl
Original file line numberDiff line numberDiff line change
Expand Up@@ -62,7 +62,7 @@ def _override(
auth_patterns = {},
available_python_versions = [],
base_url = "",
ignore_root_user_error = False,
ignore_root_user_error = True,
minor_mapping = {},
netrc = "",
register_all_versions = False):
Expand DownExpand Up@@ -139,7 +139,7 @@ def _test_default(env):
"ignore_root_user_error",
"tool_versions",
])
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(False)
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)
env.expect.that_str(py.default_python_version).equals("3.11")

want_toolchain = struct(
Expand DownExpand Up@@ -212,13 +212,13 @@ def _test_default_non_rules_python_ignore_root_user_error(env):
module_ctx = _mock_mctx(
_mod(
name = "my_module",
toolchain = [_toolchain("3.12", ignore_root_user_error = True)],
toolchain = [_toolchain("3.12", ignore_root_user_error = False)],
),
_mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
),
)

env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(False)
env.expect.that_str(py.default_python_version).equals("3.12")

my_module_toolchain = struct(
Expand All@@ -238,49 +238,17 @@ def _test_default_non_rules_python_ignore_root_user_error(env):

_tests.append(_test_default_non_rules_python_ignore_root_user_error)

def _test_default_non_rules_python_ignore_root_user_error_override(env):
py = parse_modules(
module_ctx = _mock_mctx(
_mod(
name = "my_module",
toolchain = [_toolchain("3.12")],
override = [_override(ignore_root_user_error = True)],
),
_mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
),
)

env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)
env.expect.that_str(py.default_python_version).equals("3.12")

my_module_toolchain = struct(
name = "python_3_12",
python_version = "3.12",
register_coverage_tool = False,
)
rules_python_toolchain = struct(
name = "python_3_11",
python_version = "3.11",
register_coverage_tool = False,
)
env.expect.that_collection(py.toolchains).contains_exactly([
rules_python_toolchain,
my_module_toolchain,
]).in_order()

_tests.append(_test_default_non_rules_python_ignore_root_user_error_override)

def _test_default_non_rules_python_ignore_root_user_error_non_root_module(env):
py = parse_modules(
module_ctx = _mock_mctx(
_mod(name = "my_module", toolchain = [_toolchain("3.13")]),
_mod(name = "some_module", toolchain = [_toolchain("3.12", ignore_root_user_error = True)]),
_mod(name = "some_module", toolchain = [_toolchain("3.12", ignore_root_user_error = False)]),
_mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
),
)

env.expect.that_str(py.default_python_version).equals("3.13")
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(False)
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)

my_module_toolchain = struct(
name = "python_3_13",
Expand DownExpand Up@@ -338,8 +306,8 @@ def _test_first_occurance_of_the_toolchain_wins(env):

env.expect.that_dict(py.debug_info).contains_exactly({
"toolchains_registered": [
{"ignore_root_user_error": False, "module": {"is_root": True, "name": "my_module"}, "name": "python_3_12"},
{"ignore_root_user_error": False, "module": {"is_root": False, "name": "rules_python"}, "name": "python_3_11"},
{"ignore_root_user_error": True, "module": {"is_root": True, "name": "my_module"}, "name": "python_3_12"},
{"ignore_root_user_error": True, "module": {"is_root": False, "name": "rules_python"}, "name": "python_3_11"},
],
})

Expand All@@ -364,7 +332,7 @@ def _test_auth_overrides(env):

env.expect.that_dict(py.config.default).contains_at_least({
"auth_patterns": {"foo": "bar"},
"ignore_root_user_error": False,
"ignore_root_user_error": True,
"netrc": "/my/netrc",
})
env.expect.that_str(py.default_python_version).equals("3.12")
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -59,6 +59,10 @@ Unreleased changes template.
* (pypi) The `ppc64le` is now pointing to the right target in the `platforms` package.
* (gazelle) No longer incorrectly merge `py_binary` targets during partial updates in
`file` generation mode. Fixed in [#2619](https://github.com/bazelbuild/rules_python/pull/2619).
* (bzlmod) Running as root is no longer an error. `ignore_root_user_error=True`
is now the default. Note that running as root may still cause spurious
Bazel cache invalidation
([#1169](https://github.com/bazelbuild/rules_python/issues/1169)).

{#v0-0-0-added}
### Added
Expand Down
39 changes: 16 additions & 23 deletions python/private/python.bzl
Original file line numberDiff line numberDiff line change
Expand Up@@ -72,9 +72,9 @@ def parse_modules(*, module_ctx, _fail = fail):
logger = repo_utils.logger(module_ctx, "python")

# if the root module does not register any toolchain then the
# ignore_root_user_error takes its default value: False
# ignore_root_user_error takes its default value: True
if not module_ctx.modules[0].tags.toolchain:
ignore_root_user_error = False
ignore_root_user_error = True

config = _get_toolchain_config(modules = module_ctx.modules, _fail = _fail)

Expand DownExpand Up@@ -559,7 +559,7 @@ def _create_toolchain_attrs_struct(*, tag = None, python_version = None, toolcha
is_default = is_default,
python_version = python_version if python_version else tag.python_version,
configure_coverage_tool = getattr(tag, "configure_coverage_tool", False),
ignore_root_user_error = getattr(tag, "ignore_root_user_error", False),
ignore_root_user_error = getattr(tag, "ignore_root_user_error", True),
)

def _get_bazel_version_specific_kwargs():
Expand DownExpand Up@@ -636,16 +636,18 @@ Then the python interpreter will be available as `my_python_name`.
doc = "Whether or not to configure the default coverage tool provided by `rules_python` for the compatible toolchains.",
),
"ignore_root_user_error": attr.bool(
default = False,
default = True,
doc = """\
If `False`, the Python runtime installation will be made read only. This improves
the ability for Bazel to cache it, but prevents the interpreter from creating
`.pyc` files for the standard library dynamically at runtime as they are loaded.

If `True`, the Python runtime installation is read-write. This allows the
interpreter to create `.pyc` files for the standard library, but, because they are
created as needed, it adversely affects Bazel's ability to cache the runtime and
can result in spurious build failures.
The Python runtime installation is made read only. This improves the ability for
Bazel to cache it by preventing the interpreter from creating `.pyc` files for
the standard library dynamically at runtime as they are loaded (this often leads
to spurious cache misses or build failures).

However, if the user is running Bazel as root, this read-onlyness is not
respected. Bazel will print a warning message when it detects that the runtime
installation is writable despite being made read only (i.e. it's running with
root access). If this attribute is set to `False`, Bazel will make it a hard
error to run with root access instead.
""",
mandatory = False,
),
Expand DownExpand Up@@ -690,17 +692,8 @@ dependencies are introduced.
default = DEFAULT_RELEASE_BASE_URL,
),
"ignore_root_user_error": attr.bool(
default = False,
doc = """\
If `False`, the Python runtime installation will be made read only. This improves
the ability for Bazel to cache it, but prevents the interpreter from creating
`.pyc` files for the standard library dynamically at runtime as they are loaded.

If `True`, the Python runtime installation is read-write. This allows the
interpreter to create `.pyc` files for the standard library, but, because they are
created as needed, it adversely affects Bazel's ability to cache the runtime and
can result in spurious build failures.
""",
default = True,
doc = """Deprecated; do not use. This attribute has no effect.""",
mandatory = False,
),
"minor_mapping": attr.string_dict(
Expand Down
55 changes: 27 additions & 28 deletions python/private/python_repository.bzl
Original file line numberDiff line numberDiff line change
Expand Up@@ -127,37 +127,36 @@ def _python_repository_impl(rctx):
# pycs being generated at runtime:
# * The pycs are not deterministic (they contain timestamps)
# * Multiple processes trying to write the same pycs can result in errors.
if not rctx.attr.ignore_root_user_error:
if "windows" not in platform:
lib_dir = "lib" if "windows" not in platform else "Lib"
if "windows" not in platform:
repo_utils.execute_checked(
rctx,
op = "python_repository.MakeReadOnly",
arguments = [repo_utils.which_checked(rctx, "chmod"), "-R", "ugo-w", "lib"],
logger = logger,
)

repo_utils.execute_checked(
rctx,
op = "python_repository.MakeReadOnly",
arguments = [repo_utils.which_checked(rctx, "chmod"), "-R", "ugo-w", lib_dir],
logger = logger,
)
exec_result = repo_utils.execute_unchecked(
fail_or_warn = logger.warn if rctx.attr.ignore_root_user_error else logger.fail
exec_result = repo_utils.execute_unchecked(
rctx,
op = "python_repository.TestReadOnly",
arguments = [repo_utils.which_checked(rctx, "touch"), "lib/.test"],
logger = logger,
)

# The issue with running as root is the installation is no longer
# read-only, so the problems due to pyc can resurface.
if exec_result.return_code == 0:
stdout = repo_utils.execute_checked_stdout(
rctx,
op = "python_repository.TestReadOnly",
arguments = [repo_utils.which_checked(rctx, "touch"), "{}/.test".format(lib_dir)],
op = "python_repository.GetUserId",
arguments = [repo_utils.which_checked(rctx, "id"), "-u"],
logger = logger,
)

# The issue with running as root is the installation is no longer
# read-only, so the problems due to pyc can resurface.
if exec_result.return_code == 0:
stdout = repo_utils.execute_checked_stdout(
rctx,
op = "python_repository.GetUserId",
arguments = [repo_utils.which_checked(rctx, "id"), "-u"],
logger = logger,
)
uid = int(stdout.strip())
if uid == 0:
fail("The current user is root, please run as non-root when using the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")
else:
fail("The current user has CAP_DAC_OVERRIDE set, please drop this capability when using the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")
uid = int(stdout.strip())
if uid == 0:
fail_or_warn("The current user is root, which can cause spurious cache misses or build failures with the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")
else:
fail_or_warn("The current user has CAP_DAC_OVERRIDE set, which can cause spurious cache misses or build failures with the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.")

python_bin = "python.exe" if ("windows" in platform) else "bin/python3"

Expand DownExpand Up@@ -294,7 +293,7 @@ For more information see {attr}`py_runtime.coverage_tool`.
mandatory = False,
),
"ignore_root_user_error": attr.bool(
default = False,
default = True,
doc = "Whether the check for root should be ignored or not. This causes cache misses with .pyc files.",
mandatory = False,
),
Expand Down
50 changes: 9 additions & 41 deletions tests/python/python_tests.bzl
Original file line numberDiff line numberDiff line change
Expand Up@@ -62,7 +62,7 @@ def _override(
auth_patterns = {},
available_python_versions = [],
base_url = "",
ignore_root_user_error = False,
ignore_root_user_error = True,
minor_mapping = {},
netrc = "",
register_all_versions = False):
Expand DownExpand Up@@ -139,7 +139,7 @@ def _test_default(env):
"ignore_root_user_error",
"tool_versions",
])
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(False)
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)
env.expect.that_str(py.default_python_version).equals("3.11")

want_toolchain = struct(
Expand DownExpand Up@@ -212,13 +212,13 @@ def _test_default_non_rules_python_ignore_root_user_error(env):
module_ctx = _mock_mctx(
_mod(
name = "my_module",
toolchain = [_toolchain("3.12", ignore_root_user_error = True)],
toolchain = [_toolchain("3.12", ignore_root_user_error = False)],
),
_mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
),
)

env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(False)
env.expect.that_str(py.default_python_version).equals("3.12")

my_module_toolchain = struct(
Expand All@@ -238,49 +238,17 @@ def _test_default_non_rules_python_ignore_root_user_error(env):

_tests.append(_test_default_non_rules_python_ignore_root_user_error)

def _test_default_non_rules_python_ignore_root_user_error_override(env):
py = parse_modules(
module_ctx = _mock_mctx(
_mod(
name = "my_module",
toolchain = [_toolchain("3.12")],
override = [_override(ignore_root_user_error = True)],
),
_mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
),
)

env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)
env.expect.that_str(py.default_python_version).equals("3.12")

my_module_toolchain = struct(
name = "python_3_12",
python_version = "3.12",
register_coverage_tool = False,
)
rules_python_toolchain = struct(
name = "python_3_11",
python_version = "3.11",
register_coverage_tool = False,
)
env.expect.that_collection(py.toolchains).contains_exactly([
rules_python_toolchain,
my_module_toolchain,
]).in_order()

_tests.append(_test_default_non_rules_python_ignore_root_user_error_override)

def _test_default_non_rules_python_ignore_root_user_error_non_root_module(env):
py = parse_modules(
module_ctx = _mock_mctx(
_mod(name = "my_module", toolchain = [_toolchain("3.13")]),
_mod(name = "some_module", toolchain = [_toolchain("3.12", ignore_root_user_error = True)]),
_mod(name = "some_module", toolchain = [_toolchain("3.12", ignore_root_user_error = False)]),
_mod(name = "rules_python", toolchain = [_toolchain("3.11")]),
),
)

env.expect.that_str(py.default_python_version).equals("3.13")
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(False)
env.expect.that_bool(py.config.default["ignore_root_user_error"]).equals(True)

my_module_toolchain = struct(
name = "python_3_13",
Expand DownExpand Up@@ -338,8 +306,8 @@ def _test_first_occurance_of_the_toolchain_wins(env):

env.expect.that_dict(py.debug_info).contains_exactly({
"toolchains_registered": [
{"ignore_root_user_error": False, "module": {"is_root": True, "name": "my_module"}, "name": "python_3_12"},
{"ignore_root_user_error": False, "module": {"is_root": False, "name": "rules_python"}, "name": "python_3_11"},
{"ignore_root_user_error": True, "module": {"is_root": True, "name": "my_module"}, "name": "python_3_12"},
{"ignore_root_user_error": True, "module": {"is_root": False, "name": "rules_python"}, "name": "python_3_11"},
],
})

Expand All@@ -364,7 +332,7 @@ def _test_auth_overrides(env):

env.expect.that_dict(py.config.default).contains_at_least({
"auth_patterns": {"foo": "bar"},
"ignore_root_user_error": False,
"ignore_root_user_error": True,
"netrc": "/my/netrc",
})
env.expect.that_str(py.default_python_version).equals("3.12")
Expand Down