| "" |
| |
| load("//experimental/rules_python_external:repositories.bzl", "all_requirements", "rules_python_external_dependencies") |
| |
| DEFAULT_REPOSITORY_NAME = "pip" |
| |
| def _pip_repository_impl(rctx): |
| python_interpreter = rctx.attr.python_interpreter |
| if rctx.attr.python_interpreter_target != None: |
| target = rctx.attr.python_interpreter_target |
| python_interpreter = rctx.path(target) |
| else: |
| if "/" not in python_interpreter: |
| python_interpreter = rctx.which(python_interpreter) |
| if not python_interpreter: |
| fail("python interpreter not found") |
| |
| rctx.file("BUILD", "") |
| |
| # Get the root directory of these rules |
| rules_root = rctx.path(Label("//:BUILD")).dirname |
| thirdparty_roots = [ |
| # Includes all the external dependencies from repositories.bzl |
| rctx.path(Label("@" + repo + "//:BUILD.bazel")).dirname |
| for repo in all_requirements |
| ] |
| separator = ":" if not "windows" in rctx.os.name.lower() else ";" |
| pypath = separator.join([str(p) for p in [rules_root] + thirdparty_roots]) |
| |
| args = [ |
| python_interpreter, |
| "-m", |
| "experimental.rules_python_external.extract_wheels", |
| "--requirements", |
| rctx.path(rctx.attr.requirements), |
| "--repo", |
| "@%s" % rctx.attr.name, |
| ] |
| |
| if rctx.attr.extra_pip_args: |
| args += [ |
| "--extra_pip_args", |
| struct(args = rctx.attr.extra_pip_args).to_json(), |
| ] |
| |
| if rctx.attr.pip_data_exclude: |
| args += [ |
| "--pip_data_exclude", |
| struct(exclude = rctx.attr.pip_data_exclude).to_json(), |
| ] |
| |
| if rctx.attr.enable_implicit_namespace_pkgs: |
| args.append("--enable_implicit_namespace_pkgs") |
| |
| result = rctx.execute( |
| args, |
| environment = { |
| # Manually construct the PYTHONPATH since we cannot use the toolchain here |
| "PYTHONPATH": pypath, |
| }, |
| timeout = rctx.attr.timeout, |
| quiet = rctx.attr.quiet, |
| ) |
| if result.return_code: |
| fail("rules_python_external failed: %s (%s)" % (result.stdout, result.stderr)) |
| |
| return |
| |
| pip_repository = repository_rule( |
| attrs = { |
| "enable_implicit_namespace_pkgs": attr.bool( |
| default = False, |
| doc = """ |
| If true, disables conversion of native namespace packages into pkg-util style namespace packages. When set all py_binary |
| and py_test targets must specify either `legacy_create_init=False` or the global Bazel option |
| `--incompatible_default_to_explicit_init_py` to prevent `__init__.py` being automatically generated in every directory. |
| |
| This option is required to support some packages which cannot handle the conversion to pkg-util style. |
| """, |
| ), |
| "extra_pip_args": attr.string_list( |
| doc = "Extra arguments to pass on to pip. Must not contain spaces.", |
| ), |
| "pip_data_exclude": attr.string_list( |
| doc = "Additional data exclusion parameters to add to the pip packages BUILD file.", |
| ), |
| "python_interpreter": attr.string(default = "python3"), |
| "python_interpreter_target": attr.label(allow_single_file = True, doc = """ |
| If you are using a custom python interpreter built by another repository rule, |
| use this attribute to specify its BUILD target. This allows pip_repository to invoke |
| pip using the same interpreter as your toolchain. If set, takes precedence over |
| python_interpreter. |
| """), |
| "quiet": attr.bool( |
| default = True, |
| doc = "If True, suppress printing stdout and stderr output to the terminal.", |
| ), |
| "requirements": attr.label( |
| allow_single_file = True, |
| mandatory = True, |
| doc = "A 'requirements.txt' pip requirements file.", |
| ), |
| # 600 is documented as default here: https://docs.bazel.build/versions/master/skylark/lib/repository_ctx.html#execute |
| "timeout": attr.int( |
| default = 600, |
| doc = "Timeout (in seconds) on the rule's execution duration.", |
| ), |
| }, |
| implementation = _pip_repository_impl, |
| doc = """A rule for importing `requirements.txt` dependencies into Bazel. |
| |
| This rule imports a `requirements.txt` file and generates a new |
| `requirements.bzl` file. This is used via the `WORKSPACE` pattern: |
| |
| ```python |
| pip_repository( |
| name = "foo", |
| requirements = ":requirements.txt", |
| ) |
| ``` |
| |
| You can then reference imported dependencies from your `BUILD` file with: |
| |
| ```python |
| load("@foo//:requirements.bzl", "requirement") |
| py_library( |
| name = "bar", |
| ... |
| deps = [ |
| "//my/other:dep", |
| requirement("requests"), |
| requirement("numpy"), |
| ], |
| ) |
| ``` |
| |
| Or alternatively: |
| ```python |
| load("@foo//:requirements.bzl", "all_requirements") |
| py_binary( |
| name = "baz", |
| ... |
| deps = [ |
| ":foo", |
| ] + all_requirements, |
| ) |
| ``` |
| """, |
| ) |
| |
| def pip_install(requirements, name = DEFAULT_REPOSITORY_NAME, **kwargs): |
| """Imports a `requirements.txt` file and generates a new `requirements.bzl` file. |
| |
| This is used via the `WORKSPACE` pattern: |
| |
| ```python |
| pip_install( |
| requirements = ":requirements.txt", |
| ) |
| ``` |
| |
| You can then reference imported dependencies from your `BUILD` file with: |
| |
| ```python |
| load("@pip//:requirements.bzl", "requirement") |
| py_library( |
| name = "bar", |
| ... |
| deps = [ |
| "//my/other:dep", |
| requirement("requests"), |
| requirement("numpy"), |
| ], |
| ) |
| ``` |
| |
| Args: |
| requirements: A 'requirements.txt' pip requirements file. |
| name: A unique name for the created external repository (default 'pip'). |
| **kwargs: Keyword arguments passed directly to the `pip_repository` repository rule. |
| """ |
| # Just in case our dependencies weren't already fetched |
| rules_python_external_dependencies() |
| |
| pip_repository( |
| name = name, |
| requirements = requirements, |
| **kwargs |
| ) |