Adds the "Requires-Python" metadata support. Fixes #378 (#379)

* Adds the "Requires-Python" metadata support. Fixes #378

This adds support for allowing people to specify which versions
of python a wheel should run on. This was added in PEP 440

* Move 'Requires-Python' to a conditional

* Add python test to check metadata

Co-authored-by: Alex Eagle <eagle@post.harvard.edu>
diff --git a/experimental/examples/wheel/BUILD b/experimental/examples/wheel/BUILD
index 06371fe..d721e4c 100644
--- a/experimental/examples/wheel/BUILD
+++ b/experimental/examples/wheel/BUILD
@@ -135,6 +135,17 @@
     ],
 )
 
+py_wheel(
+    name = "python_requires_in_a_package",
+    distribution = "example_python_requires_in_a_package",
+    python_tag = "py3",
+    python_requires = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*",
+    version = "0.0.1",
+    deps = [
+        ":example_pkg",
+    ],
+)
+
 py_test(
     name = "wheel_test",
     srcs = ["wheel_test.py"],
@@ -145,5 +156,6 @@
         ":customized",
         ":minimal_with_py_library",
         ":minimal_with_py_package",
+        ":python_requires_in_a_package"
     ],
 )
diff --git a/experimental/examples/wheel/wheel_test.py b/experimental/examples/wheel/wheel_test.py
index 318367b..b392457 100644
--- a/experimental/examples/wheel/wheel_test.py
+++ b/experimental/examples/wheel/wheel_test.py
@@ -70,7 +70,8 @@
                 'example_customized-0.0.1.dist-info/WHEEL')
             metadata_contents = zf.read(
                 'example_customized-0.0.1.dist-info/METADATA')
-            entry_point_contents = zf.read('example_customized-0.0.1.dist-info/entry_points.txt')
+            entry_point_contents = zf.read(
+                'example_customized-0.0.1.dist-info/entry_points.txt')
             # The entries are guaranteed to be sorted.
             self.assertEquals(record_contents, b"""\
 example_customized-0.0.1.dist-info/METADATA,sha256=TeeEmokHE2NWjkaMcVJuSAq4_AXUoIad2-SLuquRmbg,372
@@ -162,6 +163,24 @@
                  'example_custom_package_root_multi_prefix_reverse_order-0.0.1.dist-info/METADATA',
                  'example_custom_package_root_multi_prefix_reverse_order-0.0.1.dist-info/RECORD'])
 
+    def test_python_requires_wheel(self):
+        filename = os.path.join(os.environ['TEST_SRCDIR'],
+                                'rules_python', 'experimental',
+                                'examples', 'wheel',
+                                'example_python_requires_in_a_package-0.0.1-py3-none-any.whl')
+        with zipfile.ZipFile(filename) as zf:
+            metadata_contents = zf.read(
+                'example_python_requires_in_a_package-0.0.1.dist-info/METADATA')
+            # The entries are guaranteed to be sorted.
+            self.assertEquals(metadata_contents, b"""\
+Metadata-Version: 2.1
+Name: example_python_requires_in_a_package
+Version: 0.0.1
+Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*
+
+UNKNOWN
+""")
+
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/experimental/python/wheel.bzl b/experimental/python/wheel.bzl
index 79cbc97..3de218f 100644
--- a/experimental/python/wheel.bzl
+++ b/experimental/python/wheel.bzl
@@ -110,6 +110,7 @@
     args.add("--name", ctx.attr.distribution)
     args.add("--version", ctx.attr.version)
     args.add("--python_tag", ctx.attr.python_tag)
+    args.add("--python_requires", ctx.attr.python_requires)
     args.add("--abi", ctx.attr.abi)
     args.add("--platform", ctx.attr.platform)
     args.add("--out", outfile.path)
@@ -249,6 +250,7 @@
     "description_file": attr.label(allow_single_file = True),
     "homepage": attr.string(default = ""),
     "license": attr.string(default = ""),
+    "python_requires": attr.string(default = ""),
     "strip_path_prefixes": attr.string_list(
         default = [],
         doc = "path prefixes to strip from files added to the generated package",
diff --git a/experimental/tools/wheelmaker.py b/experimental/tools/wheelmaker.py
index 38fb122..799eed7 100644
--- a/experimental/tools/wheelmaker.py
+++ b/experimental/tools/wheelmaker.py
@@ -129,8 +129,8 @@
             wheel_contents += "Tag: %s\n" % tag
         self.add_string(self.distinfo_path('WHEEL'), wheel_contents)
 
-    def add_metadata(self, extra_headers, description, classifiers, requires,
-                     extra_requires):
+    def add_metadata(self, extra_headers, description, classifiers, python_requires,
+                     requires, extra_requires):
         """Write METADATA file to the distribution."""
         # https://www.python.org/dev/peps/pep-0566/
         # https://packaging.python.org/specifications/core-metadata/
@@ -141,6 +141,8 @@
         metadata.extend(extra_headers)
         for classifier in classifiers:
             metadata.append("Classifier: %s" % classifier)
+        if python_requires:
+            metadata.append("Requires-Python: %s" % python_requires)
         for requirement in requires:
             metadata.append("Requires-Dist: %s" % requirement)
 
@@ -225,6 +227,8 @@
     wheel_group.add_argument('--classifier', action='append',
                              help="Classifiers to embed in package metadata. "
                                   "Can be supplied multiple times")
+    wheel_group.add_argument('--python_requires',
+                             help="Version of python that the wheel will work with")
     wheel_group.add_argument('--description_file',
                              help="Path to the file with package description")
     wheel_group.add_argument('--entry_points_file',
@@ -302,17 +306,20 @@
                 req, option = extra.rsplit(';', 1)
                 extra_requires[option].append(req)
         classifiers = arguments.classifier or []
+        python_requires = arguments.python_requires or ""
         requires = arguments.requires or []
         extra_headers = arguments.header or []
 
         maker.add_metadata(extra_headers=extra_headers,
                            description=description,
                            classifiers=classifiers,
+                           python_requires=python_requires,
                            requires=requires,
                            extra_requires=extra_requires)
 
         if arguments.entry_points_file:
-            maker.add_file(maker.distinfo_path("entry_points.txt"), arguments.entry_points_file)
+            maker.add_file(maker.distinfo_path(
+                "entry_points.txt"), arguments.entry_points_file)
 
         maker.add_recordfile()