Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@ def add_graalpython_core():
"_sysconfig",
"java",
"pip_hook",
"_nt",
]:
modname = f"graalpy.{os.path.basename(name)}"
modpath = os.path.join(lib_graalpython, f"{name}.py")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1551,7 +1551,6 @@ def test_take_ownership(self):
obj.clear_value()
assert value == (1, 2, 3, "hello", "world", dummy)

@skipIf(is_windows, reason="GR-52900")
def test_async_slots(self):
import asyncio, types, functools
TestTpAsync = CPyExtType("TestTpAsync",
Expand Down
22 changes: 18 additions & 4 deletions graalpython/com.oracle.graal.python.test/src/tests/test_startup.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,19 @@
import platform

# Both lists should remain as small as possible to avoid adding overhead to startup
IS_WINDOWS = platform.system() == 'Windows'
WINDOWS_CORE_MODULES = ['_nt', '_winapi', '_overlapped', 'winreg', '_winreg'] if IS_WINDOWS else []
WINDOWS_FULL_STARTUP_MODULES = [
'_datetime',
'datetime',
'_ctypes',
'_struct',
'struct',
'ctypes._endian',
'ctypes',
'ctypes.wintypes',
] if IS_WINDOWS else []

expected_nosite_startup_modules = [
'_frozen_importlib',
'_frozen_importlib_external',
Expand All @@ -52,7 +65,7 @@
'_sysconfig',
'java',
'pip_hook',
] + (['_nt'] if platform.system() == 'Windows' else [])
] + WINDOWS_CORE_MODULES

expected_full_startup_modules = expected_nosite_startup_modules + [
'_abc',
Expand All @@ -63,11 +76,12 @@
'stat',
'_collections_abc',
'genericpath',
*(['_winapi', 'ntpath'] if platform.system() == 'Windows' else ['posixpath']),
*(['ntpath'] if IS_WINDOWS else ['posixpath']),
'os',
'_sitebuiltins',
'_io',
'io',
*WINDOWS_FULL_STARTUP_MODULES,
'site',
]

Expand All @@ -76,12 +90,12 @@ class StartupTests(unittest.TestCase):
def test_startup_nosite(self):
result = subprocess.check_output([sys.executable, '--log.level=FINE', '-S', '-v', '-c', 'print("Hello")'], stderr=subprocess.STDOUT, text=True)
assert 'Hello' in result
imports = re.findall("import '(\S+)'", result)
imports = re.findall(r"import '(\S+)'", result)
self.assertEqual(expected_nosite_startup_modules, imports)

@unittest.skipUnless(sys.implementation.name == 'graalpy', "GraalPy-specific test")
def test_startup_full(self):
result = subprocess.check_output([sys.executable, '--log.level=FINE', '-s', '-v', '-c', 'print("Hello")'], stderr=subprocess.STDOUT, text=True)
assert 'Hello' in result
imports = re.findall("import '(\S+)'", result)
imports = re.findall(r"import '(\S+)'", result)
self.assertEqual(expected_full_startup_modules, imports)
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
import com.oracle.graal.python.builtins.modules.MsvcrtModuleBuiltins;
import com.oracle.graal.python.builtins.modules.NtModuleBuiltins;
import com.oracle.graal.python.builtins.modules.OperatorModuleBuiltins;
import com.oracle.graal.python.builtins.modules.OverlappedModuleBuiltins;
import com.oracle.graal.python.builtins.modules.PolyglotModuleBuiltins;
import com.oracle.graal.python.builtins.modules.PosixModuleBuiltins;
import com.oracle.graal.python.builtins.modules.PosixShMemModuleBuiltins;
Expand Down Expand Up @@ -122,6 +123,7 @@
import com.oracle.graal.python.builtins.modules.WarningsModuleBuiltins;
import com.oracle.graal.python.builtins.modules.WeakRefModuleBuiltins;
import com.oracle.graal.python.builtins.modules.WinapiModuleBuiltins;
import com.oracle.graal.python.builtins.modules.WinregLegacyModuleBuiltins;
import com.oracle.graal.python.builtins.modules.WinregModuleBuiltins;
import com.oracle.graal.python.builtins.modules.ast.AstBuiltins;
import com.oracle.graal.python.builtins.modules.ast.AstModuleBuiltins;
Expand Down Expand Up @@ -418,6 +420,7 @@
import com.oracle.truffle.api.object.Shape;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.strings.TruffleString;
import org.graalvm.nativeimage.ImageInfo;

/**
* The core is a historical artifact and PythonContext and Python3Core should be merged.
Expand All @@ -435,18 +438,13 @@ public abstract class Python3Core {

private static TruffleString[] getCoreFiles() {
// Order matters!
List<TruffleString> coreFiles = List.of(
return new TruffleString[]{
T___GRAALPYTHON__,
T__SRE,
T__SYSCONFIG,
T_JAVA,
toTruffleStringUncached("pip_hook"));
if (PythonLanguage.getPythonOS() == PythonOS.PLATFORM_WIN32) {
coreFiles = new ArrayList<>(coreFiles);
coreFiles.add(toTruffleStringUncached("_nt"));
}

return coreFiles.toArray(new TruffleString[0]);
toTruffleStringUncached("pip_hook")
};
}

private PythonBuiltins[] builtins;
Expand Down Expand Up @@ -561,6 +559,8 @@ private static PythonBuiltins[] initializeBuiltins(TruffleLanguage.Env env) {
new WinregModuleBuiltins(),
new MsvcrtModuleBuiltins(),
new WinapiModuleBuiltins(),
new OverlappedModuleBuiltins(),
new WinregLegacyModuleBuiltins(),
new CryptModuleBuiltins(),
new ScandirIteratorBuiltins(),
new DirEntryBuiltins(),
Expand Down Expand Up @@ -1051,6 +1051,17 @@ private void initializePython3Core(TruffleString coreHome) {
initialized = true;
}

private void initializeWindowsCoreFiles(TruffleString coreHome) {
if (PythonLanguage.getPythonOS() == PythonOS.PLATFORM_WIN32) {
assert !ImageInfo.inImageBuildtimeCode();
loadFile(toTruffleStringUncached("_nt"), coreHome);
loadFile(toTruffleStringUncached("_winapi"), toTruffleStringUncached("modules/_winapi"), coreHome);
loadFile(toTruffleStringUncached("_overlapped"), toTruffleStringUncached("modules/_overlapped"), coreHome);
loadFile(toTruffleStringUncached("winreg"), toTruffleStringUncached("modules/winreg"), coreHome);
loadFile(toTruffleStringUncached("_winreg"), toTruffleStringUncached("modules/_winreg"), coreHome);
}
}

/**
* Run post-initialization code that needs a fully working Python environment. This will be run
* eagerly when the context is initialized on the JVM or a new context is created on SVM, but is
Expand All @@ -1059,6 +1070,7 @@ private void initializePython3Core(TruffleString coreHome) {
public final void postInitialize(Env env) {
if (!env.isPreInitialization()) {
initialized = false;
initializeWindowsCoreFiles(getContext().getCoreHomeOrFail());

for (PythonBuiltins builtin : builtins) {
if (builtin.needsPostInitialize()) {
Expand Down Expand Up @@ -1319,26 +1331,34 @@ private Source getInternalSource(TruffleString basename, TruffleString prefix) {
}

private void loadFile(TruffleString s, TruffleString prefix) {
loadFile(s, s, prefix);
}

private void loadFile(TruffleString s, TruffleString sourceName, TruffleString prefix) {
PythonModule mod = lookupBuiltinModule(s);
if (mod == null) {
// use an anonymous module for the side-effects
mod = PFactory.createPythonModule(T___ANONYMOUS__);
}
loadFile(s, prefix, mod);
loadFile(s, sourceName, prefix, mod);
}

private void loadFile(TruffleString s, TruffleString prefix, PythonModule mod) {
loadFile(s, s, prefix, mod);
}

private void loadFile(TruffleString s, TruffleString sourceName, TruffleString prefix, PythonModule mod) {
if (ImpModuleBuiltins.importFrozenModuleObjectNoRaise(this, cat(T_GRAALPYTHON, T_DOT, s), mod) != null) {
LOGGER.log(Level.FINE, () -> "import '" + s + "' # <frozen>");
return;
}

LOGGER.log(Level.FINE, () -> "import '" + s + "'");
Supplier<CallTarget> getCode = () -> {
Source source = getInternalSource(s, prefix);
Source source = getInternalSource(sourceName, prefix);
return getLanguage().parse(getContext(), source, InputType.FILE, false, 0, false, null, EnumSet.noneOf(FutureFeature.class));
};
RootCallTarget callTarget = (RootCallTarget) getLanguage().cacheCode(s, getCode);
RootCallTarget callTarget = (RootCallTarget) getLanguage().cacheCode(sourceName, getCode);
PCode code = PFactory.createCode(language, callTarget);
CallDispatchers.SimpleIndirectInvokeNode.executeUncached(callTarget, PArguments.withGlobals(code, mod));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -563,8 +563,11 @@ private static Object doLoadBytecodeFile(Object bytecodePath, Object sourcePath,
cacheKey = ARRAY_ACCESSOR_LE.getLong(bytes, 8);
}
if (context.getOption(PythonOptions.VerboseFlag)) {
Object message = PyObjectCallMethodObjArgs.executeUncached(MESSAGE, T_FORMAT, bytecodePath, sourcePath);
CallNode.executeUncached(context.lookupBuiltinModule(T__BOOTSTRAP).getAttribute(T__VERBOSE_MESSAGE), message);
PythonModule bootstrap = context.lookupBuiltinModule(T__BOOTSTRAP);
if (bootstrap != null) {
Object message = PyObjectCallMethodObjArgs.executeUncached(MESSAGE, T_FORMAT, bytecodePath, sourcePath);
CallNode.executeUncached(bootstrap.getAttribute(T__VERBOSE_MESSAGE), message);
}
}
TruffleFile sourceFile = null;
if (sourcePath != PNone.NONE) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
*
* Subject to the condition set forth below, permission is hereby granted to any
* person obtaining a copy of this software, associated documentation and/or
* data (collectively the "Software"), free of charge and under any and all
* copyright rights in the Software, and any and all patent rights owned or
* freely licensable by each licensor hereunder covering either (i) the
* unmodified Software as contributed to or provided by such licensor, or (ii)
* the Larger Works (as defined below), to deal in both
*
* (a) the Software, and
*
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
* one is included with the Software each a "Larger Work" to which the Software
* is contributed by such licensors),
*
* without restriction, including without limitation the rights to copy, create
* derivative works of, display, perform, and distribute the Software and make,
* use, sell, offer for sale, import, export, have made, and have sold the
* Software and the Larger Work(s), and to sublicense the foregoing rights on
* either these or other terms.
*
* This license is subject to the following condition:
*
* The above copyright notice and either this complete permission notice or at a
* minimum a reference to the UPL must be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.oracle.graal.python.builtins.modules;

import java.util.List;

import com.oracle.graal.python.annotations.PythonOS;
import com.oracle.graal.python.builtins.CoreFunctions;
import com.oracle.graal.python.builtins.PythonBuiltins;
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
import com.oracle.truffle.api.dsl.NodeFactory;

@CoreFunctions(defineModule = "_overlapped", os = PythonOS.PLATFORM_WIN32)
public final class OverlappedModuleBuiltins extends PythonBuiltins {
@Override
protected List<? extends NodeFactory<? extends PythonBuiltinBaseNode>> getNodeFactories() {
return List.of();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* The Universal Permissive License (UPL), Version 1.0
*
* Subject to the condition set forth below, permission is hereby granted to any
* person obtaining a copy of this software, associated documentation and/or
* data (collectively the "Software"), free of charge and under any and all
* copyright rights in the Software, and any and all patent rights owned or
* freely licensable by each licensor hereunder covering either (i) the
* unmodified Software as contributed to or provided by such licensor, or (ii)
* the Larger Works (as defined below), to deal in both
*
* (a) the Software, and
*
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
* one is included with the Software each a "Larger Work" to which the Software
* is contributed by such licensors),
*
* without restriction, including without limitation the rights to copy, create
* derivative works of, display, perform, and distribute the Software and make,
* use, sell, offer for sale, import, export, have made, and have sold the
* Software and the Larger Work(s), and to sublicense the foregoing rights on
* either these or other terms.
*
* This license is subject to the following condition:
*
* The above copyright notice and either this complete permission notice or at a
* minimum a reference to the UPL must be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.oracle.graal.python.builtins.modules;

import java.util.List;

import com.oracle.graal.python.annotations.PythonOS;
import com.oracle.graal.python.builtins.CoreFunctions;
import com.oracle.graal.python.builtins.PythonBuiltins;
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
import com.oracle.truffle.api.dsl.NodeFactory;

@CoreFunctions(defineModule = "_winreg", os = PythonOS.PLATFORM_WIN32)
public final class WinregLegacyModuleBuiltins extends PythonBuiltins {
@Override
protected List<? extends NodeFactory<? extends PythonBuiltinBaseNode>> getNodeFactories() {
return List.of();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ private static final class Map {
private static final PythonFrozenModule GRAALPY__SYSCONFIG = new PythonFrozenModule("GRAALPY__SYSCONFIG", null, false);
private static final PythonFrozenModule GRAALPY_JAVA = new PythonFrozenModule("GRAALPY_JAVA", null, false);
private static final PythonFrozenModule GRAALPY_PIP_HOOK = new PythonFrozenModule("GRAALPY_PIP_HOOK", null, false);
private static final PythonFrozenModule GRAALPY__NT = new PythonFrozenModule("GRAALPY__NT", null, false);
}

public static final PythonFrozenModule lookup(String name) {
Expand Down Expand Up @@ -236,8 +235,6 @@ public static final PythonFrozenModule lookup(String name) {
return Map.GRAALPY_JAVA;
case "graalpy.pip_hook":
return Map.GRAALPY_PIP_HOOK;
case "graalpy._nt":
return Map.GRAALPY__NT;
default:
return null;
}
Expand Down
16 changes: 11 additions & 5 deletions graalpython/lib-graalpython/_nt.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
#
# The Universal Permissive License (UPL), Version 1.0
Expand Down Expand Up @@ -42,23 +42,29 @@

def _add_dll_directory(path):
import ctypes, os
AddDllDirectory = ctypes.windll.kernel32['AddDllDirectory']
kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
kernel32.GetLastError.argtypes = []
kernel32.GetLastError.restype = ctypes.c_ulong
AddDllDirectory = kernel32.AddDllDirectory
AddDllDirectory.argtypes = [ctypes.c_wchar_p]
AddDllDirectory.restype = ctypes.c_void_p
result = AddDllDirectory(os.fspath(path))
if result == 0:
raise OSError(f"add_dll_directory: {ctypes.windll.kernel32.GetLastError()}")
raise OSError(f"add_dll_directory: {kernel32.GetLastError()}")
return result


def _remove_dll_directory(cookie):
import ctypes
RemoveDllDirectory = ctypes.windll.kernel32['RemoveDllDirectory']
kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
kernel32.GetLastError.argtypes = []
kernel32.GetLastError.restype = ctypes.c_ulong
RemoveDllDirectory = kernel32.RemoveDllDirectory
RemoveDllDirectory.argtypes = [ctypes.c_void_p]
RemoveDllDirectory.restype = ctypes.c_int
result = RemoveDllDirectory(cookie)
if result == 0:
raise OSError(f"remove_dll_directory: {ctypes.windll.kernel32.GetLastError()}")
raise OSError(f"remove_dll_directory: {kernel32.GetLastError()}")


nt._add_dll_directory = _add_dll_directory
Expand Down
Loading
Loading