|
42 | 42 | from pathlib import Path
|
43 | 43 | from subprocess import check_output as subprocess_check_output
|
44 | 44 |
|
45 |
| -from virtualenv.create.creator import Creator |
46 |
| -from virtualenv.create.describe import PosixSupports, WindowsSupports |
47 | 45 | from virtualenv.seed.embed.pip_invoke import PipInvoke
|
48 | 46 | from virtualenv.seed.wheels import get_wheel
|
49 | 47 | from virtualenv.seed.wheels.bundle import from_dir
|
50 | 48 |
|
51 |
| - |
52 |
| -class AbstractGraalPyCreator(Creator): |
53 |
| - """ |
54 |
| - Describe and fake Creator service for GraalPy. |
55 |
| -
|
56 |
| - For the time being, we expect users to just use the builtin 'venv' creator |
57 |
| - with GraalPy, but the virtualenv package cannot just support GraalPy as a |
58 |
| - generic "venv capable python", because it needs a Describe service for the |
59 |
| - Seeder and Activator services. |
60 |
| -
|
61 |
| - Note: that even if we provided GraalPy specific Creator service, the builtin |
62 |
| - 'venv' Creator takes precedence as the default. Therefore, the users would |
63 |
| - have to enable the GraalPy Creator via explicit option `--creator graalpy`. |
64 |
| -
|
65 |
| - Note: GraalPy cannot just simply pretend that it is CPython, because then the |
66 |
| - virtualenv Creator would not set up the toolchain executables in the new virtual |
67 |
| - environment. |
68 |
| - """ |
69 |
| - |
70 |
| - def __init__(self, options, interpreter): |
71 |
| - super().__init__(options, interpreter) |
72 |
| - self.options = options |
73 |
| - |
74 |
| - @classmethod |
75 |
| - def can_create(cls, interpreter): |
76 |
| - # We are fake creator, we actually do not want to be used as Creator |
77 |
| - return None |
78 |
| - |
79 |
| - @classmethod |
80 |
| - def can_describe(cls, interpreter): |
81 |
| - if not (interpreter.implementation == "GraalVM" and super().can_describe(interpreter)): |
82 |
| - return False |
83 |
| - |
84 |
| - # We monkey patch SeederSelector to use our ensurepip, but only if we know that there |
85 |
| - # is a chance that we are creating virtualenv for GraalPy, so that we do not mess up |
86 |
| - # with virtualenv if not necessary. This relies on the fact that virtualenv calls this |
87 |
| - # because it calls SeederSelector._get_default, but even if we monkey patched SeederSelector |
88 |
| - # eagerly, that code would still be executed only when virtualenv loads our plugin, which |
89 |
| - # happens to be very close to the point when it calls can_describe |
90 |
| - try: |
91 |
| - from virtualenv.run import SeederSelector |
92 |
| - _get_default_orig = SeederSelector._get_default |
93 |
| - |
94 |
| - def _seeder_selector_get_default_override(self): |
95 |
| - if self.interpreter.implementation == "GraalVM": |
96 |
| - return "graalpy" |
97 |
| - else: |
98 |
| - return _get_default_orig() |
99 |
| - |
100 |
| - SeederSelector._get_default = _seeder_selector_get_default_override |
101 |
| - except ImportError: |
102 |
| - pass |
103 |
| - except AttributeError: |
104 |
| - pass |
105 |
| - return True |
106 |
| - |
107 |
| - @classmethod |
108 |
| - def exe_stem(cls): |
109 |
| - return "graalpy" |
110 |
| - |
111 |
| - def create(self): |
112 |
| - raise RuntimeError("Please use the 'venv' creator with GraalPy. " |
113 |
| - "It should be the default and can be explicitly requested " |
114 |
| - "with command line option `--creator venv`, " |
115 |
| - "or environment variable VIRTUALENV_CREATOR=venv") |
116 |
| - |
117 |
| - |
118 |
| -class GraalPyCreatorPosix(AbstractGraalPyCreator, PosixSupports): |
119 |
| - pass |
120 |
| - |
121 |
| - |
122 |
| -class GraalPyCreatorWindows(AbstractGraalPyCreator, WindowsSupports): |
| 49 | +try: |
| 50 | + from virtualenv.create.via_global_ref.builtin.graalpy import GraalPyPosix, GraalPyWindows |
| 51 | +except ImportError: |
| 52 | + from abc import ABC |
| 53 | + from pathlib import Path |
| 54 | + |
| 55 | + from virtualenv.create.describe import PosixSupports, WindowsSupports |
| 56 | + from virtualenv.create.via_global_ref.builtin.ref import PathRefToDest, RefMust, RefWhen |
| 57 | + from virtualenv.create.via_global_ref.builtin.via_global_self_do import ViaGlobalRefVirtualenvBuiltin |
| 58 | + |
| 59 | + class GraalPy(ViaGlobalRefVirtualenvBuiltin, ABC): |
| 60 | + @classmethod |
| 61 | + def can_describe(cls, interpreter): |
| 62 | + return interpreter.implementation == "GraalVM" and super().can_describe(interpreter) |
| 63 | + |
| 64 | + @classmethod |
| 65 | + def exe_stem(cls): |
| 66 | + return "graalpy" |
| 67 | + |
| 68 | + @classmethod |
| 69 | + def exe_names(cls, interpreter): |
| 70 | + return { |
| 71 | + cls.exe_stem(), |
| 72 | + "python", |
| 73 | + f"python{interpreter.version_info.major}", |
| 74 | + f"python{interpreter.version_info.major}.{interpreter.version_info.minor}", |
| 75 | + } |
| 76 | + |
| 77 | + @classmethod |
| 78 | + def _executables(cls, interpreter): |
| 79 | + host = Path(interpreter.system_executable) |
| 80 | + targets = sorted(f"{name}{cls.suffix}" for name in cls.exe_names(interpreter)) |
| 81 | + yield host, targets, RefMust.NA, RefWhen.ANY |
| 82 | + |
| 83 | + @classmethod |
| 84 | + def sources(cls, interpreter): |
| 85 | + yield from super().sources(interpreter) |
| 86 | + python_dir = Path(interpreter.system_executable).resolve().parent |
| 87 | + if python_dir.name in {"bin", "Scripts"}: |
| 88 | + python_dir = python_dir.parent |
| 89 | + |
| 90 | + native_lib = cls._native_lib(python_dir / "lib", interpreter.platform) |
| 91 | + if native_lib.exists(): |
| 92 | + yield PathRefToDest(native_lib, dest=lambda self, s: self.bin_dir.parent / "lib" / s.name) |
| 93 | + |
| 94 | + for jvm_dir_name in ("jvm", "jvmlibs", "modules"): |
| 95 | + jvm_dir = python_dir / jvm_dir_name |
| 96 | + if jvm_dir.exists(): |
| 97 | + yield PathRefToDest(jvm_dir, dest=lambda self, s: self.bin_dir.parent / s.name) |
| 98 | + |
| 99 | + @classmethod |
| 100 | + def _shared_libs(cls, python_dir): |
| 101 | + raise NotImplementedError |
| 102 | + |
| 103 | + |
| 104 | + class GraalPyPosix(GraalPy, PosixSupports): |
| 105 | + @classmethod |
| 106 | + def _native_lib(cls, lib_dir, platform): |
| 107 | + if platform == "darwin": |
| 108 | + return lib_dir / "libpythonvm.dylib" |
| 109 | + return lib_dir / "libpythonvm.so" |
| 110 | + |
| 111 | + |
| 112 | + class GraalPyWindows(GraalPy, WindowsSupports): |
| 113 | + @classmethod |
| 114 | + def _native_lib(cls, lib_dir, _platform): |
| 115 | + return lib_dir / "pythonvm.dll" |
| 116 | + |
| 117 | + def set_pyenv_cfg(self): |
| 118 | + # GraalPy needs an additional entry in pyvenv.cfg on Windows |
| 119 | + super().set_pyenv_cfg() |
| 120 | + self.pyenv_cfg["venvlauncher_command"] = self.interpreter.system_executable |
| 121 | + |
| 122 | + |
| 123 | + |
| 124 | +# We monkey patch SeederSelector to use our seeder by default when creating |
| 125 | +# a GraalPy virtualenv. |
| 126 | +try: |
| 127 | + from virtualenv.run import SeederSelector |
| 128 | + _get_default_orig = SeederSelector._get_default |
| 129 | + |
| 130 | + def _seeder_selector_get_default_override(self): |
| 131 | + if self.interpreter.implementation == "GraalVM": |
| 132 | + return "graalpy" |
| 133 | + else: |
| 134 | + return _get_default_orig() |
| 135 | + |
| 136 | + SeederSelector._get_default = _seeder_selector_get_default_override |
| 137 | +except Exception: |
123 | 138 | pass
|
124 | 139 |
|
125 | 140 |
|
|
0 commit comments