Skip to content

Commit

Permalink
Backport gh-113628: Fix test_site test with long stdlib paths
Browse files Browse the repository at this point in the history
Summary:
upstream issue: python/cpython#113628
upstream PR: python/cpython#113640
upstream commit: python/cpython@5dc79e3

 ---

The `getpath` module [tries to read certain `_pth` files during initialization](https://github.com/python/cpython/blob/b4b2cc101216ae1017898dfbe43c90da2fd0a308/Modules/getpath.py#L462)).

This is tested in `test_site` with generated `_pth` files that [include the stdlib path 200 times](https://github.com/python/cpython/blob/b4b2cc101216ae1017898dfbe43c90da2fd0a308/Lib/test/test_site.py#L669).

The `getpath` module [disallows reading files over 32KB during initialization](https://github.com/python/cpython/blob/b4b2cc101216ae1017898dfbe43c90da2fd0a308/Modules/getpath.c#L375).

If the test suite runs from a very long base path, 200 repetitions of the stdlib path in the `_pth` file would be enough to exceed the 32KB limit.

To demonstrate, artificially increase the number of repetitions to some high number that would exceed 32KB, e.g. this patch:

```
 diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py
 --- a/Lib/test/test_site.py
+++ b/Lib/test/test_site.py
@@ -631,5 +631,5 @@
         pth_lines = [
             'fake-path-name',
-            *[libpath for _ in range(200)],
+            *[libpath for _ in range(5000)],
             '',
             '# comment',
```

and observe the test failing with the following error:

```
python3.12 -m test test_site -v -m '*_pthFileTests.test_underpth_nosite_file'
== CPython 3.12.1+meta (3.12:2305ca5, Dec 07 2023, 21:46:47) [Clang 15.0.7 (mononoke://mononoke.internal.tfbnw.net/fbsource 3e29b2044f484840f
== Linux-5.12.0-0_fbk16_hardened_7661_geb00762ce6d2-x86_64-with-glibc2.34 little-endian
== Python build: release ThinLTO dtrace
== cwd: /tmp/test_python_worker_2504793æ
== CPU count: 72
== encodings: locale=UTF-8 FS=utf-8
== resources: all test resources are disabled, use -u option to unskip tests

Using random seed: 3001644498
0:00:00 load avg: 6.52 Run 1 test sequentially
0:00:00 load avg: 6.52 [1/1] test_site
test_underpth_nosite_file (test.test_site._pthFileTests.test_underpth_nosite_file) ... Exception ignored error evaluating path:
Traceback (most recent call last):
  File "<frozen getpath>", line 463, in <module>
MemoryError: cannot read file larger than 32KB during initialization
Fatal Python error: error evaluating path
Python runtime state: core initialized

Current thread 0x00007fb01f2d0740 (most recent call first):
  <no Python frame>
ERROR

======================================================================
ERROR: test_underpth_nosite_file (test.test_site._pthFileTests.test_underpth_nosite_file)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/data/users/itamaro/fbsource/buck-out/v2/gen/fbsource/da203790281a65b9/third-party/python/3.12/__install-base__/out/install/lib/python3.12/test/test_site.py", line 647, in test_underpth_nosite_file
    output = subprocess.check_output([exe_file, '-c',
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/data/users/itamaro/fbsource/buck-out/v2/gen/fbsource/da203790281a65b9/third-party/python/3.12/__install-base__/out/install/lib/python3.12/subprocess.py", line 466, in check_output
    return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/data/users/itamaro/fbsource/buck-out/v2/gen/fbsource/da203790281a65b9/third-party/python/3.12/__install-base__/out/install/lib/python3.12/subprocess.py", line 571, in run
    raise CalledProcessError(retcode, process.args,
subprocess.CalledProcessError: Command '['/tmp/tmpsq9c4frs/python3.12', '-c', 'import sys; print("\\n".join(sys.path) if sys.flags.no_site else "")']' returned non-zero exit status 1.

----------------------------------------------------------------------
Ran 1 test in 0.019s

FAILED (errors=1)
test test_site failed
test_site failed (1 error)

== Tests result: FAILURE ==

1 test failed:
    test_site

Total duration: 118 ms
Total tests: run=1 (filtered)
Total test files: run=1/1 (filtered) failed=1
Result: FAILURE
```

See python/cpython#113628 for an upstream issue report.

Reviewed By: carljm

Differential Revision: D52475050

fbshipit-source-id: 3d8d19bc9058d8c78eb65fd4477e0c7e848c7e81
  • Loading branch information
itamaro authored and facebook-github-bot committed Jan 3, 2024
1 parent ec52fb8 commit 9919628
Showing 1 changed file with 21 additions and 22 deletions.
43 changes: 21 additions & 22 deletions Lib/test/test_site.py
Original file line number Diff line number Diff line change
Expand Up @@ -603,10 +603,24 @@ def _calc_sys_path_for_underpth_nosite(self, sys_prefix, lines):
sys_path.append(abs_path)
return sys_path

def _get_pth_lines(self, libpath: str, *, import_site: bool):
pth_lines = ['fake-path-name']
# include 200 lines of `libpath` in _pth lines (or fewer
# if the `libpath` is long enough to get close to 32KB
# see https://github.com/python/cpython/issues/113628)
encoded_libpath_length = len(libpath.encode("utf-8"))
repetitions = min(200, 30000 // encoded_libpath_length)
if repetitions <= 2:
self.skipTest(
f"Python stdlib path is too long ({encoded_libpath_length:,} bytes)")
pth_lines.extend(libpath for _ in range(repetitions))
pth_lines.extend(['', '# comment'])
if import_site:
pth_lines.append('import site')
return pth_lines

@support.requires_subprocess()
def test_underpth_basic(self):
libpath = test.support.STDLIB_DIR
exe_prefix = os.path.dirname(sys.executable)
pth_lines = ['#.', '# ..', *sys.path, '.', '..']
exe_file = self._create_underpth_exe(pth_lines)
sys_path = self._calc_sys_path_for_underpth_nosite(
Expand All @@ -628,12 +642,7 @@ def test_underpth_basic(self):
def test_underpth_nosite_file(self):
libpath = test.support.STDLIB_DIR
exe_prefix = os.path.dirname(sys.executable)
pth_lines = [
'fake-path-name',
*[libpath for _ in range(200)],
'',
'# comment',
]
pth_lines = self._get_pth_lines(libpath, import_site=False)
exe_file = self._create_underpth_exe(pth_lines)
sys_path = self._calc_sys_path_for_underpth_nosite(
os.path.dirname(exe_file),
Expand All @@ -657,13 +666,8 @@ def test_underpth_nosite_file(self):
def test_underpth_file(self):
libpath = test.support.STDLIB_DIR
exe_prefix = os.path.dirname(sys.executable)
exe_file = self._create_underpth_exe([
'fake-path-name',
*[libpath for _ in range(200)],
'',
'# comment',
'import site'
])
exe_file = self._create_underpth_exe(
self._get_pth_lines(libpath, import_site=True))
sys_prefix = os.path.dirname(exe_file)
env = os.environ.copy()
env['PYTHONPATH'] = 'from-env'
Expand All @@ -682,13 +686,8 @@ def test_underpth_file(self):
def test_underpth_dll_file(self):
libpath = test.support.STDLIB_DIR
exe_prefix = os.path.dirname(sys.executable)
exe_file = self._create_underpth_exe([
'fake-path-name',
*[libpath for _ in range(200)],
'',
'# comment',
'import site'
], exe_pth=False)
exe_file = self._create_underpth_exe(
self._get_pth_lines(libpath, import_site=True), exe_pth=False)
sys_prefix = os.path.dirname(exe_file)
env = os.environ.copy()
env['PYTHONPATH'] = 'from-env'
Expand Down

0 comments on commit 9919628

Please sign in to comment.