|  | 
| 17 | 17 | import sys | 
| 18 | 18 | import sysconfig | 
| 19 | 19 | import tempfile | 
|  | 20 | +import shlex | 
| 20 | 21 | from test.support import (captured_stdout, captured_stderr, | 
| 21 | 22 |                           skip_if_broken_multiprocessing_synchronize, verbose, | 
| 22 | 23 |                           requires_subprocess, is_emscripten, is_wasi, | 
| @@ -97,6 +98,10 @@ def get_text_file_contents(self, *args, encoding='utf-8'): | 
| 97 | 98 |             result = f.read() | 
| 98 | 99 |         return result | 
| 99 | 100 | 
 | 
|  | 101 | +    def assertEndsWith(self, string, tail): | 
|  | 102 | +        if not string.endswith(tail): | 
|  | 103 | +            self.fail(f"String {string!r} does not end with {tail!r}") | 
|  | 104 | + | 
| 100 | 105 | class BasicTest(BaseTest): | 
| 101 | 106 |     """Test venv module functionality.""" | 
| 102 | 107 | 
 | 
| @@ -446,6 +451,82 @@ def test_executable_symlinks(self): | 
| 446 | 451 |             'import sys; print(sys.executable)']) | 
| 447 | 452 |         self.assertEqual(out.strip(), envpy.encode()) | 
| 448 | 453 | 
 | 
|  | 454 | +    # gh-124651: test quoted strings | 
|  | 455 | +    @unittest.skipIf(os.name == 'nt', 'contains invalid characters on Windows') | 
|  | 456 | +    def test_special_chars_bash(self): | 
|  | 457 | +        """ | 
|  | 458 | +        Test that the template strings are quoted properly (bash) | 
|  | 459 | +        """ | 
|  | 460 | +        rmtree(self.env_dir) | 
|  | 461 | +        bash = shutil.which('bash') | 
|  | 462 | +        if bash is None: | 
|  | 463 | +            self.skipTest('bash required for this test') | 
|  | 464 | +        env_name = '"\';&&$e|\'"' | 
|  | 465 | +        env_dir = os.path.join(os.path.realpath(self.env_dir), env_name) | 
|  | 466 | +        builder = venv.EnvBuilder(clear=True) | 
|  | 467 | +        builder.create(env_dir) | 
|  | 468 | +        activate = os.path.join(env_dir, self.bindir, 'activate') | 
|  | 469 | +        test_script = os.path.join(self.env_dir, 'test_special_chars.sh') | 
|  | 470 | +        with open(test_script, "w") as f: | 
|  | 471 | +            f.write(f'source {shlex.quote(activate)}\n' | 
|  | 472 | +                    'python -c \'import sys; print(sys.executable)\'\n' | 
|  | 473 | +                    'python -c \'import os; print(os.environ["VIRTUAL_ENV"])\'\n' | 
|  | 474 | +                    'deactivate\n') | 
|  | 475 | +        out, err = check_output([bash, test_script]) | 
|  | 476 | +        lines = out.splitlines() | 
|  | 477 | +        self.assertTrue(env_name.encode() in lines[0]) | 
|  | 478 | +        self.assertEndsWith(lines[1], env_name.encode()) | 
|  | 479 | + | 
|  | 480 | +    # gh-124651: test quoted strings | 
|  | 481 | +    @unittest.skipIf(os.name == 'nt', 'contains invalid characters on Windows') | 
|  | 482 | +    def test_special_chars_csh(self): | 
|  | 483 | +        """ | 
|  | 484 | +        Test that the template strings are quoted properly (csh) | 
|  | 485 | +        """ | 
|  | 486 | +        rmtree(self.env_dir) | 
|  | 487 | +        csh = shutil.which('tcsh') or shutil.which('csh') | 
|  | 488 | +        if csh is None: | 
|  | 489 | +            self.skipTest('csh required for this test') | 
|  | 490 | +        env_name = '"\';&&$e|\'"' | 
|  | 491 | +        env_dir = os.path.join(os.path.realpath(self.env_dir), env_name) | 
|  | 492 | +        builder = venv.EnvBuilder(clear=True) | 
|  | 493 | +        builder.create(env_dir) | 
|  | 494 | +        activate = os.path.join(env_dir, self.bindir, 'activate.csh') | 
|  | 495 | +        test_script = os.path.join(self.env_dir, 'test_special_chars.csh') | 
|  | 496 | +        with open(test_script, "w") as f: | 
|  | 497 | +            f.write(f'source {shlex.quote(activate)}\n' | 
|  | 498 | +                    'python -c \'import sys; print(sys.executable)\'\n' | 
|  | 499 | +                    'python -c \'import os; print(os.environ["VIRTUAL_ENV"])\'\n' | 
|  | 500 | +                    'deactivate\n') | 
|  | 501 | +        out, err = check_output([csh, test_script]) | 
|  | 502 | +        lines = out.splitlines() | 
|  | 503 | +        self.assertTrue(env_name.encode() in lines[0]) | 
|  | 504 | +        self.assertEndsWith(lines[1], env_name.encode()) | 
|  | 505 | + | 
|  | 506 | +    # gh-124651: test quoted strings on Windows | 
|  | 507 | +    @unittest.skipUnless(os.name == 'nt', 'only relevant on Windows') | 
|  | 508 | +    def test_special_chars_windows(self): | 
|  | 509 | +        """ | 
|  | 510 | +        Test that the template strings are quoted properly on Windows | 
|  | 511 | +        """ | 
|  | 512 | +        rmtree(self.env_dir) | 
|  | 513 | +        env_name = "'&&^$e" | 
|  | 514 | +        env_dir = os.path.join(os.path.realpath(self.env_dir), env_name) | 
|  | 515 | +        builder = venv.EnvBuilder(clear=True) | 
|  | 516 | +        builder.create(env_dir) | 
|  | 517 | +        activate = os.path.join(env_dir, self.bindir, 'activate.bat') | 
|  | 518 | +        test_batch = os.path.join(self.env_dir, 'test_special_chars.bat') | 
|  | 519 | +        with open(test_batch, "w") as f: | 
|  | 520 | +            f.write('@echo off\n' | 
|  | 521 | +                    f'"{activate}" & ' | 
|  | 522 | +                    f'{self.exe} -c "import sys; print(sys.executable)" & ' | 
|  | 523 | +                    f'{self.exe} -c "import os; print(os.environ[\'VIRTUAL_ENV\'])" & ' | 
|  | 524 | +                    'deactivate') | 
|  | 525 | +        out, err = check_output([test_batch]) | 
|  | 526 | +        lines = out.splitlines() | 
|  | 527 | +        self.assertTrue(env_name.encode() in lines[0]) | 
|  | 528 | +        self.assertEndsWith(lines[1], env_name.encode()) | 
|  | 529 | + | 
| 449 | 530 |     @unittest.skipUnless(os.name == 'nt', 'only relevant on Windows') | 
| 450 | 531 |     def test_unicode_in_batch_file(self): | 
| 451 | 532 |         """ | 
|  | 
0 commit comments