test_site.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  1. """Tests for 'site'.
  2. Tests assume the initial paths in sys.path once the interpreter has begun
  3. executing have not been removed.
  4. """
  5. import unittest
  6. import test.support
  7. from test import support
  8. from test.support import os_helper
  9. from test.support import socket_helper
  10. from test.support import captured_stderr
  11. from test.support.os_helper import TESTFN, EnvironmentVarGuard, change_cwd
  12. import ast
  13. import builtins
  14. import encodings
  15. import glob
  16. import io
  17. import os
  18. import re
  19. import shutil
  20. import subprocess
  21. import sys
  22. import sysconfig
  23. import tempfile
  24. import urllib.error
  25. import urllib.request
  26. from unittest import mock
  27. from copy import copy
  28. # These tests are not particularly useful if Python was invoked with -S.
  29. # If you add tests that are useful under -S, this skip should be moved
  30. # to the class level.
  31. if sys.flags.no_site:
  32. raise unittest.SkipTest("Python was invoked with -S")
  33. import site
  34. HAS_USER_SITE = (site.USER_SITE is not None)
  35. OLD_SYS_PATH = None
  36. def setUpModule():
  37. global OLD_SYS_PATH
  38. OLD_SYS_PATH = sys.path[:]
  39. if site.ENABLE_USER_SITE and not os.path.isdir(site.USER_SITE):
  40. # need to add user site directory for tests
  41. try:
  42. os.makedirs(site.USER_SITE)
  43. # modify sys.path: will be restored by tearDownModule()
  44. site.addsitedir(site.USER_SITE)
  45. except PermissionError as exc:
  46. raise unittest.SkipTest('unable to create user site directory (%r): %s'
  47. % (site.USER_SITE, exc))
  48. def tearDownModule():
  49. sys.path[:] = OLD_SYS_PATH
  50. class HelperFunctionsTests(unittest.TestCase):
  51. """Tests for helper functions.
  52. """
  53. def setUp(self):
  54. """Save a copy of sys.path"""
  55. self.sys_path = sys.path[:]
  56. self.old_base = site.USER_BASE
  57. self.old_site = site.USER_SITE
  58. self.old_prefixes = site.PREFIXES
  59. self.original_vars = sysconfig._CONFIG_VARS
  60. self.old_vars = copy(sysconfig._CONFIG_VARS)
  61. def tearDown(self):
  62. """Restore sys.path"""
  63. sys.path[:] = self.sys_path
  64. site.USER_BASE = self.old_base
  65. site.USER_SITE = self.old_site
  66. site.PREFIXES = self.old_prefixes
  67. sysconfig._CONFIG_VARS = self.original_vars
  68. # _CONFIG_VARS is None before get_config_vars() is called
  69. if sysconfig._CONFIG_VARS is not None:
  70. sysconfig._CONFIG_VARS.clear()
  71. sysconfig._CONFIG_VARS.update(self.old_vars)
  72. def test_makepath(self):
  73. # Test makepath() have an absolute path for its first return value
  74. # and a case-normalized version of the absolute path for its
  75. # second value.
  76. path_parts = ("Beginning", "End")
  77. original_dir = os.path.join(*path_parts)
  78. abs_dir, norm_dir = site.makepath(*path_parts)
  79. self.assertEqual(os.path.abspath(original_dir), abs_dir)
  80. if original_dir == os.path.normcase(original_dir):
  81. self.assertEqual(abs_dir, norm_dir)
  82. else:
  83. self.assertEqual(os.path.normcase(abs_dir), norm_dir)
  84. def test_init_pathinfo(self):
  85. dir_set = site._init_pathinfo()
  86. for entry in [site.makepath(path)[1] for path in sys.path
  87. if path and os.path.exists(path)]:
  88. self.assertIn(entry, dir_set,
  89. "%s from sys.path not found in set returned "
  90. "by _init_pathinfo(): %s" % (entry, dir_set))
  91. def pth_file_tests(self, pth_file):
  92. """Contain common code for testing results of reading a .pth file"""
  93. self.assertIn(pth_file.imported, sys.modules,
  94. "%s not in sys.modules" % pth_file.imported)
  95. self.assertIn(site.makepath(pth_file.good_dir_path)[0], sys.path)
  96. self.assertFalse(os.path.exists(pth_file.bad_dir_path))
  97. def test_addpackage(self):
  98. # Make sure addpackage() imports if the line starts with 'import',
  99. # adds directories to sys.path for any line in the file that is not a
  100. # comment or import that is a valid directory name for where the .pth
  101. # file resides; invalid directories are not added
  102. pth_file = PthFile()
  103. pth_file.cleanup(prep=True) # to make sure that nothing is
  104. # pre-existing that shouldn't be
  105. try:
  106. pth_file.create()
  107. site.addpackage(pth_file.base_dir, pth_file.filename, set())
  108. self.pth_file_tests(pth_file)
  109. finally:
  110. pth_file.cleanup()
  111. def make_pth(self, contents, pth_dir='.', pth_name=TESTFN):
  112. # Create a .pth file and return its (abspath, basename).
  113. pth_dir = os.path.abspath(pth_dir)
  114. pth_basename = pth_name + '.pth'
  115. pth_fn = os.path.join(pth_dir, pth_basename)
  116. with open(pth_fn, 'w', encoding='utf-8') as pth_file:
  117. self.addCleanup(lambda: os.remove(pth_fn))
  118. pth_file.write(contents)
  119. return pth_dir, pth_basename
  120. def test_addpackage_import_bad_syntax(self):
  121. # Issue 10642
  122. pth_dir, pth_fn = self.make_pth("import bad-syntax\n")
  123. with captured_stderr() as err_out:
  124. site.addpackage(pth_dir, pth_fn, set())
  125. self.assertRegex(err_out.getvalue(), "line 1")
  126. self.assertRegex(err_out.getvalue(),
  127. re.escape(os.path.join(pth_dir, pth_fn)))
  128. # XXX: the previous two should be independent checks so that the
  129. # order doesn't matter. The next three could be a single check
  130. # but my regex foo isn't good enough to write it.
  131. self.assertRegex(err_out.getvalue(), 'Traceback')
  132. self.assertRegex(err_out.getvalue(), r'import bad-syntax')
  133. self.assertRegex(err_out.getvalue(), 'SyntaxError')
  134. def test_addpackage_import_bad_exec(self):
  135. # Issue 10642
  136. pth_dir, pth_fn = self.make_pth("randompath\nimport nosuchmodule\n")
  137. with captured_stderr() as err_out:
  138. site.addpackage(pth_dir, pth_fn, set())
  139. self.assertRegex(err_out.getvalue(), "line 2")
  140. self.assertRegex(err_out.getvalue(),
  141. re.escape(os.path.join(pth_dir, pth_fn)))
  142. # XXX: ditto previous XXX comment.
  143. self.assertRegex(err_out.getvalue(), 'Traceback')
  144. self.assertRegex(err_out.getvalue(), 'ModuleNotFoundError')
  145. def test_addpackage_empty_lines(self):
  146. # Issue 33689
  147. pth_dir, pth_fn = self.make_pth("\n\n \n\n")
  148. known_paths = site.addpackage(pth_dir, pth_fn, set())
  149. self.assertEqual(known_paths, set())
  150. def test_addpackage_import_bad_pth_file(self):
  151. # Issue 5258
  152. pth_dir, pth_fn = self.make_pth("abc\x00def\n")
  153. with captured_stderr() as err_out:
  154. self.assertFalse(site.addpackage(pth_dir, pth_fn, set()))
  155. self.maxDiff = None
  156. self.assertEqual(err_out.getvalue(), "")
  157. for path in sys.path:
  158. if isinstance(path, str):
  159. self.assertNotIn("abc\x00def", path)
  160. def test_addsitedir(self):
  161. # Same tests for test_addpackage since addsitedir() essentially just
  162. # calls addpackage() for every .pth file in the directory
  163. pth_file = PthFile()
  164. pth_file.cleanup(prep=True) # Make sure that nothing is pre-existing
  165. # that is tested for
  166. try:
  167. pth_file.create()
  168. site.addsitedir(pth_file.base_dir, set())
  169. self.pth_file_tests(pth_file)
  170. finally:
  171. pth_file.cleanup()
  172. # This tests _getuserbase, hence the double underline
  173. # to distinguish from a test for getuserbase
  174. def test__getuserbase(self):
  175. self.assertEqual(site._getuserbase(), sysconfig._getuserbase())
  176. @unittest.skipUnless(HAS_USER_SITE, 'need user site')
  177. def test_get_path(self):
  178. if sys.platform == 'darwin' and sys._framework:
  179. scheme = 'osx_framework_user'
  180. else:
  181. scheme = os.name + '_user'
  182. self.assertEqual(os.path.normpath(site._get_path(site._getuserbase())),
  183. sysconfig.get_path('purelib', scheme))
  184. @unittest.skipUnless(site.ENABLE_USER_SITE, "requires access to PEP 370 "
  185. "user-site (site.ENABLE_USER_SITE)")
  186. @support.requires_subprocess()
  187. def test_s_option(self):
  188. # (ncoghlan) Change this to use script_helper...
  189. usersite = os.path.normpath(site.USER_SITE)
  190. self.assertIn(usersite, sys.path)
  191. env = os.environ.copy()
  192. rc = subprocess.call([sys.executable, '-c',
  193. 'import sys; sys.exit(%r in sys.path)' % usersite],
  194. env=env)
  195. self.assertEqual(rc, 1)
  196. env = os.environ.copy()
  197. rc = subprocess.call([sys.executable, '-s', '-c',
  198. 'import sys; sys.exit(%r in sys.path)' % usersite],
  199. env=env)
  200. if usersite == site.getsitepackages()[0]:
  201. self.assertEqual(rc, 1)
  202. else:
  203. self.assertEqual(rc, 0, "User site still added to path with -s")
  204. env = os.environ.copy()
  205. env["PYTHONNOUSERSITE"] = "1"
  206. rc = subprocess.call([sys.executable, '-c',
  207. 'import sys; sys.exit(%r in sys.path)' % usersite],
  208. env=env)
  209. if usersite == site.getsitepackages()[0]:
  210. self.assertEqual(rc, 1)
  211. else:
  212. self.assertEqual(rc, 0,
  213. "User site still added to path with PYTHONNOUSERSITE")
  214. env = os.environ.copy()
  215. env["PYTHONUSERBASE"] = "/tmp"
  216. rc = subprocess.call([sys.executable, '-c',
  217. 'import sys, site; sys.exit(site.USER_BASE.startswith("/tmp"))'],
  218. env=env)
  219. self.assertEqual(rc, 1,
  220. "User base not set by PYTHONUSERBASE")
  221. @unittest.skipUnless(HAS_USER_SITE, 'need user site')
  222. def test_getuserbase(self):
  223. site.USER_BASE = None
  224. user_base = site.getuserbase()
  225. # the call sets site.USER_BASE
  226. self.assertEqual(site.USER_BASE, user_base)
  227. # let's set PYTHONUSERBASE and see if it uses it
  228. site.USER_BASE = None
  229. import sysconfig
  230. sysconfig._CONFIG_VARS = None
  231. with EnvironmentVarGuard() as environ:
  232. environ['PYTHONUSERBASE'] = 'xoxo'
  233. self.assertTrue(site.getuserbase().startswith('xoxo'),
  234. site.getuserbase())
  235. @unittest.skipUnless(HAS_USER_SITE, 'need user site')
  236. def test_getusersitepackages(self):
  237. site.USER_SITE = None
  238. site.USER_BASE = None
  239. user_site = site.getusersitepackages()
  240. # the call sets USER_BASE *and* USER_SITE
  241. self.assertEqual(site.USER_SITE, user_site)
  242. self.assertTrue(user_site.startswith(site.USER_BASE), user_site)
  243. self.assertEqual(site.USER_BASE, site.getuserbase())
  244. def test_getsitepackages(self):
  245. site.PREFIXES = ['xoxo']
  246. dirs = site.getsitepackages()
  247. if os.sep == '/':
  248. # OS X, Linux, FreeBSD, etc
  249. if sys.platlibdir != "lib":
  250. self.assertEqual(len(dirs), 2)
  251. wanted = os.path.join('xoxo', sys.platlibdir,
  252. 'python%d.%d' % sys.version_info[:2],
  253. 'site-packages')
  254. self.assertEqual(dirs[0], wanted)
  255. else:
  256. self.assertEqual(len(dirs), 1)
  257. wanted = os.path.join('xoxo', 'lib',
  258. 'python%d.%d' % sys.version_info[:2],
  259. 'site-packages')
  260. self.assertEqual(dirs[-1], wanted)
  261. else:
  262. # other platforms
  263. self.assertEqual(len(dirs), 2)
  264. self.assertEqual(dirs[0], 'xoxo')
  265. wanted = os.path.join('xoxo', 'lib', 'site-packages')
  266. self.assertEqual(os.path.normcase(dirs[1]),
  267. os.path.normcase(wanted))
  268. @unittest.skipUnless(HAS_USER_SITE, 'need user site')
  269. def test_no_home_directory(self):
  270. # bpo-10496: getuserbase() and getusersitepackages() must not fail if
  271. # the current user has no home directory (if expanduser() returns the
  272. # path unchanged).
  273. site.USER_SITE = None
  274. site.USER_BASE = None
  275. with EnvironmentVarGuard() as environ, \
  276. mock.patch('os.path.expanduser', lambda path: path):
  277. del environ['PYTHONUSERBASE']
  278. del environ['APPDATA']
  279. user_base = site.getuserbase()
  280. self.assertTrue(user_base.startswith('~' + os.sep),
  281. user_base)
  282. user_site = site.getusersitepackages()
  283. self.assertTrue(user_site.startswith(user_base), user_site)
  284. with mock.patch('os.path.isdir', return_value=False) as mock_isdir, \
  285. mock.patch.object(site, 'addsitedir') as mock_addsitedir, \
  286. support.swap_attr(site, 'ENABLE_USER_SITE', True):
  287. # addusersitepackages() must not add user_site to sys.path
  288. # if it is not an existing directory
  289. known_paths = set()
  290. site.addusersitepackages(known_paths)
  291. mock_isdir.assert_called_once_with(user_site)
  292. mock_addsitedir.assert_not_called()
  293. self.assertFalse(known_paths)
  294. def test_trace(self):
  295. message = "bla-bla-bla"
  296. for verbose, out in (True, message + "\n"), (False, ""):
  297. with mock.patch('sys.flags', mock.Mock(verbose=verbose)), \
  298. mock.patch('sys.stderr', io.StringIO()):
  299. site._trace(message)
  300. self.assertEqual(sys.stderr.getvalue(), out)
  301. class PthFile(object):
  302. """Helper class for handling testing of .pth files"""
  303. def __init__(self, filename_base=TESTFN, imported="time",
  304. good_dirname="__testdir__", bad_dirname="__bad"):
  305. """Initialize instance variables"""
  306. self.filename = filename_base + ".pth"
  307. self.base_dir = os.path.abspath('')
  308. self.file_path = os.path.join(self.base_dir, self.filename)
  309. self.imported = imported
  310. self.good_dirname = good_dirname
  311. self.bad_dirname = bad_dirname
  312. self.good_dir_path = os.path.join(self.base_dir, self.good_dirname)
  313. self.bad_dir_path = os.path.join(self.base_dir, self.bad_dirname)
  314. def create(self):
  315. """Create a .pth file with a comment, blank lines, an ``import
  316. <self.imported>``, a line with self.good_dirname, and a line with
  317. self.bad_dirname.
  318. Creation of the directory for self.good_dir_path (based off of
  319. self.good_dirname) is also performed.
  320. Make sure to call self.cleanup() to undo anything done by this method.
  321. """
  322. FILE = open(self.file_path, 'w')
  323. try:
  324. print("#import @bad module name", file=FILE)
  325. print("\n", file=FILE)
  326. print("import %s" % self.imported, file=FILE)
  327. print(self.good_dirname, file=FILE)
  328. print(self.bad_dirname, file=FILE)
  329. finally:
  330. FILE.close()
  331. os.mkdir(self.good_dir_path)
  332. def cleanup(self, prep=False):
  333. """Make sure that the .pth file is deleted, self.imported is not in
  334. sys.modules, and that both self.good_dirname and self.bad_dirname are
  335. not existing directories."""
  336. if os.path.exists(self.file_path):
  337. os.remove(self.file_path)
  338. if prep:
  339. self.imported_module = sys.modules.get(self.imported)
  340. if self.imported_module:
  341. del sys.modules[self.imported]
  342. else:
  343. if self.imported_module:
  344. sys.modules[self.imported] = self.imported_module
  345. if os.path.exists(self.good_dir_path):
  346. os.rmdir(self.good_dir_path)
  347. if os.path.exists(self.bad_dir_path):
  348. os.rmdir(self.bad_dir_path)
  349. class ImportSideEffectTests(unittest.TestCase):
  350. """Test side-effects from importing 'site'."""
  351. def setUp(self):
  352. """Make a copy of sys.path"""
  353. self.sys_path = sys.path[:]
  354. def tearDown(self):
  355. """Restore sys.path"""
  356. sys.path[:] = self.sys_path
  357. def test_abs_paths_cached_None(self):
  358. """Test for __cached__ is None.
  359. Regarding to PEP 3147, __cached__ can be None.
  360. See also: https://bugs.python.org/issue30167
  361. """
  362. sys.modules['test'].__cached__ = None
  363. site.abs_paths()
  364. self.assertIsNone(sys.modules['test'].__cached__)
  365. def test_no_duplicate_paths(self):
  366. # No duplicate paths should exist in sys.path
  367. # Handled by removeduppaths()
  368. site.removeduppaths()
  369. seen_paths = set()
  370. for path in sys.path:
  371. self.assertNotIn(path, seen_paths)
  372. seen_paths.add(path)
  373. @unittest.skip('test not implemented')
  374. def test_add_build_dir(self):
  375. # Test that the build directory's Modules directory is used when it
  376. # should be.
  377. # XXX: implement
  378. pass
  379. def test_setting_quit(self):
  380. # 'quit' and 'exit' should be injected into builtins
  381. self.assertTrue(hasattr(builtins, "quit"))
  382. self.assertTrue(hasattr(builtins, "exit"))
  383. def test_setting_copyright(self):
  384. # 'copyright', 'credits', and 'license' should be in builtins
  385. self.assertTrue(hasattr(builtins, "copyright"))
  386. self.assertTrue(hasattr(builtins, "credits"))
  387. self.assertTrue(hasattr(builtins, "license"))
  388. def test_setting_help(self):
  389. # 'help' should be set in builtins
  390. self.assertTrue(hasattr(builtins, "help"))
  391. def test_sitecustomize_executed(self):
  392. # If sitecustomize is available, it should have been imported.
  393. if "sitecustomize" not in sys.modules:
  394. try:
  395. import sitecustomize
  396. except ImportError:
  397. pass
  398. else:
  399. self.fail("sitecustomize not imported automatically")
  400. @test.support.requires_resource('network')
  401. @test.support.system_must_validate_cert
  402. @unittest.skipUnless(hasattr(urllib.request, "HTTPSHandler"),
  403. 'need SSL support to download license')
  404. def test_license_exists_at_url(self):
  405. # This test is a bit fragile since it depends on the format of the
  406. # string displayed by license in the absence of a LICENSE file.
  407. url = license._Printer__data.split()[1]
  408. req = urllib.request.Request(url, method='HEAD')
  409. # Reset global urllib.request._opener
  410. self.addCleanup(urllib.request.urlcleanup)
  411. try:
  412. with socket_helper.transient_internet(url):
  413. with urllib.request.urlopen(req) as data:
  414. code = data.getcode()
  415. except urllib.error.HTTPError as e:
  416. code = e.code
  417. self.assertEqual(code, 200, msg="Can't find " + url)
  418. class StartupImportTests(unittest.TestCase):
  419. @support.requires_subprocess()
  420. def test_startup_imports(self):
  421. # Get sys.path in isolated mode (python3 -I)
  422. popen = subprocess.Popen([sys.executable, '-X', 'utf8', '-I',
  423. '-c', 'import sys; print(repr(sys.path))'],
  424. stdout=subprocess.PIPE,
  425. encoding='utf-8',
  426. errors='surrogateescape')
  427. stdout = popen.communicate()[0]
  428. self.assertEqual(popen.returncode, 0, repr(stdout))
  429. isolated_paths = ast.literal_eval(stdout)
  430. # bpo-27807: Even with -I, the site module executes all .pth files
  431. # found in sys.path (see site.addpackage()). Skip the test if at least
  432. # one .pth file is found.
  433. for path in isolated_paths:
  434. pth_files = glob.glob(os.path.join(glob.escape(path), "*.pth"))
  435. if pth_files:
  436. self.skipTest(f"found {len(pth_files)} .pth files in: {path}")
  437. # This tests checks which modules are loaded by Python when it
  438. # initially starts upon startup.
  439. popen = subprocess.Popen([sys.executable, '-X', 'utf8', '-I', '-v',
  440. '-c', 'import sys; print(set(sys.modules))'],
  441. stdout=subprocess.PIPE,
  442. stderr=subprocess.PIPE,
  443. encoding='utf-8',
  444. errors='surrogateescape')
  445. stdout, stderr = popen.communicate()
  446. self.assertEqual(popen.returncode, 0, (stdout, stderr))
  447. modules = ast.literal_eval(stdout)
  448. self.assertIn('site', modules)
  449. # http://bugs.python.org/issue19205
  450. re_mods = {'re', '_sre', 're._compiler', 're._constants', 're._parser'}
  451. self.assertFalse(modules.intersection(re_mods), stderr)
  452. # http://bugs.python.org/issue9548
  453. self.assertNotIn('locale', modules, stderr)
  454. # http://bugs.python.org/issue19209
  455. self.assertNotIn('copyreg', modules, stderr)
  456. # http://bugs.python.org/issue19218
  457. collection_mods = {'_collections', 'collections', 'functools',
  458. 'heapq', 'itertools', 'keyword', 'operator',
  459. 'reprlib', 'types', 'weakref'
  460. }.difference(sys.builtin_module_names)
  461. self.assertFalse(modules.intersection(collection_mods), stderr)
  462. @support.requires_subprocess()
  463. def test_startup_interactivehook(self):
  464. r = subprocess.Popen([sys.executable, '-c',
  465. 'import sys; sys.exit(hasattr(sys, "__interactivehook__"))']).wait()
  466. self.assertTrue(r, "'__interactivehook__' not added by site")
  467. @support.requires_subprocess()
  468. def test_startup_interactivehook_isolated(self):
  469. # issue28192 readline is not automatically enabled in isolated mode
  470. r = subprocess.Popen([sys.executable, '-I', '-c',
  471. 'import sys; sys.exit(hasattr(sys, "__interactivehook__"))']).wait()
  472. self.assertFalse(r, "'__interactivehook__' added in isolated mode")
  473. @support.requires_subprocess()
  474. def test_startup_interactivehook_isolated_explicit(self):
  475. # issue28192 readline can be explicitly enabled in isolated mode
  476. r = subprocess.Popen([sys.executable, '-I', '-c',
  477. 'import site, sys; site.enablerlcompleter(); sys.exit(hasattr(sys, "__interactivehook__"))']).wait()
  478. self.assertTrue(r, "'__interactivehook__' not added by enablerlcompleter()")
  479. class _pthFileTests(unittest.TestCase):
  480. if sys.platform == 'win32':
  481. def _create_underpth_exe(self, lines, exe_pth=True):
  482. import _winapi
  483. temp_dir = tempfile.mkdtemp()
  484. self.addCleanup(os_helper.rmtree, temp_dir)
  485. exe_file = os.path.join(temp_dir, os.path.split(sys.executable)[1])
  486. dll_src_file = _winapi.GetModuleFileName(sys.dllhandle)
  487. dll_file = os.path.join(temp_dir, os.path.split(dll_src_file)[1])
  488. shutil.copy(sys.executable, exe_file)
  489. shutil.copy(dll_src_file, dll_file)
  490. for fn in glob.glob(os.path.join(os.path.split(dll_src_file)[0], "vcruntime*.dll")):
  491. shutil.copy(fn, os.path.join(temp_dir, os.path.split(fn)[1]))
  492. if exe_pth:
  493. _pth_file = os.path.splitext(exe_file)[0] + '._pth'
  494. else:
  495. _pth_file = os.path.splitext(dll_file)[0] + '._pth'
  496. with open(_pth_file, 'w') as f:
  497. for line in lines:
  498. print(line, file=f)
  499. return exe_file
  500. else:
  501. def _create_underpth_exe(self, lines, exe_pth=True):
  502. if not exe_pth:
  503. raise unittest.SkipTest("library ._pth file not supported on this platform")
  504. temp_dir = tempfile.mkdtemp()
  505. self.addCleanup(os_helper.rmtree, temp_dir)
  506. exe_file = os.path.join(temp_dir, os.path.split(sys.executable)[1])
  507. os.symlink(sys.executable, exe_file)
  508. _pth_file = exe_file + '._pth'
  509. with open(_pth_file, 'w') as f:
  510. for line in lines:
  511. print(line, file=f)
  512. return exe_file
  513. def _calc_sys_path_for_underpth_nosite(self, sys_prefix, lines):
  514. sys_path = []
  515. for line in lines:
  516. if not line or line[0] == '#':
  517. continue
  518. abs_path = os.path.abspath(os.path.join(sys_prefix, line))
  519. sys_path.append(abs_path)
  520. return sys_path
  521. @support.requires_subprocess()
  522. def test_underpth_basic(self):
  523. libpath = test.support.STDLIB_DIR
  524. exe_prefix = os.path.dirname(sys.executable)
  525. pth_lines = ['#.', '# ..', *sys.path, '.', '..']
  526. exe_file = self._create_underpth_exe(pth_lines)
  527. sys_path = self._calc_sys_path_for_underpth_nosite(
  528. os.path.dirname(exe_file),
  529. pth_lines)
  530. output = subprocess.check_output([exe_file, '-c',
  531. 'import sys; print("\\n".join(sys.path) if sys.flags.no_site else "")'
  532. ], encoding='utf-8', errors='surrogateescape')
  533. actual_sys_path = output.rstrip().split('\n')
  534. self.assertTrue(actual_sys_path, "sys.flags.no_site was False")
  535. self.assertEqual(
  536. actual_sys_path,
  537. sys_path,
  538. "sys.path is incorrect"
  539. )
  540. @support.requires_subprocess()
  541. def test_underpth_nosite_file(self):
  542. libpath = test.support.STDLIB_DIR
  543. exe_prefix = os.path.dirname(sys.executable)
  544. pth_lines = [
  545. 'fake-path-name',
  546. *[libpath for _ in range(200)],
  547. '',
  548. '# comment',
  549. ]
  550. exe_file = self._create_underpth_exe(pth_lines)
  551. sys_path = self._calc_sys_path_for_underpth_nosite(
  552. os.path.dirname(exe_file),
  553. pth_lines)
  554. env = os.environ.copy()
  555. env['PYTHONPATH'] = 'from-env'
  556. env['PATH'] = '{}{}{}'.format(exe_prefix, os.pathsep, os.getenv('PATH'))
  557. output = subprocess.check_output([exe_file, '-c',
  558. 'import sys; print("\\n".join(sys.path) if sys.flags.no_site else "")'
  559. ], env=env, encoding='utf-8', errors='surrogateescape')
  560. actual_sys_path = output.rstrip().split('\n')
  561. self.assertTrue(actual_sys_path, "sys.flags.no_site was False")
  562. self.assertEqual(
  563. actual_sys_path,
  564. sys_path,
  565. "sys.path is incorrect"
  566. )
  567. @support.requires_subprocess()
  568. def test_underpth_file(self):
  569. libpath = test.support.STDLIB_DIR
  570. exe_prefix = os.path.dirname(sys.executable)
  571. exe_file = self._create_underpth_exe([
  572. 'fake-path-name',
  573. *[libpath for _ in range(200)],
  574. '',
  575. '# comment',
  576. 'import site'
  577. ])
  578. sys_prefix = os.path.dirname(exe_file)
  579. env = os.environ.copy()
  580. env['PYTHONPATH'] = 'from-env'
  581. env['PATH'] = '{};{}'.format(exe_prefix, os.getenv('PATH'))
  582. rc = subprocess.call([exe_file, '-c',
  583. 'import sys; sys.exit(not sys.flags.no_site and '
  584. '%r in sys.path and %r in sys.path and %r not in sys.path and '
  585. 'all("\\r" not in p and "\\n" not in p for p in sys.path))' % (
  586. os.path.join(sys_prefix, 'fake-path-name'),
  587. libpath,
  588. os.path.join(sys_prefix, 'from-env'),
  589. )], env=env)
  590. self.assertTrue(rc, "sys.path is incorrect")
  591. @support.requires_subprocess()
  592. def test_underpth_dll_file(self):
  593. libpath = test.support.STDLIB_DIR
  594. exe_prefix = os.path.dirname(sys.executable)
  595. exe_file = self._create_underpth_exe([
  596. 'fake-path-name',
  597. *[libpath for _ in range(200)],
  598. '',
  599. '# comment',
  600. 'import site'
  601. ], exe_pth=False)
  602. sys_prefix = os.path.dirname(exe_file)
  603. env = os.environ.copy()
  604. env['PYTHONPATH'] = 'from-env'
  605. env['PATH'] = '{};{}'.format(exe_prefix, os.getenv('PATH'))
  606. rc = subprocess.call([exe_file, '-c',
  607. 'import sys; sys.exit(not sys.flags.no_site and '
  608. '%r in sys.path and %r in sys.path and %r not in sys.path and '
  609. 'all("\\r" not in p and "\\n" not in p for p in sys.path))' % (
  610. os.path.join(sys_prefix, 'fake-path-name'),
  611. libpath,
  612. os.path.join(sys_prefix, 'from-env'),
  613. )], env=env)
  614. self.assertTrue(rc, "sys.path is incorrect")
  615. if __name__ == "__main__":
  616. unittest.main()