test_getpath.py 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213
  1. import copy
  2. import ntpath
  3. import pathlib
  4. import posixpath
  5. import sys
  6. import unittest
  7. from test.support import verbose
  8. try:
  9. # If we are in a source tree, use the original source file for tests
  10. SOURCE = (pathlib.Path(__file__).absolute().parent.parent.parent / "Modules/getpath.py").read_bytes()
  11. except FileNotFoundError:
  12. # Try from _testcapimodule instead
  13. from _testinternalcapi import get_getpath_codeobject
  14. SOURCE = get_getpath_codeobject()
  15. class MockGetPathTests(unittest.TestCase):
  16. def __init__(self, *a, **kw):
  17. super().__init__(*a, **kw)
  18. self.maxDiff = None
  19. def test_normal_win32(self):
  20. "Test a 'standard' install layout on Windows."
  21. ns = MockNTNamespace(
  22. argv0=r"C:\Python\python.exe",
  23. real_executable=r"C:\Python\python.exe",
  24. )
  25. ns.add_known_xfile(r"C:\Python\python.exe")
  26. ns.add_known_file(r"C:\Python\Lib\os.py")
  27. ns.add_known_dir(r"C:\Python\DLLs")
  28. expected = dict(
  29. executable=r"C:\Python\python.exe",
  30. base_executable=r"C:\Python\python.exe",
  31. prefix=r"C:\Python",
  32. exec_prefix=r"C:\Python",
  33. module_search_paths_set=1,
  34. module_search_paths=[
  35. r"C:\Python\python98.zip",
  36. r"C:\Python\DLLs",
  37. r"C:\Python\Lib",
  38. r"C:\Python",
  39. ],
  40. )
  41. actual = getpath(ns, expected)
  42. self.assertEqual(expected, actual)
  43. def test_buildtree_win32(self):
  44. "Test an in-build-tree layout on Windows."
  45. ns = MockNTNamespace(
  46. argv0=r"C:\CPython\PCbuild\amd64\python.exe",
  47. real_executable=r"C:\CPython\PCbuild\amd64\python.exe",
  48. )
  49. ns.add_known_xfile(r"C:\CPython\PCbuild\amd64\python.exe")
  50. ns.add_known_file(r"C:\CPython\Lib\os.py")
  51. ns.add_known_file(r"C:\CPython\PCbuild\amd64\pybuilddir.txt", [""])
  52. expected = dict(
  53. executable=r"C:\CPython\PCbuild\amd64\python.exe",
  54. base_executable=r"C:\CPython\PCbuild\amd64\python.exe",
  55. prefix=r"C:\CPython",
  56. exec_prefix=r"C:\CPython",
  57. build_prefix=r"C:\CPython",
  58. _is_python_build=1,
  59. module_search_paths_set=1,
  60. module_search_paths=[
  61. r"C:\CPython\PCbuild\amd64\python98.zip",
  62. r"C:\CPython\PCbuild\amd64",
  63. r"C:\CPython\Lib",
  64. ],
  65. )
  66. actual = getpath(ns, expected)
  67. self.assertEqual(expected, actual)
  68. def test_venv_win32(self):
  69. """Test a venv layout on Windows.
  70. This layout is discovered by the presence of %__PYVENV_LAUNCHER__%,
  71. specifying the original launcher executable. site.py is responsible
  72. for updating prefix and exec_prefix.
  73. """
  74. ns = MockNTNamespace(
  75. argv0=r"C:\Python\python.exe",
  76. ENV___PYVENV_LAUNCHER__=r"C:\venv\Scripts\python.exe",
  77. real_executable=r"C:\Python\python.exe",
  78. )
  79. ns.add_known_xfile(r"C:\Python\python.exe")
  80. ns.add_known_xfile(r"C:\venv\Scripts\python.exe")
  81. ns.add_known_file(r"C:\Python\Lib\os.py")
  82. ns.add_known_dir(r"C:\Python\DLLs")
  83. ns.add_known_file(r"C:\venv\pyvenv.cfg", [
  84. r"home = C:\Python"
  85. ])
  86. expected = dict(
  87. executable=r"C:\venv\Scripts\python.exe",
  88. prefix=r"C:\Python",
  89. exec_prefix=r"C:\Python",
  90. base_executable=r"C:\Python\python.exe",
  91. base_prefix=r"C:\Python",
  92. base_exec_prefix=r"C:\Python",
  93. module_search_paths_set=1,
  94. module_search_paths=[
  95. r"C:\Python\python98.zip",
  96. r"C:\Python\DLLs",
  97. r"C:\Python\Lib",
  98. r"C:\Python",
  99. ],
  100. )
  101. actual = getpath(ns, expected)
  102. self.assertEqual(expected, actual)
  103. def test_registry_win32(self):
  104. """Test registry lookup on Windows.
  105. On Windows there are registry entries that are intended for other
  106. applications to register search paths.
  107. """
  108. hkey = rf"HKLM\Software\Python\PythonCore\9.8-XY\PythonPath"
  109. winreg = MockWinreg({
  110. hkey: None,
  111. f"{hkey}\\Path1": "path1-dir",
  112. f"{hkey}\\Path1\\Subdir": "not-subdirs",
  113. })
  114. ns = MockNTNamespace(
  115. argv0=r"C:\Python\python.exe",
  116. real_executable=r"C:\Python\python.exe",
  117. winreg=winreg,
  118. )
  119. ns.add_known_xfile(r"C:\Python\python.exe")
  120. ns.add_known_file(r"C:\Python\Lib\os.py")
  121. ns.add_known_dir(r"C:\Python\DLLs")
  122. expected = dict(
  123. module_search_paths_set=1,
  124. module_search_paths=[
  125. r"C:\Python\python98.zip",
  126. "path1-dir",
  127. # should not contain not-subdirs
  128. r"C:\Python\DLLs",
  129. r"C:\Python\Lib",
  130. r"C:\Python",
  131. ],
  132. )
  133. actual = getpath(ns, expected)
  134. self.assertEqual(expected, actual)
  135. ns["config"]["use_environment"] = 0
  136. ns["config"]["module_search_paths_set"] = 0
  137. ns["config"]["module_search_paths"] = None
  138. expected = dict(
  139. module_search_paths_set=1,
  140. module_search_paths=[
  141. r"C:\Python\python98.zip",
  142. r"C:\Python\DLLs",
  143. r"C:\Python\Lib",
  144. r"C:\Python",
  145. ],
  146. )
  147. actual = getpath(ns, expected)
  148. self.assertEqual(expected, actual)
  149. def test_symlink_normal_win32(self):
  150. "Test a 'standard' install layout via symlink on Windows."
  151. ns = MockNTNamespace(
  152. argv0=r"C:\LinkedFrom\python.exe",
  153. real_executable=r"C:\Python\python.exe",
  154. )
  155. ns.add_known_xfile(r"C:\LinkedFrom\python.exe")
  156. ns.add_known_xfile(r"C:\Python\python.exe")
  157. ns.add_known_link(r"C:\LinkedFrom\python.exe", r"C:\Python\python.exe")
  158. ns.add_known_file(r"C:\Python\Lib\os.py")
  159. ns.add_known_dir(r"C:\Python\DLLs")
  160. expected = dict(
  161. executable=r"C:\LinkedFrom\python.exe",
  162. base_executable=r"C:\LinkedFrom\python.exe",
  163. prefix=r"C:\Python",
  164. exec_prefix=r"C:\Python",
  165. module_search_paths_set=1,
  166. module_search_paths=[
  167. r"C:\Python\python98.zip",
  168. r"C:\Python\DLLs",
  169. r"C:\Python\Lib",
  170. r"C:\Python",
  171. ],
  172. )
  173. actual = getpath(ns, expected)
  174. self.assertEqual(expected, actual)
  175. def test_symlink_buildtree_win32(self):
  176. "Test an in-build-tree layout via symlink on Windows."
  177. ns = MockNTNamespace(
  178. argv0=r"C:\LinkedFrom\python.exe",
  179. real_executable=r"C:\CPython\PCbuild\amd64\python.exe",
  180. )
  181. ns.add_known_xfile(r"C:\LinkedFrom\python.exe")
  182. ns.add_known_xfile(r"C:\CPython\PCbuild\amd64\python.exe")
  183. ns.add_known_link(r"C:\LinkedFrom\python.exe", r"C:\CPython\PCbuild\amd64\python.exe")
  184. ns.add_known_file(r"C:\CPython\Lib\os.py")
  185. ns.add_known_file(r"C:\CPython\PCbuild\amd64\pybuilddir.txt", [""])
  186. expected = dict(
  187. executable=r"C:\LinkedFrom\python.exe",
  188. base_executable=r"C:\LinkedFrom\python.exe",
  189. prefix=r"C:\CPython",
  190. exec_prefix=r"C:\CPython",
  191. build_prefix=r"C:\CPython",
  192. _is_python_build=1,
  193. module_search_paths_set=1,
  194. module_search_paths=[
  195. r"C:\CPython\PCbuild\amd64\python98.zip",
  196. r"C:\CPython\PCbuild\amd64",
  197. r"C:\CPython\Lib",
  198. ],
  199. )
  200. actual = getpath(ns, expected)
  201. self.assertEqual(expected, actual)
  202. def test_buildtree_pythonhome_win32(self):
  203. "Test an out-of-build-tree layout on Windows with PYTHONHOME override."
  204. ns = MockNTNamespace(
  205. argv0=r"C:\Out\python.exe",
  206. real_executable=r"C:\Out\python.exe",
  207. ENV_PYTHONHOME=r"C:\CPython",
  208. )
  209. ns.add_known_xfile(r"C:\Out\python.exe")
  210. ns.add_known_file(r"C:\CPython\Lib\os.py")
  211. ns.add_known_file(r"C:\Out\pybuilddir.txt", [""])
  212. expected = dict(
  213. executable=r"C:\Out\python.exe",
  214. base_executable=r"C:\Out\python.exe",
  215. prefix=r"C:\CPython",
  216. exec_prefix=r"C:\CPython",
  217. # This build_prefix is a miscalculation, because we have
  218. # moved the output direction out of the prefix.
  219. # Specify PYTHONHOME to get the correct prefix/exec_prefix
  220. build_prefix="C:\\",
  221. _is_python_build=1,
  222. module_search_paths_set=1,
  223. module_search_paths=[
  224. r"C:\Out\python98.zip",
  225. r"C:\Out",
  226. r"C:\CPython\Lib",
  227. ],
  228. )
  229. actual = getpath(ns, expected)
  230. self.assertEqual(expected, actual)
  231. def test_no_dlls_win32(self):
  232. "Test a layout on Windows with no DLLs directory."
  233. ns = MockNTNamespace(
  234. argv0=r"C:\Python\python.exe",
  235. real_executable=r"C:\Python\python.exe",
  236. )
  237. ns.add_known_xfile(r"C:\Python\python.exe")
  238. ns.add_known_file(r"C:\Python\Lib\os.py")
  239. expected = dict(
  240. executable=r"C:\Python\python.exe",
  241. base_executable=r"C:\Python\python.exe",
  242. prefix=r"C:\Python",
  243. exec_prefix=r"C:\Python",
  244. module_search_paths_set=1,
  245. module_search_paths=[
  246. r"C:\Python\python98.zip",
  247. r"C:\Python",
  248. r"C:\Python\Lib",
  249. ],
  250. )
  251. actual = getpath(ns, expected)
  252. self.assertEqual(expected, actual)
  253. def test_normal_posix(self):
  254. "Test a 'standard' install layout on *nix"
  255. ns = MockPosixNamespace(
  256. PREFIX="/usr",
  257. argv0="python",
  258. ENV_PATH="/usr/bin",
  259. )
  260. ns.add_known_xfile("/usr/bin/python")
  261. ns.add_known_file("/usr/lib/python9.8/os.py")
  262. ns.add_known_dir("/usr/lib/python9.8/lib-dynload")
  263. expected = dict(
  264. executable="/usr/bin/python",
  265. base_executable="/usr/bin/python",
  266. prefix="/usr",
  267. exec_prefix="/usr",
  268. module_search_paths_set=1,
  269. module_search_paths=[
  270. "/usr/lib/python98.zip",
  271. "/usr/lib/python9.8",
  272. "/usr/lib/python9.8/lib-dynload",
  273. ],
  274. )
  275. actual = getpath(ns, expected)
  276. self.assertEqual(expected, actual)
  277. def test_buildpath_posix(self):
  278. """Test an in-build-tree layout on POSIX.
  279. This layout is discovered from the presence of pybuilddir.txt, which
  280. contains the relative path from the executable's directory to the
  281. platstdlib path.
  282. """
  283. ns = MockPosixNamespace(
  284. argv0=r"/home/cpython/python",
  285. PREFIX="/usr/local",
  286. )
  287. ns.add_known_xfile("/home/cpython/python")
  288. ns.add_known_xfile("/usr/local/bin/python")
  289. ns.add_known_file("/home/cpython/pybuilddir.txt", ["build/lib.linux-x86_64-9.8"])
  290. ns.add_known_file("/home/cpython/Lib/os.py")
  291. ns.add_known_dir("/home/cpython/lib-dynload")
  292. expected = dict(
  293. executable="/home/cpython/python",
  294. prefix="/usr/local",
  295. exec_prefix="/usr/local",
  296. base_executable="/home/cpython/python",
  297. build_prefix="/home/cpython",
  298. _is_python_build=1,
  299. module_search_paths_set=1,
  300. module_search_paths=[
  301. "/usr/local/lib/python98.zip",
  302. "/home/cpython/Lib",
  303. "/home/cpython/build/lib.linux-x86_64-9.8",
  304. ],
  305. )
  306. actual = getpath(ns, expected)
  307. self.assertEqual(expected, actual)
  308. def test_venv_posix(self):
  309. "Test a venv layout on *nix."
  310. ns = MockPosixNamespace(
  311. argv0="python",
  312. PREFIX="/usr",
  313. ENV_PATH="/venv/bin:/usr/bin",
  314. )
  315. ns.add_known_xfile("/usr/bin/python")
  316. ns.add_known_xfile("/venv/bin/python")
  317. ns.add_known_file("/usr/lib/python9.8/os.py")
  318. ns.add_known_dir("/usr/lib/python9.8/lib-dynload")
  319. ns.add_known_file("/venv/pyvenv.cfg", [
  320. r"home = /usr/bin"
  321. ])
  322. expected = dict(
  323. executable="/venv/bin/python",
  324. prefix="/usr",
  325. exec_prefix="/usr",
  326. base_executable="/usr/bin/python",
  327. base_prefix="/usr",
  328. base_exec_prefix="/usr",
  329. module_search_paths_set=1,
  330. module_search_paths=[
  331. "/usr/lib/python98.zip",
  332. "/usr/lib/python9.8",
  333. "/usr/lib/python9.8/lib-dynload",
  334. ],
  335. )
  336. actual = getpath(ns, expected)
  337. self.assertEqual(expected, actual)
  338. def test_venv_changed_name_posix(self):
  339. "Test a venv layout on *nix."
  340. ns = MockPosixNamespace(
  341. argv0="python",
  342. PREFIX="/usr",
  343. ENV_PATH="/venv/bin:/usr/bin",
  344. )
  345. ns.add_known_xfile("/usr/bin/python3")
  346. ns.add_known_xfile("/venv/bin/python")
  347. ns.add_known_link("/venv/bin/python", "/usr/bin/python3")
  348. ns.add_known_file("/usr/lib/python9.8/os.py")
  349. ns.add_known_dir("/usr/lib/python9.8/lib-dynload")
  350. ns.add_known_file("/venv/pyvenv.cfg", [
  351. r"home = /usr/bin"
  352. ])
  353. expected = dict(
  354. executable="/venv/bin/python",
  355. prefix="/usr",
  356. exec_prefix="/usr",
  357. base_executable="/usr/bin/python3",
  358. base_prefix="/usr",
  359. base_exec_prefix="/usr",
  360. module_search_paths_set=1,
  361. module_search_paths=[
  362. "/usr/lib/python98.zip",
  363. "/usr/lib/python9.8",
  364. "/usr/lib/python9.8/lib-dynload",
  365. ],
  366. )
  367. actual = getpath(ns, expected)
  368. self.assertEqual(expected, actual)
  369. def test_venv_non_installed_zip_path_posix(self):
  370. "Test a venv created from non-installed python has correct zip path."""
  371. ns = MockPosixNamespace(
  372. argv0="/venv/bin/python",
  373. PREFIX="/usr",
  374. ENV_PATH="/venv/bin:/usr/bin",
  375. )
  376. ns.add_known_xfile("/path/to/non-installed/bin/python")
  377. ns.add_known_xfile("/venv/bin/python")
  378. ns.add_known_link("/venv/bin/python",
  379. "/path/to/non-installed/bin/python")
  380. ns.add_known_file("/path/to/non-installed/lib/python9.8/os.py")
  381. ns.add_known_dir("/path/to/non-installed/lib/python9.8/lib-dynload")
  382. ns.add_known_file("/venv/pyvenv.cfg", [
  383. r"home = /path/to/non-installed"
  384. ])
  385. expected = dict(
  386. executable="/venv/bin/python",
  387. prefix="/path/to/non-installed",
  388. exec_prefix="/path/to/non-installed",
  389. base_executable="/path/to/non-installed/bin/python",
  390. base_prefix="/path/to/non-installed",
  391. base_exec_prefix="/path/to/non-installed",
  392. module_search_paths_set=1,
  393. module_search_paths=[
  394. "/path/to/non-installed/lib/python98.zip",
  395. "/path/to/non-installed/lib/python9.8",
  396. "/path/to/non-installed/lib/python9.8/lib-dynload",
  397. ],
  398. )
  399. actual = getpath(ns, expected)
  400. self.assertEqual(expected, actual)
  401. def test_venv_changed_name_copy_posix(self):
  402. "Test a venv --copies layout on *nix that lacks a distributed 'python'"
  403. ns = MockPosixNamespace(
  404. argv0="python",
  405. PREFIX="/usr",
  406. ENV_PATH="/venv/bin:/usr/bin",
  407. )
  408. ns.add_known_xfile("/usr/bin/python9")
  409. ns.add_known_xfile("/venv/bin/python")
  410. ns.add_known_file("/usr/lib/python9.8/os.py")
  411. ns.add_known_dir("/usr/lib/python9.8/lib-dynload")
  412. ns.add_known_file("/venv/pyvenv.cfg", [
  413. r"home = /usr/bin"
  414. ])
  415. expected = dict(
  416. executable="/venv/bin/python",
  417. prefix="/usr",
  418. exec_prefix="/usr",
  419. base_executable="/usr/bin/python9",
  420. base_prefix="/usr",
  421. base_exec_prefix="/usr",
  422. module_search_paths_set=1,
  423. module_search_paths=[
  424. "/usr/lib/python98.zip",
  425. "/usr/lib/python9.8",
  426. "/usr/lib/python9.8/lib-dynload",
  427. ],
  428. )
  429. actual = getpath(ns, expected)
  430. self.assertEqual(expected, actual)
  431. def test_symlink_normal_posix(self):
  432. "Test a 'standard' install layout via symlink on *nix"
  433. ns = MockPosixNamespace(
  434. PREFIX="/usr",
  435. argv0="/linkfrom/python",
  436. )
  437. ns.add_known_xfile("/linkfrom/python")
  438. ns.add_known_xfile("/usr/bin/python")
  439. ns.add_known_link("/linkfrom/python", "/usr/bin/python")
  440. ns.add_known_file("/usr/lib/python9.8/os.py")
  441. ns.add_known_dir("/usr/lib/python9.8/lib-dynload")
  442. expected = dict(
  443. executable="/linkfrom/python",
  444. base_executable="/linkfrom/python",
  445. prefix="/usr",
  446. exec_prefix="/usr",
  447. module_search_paths_set=1,
  448. module_search_paths=[
  449. "/usr/lib/python98.zip",
  450. "/usr/lib/python9.8",
  451. "/usr/lib/python9.8/lib-dynload",
  452. ],
  453. )
  454. actual = getpath(ns, expected)
  455. self.assertEqual(expected, actual)
  456. def test_symlink_buildpath_posix(self):
  457. """Test an in-build-tree layout on POSIX.
  458. This layout is discovered from the presence of pybuilddir.txt, which
  459. contains the relative path from the executable's directory to the
  460. platstdlib path.
  461. """
  462. ns = MockPosixNamespace(
  463. argv0=r"/linkfrom/python",
  464. PREFIX="/usr/local",
  465. )
  466. ns.add_known_xfile("/linkfrom/python")
  467. ns.add_known_xfile("/home/cpython/python")
  468. ns.add_known_link("/linkfrom/python", "/home/cpython/python")
  469. ns.add_known_xfile("/usr/local/bin/python")
  470. ns.add_known_file("/home/cpython/pybuilddir.txt", ["build/lib.linux-x86_64-9.8"])
  471. ns.add_known_file("/home/cpython/Lib/os.py")
  472. ns.add_known_dir("/home/cpython/lib-dynload")
  473. expected = dict(
  474. executable="/linkfrom/python",
  475. prefix="/usr/local",
  476. exec_prefix="/usr/local",
  477. base_executable="/linkfrom/python",
  478. build_prefix="/home/cpython",
  479. _is_python_build=1,
  480. module_search_paths_set=1,
  481. module_search_paths=[
  482. "/usr/local/lib/python98.zip",
  483. "/home/cpython/Lib",
  484. "/home/cpython/build/lib.linux-x86_64-9.8",
  485. ],
  486. )
  487. actual = getpath(ns, expected)
  488. self.assertEqual(expected, actual)
  489. def test_custom_platlibdir_posix(self):
  490. "Test an install with custom platlibdir on *nix"
  491. ns = MockPosixNamespace(
  492. PREFIX="/usr",
  493. argv0="/linkfrom/python",
  494. PLATLIBDIR="lib64",
  495. )
  496. ns.add_known_xfile("/usr/bin/python")
  497. ns.add_known_file("/usr/lib64/python9.8/os.py")
  498. ns.add_known_dir("/usr/lib64/python9.8/lib-dynload")
  499. expected = dict(
  500. executable="/linkfrom/python",
  501. base_executable="/linkfrom/python",
  502. prefix="/usr",
  503. exec_prefix="/usr",
  504. module_search_paths_set=1,
  505. module_search_paths=[
  506. "/usr/lib64/python98.zip",
  507. "/usr/lib64/python9.8",
  508. "/usr/lib64/python9.8/lib-dynload",
  509. ],
  510. )
  511. actual = getpath(ns, expected)
  512. self.assertEqual(expected, actual)
  513. def test_framework_macos(self):
  514. """ Test framework layout on macOS
  515. This layout is primarily detected using a compile-time option
  516. (WITH_NEXT_FRAMEWORK).
  517. """
  518. ns = MockPosixNamespace(
  519. os_name="darwin",
  520. argv0="/Library/Frameworks/Python.framework/Versions/9.8/Resources/Python.app/Contents/MacOS/Python",
  521. WITH_NEXT_FRAMEWORK=1,
  522. PREFIX="/Library/Frameworks/Python.framework/Versions/9.8",
  523. EXEC_PREFIX="/Library/Frameworks/Python.framework/Versions/9.8",
  524. ENV___PYVENV_LAUNCHER__="/Library/Frameworks/Python.framework/Versions/9.8/bin/python9.8",
  525. real_executable="/Library/Frameworks/Python.framework/Versions/9.8/Resources/Python.app/Contents/MacOS/Python",
  526. library="/Library/Frameworks/Python.framework/Versions/9.8/Python",
  527. )
  528. ns.add_known_xfile("/Library/Frameworks/Python.framework/Versions/9.8/Resources/Python.app/Contents/MacOS/Python")
  529. ns.add_known_xfile("/Library/Frameworks/Python.framework/Versions/9.8/bin/python9.8")
  530. ns.add_known_dir("/Library/Frameworks/Python.framework/Versions/9.8/lib/python9.8/lib-dynload")
  531. ns.add_known_file("/Library/Frameworks/Python.framework/Versions/9.8/lib/python9.8/os.py")
  532. # This is definitely not the stdlib (see discusion in bpo-46890)
  533. #ns.add_known_file("/Library/Frameworks/lib/python98.zip")
  534. expected = dict(
  535. executable="/Library/Frameworks/Python.framework/Versions/9.8/bin/python9.8",
  536. prefix="/Library/Frameworks/Python.framework/Versions/9.8",
  537. exec_prefix="/Library/Frameworks/Python.framework/Versions/9.8",
  538. base_executable="/Library/Frameworks/Python.framework/Versions/9.8/bin/python9.8",
  539. base_prefix="/Library/Frameworks/Python.framework/Versions/9.8",
  540. base_exec_prefix="/Library/Frameworks/Python.framework/Versions/9.8",
  541. module_search_paths_set=1,
  542. module_search_paths=[
  543. "/Library/Frameworks/Python.framework/Versions/9.8/lib/python98.zip",
  544. "/Library/Frameworks/Python.framework/Versions/9.8/lib/python9.8",
  545. "/Library/Frameworks/Python.framework/Versions/9.8/lib/python9.8/lib-dynload",
  546. ],
  547. )
  548. actual = getpath(ns, expected)
  549. self.assertEqual(expected, actual)
  550. def test_alt_framework_macos(self):
  551. """ Test framework layout on macOS with alternate framework name
  552. ``--with-framework-name=DebugPython``
  553. This layout is primarily detected using a compile-time option
  554. (WITH_NEXT_FRAMEWORK).
  555. """
  556. ns = MockPosixNamespace(
  557. argv0="/Library/Frameworks/DebugPython.framework/Versions/9.8/Resources/Python.app/Contents/MacOS/DebugPython",
  558. os_name="darwin",
  559. WITH_NEXT_FRAMEWORK=1,
  560. PREFIX="/Library/Frameworks/DebugPython.framework/Versions/9.8",
  561. EXEC_PREFIX="/Library/Frameworks/DebugPython.framework/Versions/9.8",
  562. ENV___PYVENV_LAUNCHER__="/Library/Frameworks/DebugPython.framework/Versions/9.8/bin/python9.8",
  563. real_executable="/Library/Frameworks/DebugPython.framework/Versions/9.8/Resources/Python.app/Contents/MacOS/DebugPython",
  564. library="/Library/Frameworks/DebugPython.framework/Versions/9.8/DebugPython",
  565. PYTHONPATH=None,
  566. ENV_PYTHONHOME=None,
  567. ENV_PYTHONEXECUTABLE=None,
  568. executable_dir=None,
  569. py_setpath=None,
  570. )
  571. ns.add_known_xfile("/Library/Frameworks/DebugPython.framework/Versions/9.8/Resources/Python.app/Contents/MacOS/DebugPython")
  572. ns.add_known_xfile("/Library/Frameworks/DebugPython.framework/Versions/9.8/bin/python9.8")
  573. ns.add_known_dir("/Library/Frameworks/DebugPython.framework/Versions/9.8/lib/python9.8/lib-dynload")
  574. ns.add_known_xfile("/Library/Frameworks/DebugPython.framework/Versions/9.8/lib/python9.8/os.py")
  575. # This is definitely not the stdlib (see discusion in bpo-46890)
  576. #ns.add_known_xfile("/Library/lib/python98.zip")
  577. expected = dict(
  578. executable="/Library/Frameworks/DebugPython.framework/Versions/9.8/bin/python9.8",
  579. prefix="/Library/Frameworks/DebugPython.framework/Versions/9.8",
  580. exec_prefix="/Library/Frameworks/DebugPython.framework/Versions/9.8",
  581. base_executable="/Library/Frameworks/DebugPython.framework/Versions/9.8/bin/python9.8",
  582. base_prefix="/Library/Frameworks/DebugPython.framework/Versions/9.8",
  583. base_exec_prefix="/Library/Frameworks/DebugPython.framework/Versions/9.8",
  584. module_search_paths_set=1,
  585. module_search_paths=[
  586. "/Library/Frameworks/DebugPython.framework/Versions/9.8/lib/python98.zip",
  587. "/Library/Frameworks/DebugPython.framework/Versions/9.8/lib/python9.8",
  588. "/Library/Frameworks/DebugPython.framework/Versions/9.8/lib/python9.8/lib-dynload",
  589. ],
  590. )
  591. actual = getpath(ns, expected)
  592. self.assertEqual(expected, actual)
  593. def test_venv_framework_macos(self):
  594. """Test a venv layout on macOS using a framework build
  595. """
  596. venv_path = "/tmp/workdir/venv"
  597. ns = MockPosixNamespace(
  598. os_name="darwin",
  599. argv0="/Library/Frameworks/Python.framework/Versions/9.8/Resources/Python.app/Contents/MacOS/Python",
  600. WITH_NEXT_FRAMEWORK=1,
  601. PREFIX="/Library/Frameworks/Python.framework/Versions/9.8",
  602. EXEC_PREFIX="/Library/Frameworks/Python.framework/Versions/9.8",
  603. ENV___PYVENV_LAUNCHER__=f"{venv_path}/bin/python",
  604. real_executable="/Library/Frameworks/Python.framework/Versions/9.8/Resources/Python.app/Contents/MacOS/Python",
  605. library="/Library/Frameworks/Python.framework/Versions/9.8/Python",
  606. )
  607. ns.add_known_dir(venv_path)
  608. ns.add_known_dir(f"{venv_path}/bin")
  609. ns.add_known_dir(f"{venv_path}/lib")
  610. ns.add_known_dir(f"{venv_path}/lib/python9.8")
  611. ns.add_known_xfile(f"{venv_path}/bin/python")
  612. ns.add_known_xfile("/Library/Frameworks/Python.framework/Versions/9.8/Resources/Python.app/Contents/MacOS/Python")
  613. ns.add_known_xfile("/Library/Frameworks/Python.framework/Versions/9.8/bin/python9.8")
  614. ns.add_known_dir("/Library/Frameworks/Python.framework/Versions/9.8/lib/python9.8/lib-dynload")
  615. ns.add_known_xfile("/Library/Frameworks/Python.framework/Versions/9.8/lib/python9.8/os.py")
  616. ns.add_known_file(f"{venv_path}/pyvenv.cfg", [
  617. "home = /Library/Frameworks/Python.framework/Versions/9.8/bin"
  618. ])
  619. expected = dict(
  620. executable=f"{venv_path}/bin/python",
  621. prefix="/Library/Frameworks/Python.framework/Versions/9.8",
  622. exec_prefix="/Library/Frameworks/Python.framework/Versions/9.8",
  623. base_executable="/Library/Frameworks/Python.framework/Versions/9.8/bin/python9.8",
  624. base_prefix="/Library/Frameworks/Python.framework/Versions/9.8",
  625. base_exec_prefix="/Library/Frameworks/Python.framework/Versions/9.8",
  626. module_search_paths_set=1,
  627. module_search_paths=[
  628. "/Library/Frameworks/Python.framework/Versions/9.8/lib/python98.zip",
  629. "/Library/Frameworks/Python.framework/Versions/9.8/lib/python9.8",
  630. "/Library/Frameworks/Python.framework/Versions/9.8/lib/python9.8/lib-dynload",
  631. ],
  632. )
  633. actual = getpath(ns, expected)
  634. self.assertEqual(expected, actual)
  635. def test_venv_alt_framework_macos(self):
  636. """Test a venv layout on macOS using a framework build
  637. ``--with-framework-name=DebugPython``
  638. """
  639. venv_path = "/tmp/workdir/venv"
  640. ns = MockPosixNamespace(
  641. os_name="darwin",
  642. argv0="/Library/Frameworks/DebugPython.framework/Versions/9.8/Resources/Python.app/Contents/MacOS/DebugPython",
  643. WITH_NEXT_FRAMEWORK=1,
  644. PREFIX="/Library/Frameworks/DebugPython.framework/Versions/9.8",
  645. EXEC_PREFIX="/Library/Frameworks/DebugPython.framework/Versions/9.8",
  646. ENV___PYVENV_LAUNCHER__=f"{venv_path}/bin/python",
  647. real_executable="/Library/Frameworks/DebugPython.framework/Versions/9.8/Resources/Python.app/Contents/MacOS/DebugPython",
  648. library="/Library/Frameworks/DebugPython.framework/Versions/9.8/DebugPython",
  649. )
  650. ns.add_known_dir(venv_path)
  651. ns.add_known_dir(f"{venv_path}/bin")
  652. ns.add_known_dir(f"{venv_path}/lib")
  653. ns.add_known_dir(f"{venv_path}/lib/python9.8")
  654. ns.add_known_xfile(f"{venv_path}/bin/python")
  655. ns.add_known_xfile("/Library/Frameworks/DebugPython.framework/Versions/9.8/Resources/Python.app/Contents/MacOS/DebugPython")
  656. ns.add_known_xfile("/Library/Frameworks/DebugPython.framework/Versions/9.8/bin/python9.8")
  657. ns.add_known_dir("/Library/Frameworks/DebugPython.framework/Versions/9.8/lib/python9.8/lib-dynload")
  658. ns.add_known_xfile("/Library/Frameworks/DebugPython.framework/Versions/9.8/lib/python9.8/os.py")
  659. ns.add_known_file(f"{venv_path}/pyvenv.cfg", [
  660. "home = /Library/Frameworks/DebugPython.framework/Versions/9.8/bin"
  661. ])
  662. expected = dict(
  663. executable=f"{venv_path}/bin/python",
  664. prefix="/Library/Frameworks/DebugPython.framework/Versions/9.8",
  665. exec_prefix="/Library/Frameworks/DebugPython.framework/Versions/9.8",
  666. base_executable="/Library/Frameworks/DebugPython.framework/Versions/9.8/bin/python9.8",
  667. base_prefix="/Library/Frameworks/DebugPython.framework/Versions/9.8",
  668. base_exec_prefix="/Library/Frameworks/DebugPython.framework/Versions/9.8",
  669. module_search_paths_set=1,
  670. module_search_paths=[
  671. "/Library/Frameworks/DebugPython.framework/Versions/9.8/lib/python98.zip",
  672. "/Library/Frameworks/DebugPython.framework/Versions/9.8/lib/python9.8",
  673. "/Library/Frameworks/DebugPython.framework/Versions/9.8/lib/python9.8/lib-dynload",
  674. ],
  675. )
  676. actual = getpath(ns, expected)
  677. self.assertEqual(expected, actual)
  678. def test_venv_macos(self):
  679. """Test a venv layout on macOS.
  680. This layout is discovered when 'executable' and 'real_executable' match,
  681. but $__PYVENV_LAUNCHER__ has been set to the original process.
  682. """
  683. ns = MockPosixNamespace(
  684. os_name="darwin",
  685. argv0="/usr/bin/python",
  686. PREFIX="/usr",
  687. ENV___PYVENV_LAUNCHER__="/framework/Python9.8/python",
  688. real_executable="/usr/bin/python",
  689. )
  690. ns.add_known_xfile("/usr/bin/python")
  691. ns.add_known_xfile("/framework/Python9.8/python")
  692. ns.add_known_file("/usr/lib/python9.8/os.py")
  693. ns.add_known_dir("/usr/lib/python9.8/lib-dynload")
  694. ns.add_known_file("/framework/Python9.8/pyvenv.cfg", [
  695. "home = /usr/bin"
  696. ])
  697. expected = dict(
  698. executable="/framework/Python9.8/python",
  699. prefix="/usr",
  700. exec_prefix="/usr",
  701. base_executable="/usr/bin/python",
  702. base_prefix="/usr",
  703. base_exec_prefix="/usr",
  704. module_search_paths_set=1,
  705. module_search_paths=[
  706. "/usr/lib/python98.zip",
  707. "/usr/lib/python9.8",
  708. "/usr/lib/python9.8/lib-dynload",
  709. ],
  710. )
  711. actual = getpath(ns, expected)
  712. self.assertEqual(expected, actual)
  713. def test_symlink_normal_macos(self):
  714. "Test a 'standard' install layout via symlink on macOS"
  715. ns = MockPosixNamespace(
  716. os_name="darwin",
  717. PREFIX="/usr",
  718. argv0="python",
  719. ENV_PATH="/linkfrom:/usr/bin",
  720. # real_executable on macOS matches the invocation path
  721. real_executable="/linkfrom/python",
  722. )
  723. ns.add_known_xfile("/linkfrom/python")
  724. ns.add_known_xfile("/usr/bin/python")
  725. ns.add_known_link("/linkfrom/python", "/usr/bin/python")
  726. ns.add_known_file("/usr/lib/python9.8/os.py")
  727. ns.add_known_dir("/usr/lib/python9.8/lib-dynload")
  728. expected = dict(
  729. executable="/linkfrom/python",
  730. base_executable="/linkfrom/python",
  731. prefix="/usr",
  732. exec_prefix="/usr",
  733. module_search_paths_set=1,
  734. module_search_paths=[
  735. "/usr/lib/python98.zip",
  736. "/usr/lib/python9.8",
  737. "/usr/lib/python9.8/lib-dynload",
  738. ],
  739. )
  740. actual = getpath(ns, expected)
  741. self.assertEqual(expected, actual)
  742. def test_symlink_buildpath_macos(self):
  743. """Test an in-build-tree layout via symlink on macOS.
  744. This layout is discovered from the presence of pybuilddir.txt, which
  745. contains the relative path from the executable's directory to the
  746. platstdlib path.
  747. """
  748. ns = MockPosixNamespace(
  749. os_name="darwin",
  750. argv0=r"python",
  751. ENV_PATH="/linkfrom:/usr/bin",
  752. PREFIX="/usr/local",
  753. # real_executable on macOS matches the invocation path
  754. real_executable="/linkfrom/python",
  755. )
  756. ns.add_known_xfile("/linkfrom/python")
  757. ns.add_known_xfile("/home/cpython/python")
  758. ns.add_known_link("/linkfrom/python", "/home/cpython/python")
  759. ns.add_known_xfile("/usr/local/bin/python")
  760. ns.add_known_file("/home/cpython/pybuilddir.txt", ["build/lib.macos-9.8"])
  761. ns.add_known_file("/home/cpython/Lib/os.py")
  762. ns.add_known_dir("/home/cpython/lib-dynload")
  763. expected = dict(
  764. executable="/linkfrom/python",
  765. prefix="/usr/local",
  766. exec_prefix="/usr/local",
  767. base_executable="/linkfrom/python",
  768. build_prefix="/home/cpython",
  769. _is_python_build=1,
  770. module_search_paths_set=1,
  771. module_search_paths=[
  772. "/usr/local/lib/python98.zip",
  773. "/home/cpython/Lib",
  774. "/home/cpython/build/lib.macos-9.8",
  775. ],
  776. )
  777. actual = getpath(ns, expected)
  778. self.assertEqual(expected, actual)
  779. # ******************************************************************************
  780. DEFAULT_NAMESPACE = dict(
  781. PREFIX="",
  782. EXEC_PREFIX="",
  783. PYTHONPATH="",
  784. VPATH="",
  785. PLATLIBDIR="",
  786. PYDEBUGEXT="",
  787. VERSION_MAJOR=9, # fixed version number for ease
  788. VERSION_MINOR=8, # of testing
  789. PYWINVER=None,
  790. EXE_SUFFIX=None,
  791. ENV_PATH="",
  792. ENV_PYTHONHOME="",
  793. ENV_PYTHONEXECUTABLE="",
  794. ENV___PYVENV_LAUNCHER__="",
  795. argv0="",
  796. py_setpath="",
  797. real_executable="",
  798. executable_dir="",
  799. library="",
  800. winreg=None,
  801. build_prefix=None,
  802. venv_prefix=None,
  803. )
  804. DEFAULT_CONFIG = dict(
  805. home=None,
  806. platlibdir=None,
  807. pythonpath=None,
  808. program_name=None,
  809. prefix=None,
  810. exec_prefix=None,
  811. base_prefix=None,
  812. base_exec_prefix=None,
  813. executable=None,
  814. base_executable="",
  815. stdlib_dir=None,
  816. platstdlib_dir=None,
  817. module_search_paths=None,
  818. module_search_paths_set=0,
  819. pythonpath_env=None,
  820. argv=None,
  821. orig_argv=None,
  822. isolated=0,
  823. use_environment=1,
  824. use_site=1,
  825. )
  826. class MockNTNamespace(dict):
  827. def __init__(self, *a, argv0=None, config=None, **kw):
  828. self.update(DEFAULT_NAMESPACE)
  829. self["config"] = DEFAULT_CONFIG.copy()
  830. self["os_name"] = "nt"
  831. self["PLATLIBDIR"] = "DLLs"
  832. self["PYWINVER"] = "9.8-XY"
  833. self["VPATH"] = r"..\.."
  834. super().__init__(*a, **kw)
  835. if argv0:
  836. self["config"]["orig_argv"] = [argv0]
  837. if config:
  838. self["config"].update(config)
  839. self._files = {}
  840. self._links = {}
  841. self._dirs = set()
  842. self._warnings = []
  843. def add_known_file(self, path, lines=None):
  844. self._files[path.casefold()] = list(lines or ())
  845. self.add_known_dir(path.rpartition("\\")[0])
  846. def add_known_xfile(self, path):
  847. self.add_known_file(path)
  848. def add_known_link(self, path, target):
  849. self._links[path.casefold()] = target
  850. def add_known_dir(self, path):
  851. p = path.rstrip("\\").casefold()
  852. while p:
  853. self._dirs.add(p)
  854. p = p.rpartition("\\")[0]
  855. def __missing__(self, key):
  856. try:
  857. return getattr(self, key)
  858. except AttributeError:
  859. raise KeyError(key) from None
  860. def abspath(self, path):
  861. if self.isabs(path):
  862. return path
  863. return self.joinpath("C:\\Absolute", path)
  864. def basename(self, path):
  865. return path.rpartition("\\")[2]
  866. def dirname(self, path):
  867. name = path.rstrip("\\").rpartition("\\")[0]
  868. if name[1:] == ":":
  869. return name + "\\"
  870. return name
  871. def hassuffix(self, path, suffix):
  872. return path.casefold().endswith(suffix.casefold())
  873. def isabs(self, path):
  874. return path[1:3] == ":\\"
  875. def isdir(self, path):
  876. if verbose:
  877. print("Check if", path, "is a dir")
  878. return path.casefold() in self._dirs
  879. def isfile(self, path):
  880. if verbose:
  881. print("Check if", path, "is a file")
  882. return path.casefold() in self._files
  883. def ismodule(self, path):
  884. if verbose:
  885. print("Check if", path, "is a module")
  886. path = path.casefold()
  887. return path in self._files and path.rpartition(".")[2] == "py".casefold()
  888. def isxfile(self, path):
  889. if verbose:
  890. print("Check if", path, "is a executable")
  891. path = path.casefold()
  892. return path in self._files and path.rpartition(".")[2] == "exe".casefold()
  893. def joinpath(self, *path):
  894. return ntpath.normpath(ntpath.join(*path))
  895. def readlines(self, path):
  896. try:
  897. return self._files[path.casefold()]
  898. except KeyError:
  899. raise FileNotFoundError(path) from None
  900. def realpath(self, path, _trail=None):
  901. if verbose:
  902. print("Read link from", path)
  903. try:
  904. link = self._links[path.casefold()]
  905. except KeyError:
  906. return path
  907. if _trail is None:
  908. _trail = set()
  909. elif link.casefold() in _trail:
  910. raise OSError("circular link")
  911. _trail.add(link.casefold())
  912. return self.realpath(link, _trail)
  913. def warn(self, message):
  914. self._warnings.append(message)
  915. if verbose:
  916. print(message)
  917. class MockWinreg:
  918. HKEY_LOCAL_MACHINE = "HKLM"
  919. HKEY_CURRENT_USER = "HKCU"
  920. def __init__(self, keys):
  921. self.keys = {k.casefold(): v for k, v in keys.items()}
  922. self.open = {}
  923. def __repr__(self):
  924. return "<MockWinreg>"
  925. def __eq__(self, other):
  926. return isinstance(other, type(self))
  927. def open_keys(self):
  928. return list(self.open)
  929. def OpenKeyEx(self, hkey, subkey):
  930. if verbose:
  931. print(f"OpenKeyEx({hkey}, {subkey})")
  932. key = f"{hkey}\\{subkey}".casefold()
  933. if key in self.keys:
  934. self.open[key] = self.open.get(key, 0) + 1
  935. return key
  936. raise FileNotFoundError()
  937. def CloseKey(self, hkey):
  938. if verbose:
  939. print(f"CloseKey({hkey})")
  940. hkey = hkey.casefold()
  941. if hkey not in self.open:
  942. raise RuntimeError("key is not open")
  943. self.open[hkey] -= 1
  944. if not self.open[hkey]:
  945. del self.open[hkey]
  946. def EnumKey(self, hkey, i):
  947. if verbose:
  948. print(f"EnumKey({hkey}, {i})")
  949. hkey = hkey.casefold()
  950. if hkey not in self.open:
  951. raise RuntimeError("key is not open")
  952. prefix = f'{hkey}\\'
  953. subkeys = [k[len(prefix):] for k in sorted(self.keys) if k.startswith(prefix)]
  954. subkeys[:] = [k for k in subkeys if '\\' not in k]
  955. for j, n in enumerate(subkeys):
  956. if j == i:
  957. return n.removeprefix(prefix)
  958. raise OSError("end of enumeration")
  959. def QueryValue(self, hkey, subkey):
  960. if verbose:
  961. print(f"QueryValue({hkey}, {subkey})")
  962. hkey = hkey.casefold()
  963. if hkey not in self.open:
  964. raise RuntimeError("key is not open")
  965. if subkey:
  966. subkey = subkey.casefold()
  967. hkey = f'{hkey}\\{subkey}'
  968. try:
  969. return self.keys[hkey]
  970. except KeyError:
  971. raise OSError()
  972. class MockPosixNamespace(dict):
  973. def __init__(self, *a, argv0=None, config=None, **kw):
  974. self.update(DEFAULT_NAMESPACE)
  975. self["config"] = DEFAULT_CONFIG.copy()
  976. self["os_name"] = "posix"
  977. self["PLATLIBDIR"] = "lib"
  978. self["WITH_NEXT_FRAMEWORK"] = 0
  979. super().__init__(*a, **kw)
  980. if argv0:
  981. self["config"]["orig_argv"] = [argv0]
  982. if config:
  983. self["config"].update(config)
  984. self._files = {}
  985. self._xfiles = set()
  986. self._links = {}
  987. self._dirs = set()
  988. self._warnings = []
  989. def add_known_file(self, path, lines=None):
  990. self._files[path] = list(lines or ())
  991. self.add_known_dir(path.rpartition("/")[0])
  992. def add_known_xfile(self, path):
  993. self.add_known_file(path)
  994. self._xfiles.add(path)
  995. def add_known_link(self, path, target):
  996. self._links[path] = target
  997. def add_known_dir(self, path):
  998. p = path.rstrip("/")
  999. while p:
  1000. self._dirs.add(p)
  1001. p = p.rpartition("/")[0]
  1002. def __missing__(self, key):
  1003. try:
  1004. return getattr(self, key)
  1005. except AttributeError:
  1006. raise KeyError(key) from None
  1007. def abspath(self, path):
  1008. if self.isabs(path):
  1009. return path
  1010. return self.joinpath("/Absolute", path)
  1011. def basename(self, path):
  1012. return path.rpartition("/")[2]
  1013. def dirname(self, path):
  1014. return path.rstrip("/").rpartition("/")[0]
  1015. def hassuffix(self, path, suffix):
  1016. return path.endswith(suffix)
  1017. def isabs(self, path):
  1018. return path[0:1] == "/"
  1019. def isdir(self, path):
  1020. if verbose:
  1021. print("Check if", path, "is a dir")
  1022. return path in self._dirs
  1023. def isfile(self, path):
  1024. if verbose:
  1025. print("Check if", path, "is a file")
  1026. return path in self._files
  1027. def ismodule(self, path):
  1028. if verbose:
  1029. print("Check if", path, "is a module")
  1030. return path in self._files and path.rpartition(".")[2] == "py"
  1031. def isxfile(self, path):
  1032. if verbose:
  1033. print("Check if", path, "is an xfile")
  1034. return path in self._xfiles
  1035. def joinpath(self, *path):
  1036. return posixpath.normpath(posixpath.join(*path))
  1037. def readlines(self, path):
  1038. try:
  1039. return self._files[path]
  1040. except KeyError:
  1041. raise FileNotFoundError(path) from None
  1042. def realpath(self, path, _trail=None):
  1043. if verbose:
  1044. print("Read link from", path)
  1045. try:
  1046. link = self._links[path]
  1047. except KeyError:
  1048. return path
  1049. if _trail is None:
  1050. _trail = set()
  1051. elif link in _trail:
  1052. raise OSError("circular link")
  1053. _trail.add(link)
  1054. return self.realpath(link, _trail)
  1055. def warn(self, message):
  1056. self._warnings.append(message)
  1057. if verbose:
  1058. print(message)
  1059. def diff_dict(before, after, prefix="global"):
  1060. diff = []
  1061. for k in sorted(before):
  1062. if k[:2] == "__":
  1063. continue
  1064. if k == "config":
  1065. diff_dict(before[k], after[k], prefix="config")
  1066. continue
  1067. if k in after and after[k] != before[k]:
  1068. diff.append((k, before[k], after[k]))
  1069. if not diff:
  1070. return
  1071. max_k = max(len(k) for k, _, _ in diff)
  1072. indent = " " * (len(prefix) + 1 + max_k)
  1073. if verbose:
  1074. for k, b, a in diff:
  1075. if b:
  1076. print("{}.{} -{!r}\n{} +{!r}".format(prefix, k.ljust(max_k), b, indent, a))
  1077. else:
  1078. print("{}.{} +{!r}".format(prefix, k.ljust(max_k), a))
  1079. def dump_dict(before, after, prefix="global"):
  1080. if not verbose or not after:
  1081. return
  1082. max_k = max(len(k) for k in after)
  1083. for k, v in sorted(after.items(), key=lambda i: i[0]):
  1084. if k[:2] == "__":
  1085. continue
  1086. if k == "config":
  1087. dump_dict(before[k], after[k], prefix="config")
  1088. continue
  1089. try:
  1090. if v != before[k]:
  1091. print("{}.{} {!r} (was {!r})".format(prefix, k.ljust(max_k), v, before[k]))
  1092. continue
  1093. except KeyError:
  1094. pass
  1095. print("{}.{} {!r}".format(prefix, k.ljust(max_k), v))
  1096. def getpath(ns, keys):
  1097. before = copy.deepcopy(ns)
  1098. failed = True
  1099. try:
  1100. exec(SOURCE, ns)
  1101. failed = False
  1102. finally:
  1103. if failed:
  1104. dump_dict(before, ns)
  1105. else:
  1106. diff_dict(before, ns)
  1107. return {
  1108. k: ns['config'].get(k, ns.get(k, ...))
  1109. for k in keys
  1110. }