|
1 | 1 | import os
|
| 2 | +import sys |
| 3 | +import sysconfig |
| 4 | +from contextlib import ExitStack |
| 5 | +import importlib |
2 | 6 | from unittest import mock
|
3 | 7 |
|
4 | 8 | import pytest
|
5 | 9 |
|
6 | 10 | from pipenv.exceptions import PipenvUsageError
|
7 |
| -from pipenv.utils import dependencies, indexes, internet, shell, toml |
| 11 | +from pipenv.utils import dependencies, indexes, internet, shell, toml, virtualenv |
8 | 12 |
|
9 | 13 | # Pipfile format <-> requirements.txt format.
|
10 | 14 | DEP_PIP_PAIRS = [
|
@@ -547,3 +551,80 @@ def test_is_env_truthy_does_not_exisxt(self, monkeypatch):
|
547 | 551 | name = "ZZZ"
|
548 | 552 | monkeypatch.delenv(name, raising=False)
|
549 | 553 | assert shell.is_env_truthy(name) is False
|
| 554 | + |
| 555 | + @pytest.mark.utils |
| 556 | + @pytest.mark.parametrize( |
| 557 | + "os_name, platform, version, preferred_schemes, expected", |
| 558 | + [ |
| 559 | + ("nt", "win32", "any",{'prefix': 'posix_prefix'}, 'foobar/bin'), |
| 560 | + ("nt", "win32", "GCC",{'prefix': 'posix_prefix'}, 'foobar/bin'), |
| 561 | + ("nt", "win32", "GCC", {'prefix': 'nt'}, 'foobar/bin'), |
| 562 | + ("nt", "win32", "any", {'prefix': 'nt'}, 'foobar/Scripts'), |
| 563 | + ("nt", "win32", "any", {'prefix': 'venv'}, 'foobar/Scripts'), |
| 564 | + ("nt", "win32", "GCC", {'prefix': 'venv'}, 'foobar/bin'), |
| 565 | + ] |
| 566 | + ) |
| 567 | + def test_virtualenv_scripts_dir(self, os_name, platform, version, preferred_schemes, expected): |
| 568 | + """Test for CPython compiled against various platforms |
| 569 | +
|
| 570 | + To simulate the differing environments as best as possible, we're |
| 571 | + mocking `os.name`, and `sys.platform`, and |
| 572 | + `sysconfig._get_preferred_schemes` as this is the lowest we must go to |
| 573 | + have full control for mocking all possible combinations relevant for the |
| 574 | + function we're testing here. MSYS2 is the most extreme edge-case I could |
| 575 | + find, since it patches sysconfig at compile time. |
| 576 | +
|
| 577 | + References (CPython main branch is also applicable to 3.12 branch): |
| 578 | +
|
| 579 | + - https://github.com/msys2/MINGW-packages/blob/master/mingw-w64-python/0017-sysconfig-treat-MINGW-builds-as-POSIX-builds.patch |
| 580 | + - https://github.com/python/cpython/blob/main/Lib/sysconfig/__init__.py#L28 |
| 581 | + - https://github.com/python/cpython/blob/main/Include/cpython/initconfig.h#L209 |
| 582 | + - https://github.com/python/cpython/blob/main/Python/sysmodule.c#L3894 |
| 583 | + - https://github.com/python/cpython/blob/main/Modules/getpath.py#L227 |
| 584 | +
|
| 585 | + :param os_name: os.name |
| 586 | + :param platform: sys.platform |
| 587 | + :param version: sys.version |
| 588 | + :param preferred_schemes: sysconfig._get_preferred_schemes() |
| 589 | + """ |
| 590 | + with ExitStack() as stack: |
| 591 | + stack.enter_context(mock.patch.object(os, 'name', os_name)) |
| 592 | + stack.enter_context(mock.patch.object(sys, 'platform', platform)) |
| 593 | + stack.enter_context(mock.patch.object(sys, 'version', version)) |
| 594 | + |
| 595 | + # ensure, that when the test suite is executed from within a venv, |
| 596 | + # that this does not trigger defaulting to choosing the venv prefix |
| 597 | + if preferred_schemes['prefix'] != 'venv': |
| 598 | + stack.enter_context(mock.patch.object(sys, 'prefix', sys.base_prefix)) |
| 599 | + |
| 600 | + # we need to unload sysconfig, as well as the virtualenv util |
| 601 | + # module, as a constant dependent on os.name |
| 602 | + # and sys.version is being initialized during import, which we need |
| 603 | + # to rewrite to be able to simulate different platforms and patches |
| 604 | + # applied by CPython distributors. |
| 605 | + if "sysconfig" in importlib.sys.modules: |
| 606 | + del importlib.sys.modules["sysconfig"] |
| 607 | + if "pipenv.utils.virtualenv" in importlib.sys.modules: |
| 608 | + del importlib.sys.modules["pipenv.utils.virtualenv"] |
| 609 | + |
| 610 | + sysconfig = importlib.import_module("sysconfig") |
| 611 | + virtualenv = importlib.import_module("pipenv.utils.virtualenv") |
| 612 | + |
| 613 | + # MSYS2 specific reversion of PATCH #23, which seems to be |
| 614 | + # unnecessary, as the logic for correct platform switching is |
| 615 | + # already handled by PATCH #17 (see references of this test) and |
| 616 | + # also introduces inconsistencies. This overwrite only applies to a |
| 617 | + # single parameter set though. As soon as the issue is resolved, |
| 618 | + # this MSYS2 specific override can be removed |
| 619 | + # |
| 620 | + # The issue is tracked here: https://github.com/msys2/MINGW-packages/issues/23992 |
| 621 | + # MSYS2-specific PATCH #23: https://github.com/msys2/MINGW-packages/blob/c8e881f77f804dd1721f7ff90e2931593eb6ac1a/mingw-w64-python/0023-sysconfig-mingw-sysconfig-like-posix.patch#L24 |
| 622 | + if preferred_schemes['prefix'] == 'nt' and 'GCC' not in version: |
| 623 | + sysconfig._INSTALL_SCHEMES['nt']['scripts'] = '{base}/Scripts' |
| 624 | + |
| 625 | + stack.enter_context(mock.patch.object( |
| 626 | + sysconfig, "_get_preferred_schemes", return_value=preferred_schemes |
| 627 | + )) |
| 628 | + |
| 629 | + assert str(virtualenv.virtualenv_scripts_dir('foobar')) == expected |
| 630 | + |
0 commit comments