Skip to content

Commit

Permalink
feat: add a virtual environment rule
Browse files Browse the repository at this point in the history
  • Loading branch information
oxidase committed Feb 21, 2024
1 parent 194e9be commit 22ea280
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 1 deletion.
8 changes: 7 additions & 1 deletion python/BUILD
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
py_binary(
name = "poetry_deps",
srcs = ["poetry_deps.py"],
visibility = ["//visibility:public"],
visibility = ["__subpackages__"],
deps = [
"@rules_poetry_pip//:pkg",
],
)

py_binary(
name = "py_venv",
srcs = ["py_venv.py"],
visibility = ["__subpackages__"],
)
48 changes: 48 additions & 0 deletions python/py_venv.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
def _py_venv_impl(ctx):
"""
Rule to link Python package into a virtual environment.
Arguments:
ctx: The rule context.
Attributes:
deps: label_list The package dependencies list.
Private attributes:
_py_venv:
Returns:
The providers list or a tuple with a venv package.
"""

deps = ctx.attr.deps
output = ctx.actions.declare_directory("venv/{}".format(ctx.label.name))
import_paths = ["{}/external/{}".format(ctx.bin_dir.path, path) for dep in deps for path in dep[PyInfo].imports.to_list()]

ctx.actions.run(
outputs = [output],
inputs = ctx.files.deps,
mnemonic = "CreateVenv",
progress_message = "Creating venv {}".format(ctx.label.name),
arguments = [output.path] + import_paths,
use_default_shell_env = True,
executable = ctx.executable._py_venv,
)

transitive_depsets = [dep[PyInfo].transitive_sources for dep in deps]
runfiles = [output] + [item for dep in transitive_depsets for item in dep.to_list()]
files = depset([output], transitive = transitive_depsets)

return [
DefaultInfo(files = files, runfiles = ctx.runfiles(files = runfiles)),
PyInfo(transitive_sources = files, imports = depset(["_main/" + output.short_path])),
]

py_venv = rule(
implementation = _py_venv_impl,
provides = [PyInfo],
attrs = {
"deps": attr.label_list(doc = "The package dependencies list"),
"_py_venv": attr.label(default = ":py_venv", cfg = "exec", executable = True),
},
)
30 changes: 30 additions & 0 deletions python/py_venv.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import argparse
from pathlib import Path

SKIP_SET = {"requirements.txt"}
MERGE_SET = {"bin"}

if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Download and install a Poetry package")

parser.add_argument("venv", type=Path, help="output environment directory")
parser.add_argument("path", type=Path, nargs="*", help="package path")

args = parser.parse_args()

args.venv.mkdir(parents=True, exist_ok=True)
for path in args.path:
if not path.exists():
raise RuntimeError(f"Required {path} does not exist")

for item in path.iterdir():
name = item.name
if name in SKIP_SET:
continue
if name in MERGE_SET:
continue # TODO

# Requires permits_treeartifact_uplevel_symlinks feature
symlink_path = args.venv / name
target_path = item.relative_to(args.venv, walk_up=True)
symlink_path.symlink_to(target_path, target_is_directory=True)

0 comments on commit 22ea280

Please sign in to comment.