aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormattmoor <mattmoor@google.com>2017-09-15 14:12:03 -0700
committerGitHub <noreply@github.com>2017-09-15 14:12:03 -0700
commit7f4cc9244dac7637d514e4f86364507681dda37e (patch)
tree60ba189ef3271ac9e79ab0b1f18963125b4510ab
parent9160fc7367be8db56ae90c581bd0ceebda969e24 (diff)
downloadbazelbuild-rules_python-7f4cc9244dac7637d514e4f86364507681dda37e.tar.gz
PIP dependency support (#1)
* Check in an initial version of the pip rules. This is broken off of my prototype repository, and there are a handful of TODOs left to resolve. * Remove some vestigial convenience code. Elaborate on why the TODO to rewrite {pip,whl}.sh as Python is harder than it looks. * Incorporate Code Review Feedback * First cut at skydoc-based documentation. * Move whl.sh into a Python script that has unit testing. * Migrate pip.sh into piptool.py This migrates the logic from pip.sh into piptool.py, which should improve portability by removing the bash dependency. This also has the beginnings of wrapping piptool as a closed redistributable that doesn't rely on a system-installed copy of PIP, but instead uses these rules to pull pip into a PAR bundle. Besides needing to work out the details of releasing and redistributing the PAR, we have two unresolved issues: * When bundled as a PAR (vs. py_binary), piptool seems to pick up the system-installed version of pip. * When bundled as a PAR, piptool sometimes sees cert issues resolving requirements (similar to what we see with httplib2). * Address the cert issue in piptool as a PAR. With this change I am able to build/test my PR on my macbook without pip installed. The only additional change I have locally is to switch from running piptool.py as a simple .py file to downloading/using a PAR built from this change. I believe we still have the problem that the .par picks the host's version of pip instead of our embedded copy, but I haven't reverified that issue still exists (this just does nothing to address that issue). * Fix assorted buildifier issues. * Fix a typo in docs * Incorporate @duggelz code review feedback. * Incorporate review feedback move python tools under a top-level rules_python package simplify version_test * Adopt a canonical naming format for imported whl_library rules. `whl_library` rules generated by `pip_import` are now named as: ``` pypi__{distribution}_{version} ``` Substituting illegal characters (e.g. `-`, `.`) with underscores. * Move the piptool dependency into a .PAR file. Add a script for updating the tools and document this and the docs script. * Fix buildifier issues.
-rw-r--r--.ci/rules_python.json10
-rw-r--r--.gitignore39
-rw-r--r--.travis.yml27
-rw-r--r--README.md96
-rw-r--r--WORKSPACE109
-rw-r--r--docs/BUILD60
-rw-r--r--docs/index.html195
-rw-r--r--docs/index.md121
-rwxr-xr-xdocs/main.css3
-rw-r--r--docs/python/pip.html161
-rw-r--r--docs/python/pip.md101
-rw-r--r--docs/python/python.html115
-rw-r--r--docs/python/python.md51
-rw-r--r--docs/python/whl.html135
-rw-r--r--docs/python/whl.md72
-rw-r--r--examples/BUILD16
-rw-r--r--examples/helloworld/BUILD31
-rw-r--r--examples/helloworld/helloworld.py29
-rw-r--r--examples/helloworld/helloworld_test.py41
-rw-r--r--examples/helloworld/requirements.txt1
-rw-r--r--examples/version/BUILD25
-rw-r--r--examples/version/requirements.txt1
-rw-r--r--examples/version/version_test.py26
-rw-r--r--python/BUILD8
-rw-r--r--python/pip.bzl101
-rw-r--r--python/python.bzl18
-rw-r--r--python/requirements.txt2
-rw-r--r--python/whl.bzl67
-rw-r--r--rules_python/BUILD47
-rw-r--r--rules_python/piptool.py154
-rw-r--r--rules_python/whl.py131
-rw-r--r--rules_python/whl_test.py57
-rw-r--r--tools/BUILD19
-rwxr-xr-xtools/piptool.parbin0 -> 1346232 bytes
-rwxr-xr-xupdate_docs.sh21
-rwxr-xr-xupdate_piptool.sh20
-rwxr-xr-xupdate_tools.sh20
37 files changed, 2118 insertions, 12 deletions
diff --git a/.ci/rules_python.json b/.ci/rules_python.json
new file mode 100644
index 0000000..760a9ef
--- /dev/null
+++ b/.ci/rules_python.json
@@ -0,0 +1,10 @@
+[
+ {
+ "variation": "",
+ "configurations": [
+ {"node": "linux-x86_64"},
+ {"node": "ubuntu_16.04-x86_64"},
+ {"node": "darwin-x86_64"}
+ ]
+ }
+]
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..dcfa539
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,39 @@
+# Compiled Object files
+*.slo
+*.lo
+*.o
+*.obj
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Compiled Dynamic libraries
+*.so
+*.dylib
+*.dll
+
+# Fortran module files
+*.mod
+*.smod
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+*.lib
+
+# Executables
+*.exe
+*.out
+*.app
+
+# Emacs garbage
+*~
+
+# Bazel directories
+bazel-*
+bazel-bin
+bazel-genfiles
+bazel-out
+bazel-testlogs
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..8b44fca
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,27 @@
+sudo: required
+dist: trusty
+language:
+ - java
+jdk:
+ - oraclejdk8 # Building Bazel requires JDK8.
+addons:
+ apt:
+ sources:
+ - sourceline: 'deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8'
+ key_url: 'https://storage.googleapis.com/bazel-apt/doc/apt-key.pub.gpg'
+ packages:
+ - bazel
+
+install:
+ - go get -u github.com/bazelbuild/buildifier/buildifier
+
+script:
+ # Check that all of our tools and samples build
+ - bazel clean && bazel build //...
+ # Check that all of our tests pass
+ - bazel clean && bazel test //...
+
+ # Check for issues with the format of our bazel config files.
+ - buildifier -mode=check $(find . -name BUILD -type f)
+ - buildifier -mode=check $(find . -name WORKSPACE -type f)
+ - buildifier -mode=check $(find . -name '*.bzl' -type f)
diff --git a/README.md b/README.md
index 69ecc92..0631198 100644
--- a/README.md
+++ b/README.md
@@ -4,14 +4,17 @@
## Rules
-* [py_library](#py_library)
-* [py_binary](#py_binary)
+* [pip_import](docs/python/pip.md#pip_import)
+* [py_library](docs/python/python.md#py_library)
+* [py_binary](docs/python/python.md#py_binary)
+* [py_test](docs/python/python.md#py_test)
## Overview
-This is a placeholder repository that provides aliases for the native Bazel
-python rules. In the future, this will also become the home for rules that
-download `pip` packages, and other non-Core Python functionality.
+This repository provides Python rules for Bazel. Currently, support for
+rules that are available from Bazel core are simple aliases to that bundled
+functionality. On top of that, this repository provides support for installing
+dependencies typically managed via `pip`.
## Setup
@@ -23,6 +26,11 @@ git_repository(
remote = "https://github.com/bazelbuild/rules_python.git",
commit = "{HEAD}",
)
+
+# Only needed for PIP support:
+load("//python:pip.bzl", "pip_repositories")
+
+pip_repositories()
```
Then in your `BUILD` files load the python rules with:
@@ -30,7 +38,7 @@ Then in your `BUILD` files load the python rules with:
``` python
load(
"@io_bazel_rules_python//python:python.bzl",
- "py_binary", "py_library"
+ "py_binary", "py_library", "py_test",
)
py_binary(
@@ -39,12 +47,76 @@ py_binary(
)
```
-<a name="py_library"></a>
-## py_library
+## Importing `pip` dependencies
+
+These rules are designed to have developers continue using `requirements.txt`
+to express their dependencies in a Python idiomatic manner. These dependencies
+are imported into the Bazel dependency graph via a two-phased process in
+`WORKSPACE`:
+
+```python
+load("@io_bazel_rules_python//python:pip.bzl", "pip_import")
+
+# This rule translates the specified requirements.txt into
+# @my_deps//:requirements.bzl, which itself exposes a pip_install method.
+pip_import(
+ name = "my_deps",
+ requirements = "//path/to:requirements.txt",
+)
+
+# Load the pip_install symbol for my_deps, and create the dependencies'
+# repositories.
+load("@my_deps//:requirements.bzl", "pip_install")
+pip_install()
+```
+
+## Consuming `pip` dependencies
+
+Once a set of dependencies has been imported via `pip_import` and `pip_install`
+we can start consuming them in our `py_{binary,library,test}` rules. In support
+of this, the generated `requirements.bzl` also contains a `package` method,
+which can be used directly in `deps=[]` to reference an imported `py_library`.
+
+```python
+load("@my_deps//:requirements.bzl", "package")
+
+py_library(
+ name = "mylib",
+ srcs = ["mylib.py"],
+ deps = [
+ ":myotherlib",
+ # This takes the name as specified in requirements.txt
+ package("importeddep"),
+ ]
+)
+```
+
+## Canonical `whl_library` naming
-See Bazel core [documentation](https://docs.bazel.build/versions/master/be/python.html#py_library).
+It is notable that `whl_library` rules imported via `pip_import` are canonically
+named, following the pattern: `pypi__{distribution}_{version}`. Characters in
+these components that are illegal in Bazel label names (e.g. `-`, `.`) are
+replaced with `_`.
-<a name="py_binary"></a>
-## py_binary
+This canonical naming helps avoid redundant work to import the same library
+multiple times. It is expected that this naming will remain stable, so folks
+should be able to reliably depend directly on e.g. `@pypi__futures_3_1_1//:pkg`
+for dependencies, however, it is recommended that folks stick with the `package`
+pattern in case the need arises for us to make changes to this format in the
+future.
-See Bazel core [documentation](https://docs.bazel.build/versions/master/be/python.html#py_binary).
+## Updating `docs/`
+
+All of the content (except `BUILD`) under `docs/` is generated. To update the
+documentation simply run this in the root of the repository:
+```shell
+./update_docs.sh
+```
+
+## Updating `tools/`
+
+All of the content (except `BUILD`) under `tools/` is generated. To update the
+documentation simply run this in the root of the repository:
+```shell
+./update_tools.sh
+```
diff --git a/WORKSPACE b/WORKSPACE
new file mode 100644
index 0000000..ddab657
--- /dev/null
+++ b/WORKSPACE
@@ -0,0 +1,109 @@
+# Copyright 2017 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+workspace(name = "io_bazel_rules_python")
+
+# Skydoc stuff
+git_repository(
+ name = "io_bazel_rules_sass",
+ remote = "https://github.com/bazelbuild/rules_sass.git",
+ tag = "0.0.2",
+)
+
+load("@io_bazel_rules_sass//sass:sass.bzl", "sass_repositories")
+
+sass_repositories()
+
+git_repository(
+ name = "io_bazel_skydoc",
+ remote = "https://github.com/bazelbuild/skydoc.git",
+ tag = "0.1.3",
+)
+
+load("@io_bazel_skydoc//skylark:skylark.bzl", "skydoc_repositories")
+
+skydoc_repositories()
+
+# Requirements for building our piptool.
+load("//python:pip.bzl", "pip_import")
+
+pip_import(
+ name = "piptool_deps",
+ requirements = "//python:requirements.txt",
+)
+
+load(
+ "@piptool_deps//:requirements.bzl",
+ _piptool_install = "pip_install",
+)
+
+_piptool_install()
+
+git_repository(
+ name = "subpar",
+ remote = "https://github.com/google/subpar",
+ tag = "1.0.0",
+)
+
+# Test data for WHL tool testing.
+http_file(
+ name = "grpc_whl",
+ sha256 = "c232d6d168cb582e5eba8e1c0da8d64b54b041dd5ea194895a2fe76050916561",
+ # From https://pypi.python.org/pypi/grpcio/1.6.0
+ url = ("https://pypi.python.org/packages/c6/28/" +
+ "67651b4eabe616b27472c5518f9b2aa3f63beab8f62100b26f05ac428639/" +
+ "grpcio-1.6.0-cp27-cp27m-manylinux1_i686.whl"),
+)
+
+http_file(
+ name = "futures_whl",
+ sha256 = "c4884a65654a7c45435063e14ae85280eb1f111d94e542396717ba9828c4337f",
+ # From https://pypi.python.org/pypi/futures
+ url = ("https://pypi.python.org/packages/a6/1c/" +
+ "72a18c8c7502ee1b38a604a5c5243aa8c2a64f4bba4e6631b1b8972235dd/" +
+ "futures-3.1.1-py2-none-any.whl"),
+)
+
+http_file(
+ name = "mock_whl",
+ sha256 = "5ce3c71c5545b472da17b72268978914d0252980348636840bd34a00b5cc96c1",
+ # From https://pypi.python.org/pypi/mock
+ url = ("https://pypi.python.org/packages/e6/35/" +
+ "f187bdf23be87092bd0f1200d43d23076cee4d0dec109f195173fd3ebc79/" +
+ "mock-2.0.0-py2.py3-none-any.whl"),
+)
+
+# Imports for examples
+pip_import(
+ name = "examples_helloworld",
+ requirements = "//examples/helloworld:requirements.txt",
+)
+
+load(
+ "@examples_helloworld//:requirements.bzl",
+ _helloworld_install = "pip_install",
+)
+
+_helloworld_install()
+
+pip_import(
+ name = "examples_version",
+ requirements = "//examples/version:requirements.txt",
+)
+
+load(
+ "@examples_version//:requirements.bzl",
+ _version_install = "pip_install",
+)
+
+_version_install()
diff --git a/docs/BUILD b/docs/BUILD
new file mode 100644
index 0000000..b2a4da3
--- /dev/null
+++ b/docs/BUILD
@@ -0,0 +1,60 @@
+# Copyright 2017 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"]) # Apache 2.0
+
+# To regenerate html docs, run:
+# ./update_docs.sh
+
+load("@io_bazel_skydoc//skylark:skylark.bzl", "skylark_doc", "skylark_library")
+
+skylark_library(
+ name = "whl",
+ srcs = ["//python:whl.bzl"],
+)
+
+skylark_library(
+ name = "pip",
+ srcs = ["//python:pip.bzl"],
+)
+
+skylark_library(
+ name = "python",
+ srcs = ["//python:python.bzl"],
+)
+
+skylark_doc(
+ name = "docs-md",
+ format = "markdown",
+ overview = True,
+ site_root = ".",
+ deps = [
+ ":pip",
+ ":python",
+ ":whl",
+ ],
+)
+
+skylark_doc(
+ name = "docs-html",
+ format = "html",
+ overview = True,
+ site_root = ".",
+ deps = [
+ ":pip",
+ ":python",
+ ":whl",
+ ],
+)
diff --git a/docs/index.html b/docs/index.html
new file mode 100644
index 0000000..d1b1dc4
--- /dev/null
+++ b/docs/index.html
@@ -0,0 +1,195 @@
+
+
+<!--
+Documentation generated by Skydoc
+-->
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width initial-scale=1" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+
+ <title>Overview</title>
+
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,600,700" type="text/css">
+ <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
+ <link rel="stylesheet" href="https://code.getmdl.io/1.1.1/material.green-light_blue.min.css">
+ <script defer src="https://code.getmdl.io/1.1.1/material.min.js"></script>
+ <link rel="stylesheet" href="./main.css">
+ </head>
+ <body>
+ <div class="mdl-layout mdl-js-layout mdl-layout--fixed-drawer
+ mdl-layout--fixed-header">
+ <header class="mdl-layout__header">
+ <div class="mdl-layout__header-row">
+ <span class="mdl-layout-title">Overview</span>
+ </div>
+ </header>
+ <div class="mdl-layout__drawer">
+ <span class="mdl-layout-title">Bazel</span>
+ <nav class="drawer-nav">
+ <ul class="drawer-nav">
+
+<li><a href="./index.html">Overview</a></li>
+<li>
+ <a href="./python/python.html">python Rules</a>
+ <ul>
+ </ul>
+</li>
+<li><a href="./index.html">Overview</a></li>
+<li>
+ <a href="./python/pip.html">Import pip requirements into Bazel.</a>
+ <ul>
+ </ul>
+</li>
+<li><a href="./index.html">Overview</a></li>
+<li>
+ <a href="./python/whl.html">Import .whl files into Bazel.</a>
+ <ul>
+ </ul>
+</li>
+
+ </ul>
+ </nav>
+ </div>
+
+ <main class="mdl-layout__content">
+ <div class="page-content">
+<h1>Overview</h1>
+
+
+<nav class="toc">
+ <h2>Rule sets</h2>
+ <ul>
+ <li><a href="#python">python Rules</a></li>
+ <li><a href="#pip">Import pip requirements into Bazel.</a></li>
+ <li><a href="#whl">Import .whl files into Bazel.</a></li>
+ </ul>
+</nav>
+
+<h2><a href="./python/python.html">python Rules</a></h2>
+
+<h3>Macros</h3>
+<table class="overview-table">
+ <colgroup>
+ <col class="col-name" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+ <tr>
+ <td>
+ <a href="./python/python.html#py_library">
+ <code>py_library</code>
+ </a>
+ </td>
+ <td>
+ <p>See the Bazel core py_library documentation.</p>
+
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <a href="./python/python.html#py_binary">
+ <code>py_binary</code>
+ </a>
+ </td>
+ <td>
+ <p>See the Bazel core py_binary documentation.</p>
+
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <a href="./python/python.html#py_test">
+ <code>py_test</code>
+ </a>
+ </td>
+ <td>
+ <p>See the Bazel core py_test documentation.</p>
+
+ </td>
+ </tr>
+ </tbody>
+</table>
+<h2><a href="./python/pip.html">Import pip requirements into Bazel.</a></h2>
+
+<h3>Macros</h3>
+<table class="overview-table">
+ <colgroup>
+ <col class="col-name" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+ <tr>
+ <td>
+ <a href="./python/pip.html#pip_repositories">
+ <code>pip_repositories</code>
+ </a>
+ </td>
+ <td>
+ <p>Pull in dependencies needed for pulling in pip dependencies.</p>
+
+ </td>
+ </tr>
+ </tbody>
+</table>
+<h3>Repository Rules</h3>
+<table class="overview-table">
+ <colgroup>
+ <col class="col-name" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+ <tr>
+ <td>
+ <a href="./python/pip.html#pip_import">
+ <code>pip_import</code>
+ </a>
+ </td>
+ <td>
+ <p>A rule for importing &lt;code&gt;requirements.txt&lt;/code&gt; dependencies into Bazel.</p>
+
+ </td>
+ </tr>
+ </tbody>
+</table>
+<h2><a href="./python/whl.html">Import .whl files into Bazel.</a></h2>
+
+<h3>Repository Rules</h3>
+<table class="overview-table">
+ <colgroup>
+ <col class="col-name" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+ <tr>
+ <td>
+ <a href="./python/whl.html#whl_library">
+ <code>whl_library</code>
+ </a>
+ </td>
+ <td>
+ <p>A rule for importing &lt;code&gt;.whl&lt;/code&gt; dependencies into Bazel.</p>
+
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+
+ </div>
+
+ <footer class="mdl-mini-footer">
+ <div class="mdl-mini-footer__left-section">
+ <div class="mdl-logo">Bazel</div>
+ <ul class="mdl-mini-footer__link-list">
+ <li><a href="http://bazel.io">Home</a></li>
+ <li><a href="https://github.com/bazelbuild">GitHub</a></li>
+ </ul>
+ </div>
+ </footer>
+ </main>
+ </div>
+ </body>
+</html>
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..febdbcb
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,121 @@
+
+# Overview
+
+
+<nav class="toc">
+ <h2>Rule sets</h2>
+ <ul>
+ <li><a href="#python">python Rules</a></li>
+ <li><a href="#pip">Import pip requirements into Bazel.</a></li>
+ <li><a href="#whl">Import .whl files into Bazel.</a></li>
+ </ul>
+</nav>
+
+<h2><a href="./python/python.html">python Rules</a></h2>
+
+<h3>Macros</h3>
+<table class="overview-table">
+ <colgroup>
+ <col class="col-name" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+ <tr>
+ <td>
+ <a href="./python/python.html#py_library">
+ <code>py_library</code>
+ </a>
+ </td>
+ <td>
+ <p>See the Bazel core py_library documentation.</p>
+
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <a href="./python/python.html#py_binary">
+ <code>py_binary</code>
+ </a>
+ </td>
+ <td>
+ <p>See the Bazel core py_binary documentation.</p>
+
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <a href="./python/python.html#py_test">
+ <code>py_test</code>
+ </a>
+ </td>
+ <td>
+ <p>See the Bazel core py_test documentation.</p>
+
+ </td>
+ </tr>
+ </tbody>
+</table>
+<h2><a href="./python/pip.html">Import pip requirements into Bazel.</a></h2>
+
+<h3>Macros</h3>
+<table class="overview-table">
+ <colgroup>
+ <col class="col-name" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+ <tr>
+ <td>
+ <a href="./python/pip.html#pip_repositories">
+ <code>pip_repositories</code>
+ </a>
+ </td>
+ <td>
+ <p>Pull in dependencies needed for pulling in pip dependencies.</p>
+
+ </td>
+ </tr>
+ </tbody>
+</table>
+<h3>Repository Rules</h3>
+<table class="overview-table">
+ <colgroup>
+ <col class="col-name" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+ <tr>
+ <td>
+ <a href="./python/pip.html#pip_import">
+ <code>pip_import</code>
+ </a>
+ </td>
+ <td>
+ <p>A rule for importing &lt;code&gt;requirements.txt&lt;/code&gt; dependencies into Bazel.</p>
+
+ </td>
+ </tr>
+ </tbody>
+</table>
+<h2><a href="./python/whl.html">Import .whl files into Bazel.</a></h2>
+
+<h3>Repository Rules</h3>
+<table class="overview-table">
+ <colgroup>
+ <col class="col-name" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+ <tr>
+ <td>
+ <a href="./python/whl.html#whl_library">
+ <code>whl_library</code>
+ </a>
+ </td>
+ <td>
+ <p>A rule for importing &lt;code&gt;.whl&lt;/code&gt; dependencies into Bazel.</p>
+
+ </td>
+ </tr>
+ </tbody>
+</table>
diff --git a/docs/main.css b/docs/main.css
new file mode 100755
index 0000000..f506e12
--- /dev/null
+++ b/docs/main.css
@@ -0,0 +1,3 @@
+body{background-color:#fafafa}pre,code{font-family:'Liberation Mono', Consolas, Monaco, 'Andale Mono', monospace}pre{background-color:#eee;padding:20px;overflow-x:auto;word-wrap:normal}pre code{overflow-wrap:normal;white-space:pre}code{display:inline-block;font-size:90%;white-space:pre-wrap}.mdl-layout__drawer{background-color:#fff}.mdl-layout__drawer .mdl-layout-title{border-bottom:1px solid #e0e0e0;padding-left:24px}.drawer-nav ul{list-style:none;padding-left:0}.drawer-nav ul li{display:block;padding:0}.drawer-nav ul li ul li a{padding-left:44px;font-weight:400}.drawer-nav ul li a{display:block;flex-shrink:0;padding:15px 0 15px 22px;margin:0;font-weight:600;color:#757575;line-height:1em;text-decoration:none;cursor:pointer}.drawer-nav ul li a:active,.drawer-nav ul li a:hover{background-color:#f0f0f0}.drawer-nav ul li.active a{color:#4caf50;font-weight:500}h1.page-title{font-size:34px;font-weight:400;line-height:40px;margin-bottom:30px;color:#4caf50}p.lead{font-size:20px;line-height:32px}table{border-collapse:collapse;border-spacing:0;background-color:#fff;table-layout:auto}table thead th{background-color:#fafafa;border:1px solid #eee;color:#757575;padding:12px 12px 12px 24px;vertical-align:top}table tbody td{border:1px solid #eee;padding:12px 12px 12px 24px;vertical-align:top}table.params-table{width:100%}table.params-table col.col-param{width:25%}table.params-table col.col-description{width:75%}table.overview-table{width:100%}table.overview-table col.col-name{width:25%}table.overview-table col.col-description{width:75%}table.overview-table td p{margin:0}hr{margin-top:80px;margin-bottom:80px}nav.toc{border-left:5px solid #4caf50;padding-left:20px;margin-bottom:48px}nav.toc h1,nav.toc h2{font-size:15px;line-height:16px;padding-bottom:12px;margin-bottom:0;font-weight:400;color:#757575}nav.toc ul{list-style:none;margin-top:0;padding-left:0}nav.toc ul li{font-size:20px;line-height:40px}nav.toc ul li a{color:#4caf50}.page-content{margin-left:auto;margin-right:auto;padding-top:60px;padding-bottom:60px;width:760px}.page-content a{text-decoration:none}.page-content h1{font-size:34px;font-weight:400;line-height:40px;margin-bottom:30px;color:#4caf50}.page-content h2{font-size:24px;font-weight:400;line-height:32px;margin-bottom:30px;color:#4caf50}.page-content h3{font-size:20px;font-weight:400;line-height:32px;margin-bottom:30px;color:#4caf50}@media (max-width: 768px){.page-content{width:360px}}@media (min-width: 768px){.page-content{width:760px}}@media (min-width: 1476px){.page-content{width:1160px}}.mdl-mini-footer{padding-left:40px}
+
+/*# sourceMappingURL=main.css.map */ \ No newline at end of file
diff --git a/docs/python/pip.html b/docs/python/pip.html
new file mode 100644
index 0000000..2d43cb7
--- /dev/null
+++ b/docs/python/pip.html
@@ -0,0 +1,161 @@
+
+
+<!--
+Documentation generated by Skydoc
+-->
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width initial-scale=1" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+
+ <title>Import pip requirements into Bazel.</title>
+
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,600,700" type="text/css">
+ <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
+ <link rel="stylesheet" href="https://code.getmdl.io/1.1.1/material.green-light_blue.min.css">
+ <script defer src="https://code.getmdl.io/1.1.1/material.min.js"></script>
+ <link rel="stylesheet" href="./main.css">
+ </head>
+ <body>
+ <div class="mdl-layout mdl-js-layout mdl-layout--fixed-drawer
+ mdl-layout--fixed-header">
+ <header class="mdl-layout__header">
+ <div class="mdl-layout__header-row">
+ <span class="mdl-layout-title">Import pip requirements into Bazel.</span>
+ </div>
+ </header>
+ <div class="mdl-layout__drawer">
+ <span class="mdl-layout-title">Bazel</span>
+ <nav class="drawer-nav">
+ <ul class="drawer-nav">
+
+<li><a href="./index.html">Overview</a></li>
+<li>
+ <a href="./python/python.html">python Rules</a>
+ <ul>
+ </ul>
+</li>
+<li><a href="./index.html">Overview</a></li>
+<li>
+ <a href="./python/pip.html">Import pip requirements into Bazel.</a>
+ <ul>
+ </ul>
+</li>
+<li><a href="./index.html">Overview</a></li>
+<li>
+ <a href="./python/whl.html">Import .whl files into Bazel.</a>
+ <ul>
+ </ul>
+</li>
+
+ </ul>
+ </nav>
+ </div>
+
+ <main class="mdl-layout__content">
+ <div class="page-content">
+ <h1>Import pip requirements into Bazel.</h1>
+
+<nav class="toc">
+ <h2>Repository Rules</h2>
+ <ul>
+ <li><a href="#pip_import">pip_import</a></li>
+ </ul>
+ <h2>Macros</h2>
+ <ul>
+ <li><a href="#pip_repositories">pip_repositories</a></li>
+ </ul>
+</nav>
+ <hr>
+
+ <h2 id="pip_repositories">pip_repositories</h2>
+
+ <pre>pip_repositories()</pre>
+
+ <p>Pull in dependencies needed for pulling in pip dependencies.</p>
+<p>A placeholder method that will eventually pull in any dependencies
+needed to install pip dependencies.</p>
+
+
+ <hr>
+
+ <h2 id="pip_import">pip_import</h2>
+
+ <pre>pip_import(<a href="#pip_import.name">name</a>, <a href="#pip_import.requirements">requirements</a>)</pre>
+
+ <p>A rule for importing &lt;code&gt;requirements.txt&lt;/code&gt; dependencies into Bazel.</p>
+<p>This rule imports a &lt;code&gt;requirements.txt&lt;/code&gt; file and generates a new
+&lt;code&gt;requirements.bzl&lt;/code&gt; file. This is used via the &lt;code&gt;WORKSPACE&lt;/code&gt;
+pattern:</p>
+&lt;pre&gt;&lt;code&gt;pip_import(
+ name = "foo",
+ requirements = ":requirements.txt",
+)
+load("@foo//:requirements.bzl", "pip_install")
+pip_install()
+&lt;/code&gt;&lt;/pre&gt;<p>You can then reference imported dependencies from your &lt;code&gt;BUILD&lt;/code&gt;
+file with:</p>
+&lt;pre&gt;&lt;code&gt;load("@foo//:requirements.bzl", "package")
+py_library(
+ name = "bar",
+ ...
+ deps = [
+ "//my/other:dep",
+ package("futures"),
+ package("mock"),
+ ],
+)
+&lt;/code&gt;&lt;/pre&gt;<p>Or alternatively:</p>
+&lt;pre&gt;&lt;code&gt;load("@foo//:requirements.bzl", "all_packages")
+py_binary(
+ name = "baz",
+ ...
+ deps = [
+ ":foo",
+ ] + all_packages,
+)
+&lt;/code&gt;&lt;/pre&gt;
+
+ <h3 id="pip_import_args">Attributes</h3>
+
+<table class="params-table">
+ <colgroup>
+ <col class="col-param" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+ <tr id="pip_import.name">
+ <td><code>name</code></td>
+ <td>
+ <p><code><a href="https://bazel.build/docs/build-ref.html#name">Name</a>; Required</code></p>
+ <p>A unique name for this rule.</p>
+ </td>
+ </tr>
+ <tr id="pip_import.requirements">
+ <td><code>requirements</code></td>
+ <td>
+ <p><code><a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; Required</code></p>
+ <p>The label of a requirements.txt file.</p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+
+ </div>
+
+ <footer class="mdl-mini-footer">
+ <div class="mdl-mini-footer__left-section">
+ <div class="mdl-logo">Bazel</div>
+ <ul class="mdl-mini-footer__link-list">
+ <li><a href="http://bazel.io">Home</a></li>
+ <li><a href="https://github.com/bazelbuild">GitHub</a></li>
+ </ul>
+ </div>
+ </footer>
+ </main>
+ </div>
+ </body>
+</html>
diff --git a/docs/python/pip.md b/docs/python/pip.md
new file mode 100644
index 0000000..2a373d1
--- /dev/null
+++ b/docs/python/pip.md
@@ -0,0 +1,101 @@
+
+<!---
+Documentation generated by Skydoc
+-->
+<h1>Import pip requirements into Bazel.</h1>
+
+
+<nav class="toc">
+ <h2>Repository Rules</h2>
+ <ul>
+ <li><a href="#pip_import">pip_import</a></li>
+ </ul>
+ <h2>Macros</h2>
+ <ul>
+ <li><a href="#pip_repositories">pip_repositories</a></li>
+ </ul>
+</nav>
+<a name="pip_repositories"></a>
+## pip_repositories
+
+<pre>
+pip_repositories()
+</pre>
+
+Pull in dependencies needed for pulling in pip dependencies.
+
+A placeholder method that will eventually pull in any dependencies
+needed to install pip dependencies.
+
+<a name="pip_import"></a>
+## pip_import
+
+<pre>
+pip_import(<a href="#pip_import.name">name</a>, <a href="#pip_import.requirements">requirements</a>)
+</pre>
+
+A rule for importing <code>requirements.txt</code> dependencies into Bazel.
+
+This rule imports a <code>requirements.txt</code> file and generates a new
+<code>requirements.bzl</code> file. This is used via the <code>WORKSPACE</code>
+pattern:
+<pre><code>pip_import(
+ name = "foo",
+ requirements = ":requirements.txt",
+)
+load("@foo//:requirements.bzl", "pip_install")
+pip_install()
+</code></pre>
+
+You can then reference imported dependencies from your <code>BUILD</code>
+file with:
+<pre><code>load("@foo//:requirements.bzl", "package")
+py_library(
+ name = "bar",
+ ...
+ deps = [
+ "//my/other:dep",
+ package("futures"),
+ package("mock"),
+ ],
+)
+</code></pre>
+
+Or alternatively:
+<pre><code>load("@foo//:requirements.bzl", "all_packages")
+py_binary(
+ name = "baz",
+ ...
+ deps = [
+ ":foo",
+ ] + all_packages,
+)
+</code></pre>
+
+
+<a name="pip_import_args"></a>
+### Attributes
+
+
+<table class="params-table">
+ <colgroup>
+ <col class="col-param" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+ <tr id="pip_import.name">
+ <td><code>name</code></td>
+ <td>
+ <p><code><a href="https://bazel.build/docs/build-ref.html#name">Name</a>; Required</code></p>
+ <p>A unique name for this rule.</p>
+ </td>
+ </tr>
+ <tr id="pip_import.requirements">
+ <td><code>requirements</code></td>
+ <td>
+ <p><code><a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; Required</code></p>
+ <p>The label of a requirements.txt file.</p>
+ </td>
+ </tr>
+ </tbody>
+</table>
diff --git a/docs/python/python.html b/docs/python/python.html
new file mode 100644
index 0000000..c98039a
--- /dev/null
+++ b/docs/python/python.html
@@ -0,0 +1,115 @@
+
+
+<!--
+Documentation generated by Skydoc
+-->
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width initial-scale=1" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+
+ <title>python Rules</title>
+
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,600,700" type="text/css">
+ <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
+ <link rel="stylesheet" href="https://code.getmdl.io/1.1.1/material.green-light_blue.min.css">
+ <script defer src="https://code.getmdl.io/1.1.1/material.min.js"></script>
+ <link rel="stylesheet" href="./main.css">
+ </head>
+ <body>
+ <div class="mdl-layout mdl-js-layout mdl-layout--fixed-drawer
+ mdl-layout--fixed-header">
+ <header class="mdl-layout__header">
+ <div class="mdl-layout__header-row">
+ <span class="mdl-layout-title">python Rules</span>
+ </div>
+ </header>
+ <div class="mdl-layout__drawer">
+ <span class="mdl-layout-title">Bazel</span>
+ <nav class="drawer-nav">
+ <ul class="drawer-nav">
+
+<li><a href="./index.html">Overview</a></li>
+<li>
+ <a href="./python/python.html">python Rules</a>
+ <ul>
+ </ul>
+</li>
+<li><a href="./index.html">Overview</a></li>
+<li>
+ <a href="./python/pip.html">Import pip requirements into Bazel.</a>
+ <ul>
+ </ul>
+</li>
+<li><a href="./index.html">Overview</a></li>
+<li>
+ <a href="./python/whl.html">Import .whl files into Bazel.</a>
+ <ul>
+ </ul>
+</li>
+
+ </ul>
+ </nav>
+ </div>
+
+ <main class="mdl-layout__content">
+ <div class="page-content">
+ <h1>python Rules</h1>
+
+<nav class="toc">
+ <h2>Macros</h2>
+ <ul>
+ <li><a href="#py_library">py_library</a></li>
+ <li><a href="#py_binary">py_binary</a></li>
+ <li><a href="#py_test">py_test</a></li>
+ </ul>
+</nav>
+ <hr>
+
+ <h2 id="py_library">py_library</h2>
+
+ <pre>py_library()</pre>
+
+ <p>See the Bazel core py_library documentation.</p>
+<p><a href="https://docs.bazel.build/versions/master/be/python.html#py_library">available here</a>.</p>
+
+
+ <hr>
+
+ <h2 id="py_binary">py_binary</h2>
+
+ <pre>py_binary()</pre>
+
+ <p>See the Bazel core py_binary documentation.</p>
+<p><a href="https://docs.bazel.build/versions/master/be/python.html#py_binary">available here</a>.</p>
+
+
+ <hr>
+
+ <h2 id="py_test">py_test</h2>
+
+ <pre>py_test()</pre>
+
+ <p>See the Bazel core py_test documentation.</p>
+<p><a href="https://docs.bazel.build/versions/master/be/python.html#py_test">available here</a>.</p>
+
+
+
+
+ </div>
+
+ <footer class="mdl-mini-footer">
+ <div class="mdl-mini-footer__left-section">
+ <div class="mdl-logo">Bazel</div>
+ <ul class="mdl-mini-footer__link-list">
+ <li><a href="http://bazel.io">Home</a></li>
+ <li><a href="https://github.com/bazelbuild">GitHub</a></li>
+ </ul>
+ </div>
+ </footer>
+ </main>
+ </div>
+ </body>
+</html>
diff --git a/docs/python/python.md b/docs/python/python.md
new file mode 100644
index 0000000..a70c86a
--- /dev/null
+++ b/docs/python/python.md
@@ -0,0 +1,51 @@
+
+<!---
+Documentation generated by Skydoc
+-->
+<h1>python Rules</h1>
+
+
+<nav class="toc">
+ <h2>Macros</h2>
+ <ul>
+ <li><a href="#py_library">py_library</a></li>
+ <li><a href="#py_binary">py_binary</a></li>
+ <li><a href="#py_test">py_test</a></li>
+ </ul>
+</nav>
+<a name="py_library"></a>
+## py_library
+
+<pre>
+py_library()
+</pre>
+
+See the Bazel core py_library documentation.
+
+[available here](
+https://docs.bazel.build/versions/master/be/python.html#py_library).
+
+<a name="py_binary"></a>
+## py_binary
+
+<pre>
+py_binary()
+</pre>
+
+See the Bazel core py_binary documentation.
+
+[available here](
+https://docs.bazel.build/versions/master/be/python.html#py_binary).
+
+<a name="py_test"></a>
+## py_test
+
+<pre>
+py_test()
+</pre>
+
+See the Bazel core py_test documentation.
+
+[available here](
+https://docs.bazel.build/versions/master/be/python.html#py_test).
+
diff --git a/docs/python/whl.html b/docs/python/whl.html
new file mode 100644
index 0000000..5fcd672
--- /dev/null
+++ b/docs/python/whl.html
@@ -0,0 +1,135 @@
+
+
+<!--
+Documentation generated by Skydoc
+-->
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width initial-scale=1" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+
+ <title>Import .whl files into Bazel.</title>
+
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,600,700" type="text/css">
+ <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
+ <link rel="stylesheet" href="https://code.getmdl.io/1.1.1/material.green-light_blue.min.css">
+ <script defer src="https://code.getmdl.io/1.1.1/material.min.js"></script>
+ <link rel="stylesheet" href="./main.css">
+ </head>
+ <body>
+ <div class="mdl-layout mdl-js-layout mdl-layout--fixed-drawer
+ mdl-layout--fixed-header">
+ <header class="mdl-layout__header">
+ <div class="mdl-layout__header-row">
+ <span class="mdl-layout-title">Import .whl files into Bazel.</span>
+ </div>
+ </header>
+ <div class="mdl-layout__drawer">
+ <span class="mdl-layout-title">Bazel</span>
+ <nav class="drawer-nav">
+ <ul class="drawer-nav">
+
+<li><a href="./index.html">Overview</a></li>
+<li>
+ <a href="./python/python.html">python Rules</a>
+ <ul>
+ </ul>
+</li>
+<li><a href="./index.html">Overview</a></li>
+<li>
+ <a href="./python/pip.html">Import pip requirements into Bazel.</a>
+ <ul>
+ </ul>
+</li>
+<li><a href="./index.html">Overview</a></li>
+<li>
+ <a href="./python/whl.html">Import .whl files into Bazel.</a>
+ <ul>
+ </ul>
+</li>
+
+ </ul>
+ </nav>
+ </div>
+
+ <main class="mdl-layout__content">
+ <div class="page-content">
+ <h1>Import .whl files into Bazel.</h1>
+
+<nav class="toc">
+ <h2>Repository Rules</h2>
+ <ul>
+ <li><a href="#whl_library">whl_library</a></li>
+ </ul>
+</nav>
+ <hr>
+
+ <h2 id="whl_library">whl_library</h2>
+
+ <pre>whl_library(<a href="#whl_library.name">name</a>, <a href="#whl_library.requirements">requirements</a>, <a href="#whl_library.whl">whl</a>)</pre>
+
+ <p>A rule for importing &lt;code&gt;.whl&lt;/code&gt; dependencies into Bazel.</p>
+<p>&lt;b&gt;This rule is currently used to implement &lt;code&gt;pip_import&lt;/code&gt;,
+it is not intended to work standalone, and the interface may change.&lt;/b&gt;
+See &lt;code&gt;pip_import&lt;/code&gt; for proper usage.</p>
+<p>This rule imports a &lt;code&gt;.whl&lt;/code&gt; file as a &lt;code&gt;py_library&lt;/code&gt;:</p>
+&lt;pre&gt;&lt;code&gt;whl_library(
+ name = "foo",
+ whl = ":my-whl-file",
+ requirements = "name of pip_import rule",
+)
+&lt;/code&gt;&lt;/pre&gt;<p>This rule defines a &lt;code&gt;@foo//:pkg&lt;/code&gt; &lt;code&gt;py_library&lt;/code&gt; target.</p>
+
+
+ <h3 id="whl_library_args">Attributes</h3>
+
+<table class="params-table">
+ <colgroup>
+ <col class="col-param" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+ <tr id="whl_library.name">
+ <td><code>name</code></td>
+ <td>
+ <p><code><a href="https://bazel.build/docs/build-ref.html#name">Name</a>; Required</code></p>
+ <p>A unique name for this rule.</p>
+ </td>
+ </tr>
+ <tr id="whl_library.requirements">
+ <td><code>requirements</code></td>
+ <td>
+ <p><code>String; Optional; Default is ''</code></p>
+ <p>The name of the pip_import repository rule from which to
+load this .whl's dependencies.</p>
+ </td>
+ </tr>
+ <tr id="whl_library.whl">
+ <td><code>whl</code></td>
+ <td>
+ <p><code><a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; Required</code></p>
+ <p>The path to the .whl file (the name is expected to follow <a href="https://www.python.org/dev/peps/pep-0427/#file-name-convention">this
+convention</a>)</p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+
+ </div>
+
+ <footer class="mdl-mini-footer">
+ <div class="mdl-mini-footer__left-section">
+ <div class="mdl-logo">Bazel</div>
+ <ul class="mdl-mini-footer__link-list">
+ <li><a href="http://bazel.io">Home</a></li>
+ <li><a href="https://github.com/bazelbuild">GitHub</a></li>
+ </ul>
+ </div>
+ </footer>
+ </main>
+ </div>
+ </body>
+</html>
diff --git a/docs/python/whl.md b/docs/python/whl.md
new file mode 100644
index 0000000..41467ce
--- /dev/null
+++ b/docs/python/whl.md
@@ -0,0 +1,72 @@
+
+<!---
+Documentation generated by Skydoc
+-->
+<h1>Import .whl files into Bazel.</h1>
+
+
+<nav class="toc">
+ <h2>Repository Rules</h2>
+ <ul>
+ <li><a href="#whl_library">whl_library</a></li>
+ </ul>
+</nav>
+<a name="whl_library"></a>
+## whl_library
+
+<pre>
+whl_library(<a href="#whl_library.name">name</a>, <a href="#whl_library.requirements">requirements</a>, <a href="#whl_library.whl">whl</a>)
+</pre>
+
+A rule for importing <code>.whl</code> dependencies into Bazel.
+
+<b>This rule is currently used to implement <code>pip_import</code>,
+it is not intended to work standalone, and the interface may change.</b>
+See <code>pip_import</code> for proper usage.
+
+This rule imports a <code>.whl</code> file as a <code>py_library</code>:
+<pre><code>whl_library(
+ name = "foo",
+ whl = ":my-whl-file",
+ requirements = "name of pip_import rule",
+)
+</code></pre>
+
+This rule defines a <code>@foo//:pkg</code> <code>py_library</code> target.
+
+
+<a name="whl_library_args"></a>
+### Attributes
+
+
+<table class="params-table">
+ <colgroup>
+ <col class="col-param" />
+ <col class="col-description" />
+ </colgroup>
+ <tbody>
+ <tr id="whl_library.name">
+ <td><code>name</code></td>
+ <td>
+ <p><code><a href="https://bazel.build/docs/build-ref.html#name">Name</a>; Required</code></p>
+ <p>A unique name for this rule.</p>
+ </td>
+ </tr>
+ <tr id="whl_library.requirements">
+ <td><code>requirements</code></td>
+ <td>
+ <p><code>String; Optional; Default is ''</code></p>
+ <p>The name of the pip_import repository rule from which to
+load this .whl's dependencies.</p>
+ </td>
+ </tr>
+ <tr id="whl_library.whl">
+ <td><code>whl</code></td>
+ <td>
+ <p><code><a href="https://bazel.build/docs/build-ref.html#labels">Label</a>; Required</code></p>
+ <p>The path to the .whl file (the name is expected to follow <a href="https://www.python.org/dev/peps/pep-0427/#file-name-convention">this
+convention</a>)</p>
+ </td>
+ </tr>
+ </tbody>
+</table>
diff --git a/examples/BUILD b/examples/BUILD
new file mode 100644
index 0000000..426ddc0
--- /dev/null
+++ b/examples/BUILD
@@ -0,0 +1,16 @@
+# Copyright 2017 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"]) # Apache 2.0
diff --git a/examples/helloworld/BUILD b/examples/helloworld/BUILD
new file mode 100644
index 0000000..ccb8e36
--- /dev/null
+++ b/examples/helloworld/BUILD
@@ -0,0 +1,31 @@
+# Copyright 2017 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"]) # Apache 2.0
+
+load("@examples_helloworld//:requirements.bzl", "package")
+load("//python:python.bzl", "py_library", "py_test")
+
+py_library(
+ name = "helloworld",
+ srcs = ["helloworld.py"],
+ deps = [package("futures")],
+)
+
+py_test(
+ name = "helloworld_test",
+ srcs = ["helloworld_test.py"],
+ deps = [":helloworld"],
+)
diff --git a/examples/helloworld/helloworld.py b/examples/helloworld/helloworld.py
new file mode 100644
index 0000000..b629e80
--- /dev/null
+++ b/examples/helloworld/helloworld.py
@@ -0,0 +1,29 @@
+# Copyright 2017 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from concurrent import futures
+
+
+class HelloWorld(object):
+ def __init__(self):
+ self._threadpool = futures.ThreadPoolExecutor(max_workers=5)
+
+ def SayHello(self):
+ print("Hello World")
+
+ def SayHelloAsync(self):
+ self._threadpool.submit(self.SayHello)
+
+ def Stop(self):
+ self._threadpool.shutdown(wait = True)
diff --git a/examples/helloworld/helloworld_test.py b/examples/helloworld/helloworld_test.py
new file mode 100644
index 0000000..ca9f6b1
--- /dev/null
+++ b/examples/helloworld/helloworld_test.py
@@ -0,0 +1,41 @@
+# Copyright 2017 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import unittest
+
+from examples.helloworld import helloworld
+
+
+class HelloWorldTest(unittest.TestCase):
+
+ def test_helloworld(self):
+ hw = helloworld.HelloWorld()
+ hw.SayHello()
+
+ def test_helloworld_async(self):
+ hw = helloworld.HelloWorld()
+ hw.SayHelloAsync()
+ hw.Stop()
+
+ def test_helloworld_multiple(self):
+ hw = helloworld.HelloWorld()
+ hw.SayHelloAsync()
+ hw.SayHelloAsync()
+ hw.SayHelloAsync()
+ hw.SayHelloAsync()
+ hw.Stop()
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/examples/helloworld/requirements.txt b/examples/helloworld/requirements.txt
new file mode 100644
index 0000000..372420d
--- /dev/null
+++ b/examples/helloworld/requirements.txt
@@ -0,0 +1 @@
+futures>=3.1
diff --git a/examples/version/BUILD b/examples/version/BUILD
new file mode 100644
index 0000000..e999ba6
--- /dev/null
+++ b/examples/version/BUILD
@@ -0,0 +1,25 @@
+# Copyright 2017 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"]) # Apache 2.0
+
+load("@examples_version//:requirements.bzl", "package")
+load("//python:python.bzl", "py_test")
+
+py_test(
+ name = "version_test",
+ srcs = ["version_test.py"],
+ deps = [package("pip")],
+)
diff --git a/examples/version/requirements.txt b/examples/version/requirements.txt
new file mode 100644
index 0000000..433e330
--- /dev/null
+++ b/examples/version/requirements.txt
@@ -0,0 +1 @@
+pip==9.0.1
diff --git a/examples/version/version_test.py b/examples/version/version_test.py
new file mode 100644
index 0000000..9b469b7
--- /dev/null
+++ b/examples/version/version_test.py
@@ -0,0 +1,26 @@
+# Copyright 2017 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import pip
+import unittest
+
+
+class VersionTest(unittest.TestCase):
+
+ def test_version(self):
+ self.assertEqual(pip.__version__, '9.0.1')
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/python/BUILD b/python/BUILD
index 426ddc0..be4b731 100644
--- a/python/BUILD
+++ b/python/BUILD
@@ -14,3 +14,11 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"]) # Apache 2.0
+
+exports_files([
+ "pip.bzl",
+ "pip.sh",
+ "python.bzl",
+ "whl.bzl",
+ "whl.sh",
+])
diff --git a/python/pip.bzl b/python/pip.bzl
new file mode 100644
index 0000000..0345b50
--- /dev/null
+++ b/python/pip.bzl
@@ -0,0 +1,101 @@
+# Copyright 2017 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Import pip requirements into Bazel."""
+
+def _pip_import_impl(repository_ctx):
+ """Core implementation of pip_import."""
+
+ # Add an empty top-level BUILD file.
+ # This is because Bazel requires BUILD files along all paths accessed
+ # via //this/sort/of:path and we wouldn't be able to load our generated
+ # requirements.bzl without it.
+ repository_ctx.file("BUILD", "")
+
+ # To see the output, pass: quiet=False
+ 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(""),
+ ])
+
+ if result.return_code:
+ fail("pip_import failed: %s (%s)" % (result.stdout, result.stderr))
+
+pip_import = repository_rule(
+ attrs = {
+ "requirements": attr.label(
+ allow_files = True,
+ mandatory = True,
+ single_file = True,
+ ),
+ "_script": attr.label(
+ executable = True,
+ default = Label("//tools:piptool.par"),
+ cfg = "host",
+ ),
+ },
+ implementation = _pip_import_impl,
+)
+
+"""A rule for importing <code>requirements.txt</code> dependencies into Bazel.
+
+This rule imports a <code>requirements.txt</code> file and generates a new
+<code>requirements.bzl</code> file. This is used via the <code>WORKSPACE</code>
+pattern:
+<pre><code>pip_import(
+ name = "foo",
+ requirements = ":requirements.txt",
+)
+load("@foo//:requirements.bzl", "pip_install")
+pip_install()
+</code></pre>
+
+You can then reference imported dependencies from your <code>BUILD</code>
+file with:
+<pre><code>load("@foo//:requirements.bzl", "package")
+py_library(
+ name = "bar",
+ ...
+ deps = [
+ "//my/other:dep",
+ package("futures"),
+ package("mock"),
+ ],
+)
+</code></pre>
+
+Or alternatively:
+<pre><code>load("@foo//:requirements.bzl", "all_packages")
+py_binary(
+ name = "baz",
+ ...
+ deps = [
+ ":foo",
+ ] + all_packages,
+)
+</code></pre>
+
+Args:
+ requirements: The label of a requirements.txt file.
+"""
+
+def pip_repositories():
+ """Pull in dependencies needed for pulling in pip dependencies.
+
+ A placeholder method that will eventually pull in any dependencies
+ needed to install pip dependencies.
+ """
+ pass
diff --git a/python/python.bzl b/python/python.bzl
index fca849d..8cbb11a 100644
--- a/python/python.bzl
+++ b/python/python.bzl
@@ -13,7 +13,25 @@
# limitations under the License.
def py_library(*args, **kwargs):
+ """See the Bazel core py_library documentation.
+
+ [available here](
+ https://docs.bazel.build/versions/master/be/python.html#py_library).
+ """
native.py_library(*args, **kwargs)
def py_binary(*args, **kwargs):
+ """See the Bazel core py_binary documentation.
+
+ [available here](
+ https://docs.bazel.build/versions/master/be/python.html#py_binary).
+ """
native.py_binary(*args, **kwargs)
+
+def py_test(*args, **kwargs):
+ """See the Bazel core py_test documentation.
+
+ [available here](
+ https://docs.bazel.build/versions/master/be/python.html#py_test).
+ """
+ native.py_test(*args, **kwargs)
diff --git a/python/requirements.txt b/python/requirements.txt
new file mode 100644
index 0000000..0fd2ec6
--- /dev/null
+++ b/python/requirements.txt
@@ -0,0 +1,2 @@
+pip==9.0.1
+wheel==0.30.0a0
diff --git a/python/whl.bzl b/python/whl.bzl
new file mode 100644
index 0000000..f7827ed
--- /dev/null
+++ b/python/whl.bzl
@@ -0,0 +1,67 @@
+# Copyright 2017 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Import .whl files into Bazel."""
+
+def _whl_impl(repository_ctx):
+ """Core implementation of whl_library."""
+
+ result = repository_ctx.execute([
+ "python",
+ repository_ctx.path(repository_ctx.attr._script),
+ "--whl", repository_ctx.path(repository_ctx.attr.whl),
+ "--requirements", repository_ctx.attr.requirements,
+ ])
+ if result.return_code:
+ fail("whl_library failed: %s (%s)" % (result.stdout, result.stderr))
+
+whl_library = repository_rule(
+ attrs = {
+ "whl": attr.label(
+ allow_files = True,
+ mandatory = True,
+ single_file = True,
+ ),
+ "requirements": attr.string(),
+ "_script": attr.label(
+ executable = True,
+ default = Label("//rules_python:whl.py"),
+ cfg = "host",
+ ),
+ },
+ implementation = _whl_impl,
+)
+
+"""A rule for importing <code>.whl</code> dependencies into Bazel.
+
+<b>This rule is currently used to implement <code>pip_import</code>,
+it is not intended to work standalone, and the interface may change.</b>
+See <code>pip_import</code> for proper usage.
+
+This rule imports a <code>.whl</code> file as a <code>py_library</code>:
+<pre><code>whl_library(
+ name = "foo",
+ whl = ":my-whl-file",
+ requirements = "name of pip_import rule",
+)
+</code></pre>
+
+This rule defines a <code>@foo//:pkg</code> <code>py_library</code> target.
+
+Args:
+ whl: The path to the .whl file (the name is expected to follow [this
+ convention](https://www.python.org/dev/peps/pep-0427/#file-name-convention))
+
+ requirements: The name of the pip_import repository rule from which to
+ load this .whl's dependencies.
+"""
diff --git a/rules_python/BUILD b/rules_python/BUILD
new file mode 100644
index 0000000..a0534fd
--- /dev/null
+++ b/rules_python/BUILD
@@ -0,0 +1,47 @@
+# Copyright 2017 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"]) # Apache 2.0
+
+load("//python:python.bzl", "py_binary", "py_library", "py_test")
+
+py_library(
+ name = "whl",
+ srcs = ["whl.py"],
+)
+
+py_test(
+ name = "whl_test",
+ srcs = ["whl_test.py"],
+ data = [
+ "@futures_whl//file",
+ "@grpc_whl//file",
+ "@mock_whl//file",
+ ],
+ deps = [":whl"],
+)
+
+load("@subpar//:subpar.bzl", "par_binary")
+load("@piptool_deps//:requirements.bzl", "all_packages")
+
+# TODO(mattmoor): Bundle this tool as a PAR without any
+# system-installed pre-requisites. See TODOs in piptool.py.
+par_binary(
+ name = "piptool",
+ srcs = ["piptool.py"],
+ deps = [
+ ":whl",
+ ] + all_packages,
+)
diff --git a/rules_python/piptool.py b/rules_python/piptool.py
new file mode 100644
index 0000000..b5f4121
--- /dev/null
+++ b/rules_python/piptool.py
@@ -0,0 +1,154 @@
+# Copyright 2017 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""The piptool module imports pip requirements into Bazel rules."""
+
+import argparse
+import json
+import os
+import pkgutil
+import re
+import sys
+import tempfile
+import zipfile
+
+# TODO(mattmoor): When this tool is invoked bundled as a PAR file,
+# but not as a py_binary, we get a warning that indicates the system
+# installed version of PIP is being picked up instead of our bundled
+# version, which should be 9.0.1, e.g.
+# You are using pip version 1.5.4, however version 9.0.1 is available.
+# You should consider upgrading via the 'pip install --upgrade pip' command.
+try:
+ # Make sure we're using a suitable version of pip as a library.
+ # Fallback on using it as a CLI.
+ from pip._vendor import requests
+
+ from pip import main as _pip_main
+ def pip_main(argv):
+ # Extract the certificates from the PAR following the example of get-pip.py
+ # https://github.com/pypa/get-pip/blob/430ba37776ae2ad89/template.py#L164-L168
+ cert_path = os.path.join(tempfile.mkdtemp(), "cacert.pem")
+ with open(cert_path, "wb") as cert:
+ cert.write(pkgutil.get_data("pip._vendor.requests", "cacert.pem"))
+ return _pip_main(argv + ["--cert", cert_path])
+
+except:
+ import subprocess
+
+ def pip_main(argv):
+ return subprocess.call(['pip'] + argv)
+
+# TODO(mattmoor): We can't easily depend on other libraries when
+# being invoked as a raw .py file. Once bundled, we should be able
+# to remove this fallback on a stub implementation of Wheel.
+try:
+ from rules_python.whl import Wheel
+except:
+ class Wheel(object):
+
+ def __init__(self, path):
+ self._path = path
+
+ def basename(self):
+ return os.path.basename(self._path)
+
+ def distribution(self):
+ # See https://www.python.org/dev/peps/pep-0427/#file-name-convention
+ parts = self.basename().split('-')
+ return parts[0]
+
+ def version(self):
+ # See https://www.python.org/dev/peps/pep-0427/#file-name-convention
+ parts = self.basename().split('-')
+ return parts[1]
+
+ def repository_name(self):
+ # Returns the canonical name of the Bazel repository for this package.
+ canonical = 'pypi__{}_{}'.format(self.distribution(), self.version())
+ # Escape any illegal characters with underscore.
+ return re.sub('[-.]', '_', canonical)
+
+parser = argparse.ArgumentParser(
+ description='Import Python dependencies into Bazel.')
+
+parser.add_argument('--name', action='store',
+ help=('The namespace of the import.'))
+
+parser.add_argument('--input', action='store',
+ help=('The requirements.txt file to import.'))
+
+parser.add_argument('--output', action='store',
+ help=('The requirements.bzl file to export.'))
+
+parser.add_argument('--directory', action='store',
+ help=('The directory into which to put .whl files.'))
+
+
+def main():
+ args = parser.parse_args()
+
+ # https://github.com/pypa/pip/blob/9.0.1/pip/__init__.py#L209
+ if pip_main(["wheel", "-w", args.directory, "-r", args.input]):
+ sys.exit(1)
+
+ # Enumerate the .whl files we downloaded.
+ def list_whls():
+ dir = args.directory + '/'
+ for root, unused_dirnames, filenames in os.walk(dir):
+ for fname in filenames:
+ if fname.endswith('.whl'):
+ yield os.path.join(root, fname)
+
+ def whl_library(wheel):
+ # Indentation here matters. whl_library must be within the scope
+ # of the function below. We also avoid reimporting an existing WHL.
+ return """
+ if "{repo_name}" not in native.existing_rules():
+ whl_library(
+ name = "{repo_name}",
+ whl = "@{name}//:{path}",
+ requirements = "@{name}//:requirements.bzl",
+ )""".format(name=args.name, repo_name=wheel.repository_name(),
+ path=wheel.basename())
+
+ whls = [Wheel(path) for path in list_whls()]
+
+ with open(args.output, 'w') as f:
+ f.write("""\
+# Install pip requirements.
+#
+# Generated from {input}
+
+load("@io_bazel_rules_python//python:whl.bzl", "whl_library")
+
+def pip_install():
+ {whl_libraries}
+
+_packages = {{
+ {mappings}
+}}
+
+all_packages = _packages.values()
+
+def package(name):
+ name = name.replace("-", "_")
+ return _packages[name]
+""".format(input=args.input,
+ whl_libraries='\n'.join(map(whl_library, whls)),
+ mappings=','.join([
+ '"%s": "@%s//:pkg"' % (wheel.distribution(), wheel.repository_name())
+ for wheel in whls
+ ])))
+
+if __name__ == '__main__':
+ main()
diff --git a/rules_python/whl.py b/rules_python/whl.py
new file mode 100644
index 0000000..d47364a
--- /dev/null
+++ b/rules_python/whl.py
@@ -0,0 +1,131 @@
+# Copyright 2017 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""The whl modules defines classes for interacting with Python packages."""
+
+import argparse
+import json
+import os
+import re
+import zipfile
+
+
+class Wheel(object):
+
+ def __init__(self, path):
+ self._path = path
+
+ def path(self):
+ return self._path
+
+ def basename(self):
+ return os.path.basename(self.path())
+
+ def distribution(self):
+ # See https://www.python.org/dev/peps/pep-0427/#file-name-convention
+ parts = self.basename().split('-')
+ return parts[0]
+
+ def version(self):
+ # See https://www.python.org/dev/peps/pep-0427/#file-name-convention
+ parts = self.basename().split('-')
+ return parts[1]
+
+ def repository_name(self):
+ # Returns the canonical name of the Bazel repository for this package.
+ canonical = 'pypi__{}_{}'.format(self.distribution(), self.version())
+ # Escape any illegal characters with underscore.
+ return re.sub('[-.]', '_', canonical)
+
+ def _dist_info(self):
+ # Return the name of the dist-info directory within the .whl file.
+ # e.g. google_cloud-0.27.0-py2.py3-none-any.whl ->
+ # google_cloud-0.27.0.dist-info
+ return '{}-{}.dist-info'.format(self.distribution(), self.version())
+
+ def metadata(self):
+ # Extract the structured data from metadata.json in the WHL's dist-info
+ # directory.
+ with zipfile.ZipFile(self.path(), 'r') as whl:
+ with whl.open(os.path.join(self._dist_info(), 'metadata.json')) as f:
+ return json.loads(f.read())
+
+ def name(self):
+ return self.metadata().get('name')
+
+ def dependencies(self):
+ # TODO(mattmoor): Is there a schema to follow for this?
+ run_requires = self.metadata().get('run_requires', [])
+ for requirement in run_requires:
+ if 'extra' in requirement:
+ # TODO(mattmoor): What's the best way to support "extras"?
+ # https://packaging.python.org/tutorials/installing-packages/#installing-setuptools-extras
+ continue
+ if 'environment' in requirement:
+ # TODO(mattmoor): What's the best way to support "environment"?
+ # This typically communicates things like python version (look at
+ # "wheel" for a good example)
+ continue
+ requires = requirement.get('requires', [])
+ for entry in requires:
+ # Strip off any trailing versioning data.
+ parts = entry.split(' ', 1)
+ yield parts[0]
+
+ def expand(self, directory):
+ with zipfile.ZipFile(self.path(), 'r') as whl:
+ whl.extractall(directory)
+
+
+parser = argparse.ArgumentParser(
+ description='Unpack a WHL file as a py_library.')
+
+parser.add_argument('--whl', action='store',
+ help=('The .whl file we are expanding.'))
+
+parser.add_argument('--requirements', action='store',
+ help='The pip_import from which to draw dependencies.')
+
+parser.add_argument('--directory', action='store', default='.',
+ help='The directory into which to expand things.')
+
+def main():
+ args = parser.parse_args()
+ whl = Wheel(args.whl)
+
+ # Extract the files into the current directory
+ whl.expand(args.directory)
+
+ with open(os.path.join(args.directory, 'BUILD'), 'w') as f:
+ f.write("""
+package(default_visibility = ["//visibility:public"])
+
+load("{requirements}", "package")
+
+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 = [{dependencies}],
+ )""".format(
+ requirements=args.requirements,
+ dependencies=','.join([
+ 'package("%s")' % d
+ for d in whl.dependencies()
+ ])))
+
+if __name__ == '__main__':
+ main()
diff --git a/rules_python/whl_test.py b/rules_python/whl_test.py
new file mode 100644
index 0000000..1b06821
--- /dev/null
+++ b/rules_python/whl_test.py
@@ -0,0 +1,57 @@
+# Copyright 2017 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import unittest
+
+from rules_python import whl
+
+
+def TestData(name):
+ return os.path.join(os.environ['TEST_SRCDIR'], name)
+
+
+class WheelTest(unittest.TestCase):
+
+ def test_grpc_whl(self):
+ td = TestData('grpc_whl/file/grpcio-1.6.0-cp27-cp27m-manylinux1_i686.whl')
+ wheel = whl.Wheel(td)
+ self.assertEqual(wheel.name(), 'grpcio')
+ self.assertEqual(wheel.distribution(), 'grpcio')
+ self.assertEqual(wheel.version(), '1.6.0')
+ self.assertEqual(set(wheel.dependencies()),
+ set(['enum34', 'futures', 'protobuf', 'six']))
+ self.assertEqual('pypi__grpcio_1_6_0', wheel.repository_name())
+
+ def test_futures_whl(self):
+ td = TestData('futures_whl/file/futures-3.1.1-py2-none-any.whl')
+ wheel = whl.Wheel(td)
+ self.assertEqual(wheel.name(), 'futures')
+ self.assertEqual(wheel.distribution(), 'futures')
+ self.assertEqual(wheel.version(), '3.1.1')
+ self.assertEqual(set(wheel.dependencies()), set())
+ self.assertEqual('pypi__futures_3_1_1', wheel.repository_name())
+
+ def test_mock_whl(self):
+ td = TestData('mock_whl/file/mock-2.0.0-py2.py3-none-any.whl')
+ wheel = whl.Wheel(td)
+ self.assertEqual(wheel.name(), 'mock')
+ self.assertEqual(wheel.distribution(), 'mock')
+ self.assertEqual(wheel.version(), '2.0.0')
+ self.assertEqual(set(wheel.dependencies()),
+ set(['pbr', 'six']))
+ self.assertEqual('pypi__mock_2_0_0', wheel.repository_name())
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tools/BUILD b/tools/BUILD
new file mode 100644
index 0000000..c70f030
--- /dev/null
+++ b/tools/BUILD
@@ -0,0 +1,19 @@
+# Copyright 2017 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"]) # Apache 2.0
+
+# This is generated and updated by ./update_piptool.sh
+exports_files(["piptool.par"])
diff --git a/tools/piptool.par b/tools/piptool.par
new file mode 100755
index 0000000..86fed6d
--- /dev/null
+++ b/tools/piptool.par
Binary files differ
diff --git a/update_docs.sh b/update_docs.sh
new file mode 100755
index 0000000..bb9dec9
--- /dev/null
+++ b/update_docs.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+# Copyright 2017 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -euo pipefail
+
+bazel build //docs/...
+unzip -d docs/ -o bazel-bin/docs/docs-md-skydoc.zip
+unzip -d docs/ -o bazel-bin/docs/docs-html-skydoc.zip
diff --git a/update_piptool.sh b/update_piptool.sh
new file mode 100755
index 0000000..7eb8450
--- /dev/null
+++ b/update_piptool.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Copyright 2017 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -euo pipefail
+
+bazel build //rules_python:piptool.par
+cp bazel-bin/rules_python/piptool.par tools/piptool.par
diff --git a/update_tools.sh b/update_tools.sh
new file mode 100755
index 0000000..7eb8450
--- /dev/null
+++ b/update_tools.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# Copyright 2017 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -euo pipefail
+
+bazel build //rules_python:piptool.par
+cp bazel-bin/rules_python/piptool.par tools/piptool.par