is_emscripten, is_wasi,
requires_venv_with_pip, TEST_HOME_DIR,
requires_resource, copy_python_src_ignore)
-from test.support.os_helper import (can_symlink, EnvironmentVarGuard, rmtree)
+from test.support.os_helper import (can_symlink, EnvironmentVarGuard, rmtree,
+ TESTFN)
import unittest
import venv
from unittest.mock import patch, Mock
with self.assertRaises(FileNotFoundError):
self.get_text_file_contents('.gitignore')
+ def test_venv_same_path(self):
+ same_path = venv.EnvBuilder._same_path
+ if sys.platform == 'win32':
+ # Case-insensitive, and handles short/long names
+ tests = [
+ (True, TESTFN, TESTFN),
+ (True, TESTFN.lower(), TESTFN.upper()),
+ ]
+ import _winapi
+ # ProgramFiles is the most reliable path that will have short/long
+ progfiles = os.getenv('ProgramFiles')
+ if progfiles:
+ tests = [
+ *tests,
+ (True, progfiles, progfiles),
+ (True, _winapi.GetShortPathName(progfiles), _winapi.GetLongPathName(progfiles)),
+ ]
+ else:
+ # Just a simple case-sensitive comparison
+ tests = [
+ (True, TESTFN, TESTFN),
+ (False, TESTFN.lower(), TESTFN.upper()),
+ ]
+ for r, path1, path2 in tests:
+ with self.subTest(f"{path1}-{path2}"):
+ if r:
+ self.assertTrue(same_path(path1, path2))
+ else:
+ self.assertFalse(same_path(path1, path2))
+
@requireVenvCreate
class EnsurePipTest(BaseTest):
"""Test venv module installation of pip."""
# Test the Windows-only _winapi module
+import os
+import pathlib
import random
+import re
import threading
import time
import unittest
def test_max_events_waitany(self):
self._events_waitany_test(MAXIMUM_BATCHED_WAIT_OBJECTS)
+
+
+class WinAPITests(unittest.TestCase):
+ def test_getlongpathname(self):
+ testfn = pathlib.Path(os.getenv("ProgramFiles")).parents[-1] / "PROGRA~1"
+ if not os.path.isdir(testfn):
+ raise unittest.SkipTest("require x:\\PROGRA~1 to test")
+
+ # pathlib.Path will be rejected - only str is accepted
+ with self.assertRaises(TypeError):
+ _winapi.GetLongPathName(testfn)
+
+ actual = _winapi.GetLongPathName(os.fsdecode(testfn))
+
+ # Can't assume that PROGRA~1 expands to any particular variation, so
+ # ensure it matches any one of them.
+ candidates = set(testfn.parent.glob("Progra*"))
+ self.assertIn(pathlib.Path(actual), candidates)
+
+ def test_getshortpathname(self):
+ testfn = pathlib.Path(os.getenv("ProgramFiles"))
+ if not os.path.isdir(testfn):
+ raise unittest.SkipTest("require '%ProgramFiles%' to test")
+
+ # pathlib.Path will be rejected - only str is accepted
+ with self.assertRaises(TypeError):
+ _winapi.GetShortPathName(testfn)
+
+ actual = _winapi.GetShortPathName(os.fsdecode(testfn))
+
+ # Should contain "PROGRA~" but we can't predict the number
+ self.assertIsNotNone(re.match(r".\:\\PROGRA~\d", actual.upper()), actual)
}
return sysconfig.get_path(name, scheme='venv', vars=vars)
+ @classmethod
+ def _same_path(cls, path1, path2):
+ """Check whether two paths appear the same.
+
+ Whether they refer to the same file is irrelevant; we're testing for
+ whether a human reader would look at the path string and easily tell
+ that they're the same file.
+ """
+ if sys.platform == 'win32':
+ if os.path.normcase(path1) == os.path.normcase(path2):
+ return True
+ # gh-90329: Don't display a warning for short/long names
+ import _winapi
+ try:
+ path1 = _winapi.GetLongPathName(os.fsdecode(path1))
+ except OSError:
+ pass
+ try:
+ path2 = _winapi.GetLongPathName(os.fsdecode(path2))
+ except OSError:
+ pass
+ if os.path.normcase(path1) == os.path.normcase(path2):
+ return True
+ return False
+ else:
+ return path1 == path2
+
def ensure_directories(self, env_dir):
"""
Create the directories for the environment.
# bpo-45337: Fix up env_exec_cmd to account for file system redirections.
# Some redirects only apply to CreateFile and not CreateProcess
real_env_exe = os.path.realpath(context.env_exe)
- if os.path.normcase(real_env_exe) != os.path.normcase(context.env_exe):
+ if not self._same_path(real_env_exe, context.env_exe):
logger.warning('Actual environment location may have moved due to '
'redirects, links or junctions.\n'
' Requested location: "%s"\n'
--- /dev/null
+Suppress the warning displayed on virtual environment creation when the
+requested and created paths differ only by a short (8.3 style) name.
+Warnings will continue to be shown if a junction or symlink in the path
+caused the venv to be created in a different location than originally
+requested.
return GetLastError();
}
+
+/*[clinic input]
+_winapi.GetLongPathName
+
+ path: LPCWSTR
+
+Return the long version of the provided path.
+
+If the path is already in its long form, returns the same value.
+
+The path must already be a 'str'. If the type is not known, use
+os.fsdecode before calling this function.
+[clinic start generated code]*/
+
+static PyObject *
+_winapi_GetLongPathName_impl(PyObject *module, LPCWSTR path)
+/*[clinic end generated code: output=c4774b080275a2d0 input=9872e211e3a4a88f]*/
+{
+ DWORD cchBuffer;
+ PyObject *result = NULL;
+
+ Py_BEGIN_ALLOW_THREADS
+ cchBuffer = GetLongPathNameW(path, NULL, 0);
+ Py_END_ALLOW_THREADS
+ if (cchBuffer) {
+ WCHAR *buffer = (WCHAR *)PyMem_Malloc(cchBuffer * sizeof(WCHAR));
+ if (buffer) {
+ Py_BEGIN_ALLOW_THREADS
+ cchBuffer = GetLongPathNameW(path, buffer, cchBuffer);
+ Py_END_ALLOW_THREADS
+ if (cchBuffer) {
+ result = PyUnicode_FromWideChar(buffer, cchBuffer);
+ } else {
+ PyErr_SetFromWindowsErr(0);
+ }
+ PyMem_Free((void *)buffer);
+ }
+ } else {
+ PyErr_SetFromWindowsErr(0);
+ }
+ return result;
+}
+
/*[clinic input]
_winapi.GetModuleFileName
return PyUnicode_FromWideChar(filename, wcslen(filename));
}
+/*[clinic input]
+_winapi.GetShortPathName
+
+ path: LPCWSTR
+
+Return the short version of the provided path.
+
+If the path is already in its short form, returns the same value.
+
+The path must already be a 'str'. If the type is not known, use
+os.fsdecode before calling this function.
+[clinic start generated code]*/
+
+static PyObject *
+_winapi_GetShortPathName_impl(PyObject *module, LPCWSTR path)
+/*[clinic end generated code: output=dab6ae494c621e81 input=43fa349aaf2ac718]*/
+{
+ DWORD cchBuffer;
+ PyObject *result = NULL;
+
+ Py_BEGIN_ALLOW_THREADS
+ cchBuffer = GetShortPathNameW(path, NULL, 0);
+ Py_END_ALLOW_THREADS
+ if (cchBuffer) {
+ WCHAR *buffer = (WCHAR *)PyMem_Malloc(cchBuffer * sizeof(WCHAR));
+ if (buffer) {
+ Py_BEGIN_ALLOW_THREADS
+ cchBuffer = GetShortPathNameW(path, buffer, cchBuffer);
+ Py_END_ALLOW_THREADS
+ if (cchBuffer) {
+ result = PyUnicode_FromWideChar(buffer, cchBuffer);
+ } else {
+ PyErr_SetFromWindowsErr(0);
+ }
+ PyMem_Free((void *)buffer);
+ }
+ } else {
+ PyErr_SetFromWindowsErr(0);
+ }
+ return result;
+}
+
/*[clinic input]
_winapi.GetStdHandle -> HANDLE
_WINAPI_GETCURRENTPROCESS_METHODDEF
_WINAPI_GETEXITCODEPROCESS_METHODDEF
_WINAPI_GETLASTERROR_METHODDEF
+ _WINAPI_GETLONGPATHNAME_METHODDEF
_WINAPI_GETMODULEFILENAME_METHODDEF
+ _WINAPI_GETSHORTPATHNAME_METHODDEF
_WINAPI_GETSTDHANDLE_METHODDEF
_WINAPI_GETVERSION_METHODDEF
_WINAPI_MAPVIEWOFFILE_METHODDEF
return return_value;
}
+PyDoc_STRVAR(_winapi_GetLongPathName__doc__,
+"GetLongPathName($module, /, path)\n"
+"--\n"
+"\n"
+"Return the long version of the provided path.\n"
+"\n"
+"If the path is already in its long form, returns the same value.\n"
+"\n"
+"The path must already be a \'str\'. If the type is not known, use\n"
+"os.fsdecode before calling this function.");
+
+#define _WINAPI_GETLONGPATHNAME_METHODDEF \
+ {"GetLongPathName", _PyCFunction_CAST(_winapi_GetLongPathName), METH_FASTCALL|METH_KEYWORDS, _winapi_GetLongPathName__doc__},
+
+static PyObject *
+_winapi_GetLongPathName_impl(PyObject *module, LPCWSTR path);
+
+static PyObject *
+_winapi_GetLongPathName(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ PyObject *return_value = NULL;
+ #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
+
+ #define NUM_KEYWORDS 1
+ static struct {
+ PyGC_Head _this_is_not_used;
+ PyObject_VAR_HEAD
+ PyObject *ob_item[NUM_KEYWORDS];
+ } _kwtuple = {
+ .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
+ .ob_item = { &_Py_ID(path), },
+ };
+ #undef NUM_KEYWORDS
+ #define KWTUPLE (&_kwtuple.ob_base.ob_base)
+
+ #else // !Py_BUILD_CORE
+ # define KWTUPLE NULL
+ #endif // !Py_BUILD_CORE
+
+ static const char * const _keywords[] = {"path", NULL};
+ static _PyArg_Parser _parser = {
+ .keywords = _keywords,
+ .fname = "GetLongPathName",
+ .kwtuple = KWTUPLE,
+ };
+ #undef KWTUPLE
+ PyObject *argsbuf[1];
+ LPCWSTR path = NULL;
+
+ args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf);
+ if (!args) {
+ goto exit;
+ }
+ if (!PyUnicode_Check(args[0])) {
+ _PyArg_BadArgument("GetLongPathName", "argument 'path'", "str", args[0]);
+ goto exit;
+ }
+ path = PyUnicode_AsWideCharString(args[0], NULL);
+ if (path == NULL) {
+ goto exit;
+ }
+ return_value = _winapi_GetLongPathName_impl(module, path);
+
+exit:
+ /* Cleanup for path */
+ PyMem_Free((void *)path);
+
+ return return_value;
+}
+
PyDoc_STRVAR(_winapi_GetModuleFileName__doc__,
"GetModuleFileName($module, module_handle, /)\n"
"--\n"
return return_value;
}
+PyDoc_STRVAR(_winapi_GetShortPathName__doc__,
+"GetShortPathName($module, /, path)\n"
+"--\n"
+"\n"
+"Return the short version of the provided path.\n"
+"\n"
+"If the path is already in its short form, returns the same value.\n"
+"\n"
+"The path must already be a \'str\'. If the type is not known, use\n"
+"os.fsdecode before calling this function.");
+
+#define _WINAPI_GETSHORTPATHNAME_METHODDEF \
+ {"GetShortPathName", _PyCFunction_CAST(_winapi_GetShortPathName), METH_FASTCALL|METH_KEYWORDS, _winapi_GetShortPathName__doc__},
+
+static PyObject *
+_winapi_GetShortPathName_impl(PyObject *module, LPCWSTR path);
+
+static PyObject *
+_winapi_GetShortPathName(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ PyObject *return_value = NULL;
+ #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
+
+ #define NUM_KEYWORDS 1
+ static struct {
+ PyGC_Head _this_is_not_used;
+ PyObject_VAR_HEAD
+ PyObject *ob_item[NUM_KEYWORDS];
+ } _kwtuple = {
+ .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
+ .ob_item = { &_Py_ID(path), },
+ };
+ #undef NUM_KEYWORDS
+ #define KWTUPLE (&_kwtuple.ob_base.ob_base)
+
+ #else // !Py_BUILD_CORE
+ # define KWTUPLE NULL
+ #endif // !Py_BUILD_CORE
+
+ static const char * const _keywords[] = {"path", NULL};
+ static _PyArg_Parser _parser = {
+ .keywords = _keywords,
+ .fname = "GetShortPathName",
+ .kwtuple = KWTUPLE,
+ };
+ #undef KWTUPLE
+ PyObject *argsbuf[1];
+ LPCWSTR path = NULL;
+
+ args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf);
+ if (!args) {
+ goto exit;
+ }
+ if (!PyUnicode_Check(args[0])) {
+ _PyArg_BadArgument("GetShortPathName", "argument 'path'", "str", args[0]);
+ goto exit;
+ }
+ path = PyUnicode_AsWideCharString(args[0], NULL);
+ if (path == NULL) {
+ goto exit;
+ }
+ return_value = _winapi_GetShortPathName_impl(module, path);
+
+exit:
+ /* Cleanup for path */
+ PyMem_Free((void *)path);
+
+ return return_value;
+}
+
PyDoc_STRVAR(_winapi_GetStdHandle__doc__,
"GetStdHandle($module, std_handle, /)\n"
"--\n"
return return_value;
}
-/*[clinic end generated code: output=1f5bbcfa8d1847c5 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=ed94a2482ede3744 input=a9049054013a1b77]*/