|
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