• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2018 The Bazel Authors. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#    http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import os
16import platform
17import subprocess
18import unittest
19import zipfile
20
21
22class WheelTest(unittest.TestCase):
23    maxDiff = None
24
25    def test_py_library_wheel(self):
26        filename = os.path.join(
27            os.environ["TEST_SRCDIR"],
28            "rules_python",
29            "examples",
30            "wheel",
31            "example_minimal_library-0.0.1-py3-none-any.whl",
32        )
33        with zipfile.ZipFile(filename) as zf:
34            self.assertEqual(
35                zf.namelist(),
36                [
37                    "examples/wheel/lib/module_with_data.py",
38                    "examples/wheel/lib/simple_module.py",
39                    "example_minimal_library-0.0.1.dist-info/WHEEL",
40                    "example_minimal_library-0.0.1.dist-info/METADATA",
41                    "example_minimal_library-0.0.1.dist-info/RECORD",
42                ],
43            )
44
45    def test_py_package_wheel(self):
46        filename = os.path.join(
47            os.environ["TEST_SRCDIR"],
48            "rules_python",
49            "examples",
50            "wheel",
51            "example_minimal_package-0.0.1-py3-none-any.whl",
52        )
53        with zipfile.ZipFile(filename) as zf:
54            self.assertEqual(
55                zf.namelist(),
56                [
57                    "examples/wheel/lib/data.txt",
58                    "examples/wheel/lib/module_with_data.py",
59                    "examples/wheel/lib/simple_module.py",
60                    "examples/wheel/main.py",
61                    "example_minimal_package-0.0.1.dist-info/WHEEL",
62                    "example_minimal_package-0.0.1.dist-info/METADATA",
63                    "example_minimal_package-0.0.1.dist-info/RECORD",
64                ],
65            )
66
67    def test_customized_wheel(self):
68        filename = os.path.join(
69            os.environ["TEST_SRCDIR"],
70            "rules_python",
71            "examples",
72            "wheel",
73            "example_customized-0.0.1-py3-none-any.whl",
74        )
75        with zipfile.ZipFile(filename) as zf:
76            self.assertEqual(
77                zf.namelist(),
78                [
79                    "examples/wheel/lib/data.txt",
80                    "examples/wheel/lib/module_with_data.py",
81                    "examples/wheel/lib/simple_module.py",
82                    "examples/wheel/main.py",
83                    "example_customized-0.0.1.dist-info/WHEEL",
84                    "example_customized-0.0.1.dist-info/METADATA",
85                    "example_customized-0.0.1.dist-info/entry_points.txt",
86                    "example_customized-0.0.1.dist-info/NOTICE",
87                    "example_customized-0.0.1.dist-info/README",
88                    "example_customized-0.0.1.dist-info/RECORD",
89                ],
90            )
91            record_contents = zf.read("example_customized-0.0.1.dist-info/RECORD")
92            wheel_contents = zf.read("example_customized-0.0.1.dist-info/WHEEL")
93            metadata_contents = zf.read("example_customized-0.0.1.dist-info/METADATA")
94            entry_point_contents = zf.read(
95                "example_customized-0.0.1.dist-info/entry_points.txt"
96            )
97
98            self.assertEqual(
99                record_contents,
100                # The entries are guaranteed to be sorted.
101                b"""\
102example_customized-0.0.1.dist-info/METADATA,sha256=QYQcDJFQSIqan8eiXqL67bqsUfgEAwf2hoK_Lgi1S-0,559
103example_customized-0.0.1.dist-info/NOTICE,sha256=Xpdw-FXET1IRgZ_wTkx1YQfo1-alET0FVf6V1LXO4js,76
104example_customized-0.0.1.dist-info/README,sha256=WmOFwZ3Jga1bHG3JiGRsUheb4UbLffUxyTdHczS27-o,40
105example_customized-0.0.1.dist-info/RECORD,,
106example_customized-0.0.1.dist-info/WHEEL,sha256=sobxWSyDDkdg_rinUth-jxhXHqoNqlmNMJY3aTZn2Us,91
107example_customized-0.0.1.dist-info/entry_points.txt,sha256=pqzpbQ8MMorrJ3Jp0ntmpZcuvfByyqzMXXi2UujuXD0,137
108examples/wheel/lib/data.txt,sha256=9vJKEdfLu8bZRArKLroPZJh1XKkK3qFMXiM79MBL2Sg,12
109examples/wheel/lib/module_with_data.py,sha256=8s0Khhcqz3yVsBKv2IB5u4l4TMKh7-c_V6p65WVHPms,637
110examples/wheel/lib/simple_module.py,sha256=z2hwciab_XPNIBNH8B1Q5fYgnJvQTeYf0ZQJpY8yLLY,637
111examples/wheel/main.py,sha256=sgg5iWN_9inYBjm6_Zw27hYdmo-l24fA-2rfphT-IlY,909
112""",
113            )
114            self.assertEqual(
115                wheel_contents,
116                b"""\
117Wheel-Version: 1.0
118Generator: bazel-wheelmaker 1.0
119Root-Is-Purelib: true
120Tag: py3-none-any
121""",
122            )
123            self.assertEqual(
124                metadata_contents,
125                b"""\
126Metadata-Version: 2.1
127Name: example_customized
128Author: Example Author with non-ascii characters: \xc5\xbc\xc3\xb3\xc5\x82w
129Author-email: example@example.com
130Home-page: www.example.com
131License: Apache 2.0
132Description-Content-Type: text/markdown
133Summary: A one-line summary of this test package
134Project-URL: Bug Tracker, www.example.com/issues
135Project-URL: Documentation, www.example.com/docs
136Classifier: License :: OSI Approved :: Apache Software License
137Classifier: Intended Audience :: Developers
138Requires-Dist: pytest
139Version: 0.0.1
140
141This is a sample description of a wheel.
142""",
143            )
144            self.assertEqual(
145                entry_point_contents,
146                b"""\
147[console_scripts]
148another = foo.bar:baz
149customized_wheel = examples.wheel.main:main
150
151[group2]
152first = first.main:f
153second = second.main:s""",
154            )
155
156    def test_filename_escaping(self):
157        filename = os.path.join(
158            os.environ["TEST_SRCDIR"],
159            "rules_python",
160            "examples",
161            "wheel",
162            "file_name_escaping-0.0.1_r7-py3-none-any.whl",
163        )
164        with zipfile.ZipFile(filename) as zf:
165            self.assertEqual(
166                zf.namelist(),
167                [
168                    "examples/wheel/lib/data.txt",
169                    "examples/wheel/lib/module_with_data.py",
170                    "examples/wheel/lib/simple_module.py",
171                    "examples/wheel/main.py",
172                    # PEP calls for replacing only in the archive filename.
173                    # Alas setuptools also escapes in the dist-info directory
174                    # name, so let's be compatible.
175                    "file_name_escaping-0.0.1_r7.dist-info/WHEEL",
176                    "file_name_escaping-0.0.1_r7.dist-info/METADATA",
177                    "file_name_escaping-0.0.1_r7.dist-info/RECORD",
178                ],
179            )
180            metadata_contents = zf.read(
181                "file_name_escaping-0.0.1_r7.dist-info/METADATA"
182            )
183            self.assertEqual(
184                metadata_contents,
185                b"""\
186Metadata-Version: 2.1
187Name: file~~name-escaping
188Version: 0.0.1-r7
189
190UNKNOWN
191""",
192            )
193
194    def test_custom_package_root_wheel(self):
195        filename = os.path.join(
196            os.environ["TEST_SRCDIR"],
197            "rules_python",
198            "examples",
199            "wheel",
200            "examples_custom_package_root-0.0.1-py3-none-any.whl",
201        )
202
203        with zipfile.ZipFile(filename) as zf:
204            self.assertEqual(
205                zf.namelist(),
206                [
207                    "wheel/lib/data.txt",
208                    "wheel/lib/module_with_data.py",
209                    "wheel/lib/simple_module.py",
210                    "wheel/main.py",
211                    "examples_custom_package_root-0.0.1.dist-info/WHEEL",
212                    "examples_custom_package_root-0.0.1.dist-info/METADATA",
213                    "examples_custom_package_root-0.0.1.dist-info/entry_points.txt",
214                    "examples_custom_package_root-0.0.1.dist-info/RECORD",
215                ],
216            )
217
218            record_contents = zf.read(
219                "examples_custom_package_root-0.0.1.dist-info/RECORD"
220            ).decode("utf-8")
221
222            # Ensure RECORD files do not have leading forward slashes
223            for line in record_contents.splitlines():
224                self.assertFalse(line.startswith("/"))
225
226    def test_custom_package_root_multi_prefix_wheel(self):
227        filename = os.path.join(
228            os.environ["TEST_SRCDIR"],
229            "rules_python",
230            "examples",
231            "wheel",
232            "example_custom_package_root_multi_prefix-0.0.1-py3-none-any.whl",
233        )
234
235        with zipfile.ZipFile(filename) as zf:
236            self.assertEqual(
237                zf.namelist(),
238                [
239                    "data.txt",
240                    "module_with_data.py",
241                    "simple_module.py",
242                    "main.py",
243                    "example_custom_package_root_multi_prefix-0.0.1.dist-info/WHEEL",
244                    "example_custom_package_root_multi_prefix-0.0.1.dist-info/METADATA",
245                    "example_custom_package_root_multi_prefix-0.0.1.dist-info/RECORD",
246                ],
247            )
248
249            record_contents = zf.read(
250                "example_custom_package_root_multi_prefix-0.0.1.dist-info/RECORD"
251            ).decode("utf-8")
252
253            # Ensure RECORD files do not have leading forward slashes
254            for line in record_contents.splitlines():
255                self.assertFalse(line.startswith("/"))
256
257    def test_custom_package_root_multi_prefix_reverse_order_wheel(self):
258        filename = os.path.join(
259            os.environ["TEST_SRCDIR"],
260            "rules_python",
261            "examples",
262            "wheel",
263            "example_custom_package_root_multi_prefix_reverse_order-0.0.1-py3-none-any.whl",
264        )
265
266        with zipfile.ZipFile(filename) as zf:
267            self.assertEqual(
268                zf.namelist(),
269                [
270                    "lib/data.txt",
271                    "lib/module_with_data.py",
272                    "lib/simple_module.py",
273                    "main.py",
274                    "example_custom_package_root_multi_prefix_reverse_order-0.0.1.dist-info/WHEEL",
275                    "example_custom_package_root_multi_prefix_reverse_order-0.0.1.dist-info/METADATA",
276                    "example_custom_package_root_multi_prefix_reverse_order-0.0.1.dist-info/RECORD",
277                ],
278            )
279
280            record_contents = zf.read(
281                "example_custom_package_root_multi_prefix_reverse_order-0.0.1.dist-info/RECORD"
282            ).decode("utf-8")
283
284            # Ensure RECORD files do not have leading forward slashes
285            for line in record_contents.splitlines():
286                self.assertFalse(line.startswith("/"))
287
288    def test_python_requires_wheel(self):
289        filename = os.path.join(
290            os.environ["TEST_SRCDIR"],
291            "rules_python",
292            "examples",
293            "wheel",
294            "example_python_requires_in_a_package-0.0.1-py3-none-any.whl",
295        )
296        with zipfile.ZipFile(filename) as zf:
297            metadata_contents = zf.read(
298                "example_python_requires_in_a_package-0.0.1.dist-info/METADATA"
299            )
300            # The entries are guaranteed to be sorted.
301            self.assertEqual(
302                metadata_contents,
303                b"""\
304Metadata-Version: 2.1
305Name: example_python_requires_in_a_package
306Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*
307Version: 0.0.1
308
309UNKNOWN
310""",
311            )
312
313    def test_python_abi3_binary_wheel(self):
314        arch = "amd64"
315        if platform.system() != "Windows":
316            arch = subprocess.check_output(["uname", "-m"]).strip().decode()
317        # These strings match the strings from py_wheel() in BUILD
318        os_strings = {
319            "Linux": "manylinux2014",
320            "Darwin": "macosx_11_0",
321            "Windows": "win",
322        }
323        os_string = os_strings[platform.system()]
324        filename = os.path.join(
325            os.environ["TEST_SRCDIR"],
326            "rules_python",
327            "examples",
328            "wheel",
329            f"example_python_abi3_binary_wheel-0.0.1-cp38-abi3-{os_string}_{arch}.whl",
330        )
331        with zipfile.ZipFile(filename) as zf:
332            metadata_contents = zf.read(
333                "example_python_abi3_binary_wheel-0.0.1.dist-info/METADATA"
334            )
335            # The entries are guaranteed to be sorted.
336            self.assertEqual(
337                metadata_contents,
338                b"""\
339Metadata-Version: 2.1
340Name: example_python_abi3_binary_wheel
341Requires-Python: >=3.8
342Version: 0.0.1
343
344UNKNOWN
345""",
346            )
347            wheel_contents = zf.read(
348                "example_python_abi3_binary_wheel-0.0.1.dist-info/WHEEL"
349            )
350            self.assertEqual(
351                wheel_contents.decode(),
352                f"""\
353Wheel-Version: 1.0
354Generator: bazel-wheelmaker 1.0
355Root-Is-Purelib: false
356Tag: cp38-abi3-{os_string}_{arch}
357""",
358            )
359
360    def test_rule_creates_directory_and_is_included_in_wheel(self):
361        filename = os.path.join(
362            os.environ["TEST_SRCDIR"],
363            "rules_python",
364            "examples",
365            "wheel",
366            "use_rule_with_dir_in_outs-0.0.1-py3-none-any.whl",
367        )
368
369        with zipfile.ZipFile(filename) as zf:
370            self.assertEqual(
371                zf.namelist(),
372                [
373                    "examples/wheel/main.py",
374                    "examples/wheel/someDir/foo.py",
375                    "use_rule_with_dir_in_outs-0.0.1.dist-info/WHEEL",
376                    "use_rule_with_dir_in_outs-0.0.1.dist-info/METADATA",
377                    "use_rule_with_dir_in_outs-0.0.1.dist-info/RECORD",
378                ],
379            )
380
381    def test_rule_expands_workspace_status_keys_in_wheel_metadata(self):
382        filename = os.path.join(
383            os.environ["TEST_SRCDIR"],
384            "rules_python",
385            "examples",
386            "wheel",
387            "example_minimal_library_BUILD_USER_-0.1._BUILD_TIMESTAMP_-py3-none-any.whl",
388        )
389
390        with zipfile.ZipFile(filename) as zf:
391            metadata_file = None
392            for f in zf.namelist():
393                self.assertNotIn("_BUILD_TIMESTAMP_", f)
394                self.assertNotIn("_BUILD_USER_", f)
395                if os.path.basename(f) == "METADATA":
396                    metadata_file = f
397            self.assertIsNotNone(metadata_file)
398
399            version = None
400            name = None
401            with zf.open(metadata_file) as fp:
402                for line in fp:
403                    if line.startswith(b"Version:"):
404                        version = line.decode().split()[-1]
405                    if line.startswith(b"Name:"):
406                        name = line.decode().split()[-1]
407            self.assertIsNotNone(version)
408            self.assertIsNotNone(name)
409            self.assertNotIn("{BUILD_TIMESTAMP}", version)
410            self.assertNotIn("{BUILD_USER}", name)
411
412
413if __name__ == "__main__":
414    unittest.main()
415