Git Product home page Git Product logo

rules_python's Introduction

Python Rules for Bazel

Build status

Overview

This repository is the home of the core Python rules -- py_library, py_binary, py_test, py_proto_library, and related symbols that provide the basis for Python support in Bazel. It also contains package installation rules for integrating with PyPI and other indices.

Documentation for rules_python is at https://rules-python.readthedocs.io and in the Bazel Build Encyclopedia.

Examples live in the examples directory.

Currently, the core rules build into the Bazel binary, and the symbols in this repository are simple aliases. However, we are migrating the rules to Starlark and removing them from the Bazel binary. Therefore, the future-proof way to depend on Python rules is via this repository. SeeMigrating from the Bundled Rules below.

The core rules are stable. Their implementation in Bazel is subject to Bazel's backward compatibility policy. Once migrated to rules_python, they may evolve at a different rate, but this repository will still follow semantic versioning.

The Bazel community maintains this repository. Neither Google nor the Bazel team provides support for the code. However, this repository is part of the test suite used to vet new Bazel releases. See How to contribute page for information on our development workflow.

Documentation

For detailed documentation, see https://rules-python.readthedocs.io

Bzlmod support

  • Status: Beta
  • Full Feature Parity: No

See Bzlmod support for more details.

rules_python's People

Contributors

adzenith avatar aignas avatar alexeagle avatar brandjon avatar chrislovecnm avatar comius avatar dependabot[bot] avatar dillon-giacoppo avatar dougthor42 avatar f0rmiga avatar fmeum avatar gergelyfabian avatar groodt avatar hrfuller avatar keith avatar kormide avatar laurentlb avatar linzhp avatar martis42 avatar mattem avatar mattmoor avatar person142 avatar philsc avatar phst avatar pstradomski avatar rickeylev avatar tetsuok avatar trentontrees avatar uebelandre avatar vonschultz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rules_python's Issues

Pip fails dictionary lookup.

Requirements file of boto3==1.4.7 fails with

ERROR: /private/var/tmp/_bazel_edwardmcfarlane/ad32b996cb424fa4cc44a5c86220cb2f/external/pypi__boto3_1_4_7/BUILD:6:1: Traceback (most recent call last):
	File "/private/var/tmp/_bazel_edwardmcfarlane/ad32b996cb424fa4cc44a5c86220cb2f/external/pypi__boto3_1_4_7/BUILD", line 6
		py_library(name = "pkg", srcs = glob(["**/*.p..."]), <3 more arguments>)
	File "/private/var/tmp/_bazel_edwardmcfarlane/ad32b996cb424fa4cc44a5c86220cb2f/external/pypi__boto3_1_4_7/BUILD", line 13, in py_library
		requirement("botocore>=1.7.0,<1.8.0")
	File "/private/var/tmp/_bazel_edwardmcfarlane/ad32b996cb424fa4cc44a5c86220cb2f/external/pip/requirements.bzl", line 150, in requirement
		_requirements[name]
key "botocore>=1.7.0,<1.8.0" not found in dictionary.

Feature request: conda_import rule

Anaconda is a popular Python distribution for doing data science and ML. Their conda package manager is central to it and also very popular. It is Python-centric, but not exclusive to Python packages (for example, PhantomJS is included in the conda-forge repo for doing browser integration testing).

conda is a central part of the Python dev process for our organization, so having access to it within bazel would be a big step forward in terms of consolidating our workflow. bazel + conda means instant access to a huge set of analytics and visualization tooling, full integration testing, AND hermetic cross-platform builds (otherwise known as "the dream")!

At the same time, I'm not sure whether support would fit in as part of this repository, or whether it belongs elsewhere. So I'd like to begin a conversation of what this support would look like.

Support for pip --index-url, --extra-index-url, --find-links

Atm it doesn't seem possible to fetch wheels from anywhere except the default PyPI index. We have some custom built wheels that we store in a directory and commit with the repository, and want to use those with --find-links, not something from PyPI.

Generated binary file fails when asyncio imported

I created an example repository illustrating the issue:
https://github.com/kgreenek/bazel_asyncio

After building the :bazel_asyncio target, you would expect to run the command with:

./bazel-bin/bazel_asyncio

However, that command fails with the following traceback:

Traceback (most recent call last):
  File "/home/kevin/Projects/bazel_asyncio/bazel-bin/bazel_asyncio.runfiles/__main__/bazel_asyncio.py", line 1, in <module>
    import asyncio
  File "/home/kevin/Projects/bazel_asyncio/bazel-bin/bazel_asyncio.runfiles/pypi__asyncio_3_4_3/asyncio/__init__.py", line 9, in <module>
    from . import selectors
  File "/home/kevin/Projects/bazel_asyncio/bazel-bin/bazel_asyncio.runfiles/pypi__asyncio_3_4_3/asyncio/selectors.py", line 39
    "{!r}".format(fileobj)) from None
                               ^
SyntaxError: invalid syntax

It also fails if I explicitly use python3 to run that file:

python3 bazel-bin/bazel_asyncio

This seems to to be a good work-around. It works even without asyncio pip installed on my machine:

python3 bazel-bin/bazel_asyncio.runfiles/__main__/bazel_asyncio.py

PyYAML pip import fails with python3

Using this repository:

$ bazel run :hello
INFO: Found 1 target...
Target //:hello up-to-date:
  bazel-bin/hello
INFO: Elapsed time: 1.910s, Critical Path: 0.03s

INFO: Running command line: bazel-bin/hello
Hello
$

But forcing python 3 gives an error:

$ bazel run --python_path=/usr/local/bin/python3 :hello 
INFO: Found 1 target...
Target //:hello up-to-date:
  bazel-bin/hello
INFO: Elapsed time: 0.174s, Critical Path: 0.01s

INFO: Running command line: bazel-bin/hello
Traceback (most recent call last):
  File "/private/var/tmp/_bazel_hwright/0c1dcd494edf6933a7c645d083d11d41/execroot/foo/bazel-out/darwin_x86_64-fastbuild/bin/hello.runfiles/foo/hello.py", line 1, in <module>
    import yaml
  File "/private/var/tmp/_bazel_hwright/0c1dcd494edf6933a7c645d083d11d41/execroot/foo/bazel-out/darwin_x86_64-fastbuild/bin/hello.runfiles/pypi__PyYAML_3_12/yaml/__init__.py", line 2, in <module>
    from error import *
ModuleNotFoundError: No module named 'error'
ERROR: Non-zero return code '1' from command: Process exited with status 1.
$

This doesn't happen with all packages pulled in through pip, just PyYAML. I believe it might be related to the way that PyYAML handles py2/3 compat: instead of enabling the same code to run under both environments, PyYAML has a separate tree for each version internal to the package, and presumably selectively installs one or the other depending on the request. It doesn't look like Bazel is picking the right tree when running under python3.

Update documentation for utilizing py_library from nested folder

Hi Everyone,

My current folder structure is as follows:

WORKSPACE
analysis/
analysis/analysis.py
analysis/BUILD
analysis/analyzers/average.py
analysis/analyzers/BUILD

I am trying to import all the analyzers in my analysis.py file, and the two BUILD rules look as follows:

py_binary(
    name = "analysis",
    srcs = ["analysis.py"],
    deps = [
      "//analysis/analyzers",
      requirement("pandas"),
      requirement("plotly"),
    ],
)
py_library(
    name = "analyzers",
    srcs = ["average.py"],
    deps = [
       requirement("pandas"),
    ],
)

It seems like I have tried every way of attempting to import analyzers into my analysis.py file, including;

import analyzers
import analysis.analyzers
from analysis import analyzers
from analysis.analyzers import analyzers
from analysis.analyzers import average

along with those combinations with my WORKSPACE name.

Am I missing something obvious? thanks!

pip_import() chokes on requirement markers/specifiers

Summary

The requirements format supports not just version numbers but also markers for various options. When a dependency uses these indirectly for its own dependencies, it produces a breakage.

Reproduce case

WORKSPACE:

load("@io_bazel_rules_python//python:pip.bzl", "pip_repositories", "pip_import")
pip_repositories()
pip_import(
   name = "pip_deps",
   requirements = "//:requirements.txt",
 )
 load("@pip_deps//:requirements.bzl", "pip_install")
 pip_install()

requirements.txt:

 google-cloud-datastore==1.4.0

Observed

The generated file external/pypi__gapic_google_cloud_datastore_v1_0_15_3/BUILD contains:

package(default_visibility = ["//visibility:public"])

load("@pip_deps//:requirements.bzl", "requirement")

py_library(
     name = "pkg",
     srcs = glob(["**/*.py"]),
     data = glob(["**/*"], exclude=["**/*.py", "**/* *", "BUILD", "WORKSPACE"]),
     # This makes this directory a top-level in the python import
     # search path for anything that depends on this.
     imports = ["."],
     deps = [requirement("google-gax"),requirement("googleapis-common-protos[grpc]"),requirement("oauth2client"),requirement("proto-google-cloud-datastore-v1[grpc]")],
 )

Note in the above the [grpc] marker attached to some of these requirements. The "requirement()" function does not handle these properly, and the result is an error that looks like:

 key "googleapis_common_protos[grpc]" not found in dictionary

It seems like the code that generates the BUILD file here should omit the bracketed terms (these terms should factor in the particular version/variant fetched but not in the reference to the library).

Expected

Depending on google-cloud-datastore==1.4.0 (or any pip dependency) should work.

CentOS 7 failure

Version which works correctly

git_repository(
    name = "io_bazel_rules_python",
    remote = "https://github.com/bazelbuild/rules_python.git",
    commit = "8f3daa17c1e5050cf0382f7fe60c1cd7c7067f3c",
)

Broken version (latest master)

git_repository(
    name = "io_bazel_rules_python",
    remote = "https://github.com/bazelbuild/rules_python.git",
    commit = "3e167dcfb17356c68588715ed324c5e9b76f391d",
)

CentOS 7 and python 2.7 failure

ERROR: error loading package '': Encountered error while reading extension file 'requirements.bzl': no such package '@requirements_xunitmerge//': pip_import failed:  (Traceback (most recent call last):
  File "/usr/lib64/python2.7/runpy.py", line 162, in _run_module_as_main
    "__main__", fname, loader, pkg_name)
  File "/usr/lib64/python2.7/runpy.py", line 72, in _run_code
    exec code in run_globals
  File "/home/jenkins/.cache/bazel/_bazel_jenkins/cff2319697c0ff2c301458e924e0bef7/external/io_bazel_rules_python/tools/piptool.par/__main__.py", line 16, in <module>
  File "/home/jenkins/.cache/bazel/_bazel_jenkins/cff2319697c0ff2c301458e924e0bef7/external/io_bazel_rules_python/tools/piptool.par/subpar/runtime/support.py", line 228, in setup
  File "/home/jenkins/.cache/bazel/_bazel_jenkins/cff2319697c0ff2c301458e924e0bef7/external/io_bazel_rules_python/tools/piptool.par/subpar/runtime/support.py", line 209, in _setup_pkg_resources
TypeError: add() got an unexpected keyword argument 'replace'
)
INFO: Elapsed time: 4.130s
FAILED: Build did NOT complete successfully

WORKSPACE file

git_repository(
    name = "io_bazel_rules_python",
    remote = "https://github.com/bazelbuild/rules_python.git",
    commit = "3e167dcfb17356c68588715ed324c5e9b76f391d",
)
load("@io_bazel_rules_python//python:pip.bzl", "pip_repositories", "pip_import")
pip_repositories()

pip_import(name = "requirements_xunitmerge", requirements = "//py:requirements.xunitmerge.txt")
load("@requirements_xunitmerge//:requirements.bzl", pip_install_xunitmerge="pip_install")
pip_install_xunitmerge()

py/requirements/xunitmerge.txt

xunitmerge

pip_import rule with invalid requirements.txt file does not throw an error

With a directory layout like so:

/WORKSPACE
/common
/common/requirements.txt

None of the following attempts at the requirements string in pip_import worked:

"//common/requirements.txt"
"//common:requirements.txt"
"common/requirements.txt"

And the only failure reported was the load("@gs_pip_deps//:requirements.bzl", "pip_install") invocation. pip_import itself should report an error instead of silently succeeding.

To fix this I had to add a BUILD file to common, set it to visibility public, and use "//common:requirements.txt"

Lost permissions to execute

Description of the problem

I am using pyspark in my project and want to import pyspark using rules_python.

Here is my requirements.txt:
py4j==0.10.4
pyspark==2.2.0.post0

After bazel build, I found in ~/.cache/bazel/_bazel_xxx/xxxxx/external/pypi__pyspark_2_2_0/pyspark/bin and all files lost execute permission.

Environment info

  • Operating System
    Ubuntu 16.04 LTS

pip_import broken on Windows

pip_import doesn't work on Windows (funcsigs is a dependency for unittest, so it's pretty much required for every project).

$ bazel.exe build ...
Loading:
Loading: 0 packages loaded
ERROR: error loading package '': Encountered error while reading extension file 'requirements.bzl': no such package '@piptool_deps//': pip_import failed: Collecting pip==9.0.1 (from -r C:/users/asum/windev/rules_python/python/requirements.txt (line 1))
  Using cached pip-9.0.1-py2.py3-none-any.whl
  Saved c:\users\asum\appdata\local\temp\_bazel_asum\uub-8znx\external\piptool_deps\pip-9.0.1-py2.py3-none-any.whl
Collecting setuptools==38.2.4 (from -r C:/users/asum/windev/rules_python/python/requirements.txt (line 2))
  Using cached setuptools-38.2.4-py2.py3-none-any.whl
  Saved c:\users\asum\appdata\local\temp\_bazel_asum\uub-8znx\external\piptool_deps\setuptools-38.2.4-py2.py3-none-any.whl
Collecting wheel==0.30.0a0 (from -r C:/users/asum/windev/rules_python/python/requirements.txt (line 3))
  Using cached wheel-0.30.0a0-py2.py3-none-any.whl
  Saved c:\users\asum\appdata\local\temp\_bazel_asum\uub-8znx\external\piptool_deps\wheel-0.30.0a0-py2.py3-none-any.whl
Collecting mock==2.0.0 (from -r C:/users/asum/windev/rules_python/python/requirements.txt (line 6))
  Using cached mock-2.0.0-py2.py3-none-any.whl
  Saved c:\users\asum\appdata\local\temp\_bazel_asum\uub-8znx\external\piptool_deps\mock-2.0.0-py2.py3-none-any.whl
Collecting six>=1.9 (from mock==2.0.0->-r C:/users/asum/windev/rules_python/python/requirements.txt (line 6))
  Using cached six-1.11.0-py2.py3-none-any.whl
  Saved c:\users\asum\appdata\local\temp\_bazel_asum\uub-8znx\external\piptool_deps\six-1.11.0-py2.py3-none-any.whl
Collecting funcsigs>=1; python_version < "3.3" (from mock==2.0.0->-r C:/users/asum/windev/rules_python/python/requirements.txt (line 6))
  Using cached funcsigs-1.0.2-py2.py3-none-any.whl
  Saved c:\users\asum\appdata\local\temp\_bazel_asum\uub-8znx\external\piptool_deps\funcsigs-1.0.2-py2.py3-none-any.whl
Collecting pbr>=0.11 (from mock==2.0.0->-r C:/users/asum/windev/rules_python/python/requirements.txt (line 6))
  Using cached pbr-3.1.1-py2.py3-none-any.whl
  Saved c:\users\asum\appdata\local\temp\_bazel_asum\uub-8znx\external\piptool_deps\pbr-3.1.1-py2.py3-none-any.whl
Skipping pip, due to already being wheel.
Skipping setuptools, due to already being wheel.
Skipping wheel, due to already being wheel.
Skipping mock, due to already being wheel.
Skipping six, due to already being wheel.
Skipping funcsigs, due to already being wheel.
Skipping pbr, due to already being wheel.
 (Traceback (most recent call last):
  File "C:\Python27\lib\runpy.py", line 174, in _run_module_as_main
    "__main__", fname, loader, pkg_name)
  File "C:\Python27\lib\runpy.py", line 72, in _run_code
    exec code in run_globals
  File "C:\users\asum\windev\rules_python\tools\piptool.par\__main__.py", line 232, in <module>
  File "C:\users\asum\windev\rules_python\tools\piptool.par\__main__.py", line 174, in main
  File "C:\users\asum\windev\rules_python\tools\piptool.par\__main__.py", line 155, in determine_possible_extras
  File "C:\users\asum\windev\rules_python\tools\piptool.par\__main__.py", line 155, in <dictcomp>
  File "C:\users\asum\windev\rules_python\tools\piptool.par\io_bazel_rules_python\rules_python\whl.py", line 102, in extras
  File "C:\users\asum\windev\rules_python\tools\piptool.par\io_bazel_rules_python\rules_python\whl.py", line 68, in metadata
  File "C:\Python27\lib\zipfile.py", line 961, in open
    zinfo = self.getinfo(name)
  File "C:\Python27\lib\zipfile.py", line 909, in getinfo
    'There is no item named %r in the archive' % name)
KeyError: "There is no item named 'funcsigs-1.0.2.dist-info\\\\METADATA' in the archive"
)
ERROR: error loading package '': Encountered error while reading extension file 'requirements.bzl': no such package '@piptool_deps//': pip_import failed: Collecting pip==9.0.1 (from -r C:/users/asum/windev/rules_python/python/requirements.txt (line 1))
  Using cached pip-9.0.1-py2.py3-none-any.whl
  Saved c:\users\asum\appdata\local\temp\_bazel_asum\uub-8znx\external\piptool_deps\pip-9.0.1-py2.py3-none-any.whl
Collecting setuptools==38.2.4 (from -r C:/users/asum/windev/rules_python/python/requirements.txt (line 2))
  Using cached setuptools-38.2.4-py2.py3-none-any.whl
  Saved c:\users\asum\appdata\local\temp\_bazel_asum\uub-8znx\external\piptool_deps\setuptools-38.2.4-py2.py3-none-any.whl
Collecting wheel==0.30.0a0 (from -r C:/users/asum/windev/rules_python/python/requirements.txt (line 3))
  Using cached wheel-0.30.0a0-py2.py3-none-any.whl
  Saved c:\users\asum\appdata\local\temp\_bazel_asum\uub-8znx\external\piptool_deps\wheel-0.30.0a0-py2.py3-none-any.whl
Collecting mock==2.0.0 (from -r C:/users/asum/windev/rules_python/python/requirements.txt (line 6))
  Using cached mock-2.0.0-py2.py3-none-any.whl
  Saved c:\users\asum\appdata\local\temp\_bazel_asum\uub-8znx\external\piptool_deps\mock-2.0.0-py2.py3-none-any.whl
Collecting six>=1.9 (from mock==2.0.0->-r C:/users/asum/windev/rules_python/python/requirements.txt (line 6))
  Using cached six-1.11.0-py2.py3-none-any.whl
  Saved c:\users\asum\appdata\local\temp\_bazel_asum\uub-8znx\external\piptool_deps\six-1.11.0-py2.py3-none-any.whl
Collecting funcsigs>=1; python_version < "3.3" (from mock==2.0.0->-r C:/users/asum/windev/rules_python/python/requirements.txt (line 6))
  Using cached funcsigs-1.0.2-py2.py3-none-any.whl
  Saved c:\users\asum\appdata\local\temp\_bazel_asum\uub-8znx\external\piptool_deps\funcsigs-1.0.2-py2.py3-none-any.whl
Collecting pbr>=0.11 (from mock==2.0.0->-r C:/users/asum/windev/rules_python/python/requirements.txt (line 6))
  Using cached pbr-3.1.1-py2.py3-none-any.whl
  Saved c:\users\asum\appdata\local\temp\_bazel_asum\uub-8znx\external\piptool_deps\pbr-3.1.1-py2.py3-none-any.whl
Skipping pip, due to already being wheel.
Skipping setuptools, due to already being wheel.
Skipping wheel, due to already being wheel.
Skipping mock, due to already being wheel.
Skipping six, due to already being wheel.
Skipping funcsigs, due to already being wheel.
Skipping pbr, due to already being wheel.
 (Traceback (most recent call last):
  File "C:\Python27\lib\runpy.py", line 174, in _run_module_as_main
    "__main__", fname, loader, pkg_name)
  File "C:\Python27\lib\runpy.py", line 72, in _run_code
    exec code in run_globals
  File "C:\users\asum\windev\rules_python\tools\piptool.par\__main__.py", line 232, in <module>
  File "C:\users\asum\windev\rules_python\tools\piptool.par\__main__.py", line 174, in main
  File "C:\users\asum\windev\rules_python\tools\piptool.par\__main__.py", line 155, in determine_possible_extras
  File "C:\users\asum\windev\rules_python\tools\piptool.par\__main__.py", line 155, in <dictcomp>
  File "C:\users\asum\windev\rules_python\tools\piptool.par\io_bazel_rules_python\rules_python\whl.py", line 102, in extras
  File "C:\users\asum\windev\rules_python\tools\piptool.par\io_bazel_rules_python\rules_python\whl.py", line 68, in metadata
  File "C:\Python27\lib\zipfile.py", line 961, in open
    zinfo = self.getinfo(name)
  File "C:\Python27\lib\zipfile.py", line 909, in getinfo
    'There is no item named %r in the archive' % name)
KeyError: "There is no item named 'funcsigs-1.0.2.dist-info\\\\METADATA' in the archive"
)
INFO: Elapsed time: 8,251s
FAILED: Build did NOT complete successfully (0 packages loaded)

Specify different interpreter for py_binary rules

Hey all,

Problem Statement

It's quite confusing how a user can specify that a given rule need to use Python3 or that rule need Python2, where we have multiple attributes documented like default_python_version, srcs_version and undocumented like :py_interpreter (which can't be used but out of curiosity what is the prefix : doing), however I am still confused regarding what does what, in the end, what I think we should have is a way to tell py_binary to execute using the interpreter X, I know about py_runtime which is supposed to be the future but there are multiple py_binary rules out there with unspecify python version which lead to issues of python3 and 2 compatibilities, beside there is an assumption with the introduced with --python_top argument that all code need one interepreter instead of having local to each target.

Workaround

Currently we use the following workaround, maybe useful for others that suffer same issue:

  • When building default to python2 since that's the safest bet and what most extensions requires.
  • When running a target we dynamically switch to the needed interpreter with something similar to decorator pattern, as described below:

The Decorator Pattern

  1. Create a python file with the following format, let's name it force_python3.py:
import sys
import os


PYTHON3_PATH = os.environ['PYTHON3_PATH']


if __name__ == '__main__':
    argv = sys.argv
    path, _ = os.path.split(argv[0])
    argv[0] = os.path.join(path, '<real main file>')
    argv = [PYTHON3_PATH] + argv

    os.execv(argv[0], argv)

(This assume that we expose a PYTHON3_PATH environment variable with --action_env=PYTHON3_PATH=<path>).

  1. Write py_binary rule by pointing main attribute to the file above:
py_binary(
    name = "my_target",
    srcs = ["main.py", "force_python3.py"],
    main = "force_python3.py"
)

FYI we have a workaround of our own to specify which python to use for pip requirements.

Goals

What I would like to see are the followings:

  • Add an attribute to py_binary and py_test rules to specify which python interpreter to use, it should be a label and point to a py_runtime, if not specified revert to default py_runtime (for backward compatibility too).
    • this means dropping probably --python_top argument b/c the assumption that you need one global Python version is bogus as explained above.
  • py_runtime should be specified in WORKSPACE so that rules that download dependencies can use it, e.g. https://github.com/bazelbuild/rules_python#importing-pip-dependencies.
  • Build on previous point, if py_runtime works in same say way go rules do, which use the new toolchains idea it will be awesome.
    • but keep in mind that's you may want to package the py_binary result somehow e.g. par, pex, docker image and shipping the interpreter with it will be very convenient, or at least not have a hardcoded python path (which is what you get if you point the py_runtime to an host binary path).

Unused requirements are still installed

We have a collection of federated repositories which share a reference to a canonical set of third-party dependencies. There is a single requirements.txt which is imported by every repository, and not every repository uses all the entries in that requirements file.

I'm discovering that the pip_import rules install everything in the requirements file, regardless of whether it is needed in the dependencies of the target currently begin built. This is suboptimal, particularly since some packages may have errors which prevent them from being built on the given system.

Instead, we should only install the package set required to satisfy the required build targets.

Direct dependency `funcsigs` of pip_imported `mock` package not available

A minimal repro needs multiple files (also in the attachment funcsigs-repro.tar.gz for convenience):

WORKSPACE:

git_repository(
    name = "io_bazel_rules_python",
    remote = "https://github.com/bazelbuild/rules_python.git",
    commit = "346b898e15e75f832b89e5da6a78ee79593237f0"
)

load("@io_bazel_rules_python//python:pip.bzl", "pip_import")
pip_import(
   name = "requirements",
   requirements = "//:requirements.txt",
)

load("@requirements//:requirements.bzl", "pip_install")
pip_install()

BUILD:

exports_files(["requirements.txt"])

load("@requirements//:requirements.bzl", "requirement")

py_binary(
    name="run",
    srcs=["run.py"],
    deps=[
        requirement('mock'),
    ],
)

requirements.txt:

mock==2.0.0

run.py:

import mock

Then to reproduce run:

$ bazel run //:run
INFO: Found 1 target...
Target //:run up-to-date:
  bazel-bin/run
INFO: Elapsed time: 1.138s, Critical Path: 0.03s

INFO: Running command line: bazel-bin/run
Traceback (most recent call last):
  File "/private/var/tmp/_bazel_nik/5a08842075e4d3de340bcb5074db6110/execroot/__main__/bazel-out/darwin_x86_64-fastbuild/bin/run.runfiles/__main__/run.py", line 1, in <module>
    import mock
  File "/private/var/tmp/_bazel_nik/5a08842075e4d3de340bcb5074db6110/execroot/__main__/bazel-out/darwin_x86_64-fastbuild/bin/run.runfiles/pypi__mock_2_0_0/mock/__init__.py", line 2, in <module>
    import mock.mock as _mock
  File "/private/var/tmp/_bazel_nik/5a08842075e4d3de340bcb5074db6110/execroot/__main__/bazel-out/darwin_x86_64-fastbuild/bin/run.runfiles/pypi__mock_2_0_0/mock/mock.py", line 80, in <module>
    import funcsigs
ImportError: No module named funcsigs
ERROR: Non-zero return code '1' from command: Process exited with status 1.

Important: This doesn't reproduce if funcsigs is globally installed (which it will be on many Linuxes). So you may have to do the following:

$ virtualenv --no-site-packages venv
$ source venv/bin/activate
$ bazel run //:run
...

I can reproduce this with bazel 0.7 on OS X Sierra and Ubuntu 16.04. As far as I can tell, the funcsigs wheel gets pulled when requirements.txt is processed but when the Python dependencies for the build target are put together, it's somehow not included. I can see it's not in bazel-bin/run.runfiles, even though the other dependencies (six and pbr) are.

I did notice that the version specification at https://github.com/testing-cabal/mock/blob/master/requirements.txt#L1 is a bit funky (funcsigs>=1;python_version<"3.3"). Maybe it's failing to parse that correctly?

supporting google_cloud_core[grpc] style package names

Trying to pip package google-cloud-logging, which depends on the strangely named google_cloud_core[grpc] package causes problems.
if my py_image target has a layer thats looking for requirement("google-cloud-logging"), I get the following:

ubuntu@ubuntu-xenial:/code$ bazel build app:app_image
ERROR: /home/ubuntu/.cache/bazel/_bazel_ubuntu/3a20ab50938efa4ee22db80044ad3a50/external/pypi__google_cloud_logging_1_4_0/BUILD:6:1: Traceback (most recent call last):
File "/home/ubuntu/.cache/bazel/_bazel_ubuntu/3a20ab50938efa4ee22db80044ad3a50/external/pypi__google_cloud_logging_1_4_0/BUILD", line 6
py_library(name = "pkg", srcs = glob(["**/*.p..."]), <3 more arguments>)
File "/home/ubuntu/.cache/bazel/_bazel_ubuntu/3a20ab50938efa4ee22db80044ad3a50/external/pypi__google_cloud_logging_1_4_0/BUILD", line 13, in py_library
requirement("google-cloud-core[grpc]")
File "/home/ubuntu/.cache/bazel/_bazel_ubuntu/3a20ab50938efa4ee22db80044ad3a50/external/gcloud_minimal/requirements.bzl", line 290, in requirement
_requirements[name]
key "google_cloud_core[grpc]" not found in dictionary
ERROR: /code/app/BUILD:37:1: no such target '@pypi__google_cloud_logging_1_4_0//:pkg': target 'pkg' not declared in package '' defined by /home/ubuntu/.cache/bazel/_bazel_ubuntu/3a20ab50938efa4ee22db80044ad3a50/external/pypi__google_cloud_logging_1_4_0/BUILD and referenced by '//app:app_image.binary'

Many other pip packages install fine this way, just not the google cloud ones that end up looking for grpc this way, such as googleapis-common-protos[grpc]

Any help would be great.

Support pyd creation

Description of the problem / feature request / question:

Native Python modules on Windows need to have a .pyd extension (or they will not be recognized).
cc_binary does not handle correctly if name is e.g. foo.pyd.
You get error message like:

ERROR: ... in linkshared attribute of cc_binary rule //foo.pyd: 'linkshared' used in non-shared library.

If possible, provide a minimal example to reproduce the problem:

cc_binary(
    name = "foo.pyd",
    linkstatic = 1,
    linkshared = 1,
)

Environment info

  • Operating System: Windows 10
  • Bazel version (output of bazel info release): 0.4.4

Have you found anything relevant by searching the web? (e.g. GitHub issues, email threads in the [email protected] archive)

Nope

Anything else, information or logs or outputs that would be helpful?

So IMO either allow any name to be a shared library or perhaps provide a py_cc_library, which handles this specially.

Dependencies missing with jupyter

I created a jupyter.py file like this:

from IPython import embed
embed()

With BUILD:

load("@pip_packages//:requirements.bzl", "requirement")
py_binary(
    name = "jupyter",
    srcs = ["jupyter.py"],
    data = [
        requirement("jupyter"),
    ],
)

When I run

bazel build --python_path=$(which python3) :jupyter

I get the error

$ bazel build :jupyter 
ERROR: /home/fortuna/.cache/bazel/_bazel_fortuna/deb90c3b6b4ea18fe8c1b7e149bb5ceb/external/pypi__jupyter_console_5_2_0/BUILD:15:1: Traceback (most recent call last):
	File "/home/fortuna/.cache/bazel/_bazel_fortuna/deb90c3b6b4ea18fe8c1b7e149bb5ceb/external/pypi__jupyter_console_5_2_0/BUILD", line 15
		py_library(name = "test", deps = [":pkg", req...")])
	File "/home/fortuna/.cache/bazel/_bazel_fortuna/deb90c3b6b4ea18fe8c1b7e149bb5ceb/external/pypi__jupyter_console_5_2_0/BUILD", line 18, in py_library
		requirement("mock")
	File "/home/fortuna/.cache/bazel/_bazel_fortuna/deb90c3b6b4ea18fe8c1b7e149bb5ceb/external/pip_packages/requirements.bzl", line 666, in requirement
		fail(("Could not find pip-provided de...))
Could not find pip-provided dependency: 'mock'
ERROR: /home/fortuna/.cache/bazel/_bazel_fortuna/deb90c3b6b4ea18fe8c1b7e149bb5ceb/external/pypi__jupyter_console_5_2_0/BUILD:6:1: Target '@pypi__jupyter_console_5_2_0//:jupyter_console-5.2.0.dist-info/DESCRIPTION.rst' contains an error and its package is in error and referenced by '@pypi__jupyter_console_5_2_0//:pkg'
...

I have jupyter in my requirements.txt file.

Support transitive dependencies

https://docs.bazel.build/versions/master/external.html#transitive-dependencies explicitly says that WORKSPACE files must include all transitive dependencies for a project. That make sense, given the very really possibility of conflicting version requirements for dependent projects.

However, one of the promises of pip is that transitive dependencies are resolved and installed automatically. With the Bazel python rules, using an external package which internally uses pip_import and friends means that we loose the transitive dependency resolution of pip for that package. The conflicting version problem hasn't gone away, since it is still possible it could occur with the pip_import-managed packages, we've just made life harder on ourselves.

It would be nice to have a way of pulling down the transitive dependencies for external Bazel projects using the pip_import infrastructure. This could be in the form of generate_workspace from Java-land, or it could be a way to pull down those dependencies and create a master requirements file, similar to pip-compile. In any case, manually managing dependencies for external Bazel projects is going to get unwieldy very quickly.

Documentation should note that "{HEAD}" is not a valid ref

I was getting the following error:

$ bazel build
ERROR: error loading package '': Encountered error while reading extension file 'python/pip.bzl': no such package '@io_bazel_rules_python//python': Invalid branch, tag, or commit: Ref {HEAD} can not be resolved
ERROR: error loading package '': Encountered error while reading extension file 'python/pip.bzl': no such package '@io_bazel_rules_python//python': Invalid branch, tag, or commit: Ref {HEAD} can not be resolved

and found pubref/rules_node#15.

Unable to load package

OSX Bazel 0.5.4 (also 0.5.2)
Constantly getting
ERROR: error loading package '': Extension file not found. Unable to load package for '//python:pip.bzl': BUILD file not found on package path.
after following setup instructions. Won't even get past this part in WORKSPACE

git_repository(
    name = "io_bazel_rules_python",
    remote = "https://github.com/bazelbuild/rules_python.git",
    commit = "d6fbb0fb2a5c8e318dd4de5104dc41358cefaa90",
)

# Only needed for PIP support:
load("//python:pip.bzl", "pip_repositories")

pip_import doesn't work with tensorflow

WORKSPACE

http_archive(
    name = "io_bazel_rules_python",
    sha256 = "0d1810fecc1bf2b6979d2af60e157d93d3004805bd8b7fda6eb52dda13480dca",
    strip_prefix = "rules_python-115e3a0dab4291184fdcb0d4e564a0328364571a",
    urls = ["https://github.com/bazelbuild/rules_python/archive/115e3a0.tar.gz"],
)

load("@io_bazel_rules_python//python:pip.bzl", "pip_import")

pip_import(
    name = "pip",
    requirements = "//:requirements.txt",
)

load("@pip//:requirements.bzl", "pip_install")

pip_install()

requirements.txt

tensorflow

BUILD

load("@io_bazel_rules_python//python:python.bzl", "py_binary")
load("@pip//:requirements.bzl", "requirement")

py_binary(
    name = "test",
    srcs = ["test.py"],
    deps = [
        requirement("tensorflow"),
    ],
)

test.py

import tensorflow as tf

if __name__ == "__main__":
  pass

Raises:
ImportError: 'No module named tensorflow'

pip_import doesn't respect --python_path

Presently, if a library on PyPI shows different dependencies when pulled in from Python 3, rules_python will still pull in the Python 2 requirements. For example, grpcio has a cp27 and a cp36 wheel. The metadata.json in the cp27 version depends on enum34, and the cp36 one doesn't. If you're using bazel build --python_path="python3.6" then this can mess things up, as enum34 shadows the Python 3 stdlib's enum, but doesn't provide all the same attributes.

I've tracked the cause down to this call:

result = repository_ctx.execute([
"python", repository_ctx.path(repository_ctx.attr._script),
"--name", repository_ctx.attr.name,
"--input", repository_ctx.path(repository_ctx.attr.requirements),
"--output", repository_ctx.path("requirements.bzl"),
"--directory", repository_ctx.path(""),
])

In line 27, you'll note piptool.par is invoked with just python. As far as I can tell, this means piptool queries the cheeseshop with 2.7 as its version, and therefore gets the cp27 wheels.

This has taken me quite a while to figure out, and I'm happy to go the last mile and submit a PR for this, but I'm not sure of the best way to go about it. I can't figure out a way to access the --python_path arg from the Skylark script. Next idea was to piptool.par with par_binary as part of the pip_import process, in the hope that it would set the correct hashbang (i.e. would follow the --python_path) for the par, so that the par could be executed directly rather than as an arg to python. I assume there's a reason you've set it up to have the par be manually built, though.

Python 3: Dependency with cffi-generated interface can't be imported

Using this sample repository:

$ bazel test :bcrypt_test 
..........................................................
INFO: Found 1 test target...
FAIL: //:bcrypt_test (see /private/var/tmp/_bazel_hwright/a6f0babcfe493d859400aa369b17eeeb/execroot/foo/bazel-out/darwin_x86_64-py3-fastbuild/testlogs/bcrypt_test/test.log).
INFO: From Testing //:bcrypt_test:
==================== Test output for //:bcrypt_test:
Traceback (most recent call last):
  File "/private/var/tmp/_bazel_hwright/a6f0babcfe493d859400aa369b17eeeb/bazel-sandbox/9102214513008705191/execroot/foo/bazel-out/darwin_x86_64-py3-fastbuild/bin/bcrypt_test.runfiles/foo/bcrypt_test.py", line 4, in <module>
    import bcrypt
  File "/private/var/tmp/_bazel_hwright/a6f0babcfe493d859400aa369b17eeeb/bazel-sandbox/9102214513008705191/execroot/foo/bazel-out/darwin_x86_64-py3-fastbuild/bin/bcrypt_test.runfiles/pypi__bcrypt_3_1_4/bcrypt/__init__.py", line 25, in <module>
    from bcrypt import _bcrypt
ImportError: dlopen(/private/var/tmp/_bazel_hwright/a6f0babcfe493d859400aa369b17eeeb/bazel-sandbox/9102214513008705191/execroot/foo/bazel-out/darwin_x86_64-py3-fastbuild/bin/bcrypt_test.runfiles/pypi__bcrypt_3_1_4/bcrypt/_bcrypt.so, 2): Symbol not found: _PyInt_FromLong
  Referenced from: /private/var/tmp/_bazel_hwright/a6f0babcfe493d859400aa369b17eeeb/bazel-sandbox/9102214513008705191/execroot/foo/bazel-out/darwin_x86_64-py3-fastbuild/bin/bcrypt_test.runfiles/pypi__bcrypt_3_1_4/bcrypt/_bcrypt.so
  Expected in: flat namespace
 in /private/var/tmp/_bazel_hwright/a6f0babcfe493d859400aa369b17eeeb/bazel-sandbox/9102214513008705191/execroot/foo/bazel-out/darwin_x86_64-py3-fastbuild/bin/bcrypt_test.runfiles/pypi__bcrypt_3_1_4/bcrypt/_bcrypt.so
================================================================================
Target //:bcrypt_test up-to-date:
  bazel-bin/bcrypt_test
INFO: Elapsed time: 16.377s, Critical Path: 0.37s
//:bcrypt_test                                                           FAILED in 0.3s
  /private/var/tmp/_bazel_hwright/a6f0babcfe493d859400aa369b17eeeb/execroot/foo/bazel-out/darwin_x86_64-py3-fastbuild/testlogs/bcrypt_test/test.log

Executed 1 out of 1 test: 1 fails locally.

I'm not sure if this specific to bcrypt, or more general (hence the issue description). Since this works under python 2, and the module API changed between them, I wonder if this is part of the same underlying problem as #33. Because we're running the import tooling inside of Bazel under python2, the wrong package gets pulled from pypi, and hence doesn't work with python 3.

If it is the same as #33, feel free to de-dupe (and make of of these a more general "python 3" issue).

"Extras" support

We currently don't support "extras", which look like: googleapis_common_protos[grpc] (this uses the grpc extras).

google-cloud is an example that exhibits this (it depends on the above w/ extras).

Need a method to build native libraries for pip imports

This is probably a long-term / aspirational wish, though I'd be happy to be shown otherwise.

Packages installed via pip often depend on native libraries. As a random example, https://pypi.python.org/pypi/cairocffi is a set of python bindings around the native cairo library. The bindings themselves don't include the native library, but they do require it to be installed on the system in a way which can be found by setuptools when compiling native code to build the relevant wheel.

It seems like requiring a system-installed library (which could change between builds) would violate hermeticity. We should instead provide a way to specify the dependent libraries required for a given pip import as Bazel targets, which can themselves be build.

This is likely related to bazelbuild/bazel#1475

Windows support

We should get rules_python to a state where Bazel can enable:

                {"node": "windows-x86_64"}

... in .ci/rules_python.json.

Some early scouting of this on top of my pip_import PR reveals that there are at least some blocking issues (I believe) unrelated to my changes.

Pipfile support?

Probably not an immediate priority, but it may be worth considering how/when to support the new 'Pipfile' format in addition to requirements.txt

Issue with the way we declare pypi dependencies

I have a project with multiple python sub-project, respectively multiple requirements.txt, however, there is an issue in the way we layout whl_library dependencies which lead to test operations to fail with no such target '@pypi__setuptools_38_4_0//:certs': target 'certs' not declared in package '' defined by.

The reason for such failure is b/c the two python subprojects generate the following snippet requirements.bzl file:

Project A:

if "pypi__setuptools_38_4_0" not in native.existing_rules():
    whl_library(
        name = "pypi__setuptools_38_4_0",
        whl = "@project_a_deps//:setuptools-38.4.0-py2.py3-none-any.whl",
        requirements = "@project_a_deps//:requirements.bzl",
        extras = ["certs","ssl"]
    )

Project B:

if "pypi__setuptools_38_4_0" not in native.existing_rules():
    whl_library(
        name = "pypi__setuptools_38_4_0",
        whl = "@project_b_deps//:setuptools-38.4.0-py2.py3-none-any.whl",
        requirements = "@project_b_deps//:requirements.bzl",
        extras = ["ssl"]
    )

While since this open for implicit ordering in WORKSPACE file, its possible that Project B was first to generate pypi__setuptools_38_4_0/BUILD which mean we end up with the following BUILD file:


package(default_visibility = ["//visibility:public"])

load("@delivery_service_deps//:requirements.bzl", "requirement")

py_library(
    name = "pkg",
    srcs = glob(["**/*.py"]),
    data = glob(["**/*"], exclude=["**/*.py", "**/* *", "BUILD", "WORKSPACE"]),
    # This makes this directory a top-level in the python import
    # search path for anything that depends on this.
    imports = ["."],
    deps = [],
)
py_library(
    name = "ssl",
    deps = [
        ":pkg",
    ],
)

As you can see target certs is missing in setuptools BUILD hence it explains the failure above.

wheel installations

I want to use a .whl file dependency. How can I do this with rules_python. I can't find any way to do this in the documentation.

requirement on mock can't install dependency

On Linux with Python 2.7, adding a requirement("mock") to a py_test target (and possibly other targets) breaks like so:

INFO: Running command line: .bazel/bin/toolchain/generate_crosstool_test
Traceback (most recent call last):
  File "/tmp/bazel/out/db099189d67c0d8a/faedb999bdce730c9c495251de1ca1a4/execroot/__main__/bazel-out/k8-fastbuild/bin/toolchain/generate_crosstool_test.runfiles/__main__/toolchain/generate_crosstool_test.py", line 2, in <module>
    import mock
  File "/tmp/bazel/out/db099189d67c0d8a/faedb999bdce730c9c495251de1ca1a4/execroot/__main__/bazel-out/k8-fastbuild/bin/toolchain/generate_crosstool_test.runfiles/pypi__mock_2_0_0/mock/__init__.py", line 2, in <module>
    import mock.mock as _mock
  File "/tmp/bazel/out/db099189d67c0d8a/faedb999bdce730c9c495251de1ca1a4/execroot/__main__/bazel-out/k8-fastbuild/bin/toolchain/generate_crosstool_test.runfiles/pypi__mock_2_0_0/mock/mock.py", line 80, in <module>
    import funcsigs
ImportError: No module named funcsigs
ERROR: Non-zero return code '1' from command: Process exited with status 1

The mock library has a dependency on funcsigs. It gets installed automatically if you pip install mock For some reason this transitive dependency breaks in the bazel world.

A workaround is to add funcsigs explicitly to the requirements.txt and also as a requirement("funcsigs") to the py_test target. Then everything works fine.

Peer Dependency Support

Some packages, like scipy, depend on packages already being installed on your system. In scipy's case, its numpy.

I tried making piptool respect the order of requirements.txt, but it wasn't enough, because the scipy wheel won't build unless numpy is already installed.

Maybe we need a deps attribute in pip_import?

Bazel breaks python import with __init__.py

System information:

  • Bazel version: 0.9.0
  • Python 3.6
  • Operation System: Debian 9.0
  • Context Runner: Docker

Description

I have few examples of how Bazel introduce __init__.py in places where none was and by doing this it breaks python import, e.g.

Python dependencies that are package namespaces.

An example is zope.interface package, which is a dependency of pyramid library same for zope.deprecation, we have some code that both when tests and one code is run it fails with ModuleNotFoundError: No module named 'zope.interface'.

I spend some time investigating this issue and the result of my investigation lead to two conclusions:

  • Bazel introduce __init__.py files and break namespacing
  • Python namespaces wheels installed in no-site directories wouldn't work

Let's looks at each one of them in details:

Bazel introduce __init__.py files and break namespacing

Looking at what wheel contains (using unzip -l ....whl) and how the directory is layout is in the .cache/bazel/<...>/external/pypi__zope_interface_4_4_0/, we see the same structure:

+ zope
    | + interface
          | - __init__.py
          | - _compat.py
          | ....
- zope.interface-4.4.0-py3.6-nspkg.pth
+ zope.interface-4.4.0.dist-info

While if you look inside the .runfiles of a py_binary or py_test rule you will see some __init__.py files being introduced e.g. in ls bazel-out/k8-fastbuild/bin/<some-repository-path>/default_test.runfiles/pypi__zope_interface_4_4_0/

- __init__.py    <<<<<<<<<<<<<<<<<<
+ zope
  | - __init__.py    <<<<<<<<<<<<<<<<<<<<<
   | + interface
         | - __init__.py
         | - _compat.py
         | ....
- zope.interface-4.4.0-py3.6-nspkg.pth
+ zope.interface-4.4.0.dist-info

Those __init__.py break the logic inside .pth that make sure that a package namespace is created.

Python namespaces wheels installed in no-site directories wouldn't work

The above is not all the story, because even if you remove __init__,py files, still namespace packages like zope.interface will not work as they should be b/c this later relay on the fact that the .pth files are executed which patch python module's __path__ of the given package (and that no __init__.py file exists in the subdirectory hence the first issue), however .pth files (like zope.interface-4.4.0-py3.6-nspkg.pth) are only parsed if the enclosing directory is considered a site directory as mentioned by documentation here:

A path configuration file is a file whose name has the form name.pth and exists in one of the four directories mentioned above; ...

Which refer to the paragraph before where it said.

It starts by constructing up to four directories from a head and a tail part. For the head part, it uses sys.prefix and sys.exec_prefix; empty heads are skipped. For the tail part, it uses the empty string and then lib/site-packages (on Windows) or lib/pythonX.Y/site-packages (on Unix and Macintosh)

Now long story, short, the .pth files are not parsed just by adding a directory to PYTHONPATH which is what the runner here, instead you will want to do add something like:

import site

for dir in python_imports.split(':'):
    if any(fpath.endswith('.pth') for fpath in os.listdir(dir)):
       site.addsitedir(dir)

Running tests with pytest

We are using pytest as a test runner, but for some of our existing code, switching to Bazel is breaking the tests, reason for this is again because of the introduced __init__.py files.

To give more details, our monorepo has the following structure:

+ services
   | + some_api
        | - BUILD.bazel
          - setup.py
          + services
             | - __init__.py
               - some_code.py
         
+ libraries
    | + python
        | + some_library
           | - setup.py
             - BUILD.bazel
             + shared

Inside BUILD.bazel we have rules to run tests by calling a python script that use pytest, again the same issue is that import is broken in some of our code that relies on import side effect (not good but hard to change) b/c same python modules get imported twice with different __name__ one is libraries.python.some_library... and another time with some_library.... and this is due to first pytest autodiscovery magic but mostly because Bazel introduce __init__.py files in libraries/ folder and libraries/python/ folder, which sadly confuse pytest.

Workarounds

So far, we are working around the later issue by misusing --run_under Bazel flag to patch .runfiles and delete __init__.py in those places where they shouldn't be any.

To give an idea of the snippet of the code that we have in the script passed to --run_under here it is:

for entry in $(ls $RUNFILES_DIR); do
  if [[ "$entry" == pypi__* ]]; then
    python_ns_package='no'
    for subentry in $(ls $RUNFILES_DIR/$entry); do
      if [[ "$subentry" == *.pth ]]; then
        python_ns_package='yes'
        break
      fi
    done

    if [[ "$python_ns_package" == 'yes' ]]; then
      rm -f $RUNFILES_DIR/$entry/__init__.py

      for subentry in $(ls $RUNFILES_DIR/$entry); do
        if [ -f $RUNFILES_DIR/$entry/$subentry/__init__.py ]; then
          rm -f $RUNFILES_DIR/$entry/$subentry/__init__.py
        fi
      done

      if [ ! -z $SRV_NAME ]; then
        sed -i "\|#!/usr/bin/env python|a import site; site.addsitedir('$entry')" $RUNFILES_DIR/__main__/services/$SRV_NAME/service_test
      elif [ ! -z $LIB_NAME ]; then
        sed -i "\|#!/usr/bin/env python|a import site; site.addsitedir('$entry')" $RUNFILES_DIR/__main__/libraries/python/$LIB_NAME/default_test
      fi
    fi
  fi
done

It's really ugly but fixes the test. However the workaround is harder to implement when we integrate with py_image rule from docker_rules.

Proposed solution

I still don't understand why inserting __init__.py is needed and in which case, but IMHO such changes mess up with the way Python work and worst create a different environment than the one you see, so my proposal maybe adds a new flag in Bazel test and build to skip the __init__.py creation or maybe better (read local control) add a flag to py_binary and py_test rules to skip auto __init__.py creation, thoughts?

N.B. While Bazel has been great addon to our stack and I am very grateful to the effort the community put in to make it better, I am a bit unhappy with the fact that python rules are written not as Bazel extensions (using Skylar) but in Java, because this way fixing issues like the above is not as easy as forking repository changing code and changing WORKSPACE to use fork :(

`pip_install` fails with empty `requirements.txt`

With an empty requirements.txt file, the generated requirements.bzl looks something like this:

# Install pip requirements.
#
# Generated from /private/var/tmp/_bazel_hwright/a2425324ff1a5e603c63e1b9994a2d28/external/third_party/py/requirements.txt

load("@io_bazel_rules_python//python:whl.bzl", "whl_library")

def pip_install():
  

_requirements = {
  
}

all_requirements = _requirements.values()

def requirement(name):
  name = name.replace("-", "_").lower()
  return _requirements[name]

The lack of any body in the pip_install function yields this kind of error:

$ bazel test ...
..........................................................
ERROR: /private/var/tmp/_bazel_hwright/a2425324ff1a5e603c63e1b9994a2d28/external/pip_deps/requirements.bzl:10:1: expected an indented block.
ERROR: error loading package '': Extension 'requirements.bzl' has errors.
INFO: Elapsed time: 1.741s
ERROR: Couldn't start the build. Unable to run tests.

While an empty requirements.txt is admittedly nonsensical, we should still support this case by inserting a pass statement inside the function body.

pypi libraries installed to system python are implicitly available to builds

It looks like any libraries installed to my system python's site-packages are available to bazel, whether or not they are expressed as a dependency to bazel.

This seems like an issue from a reproducible build standpoint. I.e.: it'd be easy to forget to include an external library in bazel, and create implicit dependencies for others using your BUILD files or for any par files or similar archives you build.

Does not gracefully handle dependencies with invalid characters in file names

I added pyfiglet==0.7.5 to the requirements.txt loaded by pip_import which made the rules explode because of this file name in the pyfiglet package: pyfiglet/fonts/ripper!_.flf:

ERROR: /home/marc.zych/.cache/bazel/_bazel_marc.zych/45ba23047e061ecba1878da72758f36b/external/pypi__pyfiglet_0_7_5/BUILD:6:1: @pypi__pyfiglet_0_7_5//:pkg: invalid label 'pyfiglet/fonts/ripper!_.flf' in element 370 of attribute 'data' in 'py_library' rule: invalid target
name 'pyfiglet/fonts/ripper!_.flf': target names may not contain '!'
ERROR: /home/marc.zych/.cache/bazel/_bazel_marc.zych/45ba23047e061ecba1878da72758f36b/external/pypi__pyfiglet_0_7_5/BUILD:6:1: Target '@pypi__pyfiglet_0_7_5//:pyfiglet/__init__.py' contains an error and its package is in error and referenced by '@pypi__pyfiglet_0_7_5//:pk
g'
ERROR: /home/marc.zych/.cache/bazel/_bazel_marc.zych/45ba23047e061ecba1878da72758f36b/external/pypi__pyfiglet_0_7_5/BUILD:6:1: Target '@pypi__pyfiglet_0_7_5//:pyfiglet/__main__.py' contains an error and its package is in error and referenced by '@pypi__pyfiglet_0_7_5//:pk
g'
ERROR: /home/marc.zych/.cache/bazel/_bazel_marc.zych/45ba23047e061ecba1878da72758f36b/external/pypi__pyfiglet_0_7_5/BUILD:6:1: Target '@pypi__pyfiglet_0_7_5//:pyfiglet/fonts/__init__.py' contains an error and its package is in error and referenced by '@pypi__pyfiglet_0_7_
5//:pkg'
ERROR: /home/marc.zych/.cache/bazel/_bazel_marc.zych/45ba23047e061ecba1878da72758f36b/external/pypi__pyfiglet_0_7_5/BUILD:6:1: Target '@pypi__pyfiglet_0_7_5//:pyfiglet/test.py' contains an error and its package is in error and referenced by '@pypi__pyfiglet_0_7_5//:pkg'
ERROR: /home/marc.zych/.cache/bazel/_bazel_marc.zych/45ba23047e061ecba1878da72758f36b/external/pypi__pyfiglet_0_7_5/BUILD:6:1: Target '@pypi__pyfiglet_0_7_5//:pyfiglet/version.py' contains an error and its package is in error and referenced by '@pypi__pyfiglet_0_7_5//:pkg
'
ERROR: /home/marc.zych/code/driving/experimental/marczych/rules_python/BUILD:6:1: Target '@pypi__pyfiglet_0_7_5//:pkg' contains an error and its package is in error and referenced by '//experimental/marczych/rules_python:rules_python_test'
ERROR: Analysis of target '//experimental/marczych/rules_python:rules_python_test' failed; build aborted: Loading failed
INFO: Elapsed time: 9.000s
FAILED: Build did NOT complete successfully (6 packages loaded)
ERROR: Couldn't start the build. Unable to run tests

This is likely a pyfiglet or Bazel issue but I don't think that either of them would fix it which is why I'm soliciting help here. Is there any workaround for this?

Bazel Python can't do namespaces with google.cloud

I'm trying to use google-cloud-bigquery, but my code fails to find bigquery in google.cloud.

I am able to import google.cloud, but that loads pypi__google_cloud_core_0_28_0 only, and not the other dependencies that add to google.cloud, despite pypi__google_cloud_bigquery_0_29_0 and all other transitive dependencies being in the runfiles.

Here is a debugging session:

In [22]: import google.cloud

In [23]: from  google.cloud import bigquery
---------------------------------------------------------------------------
ImportError                               Traceback (most recent call last)
~/firehook/cwm/bazel-bin/jupyter.runfiles/__main__/jupyter.py in <module>()
----> 1 from  google.cloud import bigquery

ImportError: cannot import name 'bigquery'

In [24]: google.__spec__
Out[24]: ModuleSpec(name='google', loader=<_frozen_importlib_external.SourceFileLoader object at 0x7f2db22a6ef0>, origin='/home/fortuna/firehook/cwm/bazel-bin/jupyter.runfiles/pypi__google_cloud_core_0_28_0/google/__init__.py', submodule_search_locations=['/home/fortuna/firehook/cwm/bazel-bin/jupyter.runfiles/pypi__google_cloud_core_0_28_0/google'])

In [25]: google.cloud.__spec__
Out[25]: ModuleSpec(name='google.cloud', loader=<_frozen_importlib_external.SourceFileLoader object at 0x7f2db22a6eb8>, origin='/home/fortuna/firehook/cwm/bazel-bin/jupyter.runfiles/pypi__google_cloud_core_0_28_0/google/cloud/__init__.py', submodule_search_locations=['/home/fortuna/firehook/cwm/bazel-bin/jupyter.runfiles/pypi__google_cloud_core_0_28_0/google/cloud'])

In [26]: ! ls /home/fortuna/firehook/cwm/bazel-bin/jupyter.runfiles
__init__.py		 pypi__futures_3_2_0		       pypi__google_resumable_media_0_3_1  pypi__requests_2_18_4
__main__		 pypi__google_api_core_0_1_4	       pypi__idna_2_6			   pypi__rsa_3_4_2
MANIFEST		 pypi__googleapis_common_protos_1_5_3  pypi__protobuf_3_5_1		   pypi__setuptools_38_4_0
pypi__cachetools_2_0_1	 pypi__google_auth_1_3_0	       pypi__pyasn1_0_4_2		   pypi__six_1_11_0
pypi__certifi_2018_1_18  pypi__google_cloud_bigquery_0_29_0    pypi__pyasn1_modules_0_2_1	   pypi__urllib3_1_22
pypi__chardet_3_0_4	 pypi__google_cloud_core_0_28_0        pypi__pytz_2017_3

My BUILD rule is

py_binary(
    name = "jupyter",
    srcs = ["jupyter.py"],
    data = [
        requirement("google-cloud-bigquery"),
    ],
)

Where jupyter.py starts an IPython REPL.

Pip packages specified with brackets fail

gapic-google-cloud-datastore depends on googleapis-common-protos[grpc] and proto-google-cloud-datastore-v1[grpc]. I believe pip canonically drops these extra bracketed tags, but the requirement function attempts to look it up in _requirements by the full key which fails.

Support coverage for `py_test`

bazel coverage should generate a coverage report for the python tests it runs. Ideally, it gathers the list of dependencies, and then computes coverage over that for a given test. When multiple tests are run in the same Bazel invocation, their reports should be merged into a single one.

I'm dropping this here mostly as a python-specific version of bazelbuild/bazel#1118. I know that the py_test rule is currently implemented natively in Bazel, and the implementation in rules_python just forwards there, but this is fundamentally a python issue, so this repo feels appropriate.

The general plan is to modify the python test runner within Bazel to use coverage.py to generate reports when running under coverage. I suspect it is a journey fraught with peril.

Alternatively, if it is possible to invoke coverage.py in ways other than modifying core Bazel, I'm open to suggestions.

rules_python chokes on packages with "+" in the version string

"+" denotes build metadata in semantic versioning.

ERROR: .../external/pypi__setuptools_39_0_1/BUILD:15:1: @pypi__setuptools_39_0_1//:foobar: invalid label '@py
foo_1_2_3+bar//:pkg' in element 1 of attribute 'deps' in 'py_library' rule: invalid repository name '@foo_1_2_3+bar': workspace names may contain only A-Z, a
-z, 0-9, '-', '_' and '.'

Support environment markers

From PEP 508:

Environment markers allow a dependency specification to provide a rule that describes when the dependency should be used.

PEP 508 also describes the syntax of environment markers.

For example, here is subset of the metadata.json for google-cloud-core==0.27.0:

  "run_requires": [
    {
      "requires": [
        "google-auth (>=0.4.0,<2.0.0dev)",
        "googleapis-common-protos (>=1.3.4)",
        "protobuf (>=3.0.0)",
        "requests (<3.0.0dev,>=2.18.0)",
        "setuptools (>=34.0.0)",
        "six"
      ]
    },
    {
      "environment": "python_version<\"3.2\"",
      "requires": [
        "futures (>=3.0.0)"
      ]
    }
  ],

For Python 2.7, rules_python should install the futures package.

You can work around this by manually declaring the dependency on futures in WORKSPACE and BUILD.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.