|
42 | 42 | import subprocess
|
43 | 43 | import sys
|
44 | 44 | import tempfile
|
| 45 | +import threading |
45 | 46 | import unittest
|
| 47 | +from http.server import HTTPServer, SimpleHTTPRequestHandler |
46 | 48 | from pathlib import Path
|
| 49 | +from urllib.parse import urljoin |
| 50 | +from urllib.request import pathname2url |
47 | 51 |
|
48 | 52 | SETUP_PY_TEMPLATE = '''
|
49 | 53 | from setuptools import setup, find_packages
|
@@ -145,27 +149,37 @@ def add_package_to_index(self, name, version, dist_type):
|
145 | 149 | package = self.build_package(name, version)[dist_type]
|
146 | 150 | shutil.copy(package, self.index_dir)
|
147 | 151 |
|
148 |
| - def run_venv_pip_install(self, package, extra_env=None): |
| 152 | + def run_venv_pip_install(self, package, extra_env=None, assert_stderr_matches=None): |
149 | 153 | env = self.pip_env.copy()
|
150 | 154 | if extra_env:
|
151 | 155 | env.update(extra_env)
|
152 |
| - out = subprocess.check_output([ |
153 |
| - self.venv_python, |
154 |
| - '--experimental-options', '--python.EnableDebuggingBuiltins=true', |
155 |
| - '-m', 'pip', 'install', '--force-reinstall', |
156 |
| - '--find-links', self.index_dir, '--no-index', '--no-cache-dir', |
157 |
| - package], |
158 |
| - env=env, universal_newlines=True) |
159 |
| - assert 'Applying GraalPy patch failed for' not in out |
160 |
| - return re.findall(r'Successfully installed (\S+)', out) |
| 156 | + proc = subprocess.run( |
| 157 | + [ |
| 158 | + str(self.venv_dir / 'bin' / 'pip'), |
| 159 | + '--isolated', |
| 160 | + 'install', |
| 161 | + '--force-reinstall', |
| 162 | + '--find-links', self.index_dir, |
| 163 | + '--no-index', |
| 164 | + '--no-cache-dir', |
| 165 | + package, |
| 166 | + ], |
| 167 | + check=True, |
| 168 | + capture_output=True, |
| 169 | + env=env, |
| 170 | + universal_newlines=True, |
| 171 | + ) |
| 172 | + print(proc.stderr) |
| 173 | + assert 'Applying GraalPy patch failed for' not in proc.stderr |
| 174 | + if assert_stderr_matches: |
| 175 | + assert re.search(assert_stderr_matches, proc.stderr), \ |
| 176 | + f"Didn't match expected stderr.\nExpected (regex): {assert_stderr_matches}\nActual:{proc.stderr}" |
| 177 | + return re.findall(r'Successfully installed (\S+)', proc.stdout) |
161 | 178 |
|
162 | 179 | def run_test_fun(self):
|
163 | 180 | code = "import patched_package; print(patched_package.test_fun())"
|
164 | 181 | return subprocess.check_output([self.venv_python, '-c', code], universal_newlines=True).strip()
|
165 | 182 |
|
166 |
| - def test_pip_launcher(self): |
167 |
| - subprocess.check_output([str(self.venv_dir / 'bin' / 'pip'), 'install', '--help']) |
168 |
| - |
169 | 183 | def test_wheel_unpatched_version(self):
|
170 | 184 | self.add_package_to_index('foo', '1.0.0', 'wheel')
|
171 | 185 | self.prepare_config('foo', [{
|
@@ -386,3 +400,63 @@ def test_name_with_dashes(self):
|
386 | 400 | }])
|
387 | 401 | assert self.run_venv_pip_install('package-with-dashes') == ['package-with-dashes-1.0.0']
|
388 | 402 | assert self.run_test_fun() == "Patched"
|
| 403 | + |
| 404 | + def check_installing_with_patch_repo(self, url_or_path: str, *, graalpy_version=None, should_be_skipped=False, |
| 405 | + assert_stderr_matches=None): |
| 406 | + self.pip_env['PIP_GRAALPY_PATCHES_URL'] = url_or_path |
| 407 | + if graalpy_version: |
| 408 | + self.pip_env['TEST_PIP_GRAALPY_VERSION'] = graalpy_version |
| 409 | + self.add_package_to_index('foo', '1.1.0', 'wheel') |
| 410 | + self.prepare_config('foo', [{'patch': 'foo.patch'}]) |
| 411 | + assert self.run_venv_pip_install('foo', assert_stderr_matches=assert_stderr_matches) == ['foo-1.1.0'] |
| 412 | + assert self.run_test_fun() == ("Unpatched" if should_be_skipped else "Patched") |
| 413 | + |
| 414 | + def test_broken_patches_path(self): |
| 415 | + self.check_installing_with_patch_repo( |
| 416 | + '/tmp/not-there', |
| 417 | + should_be_skipped=True, |
| 418 | + assert_stderr_matches="WARNING: Failed to load GraalPy patch repository", |
| 419 | + ) |
| 420 | + |
| 421 | + def test_patches_file_url(self): |
| 422 | + self.check_installing_with_patch_repo(urljoin('file:', pathname2url(str(self.patch_dir.absolute())))) |
| 423 | + |
| 424 | + def test_patches_http_url(self): |
| 425 | + patch_dir = self.patch_dir |
| 426 | + |
| 427 | + class Handler(SimpleHTTPRequestHandler): |
| 428 | + def __init__(self, *args, **kwargs): |
| 429 | + super().__init__(*args, directory=str(patch_dir), **kwargs) |
| 430 | + |
| 431 | + try: |
| 432 | + with HTTPServer(('localhost', 0), Handler) as server: |
| 433 | + thread = threading.Thread(target=server.serve_forever) |
| 434 | + thread.start() |
| 435 | + try: |
| 436 | + self.check_installing_with_patch_repo(f'http://localhost:{server.server_port}') |
| 437 | + finally: |
| 438 | + server.shutdown() |
| 439 | + finally: |
| 440 | + thread.join() |
| 441 | + |
| 442 | + def test_patches_repo_version_resolution_exact(self): |
| 443 | + patch_dir_parent = self.patch_dir |
| 444 | + self.patch_dir = patch_dir_parent / '1.3.2' |
| 445 | + self.patch_dir.mkdir() |
| 446 | + self.check_installing_with_patch_repo(str(patch_dir_parent / '<version>'), graalpy_version='1.3.2') |
| 447 | + |
| 448 | + def test_patches_repo_version_resolution_major(self): |
| 449 | + patch_dir_parent = self.patch_dir |
| 450 | + self.patch_dir = patch_dir_parent / '1.3' |
| 451 | + self.patch_dir.mkdir() |
| 452 | + self.check_installing_with_patch_repo(str(patch_dir_parent / '<version>'), graalpy_version='1.3.2') |
| 453 | + |
| 454 | + def test_patches_repo_version_resolution_dev(self): |
| 455 | + patch_dir_parent = self.patch_dir |
| 456 | + self.patch_dir = patch_dir_parent / '1.3.2-dev' |
| 457 | + self.patch_dir.mkdir() |
| 458 | + self.check_installing_with_patch_repo( |
| 459 | + str(patch_dir_parent / '<version>'), |
| 460 | + graalpy_version='1.3.2-dev', |
| 461 | + should_be_skipped=True, |
| 462 | + ) |
0 commit comments