• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""Tests for distutils.command.install."""
2
3import os
4import sys
5import unittest
6import site
7
8from test.support import captured_stdout, run_unittest
9
10from distutils import sysconfig
11from distutils.command.install import install, HAS_USER_SITE
12from distutils.command import install as install_module
13from distutils.command.build_ext import build_ext
14from distutils.command.install import INSTALL_SCHEMES
15from distutils.core import Distribution
16from distutils.errors import DistutilsOptionError
17from distutils.extension import Extension
18
19from distutils.tests import support
20from test import support as test_support
21
22
23def _make_ext_name(modname):
24    return modname + sysconfig.get_config_var('EXT_SUFFIX')
25
26
27class InstallTestCase(support.TempdirManager,
28                      support.EnvironGuard,
29                      support.LoggingSilencer,
30                      unittest.TestCase):
31
32    def setUp(self):
33        super().setUp()
34        self._backup_config_vars = dict(sysconfig._config_vars)
35
36    def tearDown(self):
37        super().tearDown()
38        sysconfig._config_vars.clear()
39        sysconfig._config_vars.update(self._backup_config_vars)
40
41    def test_home_installation_scheme(self):
42        # This ensure two things:
43        # - that --home generates the desired set of directory names
44        # - test --home is supported on all platforms
45        builddir = self.mkdtemp()
46        destination = os.path.join(builddir, "installation")
47
48        dist = Distribution({"name": "foopkg"})
49        # script_name need not exist, it just need to be initialized
50        dist.script_name = os.path.join(builddir, "setup.py")
51        dist.command_obj["build"] = support.DummyCommand(
52            build_base=builddir,
53            build_lib=os.path.join(builddir, "lib"),
54            )
55
56        cmd = install(dist)
57        cmd.home = destination
58        cmd.ensure_finalized()
59
60        self.assertEqual(cmd.install_base, destination)
61        self.assertEqual(cmd.install_platbase, destination)
62
63        def check_path(got, expected):
64            got = os.path.normpath(got)
65            expected = os.path.normpath(expected)
66            self.assertEqual(got, expected)
67
68        libdir = os.path.join(destination, "lib", "python")
69        check_path(cmd.install_lib, libdir)
70        platlibdir = os.path.join(destination, sys.platlibdir, "python")
71        check_path(cmd.install_platlib, platlibdir)
72        check_path(cmd.install_purelib, libdir)
73        check_path(cmd.install_headers,
74                   os.path.join(destination, "include", "python", "foopkg"))
75        check_path(cmd.install_scripts, os.path.join(destination, "bin"))
76        check_path(cmd.install_data, destination)
77
78    @unittest.skipUnless(HAS_USER_SITE, 'need user site')
79    def test_user_site(self):
80        # test install with --user
81        # preparing the environment for the test
82        self.old_user_base = site.USER_BASE
83        self.old_user_site = site.USER_SITE
84        self.tmpdir = self.mkdtemp()
85        self.user_base = os.path.join(self.tmpdir, 'B')
86        self.user_site = os.path.join(self.tmpdir, 'S')
87        site.USER_BASE = self.user_base
88        site.USER_SITE = self.user_site
89        install_module.USER_BASE = self.user_base
90        install_module.USER_SITE = self.user_site
91
92        def _expanduser(path):
93            return self.tmpdir
94        self.old_expand = os.path.expanduser
95        os.path.expanduser = _expanduser
96
97        def cleanup():
98            site.USER_BASE = self.old_user_base
99            site.USER_SITE = self.old_user_site
100            install_module.USER_BASE = self.old_user_base
101            install_module.USER_SITE = self.old_user_site
102            os.path.expanduser = self.old_expand
103
104        self.addCleanup(cleanup)
105
106        if HAS_USER_SITE:
107            for key in ('nt_user', 'unix_user'):
108                self.assertIn(key, INSTALL_SCHEMES)
109
110        dist = Distribution({'name': 'xx'})
111        cmd = install(dist)
112
113        # making sure the user option is there
114        options = [name for name, short, lable in
115                   cmd.user_options]
116        self.assertIn('user', options)
117
118        # setting a value
119        cmd.user = 1
120
121        # user base and site shouldn't be created yet
122        self.assertFalse(os.path.exists(self.user_base))
123        self.assertFalse(os.path.exists(self.user_site))
124
125        # let's run finalize
126        cmd.ensure_finalized()
127
128        # now they should
129        self.assertTrue(os.path.exists(self.user_base))
130        self.assertTrue(os.path.exists(self.user_site))
131
132        self.assertIn('userbase', cmd.config_vars)
133        self.assertIn('usersite', cmd.config_vars)
134
135    def test_handle_extra_path(self):
136        dist = Distribution({'name': 'xx', 'extra_path': 'path,dirs'})
137        cmd = install(dist)
138
139        # two elements
140        cmd.handle_extra_path()
141        self.assertEqual(cmd.extra_path, ['path', 'dirs'])
142        self.assertEqual(cmd.extra_dirs, 'dirs')
143        self.assertEqual(cmd.path_file, 'path')
144
145        # one element
146        cmd.extra_path = ['path']
147        cmd.handle_extra_path()
148        self.assertEqual(cmd.extra_path, ['path'])
149        self.assertEqual(cmd.extra_dirs, 'path')
150        self.assertEqual(cmd.path_file, 'path')
151
152        # none
153        dist.extra_path = cmd.extra_path = None
154        cmd.handle_extra_path()
155        self.assertEqual(cmd.extra_path, None)
156        self.assertEqual(cmd.extra_dirs, '')
157        self.assertEqual(cmd.path_file, None)
158
159        # three elements (no way !)
160        cmd.extra_path = 'path,dirs,again'
161        self.assertRaises(DistutilsOptionError, cmd.handle_extra_path)
162
163    def test_finalize_options(self):
164        dist = Distribution({'name': 'xx'})
165        cmd = install(dist)
166
167        # must supply either prefix/exec-prefix/home or
168        # install-base/install-platbase -- not both
169        cmd.prefix = 'prefix'
170        cmd.install_base = 'base'
171        self.assertRaises(DistutilsOptionError, cmd.finalize_options)
172
173        # must supply either home or prefix/exec-prefix -- not both
174        cmd.install_base = None
175        cmd.home = 'home'
176        self.assertRaises(DistutilsOptionError, cmd.finalize_options)
177
178        # can't combine user with prefix/exec_prefix/home or
179        # install_(plat)base
180        cmd.prefix = None
181        cmd.user = 'user'
182        self.assertRaises(DistutilsOptionError, cmd.finalize_options)
183
184    def test_record(self):
185        install_dir = self.mkdtemp()
186        project_dir, dist = self.create_dist(py_modules=['hello'],
187                                             scripts=['sayhi'])
188        os.chdir(project_dir)
189        self.write_file('hello.py', "def main(): print('o hai')")
190        self.write_file('sayhi', 'from hello import main; main()')
191
192        cmd = install(dist)
193        dist.command_obj['install'] = cmd
194        cmd.root = install_dir
195        cmd.record = os.path.join(project_dir, 'filelist')
196        cmd.ensure_finalized()
197        cmd.run()
198
199        f = open(cmd.record)
200        try:
201            content = f.read()
202        finally:
203            f.close()
204
205        found = [os.path.basename(line) for line in content.splitlines()]
206        expected = ['hello.py', 'hello.%s.pyc' % sys.implementation.cache_tag,
207                    'sayhi',
208                    'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]]
209        self.assertEqual(found, expected)
210
211    def test_record_extensions(self):
212        cmd = test_support.missing_compiler_executable()
213        if cmd is not None:
214            self.skipTest('The %r command is not found' % cmd)
215        install_dir = self.mkdtemp()
216        project_dir, dist = self.create_dist(ext_modules=[
217            Extension('xx', ['xxmodule.c'])])
218        os.chdir(project_dir)
219        support.copy_xxmodule_c(project_dir)
220
221        buildextcmd = build_ext(dist)
222        support.fixup_build_ext(buildextcmd)
223        buildextcmd.ensure_finalized()
224
225        cmd = install(dist)
226        dist.command_obj['install'] = cmd
227        dist.command_obj['build_ext'] = buildextcmd
228        cmd.root = install_dir
229        cmd.record = os.path.join(project_dir, 'filelist')
230        cmd.ensure_finalized()
231        cmd.run()
232
233        f = open(cmd.record)
234        try:
235            content = f.read()
236        finally:
237            f.close()
238
239        found = [os.path.basename(line) for line in content.splitlines()]
240        expected = [_make_ext_name('xx'),
241                    'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2]]
242        self.assertEqual(found, expected)
243
244    def test_debug_mode(self):
245        # this covers the code called when DEBUG is set
246        old_logs_len = len(self.logs)
247        install_module.DEBUG = True
248        try:
249            with captured_stdout():
250                self.test_record()
251        finally:
252            install_module.DEBUG = False
253        self.assertGreater(len(self.logs), old_logs_len)
254
255
256def test_suite():
257    return unittest.makeSuite(InstallTestCase)
258
259if __name__ == "__main__":
260    run_unittest(test_suite())
261