1"""Tests for distutils.command.build_py."""
2
3import os
4import sys
5import unittest
6
7from distutils.command.build_py import build_py
8from distutils.core import Distribution
9from distutils.errors import DistutilsFileError
10
11from distutils.tests import support
12from test.support import run_unittest, requires_subprocess
13
14
15class BuildPyTestCase(support.TempdirManager,
16                      support.LoggingSilencer,
17                      unittest.TestCase):
18
19    def test_package_data(self):
20        sources = self.mkdtemp()
21        f = open(os.path.join(sources, "__init__.py"), "w")
22        try:
23            f.write("# Pretend this is a package.")
24        finally:
25            f.close()
26        f = open(os.path.join(sources, "README.txt"), "w")
27        try:
28            f.write("Info about this package")
29        finally:
30            f.close()
31
32        destination = self.mkdtemp()
33
34        dist = Distribution({"packages": ["pkg"],
35                             "package_dir": {"pkg": sources}})
36        # script_name need not exist, it just need to be initialized
37        dist.script_name = os.path.join(sources, "setup.py")
38        dist.command_obj["build"] = support.DummyCommand(
39            force=0,
40            build_lib=destination)
41        dist.packages = ["pkg"]
42        dist.package_data = {"pkg": ["README.txt"]}
43        dist.package_dir = {"pkg": sources}
44
45        cmd = build_py(dist)
46        cmd.compile = 1
47        cmd.ensure_finalized()
48        self.assertEqual(cmd.package_data, dist.package_data)
49
50        cmd.run()
51
52        # This makes sure the list of outputs includes byte-compiled
53        # files for Python modules but not for package data files
54        # (there shouldn't *be* byte-code files for those!).
55        self.assertEqual(len(cmd.get_outputs()), 3)
56        pkgdest = os.path.join(destination, "pkg")
57        files = os.listdir(pkgdest)
58        pycache_dir = os.path.join(pkgdest, "__pycache__")
59        self.assertIn("__init__.py", files)
60        self.assertIn("README.txt", files)
61        if sys.dont_write_bytecode:
62            self.assertFalse(os.path.exists(pycache_dir))
63        else:
64            pyc_files = os.listdir(pycache_dir)
65            self.assertIn("__init__.%s.pyc" % sys.implementation.cache_tag,
66                          pyc_files)
67
68    def test_empty_package_dir(self):
69        # See bugs #1668596/#1720897
70        sources = self.mkdtemp()
71        open(os.path.join(sources, "__init__.py"), "w").close()
72
73        testdir = os.path.join(sources, "doc")
74        os.mkdir(testdir)
75        open(os.path.join(testdir, "testfile"), "w").close()
76
77        os.chdir(sources)
78        dist = Distribution({"packages": ["pkg"],
79                             "package_dir": {"pkg": ""},
80                             "package_data": {"pkg": ["doc/*"]}})
81        # script_name need not exist, it just need to be initialized
82        dist.script_name = os.path.join(sources, "setup.py")
83        dist.script_args = ["build"]
84        dist.parse_command_line()
85
86        try:
87            dist.run_commands()
88        except DistutilsFileError:
89            self.fail("failed package_data test when package_dir is ''")
90
91    @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled')
92    @requires_subprocess()
93    def test_byte_compile(self):
94        project_dir, dist = self.create_dist(py_modules=['boiledeggs'])
95        os.chdir(project_dir)
96        self.write_file('boiledeggs.py', 'import antigravity')
97        cmd = build_py(dist)
98        cmd.compile = 1
99        cmd.build_lib = 'here'
100        cmd.finalize_options()
101        cmd.run()
102
103        found = os.listdir(cmd.build_lib)
104        self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py'])
105        found = os.listdir(os.path.join(cmd.build_lib, '__pycache__'))
106        self.assertEqual(found,
107                         ['boiledeggs.%s.pyc' % sys.implementation.cache_tag])
108
109    @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled')
110    @requires_subprocess()
111    def test_byte_compile_optimized(self):
112        project_dir, dist = self.create_dist(py_modules=['boiledeggs'])
113        os.chdir(project_dir)
114        self.write_file('boiledeggs.py', 'import antigravity')
115        cmd = build_py(dist)
116        cmd.compile = 0
117        cmd.optimize = 1
118        cmd.build_lib = 'here'
119        cmd.finalize_options()
120        cmd.run()
121
122        found = os.listdir(cmd.build_lib)
123        self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py'])
124        found = os.listdir(os.path.join(cmd.build_lib, '__pycache__'))
125        expect = 'boiledeggs.{}.opt-1.pyc'.format(sys.implementation.cache_tag)
126        self.assertEqual(sorted(found), [expect])
127
128    def test_dir_in_package_data(self):
129        """
130        A directory in package_data should not be added to the filelist.
131        """
132        # See bug 19286
133        sources = self.mkdtemp()
134        pkg_dir = os.path.join(sources, "pkg")
135
136        os.mkdir(pkg_dir)
137        open(os.path.join(pkg_dir, "__init__.py"), "w").close()
138
139        docdir = os.path.join(pkg_dir, "doc")
140        os.mkdir(docdir)
141        open(os.path.join(docdir, "testfile"), "w").close()
142
143        # create the directory that could be incorrectly detected as a file
144        os.mkdir(os.path.join(docdir, 'otherdir'))
145
146        os.chdir(sources)
147        dist = Distribution({"packages": ["pkg"],
148                             "package_data": {"pkg": ["doc/*"]}})
149        # script_name need not exist, it just need to be initialized
150        dist.script_name = os.path.join(sources, "setup.py")
151        dist.script_args = ["build"]
152        dist.parse_command_line()
153
154        try:
155            dist.run_commands()
156        except DistutilsFileError:
157            self.fail("failed package_data when data dir includes a dir")
158
159    def test_dont_write_bytecode(self):
160        # makes sure byte_compile is not used
161        dist = self.create_dist()[1]
162        cmd = build_py(dist)
163        cmd.compile = 1
164        cmd.optimize = 1
165
166        old_dont_write_bytecode = sys.dont_write_bytecode
167        sys.dont_write_bytecode = True
168        try:
169            cmd.byte_compile([])
170        finally:
171            sys.dont_write_bytecode = old_dont_write_bytecode
172
173        self.assertIn('byte-compiling is disabled',
174                      self.logs[0][1] % self.logs[0][2])
175
176
177def test_suite():
178    return unittest.TestLoader().loadTestsFromTestCase(BuildPyTestCase)
179
180if __name__ == "__main__":
181    run_unittest(test_suite())
182